summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure.py17
-rw-r--r--node.gyp2
-rw-r--r--src/large_pages/node_large_page.cc100
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 <fcntl.h> // _O_RDWR
#include <sys/types.h>
#include <sys/mman.h>
+#if defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#endif
#include <unistd.h> // readlink
#include <cerrno> // NOLINT(build/include)
@@ -39,6 +45,7 @@
#include <fstream>
#include <iostream>
#include <sstream>
+#include <vector>
// 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<char>(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<kinfo_vmentry*>(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<char*>(hugepage_align_up(entry->kve_start));
+ nregion.to =
+ reinterpret_cast<char*>(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<void*>(&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