diff options
-rwxr-xr-x | configure.py | 33 | ||||
-rw-r--r-- | doc/api/cli.md | 17 | ||||
-rw-r--r-- | doc/node.1 | 10 | ||||
-rw-r--r-- | node.gyp | 5 | ||||
-rw-r--r-- | node.gypi | 6 | ||||
-rw-r--r-- | src/large_pages/node_large_page.cc | 19 | ||||
-rw-r--r-- | src/node.cc | 30 | ||||
-rw-r--r-- | src/node_options.cc | 9 | ||||
-rw-r--r-- | src/node_options.h | 1 | ||||
-rw-r--r-- | test/parallel/test-startup-large-pages.js | 29 |
10 files changed, 101 insertions, 58 deletions
diff --git a/configure.py b/configure.py index 95766d405a..de27bb3403 100755 --- a/configure.py +++ b/configure.py @@ -404,17 +404,6 @@ 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. This feature is supported only on Linux kernel' + - '>= 2.6.38 with Transparent Huge pages enabled and FreeBSD') - -parser.add_option('--use-largepages-script-lld', - action='store_true', - dest='node_use_large_pages_script_lld', - help='link against the LLVM ld linker script. Implies -fuse-ld=lld in the linker flags') - intl_optgroup.add_option('--with-intl', action='store', dest='with_intl', @@ -1068,28 +1057,6 @@ def configure_node(o): else: o['variables']['node_use_dtrace'] = 'false' - if options.node_use_large_pages and not flavor in ('linux', 'freebsd', 'mac'): - raise Exception( - 'Large pages are supported only on Linux, FreeBSD and MacOS Systems.') - if options.node_use_large_pages and flavor in ('linux', 'freebsd', 'mac'): - if options.shared or options.enable_static: - raise Exception( - 'Large pages are supported only while creating node executable.') - if target_arch!="x64": - raise Exception( - 'Large pages are supported only x64 platform.') - if flavor == 'mac': - info('macOS server with 32GB or more is recommended') - if flavor == 'linux': - # Example full version string: 2.6.32-696.28.1.el6.x86_64 - FULL_KERNEL_VERSION=os.uname()[2] - KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0] - if KERNEL_VERSION < "2.6.38" and flavor == 'linux': - raise Exception( - 'Large pages need Linux kernel version >= 2.6.38') - o['variables']['node_use_large_pages'] = b(options.node_use_large_pages) - o['variables']['node_use_large_pages_script_lld'] = b(options.node_use_large_pages_script_lld) - if options.no_ifaddrs: o['defines'] += ['SUNOS_NO_IFADDRS'] diff --git a/doc/api/cli.md b/doc/api/cli.md index f3d5df070c..d098a199e0 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -876,6 +876,22 @@ environment variables. See `SSL_CERT_DIR` and `SSL_CERT_FILE`. +### `--use-largepages=mode` +<!-- YAML +added: REPLACEME +--> + +Re-map the Node.js static code to large memory pages at startup. If supported on +the target system, this will cause the Node.js static code to be moved onto 2 +MiB pages instead of 4 KiB pages. + +The following values are valid for `mode`: +* `off`: No mapping will be attempted. This is the default. +* `on`: If supported by the OS, mapping will be attempted. Failure to map will + be ignored and a message will be printed to standard error. +* `silent`: If supported by the OS, mapping will be attempted. Failure to map + will be ignored and will not be reported. + ### `--v8-options` <!-- YAML added: v0.1.3 @@ -1133,6 +1149,7 @@ Node.js options that are allowed are: * `--track-heap-objects` * `--unhandled-rejections` * `--use-bundled-ca` +* `--use-largepages` * `--use-openssl-ca` * `--v8-pool-size` * `--zero-fill-buffers` diff --git a/doc/node.1 b/doc/node.1 index c783195c95..4c63477889 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -402,6 +402,16 @@ See and .Ev SSL_CERT_FILE . . +.It Fl -use-largepages Ns = Ns Ar mode +Re-map the Node.js static code to large memory pages at startup. If supported on +the target system, this will cause the Node.js static code to be moved onto 2 +MiB pages instead of 4 KiB pages. +.Pp +.Ar mode +must have one of the following values: +`off` (the default value, meaning do not map), `on` (map and ignore failure, +reporting it to stderr), or `silent` (map and silently ignore failure). +. .It Fl -v8-options Print V8 command-line options. . @@ -836,10 +836,9 @@ }], ], }], - [ 'node_use_large_pages=="true" and OS in "linux freebsd mac"', { + [ 'OS in "linux freebsd mac" and ' + 'target_arch=="x64"', { 'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ], - # The current implementation of Large Pages is under Linux. - # Other implementations are possible but not currently supported. 'sources': [ 'src/large_pages/node_large_page.cc', 'src/large_pages/node_large_page.h' @@ -308,8 +308,7 @@ }], [ 'OS=="linux" and ' 'target_arch=="x64" and ' - 'node_use_large_pages=="true" and ' - 'node_use_large_pages_script_lld=="false"', { + 'llvm_version=="0.0"', { 'ldflags': [ '-Wl,-T', '<!(realpath src/large_pages/ld.implicit.script)', @@ -317,8 +316,7 @@ }], [ 'OS=="linux" and ' 'target_arch=="x64" and ' - 'node_use_large_pages=="true" and ' - 'node_use_large_pages_script_lld=="true"', { + 'llvm_version!="0.0"', { 'ldflags': [ '-Wl,-T', '<!(realpath src/large_pages/ld.implicit.script.lld)', diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index 68fa178b40..3897b819e8 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -62,7 +62,7 @@ // 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 +// Use madvise with MADV_HUGEPAGE to use Anonymous 2M Pages // If successful copy the code there and unmap the original region. extern char __nodetext; @@ -308,7 +308,7 @@ static bool IsSuperPagesEnabled() { // a. map a new area and copy the original code there // b. mmap using the start address with MAP_FIXED so we get exactly // the same virtual address (except on macOS). -// c. madvise with MADV_HUGE_PAGE +// c. madvise with MADV_HUGEPAGE // d. If successful copy the code there and unmap the original region int #if !defined(__APPLE__) @@ -333,9 +333,6 @@ MoveTextRegionToLargePages(const text_region& r) { PrintSystemError(errno); return -1; } - auto munmap_on_return = OnScopeLeave([nmem, size]() { - if (-1 == munmap(nmem, size)) PrintSystemError(errno); - }); memcpy(nmem, r.from, size); @@ -352,13 +349,14 @@ MoveTextRegionToLargePages(const text_region& r) { return -1; } - ret = madvise(tmem, size, MADV_HUGEPAGE); + ret = madvise(tmem, size, 14 /* MADV_HUGEPAGE */); if (ret == -1) { PrintSystemError(errno); ret = munmap(tmem, size); if (ret == -1) { PrintSystemError(errno); } + if (-1 == munmap(nmem, size)) PrintSystemError(errno); return -1; } memcpy(start, nmem, size); @@ -369,6 +367,7 @@ MoveTextRegionToLargePages(const text_region& r) { MAP_ALIGNED_SUPER, -1 , 0); if (tmem == MAP_FAILED) { PrintSystemError(errno); + if (-1 == munmap(nmem, size)) PrintSystemError(errno); return -1; } #elif defined(__APPLE__) @@ -383,6 +382,7 @@ MoveTextRegionToLargePages(const text_region& r) { VM_FLAGS_SUPERPAGE_SIZE_2MB, 0); if (tmem == MAP_FAILED) { PrintSystemError(errno); + if (-1 == munmap(nmem, size)) PrintSystemError(errno); return -1; } memcpy(tmem, nmem, size); @@ -393,6 +393,7 @@ MoveTextRegionToLargePages(const text_region& r) { if (ret == -1) { PrintSystemError(errno); } + if (-1 == munmap(nmem, size)) PrintSystemError(errno); return -1; } memcpy(start, tmem, size); @@ -405,8 +406,10 @@ MoveTextRegionToLargePages(const text_region& r) { if (ret == -1) { PrintSystemError(errno); } + if (-1 == munmap(nmem, size)) PrintSystemError(errno); return -1; } + if (-1 == munmap(nmem, size)) PrintSystemError(errno); return ret; } @@ -418,12 +421,12 @@ int MapStaticCodeToLargePages() { return -1; } -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages)) return MoveTextRegionToLargePages(r); return -1; -#elif defined(__FreeBSD__) || defined(__APPLE__) +#elif defined(__APPLE__) return MoveTextRegionToLargePages(r); #endif } diff --git a/src/node.cc b/src/node.cc index f5ae0159d2..9599b3625a 100644 --- a/src/node.cc +++ b/src/node.cc @@ -64,9 +64,7 @@ #include "inspector/worker_inspector.h" // ParentInspectorHandle #endif -#ifdef NODE_ENABLE_LARGE_CODE_PAGES #include "large_pages/node_large_page.h" -#endif #ifdef NODE_REPORT #include "node_report.h" @@ -1000,14 +998,6 @@ InitializationResult InitializeOncePerProcess(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 - // Hack around with the argv pointer. Used for process.title = "blah". argv = uv_setup_args(argc, argv); @@ -1027,6 +1017,26 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) { } } +#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES + if (per_process::cli_options->use_largepages == "on" || + per_process::cli_options->use_largepages == "silent") { + if (node::IsLargePagesEnabled()) { + if (node::MapStaticCodeToLargePages() != 0 && + per_process::cli_options->use_largepages != "silent") { + fprintf(stderr, + "Mapping code to large pages failed. Reverting to default page " + "size.\n"); + } + } else if (per_process::cli_options->use_largepages != "silent") { + fprintf(stderr, "Large pages are not enabled.\n"); + } + } +#else + if (per_process::cli_options->use_largepages == "on") { + fprintf(stderr, "Mapping to large pages is not supported.\n"); + } +#endif // NODE_ENABLE_LARGE_CODE_PAGES + if (per_process::cli_options->print_version) { printf("%s\n", NODE_VERSION); result.exit_code = 0; diff --git a/src/node_options.cc b/src/node_options.cc index 622287d524..3a38017e52 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -63,6 +63,11 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors) { "used, not both"); } #endif + if (use_largepages != "off" && + use_largepages != "on" && + use_largepages != "silent") { + errors->push_back("invalid value for --use-largepages"); + } per_isolate->CheckOptions(errors); } @@ -752,6 +757,10 @@ PerProcessOptionsParser::PerProcessOptionsParser( kAllowedInEnvironment); #endif #endif + AddOption("--use-largepages", + "Map the Node.js static code to large pages", + &PerProcessOptions::use_largepages, + kAllowedInEnvironment); Insert(iop, &PerProcessOptions::get_per_isolate_options); } diff --git a/src/node_options.h b/src/node_options.h index 633d572536..35f0b032d0 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -237,6 +237,7 @@ class PerProcessOptions : public Options { bool force_fips_crypto = false; #endif #endif + std::string use_largepages = "off"; #ifdef NODE_REPORT std::vector<std::string> cmdline; diff --git a/test/parallel/test-startup-large-pages.js b/test/parallel/test-startup-large-pages.js new file mode 100644 index 0000000000..d5589ee547 --- /dev/null +++ b/test/parallel/test-startup-large-pages.js @@ -0,0 +1,29 @@ +'use strict'; + +// Make sure that Node.js runs correctly with the --use-largepages option. + +require('../common'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +{ + const child = spawnSync(process.execPath, + [ '--use-largepages=on', '-p', '42' ]); + const stdout = child.stdout.toString().match(/\S+/g); + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(stdout.length, 1); + assert.strictEqual(stdout[0], '42'); +} + +{ + const child = spawnSync(process.execPath, + [ '--use-largepages=xyzzy', '-p', '42' ]); + assert.strictEqual(child.status, 9); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stderr.toString().match(/\S+/g).slice(1).join(' '), + 'invalid value for --use-largepages'); +} + +// TODO(gabrielschulhof): Make assertions about the stderr, which may or may not +// contain a message indicating that mapping to large pages has failed. |