From a1cb14a4dc69ac2ae9eef0c4620681fada51d9f3 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 20 Jun 2019 22:23:11 +0100 Subject: 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 | 100 +++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/configure.py b/configure.py index 2376095719..44d2eb2539 100755 --- a/configure.py +++ b/configure.py @@ -1039,22 +1039,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 3f37cf45d6..ed9de85f99 100644 --- a/node.gyp +++ b/node.gyp @@ -811,7 +811,7 @@ }], ], }], - [ '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 333a119ee4..79cc013dee 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -21,10 +21,16 @@ // SPDX-License-Identifier: MIT #include "node_large_page.h" +#include "util.h" +#include "uv.h" #include // _O_RDWR #include #include +#if defined(__FreeBSD__) +#include +#include +#endif #include // readlink #include // NOLINT(build/include) @@ -39,6 +45,7 @@ #include #include #include +#include // The functions in this file map the text segment of node into 2M pages. // The algorithm is simple @@ -88,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; @@ -141,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; @@ -171,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. @@ -206,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. @@ -233,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); @@ -266,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 -- cgit v1.2.3