11cb0ef41Sopenharmony_ci// Copyright (C) 2018 Intel Corporation 21cb0ef41Sopenharmony_ci// 31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a copy 41cb0ef41Sopenharmony_ci// of this software and associated documentation files (the "Software"), 51cb0ef41Sopenharmony_ci// to deal in the Software without restriction, including without limitation 61cb0ef41Sopenharmony_ci// the rights to use, copy, modify, merge, publish, distribute, sublicense, 71cb0ef41Sopenharmony_ci// and/or sell copies of the Software, and to permit persons to whom 81cb0ef41Sopenharmony_ci// the Software is furnished to do so, subject to the following conditions: 91cb0ef41Sopenharmony_ci// 101cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included 111cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software. 121cb0ef41Sopenharmony_ci// 131cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 141cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 151cb0ef41Sopenharmony_ci// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 161cb0ef41Sopenharmony_ci// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 171cb0ef41Sopenharmony_ci// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 181cb0ef41Sopenharmony_ci// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 191cb0ef41Sopenharmony_ci// OR OTHER DEALINGS IN THE SOFTWARE. 201cb0ef41Sopenharmony_ci// 211cb0ef41Sopenharmony_ci// SPDX-License-Identifier: MIT 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ci// The functions in this file map the .text section of Node.js into 2MB pages. 241cb0ef41Sopenharmony_ci// They perform the following steps: 251cb0ef41Sopenharmony_ci// 261cb0ef41Sopenharmony_ci// 1: Find the Node.js binary's `.text` section in memory. This is done below in 271cb0ef41Sopenharmony_ci// `FindNodeTextRegion`. It is accomplished in a platform-specific way. On 281cb0ef41Sopenharmony_ci// Linux and FreeBSD, `dl_iterate_phdr(3)` is used. When the region is found, 291cb0ef41Sopenharmony_ci// it is "trimmed" as follows: 301cb0ef41Sopenharmony_ci// * Modify the start to point to the very beginning of the Node.js `.text` 311cb0ef41Sopenharmony_ci// section (from symbol `__node_text_start` declared in node_text_start.S). 321cb0ef41Sopenharmony_ci// * Possibly modify the end to account for the `lpstub` section which 331cb0ef41Sopenharmony_ci// contains `MoveTextRegionToLargePages`, the function we do not wish to 341cb0ef41Sopenharmony_ci// move (see below). 351cb0ef41Sopenharmony_ci// * Align the address of the start to its nearest higher large page 361cb0ef41Sopenharmony_ci// boundary. 371cb0ef41Sopenharmony_ci// * Align the address of the end to its nearest lower large page boundary. 381cb0ef41Sopenharmony_ci// 391cb0ef41Sopenharmony_ci// 2: Move the text region to large pages. This is done below in 401cb0ef41Sopenharmony_ci// `MoveTextRegionToLargePages`. We need to be very careful: 411cb0ef41Sopenharmony_ci// a) `MoveTextRegionToLargePages` itself should not be moved. 421cb0ef41Sopenharmony_ci// We use gcc attributes 431cb0ef41Sopenharmony_ci// (__section__) to put it outside the `.text` section, 441cb0ef41Sopenharmony_ci// (__aligned__) to align it at the 2M boundary, and 451cb0ef41Sopenharmony_ci// (__noline__) to not inline this function. 461cb0ef41Sopenharmony_ci// b) `MoveTextRegionToLargePages` should not call any function(s) that might 471cb0ef41Sopenharmony_ci// be moved. 481cb0ef41Sopenharmony_ci// To move the .text section, perform the following steps: 491cb0ef41Sopenharmony_ci// * Map a new, temporary area and copy the original code there. 501cb0ef41Sopenharmony_ci// * Use mmap using the start address with MAP_FIXED so we get exactly the 511cb0ef41Sopenharmony_ci// same virtual address (except on OSX). On platforms other than Linux, 521cb0ef41Sopenharmony_ci// use mmap flags to request hugepages. 531cb0ef41Sopenharmony_ci// * On Linux use madvise with MADV_HUGEPAGE to use anonymous 2MB pages. 541cb0ef41Sopenharmony_ci// * If successful copy the code to the newly mapped area and protect it to 551cb0ef41Sopenharmony_ci// be readable and executable. 561cb0ef41Sopenharmony_ci// * Unmap the temporary area. 571cb0ef41Sopenharmony_ci 581cb0ef41Sopenharmony_ci#include "node_large_page.h" 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_ci#include <cerrno> // NOLINT(build/include) 611cb0ef41Sopenharmony_ci 621cb0ef41Sopenharmony_ci// Besides returning ENOTSUP at runtime we do nothing if this define is missing. 631cb0ef41Sopenharmony_ci#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES 641cb0ef41Sopenharmony_ci#include "debug_utils-inl.h" 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci#if defined(__linux__) || defined(__FreeBSD__) 671cb0ef41Sopenharmony_ci#if defined(__linux__) 681cb0ef41Sopenharmony_ci#ifndef _GNU_SOURCE 691cb0ef41Sopenharmony_ci#define _GNU_SOURCE 701cb0ef41Sopenharmony_ci#endif // ifndef _GNU_SOURCE 711cb0ef41Sopenharmony_ci#include <sys/prctl.h> 721cb0ef41Sopenharmony_ci#if !defined(PR_SET_VMA) 731cb0ef41Sopenharmony_ci#define PR_SET_VMA 0x53564d41 741cb0ef41Sopenharmony_ci#define PR_SET_VMA_ANON_NAME 0 751cb0ef41Sopenharmony_ci#endif 761cb0ef41Sopenharmony_ci#elif defined(__FreeBSD__) 771cb0ef41Sopenharmony_ci#include "uv.h" // uv_exepath 781cb0ef41Sopenharmony_ci#endif // defined(__linux__) 791cb0ef41Sopenharmony_ci#include <link.h> 801cb0ef41Sopenharmony_ci#endif // defined(__linux__) || defined(__FreeBSD__) 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_ci#include <sys/types.h> 831cb0ef41Sopenharmony_ci#include <sys/mman.h> 841cb0ef41Sopenharmony_ci#if defined(__FreeBSD__) 851cb0ef41Sopenharmony_ci#include <sys/sysctl.h> 861cb0ef41Sopenharmony_ci#elif defined(__APPLE__) 871cb0ef41Sopenharmony_ci#include <mach/vm_map.h> 881cb0ef41Sopenharmony_ci#endif 891cb0ef41Sopenharmony_ci 901cb0ef41Sopenharmony_ci#include <climits> // PATH_MAX 911cb0ef41Sopenharmony_ci#include <cstdlib> 921cb0ef41Sopenharmony_ci#include <cstdint> 931cb0ef41Sopenharmony_ci#include <cstring> 941cb0ef41Sopenharmony_ci#include <string> 951cb0ef41Sopenharmony_ci#include <fstream> 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci#if defined(__linux__) || defined(__FreeBSD__) 981cb0ef41Sopenharmony_ciextern "C" { 991cb0ef41Sopenharmony_ci// This symbol must be declared weak because this file becomes part of all 1001cb0ef41Sopenharmony_ci// Node.js targets (like node_mksnapshot, node_mkcodecache, and cctest) and 1011cb0ef41Sopenharmony_ci// those files do not supply the symbol. 1021cb0ef41Sopenharmony_ciextern char __attribute__((weak)) __node_text_start; 1031cb0ef41Sopenharmony_ciextern char __start_lpstub; 1041cb0ef41Sopenharmony_ci} // extern "C" 1051cb0ef41Sopenharmony_ci#endif // defined(__linux__) || defined(__FreeBSD__) 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci#endif // defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES 1081cb0ef41Sopenharmony_cinamespace node { 1091cb0ef41Sopenharmony_ci#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_cinamespace { 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_cistruct text_region { 1141cb0ef41Sopenharmony_ci char* from = nullptr; 1151cb0ef41Sopenharmony_ci char* to = nullptr; 1161cb0ef41Sopenharmony_ci bool found_text_region = false; 1171cb0ef41Sopenharmony_ci}; 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_cistatic const size_t hps = 2L * 1024 * 1024; 1201cb0ef41Sopenharmony_ci 1211cb0ef41Sopenharmony_citemplate <typename... Args> 1221cb0ef41Sopenharmony_ciinline void Debug(std::string fmt, Args&&... args) { 1231cb0ef41Sopenharmony_ci node::Debug(&per_process::enabled_debug_list, 1241cb0ef41Sopenharmony_ci DebugCategory::HUGEPAGES, 1251cb0ef41Sopenharmony_ci (std::string("Hugepages info: ") + fmt).c_str(), 1261cb0ef41Sopenharmony_ci std::forward<Args>(args)...); 1271cb0ef41Sopenharmony_ci} 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_ciinline void PrintWarning(const char* warn) { 1301cb0ef41Sopenharmony_ci fprintf(stderr, "Hugepages WARNING: %s\n", warn); 1311cb0ef41Sopenharmony_ci} 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ciinline void PrintSystemError(int error) { 1341cb0ef41Sopenharmony_ci PrintWarning(strerror(error)); 1351cb0ef41Sopenharmony_ci} 1361cb0ef41Sopenharmony_ci 1371cb0ef41Sopenharmony_ciinline uintptr_t hugepage_align_up(uintptr_t addr) { 1381cb0ef41Sopenharmony_ci return (((addr) + (hps) - 1) & ~((hps) - 1)); 1391cb0ef41Sopenharmony_ci} 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_ciinline uintptr_t hugepage_align_down(uintptr_t addr) { 1421cb0ef41Sopenharmony_ci return ((addr) & ~((hps) - 1)); 1431cb0ef41Sopenharmony_ci} 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_ci#if defined(__linux__) || defined(__FreeBSD__) 1461cb0ef41Sopenharmony_ci#if defined(__FreeBSD__) 1471cb0ef41Sopenharmony_ci#ifndef ElfW 1481cb0ef41Sopenharmony_ci#define ElfW(name) Elf_##name 1491cb0ef41Sopenharmony_ci#endif // ifndef ElfW 1501cb0ef41Sopenharmony_ci#endif // defined(__FreeBSD__) 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_cistruct dl_iterate_params { 1531cb0ef41Sopenharmony_ci uintptr_t start = 0; 1541cb0ef41Sopenharmony_ci uintptr_t end = 0; 1551cb0ef41Sopenharmony_ci uintptr_t reference_sym = reinterpret_cast<uintptr_t>(&__node_text_start); 1561cb0ef41Sopenharmony_ci std::string exename; 1571cb0ef41Sopenharmony_ci}; 1581cb0ef41Sopenharmony_ci 1591cb0ef41Sopenharmony_ciint FindMapping(struct dl_phdr_info* info, size_t, void* data) { 1601cb0ef41Sopenharmony_ci auto dl_params = static_cast<dl_iterate_params*>(data); 1611cb0ef41Sopenharmony_ci if (dl_params->exename == std::string(info->dlpi_name)) { 1621cb0ef41Sopenharmony_ci for (int idx = 0; idx < info->dlpi_phnum; idx++) { 1631cb0ef41Sopenharmony_ci const ElfW(Phdr)* phdr = &info->dlpi_phdr[idx]; 1641cb0ef41Sopenharmony_ci if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X)) { 1651cb0ef41Sopenharmony_ci uintptr_t start = info->dlpi_addr + phdr->p_vaddr; 1661cb0ef41Sopenharmony_ci uintptr_t end = start + phdr->p_memsz; 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci if (dl_params->reference_sym >= start && 1691cb0ef41Sopenharmony_ci dl_params->reference_sym <= end) { 1701cb0ef41Sopenharmony_ci dl_params->start = start; 1711cb0ef41Sopenharmony_ci dl_params->end = end; 1721cb0ef41Sopenharmony_ci return 1; 1731cb0ef41Sopenharmony_ci } 1741cb0ef41Sopenharmony_ci } 1751cb0ef41Sopenharmony_ci } 1761cb0ef41Sopenharmony_ci } 1771cb0ef41Sopenharmony_ci return 0; 1781cb0ef41Sopenharmony_ci} 1791cb0ef41Sopenharmony_ci#endif // defined(__linux__) || defined(__FreeBSD__) 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_cistruct text_region FindNodeTextRegion() { 1821cb0ef41Sopenharmony_ci struct text_region nregion; 1831cb0ef41Sopenharmony_ci#if defined(__linux__) || defined(__FreeBSD__) 1841cb0ef41Sopenharmony_ci dl_iterate_params dl_params; 1851cb0ef41Sopenharmony_ci uintptr_t lpstub_start = reinterpret_cast<uintptr_t>(&__start_lpstub); 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ci#if defined(__FreeBSD__) 1881cb0ef41Sopenharmony_ci // On FreeBSD we need the name of the binary, because `dl_iterate_phdr` does 1891cb0ef41Sopenharmony_ci // not pass in an empty string as the `dlpi_name` of the binary but rather its 1901cb0ef41Sopenharmony_ci // absolute path. 1911cb0ef41Sopenharmony_ci { 1921cb0ef41Sopenharmony_ci char selfexe[PATH_MAX]; 1931cb0ef41Sopenharmony_ci size_t count = sizeof(selfexe); 1941cb0ef41Sopenharmony_ci if (uv_exepath(selfexe, &count)) 1951cb0ef41Sopenharmony_ci return nregion; 1961cb0ef41Sopenharmony_ci dl_params.exename = std::string(selfexe, count); 1971cb0ef41Sopenharmony_ci } 1981cb0ef41Sopenharmony_ci#endif // defined(__FreeBSD__) 1991cb0ef41Sopenharmony_ci 2001cb0ef41Sopenharmony_ci if (dl_iterate_phdr(FindMapping, &dl_params) == 1) { 2011cb0ef41Sopenharmony_ci Debug("start: %p - sym: %p - end: %p\n", 2021cb0ef41Sopenharmony_ci reinterpret_cast<void*>(dl_params.start), 2031cb0ef41Sopenharmony_ci reinterpret_cast<void*>(dl_params.reference_sym), 2041cb0ef41Sopenharmony_ci reinterpret_cast<void*>(dl_params.end)); 2051cb0ef41Sopenharmony_ci 2061cb0ef41Sopenharmony_ci dl_params.start = dl_params.reference_sym; 2071cb0ef41Sopenharmony_ci if (lpstub_start > dl_params.start && lpstub_start <= dl_params.end) { 2081cb0ef41Sopenharmony_ci Debug("Trimming end for lpstub: %p\n", 2091cb0ef41Sopenharmony_ci reinterpret_cast<void*>(lpstub_start)); 2101cb0ef41Sopenharmony_ci dl_params.end = lpstub_start; 2111cb0ef41Sopenharmony_ci } 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_ci if (dl_params.start < dl_params.end) { 2141cb0ef41Sopenharmony_ci char* from = reinterpret_cast<char*>(hugepage_align_up(dl_params.start)); 2151cb0ef41Sopenharmony_ci char* to = reinterpret_cast<char*>(hugepage_align_down(dl_params.end)); 2161cb0ef41Sopenharmony_ci Debug("Aligned range is %p - %p\n", from, to); 2171cb0ef41Sopenharmony_ci if (from < to) { 2181cb0ef41Sopenharmony_ci size_t pagecount = (to - from) / hps; 2191cb0ef41Sopenharmony_ci if (pagecount > 0) { 2201cb0ef41Sopenharmony_ci nregion.found_text_region = true; 2211cb0ef41Sopenharmony_ci nregion.from = from; 2221cb0ef41Sopenharmony_ci nregion.to = to; 2231cb0ef41Sopenharmony_ci } 2241cb0ef41Sopenharmony_ci } 2251cb0ef41Sopenharmony_ci } 2261cb0ef41Sopenharmony_ci } 2271cb0ef41Sopenharmony_ci#elif defined(__APPLE__) 2281cb0ef41Sopenharmony_ci struct vm_region_submap_info_64 map; 2291cb0ef41Sopenharmony_ci mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; 2301cb0ef41Sopenharmony_ci vm_address_t addr = 0UL; 2311cb0ef41Sopenharmony_ci vm_size_t size = 0; 2321cb0ef41Sopenharmony_ci natural_t depth = 1; 2331cb0ef41Sopenharmony_ci 2341cb0ef41Sopenharmony_ci while (true) { 2351cb0ef41Sopenharmony_ci if (vm_region_recurse_64(mach_task_self(), &addr, &size, &depth, 2361cb0ef41Sopenharmony_ci reinterpret_cast<vm_region_info_64_t>(&map), 2371cb0ef41Sopenharmony_ci &count) != KERN_SUCCESS) { 2381cb0ef41Sopenharmony_ci break; 2391cb0ef41Sopenharmony_ci } 2401cb0ef41Sopenharmony_ci 2411cb0ef41Sopenharmony_ci if (map.is_submap) { 2421cb0ef41Sopenharmony_ci depth++; 2431cb0ef41Sopenharmony_ci } else { 2441cb0ef41Sopenharmony_ci char* start = reinterpret_cast<char*>(hugepage_align_up(addr)); 2451cb0ef41Sopenharmony_ci char* end = reinterpret_cast<char*>(hugepage_align_down(addr+size)); 2461cb0ef41Sopenharmony_ci 2471cb0ef41Sopenharmony_ci if (end > start && (map.protection & VM_PROT_READ) != 0 && 2481cb0ef41Sopenharmony_ci (map.protection & VM_PROT_EXECUTE) != 0) { 2491cb0ef41Sopenharmony_ci nregion.found_text_region = true; 2501cb0ef41Sopenharmony_ci nregion.from = start; 2511cb0ef41Sopenharmony_ci nregion.to = end; 2521cb0ef41Sopenharmony_ci break; 2531cb0ef41Sopenharmony_ci } 2541cb0ef41Sopenharmony_ci 2551cb0ef41Sopenharmony_ci addr += size; 2561cb0ef41Sopenharmony_ci size = 0; 2571cb0ef41Sopenharmony_ci } 2581cb0ef41Sopenharmony_ci } 2591cb0ef41Sopenharmony_ci#endif 2601cb0ef41Sopenharmony_ci Debug("Found %d huge pages\n", (nregion.to - nregion.from) / hps); 2611cb0ef41Sopenharmony_ci return nregion; 2621cb0ef41Sopenharmony_ci} 2631cb0ef41Sopenharmony_ci 2641cb0ef41Sopenharmony_ci#if defined(__linux__) 2651cb0ef41Sopenharmony_cibool IsTransparentHugePagesEnabled() { 2661cb0ef41Sopenharmony_ci // File format reference: 2671cb0ef41Sopenharmony_ci // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/huge_memory.c?id=13391c60da3308ed9980de0168f74cce6c62ac1d#n163 2681cb0ef41Sopenharmony_ci const char* filename = "/sys/kernel/mm/transparent_hugepage/enabled"; 2691cb0ef41Sopenharmony_ci std::ifstream config_stream(filename, std::ios::in); 2701cb0ef41Sopenharmony_ci if (!config_stream.good()) { 2711cb0ef41Sopenharmony_ci PrintWarning("could not open /sys/kernel/mm/transparent_hugepage/enabled"); 2721cb0ef41Sopenharmony_ci return false; 2731cb0ef41Sopenharmony_ci } 2741cb0ef41Sopenharmony_ci 2751cb0ef41Sopenharmony_ci std::string token; 2761cb0ef41Sopenharmony_ci config_stream >> token; 2771cb0ef41Sopenharmony_ci if ("[always]" == token) return true; 2781cb0ef41Sopenharmony_ci config_stream >> token; 2791cb0ef41Sopenharmony_ci if ("[madvise]" == token) return true; 2801cb0ef41Sopenharmony_ci return false; 2811cb0ef41Sopenharmony_ci} 2821cb0ef41Sopenharmony_ci#elif defined(__FreeBSD__) 2831cb0ef41Sopenharmony_cibool IsSuperPagesEnabled() { 2841cb0ef41Sopenharmony_ci // It is enabled by default on amd64. 2851cb0ef41Sopenharmony_ci unsigned int super_pages = 0; 2861cb0ef41Sopenharmony_ci size_t super_pages_length = sizeof(super_pages); 2871cb0ef41Sopenharmony_ci return sysctlbyname("vm.pmap.pg_ps_enabled", 2881cb0ef41Sopenharmony_ci &super_pages, 2891cb0ef41Sopenharmony_ci &super_pages_length, 2901cb0ef41Sopenharmony_ci nullptr, 2911cb0ef41Sopenharmony_ci 0) != -1 && 2921cb0ef41Sopenharmony_ci super_pages >= 1; 2931cb0ef41Sopenharmony_ci} 2941cb0ef41Sopenharmony_ci#endif 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ci// Functions in this class must always be inlined because they must end up in 2971cb0ef41Sopenharmony_ci// the `lpstub` section rather than the `.text` section. 2981cb0ef41Sopenharmony_ciclass MemoryMapPointer { 2991cb0ef41Sopenharmony_ci public: 3001cb0ef41Sopenharmony_ci FORCE_INLINE explicit MemoryMapPointer() {} 3011cb0ef41Sopenharmony_ci FORCE_INLINE bool operator==(void* rhs) const { return mem_ == rhs; } 3021cb0ef41Sopenharmony_ci FORCE_INLINE void* mem() const { return mem_; } 3031cb0ef41Sopenharmony_ci MemoryMapPointer(const MemoryMapPointer&) = delete; 3041cb0ef41Sopenharmony_ci MemoryMapPointer(MemoryMapPointer&&) = delete; 3051cb0ef41Sopenharmony_ci void operator= (const MemoryMapPointer&) = delete; 3061cb0ef41Sopenharmony_ci void operator= (const MemoryMapPointer&&) = delete; 3071cb0ef41Sopenharmony_ci FORCE_INLINE void Reset(void* start, 3081cb0ef41Sopenharmony_ci size_t size, 3091cb0ef41Sopenharmony_ci int prot, 3101cb0ef41Sopenharmony_ci int flags, 3111cb0ef41Sopenharmony_ci int fd = -1, 3121cb0ef41Sopenharmony_ci size_t offset = 0) { 3131cb0ef41Sopenharmony_ci mem_ = mmap(start, size, prot, flags, fd, offset); 3141cb0ef41Sopenharmony_ci size_ = size; 3151cb0ef41Sopenharmony_ci } 3161cb0ef41Sopenharmony_ci FORCE_INLINE void Reset() { 3171cb0ef41Sopenharmony_ci mem_ = nullptr; 3181cb0ef41Sopenharmony_ci size_ = 0; 3191cb0ef41Sopenharmony_ci } 3201cb0ef41Sopenharmony_ci static void SetName(void* mem, size_t size, const char* name) { 3211cb0ef41Sopenharmony_ci#if defined(__linux__) 3221cb0ef41Sopenharmony_ci // Available since the 5.17 kernel release and if the 3231cb0ef41Sopenharmony_ci // CONFIG_ANON_VMA_NAME option, we can set an identifier 3241cb0ef41Sopenharmony_ci // to an anonymous mapped region. However if the kernel 3251cb0ef41Sopenharmony_ci // option is not present or it s an older kernel, it is a no-op. 3261cb0ef41Sopenharmony_ci if (mem != MAP_FAILED && mem != nullptr) 3271cb0ef41Sopenharmony_ci prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, 3281cb0ef41Sopenharmony_ci reinterpret_cast<uintptr_t>(mem), 3291cb0ef41Sopenharmony_ci size, 3301cb0ef41Sopenharmony_ci reinterpret_cast<uintptr_t>(name)); 3311cb0ef41Sopenharmony_ci#else 3321cb0ef41Sopenharmony_ci (void)name; 3331cb0ef41Sopenharmony_ci#endif 3341cb0ef41Sopenharmony_ci } 3351cb0ef41Sopenharmony_ci FORCE_INLINE ~MemoryMapPointer() { 3361cb0ef41Sopenharmony_ci if (mem_ == nullptr) return; 3371cb0ef41Sopenharmony_ci if (mem_ == MAP_FAILED) return; 3381cb0ef41Sopenharmony_ci if (munmap(mem_, size_) == 0) return; 3391cb0ef41Sopenharmony_ci PrintSystemError(errno); 3401cb0ef41Sopenharmony_ci } 3411cb0ef41Sopenharmony_ci 3421cb0ef41Sopenharmony_ci private: 3431cb0ef41Sopenharmony_ci size_t size_ = 0; 3441cb0ef41Sopenharmony_ci void* mem_ = nullptr; 3451cb0ef41Sopenharmony_ci}; 3461cb0ef41Sopenharmony_ci 3471cb0ef41Sopenharmony_ci} // End of anonymous namespace 3481cb0ef41Sopenharmony_ci 3491cb0ef41Sopenharmony_ciint 3501cb0ef41Sopenharmony_ci#if !defined(__APPLE__) 3511cb0ef41Sopenharmony_ci__attribute__((__section__("lpstub"))) 3521cb0ef41Sopenharmony_ci#else 3531cb0ef41Sopenharmony_ci__attribute__((__section__("__TEXT,__lpstub"))) 3541cb0ef41Sopenharmony_ci#endif 3551cb0ef41Sopenharmony_ci__attribute__((__aligned__(hps))) 3561cb0ef41Sopenharmony_ci__attribute__((__noinline__)) 3571cb0ef41Sopenharmony_ciMoveTextRegionToLargePages(const text_region& r) { 3581cb0ef41Sopenharmony_ci MemoryMapPointer nmem; 3591cb0ef41Sopenharmony_ci MemoryMapPointer tmem; 3601cb0ef41Sopenharmony_ci void* start = r.from; 3611cb0ef41Sopenharmony_ci size_t size = r.to - r.from; 3621cb0ef41Sopenharmony_ci 3631cb0ef41Sopenharmony_ci // Allocate a temporary region and back up the code we will re-map. 3641cb0ef41Sopenharmony_ci nmem.Reset(nullptr, size, 3651cb0ef41Sopenharmony_ci PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS); 3661cb0ef41Sopenharmony_ci if (nmem.mem() == MAP_FAILED) goto fail; 3671cb0ef41Sopenharmony_ci memcpy(nmem.mem(), r.from, size); 3681cb0ef41Sopenharmony_ci 3691cb0ef41Sopenharmony_ci#if defined(__linux__) 3701cb0ef41Sopenharmony_ci// We already know the original page is r-xp 3711cb0ef41Sopenharmony_ci// (PROT_READ, PROT_EXEC, MAP_PRIVATE) 3721cb0ef41Sopenharmony_ci// We want PROT_WRITE because we are writing into it. 3731cb0ef41Sopenharmony_ci// We want it at the fixed address and we use MAP_FIXED. 3741cb0ef41Sopenharmony_ci tmem.Reset(start, size, 3751cb0ef41Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 3761cb0ef41Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED); 3771cb0ef41Sopenharmony_ci if (tmem.mem() == MAP_FAILED) goto fail; 3781cb0ef41Sopenharmony_ci if (madvise(tmem.mem(), size, 14 /* MADV_HUGEPAGE */) == -1) goto fail; 3791cb0ef41Sopenharmony_ci memcpy(start, nmem.mem(), size); 3801cb0ef41Sopenharmony_ci#elif defined(__FreeBSD__) 3811cb0ef41Sopenharmony_ci tmem.Reset(start, size, 3821cb0ef41Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 3831cb0ef41Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | 3841cb0ef41Sopenharmony_ci MAP_ALIGNED_SUPER); 3851cb0ef41Sopenharmony_ci if (tmem.mem() == MAP_FAILED) goto fail; 3861cb0ef41Sopenharmony_ci memcpy(start, nmem.mem(), size); 3871cb0ef41Sopenharmony_ci#elif defined(__APPLE__) 3881cb0ef41Sopenharmony_ci // There is not enough room to reserve the mapping close 3891cb0ef41Sopenharmony_ci // to the region address so we content to give a hint 3901cb0ef41Sopenharmony_ci // without forcing the new address being closed to. 3911cb0ef41Sopenharmony_ci // We explicitally gives all permission since we plan 3921cb0ef41Sopenharmony_ci // to write into it. 3931cb0ef41Sopenharmony_ci tmem.Reset(start, size, 3941cb0ef41Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 3951cb0ef41Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS, 3961cb0ef41Sopenharmony_ci VM_FLAGS_SUPERPAGE_SIZE_2MB); 3971cb0ef41Sopenharmony_ci if (tmem.mem() == MAP_FAILED) goto fail; 3981cb0ef41Sopenharmony_ci memcpy(tmem.mem(), nmem.mem(), size); 3991cb0ef41Sopenharmony_ci if (mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) 4001cb0ef41Sopenharmony_ci goto fail; 4011cb0ef41Sopenharmony_ci memcpy(start, tmem.mem(), size); 4021cb0ef41Sopenharmony_ci#endif 4031cb0ef41Sopenharmony_ci 4041cb0ef41Sopenharmony_ci if (mprotect(start, size, PROT_READ | PROT_EXEC) == -1) goto fail; 4051cb0ef41Sopenharmony_ci MemoryMapPointer::SetName(start, size, "nodejs Large Page"); 4061cb0ef41Sopenharmony_ci 4071cb0ef41Sopenharmony_ci // We need not `munmap(tmem, size)` on success. 4081cb0ef41Sopenharmony_ci tmem.Reset(); 4091cb0ef41Sopenharmony_ci return 0; 4101cb0ef41Sopenharmony_cifail: 4111cb0ef41Sopenharmony_ci PrintSystemError(errno); 4121cb0ef41Sopenharmony_ci return -1; 4131cb0ef41Sopenharmony_ci} 4141cb0ef41Sopenharmony_ci#endif // defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES 4151cb0ef41Sopenharmony_ci 4161cb0ef41Sopenharmony_ci// This is the primary API called from main. 4171cb0ef41Sopenharmony_ciint MapStaticCodeToLargePages() { 4181cb0ef41Sopenharmony_ci#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES 4191cb0ef41Sopenharmony_ci bool have_thp = false; 4201cb0ef41Sopenharmony_ci#if defined(__linux__) 4211cb0ef41Sopenharmony_ci have_thp = IsTransparentHugePagesEnabled(); 4221cb0ef41Sopenharmony_ci#elif defined(__FreeBSD__) 4231cb0ef41Sopenharmony_ci have_thp = IsSuperPagesEnabled(); 4241cb0ef41Sopenharmony_ci#elif defined(__APPLE__) 4251cb0ef41Sopenharmony_ci // pse-36 flag is present in recent mac x64 products. 4261cb0ef41Sopenharmony_ci have_thp = true; 4271cb0ef41Sopenharmony_ci#endif 4281cb0ef41Sopenharmony_ci if (!have_thp) 4291cb0ef41Sopenharmony_ci return EACCES; 4301cb0ef41Sopenharmony_ci 4311cb0ef41Sopenharmony_ci struct text_region r = FindNodeTextRegion(); 4321cb0ef41Sopenharmony_ci if (r.found_text_region == false) 4331cb0ef41Sopenharmony_ci return ENOENT; 4341cb0ef41Sopenharmony_ci 4351cb0ef41Sopenharmony_ci return MoveTextRegionToLargePages(r); 4361cb0ef41Sopenharmony_ci#else 4371cb0ef41Sopenharmony_ci return ENOTSUP; 4381cb0ef41Sopenharmony_ci#endif 4391cb0ef41Sopenharmony_ci} 4401cb0ef41Sopenharmony_ci 4411cb0ef41Sopenharmony_ciconst char* LargePagesError(int status) { 4421cb0ef41Sopenharmony_ci switch (status) { 4431cb0ef41Sopenharmony_ci case ENOTSUP: 4441cb0ef41Sopenharmony_ci return "Mapping to large pages is not supported."; 4451cb0ef41Sopenharmony_ci 4461cb0ef41Sopenharmony_ci case EACCES: 4471cb0ef41Sopenharmony_ci return "Large pages are not enabled."; 4481cb0ef41Sopenharmony_ci 4491cb0ef41Sopenharmony_ci case ENOENT: 4501cb0ef41Sopenharmony_ci return "failed to find text region"; 4511cb0ef41Sopenharmony_ci 4521cb0ef41Sopenharmony_ci case -1: 4531cb0ef41Sopenharmony_ci return "Mapping code to large pages failed. Reverting to default page " 4541cb0ef41Sopenharmony_ci "size."; 4551cb0ef41Sopenharmony_ci 4561cb0ef41Sopenharmony_ci case 0: 4571cb0ef41Sopenharmony_ci return "OK"; 4581cb0ef41Sopenharmony_ci 4591cb0ef41Sopenharmony_ci default: 4601cb0ef41Sopenharmony_ci return "Unknown error"; 4611cb0ef41Sopenharmony_ci } 4621cb0ef41Sopenharmony_ci} 4631cb0ef41Sopenharmony_ci 4641cb0ef41Sopenharmony_ci} // namespace node 465