Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Large Page Support for Code Issue: 16198 #21064

Closed
Closed
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0a76c7e
Initial Large Page (2M) support for node.js
suresh-srinivas Mar 23, 2018
fa0e324
Add support for checking if explicitHugePages and transparentHugePage…
suresh-srinivas Mar 26, 2018
fdb45cd
Added License headers
suresh-srinivas Mar 30, 2018
b7791c2
Get rid of waring messages & code cleanup
suresh-srinivas Mar 30, 2018
6631eea
Protect the large pages under #ifdef NODE_ENABLE_LARGE_CODE_PAGES and…
suresh-srinivas Apr 4, 2018
843089c
Added configure option to enable huge pages
uttampawar Apr 9, 2018
077bc01
Added else clause to set node_use_large_pages=false by default
uttampawar Apr 9, 2018
7bdd9fc
Merge branch 'configure_fixes' into 2M-Pages-For-Code-16198
uttampawar Apr 9, 2018
b91de5a
Finished adding checks at appropriate places to handle possible error…
uttampawar May 8, 2018
7ef956a
Changed return type for two function from void to int
uttampawar May 9, 2018
b02e7a9
Fixed lint errors.
uttampawar May 24, 2018
2af82b1
Added one more condition check for verify the start address of newly …
uttampawar May 26, 2018
cae9285
Fixed syntax error due to wring data type. int64 to int64_t
uttampawar May 30, 2018
30114b6
Removed explictHugePages code
suresh-srinivas May 30, 2018
39e1f0d
Merge branch 'add-checks' into 2M-Pages-For-Code-16198
suresh-srinivas May 30, 2018
49cd0de
Merge branch '2M-Pages-For-Code-16198' of https://github.intel.com/DS…
suresh-srinivas May 30, 2018
2dd9e8c
Added Large Page Support
suresh-srinivas May 30, 2018
51d0f02
Merge branch 'intel-large-pages' of https://github.com/suresh-sriniva…
suresh-srinivas May 31, 2018
2f672ee
Update PR based on feedback
suresh-srinivas Jun 1, 2018
31504cc
Addressing the additional PR feedback
suresh-srinivas Jun 3, 2018
f998c58
Fix the gypi style issue
suresh-srinivas Jun 3, 2018
9f15cfc
Update with stylistic changes (eg char* instead of char *, IsLargePag…
suresh-srinivas Jun 4, 2018
d6de361
Add additional guard so large pages is only under Linux and target_ar…
suresh-srinivas Jun 4, 2018
29c7d13
Style fixes according to the Node C++ Style Guide
suresh-srinivas Jun 4, 2018
9828036
Eliminate ld.script and use implicit script
suresh-srinivas Jun 7, 2018
600cf54
Add #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
suresh-srinivas Jun 7, 2018
bf259e2
Fix the test failures
suresh-srinivas Jun 7, 2018
f0a6dcb
Add detailed help message to configure --with-largepages
suresh-srinivas Jun 8, 2018
4610793
Fix style issues and created inline functions instead of macros for a…
suresh-srinivas Jun 12, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,12 @@ parser.add_option('--with-etw',
dest='with_etw',
help='build with ETW (default is true on Windows)')

parser.add_option('--use-largepages',
action='store_true',
dest='node_use_large_pages',
help='build with Large Pages support (enabled only for Linux).' +
'(Needs Linux kernel >= 2.6.38 with Transparent Huge pages enabled)')

intl_optgroup.add_option('--with-intl',
action='store',
dest='with_intl',
Expand Down Expand Up @@ -936,6 +942,9 @@ def configure_node(o):
else:
o['variables']['node_use_dtrace'] = 'false'

use_large_pages = (flavor == 'linux' and options.node_use_large_pages)
o['variables']['node_use_large_pages'] = b(use_large_pages)

if options.no_ifaddrs:
o['defines'] += ['SUNOS_NO_IFADDRS']

Expand Down
8 changes: 8 additions & 0 deletions ld.implicit.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SECTIONS {
.lpstub : { *(.lpstub) }
}
PROVIDE (__nodetext = .);
PROVIDE (_nodetext = .);
PROVIDE (nodetext = .);
INSERT BEFORE .text;

13 changes: 13 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,19 @@
'src/tls_wrap.h'
],
}],
[ 'node_use_large_pages=="true"', {
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
# The current implementation of Large Pages is under Linux.
# Other implementations are possible but not currently supported.
#
'conditions': [
[ 'OS=="linux"', {
'sources': [
'src/node_large_page.cc'
],
}],
]
} ],
],
},
{
Expand Down
13 changes: 12 additions & 1 deletion node.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,18 @@
},
},
'conditions': [
['OS!="aix" and node_shared=="false"', {
['OS=="linux" and node_shared=="false" \
and target_arch=="x64" \
and node_use_large_pages=="true"', {
'ldflags': [
'-Wl,-T <(PRODUCT_DIR)/../../ld.implicit.script',
'-Wl,--whole-archive,<(obj_dir)/deps/uv/<(STATIC_LIB_PREFIX)'
'uv<(STATIC_LIB_SUFFIX)',
'-Wl,--no-whole-archive',
]
}],
['OS!="aix" and node_shared=="false" \
and node_use_large_pages=="false"', {
'ldflags': [
'-Wl,--whole-archive,<(obj_dir)/deps/uv/<(STATIC_LIB_PREFIX)'
'uv<(STATIC_LIB_SUFFIX)',
Expand Down
10 changes: 10 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
#ifdef NODE_ENABLE_VTUNE_PROFILING
#include "../deps/v8/src/third_party/vtune/v8-vtune.h"
#endif
#include "node_large_page.h"

#include <errno.h>
#include <fcntl.h> // _O_RDWR
Expand Down Expand Up @@ -4377,6 +4378,15 @@ int Start(int argc, char** argv) {

CHECK_GT(argc, 0);

#ifdef NODE_ENABLE_LARGE_CODE_PAGES
if (node::IsLargePagesEnabled()) {
if ((node::MapStaticCodeToLargePages()) != 0) {
fprintf(stderr, "Reverting to default page size\n");
}
}
#endif


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason why we are doing it here, as opposed to the very beginning of everything, in main?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particular reason. This was a place where other things were initialized like VTune support.

// Hack around with the argv pointer. Used for process.title = "blah".
argv = uv_setup_args(argc, argv);

Expand Down
253 changes: 253 additions & 0 deletions src/node_large_page.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
// Copyright (C) 2018 Intel Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom
// the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
// OR OTHER DEALINGS IN THE SOFTWARE.
//
// SPDX-License-Identifier: MIT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment in here pointing back to it's original source?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fishrock123 I dont fully understand the comment. Pointing back to the SPDX url?
https://spdx.org/licenses/MIT.html


#include <errno.h>
#include <fcntl.h> // _O_RDWR
#include <limits.h> // PATH_MAX
#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>

#include <string>
#include <fstream>
#include <iostream>
#include <sstream>

// The functions in this file map the text segment of node into 2M pages.
// The algorithm is simple
// 1. Find the text region of node binary in memory
// Examine the /proc/self/maps to determine the currently mapped text
// region and obtain the start and end
// Modify the start to point to the very beginning of node text segment
// (from variable nodetext setup in ld.script)
// Align the address of start and end to Large Page Boundaries
//
// 2. Move the text region to large pages
// Map a new area and copy the original code there
// Use mmap using the start address with MAP_FIXED so we get exactly the
// same virtual address
// Use madvise with MADV_HUGE_PAGE to use Anonymous 2M Pages
// If successful copy the code there and unmap the original region.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worthwhile to expand the algorithm a bit more - helps later maintainance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure that sounds good. I will add more details.

extern char __executable_start;
extern char __etext;
extern char __nodetext;

namespace node {
#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
#define PAGE_ALIGN_UP(x, a) ALIGN(x, a)
#define PAGE_ALIGN_DOWN(x, a) ((x) & ~((a) - 1))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: these could all be inline functions, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Yes fixed them to be inline functions.


struct text_region {
char* from;
char* to;
int total_hugepages;
bool found_text_region;
};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how many of these fields are used?
can we follow a consistent variable naming convention? (camel casing vs snake casing etc.) I suggest look around in node.cc for the prevailing nomenclature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will look and make it consistent.

static void PrintSystemError(int error) {
fprintf(stderr, "Hugepages WARNING: %s\n", strerror(error));
return;
}

// The format of the maps file is the following
// address perms offset dev inode pathname
// 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/dbus-daemon

static struct text_region FindNodeTextRegion() {
std::ifstream ifs;
std::string map_line;
std::string permission;
char dash;
int64_t start, end;
const size_t hps = 2L * 1024 * 1024;
struct text_region nregion;

nregion.found_text_region = false;

ifs.open("/proc/self/maps");
if (!ifs) {
fprintf(stderr, "Could not open /proc/self/maps\n");
return nregion;
}
std::getline(ifs, map_line);
std::istringstream iss(map_line);
ifs.close();

iss >> std::hex >> start;
iss >> dash;
iss >> std::hex >> end;
iss >> permission;

if (permission.compare("r-xp") == 0) {
start = reinterpret_cast<unsigned int64_t>(&__nodetext);
char* from = reinterpret_cast<char *>PAGE_ALIGN_UP(start, hps);
char* to = reinterpret_cast<char *>PAGE_ALIGN_DOWN(end, hps);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: left-leaning pointers (e.g. char*), and I would not rely on the implicit parentheses added by the macros here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Fixed.


if (from < to) {
size_t size = to - from;
nregion.found_text_region = true;
nregion.from = from;
nregion.to = to;
nregion.total_hugepages = size / hps;
}
}

return nregion;
}

static bool IsTransparentHugePagesEnabled() {
std::ifstream ifs;

ifs.open("/sys/kernel/mm/transparent_hugepage/enabled");
if (!ifs) {
fprintf(stderr, "Could not open file: " \
"/sys/kernel/mm/transparent_hugepage/enabled\n");
return false;
}

std::string always, madvise, never;
if (ifs.is_open()) {
while (ifs >> always >> madvise >> never) {}
}

int ret_status = false;

if (always.compare("[always]") == 0)
ret_status = true;
else if (madvise.compare("[madvise]") == 0)
ret_status = true;
else if (never.compare("[never]") == 0)
ret_status = false;

ifs.close();
return ret_status;
}

// Moving the text region to large pages. We need to be very careful.
// a) This function itself should not be moved.
// We use a gcc option to put it outside the ".text" section
// b) This function should not call any function(s) that might be moved.
// 1. map a new area and copy the original code there
// 2. mmap using the start address with MAP_FIXED so we get exactly
// the same virtual address
// 3. madvise with MADV_HUGE_PAGE
// 3. If successful copy the code there and unmap the original region
int
__attribute__((__section__(".lpstub")))
__attribute__((__aligned__(2 * 1024 * 1024)))
__attribute__((__noinline__))
__attribute__((__optimize__("2")))
MoveTextRegionToLargePages(struct text_region r) {
void* nmem = nullptr;
void* tmem = nullptr;
int ret = 0;

size_t size = r.to - r.from;
void* start = r.from;

// Allocate temporary region preparing for copy
nmem = mmap(nullptr, size,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (nmem == MAP_FAILED) {
PrintSystemError(errno);
return -1;
}

memcpy(nmem, r.from, size);

// We already know the original page is r-xp
// (PROT_READ, PROT_EXEC, MAP_PRIVATE)
// We want PROT_WRITE because we are writing into it.
// We want it at the fixed address and we use MAP_FIXED.
tmem = mmap(start, size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1 , 0);
if (tmem == MAP_FAILED) {
PrintSystemError(errno);
munmap(nmem, size);
return -1;
}

ret = madvise(tmem, size, MADV_HUGEPAGE);
if (ret == -1) {
PrintSystemError(errno);
ret = munmap(tmem, size);
if (ret == -1) {
PrintSystemError(errno);
}
ret = munmap(nmem, size);
if (ret == -1) {
PrintSystemError(errno);
}

return -1;
}

memcpy(start, nmem, size);
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
if (ret == -1) {
PrintSystemError(errno);
ret = munmap(tmem, size);
if (ret == -1) {
PrintSystemError(errno);
}
ret = munmap(nmem, size);
if (ret == -1) {
PrintSystemError(errno);
}
return -1;
}

// Release the old/temporary mapped region
ret = munmap(nmem, size);
if (ret == -1) {
PrintSystemError(errno);
}

return ret;
}

// This is the primary API called from main
int MapStaticCodeToLargePages() {
struct text_region r = FindNodeTextRegion();
if (r.found_text_region == false) {
fprintf(stderr, "Hugepages WARNING: failed to find text region \n");
return -1;
}

if (r.from > reinterpret_cast<void *> (&MoveTextRegionToLargePages))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, use void* here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Yes fixed.

return MoveTextRegionToLargePages(r);

return -1;
}

bool IsLargePagesEnabled() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: there is an extra space after bool

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Fixed this too.

return IsTransparentHugePagesEnabled();
}

} // namespace node
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I admit I kinda checked out after the first 100 lines. This file has so many style errors, can you fix those first, please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you be specific? I ran the make -s lint and fixed all the ones pointed to by that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linter doesn't catch everything but to name three things:

  1. Inconsistent indentation.
  2. Star leans left in pointers (void* p, not void *p or void * p.)
  3. Casing: IsLargePagesEnabled, not isLargePagesEnabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks I have updated the PR with 2) and 3). I am using standard indentation of 2 spaces.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@suresh-srinivas There’s https://github.com/nodejs/node/blob/master/CPP_STYLE_GUIDE.md that would help a lot with this. Also, comparing with other code in our codebase if you’re unsure. :)

(In particular, we don’t indent code inside namespaces)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@addaleax thanks much. I will look at this and fix styles accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@addaleax @bnoordhuis @gireeshpunathil fixed the style errors. Let me know if you see anything else.

36 changes: 36 additions & 0 deletions src/node_large_page.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (C) 2018 Intel Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom
// the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
// OR OTHER DEALINGS IN THE SOFTWARE.
//
// SPDX-License-Identifier: MIT

#ifndef SRC_NODE_LARGE_PAGE_H_
#define SRC_NODE_LARGE_PAGE_H_

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS


namespace node {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be enclosed in

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks I fixed it.

bool IsLargePagesEnabled();
int MapStaticCodeToLargePages();
} // namespace node

#endif // NODE_WANT_INTERNALS
#endif // SRC_NODE_LARGE_PAGE_H_