Skip to content

Commit

Permalink
src: add large page support for macOS
Browse files Browse the repository at this point in the history
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 <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
devnexen authored and Gabriel Schulhof committed Mar 5, 2020
1 parent 86419d8 commit 01de6c2
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 25 deletions.
8 changes: 5 additions & 3 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1030,16 +1030,18 @@ def configure_node(o):
else:
o['variables']['node_use_dtrace'] = 'false'

if options.node_use_large_pages and not flavor in ('linux', 'freebsd'):
if options.node_use_large_pages and not flavor in ('linux', 'freebsd', 'mac'):
raise Exception(
'Large pages are supported only on Linux Systems.')
if options.node_use_large_pages and flavor in ('linux', 'freebsd'):
'Large pages are supported only on Linux, FreeBSD and MacOS Systems.')
if options.node_use_large_pages and flavor in ('linux', 'freebsd', 'mac'):
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.')
if flavor == 'mac':
info('macOS server with 32GB or more is recommended')
if flavor == 'linux':
# Example full version string: 2.6.32-696.28.1.el6.x86_64
FULL_KERNEL_VERSION=os.uname()[2]
Expand Down
2 changes: 1 addition & 1 deletion node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@
'src/tls_wrap.h'
],
}],
[ 'node_use_large_pages=="true" and OS in "linux freebsd"', {
[ 'node_use_large_pages=="true" and OS in "linux freebsd mac"', {
'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
105 changes: 84 additions & 21 deletions src/large_pages/node_large_page.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#if defined(__FreeBSD__)
#include <sys/sysctl.h>
#include <sys/user.h>
#elif defined(__APPLE__)
#include <mach/vm_map.h>
#endif
#include <unistd.h> // readlink

Expand All @@ -47,6 +49,13 @@
#include <sstream>
#include <vector>

// Define MAP_ANONYMOUS as an alias for MAP_ANON to support OSX 10.10.
#if defined(__APPLE__)
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif

// The functions in this file map the text segment of node into 2M pages.
// The algorithm is simple
// Find the text region of node binary in memory
Expand Down Expand Up @@ -212,6 +221,42 @@ static struct text_region FindNodeTextRegion() {
}
start += cursz;
}
#elif defined(__APPLE__)
struct text_region nregion;
nregion.found_text_region = false;
struct vm_region_submap_info_64 map;
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
vm_address_t addr = 0UL;
vm_size_t size = 0;
natural_t depth = 1;

while (true) {
if (vm_region_recurse_64(mach_task_self(), &addr, &size, &depth,
reinterpret_cast<vm_region_info_64_t>(&map),
&count) != KERN_SUCCESS) {
break;
}

if (map.is_submap) {
depth++;
} else {
char* start = reinterpret_cast<char*>(hugepage_align_up(addr));
char* end = reinterpret_cast<char*>(hugepage_align_down(addr+size));
size_t esize = end - start;

if (end > start && (map.protection & VM_PROT_READ) != 0 &&
(map.protection & VM_PROT_EXECUTE) != 0) {
nregion.found_text_region = true;
nregion.from = start;
nregion.to = end;
nregion.total_hugepages = esize / hps;
break;
}

addr += size;
size = 0;
}
}
#endif
return nregion;
}
Expand Down Expand Up @@ -267,11 +312,15 @@ static bool IsSuperPagesEnabled() {
// 2: This function should not call any function(s) that might be moved.
// a. map a new area and copy the original code there
// b. mmap using the start address with MAP_FIXED so we get exactly
// the same virtual address
// the same virtual address (except on macOS).
// c. madvise with MADV_HUGE_PAGE
// d. If successful copy the code there and unmap the original region
int
#if !defined(__APPLE__)
__attribute__((__section__(".lpstub")))
#else
__attribute__((__section__("__TEXT,__lpstub")))
#endif
__attribute__((__aligned__(hps)))
__attribute__((__noinline__))
MoveTextRegionToLargePages(const text_region& r) {
Expand All @@ -289,6 +338,9 @@ MoveTextRegionToLargePages(const text_region& r) {
PrintSystemError(errno);
return -1;
}
OnScopeLeave munmap_on_return([nmem, size]() {
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
});

memcpy(nmem, r.from, size);

Expand All @@ -302,7 +354,6 @@ MoveTextRegionToLargePages(const text_region& r) {
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1 , 0);
if (tmem == MAP_FAILED) {
PrintSystemError(errno);
munmap(nmem, size);
return -1;
}

Expand All @@ -313,11 +364,6 @@ MoveTextRegionToLargePages(const text_region& r) {
if (ret == -1) {
PrintSystemError(errno);
}
ret = munmap(nmem, size);
if (ret == -1) {
PrintSystemError(errno);
}

return -1;
}
#elif defined(__FreeBSD__)
Expand All @@ -327,32 +373,46 @@ MoveTextRegionToLargePages(const text_region& r) {
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);
#elif defined(__APPLE__)
// There is not enough room to reserve the mapping close
// to the region address so we content to give a hint
// without forcing the new address being closed to.
// We explicitally gives all permission since we plan
// to write into it.
tmem = mmap(start, size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,
VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
if (tmem == MAP_FAILED) {
PrintSystemError(errno);
return -1;
}
memcpy(tmem, nmem, size);
ret = mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC);
if (ret == -1) {
PrintSystemError(errno);
ret = munmap(tmem, size);
if (ret == -1) {
PrintSystemError(errno);
}
ret = munmap(nmem, size);
if (ret == -1) {
PrintSystemError(errno);
}
return -1;
}
memcpy(start, tmem, size);
#else
memcpy(start, nmem, size);
#endif

// Release the old/temporary mapped region
ret = munmap(nmem, size);
ret = mprotect(start, size, PROT_READ | PROT_EXEC);
if (ret == -1) {
PrintSystemError(errno);
ret = munmap(tmem, size);
if (ret == -1) {
PrintSystemError(errno);
}
return -1;
}

return ret;
}

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

return -1;
#elif defined(__FreeBSD__)
#elif defined(__FreeBSD__) || defined(__APPLE__)
return MoveTextRegionToLargePages(r);
#endif
}

bool IsLargePagesEnabled() {
#if defined(__linux__)
return IsTransparentHugePagesEnabled();
#else
#elif defined(__FreeBSD__)
return IsSuperPagesEnabled();
#elif defined(__APPLE__)
// pse-36 flag is present in recent mac x64 products.
return true;
#endif
}

Expand Down

0 comments on commit 01de6c2

Please sign in to comment.