From 6b9dd7bb070129123d25d3f30057fd7c44ff40a8 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 20 Jun 2019 22:23:11 +0100 Subject: [PATCH 01/18] src: large pages option: FreeBSD support proposal Enabling on amd64 and as Linux, are 2MB large. The ELF section linkage script is compatible only with GNU ld. PR-URL: https://github.com/nodejs/node/pull/28331 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: Rich Trott --- configure.py | 17 ++-- node.gyp | 2 +- src/large_pages/node_large_page.cc | 121 ++++++++++++++++++++++++++--- 3 files changed, 122 insertions(+), 18 deletions(-) diff --git a/configure.py b/configure.py index 22861a10eeac54..15a3cf176e08b3 100755 --- a/configure.py +++ b/configure.py @@ -1025,22 +1025,23 @@ def configure_node(o): else: o['variables']['node_use_dtrace'] = 'false' - if options.node_use_large_pages and flavor != 'linux': + if options.node_use_large_pages and not flavor in ('linux', 'freebsd'): raise Exception( 'Large pages are supported only on Linux Systems.') - if options.node_use_large_pages and flavor == 'linux': + if options.node_use_large_pages and flavor in ('linux', 'freebsd'): 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.') - # 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": - raise Exception( - 'Large pages need Linux kernel version >= 2.6.38') + 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) if options.no_ifaddrs: diff --git a/node.gyp b/node.gyp index dd60ea4a937691..1676935214b254 100644 --- a/node.gyp +++ b/node.gyp @@ -606,7 +606,7 @@ 'src/tls_wrap.h' ], }], - [ 'node_use_large_pages=="true" and OS=="linux"', { + [ 'node_use_large_pages=="true" and OS in "linux freebsd"', { 'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ], # The current implementation of Large Pages is under Linux. # Other implementations are possible but not currently supported. diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index 4b860d9ccb47ca..79cc013deef402 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -20,22 +20,32 @@ // // SPDX-License-Identifier: MIT -#include +#include "node_large_page.h" +#include "util.h" +#include "uv.h" + #include // _O_RDWR -#include // PATH_MAX -#include -#include -#include -#include -#include -#include #include #include +#if defined(__FreeBSD__) +#include +#include +#endif +#include // readlink + +#include // NOLINT(build/include) +#include // PATH_MAX +#include +#include +#include +#include +#include +#include #include #include #include #include -#include // readlink +#include // The functions in this file map the text segment of node into 2M pages. // The algorithm is simple @@ -85,6 +95,7 @@ inline int64_t hugepage_align_down(int64_t addr) { // This is also handling the case where the first line is not the binary static struct text_region FindNodeTextRegion() { +#if defined(__linux__) std::ifstream ifs; std::string map_line; std::string permission; @@ -138,9 +149,68 @@ static struct text_region FindNodeTextRegion() { } ifs.close(); +#elif defined(__FreeBSD__) + struct text_region nregion; + nregion.found_text_region = false; + + std::string exename; + { + char selfexe[PATH_MAX]; + size_t count = sizeof(selfexe); + if (uv_exepath(selfexe, &count)) + return nregion; + + exename = std::string(selfexe, count); + } + + size_t numpg; + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; + const size_t miblen = arraysize(mib); + if (sysctl(mib, miblen, nullptr, &numpg, nullptr, 0) == -1) { + return nregion; + } + + // for struct kinfo_vmentry + numpg = numpg * 4 / 3; + auto alg = std::vector(numpg); + + if (sysctl(mib, miblen, alg.data(), &numpg, nullptr, 0) == -1) { + return nregion; + } + + char* start = alg.data(); + char* end = start + numpg; + + while (start < end) { + kinfo_vmentry* entry = reinterpret_cast(start); + const size_t cursz = entry->kve_structsize; + if (cursz == 0) { + break; + } + + if (entry->kve_path[0] == '\0') { + continue; + } + bool excmapping = ((entry->kve_protection & KVME_PROT_READ) && + (entry->kve_protection & KVME_PROT_EXEC)); + + if (!strcmp(exename.c_str(), entry->kve_path) && excmapping) { + size_t size = entry->kve_end - entry->kve_start; + nregion.found_text_region = true; + nregion.from = + reinterpret_cast(hugepage_align_up(entry->kve_start)); + nregion.to = + reinterpret_cast(hugepage_align_down(entry->kve_end)); + nregion.total_hugepages = size / hps; + break; + } + start += cursz; + } +#endif return nregion; } +#if defined(__linux__) static bool IsTransparentHugePagesEnabled() { std::ifstream ifs; @@ -168,6 +238,19 @@ static bool IsTransparentHugePagesEnabled() { ifs.close(); return ret_status; } +#elif defined(__FreeBSD__) +static bool IsSuperPagesEnabled() { + // It is enabled by default on amd64 + unsigned int super_pages = 0; + size_t super_pages_length = sizeof(super_pages); + if (sysctlbyname("vm.pmap.pg_ps_enabled", &super_pages, + &super_pages_length, nullptr, 0) == -1 || + super_pages < 1) { + return false; + } + return true; +} +#endif // Moving the text region to large pages. We need to be very careful. // 1: This function itself should not be moved. @@ -203,6 +286,7 @@ MoveTextRegionToLargePages(const text_region& r) { memcpy(nmem, r.from, size); +#if defined(__linux__) // 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. @@ -230,6 +314,17 @@ MoveTextRegionToLargePages(const text_region& r) { return -1; } +#elif defined(__FreeBSD__) + tmem = mmap(start, size, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | + MAP_ALIGNED_SUPER, -1 , 0); + if (tmem == MAP_FAILED) { + PrintSystemError(errno); + munmap(nmem, size); + return -1; + } +#endif memcpy(start, nmem, size); ret = mprotect(start, size, PROT_READ | PROT_EXEC); @@ -263,14 +358,22 @@ int MapStaticCodeToLargePages() { return -1; } +#if defined(__linux__) if (r.from > reinterpret_cast(&MoveTextRegionToLargePages)) return MoveTextRegionToLargePages(r); return -1; +#elif defined(__FreeBSD__) + return MoveTextRegionToLargePages(r); +#endif } bool IsLargePagesEnabled() { +#if defined(__linux__) return IsTransparentHugePagesEnabled(); +#else + return IsSuperPagesEnabled(); +#endif } } // namespace node From 6d93a8a0c2c887bf311b0346d57ef99191e44d92 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 21 Jun 2019 11:16:05 +0200 Subject: [PATCH 02/18] src: readlink("/proc/self/exe") -> uv_exename() This commit also adds error handling. A THP-enabled build terminated with an out-of-memory error on a system without /proc because it cast the -1 from readlink() to size_t (i.e. ULONG_MAX) and then tried to allocate a string of that size. PR-URL: https://github.com/nodejs/node/pull/28333 Reviewed-By: Rich Trott Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Anna Henningsen --- src/large_pages/node_large_page.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index 79cc013deef402..e6829708aa0803 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -115,8 +115,12 @@ static struct text_region FindNodeTextRegion() { std::string exename; { char selfexe[PATH_MAX]; - ssize_t count = readlink("/proc/self/exe", selfexe, PATH_MAX); - exename = std::string(selfexe, count); + + size_t size = sizeof(selfexe); + if (uv_exepath(selfexe, &size)) + return nregion; + + exename = std::string(selfexe, size); } while (std::getline(ifs, map_line)) { From bbaa065a25e71df574ac8c2b86809370aebff399 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 17 Jul 2019 19:26:14 +0100 Subject: [PATCH 03/18] src: large pages fix FreeBSD fix region size Makes the size aligned to huge page size by calculating it from the aligned lower and upper boundary of the executable address. PR-URL: https://github.com/nodejs/node/pull/28735 Reviewed-By: Rod Vagg Reviewed-By: Ben Noordhuis Reviewed-By: Rich Trott --- src/large_pages/node_large_page.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index e6829708aa0803..d87ccfaa26abcd 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -199,12 +199,14 @@ static struct text_region FindNodeTextRegion() { (entry->kve_protection & KVME_PROT_EXEC)); if (!strcmp(exename.c_str(), entry->kve_path) && excmapping) { - size_t size = entry->kve_end - entry->kve_start; + char* estart = + reinterpret_cast(hugepage_align_up(entry->kve_start)); + char* eend = + reinterpret_cast(hugepage_align_down(entry->kve_end)); + size_t size = eend - estart; nregion.found_text_region = true; - nregion.from = - reinterpret_cast(hugepage_align_up(entry->kve_start)); - nregion.to = - reinterpret_cast(hugepage_align_down(entry->kve_end)); + nregion.from = estart; + nregion.to = eend; nregion.total_hugepages = size / hps; break; } From 86419d831b464a9c040b44744e7d4574f7fd68dc Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 2 Aug 2019 11:57:02 +0100 Subject: [PATCH 04/18] build: enable linux large pages LLVM lld linkage support The custom linker script is compatible with GNU ld only. As such, providin a new expliciting option to redirect to a different one. lld seems unable to migrate this large section w/o segfaulting so providing only the base address anchor for now. PR-URL: https://github.com/nodejs/node/pull/28938 Reviewed-By: Ben Noordhuis --- configure.py | 6 ++++++ node.gypi | 14 +++++++++++++- src/large_pages/ld.implicit.script.lld | 3 +++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/large_pages/ld.implicit.script.lld diff --git a/configure.py b/configure.py index 15a3cf176e08b3..4867e4d66ff5a8 100755 --- a/configure.py +++ b/configure.py @@ -398,6 +398,11 @@ help='build with Large Pages support. This feature is supported only on Linux kernel' + '>= 2.6.38 with Transparent Huge pages enabled') +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', @@ -1043,6 +1048,7 @@ def configure_node(o): 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/node.gypi b/node.gypi index 466a1746811cfa..d9285a966968b7 100644 --- a/node.gypi +++ b/node.gypi @@ -304,12 +304,24 @@ 'ldflags': [ '-Wl,-z,relro', '-Wl,-z,now' ] }], - [ 'OS=="linux" and target_arch=="x64" and node_use_large_pages=="true"', { + [ 'OS=="linux" and ' + 'target_arch=="x64" and ' + 'node_use_large_pages=="true" and ' + 'node_use_large_pages_script_lld=="false"', { 'ldflags': [ '-Wl,-T', ' Date: Mon, 5 Aug 2019 14:00:15 +0100 Subject: [PATCH 05/18] src: add large page support for macOS Proposal to bring the support for this platform. We assume the pse36 cpu flag is present for 2MB large page support present in recent years in mac line (not to be backported to 10.x anyway). Recommended better for mac production servers rather than casual mac books. PR-URL: https://github.com/nodejs/node/pull/28977 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell --- configure.py | 8 ++- node.gyp | 2 +- src/large_pages/node_large_page.cc | 105 +++++++++++++++++++++++------ 3 files changed, 90 insertions(+), 25 deletions(-) diff --git a/configure.py b/configure.py index 4867e4d66ff5a8..e45606a0ad030a 100755 --- a/configure.py +++ b/configure.py @@ -1030,16 +1030,18 @@ def configure_node(o): else: o['variables']['node_use_dtrace'] = 'false' - if options.node_use_large_pages and not flavor in ('linux', 'freebsd'): + if options.node_use_large_pages and not flavor in ('linux', 'freebsd', 'mac'): raise Exception( - 'Large pages are supported only on Linux Systems.') - if options.node_use_large_pages and flavor in ('linux', 'freebsd'): + '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] diff --git a/node.gyp b/node.gyp index 1676935214b254..d3e766ebaa3964 100644 --- a/node.gyp +++ b/node.gyp @@ -606,7 +606,7 @@ 'src/tls_wrap.h' ], }], - [ 'node_use_large_pages=="true" and OS in "linux freebsd"', { + [ 'node_use_large_pages=="true" and OS in "linux freebsd mac"', { 'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ], # The current implementation of Large Pages is under Linux. # Other implementations are possible but not currently supported. diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index d87ccfaa26abcd..1e75f2af44fbf2 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -30,6 +30,8 @@ #if defined(__FreeBSD__) #include #include +#elif defined(__APPLE__) +#include #endif #include // readlink @@ -47,6 +49,13 @@ #include #include +// Define MAP_ANONYMOUS as an alias for MAP_ANON to support OSX 10.10. +#if defined(__APPLE__) +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif +#endif + // The functions in this file map the text segment of node into 2M pages. // The algorithm is simple // Find the text region of node binary in memory @@ -212,6 +221,42 @@ static struct text_region FindNodeTextRegion() { } start += cursz; } +#elif defined(__APPLE__) + struct text_region nregion; + nregion.found_text_region = false; + struct vm_region_submap_info_64 map; + mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; + vm_address_t addr = 0UL; + vm_size_t size = 0; + natural_t depth = 1; + + while (true) { + if (vm_region_recurse_64(mach_task_self(), &addr, &size, &depth, + reinterpret_cast(&map), + &count) != KERN_SUCCESS) { + break; + } + + if (map.is_submap) { + depth++; + } else { + char* start = reinterpret_cast(hugepage_align_up(addr)); + char* end = reinterpret_cast(hugepage_align_down(addr+size)); + size_t esize = end - start; + + if (end > start && (map.protection & VM_PROT_READ) != 0 && + (map.protection & VM_PROT_EXECUTE) != 0) { + nregion.found_text_region = true; + nregion.from = start; + nregion.to = end; + nregion.total_hugepages = esize / hps; + break; + } + + addr += size; + size = 0; + } + } #endif return nregion; } @@ -267,11 +312,15 @@ static bool IsSuperPagesEnabled() { // 2: This function should not call any function(s) that might be moved. // 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 +// the same virtual address (except on macOS). // c. madvise with MADV_HUGE_PAGE // d. If successful copy the code there and unmap the original region int +#if !defined(__APPLE__) __attribute__((__section__(".lpstub"))) +#else +__attribute__((__section__("__TEXT,__lpstub"))) +#endif __attribute__((__aligned__(hps))) __attribute__((__noinline__)) MoveTextRegionToLargePages(const text_region& r) { @@ -289,6 +338,9 @@ MoveTextRegionToLargePages(const text_region& r) { PrintSystemError(errno); return -1; } + OnScopeLeave munmap_on_return([nmem, size]() { + if (-1 == munmap(nmem, size)) PrintSystemError(errno); + }); memcpy(nmem, r.from, size); @@ -302,7 +354,6 @@ MoveTextRegionToLargePages(const text_region& r) { MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1 , 0); if (tmem == MAP_FAILED) { PrintSystemError(errno); - munmap(nmem, size); return -1; } @@ -313,11 +364,6 @@ MoveTextRegionToLargePages(const text_region& r) { if (ret == -1) { PrintSystemError(errno); } - ret = munmap(nmem, size); - if (ret == -1) { - PrintSystemError(errno); - } - return -1; } #elif defined(__FreeBSD__) @@ -327,32 +373,46 @@ MoveTextRegionToLargePages(const text_region& r) { MAP_ALIGNED_SUPER, -1 , 0); if (tmem == MAP_FAILED) { PrintSystemError(errno); - munmap(nmem, size); return -1; } -#endif - - memcpy(start, nmem, size); - ret = mprotect(start, size, PROT_READ | PROT_EXEC); +#elif defined(__APPLE__) + // There is not enough room to reserve the mapping close + // to the region address so we content to give a hint + // without forcing the new address being closed to. + // We explicitally gives all permission since we plan + // to write into it. + tmem = mmap(start, size, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, + VM_FLAGS_SUPERPAGE_SIZE_2MB, 0); + if (tmem == MAP_FAILED) { + PrintSystemError(errno); + return -1; + } + memcpy(tmem, nmem, size); + ret = mprotect(start, size, PROT_READ | PROT_WRITE | 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; } + memcpy(start, tmem, size); +#else + memcpy(start, nmem, size); +#endif - // Release the old/temporary mapped region - ret = munmap(nmem, size); + ret = mprotect(start, size, PROT_READ | PROT_EXEC); if (ret == -1) { PrintSystemError(errno); + ret = munmap(tmem, size); + if (ret == -1) { + PrintSystemError(errno); + } + return -1; } - return ret; } @@ -369,7 +429,7 @@ int MapStaticCodeToLargePages() { return MoveTextRegionToLargePages(r); return -1; -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__APPLE__) return MoveTextRegionToLargePages(r); #endif } @@ -377,8 +437,11 @@ int MapStaticCodeToLargePages() { bool IsLargePagesEnabled() { #if defined(__linux__) return IsTransparentHugePagesEnabled(); -#else +#elif defined(__FreeBSD__) return IsSuperPagesEnabled(); +#elif defined(__APPLE__) + // pse-36 flag is present in recent mac x64 products. + return true; #endif } From 2b373bd270567dfd6a0ac08e627c54cdf2081f45 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Wed, 9 Oct 2019 15:50:21 -0700 Subject: [PATCH 06/18] src: fix largepages regression During the addition of macOS support for large pages, a `memcpy` ended up under the wrong preprocessor directive. As a result, the newly allocated large pages were not initialized with a copy of the text section. Thanks to Suresh Srinivas ! PR-URL: https://github.com/nodejs/node/pull/29914 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: David Carlier --- src/large_pages/node_large_page.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index 1e75f2af44fbf2..61d89b009ac5da 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -366,6 +366,7 @@ MoveTextRegionToLargePages(const text_region& r) { } return -1; } + memcpy(start, nmem, size); #elif defined(__FreeBSD__) tmem = mmap(start, size, PROT_READ | PROT_WRITE | PROT_EXEC, @@ -400,8 +401,6 @@ MoveTextRegionToLargePages(const text_region& r) { return -1; } memcpy(start, tmem, size); -#else - memcpy(start, nmem, size); #endif ret = mprotect(start, size, PROT_READ | PROT_EXEC); From d5b5b227bdaaab191648e5769ea85785a81dda11 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Wed, 9 Oct 2019 13:27:26 -0700 Subject: [PATCH 07/18] src: refine maps parsing for large pages Multiple sections may be marked as "r-xp" and with the executable's path. We use the location of the `__nodetext` symbol added by the linker script to ensure that the range we retrieve from the maps file does indeed contain the Node.js text section. Thanks to Suresh Srinivas ! PR-URL: https://github.com/nodejs/node/pull/29973 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: David Carlier --- src/large_pages/node_large_page.cc | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index 61d89b009ac5da..7e0fb5fcef47ef 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -90,11 +90,11 @@ static void PrintSystemError(int error) { return; } -inline int64_t hugepage_align_up(int64_t addr) { +inline uintptr_t hugepage_align_up(uintptr_t addr) { return (((addr) + (hps) - 1) & ~((hps) - 1)); } -inline int64_t hugepage_align_down(int64_t addr) { +inline uintptr_t hugepage_align_down(uintptr_t addr) { return ((addr) & ~((hps) - 1)); } @@ -110,7 +110,7 @@ static struct text_region FindNodeTextRegion() { std::string permission; std::string dev; char dash; - int64_t start, end, offset, inode; + uintptr_t start, end, offset, inode; struct text_region nregion; nregion.found_text_region = false; @@ -145,18 +145,20 @@ static struct text_region FindNodeTextRegion() { std::string pathname; iss >> pathname; if (pathname == exename && permission == "r-xp") { - start = reinterpret_cast(&__nodetext); - char* from = reinterpret_cast(hugepage_align_up(start)); - char* to = reinterpret_cast(hugepage_align_down(end)); - - if (from < to) { - size_t size = to - from; - nregion.found_text_region = true; - nregion.from = from; - nregion.to = to; - nregion.total_hugepages = size / hps; + uintptr_t ntext = reinterpret_cast(&__nodetext); + if (ntext >= start && ntext < end) { + char* from = reinterpret_cast(hugepage_align_up(ntext)); + char* to = reinterpret_cast(hugepage_align_down(end)); + + if (from < to) { + size_t size = to - from; + nregion.found_text_region = true; + nregion.from = from; + nregion.to = to; + nregion.total_hugepages = size / hps; + } + break; } - break; } } } From b4d01ba466e9118481144048e2fc09cab2111323 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Fri, 13 Dec 2019 11:59:37 -0800 Subject: [PATCH 08/18] src: make --use-largepages a runtime option Moves the option that instructs Node.js to-remap its static code to large pages from a configure-time option to a runtime option. This should make it easy to assess the performance impact of such a change without having to custom-build. PR-URL: https://github.com/nodejs/node/pull/30954 Reviewed-By: Ben Noordhuis Reviewed-By: David Carlier Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Denys Otrishko Co-authored-by: David Carlier --- configure.py | 33 ----------------------- doc/api/cli.md | 17 ++++++++++++ doc/node.1 | 10 +++++++ node.gyp | 5 ++-- node.gypi | 6 ++--- src/large_pages/node_large_page.cc | 20 ++++++++------ src/node.cc | 30 ++++++++++++++------- src/node_options.cc | 9 +++++++ src/node_options.h | 1 + test/parallel/test-startup-large-pages.js | 29 ++++++++++++++++++++ 10 files changed, 102 insertions(+), 58 deletions(-) create mode 100644 test/parallel/test-startup-large-pages.js diff --git a/configure.py b/configure.py index e45606a0ad030a..5ac4122bdb54cc 100755 --- a/configure.py +++ b/configure.py @@ -392,17 +392,6 @@ 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') - -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', @@ -1030,28 +1019,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 57e2f8e8f0fb46..7eb43c648e2167 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -480,6 +480,22 @@ environment variables. See `SSL_CERT_DIR` and `SSL_CERT_FILE`. +### `--use-largepages=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. + +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`