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 <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
2a15037744
commit
a1cb14a4dc
17
configure.py
17
configure.py
@ -1039,22 +1039,23 @@ def configure_node(o):
|
|||||||
else:
|
else:
|
||||||
o['variables']['node_use_dtrace'] = 'false'
|
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(
|
raise Exception(
|
||||||
'Large pages are supported only on Linux Systems.')
|
'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:
|
if options.shared or options.enable_static:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Large pages are supported only while creating node executable.')
|
'Large pages are supported only while creating node executable.')
|
||||||
if target_arch!="x64":
|
if target_arch!="x64":
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Large pages are supported only x64 platform.')
|
'Large pages are supported only x64 platform.')
|
||||||
# Example full version string: 2.6.32-696.28.1.el6.x86_64
|
if flavor == 'linux':
|
||||||
FULL_KERNEL_VERSION=os.uname()[2]
|
# Example full version string: 2.6.32-696.28.1.el6.x86_64
|
||||||
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
|
FULL_KERNEL_VERSION=os.uname()[2]
|
||||||
if KERNEL_VERSION < "2.6.38":
|
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
|
||||||
raise Exception(
|
if KERNEL_VERSION < "2.6.38" and flavor == 'linux':
|
||||||
'Large pages need Linux kernel version >= 2.6.38')
|
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'] = b(options.node_use_large_pages)
|
||||||
|
|
||||||
if options.no_ifaddrs:
|
if options.no_ifaddrs:
|
||||||
|
2
node.gyp
2
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' ],
|
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
|
||||||
# The current implementation of Large Pages is under Linux.
|
# The current implementation of Large Pages is under Linux.
|
||||||
# Other implementations are possible but not currently supported.
|
# Other implementations are possible but not currently supported.
|
||||||
|
@ -21,10 +21,16 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "node_large_page.h"
|
#include "node_large_page.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "uv.h"
|
||||||
|
|
||||||
#include <fcntl.h> // _O_RDWR
|
#include <fcntl.h> // _O_RDWR
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#if defined(__FreeBSD__)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <sys/user.h>
|
||||||
|
#endif
|
||||||
#include <unistd.h> // readlink
|
#include <unistd.h> // readlink
|
||||||
|
|
||||||
#include <cerrno> // NOLINT(build/include)
|
#include <cerrno> // NOLINT(build/include)
|
||||||
@ -39,6 +45,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// The functions in this file map the text segment of node into 2M pages.
|
// The functions in this file map the text segment of node into 2M pages.
|
||||||
// The algorithm is simple
|
// 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
|
// This is also handling the case where the first line is not the binary
|
||||||
|
|
||||||
static struct text_region FindNodeTextRegion() {
|
static struct text_region FindNodeTextRegion() {
|
||||||
|
#if defined(__linux__)
|
||||||
std::ifstream ifs;
|
std::ifstream ifs;
|
||||||
std::string map_line;
|
std::string map_line;
|
||||||
std::string permission;
|
std::string permission;
|
||||||
@ -141,9 +149,68 @@ static struct text_region FindNodeTextRegion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ifs.close();
|
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;
|
return nregion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
static bool IsTransparentHugePagesEnabled() {
|
static bool IsTransparentHugePagesEnabled() {
|
||||||
std::ifstream ifs;
|
std::ifstream ifs;
|
||||||
|
|
||||||
@ -171,6 +238,19 @@ static bool IsTransparentHugePagesEnabled() {
|
|||||||
ifs.close();
|
ifs.close();
|
||||||
return ret_status;
|
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.
|
// Moving the text region to large pages. We need to be very careful.
|
||||||
// 1: This function itself should not be moved.
|
// 1: This function itself should not be moved.
|
||||||
@ -206,6 +286,7 @@ MoveTextRegionToLargePages(const text_region& r) {
|
|||||||
|
|
||||||
memcpy(nmem, r.from, size);
|
memcpy(nmem, r.from, size);
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
// We already know the original page is r-xp
|
// We already know the original page is r-xp
|
||||||
// (PROT_READ, PROT_EXEC, MAP_PRIVATE)
|
// (PROT_READ, PROT_EXEC, MAP_PRIVATE)
|
||||||
// We want PROT_WRITE because we are writing into it.
|
// We want PROT_WRITE because we are writing into it.
|
||||||
@ -233,6 +314,17 @@ MoveTextRegionToLargePages(const text_region& r) {
|
|||||||
|
|
||||||
return -1;
|
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);
|
memcpy(start, nmem, size);
|
||||||
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
|
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
|
||||||
@ -266,14 +358,22 @@ int MapStaticCodeToLargePages() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
|
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
|
||||||
return MoveTextRegionToLargePages(r);
|
return MoveTextRegionToLargePages(r);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
return MoveTextRegionToLargePages(r);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsLargePagesEnabled() {
|
bool IsLargePagesEnabled() {
|
||||||
|
#if defined(__linux__)
|
||||||
return IsTransparentHugePagesEnabled();
|
return IsTransparentHugePagesEnabled();
|
||||||
|
#else
|
||||||
|
return IsSuperPagesEnabled();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
Loading…
x
Reference in New Issue
Block a user