Skip to content

Commit 01de6c2

Browse files
devnexenGabriel Schulhof
authored andcommitted
src: add large page support for macOS
Proposal to bring the support for this platform. We assume the pse36 cpu flag is present for 2MB large page support present in recent years in mac line (not to be backported to 10.x anyway). Recommended better for mac production servers rather than casual mac books. PR-URL: nodejs#28977 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 86419d8 commit 01de6c2

File tree

3 files changed

+90
-25
lines changed

3 files changed

+90
-25
lines changed

configure.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,16 +1030,18 @@ def configure_node(o):
10301030
else:
10311031
o['variables']['node_use_dtrace'] = 'false'
10321032

1033-
if options.node_use_large_pages and not flavor in ('linux', 'freebsd'):
1033+
if options.node_use_large_pages and not flavor in ('linux', 'freebsd', 'mac'):
10341034
raise Exception(
1035-
'Large pages are supported only on Linux Systems.')
1036-
if options.node_use_large_pages and flavor in ('linux', 'freebsd'):
1035+
'Large pages are supported only on Linux, FreeBSD and MacOS Systems.')
1036+
if options.node_use_large_pages and flavor in ('linux', 'freebsd', 'mac'):
10371037
if options.shared or options.enable_static:
10381038
raise Exception(
10391039
'Large pages are supported only while creating node executable.')
10401040
if target_arch!="x64":
10411041
raise Exception(
10421042
'Large pages are supported only x64 platform.')
1043+
if flavor == 'mac':
1044+
info('macOS server with 32GB or more is recommended')
10431045
if flavor == 'linux':
10441046
# Example full version string: 2.6.32-696.28.1.el6.x86_64
10451047
FULL_KERNEL_VERSION=os.uname()[2]

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 in "linux freebsd"', {
609+
[ 'node_use_large_pages=="true" and OS in "linux freebsd mac"', {
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: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#if defined(__FreeBSD__)
3131
#include <sys/sysctl.h>
3232
#include <sys/user.h>
33+
#elif defined(__APPLE__)
34+
#include <mach/vm_map.h>
3335
#endif
3436
#include <unistd.h> // readlink
3537

@@ -47,6 +49,13 @@
4749
#include <sstream>
4850
#include <vector>
4951

52+
// Define MAP_ANONYMOUS as an alias for MAP_ANON to support OSX 10.10.
53+
#if defined(__APPLE__)
54+
#ifndef MAP_ANONYMOUS
55+
#define MAP_ANONYMOUS MAP_ANON
56+
#endif
57+
#endif
58+
5059
// The functions in this file map the text segment of node into 2M pages.
5160
// The algorithm is simple
5261
// Find the text region of node binary in memory
@@ -212,6 +221,42 @@ static struct text_region FindNodeTextRegion() {
212221
}
213222
start += cursz;
214223
}
224+
#elif defined(__APPLE__)
225+
struct text_region nregion;
226+
nregion.found_text_region = false;
227+
struct vm_region_submap_info_64 map;
228+
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
229+
vm_address_t addr = 0UL;
230+
vm_size_t size = 0;
231+
natural_t depth = 1;
232+
233+
while (true) {
234+
if (vm_region_recurse_64(mach_task_self(), &addr, &size, &depth,
235+
reinterpret_cast<vm_region_info_64_t>(&map),
236+
&count) != KERN_SUCCESS) {
237+
break;
238+
}
239+
240+
if (map.is_submap) {
241+
depth++;
242+
} else {
243+
char* start = reinterpret_cast<char*>(hugepage_align_up(addr));
244+
char* end = reinterpret_cast<char*>(hugepage_align_down(addr+size));
245+
size_t esize = end - start;
246+
247+
if (end > start && (map.protection & VM_PROT_READ) != 0 &&
248+
(map.protection & VM_PROT_EXECUTE) != 0) {
249+
nregion.found_text_region = true;
250+
nregion.from = start;
251+
nregion.to = end;
252+
nregion.total_hugepages = esize / hps;
253+
break;
254+
}
255+
256+
addr += size;
257+
size = 0;
258+
}
259+
}
215260
#endif
216261
return nregion;
217262
}
@@ -267,11 +312,15 @@ static bool IsSuperPagesEnabled() {
267312
// 2: This function should not call any function(s) that might be moved.
268313
// a. map a new area and copy the original code there
269314
// b. mmap using the start address with MAP_FIXED so we get exactly
270-
// the same virtual address
315+
// the same virtual address (except on macOS).
271316
// c. madvise with MADV_HUGE_PAGE
272317
// d. If successful copy the code there and unmap the original region
273318
int
319+
#if !defined(__APPLE__)
274320
__attribute__((__section__(".lpstub")))
321+
#else
322+
__attribute__((__section__("__TEXT,__lpstub")))
323+
#endif
275324
__attribute__((__aligned__(hps)))
276325
__attribute__((__noinline__))
277326
MoveTextRegionToLargePages(const text_region& r) {
@@ -289,6 +338,9 @@ MoveTextRegionToLargePages(const text_region& r) {
289338
PrintSystemError(errno);
290339
return -1;
291340
}
341+
OnScopeLeave munmap_on_return([nmem, size]() {
342+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
343+
});
292344

293345
memcpy(nmem, r.from, size);
294346

@@ -302,7 +354,6 @@ MoveTextRegionToLargePages(const text_region& r) {
302354
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1 , 0);
303355
if (tmem == MAP_FAILED) {
304356
PrintSystemError(errno);
305-
munmap(nmem, size);
306357
return -1;
307358
}
308359

@@ -313,11 +364,6 @@ MoveTextRegionToLargePages(const text_region& r) {
313364
if (ret == -1) {
314365
PrintSystemError(errno);
315366
}
316-
ret = munmap(nmem, size);
317-
if (ret == -1) {
318-
PrintSystemError(errno);
319-
}
320-
321367
return -1;
322368
}
323369
#elif defined(__FreeBSD__)
@@ -327,32 +373,46 @@ MoveTextRegionToLargePages(const text_region& r) {
327373
MAP_ALIGNED_SUPER, -1 , 0);
328374
if (tmem == MAP_FAILED) {
329375
PrintSystemError(errno);
330-
munmap(nmem, size);
331376
return -1;
332377
}
333-
#endif
334-
335-
memcpy(start, nmem, size);
336-
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
378+
#elif defined(__APPLE__)
379+
// There is not enough room to reserve the mapping close
380+
// to the region address so we content to give a hint
381+
// without forcing the new address being closed to.
382+
// We explicitally gives all permission since we plan
383+
// to write into it.
384+
tmem = mmap(start, size,
385+
PROT_READ | PROT_WRITE | PROT_EXEC,
386+
MAP_PRIVATE | MAP_ANONYMOUS,
387+
VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
388+
if (tmem == MAP_FAILED) {
389+
PrintSystemError(errno);
390+
return -1;
391+
}
392+
memcpy(tmem, nmem, size);
393+
ret = mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC);
337394
if (ret == -1) {
338395
PrintSystemError(errno);
339396
ret = munmap(tmem, size);
340397
if (ret == -1) {
341398
PrintSystemError(errno);
342399
}
343-
ret = munmap(nmem, size);
344-
if (ret == -1) {
345-
PrintSystemError(errno);
346-
}
347400
return -1;
348401
}
402+
memcpy(start, tmem, size);
403+
#else
404+
memcpy(start, nmem, size);
405+
#endif
349406

350-
// Release the old/temporary mapped region
351-
ret = munmap(nmem, size);
407+
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
352408
if (ret == -1) {
353409
PrintSystemError(errno);
410+
ret = munmap(tmem, size);
411+
if (ret == -1) {
412+
PrintSystemError(errno);
413+
}
414+
return -1;
354415
}
355-
356416
return ret;
357417
}
358418

@@ -369,16 +429,19 @@ int MapStaticCodeToLargePages() {
369429
return MoveTextRegionToLargePages(r);
370430

371431
return -1;
372-
#elif defined(__FreeBSD__)
432+
#elif defined(__FreeBSD__) || defined(__APPLE__)
373433
return MoveTextRegionToLargePages(r);
374434
#endif
375435
}
376436

377437
bool IsLargePagesEnabled() {
378438
#if defined(__linux__)
379439
return IsTransparentHugePagesEnabled();
380-
#else
440+
#elif defined(__FreeBSD__)
381441
return IsSuperPagesEnabled();
442+
#elif defined(__APPLE__)
443+
// pse-36 flag is present in recent mac x64 products.
444+
return true;
382445
#endif
383446
}
384447

0 commit comments

Comments
 (0)