From 7a698d4bb3cb9acd476e3846435e92f490b28e59 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 5 Aug 2019 14:00:15 +0100 Subject: [PATCH 1/3] src: 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. --- configure.py | 8 ++-- node.gyp | 2 +- src/large_pages/node_large_page.cc | 59 +++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/configure.py b/configure.py index cc805d3fd165fd..bb5b2fb9eb8e7c 100755 --- a/configure.py +++ b/configure.py @@ -1037,16 +1037,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': + print("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] diff --git a/node.gyp b/node.gyp index 4eae262a61e77b..4576e5a335c3e6 100644 --- a/node.gyp +++ b/node.gyp @@ -817,7 +817,7 @@ }], ], }], - [ '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. diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index d87ccfaa26abcd..e7aa465c777ef5 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -30,6 +30,8 @@ #if defined(__FreeBSD__) #include #include +#elif defined(__APPLE__) +#include #endif #include // readlink @@ -212,6 +214,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(&map), + &count) != KERN_SUCCESS) { + break; + } + + if (map.is_submap) { + depth++; + } else { + char* start = reinterpret_cast(hugepage_align_up(addr)); + char* end = reinterpret_cast(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; } @@ -271,7 +309,11 @@ static bool IsSuperPagesEnabled() { // 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) { @@ -330,6 +372,16 @@ MoveTextRegionToLargePages(const text_region& r) { munmap(nmem, size); return -1; } +#elif defined(__APPLE__) + 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); + munmap(nmem, size); + return -1; + } #endif memcpy(start, nmem, size); @@ -369,7 +421,7 @@ int MapStaticCodeToLargePages() { return MoveTextRegionToLargePages(r); return -1; -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__APPLE__) return MoveTextRegionToLargePages(r); #endif } @@ -377,8 +429,11 @@ int MapStaticCodeToLargePages() { 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 } From 59a2232b5e45ea385bf6cdc1d2d712ab3e4e0299 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 8 Aug 2019 06:17:33 +0100 Subject: [PATCH 2/3] Fixing address copy handling. --- src/large_pages/node_large_page.cc | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index e7aa465c777ef5..5f7a56c05c0441 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -382,9 +382,25 @@ MoveTextRegionToLargePages(const text_region& r) { munmap(nmem, size); 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 - memcpy(start, nmem, size); ret = mprotect(start, size, PROT_READ | PROT_EXEC); if (ret == -1) { PrintSystemError(errno); From 0f7190f56820f22ce0642313a62055bbf1863c20 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 20 Aug 2019 12:48:10 +0100 Subject: [PATCH 3/3] Tweaks from feedback --- configure.py | 2 +- src/large_pages/node_large_page.cc | 33 ++++++++---------------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/configure.py b/configure.py index bb5b2fb9eb8e7c..e33e43b98927d7 100755 --- a/configure.py +++ b/configure.py @@ -1048,7 +1048,7 @@ def configure_node(o): raise Exception( 'Large pages are supported only x64 platform.') if flavor == 'mac': - print("macOS server with 32GB or more is recommended") + 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] diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index 5f7a56c05c0441..f6f5e72c283632 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -305,7 +305,7 @@ 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 @@ -331,6 +331,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); @@ -344,7 +347,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; } @@ -355,11 +357,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__) @@ -369,17 +366,20 @@ MoveTextRegionToLargePages(const text_region& r) { MAP_ALIGNED_SUPER, -1 , 0); if (tmem == MAP_FAILED) { PrintSystemError(errno); - munmap(nmem, size); return -1; } #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); - munmap(nmem, size); return -1; } memcpy(tmem, nmem, size); @@ -390,10 +390,6 @@ MoveTextRegionToLargePages(const text_region& r) { if (ret == -1) { PrintSystemError(errno); } - ret = munmap(nmem, size); - if (ret == -1) { - PrintSystemError(errno); - } return -1; } memcpy(start, tmem, size); @@ -408,19 +404,8 @@ MoveTextRegionToLargePages(const text_region& r) { if (ret == -1) { PrintSystemError(errno); } - ret = munmap(nmem, size); - if (ret == -1) { - PrintSystemError(errno); - } return -1; } - - // Release the old/temporary mapped region - ret = munmap(nmem, size); - if (ret == -1) { - PrintSystemError(errno); - } - return ret; }