1d0e79aa3SJason Evans #define JEMALLOC_PAGES_C_ 2b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_preamble.h" 3b7eaed25SJason Evans 4b7eaed25SJason Evans #include "jemalloc/internal/pages.h" 5b7eaed25SJason Evans 6b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_internal_includes.h" 7b7eaed25SJason Evans 8b7eaed25SJason Evans #include "jemalloc/internal/assert.h" 9b7eaed25SJason Evans #include "jemalloc/internal/malloc_io.h" 10d0e79aa3SJason Evans 111f0a49e8SJason Evans #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT 121f0a49e8SJason Evans #include <sys/sysctl.h> 130ef50b4eSJason Evans #ifdef __FreeBSD__ 14*0ae364adSKonstantin Belousov #include <sys/auxv.h> 150ef50b4eSJason Evans #include <vm/vm_param.h> 1687384c51SKonstantin Belousov #include <vm/vm.h> 170ef50b4eSJason Evans #endif 181f0a49e8SJason Evans #endif 191f0a49e8SJason Evans 201f0a49e8SJason Evans /******************************************************************************/ 211f0a49e8SJason Evans /* Data. */ 221f0a49e8SJason Evans 23b7eaed25SJason Evans /* Actual operating system page size, detected during bootstrap, <= PAGE. */ 24b7eaed25SJason Evans static size_t os_page; 25b7eaed25SJason Evans 261f0a49e8SJason Evans #ifndef _WIN32 271f0a49e8SJason Evans # define PAGES_PROT_COMMIT (PROT_READ | PROT_WRITE) 281f0a49e8SJason Evans # define PAGES_PROT_DECOMMIT (PROT_NONE) 291f0a49e8SJason Evans static int mmap_flags; 301f0a49e8SJason Evans #endif 311f0a49e8SJason Evans static bool os_overcommits; 321f0a49e8SJason Evans 330ef50b4eSJason Evans const char *thp_mode_names[] = { 340ef50b4eSJason Evans "default", 350ef50b4eSJason Evans "always", 360ef50b4eSJason Evans "never", 370ef50b4eSJason Evans "not supported" 380ef50b4eSJason Evans }; 390ef50b4eSJason Evans thp_mode_t opt_thp = THP_MODE_DEFAULT; 400ef50b4eSJason Evans thp_mode_t init_system_thp_mode; 410ef50b4eSJason Evans 420ef50b4eSJason Evans /* Runtime support for lazy purge. Irrelevant when !pages_can_purge_lazy. */ 430ef50b4eSJason Evans static bool pages_can_purge_lazy_runtime = true; 440ef50b4eSJason Evans 45d0e79aa3SJason Evans /******************************************************************************/ 46b7eaed25SJason Evans /* 47b7eaed25SJason Evans * Function prototypes for static functions that are referenced prior to 48b7eaed25SJason Evans * definition. 49b7eaed25SJason Evans */ 50d0e79aa3SJason Evans 51b7eaed25SJason Evans static void os_pages_unmap(void *addr, size_t size); 52d0e79aa3SJason Evans 53b7eaed25SJason Evans /******************************************************************************/ 54b7eaed25SJason Evans 55b7eaed25SJason Evans static void * 56b7eaed25SJason Evans os_pages_map(void *addr, size_t size, size_t alignment, bool *commit) { 57b7eaed25SJason Evans assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr); 58b7eaed25SJason Evans assert(ALIGNMENT_CEILING(size, os_page) == size); 59d0e79aa3SJason Evans assert(size != 0); 60d0e79aa3SJason Evans 61b7eaed25SJason Evans if (os_overcommits) { 621f0a49e8SJason Evans *commit = true; 63b7eaed25SJason Evans } 641f0a49e8SJason Evans 65b7eaed25SJason Evans void *ret; 66d0e79aa3SJason Evans #ifdef _WIN32 67d0e79aa3SJason Evans /* 68d0e79aa3SJason Evans * If VirtualAlloc can't allocate at the given address when one is 69d0e79aa3SJason Evans * given, it fails and returns NULL. 70d0e79aa3SJason Evans */ 711f0a49e8SJason Evans ret = VirtualAlloc(addr, size, MEM_RESERVE | (*commit ? MEM_COMMIT : 0), 72d0e79aa3SJason Evans PAGE_READWRITE); 73d0e79aa3SJason Evans #else 74d0e79aa3SJason Evans /* 75d0e79aa3SJason Evans * We don't use MAP_FIXED here, because it can cause the *replacement* 76d0e79aa3SJason Evans * of existing mappings, and we only want to create new mappings. 77d0e79aa3SJason Evans */ 781f0a49e8SJason Evans { 791f0a49e8SJason Evans int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; 801f0a49e8SJason Evans 811f0a49e8SJason Evans ret = mmap(addr, size, prot, mmap_flags, -1, 0); 821f0a49e8SJason Evans } 83d0e79aa3SJason Evans assert(ret != NULL); 84d0e79aa3SJason Evans 85b7eaed25SJason Evans if (ret == MAP_FAILED) { 86d0e79aa3SJason Evans ret = NULL; 87b7eaed25SJason Evans } else if (addr != NULL && ret != addr) { 88d0e79aa3SJason Evans /* 89d0e79aa3SJason Evans * We succeeded in mapping memory, but not in the right place. 90d0e79aa3SJason Evans */ 91b7eaed25SJason Evans os_pages_unmap(ret, size); 92d0e79aa3SJason Evans ret = NULL; 93d0e79aa3SJason Evans } 94d0e79aa3SJason Evans #endif 95b7eaed25SJason Evans assert(ret == NULL || (addr == NULL && ret != addr) || (addr != NULL && 96b7eaed25SJason Evans ret == addr)); 97b7eaed25SJason Evans return ret; 98d0e79aa3SJason Evans } 99d0e79aa3SJason Evans 100b7eaed25SJason Evans static void * 101b7eaed25SJason Evans os_pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size, 102b7eaed25SJason Evans bool *commit) { 103b7eaed25SJason Evans void *ret = (void *)((uintptr_t)addr + leadsize); 104b7eaed25SJason Evans 105b7eaed25SJason Evans assert(alloc_size >= leadsize + size); 106b7eaed25SJason Evans #ifdef _WIN32 107b7eaed25SJason Evans os_pages_unmap(addr, alloc_size); 108b7eaed25SJason Evans void *new_addr = os_pages_map(ret, size, PAGE, commit); 109b7eaed25SJason Evans if (new_addr == ret) { 110b7eaed25SJason Evans return ret; 111b7eaed25SJason Evans } 112b7eaed25SJason Evans if (new_addr != NULL) { 113b7eaed25SJason Evans os_pages_unmap(new_addr, size); 114b7eaed25SJason Evans } 115b7eaed25SJason Evans return NULL; 116b7eaed25SJason Evans #else 117b7eaed25SJason Evans size_t trailsize = alloc_size - leadsize - size; 118b7eaed25SJason Evans 119b7eaed25SJason Evans if (leadsize != 0) { 120b7eaed25SJason Evans os_pages_unmap(addr, leadsize); 121b7eaed25SJason Evans } 122b7eaed25SJason Evans if (trailsize != 0) { 123b7eaed25SJason Evans os_pages_unmap((void *)((uintptr_t)ret + size), trailsize); 124b7eaed25SJason Evans } 125b7eaed25SJason Evans return ret; 126b7eaed25SJason Evans #endif 127b7eaed25SJason Evans } 128b7eaed25SJason Evans 129b7eaed25SJason Evans static void 130b7eaed25SJason Evans os_pages_unmap(void *addr, size_t size) { 131b7eaed25SJason Evans assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr); 132b7eaed25SJason Evans assert(ALIGNMENT_CEILING(size, os_page) == size); 133d0e79aa3SJason Evans 134d0e79aa3SJason Evans #ifdef _WIN32 135d0e79aa3SJason Evans if (VirtualFree(addr, 0, MEM_RELEASE) == 0) 136d0e79aa3SJason Evans #else 137d0e79aa3SJason Evans if (munmap(addr, size) == -1) 138d0e79aa3SJason Evans #endif 139d0e79aa3SJason Evans { 140d0e79aa3SJason Evans char buf[BUFERROR_BUF]; 141d0e79aa3SJason Evans 142d0e79aa3SJason Evans buferror(get_errno(), buf, sizeof(buf)); 143d0e79aa3SJason Evans malloc_printf("<jemalloc>: Error in " 144d0e79aa3SJason Evans #ifdef _WIN32 145d0e79aa3SJason Evans "VirtualFree" 146d0e79aa3SJason Evans #else 147d0e79aa3SJason Evans "munmap" 148d0e79aa3SJason Evans #endif 149d0e79aa3SJason Evans "(): %s\n", buf); 150b7eaed25SJason Evans if (opt_abort) { 151d0e79aa3SJason Evans abort(); 152d0e79aa3SJason Evans } 153d0e79aa3SJason Evans } 154b7eaed25SJason Evans } 155b7eaed25SJason Evans 156b7eaed25SJason Evans static void * 157b7eaed25SJason Evans pages_map_slow(size_t size, size_t alignment, bool *commit) { 158b7eaed25SJason Evans size_t alloc_size = size + alignment - os_page; 159b7eaed25SJason Evans /* Beware size_t wrap-around. */ 160b7eaed25SJason Evans if (alloc_size < size) { 161b7eaed25SJason Evans return NULL; 162b7eaed25SJason Evans } 163b7eaed25SJason Evans 164b7eaed25SJason Evans void *ret; 165b7eaed25SJason Evans do { 166b7eaed25SJason Evans void *pages = os_pages_map(NULL, alloc_size, alignment, commit); 167b7eaed25SJason Evans if (pages == NULL) { 168b7eaed25SJason Evans return NULL; 169b7eaed25SJason Evans } 170b7eaed25SJason Evans size_t leadsize = ALIGNMENT_CEILING((uintptr_t)pages, alignment) 171b7eaed25SJason Evans - (uintptr_t)pages; 172b7eaed25SJason Evans ret = os_pages_trim(pages, alloc_size, leadsize, size, commit); 173b7eaed25SJason Evans } while (ret == NULL); 174b7eaed25SJason Evans 175b7eaed25SJason Evans assert(ret != NULL); 176b7eaed25SJason Evans assert(PAGE_ADDR2BASE(ret) == ret); 177b7eaed25SJason Evans return ret; 178b7eaed25SJason Evans } 179d0e79aa3SJason Evans 180d0e79aa3SJason Evans void * 181b7eaed25SJason Evans pages_map(void *addr, size_t size, size_t alignment, bool *commit) { 182b7eaed25SJason Evans assert(alignment >= PAGE); 183b7eaed25SJason Evans assert(ALIGNMENT_ADDR2BASE(addr, alignment) == addr); 184d0e79aa3SJason Evans 1850c885e27SEdward Tomasz Napierala #if defined(__FreeBSD__) && defined(MAP_EXCL) 1860c885e27SEdward Tomasz Napierala /* 1870c885e27SEdward Tomasz Napierala * FreeBSD has mechanisms both to mmap at specific address without 1880c885e27SEdward Tomasz Napierala * touching existing mappings, and to mmap with specific alignment. 1890c885e27SEdward Tomasz Napierala */ 1900c885e27SEdward Tomasz Napierala { 191086165ecSEdward Tomasz Napierala if (os_overcommits) { 192086165ecSEdward Tomasz Napierala *commit = true; 193086165ecSEdward Tomasz Napierala } 194086165ecSEdward Tomasz Napierala 1950c885e27SEdward Tomasz Napierala int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; 1960c885e27SEdward Tomasz Napierala int flags = mmap_flags; 1970c885e27SEdward Tomasz Napierala 1980c885e27SEdward Tomasz Napierala if (addr != NULL) { 1990c885e27SEdward Tomasz Napierala flags |= MAP_FIXED | MAP_EXCL; 2000c885e27SEdward Tomasz Napierala } else { 2010c885e27SEdward Tomasz Napierala unsigned alignment_bits = ffs_zu(alignment); 2020c885e27SEdward Tomasz Napierala assert(alignment_bits > 1); 2030c885e27SEdward Tomasz Napierala flags |= MAP_ALIGNED(alignment_bits - 1); 2040c885e27SEdward Tomasz Napierala } 2050c885e27SEdward Tomasz Napierala 2060c885e27SEdward Tomasz Napierala void *ret = mmap(addr, size, prot, flags, -1, 0); 2070c885e27SEdward Tomasz Napierala if (ret == MAP_FAILED) { 2080c885e27SEdward Tomasz Napierala ret = NULL; 2090c885e27SEdward Tomasz Napierala } 2100c885e27SEdward Tomasz Napierala 2110c885e27SEdward Tomasz Napierala return ret; 2120c885e27SEdward Tomasz Napierala } 2130c885e27SEdward Tomasz Napierala #endif 214b7eaed25SJason Evans /* 215b7eaed25SJason Evans * Ideally, there would be a way to specify alignment to mmap() (like 216b7eaed25SJason Evans * NetBSD has), but in the absence of such a feature, we have to work 217b7eaed25SJason Evans * hard to efficiently create aligned mappings. The reliable, but 218b7eaed25SJason Evans * slow method is to create a mapping that is over-sized, then trim the 219b7eaed25SJason Evans * excess. However, that always results in one or two calls to 220b7eaed25SJason Evans * os_pages_unmap(), and it can leave holes in the process's virtual 221b7eaed25SJason Evans * memory map if memory grows downward. 222b7eaed25SJason Evans * 223b7eaed25SJason Evans * Optimistically try mapping precisely the right amount before falling 224b7eaed25SJason Evans * back to the slow method, with the expectation that the optimistic 225b7eaed25SJason Evans * approach works most of the time. 226b7eaed25SJason Evans */ 227d0e79aa3SJason Evans 228b7eaed25SJason Evans void *ret = os_pages_map(addr, size, os_page, commit); 229b7eaed25SJason Evans if (ret == NULL || ret == addr) { 230b7eaed25SJason Evans return ret; 231d0e79aa3SJason Evans } 232b7eaed25SJason Evans assert(addr == NULL); 233b7eaed25SJason Evans if (ALIGNMENT_ADDR2OFFSET(ret, alignment) != 0) { 234b7eaed25SJason Evans os_pages_unmap(ret, size); 235b7eaed25SJason Evans return pages_map_slow(size, alignment, commit); 236d0e79aa3SJason Evans } 237b7eaed25SJason Evans 238b7eaed25SJason Evans assert(PAGE_ADDR2BASE(ret) == ret); 239b7eaed25SJason Evans return ret; 240b7eaed25SJason Evans } 241b7eaed25SJason Evans 242b7eaed25SJason Evans void 243b7eaed25SJason Evans pages_unmap(void *addr, size_t size) { 244b7eaed25SJason Evans assert(PAGE_ADDR2BASE(addr) == addr); 245b7eaed25SJason Evans assert(PAGE_CEILING(size) == size); 246b7eaed25SJason Evans 247b7eaed25SJason Evans os_pages_unmap(addr, size); 248d0e79aa3SJason Evans } 249d0e79aa3SJason Evans 250d0e79aa3SJason Evans static bool 251b7eaed25SJason Evans pages_commit_impl(void *addr, size_t size, bool commit) { 252b7eaed25SJason Evans assert(PAGE_ADDR2BASE(addr) == addr); 253b7eaed25SJason Evans assert(PAGE_CEILING(size) == size); 254d0e79aa3SJason Evans 255b7eaed25SJason Evans if (os_overcommits) { 256b7eaed25SJason Evans return true; 257b7eaed25SJason Evans } 2581f0a49e8SJason Evans 2591f0a49e8SJason Evans #ifdef _WIN32 2601f0a49e8SJason Evans return (commit ? (addr != VirtualAlloc(addr, size, MEM_COMMIT, 2611f0a49e8SJason Evans PAGE_READWRITE)) : (!VirtualFree(addr, size, MEM_DECOMMIT))); 2621f0a49e8SJason Evans #else 2631f0a49e8SJason Evans { 2641f0a49e8SJason Evans int prot = commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; 2651f0a49e8SJason Evans void *result = mmap(addr, size, prot, mmap_flags | MAP_FIXED, 2661f0a49e8SJason Evans -1, 0); 267b7eaed25SJason Evans if (result == MAP_FAILED) { 268b7eaed25SJason Evans return true; 269b7eaed25SJason Evans } 270d0e79aa3SJason Evans if (result != addr) { 271d0e79aa3SJason Evans /* 272d0e79aa3SJason Evans * We succeeded in mapping memory, but not in the right 273d0e79aa3SJason Evans * place. 274d0e79aa3SJason Evans */ 275b7eaed25SJason Evans os_pages_unmap(result, size); 276b7eaed25SJason Evans return true; 277d0e79aa3SJason Evans } 278b7eaed25SJason Evans return false; 279d0e79aa3SJason Evans } 280d0e79aa3SJason Evans #endif 281d0e79aa3SJason Evans } 282d0e79aa3SJason Evans 283d0e79aa3SJason Evans bool 284b7eaed25SJason Evans pages_commit(void *addr, size_t size) { 285b7eaed25SJason Evans return pages_commit_impl(addr, size, true); 286d0e79aa3SJason Evans } 287d0e79aa3SJason Evans 288d0e79aa3SJason Evans bool 289b7eaed25SJason Evans pages_decommit(void *addr, size_t size) { 290b7eaed25SJason Evans return pages_commit_impl(addr, size, false); 291d0e79aa3SJason Evans } 292d0e79aa3SJason Evans 293d0e79aa3SJason Evans bool 294b7eaed25SJason Evans pages_purge_lazy(void *addr, size_t size) { 295c5ad8142SEric van Gyzen assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr); 296b7eaed25SJason Evans assert(PAGE_CEILING(size) == size); 297b7eaed25SJason Evans 298b7eaed25SJason Evans if (!pages_can_purge_lazy) { 299b7eaed25SJason Evans return true; 300b7eaed25SJason Evans } 3010ef50b4eSJason Evans if (!pages_can_purge_lazy_runtime) { 3020ef50b4eSJason Evans /* 3030ef50b4eSJason Evans * Built with lazy purge enabled, but detected it was not 3040ef50b4eSJason Evans * supported on the current system. 3050ef50b4eSJason Evans */ 3060ef50b4eSJason Evans return true; 3070ef50b4eSJason Evans } 308d0e79aa3SJason Evans 309d0e79aa3SJason Evans #ifdef _WIN32 310d0e79aa3SJason Evans VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE); 311b7eaed25SJason Evans return false; 312b7eaed25SJason Evans #elif defined(JEMALLOC_PURGE_MADVISE_FREE) 3130ef50b4eSJason Evans return (madvise(addr, size, 3140ef50b4eSJason Evans # ifdef MADV_FREE 3150ef50b4eSJason Evans MADV_FREE 3160ef50b4eSJason Evans # else 3170ef50b4eSJason Evans JEMALLOC_MADV_FREE 3180ef50b4eSJason Evans # endif 3190ef50b4eSJason Evans ) != 0); 320b7eaed25SJason Evans #elif defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \ 321b7eaed25SJason Evans !defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS) 322b7eaed25SJason Evans return (madvise(addr, size, MADV_DONTNEED) != 0); 323d0e79aa3SJason Evans #else 324b7eaed25SJason Evans not_reached(); 325d0e79aa3SJason Evans #endif 326d0e79aa3SJason Evans } 327d0e79aa3SJason Evans 3287fa7f12fSJason Evans bool 329b7eaed25SJason Evans pages_purge_forced(void *addr, size_t size) { 3307fa7f12fSJason Evans assert(PAGE_ADDR2BASE(addr) == addr); 3317fa7f12fSJason Evans assert(PAGE_CEILING(size) == size); 3327fa7f12fSJason Evans 333b7eaed25SJason Evans if (!pages_can_purge_forced) { 334b7eaed25SJason Evans return true; 335b7eaed25SJason Evans } 336b7eaed25SJason Evans 337b7eaed25SJason Evans #if defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \ 338b7eaed25SJason Evans defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS) 339b7eaed25SJason Evans return (madvise(addr, size, MADV_DONTNEED) != 0); 340b7eaed25SJason Evans #elif defined(JEMALLOC_MAPS_COALESCE) 341b7eaed25SJason Evans /* Try to overlay a new demand-zeroed mapping. */ 342b7eaed25SJason Evans return pages_commit(addr, size); 343b7eaed25SJason Evans #else 344b7eaed25SJason Evans not_reached(); 345b7eaed25SJason Evans #endif 346b7eaed25SJason Evans } 347b7eaed25SJason Evans 3480ef50b4eSJason Evans static bool 3490ef50b4eSJason Evans pages_huge_impl(void *addr, size_t size, bool aligned) { 3500ef50b4eSJason Evans if (aligned) { 351b7eaed25SJason Evans assert(HUGEPAGE_ADDR2BASE(addr) == addr); 352b7eaed25SJason Evans assert(HUGEPAGE_CEILING(size) == size); 3530ef50b4eSJason Evans } 3540ef50b4eSJason Evans #ifdef JEMALLOC_HAVE_MADVISE_HUGE 3557fa7f12fSJason Evans return (madvise(addr, size, MADV_HUGEPAGE) != 0); 3567fa7f12fSJason Evans #else 357b7eaed25SJason Evans return true; 3587fa7f12fSJason Evans #endif 3597fa7f12fSJason Evans } 3607fa7f12fSJason Evans 3617fa7f12fSJason Evans bool 3620ef50b4eSJason Evans pages_huge(void *addr, size_t size) { 3630ef50b4eSJason Evans return pages_huge_impl(addr, size, true); 3640ef50b4eSJason Evans } 3650ef50b4eSJason Evans 3660ef50b4eSJason Evans static bool 3670ef50b4eSJason Evans pages_huge_unaligned(void *addr, size_t size) { 3680ef50b4eSJason Evans return pages_huge_impl(addr, size, false); 3690ef50b4eSJason Evans } 3700ef50b4eSJason Evans 3710ef50b4eSJason Evans static bool 3720ef50b4eSJason Evans pages_nohuge_impl(void *addr, size_t size, bool aligned) { 3730ef50b4eSJason Evans if (aligned) { 374b7eaed25SJason Evans assert(HUGEPAGE_ADDR2BASE(addr) == addr); 375b7eaed25SJason Evans assert(HUGEPAGE_CEILING(size) == size); 3760ef50b4eSJason Evans } 3777fa7f12fSJason Evans 3780ef50b4eSJason Evans #ifdef JEMALLOC_HAVE_MADVISE_HUGE 3797fa7f12fSJason Evans return (madvise(addr, size, MADV_NOHUGEPAGE) != 0); 3807fa7f12fSJason Evans #else 381b7eaed25SJason Evans return false; 382b7eaed25SJason Evans #endif 383b7eaed25SJason Evans } 384b7eaed25SJason Evans 3850ef50b4eSJason Evans bool 3860ef50b4eSJason Evans pages_nohuge(void *addr, size_t size) { 3870ef50b4eSJason Evans return pages_nohuge_impl(addr, size, true); 3880ef50b4eSJason Evans } 3890ef50b4eSJason Evans 3900ef50b4eSJason Evans static bool 3910ef50b4eSJason Evans pages_nohuge_unaligned(void *addr, size_t size) { 3920ef50b4eSJason Evans return pages_nohuge_impl(addr, size, false); 3930ef50b4eSJason Evans } 3940ef50b4eSJason Evans 3950ef50b4eSJason Evans bool 3960ef50b4eSJason Evans pages_dontdump(void *addr, size_t size) { 3970ef50b4eSJason Evans assert(PAGE_ADDR2BASE(addr) == addr); 3980ef50b4eSJason Evans assert(PAGE_CEILING(size) == size); 3990ef50b4eSJason Evans #ifdef JEMALLOC_MADVISE_DONTDUMP 4000ef50b4eSJason Evans return madvise(addr, size, MADV_DONTDUMP) != 0; 4010ef50b4eSJason Evans #else 4020ef50b4eSJason Evans return false; 4030ef50b4eSJason Evans #endif 4040ef50b4eSJason Evans } 4050ef50b4eSJason Evans 4060ef50b4eSJason Evans bool 4070ef50b4eSJason Evans pages_dodump(void *addr, size_t size) { 4080ef50b4eSJason Evans assert(PAGE_ADDR2BASE(addr) == addr); 4090ef50b4eSJason Evans assert(PAGE_CEILING(size) == size); 4100ef50b4eSJason Evans #ifdef JEMALLOC_MADVISE_DONTDUMP 4110ef50b4eSJason Evans return madvise(addr, size, MADV_DODUMP) != 0; 4120ef50b4eSJason Evans #else 4130ef50b4eSJason Evans return false; 4140ef50b4eSJason Evans #endif 4150ef50b4eSJason Evans } 4160ef50b4eSJason Evans 4170ef50b4eSJason Evans 418b7eaed25SJason Evans static size_t 419b7eaed25SJason Evans os_page_detect(void) { 420b7eaed25SJason Evans #ifdef _WIN32 421b7eaed25SJason Evans SYSTEM_INFO si; 422b7eaed25SJason Evans GetSystemInfo(&si); 423b7eaed25SJason Evans return si.dwPageSize; 4240ef50b4eSJason Evans #elif defined(__FreeBSD__) 425c5ad8142SEric van Gyzen /* 426c5ad8142SEric van Gyzen * This returns the value obtained from 427c5ad8142SEric van Gyzen * the auxv vector, avoiding a syscall. 428c5ad8142SEric van Gyzen */ 4290ef50b4eSJason Evans return getpagesize(); 430b7eaed25SJason Evans #else 431b7eaed25SJason Evans long result = sysconf(_SC_PAGESIZE); 432b7eaed25SJason Evans if (result == -1) { 433b7eaed25SJason Evans return LG_PAGE; 434b7eaed25SJason Evans } 435b7eaed25SJason Evans return (size_t)result; 4367fa7f12fSJason Evans #endif 4377fa7f12fSJason Evans } 4387fa7f12fSJason Evans 4391f0a49e8SJason Evans #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT 4401f0a49e8SJason Evans static bool 441b7eaed25SJason Evans os_overcommits_sysctl(void) { 4421f0a49e8SJason Evans int vm_overcommit; 4431f0a49e8SJason Evans size_t sz; 444*0ae364adSKonstantin Belousov int bsdflags; 445*0ae364adSKonstantin Belousov 446*0ae364adSKonstantin Belousov if (_elf_aux_info(AT_BSDFLAGS, &bsdflags, sizeof(bsdflags)) == 0) 447*0ae364adSKonstantin Belousov return ((bsdflags & ELF_BSDF_VMNOOVERCOMMIT) == 0); 448*0ae364adSKonstantin Belousov 4491f0a49e8SJason Evans sz = sizeof(vm_overcommit); 4500ef50b4eSJason Evans #if defined(__FreeBSD__) && defined(VM_OVERCOMMIT) 4510ef50b4eSJason Evans int mib[2]; 4520ef50b4eSJason Evans 4530ef50b4eSJason Evans mib[0] = CTL_VM; 4540ef50b4eSJason Evans mib[1] = VM_OVERCOMMIT; 4550ef50b4eSJason Evans if (sysctl(mib, 2, &vm_overcommit, &sz, NULL, 0) != 0) { 4560ef50b4eSJason Evans return false; /* Error. */ 4570ef50b4eSJason Evans } 4580ef50b4eSJason Evans #else 459b7eaed25SJason Evans if (sysctlbyname("vm.overcommit", &vm_overcommit, &sz, NULL, 0) != 0) { 460b7eaed25SJason Evans return false; /* Error. */ 461b7eaed25SJason Evans } 4620ef50b4eSJason Evans #endif 4631f0a49e8SJason Evans 46487384c51SKonstantin Belousov return ((vm_overcommit & (SWAP_RESERVE_FORCE_ON | 46587384c51SKonstantin Belousov SWAP_RESERVE_RLIMIT_ON)) == 0); 4661f0a49e8SJason Evans } 4671f0a49e8SJason Evans #endif 4681f0a49e8SJason Evans 4691f0a49e8SJason Evans #ifdef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY 470bde95144SJason Evans /* 471bde95144SJason Evans * Use syscall(2) rather than {open,read,close}(2) when possible to avoid 472bde95144SJason Evans * reentry during bootstrapping if another library has interposed system call 473bde95144SJason Evans * wrappers. 474bde95144SJason Evans */ 4751f0a49e8SJason Evans static bool 476b7eaed25SJason Evans os_overcommits_proc(void) { 4771f0a49e8SJason Evans int fd; 4781f0a49e8SJason Evans char buf[1]; 4791f0a49e8SJason Evans 4807fa7f12fSJason Evans #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open) 4810ef50b4eSJason Evans #if defined(O_CLOEXEC) 482b7eaed25SJason Evans fd = (int)syscall(SYS_open, "/proc/sys/vm/overcommit_memory", O_RDONLY | 483b7eaed25SJason Evans O_CLOEXEC); 4840ef50b4eSJason Evans #else 4850ef50b4eSJason Evans fd = (int)syscall(SYS_open, "/proc/sys/vm/overcommit_memory", O_RDONLY); 4860ef50b4eSJason Evans if (fd != -1) { 4870ef50b4eSJason Evans fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 4880ef50b4eSJason Evans } 4890ef50b4eSJason Evans #endif 490b7eaed25SJason Evans #elif defined(JEMALLOC_USE_SYSCALL) && defined(SYS_openat) 4910ef50b4eSJason Evans #if defined(O_CLOEXEC) 492b7eaed25SJason Evans fd = (int)syscall(SYS_openat, 493b7eaed25SJason Evans AT_FDCWD, "/proc/sys/vm/overcommit_memory", O_RDONLY | O_CLOEXEC); 494bde95144SJason Evans #else 4950ef50b4eSJason Evans fd = (int)syscall(SYS_openat, 4960ef50b4eSJason Evans AT_FDCWD, "/proc/sys/vm/overcommit_memory", O_RDONLY); 4970ef50b4eSJason Evans if (fd != -1) { 4980ef50b4eSJason Evans fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 4990ef50b4eSJason Evans } 500bde95144SJason Evans #endif 5010ef50b4eSJason Evans #else 5020ef50b4eSJason Evans #if defined(O_CLOEXEC) 5030ef50b4eSJason Evans fd = open("/proc/sys/vm/overcommit_memory", O_RDONLY | O_CLOEXEC); 5040ef50b4eSJason Evans #else 5050ef50b4eSJason Evans fd = open("/proc/sys/vm/overcommit_memory", O_RDONLY); 5060ef50b4eSJason Evans if (fd != -1) { 5070ef50b4eSJason Evans fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 5080ef50b4eSJason Evans } 5090ef50b4eSJason Evans #endif 5100ef50b4eSJason Evans #endif 5110ef50b4eSJason Evans 512b7eaed25SJason Evans if (fd == -1) { 513b7eaed25SJason Evans return false; /* Error. */ 514b7eaed25SJason Evans } 5151f0a49e8SJason Evans 5160ef50b4eSJason Evans ssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf)); 5177fa7f12fSJason Evans #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close) 518bde95144SJason Evans syscall(SYS_close, fd); 519bde95144SJason Evans #else 520bde95144SJason Evans close(fd); 521bde95144SJason Evans #endif 522bde95144SJason Evans 523b7eaed25SJason Evans if (nread < 1) { 524b7eaed25SJason Evans return false; /* Error. */ 525b7eaed25SJason Evans } 5261f0a49e8SJason Evans /* 5271f0a49e8SJason Evans * /proc/sys/vm/overcommit_memory meanings: 5281f0a49e8SJason Evans * 0: Heuristic overcommit. 5291f0a49e8SJason Evans * 1: Always overcommit. 5301f0a49e8SJason Evans * 2: Never overcommit. 5311f0a49e8SJason Evans */ 5321f0a49e8SJason Evans return (buf[0] == '0' || buf[0] == '1'); 5331f0a49e8SJason Evans } 5341f0a49e8SJason Evans #endif 5351f0a49e8SJason Evans 5360ef50b4eSJason Evans void 5370ef50b4eSJason Evans pages_set_thp_state (void *ptr, size_t size) { 5380ef50b4eSJason Evans if (opt_thp == thp_mode_default || opt_thp == init_system_thp_mode) { 5390ef50b4eSJason Evans return; 5400ef50b4eSJason Evans } 5410ef50b4eSJason Evans assert(opt_thp != thp_mode_not_supported && 5420ef50b4eSJason Evans init_system_thp_mode != thp_mode_not_supported); 5430ef50b4eSJason Evans 5440ef50b4eSJason Evans if (opt_thp == thp_mode_always 5450ef50b4eSJason Evans && init_system_thp_mode != thp_mode_never) { 5460ef50b4eSJason Evans assert(init_system_thp_mode == thp_mode_default); 5470ef50b4eSJason Evans pages_huge_unaligned(ptr, size); 5480ef50b4eSJason Evans } else if (opt_thp == thp_mode_never) { 5490ef50b4eSJason Evans assert(init_system_thp_mode == thp_mode_default || 5500ef50b4eSJason Evans init_system_thp_mode == thp_mode_always); 5510ef50b4eSJason Evans pages_nohuge_unaligned(ptr, size); 5520ef50b4eSJason Evans } 5530ef50b4eSJason Evans } 5540ef50b4eSJason Evans 5550ef50b4eSJason Evans static void 5560ef50b4eSJason Evans init_thp_state(void) { 5570ef50b4eSJason Evans if (!have_madvise_huge) { 5580ef50b4eSJason Evans if (metadata_thp_enabled() && opt_abort) { 5590ef50b4eSJason Evans malloc_write("<jemalloc>: no MADV_HUGEPAGE support\n"); 5600ef50b4eSJason Evans abort(); 5610ef50b4eSJason Evans } 5620ef50b4eSJason Evans goto label_error; 5630ef50b4eSJason Evans } 5640ef50b4eSJason Evans 5650ef50b4eSJason Evans static const char sys_state_madvise[] = "always [madvise] never\n"; 5660ef50b4eSJason Evans static const char sys_state_always[] = "[always] madvise never\n"; 5670ef50b4eSJason Evans static const char sys_state_never[] = "always madvise [never]\n"; 5680ef50b4eSJason Evans char buf[sizeof(sys_state_madvise)]; 5690ef50b4eSJason Evans 5700ef50b4eSJason Evans #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open) 5710ef50b4eSJason Evans int fd = (int)syscall(SYS_open, 5720ef50b4eSJason Evans "/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY); 5730ef50b4eSJason Evans #else 5740ef50b4eSJason Evans int fd = open("/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY); 5750ef50b4eSJason Evans #endif 5760ef50b4eSJason Evans if (fd == -1) { 5770ef50b4eSJason Evans goto label_error; 5780ef50b4eSJason Evans } 5790ef50b4eSJason Evans 5800ef50b4eSJason Evans ssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf)); 5810ef50b4eSJason Evans #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close) 5820ef50b4eSJason Evans syscall(SYS_close, fd); 5830ef50b4eSJason Evans #else 5840ef50b4eSJason Evans close(fd); 5850ef50b4eSJason Evans #endif 5860ef50b4eSJason Evans 587c5ad8142SEric van Gyzen if (nread < 0) { 588c5ad8142SEric van Gyzen goto label_error; 589c5ad8142SEric van Gyzen } 590c5ad8142SEric van Gyzen 5910ef50b4eSJason Evans if (strncmp(buf, sys_state_madvise, (size_t)nread) == 0) { 5920ef50b4eSJason Evans init_system_thp_mode = thp_mode_default; 5930ef50b4eSJason Evans } else if (strncmp(buf, sys_state_always, (size_t)nread) == 0) { 5940ef50b4eSJason Evans init_system_thp_mode = thp_mode_always; 5950ef50b4eSJason Evans } else if (strncmp(buf, sys_state_never, (size_t)nread) == 0) { 5960ef50b4eSJason Evans init_system_thp_mode = thp_mode_never; 5970ef50b4eSJason Evans } else { 5980ef50b4eSJason Evans goto label_error; 5990ef50b4eSJason Evans } 6000ef50b4eSJason Evans return; 6010ef50b4eSJason Evans label_error: 6020ef50b4eSJason Evans opt_thp = init_system_thp_mode = thp_mode_not_supported; 6030ef50b4eSJason Evans } 6040ef50b4eSJason Evans 605b7eaed25SJason Evans bool 606b7eaed25SJason Evans pages_boot(void) { 607b7eaed25SJason Evans os_page = os_page_detect(); 608b7eaed25SJason Evans if (os_page > PAGE) { 609b7eaed25SJason Evans malloc_write("<jemalloc>: Unsupported system page size\n"); 610b7eaed25SJason Evans if (opt_abort) { 611b7eaed25SJason Evans abort(); 612b7eaed25SJason Evans } 613b7eaed25SJason Evans return true; 614b7eaed25SJason Evans } 6151f0a49e8SJason Evans 6161f0a49e8SJason Evans #ifndef _WIN32 6171f0a49e8SJason Evans mmap_flags = MAP_PRIVATE | MAP_ANON; 6181f0a49e8SJason Evans #endif 6191f0a49e8SJason Evans 6201f0a49e8SJason Evans #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT 6211f0a49e8SJason Evans os_overcommits = os_overcommits_sysctl(); 6221f0a49e8SJason Evans #elif defined(JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY) 6231f0a49e8SJason Evans os_overcommits = os_overcommits_proc(); 6241f0a49e8SJason Evans # ifdef MAP_NORESERVE 625b7eaed25SJason Evans if (os_overcommits) { 6261f0a49e8SJason Evans mmap_flags |= MAP_NORESERVE; 627b7eaed25SJason Evans } 6281f0a49e8SJason Evans # endif 6291f0a49e8SJason Evans #else 6301f0a49e8SJason Evans os_overcommits = false; 6311f0a49e8SJason Evans #endif 632b7eaed25SJason Evans 6330ef50b4eSJason Evans init_thp_state(); 6340ef50b4eSJason Evans 635bff19560SEdward Tomasz Napierala #ifdef __FreeBSD__ 636bff19560SEdward Tomasz Napierala /* 637bff19560SEdward Tomasz Napierala * FreeBSD doesn't need the check; madvise(2) is known to work. 638bff19560SEdward Tomasz Napierala */ 639bff19560SEdward Tomasz Napierala #else 6400ef50b4eSJason Evans /* Detect lazy purge runtime support. */ 6410ef50b4eSJason Evans if (pages_can_purge_lazy) { 6420ef50b4eSJason Evans bool committed = false; 6430ef50b4eSJason Evans void *madv_free_page = os_pages_map(NULL, PAGE, PAGE, &committed); 6440ef50b4eSJason Evans if (madv_free_page == NULL) { 6450ef50b4eSJason Evans return true; 6460ef50b4eSJason Evans } 6470ef50b4eSJason Evans assert(pages_can_purge_lazy_runtime); 6480ef50b4eSJason Evans if (pages_purge_lazy(madv_free_page, PAGE)) { 6490ef50b4eSJason Evans pages_can_purge_lazy_runtime = false; 6500ef50b4eSJason Evans } 6510ef50b4eSJason Evans os_pages_unmap(madv_free_page, PAGE); 6520ef50b4eSJason Evans } 653bff19560SEdward Tomasz Napierala #endif 6540ef50b4eSJason Evans 655b7eaed25SJason Evans return false; 6561f0a49e8SJason Evans } 657