Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: large pages option: FreeBSD support proposal #28331

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1055,22 +1055,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:
Expand Down
2 changes: 1 addition & 1 deletion node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,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.
Expand Down
100 changes: 100 additions & 0 deletions src/large_pages/node_large_page.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
bnoordhuis marked this conversation as resolved.
Show resolved Hide resolved

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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take your word for it that that is big enough. ^^

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;

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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