Skip to content

Commit

Permalink
src: large pages option: FreeBSD support proposal
Browse files Browse the repository at this point in the history
Enabling on amd64 and as Linux, are 2MB large.
The ELF section linkage script is compatible only with GNU ld.

PR-URL: #28331
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
  • Loading branch information
devnexen authored and Trott committed Jul 16, 2019
1 parent 2a15037 commit a1cb14a
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 9 deletions.
17 changes: 9 additions & 8 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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);
}

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;

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

0 comments on commit a1cb14a

Please sign in to comment.