| 1 | #define JEMALLOC_PAGES_C_ |
| 2 | #include "jemalloc/internal/jemalloc_internal.h" |
| 3 | |
| 4 | #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT |
| 5 | #include <sys/sysctl.h> |
| 6 | #endif |
| 7 | |
| 8 | /******************************************************************************/ |
| 9 | /* Data. */ |
| 10 | |
| 11 | #ifndef _WIN32 |
| 12 | # define PAGES_PROT_COMMIT (PROT_READ | PROT_WRITE) |
| 13 | # define PAGES_PROT_DECOMMIT (PROT_NONE) |
| 14 | static int mmap_flags; |
| 15 | #endif |
| 16 | static bool os_overcommits; |
| 17 | |
| 18 | /******************************************************************************/ |
| 19 | |
| 20 | void * |
| 21 | pages_map(void *addr, size_t size, bool *commit) |
| 22 | { |
| 23 | void *ret; |
| 24 | |
| 25 | assert(size != 0); |
| 26 | |
| 27 | if (os_overcommits) |
| 28 | *commit = true; |
| 29 | |
| 30 | #ifdef _WIN32 |
| 31 | /* |
| 32 | * If VirtualAlloc can't allocate at the given address when one is |
| 33 | * given, it fails and returns NULL. |
| 34 | */ |
| 35 | ret = VirtualAlloc(addr, size, MEM_RESERVE | (*commit ? MEM_COMMIT : 0), |
| 36 | PAGE_READWRITE); |
| 37 | #else |
| 38 | /* |
| 39 | * We don't use MAP_FIXED here, because it can cause the *replacement* |
| 40 | * of existing mappings, and we only want to create new mappings. |
| 41 | */ |
| 42 | { |
| 43 | int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; |
| 44 | |
| 45 | ret = mmap(addr, size, prot, mmap_flags, -1, 0); |
| 46 | } |
| 47 | assert(ret != NULL); |
| 48 | |
| 49 | if (ret == MAP_FAILED) |
| 50 | ret = NULL; |
| 51 | else if (addr != NULL && ret != addr) { |
| 52 | /* |
| 53 | * We succeeded in mapping memory, but not in the right place. |
| 54 | */ |
| 55 | pages_unmap(ret, size); |
| 56 | ret = NULL; |
| 57 | } |
| 58 | #endif |
| 59 | assert(ret == NULL || (addr == NULL && ret != addr) |
| 60 | || (addr != NULL && ret == addr)); |
| 61 | return (ret); |
| 62 | } |
| 63 | |
| 64 | void |
| 65 | pages_unmap(void *addr, size_t size) |
| 66 | { |
| 67 | |
| 68 | #ifdef _WIN32 |
| 69 | if (VirtualFree(addr, 0, MEM_RELEASE) == 0) |
| 70 | #else |
| 71 | if (munmap(addr, size) == -1) |
| 72 | #endif |
| 73 | { |
| 74 | char buf[BUFERROR_BUF]; |
| 75 | |
| 76 | buferror(get_errno(), buf, sizeof(buf)); |
| 77 | malloc_printf("<jemalloc>: Error in " |
| 78 | #ifdef _WIN32 |
| 79 | "VirtualFree" |
| 80 | #else |
| 81 | "munmap" |
| 82 | #endif |
| 83 | "(): %s\n" , buf); |
| 84 | if (opt_abort) |
| 85 | abort(); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | void * |
| 90 | pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size, |
| 91 | bool *commit) |
| 92 | { |
| 93 | void *ret = (void *)((uintptr_t)addr + leadsize); |
| 94 | |
| 95 | assert(alloc_size >= leadsize + size); |
| 96 | #ifdef _WIN32 |
| 97 | { |
| 98 | void *new_addr; |
| 99 | |
| 100 | pages_unmap(addr, alloc_size); |
| 101 | new_addr = pages_map(ret, size, commit); |
| 102 | if (new_addr == ret) |
| 103 | return (ret); |
| 104 | if (new_addr) |
| 105 | pages_unmap(new_addr, size); |
| 106 | return (NULL); |
| 107 | } |
| 108 | #else |
| 109 | { |
| 110 | size_t trailsize = alloc_size - leadsize - size; |
| 111 | |
| 112 | if (leadsize != 0) |
| 113 | pages_unmap(addr, leadsize); |
| 114 | if (trailsize != 0) |
| 115 | pages_unmap((void *)((uintptr_t)ret + size), trailsize); |
| 116 | return (ret); |
| 117 | } |
| 118 | #endif |
| 119 | } |
| 120 | |
| 121 | static bool |
| 122 | pages_commit_impl(void *addr, size_t size, bool commit) |
| 123 | { |
| 124 | |
| 125 | if (os_overcommits) |
| 126 | return (true); |
| 127 | |
| 128 | #ifdef _WIN32 |
| 129 | return (commit ? (addr != VirtualAlloc(addr, size, MEM_COMMIT, |
| 130 | PAGE_READWRITE)) : (!VirtualFree(addr, size, MEM_DECOMMIT))); |
| 131 | #else |
| 132 | { |
| 133 | int prot = commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; |
| 134 | void *result = mmap(addr, size, prot, mmap_flags | MAP_FIXED, |
| 135 | -1, 0); |
| 136 | if (result == MAP_FAILED) |
| 137 | return (true); |
| 138 | if (result != addr) { |
| 139 | /* |
| 140 | * We succeeded in mapping memory, but not in the right |
| 141 | * place. |
| 142 | */ |
| 143 | pages_unmap(result, size); |
| 144 | return (true); |
| 145 | } |
| 146 | return (false); |
| 147 | } |
| 148 | #endif |
| 149 | } |
| 150 | |
| 151 | bool |
| 152 | pages_commit(void *addr, size_t size) |
| 153 | { |
| 154 | |
| 155 | return (pages_commit_impl(addr, size, true)); |
| 156 | } |
| 157 | |
| 158 | bool |
| 159 | pages_decommit(void *addr, size_t size) |
| 160 | { |
| 161 | |
| 162 | return (pages_commit_impl(addr, size, false)); |
| 163 | } |
| 164 | |
| 165 | bool |
| 166 | pages_purge(void *addr, size_t size) |
| 167 | { |
| 168 | bool unzeroed; |
| 169 | |
| 170 | #ifdef _WIN32 |
| 171 | VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE); |
| 172 | unzeroed = true; |
| 173 | #elif defined(JEMALLOC_HAVE_MADVISE) |
| 174 | # ifdef JEMALLOC_PURGE_MADVISE_DONTNEED |
| 175 | # define JEMALLOC_MADV_PURGE MADV_DONTNEED |
| 176 | # define JEMALLOC_MADV_ZEROS true |
| 177 | # elif defined(JEMALLOC_PURGE_MADVISE_FREE) |
| 178 | # define JEMALLOC_MADV_PURGE MADV_FREE |
| 179 | # define JEMALLOC_MADV_ZEROS false |
| 180 | # else |
| 181 | # error "No madvise(2) flag defined for purging unused dirty pages." |
| 182 | # endif |
| 183 | int err = madvise(addr, size, JEMALLOC_MADV_PURGE); |
| 184 | unzeroed = (!JEMALLOC_MADV_ZEROS || err != 0); |
| 185 | # undef JEMALLOC_MADV_PURGE |
| 186 | # undef JEMALLOC_MADV_ZEROS |
| 187 | #else |
| 188 | /* Last resort no-op. */ |
| 189 | unzeroed = true; |
| 190 | #endif |
| 191 | return (unzeroed); |
| 192 | } |
| 193 | |
| 194 | #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT |
| 195 | static bool |
| 196 | os_overcommits_sysctl(void) |
| 197 | { |
| 198 | int vm_overcommit; |
| 199 | size_t sz; |
| 200 | |
| 201 | sz = sizeof(vm_overcommit); |
| 202 | if (sysctlbyname("vm.overcommit" , &vm_overcommit, &sz, NULL, 0) != 0) |
| 203 | return (false); /* Error. */ |
| 204 | |
| 205 | return ((vm_overcommit & 0x3) == 0); |
| 206 | } |
| 207 | #endif |
| 208 | |
| 209 | #ifdef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY |
| 210 | static bool |
| 211 | os_overcommits_proc(void) |
| 212 | { |
| 213 | int fd; |
| 214 | char buf[1]; |
| 215 | ssize_t nread; |
| 216 | |
| 217 | fd = open("/proc/sys/vm/overcommit_memory" , O_RDONLY); |
| 218 | if (fd == -1) |
| 219 | return (false); /* Error. */ |
| 220 | |
| 221 | nread = read(fd, &buf, sizeof(buf)); |
| 222 | if (nread < 1) |
| 223 | return (false); /* Error. */ |
| 224 | /* |
| 225 | * /proc/sys/vm/overcommit_memory meanings: |
| 226 | * 0: Heuristic overcommit. |
| 227 | * 1: Always overcommit. |
| 228 | * 2: Never overcommit. |
| 229 | */ |
| 230 | return (buf[0] == '0' || buf[0] == '1'); |
| 231 | } |
| 232 | #endif |
| 233 | |
| 234 | void |
| 235 | pages_boot(void) |
| 236 | { |
| 237 | |
| 238 | #ifndef _WIN32 |
| 239 | mmap_flags = MAP_PRIVATE | MAP_ANON; |
| 240 | #endif |
| 241 | |
| 242 | #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT |
| 243 | os_overcommits = os_overcommits_sysctl(); |
| 244 | #elif defined(JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY) |
| 245 | os_overcommits = os_overcommits_proc(); |
| 246 | # ifdef MAP_NORESERVE |
| 247 | if (os_overcommits) |
| 248 | mmap_flags |= MAP_NORESERVE; |
| 249 | # endif |
| 250 | #else |
| 251 | os_overcommits = false; |
| 252 | #endif |
| 253 | } |
| 254 | |