Skip to content

Commit 32df017

Browse files
devnexenTrott
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/node#28977 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 3b92998 commit 32df017

File tree

3 files changed

+83
-25
lines changed

3 files changed

+83
-25
lines changed

configure.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1035,16 +1035,18 @@ def configure_node(o):
10351035
else:
10361036
o['variables']['node_use_dtrace'] = 'false'
10371037

1038-
if options.node_use_large_pages and not flavor in ('linux', 'freebsd'):
1038+
if options.node_use_large_pages and not flavor in ('linux', 'freebsd', 'mac'):
10391039
raise Exception(
1040-
'Large pages are supported only on Linux Systems.')
1041-
if options.node_use_large_pages and flavor in ('linux', 'freebsd'):
1040+
'Large pages are supported only on Linux, FreeBSD and MacOS Systems.')
1041+
if options.node_use_large_pages and flavor in ('linux', 'freebsd', 'mac'):
10421042
if options.shared or options.enable_static:
10431043
raise Exception(
10441044
'Large pages are supported only while creating node executable.')
10451045
if target_arch!="x64":
10461046
raise Exception(
10471047
'Large pages are supported only x64 platform.')
1048+
if flavor == 'mac':
1049+
info('macOS server with 32GB or more is recommended')
10481050
if flavor == 'linux':
10491051
# Example full version string: 2.6.32-696.28.1.el6.x86_64
10501052
FULL_KERNEL_VERSION=os.uname()[2]

node.gyp

+1-1
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@
817817
}],
818818
],
819819
}],
820-
[ 'node_use_large_pages=="true" and OS in "linux freebsd"', {
820+
[ 'node_use_large_pages=="true" and OS in "linux freebsd mac"', {
821821
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
822822
# The current implementation of Large Pages is under Linux.
823823
# Other implementations are possible but not currently supported.

src/large_pages/node_large_page.cc

+77-21
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

@@ -212,6 +214,42 @@ static struct text_region FindNodeTextRegion() {
212214
}
213215
start += cursz;
214216
}
217+
#elif defined(__APPLE__)
218+
struct text_region nregion;
219+
nregion.found_text_region = false;
220+
struct vm_region_submap_info_64 map;
221+
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
222+
vm_address_t addr = 0UL;
223+
vm_size_t size = 0;
224+
natural_t depth = 1;
225+
226+
while (true) {
227+
if (vm_region_recurse_64(mach_task_self(), &addr, &size, &depth,
228+
reinterpret_cast<vm_region_info_64_t>(&map),
229+
&count) != KERN_SUCCESS) {
230+
break;
231+
}
232+
233+
if (map.is_submap) {
234+
depth++;
235+
} else {
236+
char* start = reinterpret_cast<char*>(hugepage_align_up(addr));
237+
char* end = reinterpret_cast<char*>(hugepage_align_down(addr+size));
238+
size_t esize = end - start;
239+
240+
if (end > start && (map.protection & VM_PROT_READ) != 0 &&
241+
(map.protection & VM_PROT_EXECUTE) != 0) {
242+
nregion.found_text_region = true;
243+
nregion.from = start;
244+
nregion.to = end;
245+
nregion.total_hugepages = esize / hps;
246+
break;
247+
}
248+
249+
addr += size;
250+
size = 0;
251+
}
252+
}
215253
#endif
216254
return nregion;
217255
}
@@ -267,11 +305,15 @@ static bool IsSuperPagesEnabled() {
267305
// 2: This function should not call any function(s) that might be moved.
268306
// a. map a new area and copy the original code there
269307
// b. mmap using the start address with MAP_FIXED so we get exactly
270-
// the same virtual address
308+
// the same virtual address (except on macOS).
271309
// c. madvise with MADV_HUGE_PAGE
272310
// d. If successful copy the code there and unmap the original region
273311
int
312+
#if !defined(__APPLE__)
274313
__attribute__((__section__(".lpstub")))
314+
#else
315+
__attribute__((__section__("__TEXT,__lpstub")))
316+
#endif
275317
__attribute__((__aligned__(hps)))
276318
__attribute__((__noinline__))
277319
MoveTextRegionToLargePages(const text_region& r) {
@@ -289,6 +331,9 @@ MoveTextRegionToLargePages(const text_region& r) {
289331
PrintSystemError(errno);
290332
return -1;
291333
}
334+
OnScopeLeave munmap_on_return([nmem, size]() {
335+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
336+
});
292337

293338
memcpy(nmem, r.from, size);
294339

@@ -302,7 +347,6 @@ MoveTextRegionToLargePages(const text_region& r) {
302347
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1 , 0);
303348
if (tmem == MAP_FAILED) {
304349
PrintSystemError(errno);
305-
munmap(nmem, size);
306350
return -1;
307351
}
308352

@@ -313,11 +357,6 @@ MoveTextRegionToLargePages(const text_region& r) {
313357
if (ret == -1) {
314358
PrintSystemError(errno);
315359
}
316-
ret = munmap(nmem, size);
317-
if (ret == -1) {
318-
PrintSystemError(errno);
319-
}
320-
321360
return -1;
322361
}
323362
#elif defined(__FreeBSD__)
@@ -327,32 +366,46 @@ MoveTextRegionToLargePages(const text_region& r) {
327366
MAP_ALIGNED_SUPER, -1 , 0);
328367
if (tmem == MAP_FAILED) {
329368
PrintSystemError(errno);
330-
munmap(nmem, size);
331369
return -1;
332370
}
333-
#endif
334-
335-
memcpy(start, nmem, size);
336-
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
371+
#elif defined(__APPLE__)
372+
// There is not enough room to reserve the mapping close
373+
// to the region address so we content to give a hint
374+
// without forcing the new address being closed to.
375+
// We explicitally gives all permission since we plan
376+
// to write into it.
377+
tmem = mmap(start, size,
378+
PROT_READ | PROT_WRITE | PROT_EXEC,
379+
MAP_PRIVATE | MAP_ANONYMOUS,
380+
VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
381+
if (tmem == MAP_FAILED) {
382+
PrintSystemError(errno);
383+
return -1;
384+
}
385+
memcpy(tmem, nmem, size);
386+
ret = mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC);
337387
if (ret == -1) {
338388
PrintSystemError(errno);
339389
ret = munmap(tmem, size);
340390
if (ret == -1) {
341391
PrintSystemError(errno);
342392
}
343-
ret = munmap(nmem, size);
344-
if (ret == -1) {
345-
PrintSystemError(errno);
346-
}
347393
return -1;
348394
}
395+
memcpy(start, tmem, size);
396+
#else
397+
memcpy(start, nmem, size);
398+
#endif
349399

350-
// Release the old/temporary mapped region
351-
ret = munmap(nmem, size);
400+
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
352401
if (ret == -1) {
353402
PrintSystemError(errno);
403+
ret = munmap(tmem, size);
404+
if (ret == -1) {
405+
PrintSystemError(errno);
406+
}
407+
return -1;
354408
}
355-
356409
return ret;
357410
}
358411

@@ -369,16 +422,19 @@ int MapStaticCodeToLargePages() {
369422
return MoveTextRegionToLargePages(r);
370423

371424
return -1;
372-
#elif defined(__FreeBSD__)
425+
#elif defined(__FreeBSD__) || defined(__APPLE__)
373426
return MoveTextRegionToLargePages(r);
374427
#endif
375428
}
376429

377430
bool IsLargePagesEnabled() {
378431
#if defined(__linux__)
379432
return IsTransparentHugePagesEnabled();
380-
#else
433+
#elif defined(__FreeBSD__)
381434
return IsSuperPagesEnabled();
435+
#elif defined(__APPLE__)
436+
// pse-36 flag is present in recent mac x64 products.
437+
return true;
382438
#endif
383439
}
384440

0 commit comments

Comments
 (0)