Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 1 | #define JEMALLOC_PAGES_C_ |
| 2 | #include "jemalloc/internal/jemalloc_internal.h" |
| 3 | |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 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 |
Alex Naidis | 78897ab | 2016-10-04 14:08:33 +0200 | [diff] [blame] | 16 | #if !defined(__ANDROID__) |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 17 | static bool os_overcommits; |
Alex Naidis | 78897ab | 2016-10-04 14:08:33 +0200 | [diff] [blame] | 18 | #else |
| 19 | static const bool os_overcommits = true; |
| 20 | #endif |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 21 | |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 22 | /******************************************************************************/ |
Christopher Ferris | 6f50cbc | 2015-09-09 12:17:01 -0700 | [diff] [blame] | 23 | /* Defines/includes needed for special android code. */ |
| 24 | |
| 25 | #if defined(__ANDROID__) |
| 26 | #include <sys/prctl.h> |
| 27 | |
| 28 | /* Definitions of prctl arguments to set a vma name in Android kernels. */ |
| 29 | #define ANDROID_PR_SET_VMA 0x53564d41 |
| 30 | #define ANDROID_PR_SET_VMA_ANON_NAME 0 |
| 31 | #endif |
| 32 | |
| 33 | /******************************************************************************/ |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 34 | |
| 35 | void * |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 36 | pages_map(void *addr, size_t size, bool *commit) |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 37 | { |
| 38 | void *ret; |
| 39 | |
| 40 | assert(size != 0); |
| 41 | |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 42 | if (os_overcommits) |
| 43 | *commit = true; |
| 44 | |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 45 | #ifdef _WIN32 |
| 46 | /* |
| 47 | * If VirtualAlloc can't allocate at the given address when one is |
| 48 | * given, it fails and returns NULL. |
| 49 | */ |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 50 | ret = VirtualAlloc(addr, size, MEM_RESERVE | (*commit ? MEM_COMMIT : 0), |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 51 | PAGE_READWRITE); |
| 52 | #else |
| 53 | /* |
| 54 | * We don't use MAP_FIXED here, because it can cause the *replacement* |
| 55 | * of existing mappings, and we only want to create new mappings. |
| 56 | */ |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 57 | { |
| 58 | int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; |
| 59 | |
| 60 | ret = mmap(addr, size, prot, mmap_flags, -1, 0); |
| 61 | } |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 62 | assert(ret != NULL); |
| 63 | |
| 64 | if (ret == MAP_FAILED) |
| 65 | ret = NULL; |
| 66 | else if (addr != NULL && ret != addr) { |
| 67 | /* |
| 68 | * We succeeded in mapping memory, but not in the right place. |
| 69 | */ |
| 70 | pages_unmap(ret, size); |
| 71 | ret = NULL; |
| 72 | } |
| 73 | #endif |
Christopher Ferris | 6f50cbc | 2015-09-09 12:17:01 -0700 | [diff] [blame] | 74 | #if defined(__ANDROID__) |
| 75 | if (ret != NULL) { |
| 76 | /* Name this memory as being used by libc */ |
| 77 | prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, ret, |
| 78 | size, "libc_malloc"); |
| 79 | } |
| 80 | #endif |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 81 | assert(ret == NULL || (addr == NULL && ret != addr) |
| 82 | || (addr != NULL && ret == addr)); |
| 83 | return (ret); |
| 84 | } |
| 85 | |
| 86 | void |
| 87 | pages_unmap(void *addr, size_t size) |
| 88 | { |
| 89 | |
| 90 | #ifdef _WIN32 |
| 91 | if (VirtualFree(addr, 0, MEM_RELEASE) == 0) |
| 92 | #else |
| 93 | if (munmap(addr, size) == -1) |
| 94 | #endif |
| 95 | { |
| 96 | char buf[BUFERROR_BUF]; |
| 97 | |
| 98 | buferror(get_errno(), buf, sizeof(buf)); |
| 99 | malloc_printf("<jemalloc>: Error in " |
| 100 | #ifdef _WIN32 |
| 101 | "VirtualFree" |
| 102 | #else |
| 103 | "munmap" |
| 104 | #endif |
| 105 | "(): %s\n", buf); |
| 106 | if (opt_abort) |
| 107 | abort(); |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | void * |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 112 | pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size, |
| 113 | bool *commit) |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 114 | { |
| 115 | void *ret = (void *)((uintptr_t)addr + leadsize); |
| 116 | |
| 117 | assert(alloc_size >= leadsize + size); |
| 118 | #ifdef _WIN32 |
| 119 | { |
| 120 | void *new_addr; |
| 121 | |
| 122 | pages_unmap(addr, alloc_size); |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 123 | new_addr = pages_map(ret, size, commit); |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 124 | if (new_addr == ret) |
| 125 | return (ret); |
| 126 | if (new_addr) |
| 127 | pages_unmap(new_addr, size); |
| 128 | return (NULL); |
| 129 | } |
| 130 | #else |
| 131 | { |
| 132 | size_t trailsize = alloc_size - leadsize - size; |
| 133 | |
| 134 | if (leadsize != 0) |
| 135 | pages_unmap(addr, leadsize); |
| 136 | if (trailsize != 0) |
| 137 | pages_unmap((void *)((uintptr_t)ret + size), trailsize); |
| 138 | return (ret); |
| 139 | } |
| 140 | #endif |
| 141 | } |
| 142 | |
| 143 | static bool |
| 144 | pages_commit_impl(void *addr, size_t size, bool commit) |
| 145 | { |
| 146 | |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 147 | if (os_overcommits) |
| 148 | return (true); |
| 149 | |
| 150 | #ifdef _WIN32 |
| 151 | return (commit ? (addr != VirtualAlloc(addr, size, MEM_COMMIT, |
| 152 | PAGE_READWRITE)) : (!VirtualFree(addr, size, MEM_DECOMMIT))); |
| 153 | #else |
| 154 | { |
| 155 | int prot = commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; |
| 156 | void *result = mmap(addr, size, prot, mmap_flags | MAP_FIXED, |
| 157 | -1, 0); |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 158 | if (result == MAP_FAILED) |
| 159 | return (true); |
| 160 | if (result != addr) { |
| 161 | /* |
| 162 | * We succeeded in mapping memory, but not in the right |
| 163 | * place. |
| 164 | */ |
| 165 | pages_unmap(result, size); |
| 166 | return (true); |
| 167 | } |
| 168 | return (false); |
| 169 | } |
| 170 | #endif |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | bool |
| 174 | pages_commit(void *addr, size_t size) |
| 175 | { |
| 176 | |
| 177 | return (pages_commit_impl(addr, size, true)); |
| 178 | } |
| 179 | |
| 180 | bool |
| 181 | pages_decommit(void *addr, size_t size) |
| 182 | { |
| 183 | |
| 184 | return (pages_commit_impl(addr, size, false)); |
| 185 | } |
| 186 | |
| 187 | bool |
| 188 | pages_purge(void *addr, size_t size) |
| 189 | { |
| 190 | bool unzeroed; |
| 191 | |
| 192 | #ifdef _WIN32 |
| 193 | VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE); |
| 194 | unzeroed = true; |
Jason Evans | e98a620 | 2016-11-17 13:36:17 -0800 | [diff] [blame] | 195 | #elif (defined(JEMALLOC_PURGE_MADVISE_FREE) || \ |
Jason Evans | 34a7e37 | 2016-12-03 16:06:19 -0800 | [diff] [blame] | 196 | defined(JEMALLOC_PURGE_MADVISE_DONTNEED)) |
Jason Evans | 62f2d84 | 2016-11-17 10:24:51 -0800 | [diff] [blame] | 197 | # if defined(JEMALLOC_PURGE_MADVISE_FREE) |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 198 | # define JEMALLOC_MADV_PURGE MADV_FREE |
| 199 | # define JEMALLOC_MADV_ZEROS false |
Jason Evans | 62f2d84 | 2016-11-17 10:24:51 -0800 | [diff] [blame] | 200 | # elif defined(JEMALLOC_PURGE_MADVISE_DONTNEED) |
| 201 | # define JEMALLOC_MADV_PURGE MADV_DONTNEED |
| 202 | # define JEMALLOC_MADV_ZEROS true |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 203 | # else |
Jason Evans | 62f2d84 | 2016-11-17 10:24:51 -0800 | [diff] [blame] | 204 | # error No madvise(2) flag defined for purging unused dirty pages |
Jason Evans | b49a334 | 2015-07-28 11:28:19 -0400 | [diff] [blame] | 205 | # endif |
| 206 | int err = madvise(addr, size, JEMALLOC_MADV_PURGE); |
| 207 | unzeroed = (!JEMALLOC_MADV_ZEROS || err != 0); |
| 208 | # undef JEMALLOC_MADV_PURGE |
| 209 | # undef JEMALLOC_MADV_ZEROS |
| 210 | #else |
| 211 | /* Last resort no-op. */ |
| 212 | unzeroed = true; |
| 213 | #endif |
| 214 | return (unzeroed); |
| 215 | } |
| 216 | |
Jason Evans | e98a620 | 2016-11-17 13:36:17 -0800 | [diff] [blame] | 217 | bool |
| 218 | pages_huge(void *addr, size_t size) |
| 219 | { |
| 220 | |
| 221 | assert(PAGE_ADDR2BASE(addr) == addr); |
| 222 | assert(PAGE_CEILING(size) == size); |
| 223 | |
| 224 | #ifdef JEMALLOC_THP |
| 225 | return (madvise(addr, size, MADV_HUGEPAGE) != 0); |
| 226 | #else |
| 227 | return (false); |
| 228 | #endif |
| 229 | } |
| 230 | |
| 231 | bool |
| 232 | pages_nohuge(void *addr, size_t size) |
| 233 | { |
| 234 | |
| 235 | assert(PAGE_ADDR2BASE(addr) == addr); |
| 236 | assert(PAGE_CEILING(size) == size); |
| 237 | |
| 238 | #ifdef JEMALLOC_THP |
| 239 | return (madvise(addr, size, MADV_NOHUGEPAGE) != 0); |
| 240 | #else |
| 241 | return (false); |
| 242 | #endif |
| 243 | } |
| 244 | |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 245 | #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT |
| 246 | static bool |
| 247 | os_overcommits_sysctl(void) |
| 248 | { |
| 249 | int vm_overcommit; |
| 250 | size_t sz; |
| 251 | |
| 252 | sz = sizeof(vm_overcommit); |
| 253 | if (sysctlbyname("vm.overcommit", &vm_overcommit, &sz, NULL, 0) != 0) |
| 254 | return (false); /* Error. */ |
| 255 | |
| 256 | return ((vm_overcommit & 0x3) == 0); |
| 257 | } |
| 258 | #endif |
| 259 | |
| 260 | #ifdef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY |
Jason Evans | c443b67 | 2016-10-29 22:41:04 -0700 | [diff] [blame] | 261 | /* |
| 262 | * Use syscall(2) rather than {open,read,close}(2) when possible to avoid |
| 263 | * reentry during bootstrapping if another library has interposed system call |
| 264 | * wrappers. |
| 265 | */ |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 266 | static bool |
| 267 | os_overcommits_proc(void) |
| 268 | { |
| 269 | int fd; |
| 270 | char buf[1]; |
| 271 | ssize_t nread; |
| 272 | |
Jason Evans | 145f3cd | 2016-12-03 16:47:36 -0800 | [diff] [blame] | 273 | #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open) |
Jason Evans | c443b67 | 2016-10-29 22:41:04 -0700 | [diff] [blame] | 274 | fd = (int)syscall(SYS_open, "/proc/sys/vm/overcommit_memory", O_RDONLY); |
| 275 | #else |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 276 | fd = open("/proc/sys/vm/overcommit_memory", O_RDONLY); |
Jason Evans | c443b67 | 2016-10-29 22:41:04 -0700 | [diff] [blame] | 277 | #endif |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 278 | if (fd == -1) |
| 279 | return (false); /* Error. */ |
| 280 | |
Jason Evans | 145f3cd | 2016-12-03 16:47:36 -0800 | [diff] [blame] | 281 | #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_read) |
Jason Evans | c443b67 | 2016-10-29 22:41:04 -0700 | [diff] [blame] | 282 | nread = (ssize_t)syscall(SYS_read, fd, &buf, sizeof(buf)); |
| 283 | #else |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 284 | nread = read(fd, &buf, sizeof(buf)); |
Jason Evans | c443b67 | 2016-10-29 22:41:04 -0700 | [diff] [blame] | 285 | #endif |
| 286 | |
Jason Evans | 145f3cd | 2016-12-03 16:47:36 -0800 | [diff] [blame] | 287 | #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close) |
Jason Evans | c443b67 | 2016-10-29 22:41:04 -0700 | [diff] [blame] | 288 | syscall(SYS_close, fd); |
| 289 | #else |
Brian Carlstrom | 93ea0f5 | 2016-08-30 22:03:50 -0700 | [diff] [blame] | 290 | close(fd); |
Jason Evans | c443b67 | 2016-10-29 22:41:04 -0700 | [diff] [blame] | 291 | #endif |
| 292 | |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 293 | if (nread < 1) |
| 294 | return (false); /* Error. */ |
| 295 | /* |
| 296 | * /proc/sys/vm/overcommit_memory meanings: |
| 297 | * 0: Heuristic overcommit. |
| 298 | * 1: Always overcommit. |
| 299 | * 2: Never overcommit. |
| 300 | */ |
| 301 | return (buf[0] == '0' || buf[0] == '1'); |
| 302 | } |
| 303 | #endif |
| 304 | |
| 305 | void |
| 306 | pages_boot(void) |
| 307 | { |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 308 | #ifndef _WIN32 |
| 309 | mmap_flags = MAP_PRIVATE | MAP_ANON; |
| 310 | #endif |
| 311 | |
Alex Naidis | 78897ab | 2016-10-04 14:08:33 +0200 | [diff] [blame] | 312 | #if !defined(__ANDROID__) |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 313 | #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT |
| 314 | os_overcommits = os_overcommits_sysctl(); |
| 315 | #elif defined(JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY) |
| 316 | os_overcommits = os_overcommits_proc(); |
| 317 | # ifdef MAP_NORESERVE |
| 318 | if (os_overcommits) |
| 319 | mmap_flags |= MAP_NORESERVE; |
| 320 | # endif |
| 321 | #else |
| 322 | os_overcommits = false; |
| 323 | #endif |
Alex Naidis | 78897ab | 2016-10-04 14:08:33 +0200 | [diff] [blame] | 324 | #endif |
Jason Evans | c2f970c | 2016-05-05 17:45:02 -0700 | [diff] [blame] | 325 | } |