Skip to content

Commit 6b9dd7b

Browse files
devnexenGabriel Schulhof
authored andcommitted
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: nodejs#28331 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 47046aa commit 6b9dd7b

File tree

3 files changed

+122
-18
lines changed

3 files changed

+122
-18
lines changed

configure.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,22 +1025,23 @@ def configure_node(o):
10251025
else:
10261026
o['variables']['node_use_dtrace'] = 'false'
10271027

1028-
if options.node_use_large_pages and flavor != 'linux':
1028+
if options.node_use_large_pages and not flavor in ('linux', 'freebsd'):
10291029
raise Exception(
10301030
'Large pages are supported only on Linux Systems.')
1031-
if options.node_use_large_pages and flavor == 'linux':
1031+
if options.node_use_large_pages and flavor in ('linux', 'freebsd'):
10321032
if options.shared or options.enable_static:
10331033
raise Exception(
10341034
'Large pages are supported only while creating node executable.')
10351035
if target_arch!="x64":
10361036
raise Exception(
10371037
'Large pages are supported only x64 platform.')
1038-
# Example full version string: 2.6.32-696.28.1.el6.x86_64
1039-
FULL_KERNEL_VERSION=os.uname()[2]
1040-
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
1041-
if KERNEL_VERSION < "2.6.38":
1042-
raise Exception(
1043-
'Large pages need Linux kernel version >= 2.6.38')
1038+
if flavor == 'linux':
1039+
# Example full version string: 2.6.32-696.28.1.el6.x86_64
1040+
FULL_KERNEL_VERSION=os.uname()[2]
1041+
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
1042+
if KERNEL_VERSION < "2.6.38" and flavor == 'linux':
1043+
raise Exception(
1044+
'Large pages need Linux kernel version >= 2.6.38')
10441045
o['variables']['node_use_large_pages'] = b(options.node_use_large_pages)
10451046

10461047
if options.no_ifaddrs:

node.gyp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@
606606
'src/tls_wrap.h'
607607
],
608608
}],
609-
[ 'node_use_large_pages=="true" and OS=="linux"', {
609+
[ 'node_use_large_pages=="true" and OS in "linux freebsd"', {
610610
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
611611
# The current implementation of Large Pages is under Linux.
612612
# Other implementations are possible but not currently supported.

src/large_pages/node_large_page.cc

Lines changed: 112 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,32 @@
2020
//
2121
// SPDX-License-Identifier: MIT
2222

23-
#include <errno.h>
23+
#include "node_large_page.h"
24+
#include "util.h"
25+
#include "uv.h"
26+
2427
#include <fcntl.h> // _O_RDWR
25-
#include <limits.h> // PATH_MAX
26-
#include <locale.h>
27-
#include <signal.h>
28-
#include <stdio.h>
29-
#include <stdlib.h>
30-
#include <stdint.h>
31-
#include <string.h>
3228
#include <sys/types.h>
3329
#include <sys/mman.h>
30+
#if defined(__FreeBSD__)
31+
#include <sys/sysctl.h>
32+
#include <sys/user.h>
33+
#endif
34+
#include <unistd.h> // readlink
35+
36+
#include <cerrno> // NOLINT(build/include)
37+
#include <climits> // PATH_MAX
38+
#include <clocale>
39+
#include <csignal>
40+
#include <cstdio>
41+
#include <cstdlib>
42+
#include <cstdint>
43+
#include <cstring>
3444
#include <string>
3545
#include <fstream>
3646
#include <iostream>
3747
#include <sstream>
38-
#include <unistd.h> // readlink
48+
#include <vector>
3949

4050
// The functions in this file map the text segment of node into 2M pages.
4151
// The algorithm is simple
@@ -85,6 +95,7 @@ inline int64_t hugepage_align_down(int64_t addr) {
8595
// This is also handling the case where the first line is not the binary
8696

8797
static struct text_region FindNodeTextRegion() {
98+
#if defined(__linux__)
8899
std::ifstream ifs;
89100
std::string map_line;
90101
std::string permission;
@@ -138,9 +149,68 @@ static struct text_region FindNodeTextRegion() {
138149
}
139150

140151
ifs.close();
152+
#elif defined(__FreeBSD__)
153+
struct text_region nregion;
154+
nregion.found_text_region = false;
155+
156+
std::string exename;
157+
{
158+
char selfexe[PATH_MAX];
159+
size_t count = sizeof(selfexe);
160+
if (uv_exepath(selfexe, &count))
161+
return nregion;
162+
163+
exename = std::string(selfexe, count);
164+
}
165+
166+
size_t numpg;
167+
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()};
168+
const size_t miblen = arraysize(mib);
169+
if (sysctl(mib, miblen, nullptr, &numpg, nullptr, 0) == -1) {
170+
return nregion;
171+
}
172+
173+
// for struct kinfo_vmentry
174+
numpg = numpg * 4 / 3;
175+
auto alg = std::vector<char>(numpg);
176+
177+
if (sysctl(mib, miblen, alg.data(), &numpg, nullptr, 0) == -1) {
178+
return nregion;
179+
}
180+
181+
char* start = alg.data();
182+
char* end = start + numpg;
183+
184+
while (start < end) {
185+
kinfo_vmentry* entry = reinterpret_cast<kinfo_vmentry*>(start);
186+
const size_t cursz = entry->kve_structsize;
187+
if (cursz == 0) {
188+
break;
189+
}
190+
191+
if (entry->kve_path[0] == '\0') {
192+
continue;
193+
}
194+
bool excmapping = ((entry->kve_protection & KVME_PROT_READ) &&
195+
(entry->kve_protection & KVME_PROT_EXEC));
196+
197+
if (!strcmp(exename.c_str(), entry->kve_path) && excmapping) {
198+
size_t size = entry->kve_end - entry->kve_start;
199+
nregion.found_text_region = true;
200+
nregion.from =
201+
reinterpret_cast<char*>(hugepage_align_up(entry->kve_start));
202+
nregion.to =
203+
reinterpret_cast<char*>(hugepage_align_down(entry->kve_end));
204+
nregion.total_hugepages = size / hps;
205+
break;
206+
}
207+
start += cursz;
208+
}
209+
#endif
141210
return nregion;
142211
}
143212

213+
#if defined(__linux__)
144214
static bool IsTransparentHugePagesEnabled() {
145215
std::ifstream ifs;
146216

@@ -168,6 +238,19 @@ static bool IsTransparentHugePagesEnabled() {
168238
ifs.close();
169239
return ret_status;
170240
}
241+
#elif defined(__FreeBSD__)
242+
static bool IsSuperPagesEnabled() {
243+
// It is enabled by default on amd64
244+
unsigned int super_pages = 0;
245+
size_t super_pages_length = sizeof(super_pages);
246+
if (sysctlbyname("vm.pmap.pg_ps_enabled", &super_pages,
247+
&super_pages_length, nullptr, 0) == -1 ||
248+
super_pages < 1) {
249+
return false;
250+
}
251+
return true;
252+
}
253+
#endif
171254

172255
// Moving the text region to large pages. We need to be very careful.
173256
// 1: This function itself should not be moved.
@@ -203,6 +286,7 @@ MoveTextRegionToLargePages(const text_region& r) {
203286

204287
memcpy(nmem, r.from, size);
205288

289+
#if defined(__linux__)
206290
// We already know the original page is r-xp
207291
// (PROT_READ, PROT_EXEC, MAP_PRIVATE)
208292
// We want PROT_WRITE because we are writing into it.
@@ -230,6 +314,17 @@ MoveTextRegionToLargePages(const text_region& r) {
230314

231315
return -1;
232316
}
317+
#elif defined(__FreeBSD__)
318+
tmem = mmap(start, size,
319+
PROT_READ | PROT_WRITE | PROT_EXEC,
320+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED |
321+
MAP_ALIGNED_SUPER, -1 , 0);
322+
if (tmem == MAP_FAILED) {
323+
PrintSystemError(errno);
324+
munmap(nmem, size);
325+
return -1;
326+
}
327+
#endif
233328

234329
memcpy(start, nmem, size);
235330
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
@@ -263,14 +358,22 @@ int MapStaticCodeToLargePages() {
263358
return -1;
264359
}
265360

361+
#if defined(__linux__)
266362
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
267363
return MoveTextRegionToLargePages(r);
268364

269365
return -1;
366+
#elif defined(__FreeBSD__)
367+
return MoveTextRegionToLargePages(r);
368+
#endif
270369
}
271370

272371
bool IsLargePagesEnabled() {
372+
#if defined(__linux__)
273373
return IsTransparentHugePagesEnabled();
374+
#else
375+
return IsSuperPagesEnabled();
376+
#endif
274377
}
275378

276379
} // namespace node

0 commit comments

Comments
 (0)