Skip to content

Commit 0b7feca

Browse files
devnexentargos
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: #28331 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 312f949 commit 0b7feca

File tree

3 files changed

+110
-9
lines changed

3 files changed

+110
-9
lines changed

configure.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -1039,22 +1039,23 @@ def configure_node(o):
10391039
else:
10401040
o['variables']['node_use_dtrace'] = 'false'
10411041

1042-
if options.node_use_large_pages and flavor != 'linux':
1042+
if options.node_use_large_pages and not flavor in ('linux', 'freebsd'):
10431043
raise Exception(
10441044
'Large pages are supported only on Linux Systems.')
1045-
if options.node_use_large_pages and flavor == 'linux':
1045+
if options.node_use_large_pages and flavor in ('linux', 'freebsd'):
10461046
if options.shared or options.enable_static:
10471047
raise Exception(
10481048
'Large pages are supported only while creating node executable.')
10491049
if target_arch!="x64":
10501050
raise Exception(
10511051
'Large pages are supported only x64 platform.')
1052-
# Example full version string: 2.6.32-696.28.1.el6.x86_64
1053-
FULL_KERNEL_VERSION=os.uname()[2]
1054-
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
1055-
if KERNEL_VERSION < "2.6.38":
1056-
raise Exception(
1057-
'Large pages need Linux kernel version >= 2.6.38')
1052+
if flavor == 'linux':
1053+
# Example full version string: 2.6.32-696.28.1.el6.x86_64
1054+
FULL_KERNEL_VERSION=os.uname()[2]
1055+
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
1056+
if KERNEL_VERSION < "2.6.38" and flavor == 'linux':
1057+
raise Exception(
1058+
'Large pages need Linux kernel version >= 2.6.38')
10581059
o['variables']['node_use_large_pages'] = b(options.node_use_large_pages)
10591060

10601061
if options.no_ifaddrs:

node.gyp

+1-1
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@
811811
}],
812812
],
813813
}],
814-
[ 'node_use_large_pages=="true" and OS=="linux"', {
814+
[ 'node_use_large_pages=="true" and OS in "linux freebsd"', {
815815
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
816816
# The current implementation of Large Pages is under Linux.
817817
# Other implementations are possible but not currently supported.

src/large_pages/node_large_page.cc

+100
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,16 @@
2121
// SPDX-License-Identifier: MIT
2222

2323
#include "node_large_page.h"
24+
#include "util.h"
25+
#include "uv.h"
2426

2527
#include <fcntl.h> // _O_RDWR
2628
#include <sys/types.h>
2729
#include <sys/mman.h>
30+
#if defined(__FreeBSD__)
31+
#include <sys/sysctl.h>
32+
#include <sys/user.h>
33+
#endif
2834
#include <unistd.h> // readlink
2935

3036
#include <cerrno> // NOLINT(build/include)
@@ -39,6 +45,7 @@
3945
#include <fstream>
4046
#include <iostream>
4147
#include <sstream>
48+
#include <vector>
4249

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

9097
static struct text_region FindNodeTextRegion() {
98+
#if defined(__linux__)
9199
std::ifstream ifs;
92100
std::string map_line;
93101
std::string permission;
@@ -141,9 +149,68 @@ static struct text_region FindNodeTextRegion() {
141149
}
142150

143151
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
144210
return nregion;
145211
}
146212

213+
#if defined(__linux__)
147214
static bool IsTransparentHugePagesEnabled() {
148215
std::ifstream ifs;
149216

@@ -171,6 +238,19 @@ static bool IsTransparentHugePagesEnabled() {
171238
ifs.close();
172239
return ret_status;
173240
}
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
174254

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

207287
memcpy(nmem, r.from, size);
208288

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

234315
return -1;
235316
}
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
236328

237329
memcpy(start, nmem, size);
238330
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
@@ -266,14 +358,22 @@ int MapStaticCodeToLargePages() {
266358
return -1;
267359
}
268360

361+
#if defined(__linux__)
269362
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
270363
return MoveTextRegionToLargePages(r);
271364

272365
return -1;
366+
#elif defined(__FreeBSD__)
367+
return MoveTextRegionToLargePages(r);
368+
#endif
273369
}
274370

275371
bool IsLargePagesEnabled() {
372+
#if defined(__linux__)
276373
return IsTransparentHugePagesEnabled();
374+
#else
375+
return IsSuperPagesEnabled();
376+
#endif
277377
}
278378

279379
} // namespace node

0 commit comments

Comments
 (0)