huge.c revision 301718
1#define JEMALLOC_HUGE_C_ 2#include "jemalloc/internal/jemalloc_internal.h" 3 4/******************************************************************************/ 5 6static extent_node_t * 7huge_node_get(const void *ptr) 8{ 9 extent_node_t *node; 10 11 node = chunk_lookup(ptr, true); 12 assert(!extent_node_achunk_get(node)); 13 14 return (node); 15} 16 17static bool 18huge_node_set(tsdn_t *tsdn, const void *ptr, extent_node_t *node) 19{ 20 21 assert(extent_node_addr_get(node) == ptr); 22 assert(!extent_node_achunk_get(node)); 23 return (chunk_register(tsdn, ptr, node)); 24} 25 26static void 27huge_node_reset(tsdn_t *tsdn, const void *ptr, extent_node_t *node) 28{ 29 bool err; 30 31 err = huge_node_set(tsdn, ptr, node); 32 assert(!err); 33} 34 35static void 36huge_node_unset(const void *ptr, const extent_node_t *node) 37{ 38 39 chunk_deregister(ptr, node); 40} 41 42void * 43huge_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero) 44{ 45 46 assert(usize == s2u(usize)); 47 48 return (huge_palloc(tsdn, arena, usize, chunksize, zero)); 49} 50 51void * 52huge_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment, 53 bool zero) 54{ 55 void *ret; 56 size_t ausize; 57 extent_node_t *node; 58 bool is_zeroed; 59 60 /* Allocate one or more contiguous chunks for this request. */ 61 62 assert(!tsdn_null(tsdn) || arena != NULL); 63 64 ausize = sa2u(usize, alignment); 65 if (unlikely(ausize == 0 || ausize > HUGE_MAXCLASS)) 66 return (NULL); 67 assert(ausize >= chunksize); 68 69 /* Allocate an extent node with which to track the chunk. */ 70 node = ipallocztm(tsdn, CACHELINE_CEILING(sizeof(extent_node_t)), 71 CACHELINE, false, NULL, true, arena_ichoose(tsdn, arena)); 72 if (node == NULL) 73 return (NULL); 74 75 /* 76 * Copy zero into is_zeroed and pass the copy to chunk_alloc(), so that 77 * it is possible to make correct junk/zero fill decisions below. 78 */ 79 is_zeroed = zero; 80 if (likely(!tsdn_null(tsdn))) 81 arena = arena_choose(tsdn_tsd(tsdn), arena); 82 if (unlikely(arena == NULL) || (ret = arena_chunk_alloc_huge(tsdn, 83 arena, usize, alignment, &is_zeroed)) == NULL) { 84 idalloctm(tsdn, node, NULL, true, true); 85 return (NULL); 86 } 87 88 extent_node_init(node, arena, ret, usize, is_zeroed, true); 89 90 if (huge_node_set(tsdn, ret, node)) { 91 arena_chunk_dalloc_huge(tsdn, arena, ret, usize); 92 idalloctm(tsdn, node, NULL, true, true); 93 return (NULL); 94 } 95 96 /* Insert node into huge. */ 97 malloc_mutex_lock(tsdn, &arena->huge_mtx); 98 ql_elm_new(node, ql_link); 99 ql_tail_insert(&arena->huge, node, ql_link); 100 malloc_mutex_unlock(tsdn, &arena->huge_mtx); 101 102 if (zero || (config_fill && unlikely(opt_zero))) { 103 if (!is_zeroed) 104 memset(ret, 0, usize); 105 } else if (config_fill && unlikely(opt_junk_alloc)) 106 memset(ret, JEMALLOC_ALLOC_JUNK, usize); 107 108 arena_decay_tick(tsdn, arena); 109 return (ret); 110} 111 112#ifdef JEMALLOC_JET 113#undef huge_dalloc_junk 114#define huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk_impl) 115#endif 116static void 117huge_dalloc_junk(tsdn_t *tsdn, void *ptr, size_t usize) 118{ 119 120 if (config_fill && have_dss && unlikely(opt_junk_free)) { 121 /* 122 * Only bother junk filling if the chunk isn't about to be 123 * unmapped. 124 */ 125 if (!config_munmap || (have_dss && chunk_in_dss(tsdn, ptr))) 126 memset(ptr, JEMALLOC_FREE_JUNK, usize); 127 } 128} 129#ifdef JEMALLOC_JET 130#undef huge_dalloc_junk 131#define huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk) 132huge_dalloc_junk_t *huge_dalloc_junk = JEMALLOC_N(huge_dalloc_junk_impl); 133#endif 134 135static void 136huge_ralloc_no_move_similar(tsdn_t *tsdn, void *ptr, size_t oldsize, 137 size_t usize_min, size_t usize_max, bool zero) 138{ 139 size_t usize, usize_next; 140 extent_node_t *node; 141 arena_t *arena; 142 chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER; 143 bool pre_zeroed, post_zeroed; 144 145 /* Increase usize to incorporate extra. */ 146 for (usize = usize_min; usize < usize_max && (usize_next = s2u(usize+1)) 147 <= oldsize; usize = usize_next) 148 ; /* Do nothing. */ 149 150 if (oldsize == usize) 151 return; 152 153 node = huge_node_get(ptr); 154 arena = extent_node_arena_get(node); 155 pre_zeroed = extent_node_zeroed_get(node); 156 157 /* Fill if necessary (shrinking). */ 158 if (oldsize > usize) { 159 size_t sdiff = oldsize - usize; 160 if (config_fill && unlikely(opt_junk_free)) { 161 memset((void *)((uintptr_t)ptr + usize), 162 JEMALLOC_FREE_JUNK, sdiff); 163 post_zeroed = false; 164 } else { 165 post_zeroed = !chunk_purge_wrapper(tsdn, arena, 166 &chunk_hooks, ptr, CHUNK_CEILING(oldsize), usize, 167 sdiff); 168 } 169 } else 170 post_zeroed = pre_zeroed; 171 172 malloc_mutex_lock(tsdn, &arena->huge_mtx); 173 /* Update the size of the huge allocation. */ 174 huge_node_unset(ptr, node); 175 assert(extent_node_size_get(node) != usize); 176 extent_node_size_set(node, usize); 177 huge_node_reset(tsdn, ptr, node); 178 /* Update zeroed. */ 179 extent_node_zeroed_set(node, post_zeroed); 180 malloc_mutex_unlock(tsdn, &arena->huge_mtx); 181 182 arena_chunk_ralloc_huge_similar(tsdn, arena, ptr, oldsize, usize); 183 184 /* Fill if necessary (growing). */ 185 if (oldsize < usize) { 186 if (zero || (config_fill && unlikely(opt_zero))) { 187 if (!pre_zeroed) { 188 memset((void *)((uintptr_t)ptr + oldsize), 0, 189 usize - oldsize); 190 } 191 } else if (config_fill && unlikely(opt_junk_alloc)) { 192 memset((void *)((uintptr_t)ptr + oldsize), 193 JEMALLOC_ALLOC_JUNK, usize - oldsize); 194 } 195 } 196} 197 198static bool 199huge_ralloc_no_move_shrink(tsdn_t *tsdn, void *ptr, size_t oldsize, 200 size_t usize) 201{ 202 extent_node_t *node; 203 arena_t *arena; 204 chunk_hooks_t chunk_hooks; 205 size_t cdiff; 206 bool pre_zeroed, post_zeroed; 207 208 node = huge_node_get(ptr); 209 arena = extent_node_arena_get(node); 210 pre_zeroed = extent_node_zeroed_get(node); 211 chunk_hooks = chunk_hooks_get(tsdn, arena); 212 213 assert(oldsize > usize); 214 215 /* Split excess chunks. */ 216 cdiff = CHUNK_CEILING(oldsize) - CHUNK_CEILING(usize); 217 if (cdiff != 0 && chunk_hooks.split(ptr, CHUNK_CEILING(oldsize), 218 CHUNK_CEILING(usize), cdiff, true, arena->ind)) 219 return (true); 220 221 if (oldsize > usize) { 222 size_t sdiff = oldsize - usize; 223 if (config_fill && unlikely(opt_junk_free)) { 224 huge_dalloc_junk(tsdn, (void *)((uintptr_t)ptr + usize), 225 sdiff); 226 post_zeroed = false; 227 } else { 228 post_zeroed = !chunk_purge_wrapper(tsdn, arena, 229 &chunk_hooks, CHUNK_ADDR2BASE((uintptr_t)ptr + 230 usize), CHUNK_CEILING(oldsize), 231 CHUNK_ADDR2OFFSET((uintptr_t)ptr + usize), sdiff); 232 } 233 } else 234 post_zeroed = pre_zeroed; 235 236 malloc_mutex_lock(tsdn, &arena->huge_mtx); 237 /* Update the size of the huge allocation. */ 238 huge_node_unset(ptr, node); 239 extent_node_size_set(node, usize); 240 huge_node_reset(tsdn, ptr, node); 241 /* Update zeroed. */ 242 extent_node_zeroed_set(node, post_zeroed); 243 malloc_mutex_unlock(tsdn, &arena->huge_mtx); 244 245 /* Zap the excess chunks. */ 246 arena_chunk_ralloc_huge_shrink(tsdn, arena, ptr, oldsize, usize); 247 248 return (false); 249} 250 251static bool 252huge_ralloc_no_move_expand(tsdn_t *tsdn, void *ptr, size_t oldsize, 253 size_t usize, bool zero) { 254 extent_node_t *node; 255 arena_t *arena; 256 bool is_zeroed_subchunk, is_zeroed_chunk; 257 258 node = huge_node_get(ptr); 259 arena = extent_node_arena_get(node); 260 malloc_mutex_lock(tsdn, &arena->huge_mtx); 261 is_zeroed_subchunk = extent_node_zeroed_get(node); 262 malloc_mutex_unlock(tsdn, &arena->huge_mtx); 263 264 /* 265 * Use is_zeroed_chunk to detect whether the trailing memory is zeroed, 266 * update extent's zeroed field, and zero as necessary. 267 */ 268 is_zeroed_chunk = false; 269 if (arena_chunk_ralloc_huge_expand(tsdn, arena, ptr, oldsize, usize, 270 &is_zeroed_chunk)) 271 return (true); 272 273 malloc_mutex_lock(tsdn, &arena->huge_mtx); 274 huge_node_unset(ptr, node); 275 extent_node_size_set(node, usize); 276 extent_node_zeroed_set(node, extent_node_zeroed_get(node) && 277 is_zeroed_chunk); 278 huge_node_reset(tsdn, ptr, node); 279 malloc_mutex_unlock(tsdn, &arena->huge_mtx); 280 281 if (zero || (config_fill && unlikely(opt_zero))) { 282 if (!is_zeroed_subchunk) { 283 memset((void *)((uintptr_t)ptr + oldsize), 0, 284 CHUNK_CEILING(oldsize) - oldsize); 285 } 286 if (!is_zeroed_chunk) { 287 memset((void *)((uintptr_t)ptr + 288 CHUNK_CEILING(oldsize)), 0, usize - 289 CHUNK_CEILING(oldsize)); 290 } 291 } else if (config_fill && unlikely(opt_junk_alloc)) { 292 memset((void *)((uintptr_t)ptr + oldsize), JEMALLOC_ALLOC_JUNK, 293 usize - oldsize); 294 } 295 296 return (false); 297} 298 299bool 300huge_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t usize_min, 301 size_t usize_max, bool zero) 302{ 303 304 assert(s2u(oldsize) == oldsize); 305 /* The following should have been caught by callers. */ 306 assert(usize_min > 0 && usize_max <= HUGE_MAXCLASS); 307 308 /* Both allocations must be huge to avoid a move. */ 309 if (oldsize < chunksize || usize_max < chunksize) 310 return (true); 311 312 if (CHUNK_CEILING(usize_max) > CHUNK_CEILING(oldsize)) { 313 /* Attempt to expand the allocation in-place. */ 314 if (!huge_ralloc_no_move_expand(tsdn, ptr, oldsize, usize_max, 315 zero)) { 316 arena_decay_tick(tsdn, huge_aalloc(ptr)); 317 return (false); 318 } 319 /* Try again, this time with usize_min. */ 320 if (usize_min < usize_max && CHUNK_CEILING(usize_min) > 321 CHUNK_CEILING(oldsize) && huge_ralloc_no_move_expand(tsdn, 322 ptr, oldsize, usize_min, zero)) { 323 arena_decay_tick(tsdn, huge_aalloc(ptr)); 324 return (false); 325 } 326 } 327 328 /* 329 * Avoid moving the allocation if the existing chunk size accommodates 330 * the new size. 331 */ 332 if (CHUNK_CEILING(oldsize) >= CHUNK_CEILING(usize_min) 333 && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(usize_max)) { 334 huge_ralloc_no_move_similar(tsdn, ptr, oldsize, usize_min, 335 usize_max, zero); 336 arena_decay_tick(tsdn, huge_aalloc(ptr)); 337 return (false); 338 } 339 340 /* Attempt to shrink the allocation in-place. */ 341 if (CHUNK_CEILING(oldsize) > CHUNK_CEILING(usize_max)) { 342 if (!huge_ralloc_no_move_shrink(tsdn, ptr, oldsize, 343 usize_max)) { 344 arena_decay_tick(tsdn, huge_aalloc(ptr)); 345 return (false); 346 } 347 } 348 return (true); 349} 350 351static void * 352huge_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize, 353 size_t alignment, bool zero) 354{ 355 356 if (alignment <= chunksize) 357 return (huge_malloc(tsdn, arena, usize, zero)); 358 return (huge_palloc(tsdn, arena, usize, alignment, zero)); 359} 360 361void * 362huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, 363 size_t usize, size_t alignment, bool zero, tcache_t *tcache) 364{ 365 void *ret; 366 size_t copysize; 367 368 /* The following should have been caught by callers. */ 369 assert(usize > 0 && usize <= HUGE_MAXCLASS); 370 371 /* Try to avoid moving the allocation. */ 372 if (!huge_ralloc_no_move(tsd_tsdn(tsd), ptr, oldsize, usize, usize, 373 zero)) 374 return (ptr); 375 376 /* 377 * usize and oldsize are different enough that we need to use a 378 * different size class. In that case, fall back to allocating new 379 * space and copying. 380 */ 381 ret = huge_ralloc_move_helper(tsd_tsdn(tsd), arena, usize, alignment, 382 zero); 383 if (ret == NULL) 384 return (NULL); 385 386 copysize = (usize < oldsize) ? usize : oldsize; 387 memcpy(ret, ptr, copysize); 388 isqalloc(tsd, ptr, oldsize, tcache, true); 389 return (ret); 390} 391 392void 393huge_dalloc(tsdn_t *tsdn, void *ptr) 394{ 395 extent_node_t *node; 396 arena_t *arena; 397 398 node = huge_node_get(ptr); 399 arena = extent_node_arena_get(node); 400 huge_node_unset(ptr, node); 401 malloc_mutex_lock(tsdn, &arena->huge_mtx); 402 ql_remove(&arena->huge, node, ql_link); 403 malloc_mutex_unlock(tsdn, &arena->huge_mtx); 404 405 huge_dalloc_junk(tsdn, extent_node_addr_get(node), 406 extent_node_size_get(node)); 407 arena_chunk_dalloc_huge(tsdn, extent_node_arena_get(node), 408 extent_node_addr_get(node), extent_node_size_get(node)); 409 idalloctm(tsdn, node, NULL, true, true); 410 411 arena_decay_tick(tsdn, arena); 412} 413 414arena_t * 415huge_aalloc(const void *ptr) 416{ 417 418 return (extent_node_arena_get(huge_node_get(ptr))); 419} 420 421size_t 422huge_salloc(tsdn_t *tsdn, const void *ptr) 423{ 424 size_t size; 425 extent_node_t *node; 426 arena_t *arena; 427 428 node = huge_node_get(ptr); 429 arena = extent_node_arena_get(node); 430 malloc_mutex_lock(tsdn, &arena->huge_mtx); 431 size = extent_node_size_get(node); 432 malloc_mutex_unlock(tsdn, &arena->huge_mtx); 433 434 return (size); 435} 436 437prof_tctx_t * 438huge_prof_tctx_get(tsdn_t *tsdn, const void *ptr) 439{ 440 prof_tctx_t *tctx; 441 extent_node_t *node; 442 arena_t *arena; 443 444 node = huge_node_get(ptr); 445 arena = extent_node_arena_get(node); 446 malloc_mutex_lock(tsdn, &arena->huge_mtx); 447 tctx = extent_node_prof_tctx_get(node); 448 malloc_mutex_unlock(tsdn, &arena->huge_mtx); 449 450 return (tctx); 451} 452 453void 454huge_prof_tctx_set(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) 455{ 456 extent_node_t *node; 457 arena_t *arena; 458 459 node = huge_node_get(ptr); 460 arena = extent_node_arena_get(node); 461 malloc_mutex_lock(tsdn, &arena->huge_mtx); 462 extent_node_prof_tctx_set(node, tctx); 463 malloc_mutex_unlock(tsdn, &arena->huge_mtx); 464} 465 466void 467huge_prof_tctx_reset(tsdn_t *tsdn, const void *ptr) 468{ 469 470 huge_prof_tctx_set(tsdn, ptr, (prof_tctx_t *)(uintptr_t)1U); 471} 472