1 #define JEMALLOC_EXTENT_C_ 2 #include "jemalloc/internal/jemalloc_preamble.h" 3 #include "jemalloc/internal/jemalloc_internal_includes.h" 4 5 #include "jemalloc/internal/assert.h" 6 #include "jemalloc/internal/extent_dss.h" 7 #include "jemalloc/internal/extent_mmap.h" 8 #include "jemalloc/internal/ph.h" 9 #include "jemalloc/internal/rtree.h" 10 #include "jemalloc/internal/mutex.h" 11 #include "jemalloc/internal/mutex_pool.h" 12 13 /******************************************************************************/ 14 /* Data. */ 15 16 rtree_t extents_rtree; 17 /* Keyed by the address of the extent_t being protected. */ 18 mutex_pool_t extent_mutex_pool; 19 20 size_t opt_lg_extent_max_active_fit = LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT; 21 22 static const bitmap_info_t extents_bitmap_info = 23 BITMAP_INFO_INITIALIZER(SC_NPSIZES+1); 24 25 static void *extent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr, 26 size_t size, size_t alignment, bool *zero, bool *commit, 27 unsigned arena_ind); 28 static bool extent_dalloc_default(extent_hooks_t *extent_hooks, void *addr, 29 size_t size, bool committed, unsigned arena_ind); 30 static void extent_destroy_default(extent_hooks_t *extent_hooks, void *addr, 31 size_t size, bool committed, unsigned arena_ind); 32 static bool extent_commit_default(extent_hooks_t *extent_hooks, void *addr, 33 size_t size, size_t offset, size_t length, unsigned arena_ind); 34 static bool extent_commit_impl(tsdn_t *tsdn, arena_t *arena, 35 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 36 size_t length, bool growing_retained); 37 static bool extent_decommit_default(extent_hooks_t *extent_hooks, 38 void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind); 39 #ifdef PAGES_CAN_PURGE_LAZY 40 static bool extent_purge_lazy_default(extent_hooks_t *extent_hooks, void *addr, 41 size_t size, size_t offset, size_t length, unsigned arena_ind); 42 #endif 43 static bool extent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena, 44 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 45 size_t length, bool growing_retained); 46 #ifdef PAGES_CAN_PURGE_FORCED 47 static bool extent_purge_forced_default(extent_hooks_t *extent_hooks, 48 void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind); 49 #endif 50 static bool extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena, 51 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 52 size_t length, bool growing_retained); 53 static bool extent_split_default(extent_hooks_t *extent_hooks, void *addr, 54 size_t size, size_t size_a, size_t size_b, bool committed, 55 unsigned arena_ind); 56 static extent_t *extent_split_impl(tsdn_t *tsdn, arena_t *arena, 57 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a, 58 szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b, 59 bool growing_retained); 60 static bool extent_merge_default(extent_hooks_t *extent_hooks, void *addr_a, 61 size_t size_a, void *addr_b, size_t size_b, bool committed, 62 unsigned arena_ind); 63 static bool extent_merge_impl(tsdn_t *tsdn, arena_t *arena, 64 extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b, 65 bool growing_retained); 66 67 const extent_hooks_t extent_hooks_default = { 68 extent_alloc_default, 69 extent_dalloc_default, 70 extent_destroy_default, 71 extent_commit_default, 72 extent_decommit_default 73 #ifdef PAGES_CAN_PURGE_LAZY 74 , 75 extent_purge_lazy_default 76 #else 77 , 78 NULL 79 #endif 80 #ifdef PAGES_CAN_PURGE_FORCED 81 , 82 extent_purge_forced_default 83 #else 84 , 85 NULL 86 #endif 87 , 88 extent_split_default, 89 extent_merge_default 90 }; 91 92 /* Used exclusively for gdump triggering. */ 93 static atomic_zu_t curpages; 94 static atomic_zu_t highpages; 95 96 /******************************************************************************/ 97 /* 98 * Function prototypes for static functions that are referenced prior to 99 * definition. 100 */ 101 102 static void extent_deregister(tsdn_t *tsdn, extent_t *extent); 103 static extent_t *extent_recycle(tsdn_t *tsdn, arena_t *arena, 104 extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr, 105 size_t usize, size_t pad, size_t alignment, bool slab, szind_t szind, 106 bool *zero, bool *commit, bool growing_retained); 107 static extent_t *extent_try_coalesce(tsdn_t *tsdn, arena_t *arena, 108 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents, 109 extent_t *extent, bool *coalesced, bool growing_retained); 110 static void extent_record(tsdn_t *tsdn, arena_t *arena, 111 extent_hooks_t **r_extent_hooks, extents_t *extents, extent_t *extent, 112 bool growing_retained); 113 114 /******************************************************************************/ 115 116 #define ATTR_NONE /* does nothing */ 117 118 ph_gen(ATTR_NONE, extent_avail_, extent_tree_t, extent_t, ph_link, 119 extent_esnead_comp) 120 121 #undef ATTR_NONE 122 123 typedef enum { 124 lock_result_success, 125 lock_result_failure, 126 lock_result_no_extent 127 } lock_result_t; 128 129 static lock_result_t 130 extent_rtree_leaf_elm_try_lock(tsdn_t *tsdn, rtree_leaf_elm_t *elm, 131 extent_t **result, bool inactive_only) { 132 extent_t *extent1 = rtree_leaf_elm_extent_read(tsdn, &extents_rtree, 133 elm, true); 134 135 /* Slab implies active extents and should be skipped. */ 136 if (extent1 == NULL || (inactive_only && rtree_leaf_elm_slab_read(tsdn, 137 &extents_rtree, elm, true))) { 138 return lock_result_no_extent; 139 } 140 141 /* 142 * It's possible that the extent changed out from under us, and with it 143 * the leaf->extent mapping. We have to recheck while holding the lock. 144 */ 145 extent_lock(tsdn, extent1); 146 extent_t *extent2 = rtree_leaf_elm_extent_read(tsdn, 147 &extents_rtree, elm, true); 148 149 if (extent1 == extent2) { 150 *result = extent1; 151 return lock_result_success; 152 } else { 153 extent_unlock(tsdn, extent1); 154 return lock_result_failure; 155 } 156 } 157 158 /* 159 * Returns a pool-locked extent_t * if there's one associated with the given 160 * address, and NULL otherwise. 161 */ 162 static extent_t * 163 extent_lock_from_addr(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, void *addr, 164 bool inactive_only) { 165 extent_t *ret = NULL; 166 rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &extents_rtree, 167 rtree_ctx, (uintptr_t)addr, false, false); 168 if (elm == NULL) { 169 return NULL; 170 } 171 lock_result_t lock_result; 172 do { 173 lock_result = extent_rtree_leaf_elm_try_lock(tsdn, elm, &ret, 174 inactive_only); 175 } while (lock_result == lock_result_failure); 176 return ret; 177 } 178 179 extent_t * 180 extent_alloc(tsdn_t *tsdn, arena_t *arena) { 181 malloc_mutex_lock(tsdn, &arena->extent_avail_mtx); 182 extent_t *extent = extent_avail_first(&arena->extent_avail); 183 if (extent == NULL) { 184 malloc_mutex_unlock(tsdn, &arena->extent_avail_mtx); 185 return base_alloc_extent(tsdn, arena->base); 186 } 187 extent_avail_remove(&arena->extent_avail, extent); 188 atomic_fetch_sub_zu(&arena->extent_avail_cnt, 1, ATOMIC_RELAXED); 189 malloc_mutex_unlock(tsdn, &arena->extent_avail_mtx); 190 return extent; 191 } 192 193 void 194 extent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent) { 195 malloc_mutex_lock(tsdn, &arena->extent_avail_mtx); 196 extent_avail_insert(&arena->extent_avail, extent); 197 atomic_fetch_add_zu(&arena->extent_avail_cnt, 1, ATOMIC_RELAXED); 198 malloc_mutex_unlock(tsdn, &arena->extent_avail_mtx); 199 } 200 201 extent_hooks_t * 202 extent_hooks_get(arena_t *arena) { 203 return base_extent_hooks_get(arena->base); 204 } 205 206 extent_hooks_t * 207 extent_hooks_set(tsd_t *tsd, arena_t *arena, extent_hooks_t *extent_hooks) { 208 background_thread_info_t *info; 209 if (have_background_thread) { 210 info = arena_background_thread_info_get(arena); 211 malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx); 212 } 213 extent_hooks_t *ret = base_extent_hooks_set(arena->base, extent_hooks); 214 if (have_background_thread) { 215 malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx); 216 } 217 218 return ret; 219 } 220 221 static void 222 extent_hooks_assure_initialized(arena_t *arena, 223 extent_hooks_t **r_extent_hooks) { 224 if (*r_extent_hooks == EXTENT_HOOKS_INITIALIZER) { 225 *r_extent_hooks = extent_hooks_get(arena); 226 } 227 } 228 229 #ifndef JEMALLOC_JET 230 static 231 #endif 232 size_t 233 extent_size_quantize_floor(size_t size) { 234 size_t ret; 235 pszind_t pind; 236 237 assert(size > 0); 238 assert((size & PAGE_MASK) == 0); 239 240 pind = sz_psz2ind(size - sz_large_pad + 1); 241 if (pind == 0) { 242 /* 243 * Avoid underflow. This short-circuit would also do the right 244 * thing for all sizes in the range for which there are 245 * PAGE-spaced size classes, but it's simplest to just handle 246 * the one case that would cause erroneous results. 247 */ 248 return size; 249 } 250 ret = sz_pind2sz(pind - 1) + sz_large_pad; 251 assert(ret <= size); 252 return ret; 253 } 254 255 #ifndef JEMALLOC_JET 256 static 257 #endif 258 size_t 259 extent_size_quantize_ceil(size_t size) { 260 size_t ret; 261 262 assert(size > 0); 263 assert(size - sz_large_pad <= SC_LARGE_MAXCLASS); 264 assert((size & PAGE_MASK) == 0); 265 266 ret = extent_size_quantize_floor(size); 267 if (ret < size) { 268 /* 269 * Skip a quantization that may have an adequately large extent, 270 * because under-sized extents may be mixed in. This only 271 * happens when an unusual size is requested, i.e. for aligned 272 * allocation, and is just one of several places where linear 273 * search would potentially find sufficiently aligned available 274 * memory somewhere lower. 275 */ 276 ret = sz_pind2sz(sz_psz2ind(ret - sz_large_pad + 1)) + 277 sz_large_pad; 278 } 279 return ret; 280 } 281 282 /* Generate pairing heap functions. */ 283 ph_gen(, extent_heap_, extent_heap_t, extent_t, ph_link, extent_snad_comp) 284 285 bool 286 extents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state, 287 bool delay_coalesce) { 288 if (malloc_mutex_init(&extents->mtx, "extents", WITNESS_RANK_EXTENTS, 289 malloc_mutex_rank_exclusive)) { 290 return true; 291 } 292 for (unsigned i = 0; i < SC_NPSIZES + 1; i++) { 293 extent_heap_new(&extents->heaps[i]); 294 } 295 bitmap_init(extents->bitmap, &extents_bitmap_info, true); 296 extent_list_init(&extents->lru); 297 atomic_store_zu(&extents->npages, 0, ATOMIC_RELAXED); 298 extents->state = state; 299 extents->delay_coalesce = delay_coalesce; 300 return false; 301 } 302 303 extent_state_t 304 extents_state_get(const extents_t *extents) { 305 return extents->state; 306 } 307 308 size_t 309 extents_npages_get(extents_t *extents) { 310 return atomic_load_zu(&extents->npages, ATOMIC_RELAXED); 311 } 312 313 size_t 314 extents_nextents_get(extents_t *extents, pszind_t pind) { 315 return atomic_load_zu(&extents->nextents[pind], ATOMIC_RELAXED); 316 } 317 318 size_t 319 extents_nbytes_get(extents_t *extents, pszind_t pind) { 320 return atomic_load_zu(&extents->nbytes[pind], ATOMIC_RELAXED); 321 } 322 323 static void 324 extents_stats_add(extents_t *extent, pszind_t pind, size_t sz) { 325 size_t cur = atomic_load_zu(&extent->nextents[pind], ATOMIC_RELAXED); 326 atomic_store_zu(&extent->nextents[pind], cur + 1, ATOMIC_RELAXED); 327 cur = atomic_load_zu(&extent->nbytes[pind], ATOMIC_RELAXED); 328 atomic_store_zu(&extent->nbytes[pind], cur + sz, ATOMIC_RELAXED); 329 } 330 331 static void 332 extents_stats_sub(extents_t *extent, pszind_t pind, size_t sz) { 333 size_t cur = atomic_load_zu(&extent->nextents[pind], ATOMIC_RELAXED); 334 atomic_store_zu(&extent->nextents[pind], cur - 1, ATOMIC_RELAXED); 335 cur = atomic_load_zu(&extent->nbytes[pind], ATOMIC_RELAXED); 336 atomic_store_zu(&extent->nbytes[pind], cur - sz, ATOMIC_RELAXED); 337 } 338 339 static void 340 extents_insert_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) { 341 malloc_mutex_assert_owner(tsdn, &extents->mtx); 342 assert(extent_state_get(extent) == extents->state); 343 344 size_t size = extent_size_get(extent); 345 size_t psz = extent_size_quantize_floor(size); 346 pszind_t pind = sz_psz2ind(psz); 347 if (extent_heap_empty(&extents->heaps[pind])) { 348 bitmap_unset(extents->bitmap, &extents_bitmap_info, 349 (size_t)pind); 350 } 351 extent_heap_insert(&extents->heaps[pind], extent); 352 353 if (config_stats) { 354 extents_stats_add(extents, pind, size); 355 } 356 357 extent_list_append(&extents->lru, extent); 358 size_t npages = size >> LG_PAGE; 359 /* 360 * All modifications to npages hold the mutex (as asserted above), so we 361 * don't need an atomic fetch-add; we can get by with a load followed by 362 * a store. 363 */ 364 size_t cur_extents_npages = 365 atomic_load_zu(&extents->npages, ATOMIC_RELAXED); 366 atomic_store_zu(&extents->npages, cur_extents_npages + npages, 367 ATOMIC_RELAXED); 368 } 369 370 static void 371 extents_remove_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) { 372 malloc_mutex_assert_owner(tsdn, &extents->mtx); 373 assert(extent_state_get(extent) == extents->state); 374 375 size_t size = extent_size_get(extent); 376 size_t psz = extent_size_quantize_floor(size); 377 pszind_t pind = sz_psz2ind(psz); 378 extent_heap_remove(&extents->heaps[pind], extent); 379 380 if (config_stats) { 381 extents_stats_sub(extents, pind, size); 382 } 383 384 if (extent_heap_empty(&extents->heaps[pind])) { 385 bitmap_set(extents->bitmap, &extents_bitmap_info, 386 (size_t)pind); 387 } 388 extent_list_remove(&extents->lru, extent); 389 size_t npages = size >> LG_PAGE; 390 /* 391 * As in extents_insert_locked, we hold extents->mtx and so don't need 392 * atomic operations for updating extents->npages. 393 */ 394 size_t cur_extents_npages = 395 atomic_load_zu(&extents->npages, ATOMIC_RELAXED); 396 assert(cur_extents_npages >= npages); 397 atomic_store_zu(&extents->npages, 398 cur_extents_npages - (size >> LG_PAGE), ATOMIC_RELAXED); 399 } 400 401 /* 402 * Find an extent with size [min_size, max_size) to satisfy the alignment 403 * requirement. For each size, try only the first extent in the heap. 404 */ 405 static extent_t * 406 extents_fit_alignment(extents_t *extents, size_t min_size, size_t max_size, 407 size_t alignment) { 408 pszind_t pind = sz_psz2ind(extent_size_quantize_ceil(min_size)); 409 pszind_t pind_max = sz_psz2ind(extent_size_quantize_ceil(max_size)); 410 411 for (pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap, 412 &extents_bitmap_info, (size_t)pind); i < pind_max; i = 413 (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info, 414 (size_t)i+1)) { 415 assert(i < SC_NPSIZES); 416 assert(!extent_heap_empty(&extents->heaps[i])); 417 extent_t *extent = extent_heap_first(&extents->heaps[i]); 418 uintptr_t base = (uintptr_t)extent_base_get(extent); 419 size_t candidate_size = extent_size_get(extent); 420 assert(candidate_size >= min_size); 421 422 uintptr_t next_align = ALIGNMENT_CEILING((uintptr_t)base, 423 PAGE_CEILING(alignment)); 424 if (base > next_align || base + candidate_size <= next_align) { 425 /* Overflow or not crossing the next alignment. */ 426 continue; 427 } 428 429 size_t leadsize = next_align - base; 430 if (candidate_size - leadsize >= min_size) { 431 return extent; 432 } 433 } 434 435 return NULL; 436 } 437 438 /* 439 * Do first-fit extent selection, i.e. select the oldest/lowest extent that is 440 * large enough. 441 */ 442 static extent_t * 443 extents_first_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents, 444 size_t size) { 445 extent_t *ret = NULL; 446 447 pszind_t pind = sz_psz2ind(extent_size_quantize_ceil(size)); 448 449 if (!maps_coalesce && !opt_retain) { 450 /* 451 * No split / merge allowed (Windows w/o retain). Try exact fit 452 * only. 453 */ 454 return extent_heap_empty(&extents->heaps[pind]) ? NULL : 455 extent_heap_first(&extents->heaps[pind]); 456 } 457 458 for (pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap, 459 &extents_bitmap_info, (size_t)pind); 460 i < SC_NPSIZES + 1; 461 i = (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info, 462 (size_t)i+1)) { 463 assert(!extent_heap_empty(&extents->heaps[i])); 464 extent_t *extent = extent_heap_first(&extents->heaps[i]); 465 assert(extent_size_get(extent) >= size); 466 /* 467 * In order to reduce fragmentation, avoid reusing and splitting 468 * large extents for much smaller sizes. 469 * 470 * Only do check for dirty extents (delay_coalesce). 471 */ 472 if (extents->delay_coalesce && 473 (sz_pind2sz(i) >> opt_lg_extent_max_active_fit) > size) { 474 break; 475 } 476 if (ret == NULL || extent_snad_comp(extent, ret) < 0) { 477 ret = extent; 478 } 479 if (i == SC_NPSIZES) { 480 break; 481 } 482 assert(i < SC_NPSIZES); 483 } 484 485 return ret; 486 } 487 488 /* 489 * Do first-fit extent selection, where the selection policy choice is 490 * based on extents->delay_coalesce. 491 */ 492 static extent_t * 493 extents_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents, 494 size_t esize, size_t alignment) { 495 malloc_mutex_assert_owner(tsdn, &extents->mtx); 496 497 size_t max_size = esize + PAGE_CEILING(alignment) - PAGE; 498 /* Beware size_t wrap-around. */ 499 if (max_size < esize) { 500 return NULL; 501 } 502 503 extent_t *extent = 504 extents_first_fit_locked(tsdn, arena, extents, max_size); 505 506 if (alignment > PAGE && extent == NULL) { 507 /* 508 * max_size guarantees the alignment requirement but is rather 509 * pessimistic. Next we try to satisfy the aligned allocation 510 * with sizes in [esize, max_size). 511 */ 512 extent = extents_fit_alignment(extents, esize, max_size, 513 alignment); 514 } 515 516 return extent; 517 } 518 519 static bool 520 extent_try_delayed_coalesce(tsdn_t *tsdn, arena_t *arena, 521 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents, 522 extent_t *extent) { 523 extent_state_set(extent, extent_state_active); 524 bool coalesced; 525 extent = extent_try_coalesce(tsdn, arena, r_extent_hooks, rtree_ctx, 526 extents, extent, &coalesced, false); 527 extent_state_set(extent, extents_state_get(extents)); 528 529 if (!coalesced) { 530 return true; 531 } 532 extents_insert_locked(tsdn, extents, extent); 533 return false; 534 } 535 536 extent_t * 537 extents_alloc(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks, 538 extents_t *extents, void *new_addr, size_t size, size_t pad, 539 size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) { 540 assert(size + pad != 0); 541 assert(alignment != 0); 542 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 543 WITNESS_RANK_CORE, 0); 544 545 extent_t *extent = extent_recycle(tsdn, arena, r_extent_hooks, extents, 546 new_addr, size, pad, alignment, slab, szind, zero, commit, false); 547 assert(extent == NULL || extent_dumpable_get(extent)); 548 return extent; 549 } 550 551 void 552 extents_dalloc(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks, 553 extents_t *extents, extent_t *extent) { 554 assert(extent_base_get(extent) != NULL); 555 assert(extent_size_get(extent) != 0); 556 assert(extent_dumpable_get(extent)); 557 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 558 WITNESS_RANK_CORE, 0); 559 560 extent_addr_set(extent, extent_base_get(extent)); 561 extent_zeroed_set(extent, false); 562 563 extent_record(tsdn, arena, r_extent_hooks, extents, extent, false); 564 } 565 566 extent_t * 567 extents_evict(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks, 568 extents_t *extents, size_t npages_min) { 569 rtree_ctx_t rtree_ctx_fallback; 570 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 571 572 malloc_mutex_lock(tsdn, &extents->mtx); 573 574 /* 575 * Get the LRU coalesced extent, if any. If coalescing was delayed, 576 * the loop will iterate until the LRU extent is fully coalesced. 577 */ 578 extent_t *extent; 579 while (true) { 580 /* Get the LRU extent, if any. */ 581 extent = extent_list_first(&extents->lru); 582 if (extent == NULL) { 583 goto label_return; 584 } 585 /* Check the eviction limit. */ 586 size_t extents_npages = atomic_load_zu(&extents->npages, 587 ATOMIC_RELAXED); 588 if (extents_npages <= npages_min) { 589 extent = NULL; 590 goto label_return; 591 } 592 extents_remove_locked(tsdn, extents, extent); 593 if (!extents->delay_coalesce) { 594 break; 595 } 596 /* Try to coalesce. */ 597 if (extent_try_delayed_coalesce(tsdn, arena, r_extent_hooks, 598 rtree_ctx, extents, extent)) { 599 break; 600 } 601 /* 602 * The LRU extent was just coalesced and the result placed in 603 * the LRU at its neighbor's position. Start over. 604 */ 605 } 606 607 /* 608 * Either mark the extent active or deregister it to protect against 609 * concurrent operations. 610 */ 611 switch (extents_state_get(extents)) { 612 case extent_state_active: 613 not_reached(); 614 case extent_state_dirty: 615 case extent_state_muzzy: 616 extent_state_set(extent, extent_state_active); 617 break; 618 case extent_state_retained: 619 extent_deregister(tsdn, extent); 620 break; 621 default: 622 not_reached(); 623 } 624 625 label_return: 626 malloc_mutex_unlock(tsdn, &extents->mtx); 627 return extent; 628 } 629 630 /* 631 * This can only happen when we fail to allocate a new extent struct (which 632 * indicates OOM), e.g. when trying to split an existing extent. 633 */ 634 static void 635 extents_abandon_vm(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks, 636 extents_t *extents, extent_t *extent, bool growing_retained) { 637 size_t sz = extent_size_get(extent); 638 if (config_stats) { 639 arena_stats_accum_zu(&arena->stats.abandoned_vm, sz); 640 } 641 /* 642 * Leak extent after making sure its pages have already been purged, so 643 * that this is only a virtual memory leak. 644 */ 645 if (extents_state_get(extents) == extent_state_dirty) { 646 if (extent_purge_lazy_impl(tsdn, arena, r_extent_hooks, 647 extent, 0, sz, growing_retained)) { 648 extent_purge_forced_impl(tsdn, arena, r_extent_hooks, 649 extent, 0, extent_size_get(extent), 650 growing_retained); 651 } 652 } 653 extent_dalloc(tsdn, arena, extent); 654 } 655 656 void 657 extents_prefork(tsdn_t *tsdn, extents_t *extents) { 658 malloc_mutex_prefork(tsdn, &extents->mtx); 659 } 660 661 void 662 extents_postfork_parent(tsdn_t *tsdn, extents_t *extents) { 663 malloc_mutex_postfork_parent(tsdn, &extents->mtx); 664 } 665 666 void 667 extents_postfork_child(tsdn_t *tsdn, extents_t *extents) { 668 malloc_mutex_postfork_child(tsdn, &extents->mtx); 669 } 670 671 static void 672 extent_deactivate_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents, 673 extent_t *extent) { 674 assert(extent_arena_get(extent) == arena); 675 assert(extent_state_get(extent) == extent_state_active); 676 677 extent_state_set(extent, extents_state_get(extents)); 678 extents_insert_locked(tsdn, extents, extent); 679 } 680 681 static void 682 extent_deactivate(tsdn_t *tsdn, arena_t *arena, extents_t *extents, 683 extent_t *extent) { 684 malloc_mutex_lock(tsdn, &extents->mtx); 685 extent_deactivate_locked(tsdn, arena, extents, extent); 686 malloc_mutex_unlock(tsdn, &extents->mtx); 687 } 688 689 static void 690 extent_activate_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents, 691 extent_t *extent) { 692 assert(extent_arena_get(extent) == arena); 693 assert(extent_state_get(extent) == extents_state_get(extents)); 694 695 extents_remove_locked(tsdn, extents, extent); 696 extent_state_set(extent, extent_state_active); 697 } 698 699 static bool 700 extent_rtree_leaf_elms_lookup(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, 701 const extent_t *extent, bool dependent, bool init_missing, 702 rtree_leaf_elm_t **r_elm_a, rtree_leaf_elm_t **r_elm_b) { 703 *r_elm_a = rtree_leaf_elm_lookup(tsdn, &extents_rtree, rtree_ctx, 704 (uintptr_t)extent_base_get(extent), dependent, init_missing); 705 if (!dependent && *r_elm_a == NULL) { 706 return true; 707 } 708 assert(*r_elm_a != NULL); 709 710 *r_elm_b = rtree_leaf_elm_lookup(tsdn, &extents_rtree, rtree_ctx, 711 (uintptr_t)extent_last_get(extent), dependent, init_missing); 712 if (!dependent && *r_elm_b == NULL) { 713 return true; 714 } 715 assert(*r_elm_b != NULL); 716 717 return false; 718 } 719 720 static void 721 extent_rtree_write_acquired(tsdn_t *tsdn, rtree_leaf_elm_t *elm_a, 722 rtree_leaf_elm_t *elm_b, extent_t *extent, szind_t szind, bool slab) { 723 rtree_leaf_elm_write(tsdn, &extents_rtree, elm_a, extent, szind, slab); 724 if (elm_b != NULL) { 725 rtree_leaf_elm_write(tsdn, &extents_rtree, elm_b, extent, szind, 726 slab); 727 } 728 } 729 730 static void 731 extent_interior_register(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, extent_t *extent, 732 szind_t szind) { 733 assert(extent_slab_get(extent)); 734 735 /* Register interior. */ 736 for (size_t i = 1; i < (extent_size_get(extent) >> LG_PAGE) - 1; i++) { 737 rtree_write(tsdn, &extents_rtree, rtree_ctx, 738 (uintptr_t)extent_base_get(extent) + (uintptr_t)(i << 739 LG_PAGE), extent, szind, true); 740 } 741 } 742 743 static void 744 extent_gdump_add(tsdn_t *tsdn, const extent_t *extent) { 745 cassert(config_prof); 746 /* prof_gdump() requirement. */ 747 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 748 WITNESS_RANK_CORE, 0); 749 750 if (opt_prof && extent_state_get(extent) == extent_state_active) { 751 size_t nadd = extent_size_get(extent) >> LG_PAGE; 752 size_t cur = atomic_fetch_add_zu(&curpages, nadd, 753 ATOMIC_RELAXED) + nadd; 754 size_t high = atomic_load_zu(&highpages, ATOMIC_RELAXED); 755 while (cur > high && !atomic_compare_exchange_weak_zu( 756 &highpages, &high, cur, ATOMIC_RELAXED, ATOMIC_RELAXED)) { 757 /* 758 * Don't refresh cur, because it may have decreased 759 * since this thread lost the highpages update race. 760 * Note that high is updated in case of CAS failure. 761 */ 762 } 763 if (cur > high && prof_gdump_get_unlocked()) { 764 prof_gdump(tsdn); 765 } 766 } 767 } 768 769 static void 770 extent_gdump_sub(tsdn_t *tsdn, const extent_t *extent) { 771 cassert(config_prof); 772 773 if (opt_prof && extent_state_get(extent) == extent_state_active) { 774 size_t nsub = extent_size_get(extent) >> LG_PAGE; 775 assert(atomic_load_zu(&curpages, ATOMIC_RELAXED) >= nsub); 776 atomic_fetch_sub_zu(&curpages, nsub, ATOMIC_RELAXED); 777 } 778 } 779 780 static bool 781 extent_register_impl(tsdn_t *tsdn, extent_t *extent, bool gdump_add) { 782 rtree_ctx_t rtree_ctx_fallback; 783 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 784 rtree_leaf_elm_t *elm_a, *elm_b; 785 786 /* 787 * We need to hold the lock to protect against a concurrent coalesce 788 * operation that sees us in a partial state. 789 */ 790 extent_lock(tsdn, extent); 791 792 if (extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, extent, false, true, 793 &elm_a, &elm_b)) { 794 extent_unlock(tsdn, extent); 795 return true; 796 } 797 798 szind_t szind = extent_szind_get_maybe_invalid(extent); 799 bool slab = extent_slab_get(extent); 800 extent_rtree_write_acquired(tsdn, elm_a, elm_b, extent, szind, slab); 801 if (slab) { 802 extent_interior_register(tsdn, rtree_ctx, extent, szind); 803 } 804 805 extent_unlock(tsdn, extent); 806 807 if (config_prof && gdump_add) { 808 extent_gdump_add(tsdn, extent); 809 } 810 811 return false; 812 } 813 814 static bool 815 extent_register(tsdn_t *tsdn, extent_t *extent) { 816 return extent_register_impl(tsdn, extent, true); 817 } 818 819 static bool 820 extent_register_no_gdump_add(tsdn_t *tsdn, extent_t *extent) { 821 return extent_register_impl(tsdn, extent, false); 822 } 823 824 static void 825 extent_reregister(tsdn_t *tsdn, extent_t *extent) { 826 bool err = extent_register(tsdn, extent); 827 assert(!err); 828 } 829 830 /* 831 * Removes all pointers to the given extent from the global rtree indices for 832 * its interior. This is relevant for slab extents, for which we need to do 833 * metadata lookups at places other than the head of the extent. We deregister 834 * on the interior, then, when an extent moves from being an active slab to an 835 * inactive state. 836 */ 837 static void 838 extent_interior_deregister(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, 839 extent_t *extent) { 840 size_t i; 841 842 assert(extent_slab_get(extent)); 843 844 for (i = 1; i < (extent_size_get(extent) >> LG_PAGE) - 1; i++) { 845 rtree_clear(tsdn, &extents_rtree, rtree_ctx, 846 (uintptr_t)extent_base_get(extent) + (uintptr_t)(i << 847 LG_PAGE)); 848 } 849 } 850 851 /* 852 * Removes all pointers to the given extent from the global rtree. 853 */ 854 static void 855 extent_deregister_impl(tsdn_t *tsdn, extent_t *extent, bool gdump) { 856 rtree_ctx_t rtree_ctx_fallback; 857 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 858 rtree_leaf_elm_t *elm_a, *elm_b; 859 extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, extent, true, false, 860 &elm_a, &elm_b); 861 862 extent_lock(tsdn, extent); 863 864 extent_rtree_write_acquired(tsdn, elm_a, elm_b, NULL, SC_NSIZES, false); 865 if (extent_slab_get(extent)) { 866 extent_interior_deregister(tsdn, rtree_ctx, extent); 867 extent_slab_set(extent, false); 868 } 869 870 extent_unlock(tsdn, extent); 871 872 if (config_prof && gdump) { 873 extent_gdump_sub(tsdn, extent); 874 } 875 } 876 877 static void 878 extent_deregister(tsdn_t *tsdn, extent_t *extent) { 879 extent_deregister_impl(tsdn, extent, true); 880 } 881 882 static void 883 extent_deregister_no_gdump_sub(tsdn_t *tsdn, extent_t *extent) { 884 extent_deregister_impl(tsdn, extent, false); 885 } 886 887 /* 888 * Tries to find and remove an extent from extents that can be used for the 889 * given allocation request. 890 */ 891 static extent_t * 892 extent_recycle_extract(tsdn_t *tsdn, arena_t *arena, 893 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents, 894 void *new_addr, size_t size, size_t pad, size_t alignment, bool slab, 895 bool growing_retained) { 896 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 897 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 898 assert(alignment > 0); 899 if (config_debug && new_addr != NULL) { 900 /* 901 * Non-NULL new_addr has two use cases: 902 * 903 * 1) Recycle a known-extant extent, e.g. during purging. 904 * 2) Perform in-place expanding reallocation. 905 * 906 * Regardless of use case, new_addr must either refer to a 907 * non-existing extent, or to the base of an extant extent, 908 * since only active slabs support interior lookups (which of 909 * course cannot be recycled). 910 */ 911 assert(PAGE_ADDR2BASE(new_addr) == new_addr); 912 assert(pad == 0); 913 assert(alignment <= PAGE); 914 } 915 916 size_t esize = size + pad; 917 malloc_mutex_lock(tsdn, &extents->mtx); 918 extent_hooks_assure_initialized(arena, r_extent_hooks); 919 extent_t *extent; 920 if (new_addr != NULL) { 921 extent = extent_lock_from_addr(tsdn, rtree_ctx, new_addr, 922 false); 923 if (extent != NULL) { 924 /* 925 * We might null-out extent to report an error, but we 926 * still need to unlock the associated mutex after. 927 */ 928 extent_t *unlock_extent = extent; 929 assert(extent_base_get(extent) == new_addr); 930 if (extent_arena_get(extent) != arena || 931 extent_size_get(extent) < esize || 932 extent_state_get(extent) != 933 extents_state_get(extents)) { 934 extent = NULL; 935 } 936 extent_unlock(tsdn, unlock_extent); 937 } 938 } else { 939 extent = extents_fit_locked(tsdn, arena, extents, esize, 940 alignment); 941 } 942 if (extent == NULL) { 943 malloc_mutex_unlock(tsdn, &extents->mtx); 944 return NULL; 945 } 946 947 extent_activate_locked(tsdn, arena, extents, extent); 948 malloc_mutex_unlock(tsdn, &extents->mtx); 949 950 return extent; 951 } 952 953 /* 954 * Given an allocation request and an extent guaranteed to be able to satisfy 955 * it, this splits off lead and trail extents, leaving extent pointing to an 956 * extent satisfying the allocation. 957 * This function doesn't put lead or trail into any extents_t; it's the caller's 958 * job to ensure that they can be reused. 959 */ 960 typedef enum { 961 /* 962 * Split successfully. lead, extent, and trail, are modified to extents 963 * describing the ranges before, in, and after the given allocation. 964 */ 965 extent_split_interior_ok, 966 /* 967 * The extent can't satisfy the given allocation request. None of the 968 * input extent_t *s are touched. 969 */ 970 extent_split_interior_cant_alloc, 971 /* 972 * In a potentially invalid state. Must leak (if *to_leak is non-NULL), 973 * and salvage what's still salvageable (if *to_salvage is non-NULL). 974 * None of lead, extent, or trail are valid. 975 */ 976 extent_split_interior_error 977 } extent_split_interior_result_t; 978 979 static extent_split_interior_result_t 980 extent_split_interior(tsdn_t *tsdn, arena_t *arena, 981 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, 982 /* The result of splitting, in case of success. */ 983 extent_t **extent, extent_t **lead, extent_t **trail, 984 /* The mess to clean up, in case of error. */ 985 extent_t **to_leak, extent_t **to_salvage, 986 void *new_addr, size_t size, size_t pad, size_t alignment, bool slab, 987 szind_t szind, bool growing_retained) { 988 size_t esize = size + pad; 989 size_t leadsize = ALIGNMENT_CEILING((uintptr_t)extent_base_get(*extent), 990 PAGE_CEILING(alignment)) - (uintptr_t)extent_base_get(*extent); 991 assert(new_addr == NULL || leadsize == 0); 992 if (extent_size_get(*extent) < leadsize + esize) { 993 return extent_split_interior_cant_alloc; 994 } 995 size_t trailsize = extent_size_get(*extent) - leadsize - esize; 996 997 *lead = NULL; 998 *trail = NULL; 999 *to_leak = NULL; 1000 *to_salvage = NULL; 1001 1002 /* Split the lead. */ 1003 if (leadsize != 0) { 1004 *lead = *extent; 1005 *extent = extent_split_impl(tsdn, arena, r_extent_hooks, 1006 *lead, leadsize, SC_NSIZES, false, esize + trailsize, szind, 1007 slab, growing_retained); 1008 if (*extent == NULL) { 1009 *to_leak = *lead; 1010 *lead = NULL; 1011 return extent_split_interior_error; 1012 } 1013 } 1014 1015 /* Split the trail. */ 1016 if (trailsize != 0) { 1017 *trail = extent_split_impl(tsdn, arena, r_extent_hooks, *extent, 1018 esize, szind, slab, trailsize, SC_NSIZES, false, 1019 growing_retained); 1020 if (*trail == NULL) { 1021 *to_leak = *extent; 1022 *to_salvage = *lead; 1023 *lead = NULL; 1024 *extent = NULL; 1025 return extent_split_interior_error; 1026 } 1027 } 1028 1029 if (leadsize == 0 && trailsize == 0) { 1030 /* 1031 * Splitting causes szind to be set as a side effect, but no 1032 * splitting occurred. 1033 */ 1034 extent_szind_set(*extent, szind); 1035 if (szind != SC_NSIZES) { 1036 rtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx, 1037 (uintptr_t)extent_addr_get(*extent), szind, slab); 1038 if (slab && extent_size_get(*extent) > PAGE) { 1039 rtree_szind_slab_update(tsdn, &extents_rtree, 1040 rtree_ctx, 1041 (uintptr_t)extent_past_get(*extent) - 1042 (uintptr_t)PAGE, szind, slab); 1043 } 1044 } 1045 } 1046 1047 return extent_split_interior_ok; 1048 } 1049 1050 /* 1051 * This fulfills the indicated allocation request out of the given extent (which 1052 * the caller should have ensured was big enough). If there's any unused space 1053 * before or after the resulting allocation, that space is given its own extent 1054 * and put back into extents. 1055 */ 1056 static extent_t * 1057 extent_recycle_split(tsdn_t *tsdn, arena_t *arena, 1058 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents, 1059 void *new_addr, size_t size, size_t pad, size_t alignment, bool slab, 1060 szind_t szind, extent_t *extent, bool growing_retained) { 1061 extent_t *lead; 1062 extent_t *trail; 1063 extent_t *to_leak; 1064 extent_t *to_salvage; 1065 1066 extent_split_interior_result_t result = extent_split_interior( 1067 tsdn, arena, r_extent_hooks, rtree_ctx, &extent, &lead, &trail, 1068 &to_leak, &to_salvage, new_addr, size, pad, alignment, slab, szind, 1069 growing_retained); 1070 1071 if (!maps_coalesce && result != extent_split_interior_ok 1072 && !opt_retain) { 1073 /* 1074 * Split isn't supported (implies Windows w/o retain). Avoid 1075 * leaking the extents. 1076 */ 1077 assert(to_leak != NULL && lead == NULL && trail == NULL); 1078 extent_deactivate(tsdn, arena, extents, to_leak); 1079 return NULL; 1080 } 1081 1082 if (result == extent_split_interior_ok) { 1083 if (lead != NULL) { 1084 extent_deactivate(tsdn, arena, extents, lead); 1085 } 1086 if (trail != NULL) { 1087 extent_deactivate(tsdn, arena, extents, trail); 1088 } 1089 return extent; 1090 } else { 1091 /* 1092 * We should have picked an extent that was large enough to 1093 * fulfill our allocation request. 1094 */ 1095 assert(result == extent_split_interior_error); 1096 if (to_salvage != NULL) { 1097 extent_deregister(tsdn, to_salvage); 1098 } 1099 if (to_leak != NULL) { 1100 void *leak = extent_base_get(to_leak); 1101 extent_deregister_no_gdump_sub(tsdn, to_leak); 1102 extents_abandon_vm(tsdn, arena, r_extent_hooks, extents, 1103 to_leak, growing_retained); 1104 assert(extent_lock_from_addr(tsdn, rtree_ctx, leak, 1105 false) == NULL); 1106 } 1107 return NULL; 1108 } 1109 unreachable(); 1110 } 1111 1112 static bool 1113 extent_need_manual_zero(arena_t *arena) { 1114 /* 1115 * Need to manually zero the extent on repopulating if either; 1) non 1116 * default extent hooks installed (in which case the purge semantics may 1117 * change); or 2) transparent huge pages enabled. 1118 */ 1119 return (!arena_has_default_hooks(arena) || 1120 (opt_thp == thp_mode_always)); 1121 } 1122 1123 /* 1124 * Tries to satisfy the given allocation request by reusing one of the extents 1125 * in the given extents_t. 1126 */ 1127 static extent_t * 1128 extent_recycle(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks, 1129 extents_t *extents, void *new_addr, size_t size, size_t pad, 1130 size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit, 1131 bool growing_retained) { 1132 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1133 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 1134 assert(new_addr == NULL || !slab); 1135 assert(pad == 0 || !slab); 1136 assert(!*zero || !slab); 1137 1138 rtree_ctx_t rtree_ctx_fallback; 1139 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 1140 1141 extent_t *extent = extent_recycle_extract(tsdn, arena, r_extent_hooks, 1142 rtree_ctx, extents, new_addr, size, pad, alignment, slab, 1143 growing_retained); 1144 if (extent == NULL) { 1145 return NULL; 1146 } 1147 1148 extent = extent_recycle_split(tsdn, arena, r_extent_hooks, rtree_ctx, 1149 extents, new_addr, size, pad, alignment, slab, szind, extent, 1150 growing_retained); 1151 if (extent == NULL) { 1152 return NULL; 1153 } 1154 1155 if (*commit && !extent_committed_get(extent)) { 1156 if (extent_commit_impl(tsdn, arena, r_extent_hooks, extent, 1157 0, extent_size_get(extent), growing_retained)) { 1158 extent_record(tsdn, arena, r_extent_hooks, extents, 1159 extent, growing_retained); 1160 return NULL; 1161 } 1162 if (!extent_need_manual_zero(arena)) { 1163 extent_zeroed_set(extent, true); 1164 } 1165 } 1166 1167 if (extent_committed_get(extent)) { 1168 *commit = true; 1169 } 1170 if (extent_zeroed_get(extent)) { 1171 *zero = true; 1172 } 1173 1174 if (pad != 0) { 1175 extent_addr_randomize(tsdn, extent, alignment); 1176 } 1177 assert(extent_state_get(extent) == extent_state_active); 1178 if (slab) { 1179 extent_slab_set(extent, slab); 1180 extent_interior_register(tsdn, rtree_ctx, extent, szind); 1181 } 1182 1183 if (*zero) { 1184 void *addr = extent_base_get(extent); 1185 if (!extent_zeroed_get(extent)) { 1186 size_t size = extent_size_get(extent); 1187 if (extent_need_manual_zero(arena) || 1188 pages_purge_forced(addr, size)) { 1189 memset(addr, 0, size); 1190 } 1191 } else if (config_debug) { 1192 size_t *p = (size_t *)(uintptr_t)addr; 1193 /* Check the first page only. */ 1194 for (size_t i = 0; i < PAGE / sizeof(size_t); i++) { 1195 assert(p[i] == 0); 1196 } 1197 } 1198 } 1199 return extent; 1200 } 1201 1202 /* 1203 * If the caller specifies (!*zero), it is still possible to receive zeroed 1204 * memory, in which case *zero is toggled to true. arena_extent_alloc() takes 1205 * advantage of this to avoid demanding zeroed extents, but taking advantage of 1206 * them if they are returned. 1207 */ 1208 static void * 1209 extent_alloc_core(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size, 1210 size_t alignment, bool *zero, bool *commit, dss_prec_t dss_prec) { 1211 void *ret; 1212 1213 assert(size != 0); 1214 assert(alignment != 0); 1215 1216 /* "primary" dss. */ 1217 if (have_dss && dss_prec == dss_prec_primary && (ret = 1218 extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero, 1219 commit)) != NULL) { 1220 return ret; 1221 } 1222 /* mmap. */ 1223 if ((ret = extent_alloc_mmap(new_addr, size, alignment, zero, commit)) 1224 != NULL) { 1225 return ret; 1226 } 1227 /* "secondary" dss. */ 1228 if (have_dss && dss_prec == dss_prec_secondary && (ret = 1229 extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero, 1230 commit)) != NULL) { 1231 return ret; 1232 } 1233 1234 /* All strategies for allocation failed. */ 1235 return NULL; 1236 } 1237 1238 static void * 1239 extent_alloc_default_impl(tsdn_t *tsdn, arena_t *arena, void *new_addr, 1240 size_t size, size_t alignment, bool *zero, bool *commit) { 1241 void *ret = extent_alloc_core(tsdn, arena, new_addr, size, alignment, zero, 1242 commit, (dss_prec_t)atomic_load_u(&arena->dss_prec, 1243 ATOMIC_RELAXED)); 1244 if (have_madvise_huge && ret) { 1245 pages_set_thp_state(ret, size); 1246 } 1247 return ret; 1248 } 1249 1250 static void * 1251 extent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr, size_t size, 1252 size_t alignment, bool *zero, bool *commit, unsigned arena_ind) { 1253 tsdn_t *tsdn; 1254 arena_t *arena; 1255 1256 tsdn = tsdn_fetch(); 1257 arena = arena_get(tsdn, arena_ind, false); 1258 /* 1259 * The arena we're allocating on behalf of must have been initialized 1260 * already. 1261 */ 1262 assert(arena != NULL); 1263 1264 return extent_alloc_default_impl(tsdn, arena, new_addr, size, 1265 ALIGNMENT_CEILING(alignment, PAGE), zero, commit); 1266 } 1267 1268 static void 1269 extent_hook_pre_reentrancy(tsdn_t *tsdn, arena_t *arena) { 1270 tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn); 1271 if (arena == arena_get(tsd_tsdn(tsd), 0, false)) { 1272 /* 1273 * The only legitimate case of customized extent hooks for a0 is 1274 * hooks with no allocation activities. One such example is to 1275 * place metadata on pre-allocated resources such as huge pages. 1276 * In that case, rely on reentrancy_level checks to catch 1277 * infinite recursions. 1278 */ 1279 pre_reentrancy(tsd, NULL); 1280 } else { 1281 pre_reentrancy(tsd, arena); 1282 } 1283 } 1284 1285 static void 1286 extent_hook_post_reentrancy(tsdn_t *tsdn) { 1287 tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn); 1288 post_reentrancy(tsd); 1289 } 1290 1291 /* 1292 * If virtual memory is retained, create increasingly larger extents from which 1293 * to split requested extents in order to limit the total number of disjoint 1294 * virtual memory ranges retained by each arena. 1295 */ 1296 static extent_t * 1297 extent_grow_retained(tsdn_t *tsdn, arena_t *arena, 1298 extent_hooks_t **r_extent_hooks, size_t size, size_t pad, size_t alignment, 1299 bool slab, szind_t szind, bool *zero, bool *commit) { 1300 malloc_mutex_assert_owner(tsdn, &arena->extent_grow_mtx); 1301 assert(pad == 0 || !slab); 1302 assert(!*zero || !slab); 1303 1304 size_t esize = size + pad; 1305 size_t alloc_size_min = esize + PAGE_CEILING(alignment) - PAGE; 1306 /* Beware size_t wrap-around. */ 1307 if (alloc_size_min < esize) { 1308 goto label_err; 1309 } 1310 /* 1311 * Find the next extent size in the series that would be large enough to 1312 * satisfy this request. 1313 */ 1314 pszind_t egn_skip = 0; 1315 size_t alloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip); 1316 while (alloc_size < alloc_size_min) { 1317 egn_skip++; 1318 if (arena->extent_grow_next + egn_skip >= 1319 sz_psz2ind(SC_LARGE_MAXCLASS)) { 1320 /* Outside legal range. */ 1321 goto label_err; 1322 } 1323 alloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip); 1324 } 1325 1326 extent_t *extent = extent_alloc(tsdn, arena); 1327 if (extent == NULL) { 1328 goto label_err; 1329 } 1330 bool zeroed = false; 1331 bool committed = false; 1332 1333 void *ptr; 1334 if (*r_extent_hooks == &extent_hooks_default) { 1335 ptr = extent_alloc_default_impl(tsdn, arena, NULL, 1336 alloc_size, PAGE, &zeroed, &committed); 1337 } else { 1338 extent_hook_pre_reentrancy(tsdn, arena); 1339 ptr = (*r_extent_hooks)->alloc(*r_extent_hooks, NULL, 1340 alloc_size, PAGE, &zeroed, &committed, 1341 arena_ind_get(arena)); 1342 extent_hook_post_reentrancy(tsdn); 1343 } 1344 1345 extent_init(extent, arena, ptr, alloc_size, false, SC_NSIZES, 1346 arena_extent_sn_next(arena), extent_state_active, zeroed, 1347 committed, true, EXTENT_IS_HEAD); 1348 if (ptr == NULL) { 1349 extent_dalloc(tsdn, arena, extent); 1350 goto label_err; 1351 } 1352 1353 if (extent_register_no_gdump_add(tsdn, extent)) { 1354 extent_dalloc(tsdn, arena, extent); 1355 goto label_err; 1356 } 1357 1358 if (extent_zeroed_get(extent) && extent_committed_get(extent)) { 1359 *zero = true; 1360 } 1361 if (extent_committed_get(extent)) { 1362 *commit = true; 1363 } 1364 1365 rtree_ctx_t rtree_ctx_fallback; 1366 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 1367 1368 extent_t *lead; 1369 extent_t *trail; 1370 extent_t *to_leak; 1371 extent_t *to_salvage; 1372 extent_split_interior_result_t result = extent_split_interior( 1373 tsdn, arena, r_extent_hooks, rtree_ctx, &extent, &lead, &trail, 1374 &to_leak, &to_salvage, NULL, size, pad, alignment, slab, szind, 1375 true); 1376 1377 if (result == extent_split_interior_ok) { 1378 if (lead != NULL) { 1379 extent_record(tsdn, arena, r_extent_hooks, 1380 &arena->extents_retained, lead, true); 1381 } 1382 if (trail != NULL) { 1383 extent_record(tsdn, arena, r_extent_hooks, 1384 &arena->extents_retained, trail, true); 1385 } 1386 } else { 1387 /* 1388 * We should have allocated a sufficiently large extent; the 1389 * cant_alloc case should not occur. 1390 */ 1391 assert(result == extent_split_interior_error); 1392 if (to_salvage != NULL) { 1393 if (config_prof) { 1394 extent_gdump_add(tsdn, to_salvage); 1395 } 1396 extent_record(tsdn, arena, r_extent_hooks, 1397 &arena->extents_retained, to_salvage, true); 1398 } 1399 if (to_leak != NULL) { 1400 extent_deregister_no_gdump_sub(tsdn, to_leak); 1401 extents_abandon_vm(tsdn, arena, r_extent_hooks, 1402 &arena->extents_retained, to_leak, true); 1403 } 1404 goto label_err; 1405 } 1406 1407 if (*commit && !extent_committed_get(extent)) { 1408 if (extent_commit_impl(tsdn, arena, r_extent_hooks, extent, 0, 1409 extent_size_get(extent), true)) { 1410 extent_record(tsdn, arena, r_extent_hooks, 1411 &arena->extents_retained, extent, true); 1412 goto label_err; 1413 } 1414 if (!extent_need_manual_zero(arena)) { 1415 extent_zeroed_set(extent, true); 1416 } 1417 } 1418 1419 /* 1420 * Increment extent_grow_next if doing so wouldn't exceed the allowed 1421 * range. 1422 */ 1423 if (arena->extent_grow_next + egn_skip + 1 <= 1424 arena->retain_grow_limit) { 1425 arena->extent_grow_next += egn_skip + 1; 1426 } else { 1427 arena->extent_grow_next = arena->retain_grow_limit; 1428 } 1429 /* All opportunities for failure are past. */ 1430 malloc_mutex_unlock(tsdn, &arena->extent_grow_mtx); 1431 1432 if (config_prof) { 1433 /* Adjust gdump stats now that extent is final size. */ 1434 extent_gdump_add(tsdn, extent); 1435 } 1436 if (pad != 0) { 1437 extent_addr_randomize(tsdn, extent, alignment); 1438 } 1439 if (slab) { 1440 rtree_ctx_t rtree_ctx_fallback; 1441 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, 1442 &rtree_ctx_fallback); 1443 1444 extent_slab_set(extent, true); 1445 extent_interior_register(tsdn, rtree_ctx, extent, szind); 1446 } 1447 if (*zero && !extent_zeroed_get(extent)) { 1448 void *addr = extent_base_get(extent); 1449 size_t size = extent_size_get(extent); 1450 if (extent_need_manual_zero(arena) || 1451 pages_purge_forced(addr, size)) { 1452 memset(addr, 0, size); 1453 } 1454 } 1455 1456 return extent; 1457 label_err: 1458 malloc_mutex_unlock(tsdn, &arena->extent_grow_mtx); 1459 return NULL; 1460 } 1461 1462 static extent_t * 1463 extent_alloc_retained(tsdn_t *tsdn, arena_t *arena, 1464 extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad, 1465 size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) { 1466 assert(size != 0); 1467 assert(alignment != 0); 1468 1469 malloc_mutex_lock(tsdn, &arena->extent_grow_mtx); 1470 1471 extent_t *extent = extent_recycle(tsdn, arena, r_extent_hooks, 1472 &arena->extents_retained, new_addr, size, pad, alignment, slab, 1473 szind, zero, commit, true); 1474 if (extent != NULL) { 1475 malloc_mutex_unlock(tsdn, &arena->extent_grow_mtx); 1476 if (config_prof) { 1477 extent_gdump_add(tsdn, extent); 1478 } 1479 } else if (opt_retain && new_addr == NULL) { 1480 extent = extent_grow_retained(tsdn, arena, r_extent_hooks, size, 1481 pad, alignment, slab, szind, zero, commit); 1482 /* extent_grow_retained() always releases extent_grow_mtx. */ 1483 } else { 1484 malloc_mutex_unlock(tsdn, &arena->extent_grow_mtx); 1485 } 1486 malloc_mutex_assert_not_owner(tsdn, &arena->extent_grow_mtx); 1487 1488 return extent; 1489 } 1490 1491 static extent_t * 1492 extent_alloc_wrapper_hard(tsdn_t *tsdn, arena_t *arena, 1493 extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad, 1494 size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) { 1495 size_t esize = size + pad; 1496 extent_t *extent = extent_alloc(tsdn, arena); 1497 if (extent == NULL) { 1498 return NULL; 1499 } 1500 void *addr; 1501 size_t palignment = ALIGNMENT_CEILING(alignment, PAGE); 1502 if (*r_extent_hooks == &extent_hooks_default) { 1503 /* Call directly to propagate tsdn. */ 1504 addr = extent_alloc_default_impl(tsdn, arena, new_addr, esize, 1505 palignment, zero, commit); 1506 } else { 1507 extent_hook_pre_reentrancy(tsdn, arena); 1508 addr = (*r_extent_hooks)->alloc(*r_extent_hooks, new_addr, 1509 esize, palignment, zero, commit, arena_ind_get(arena)); 1510 extent_hook_post_reentrancy(tsdn); 1511 } 1512 if (addr == NULL) { 1513 extent_dalloc(tsdn, arena, extent); 1514 return NULL; 1515 } 1516 extent_init(extent, arena, addr, esize, slab, szind, 1517 arena_extent_sn_next(arena), extent_state_active, *zero, *commit, 1518 true, EXTENT_NOT_HEAD); 1519 if (pad != 0) { 1520 extent_addr_randomize(tsdn, extent, alignment); 1521 } 1522 if (extent_register(tsdn, extent)) { 1523 extent_dalloc(tsdn, arena, extent); 1524 return NULL; 1525 } 1526 1527 return extent; 1528 } 1529 1530 extent_t * 1531 extent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena, 1532 extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad, 1533 size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) { 1534 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1535 WITNESS_RANK_CORE, 0); 1536 1537 extent_hooks_assure_initialized(arena, r_extent_hooks); 1538 1539 extent_t *extent = extent_alloc_retained(tsdn, arena, r_extent_hooks, 1540 new_addr, size, pad, alignment, slab, szind, zero, commit); 1541 if (extent == NULL) { 1542 if (opt_retain && new_addr != NULL) { 1543 /* 1544 * When retain is enabled and new_addr is set, we do not 1545 * attempt extent_alloc_wrapper_hard which does mmap 1546 * that is very unlikely to succeed (unless it happens 1547 * to be at the end). 1548 */ 1549 return NULL; 1550 } 1551 extent = extent_alloc_wrapper_hard(tsdn, arena, r_extent_hooks, 1552 new_addr, size, pad, alignment, slab, szind, zero, commit); 1553 } 1554 1555 assert(extent == NULL || extent_dumpable_get(extent)); 1556 return extent; 1557 } 1558 1559 static bool 1560 extent_can_coalesce(arena_t *arena, extents_t *extents, const extent_t *inner, 1561 const extent_t *outer) { 1562 assert(extent_arena_get(inner) == arena); 1563 if (extent_arena_get(outer) != arena) { 1564 return false; 1565 } 1566 1567 assert(extent_state_get(inner) == extent_state_active); 1568 if (extent_state_get(outer) != extents->state) { 1569 return false; 1570 } 1571 1572 if (extent_committed_get(inner) != extent_committed_get(outer)) { 1573 return false; 1574 } 1575 1576 return true; 1577 } 1578 1579 static bool 1580 extent_coalesce(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks, 1581 extents_t *extents, extent_t *inner, extent_t *outer, bool forward, 1582 bool growing_retained) { 1583 assert(extent_can_coalesce(arena, extents, inner, outer)); 1584 1585 extent_activate_locked(tsdn, arena, extents, outer); 1586 1587 malloc_mutex_unlock(tsdn, &extents->mtx); 1588 bool err = extent_merge_impl(tsdn, arena, r_extent_hooks, 1589 forward ? inner : outer, forward ? outer : inner, growing_retained); 1590 malloc_mutex_lock(tsdn, &extents->mtx); 1591 1592 if (err) { 1593 extent_deactivate_locked(tsdn, arena, extents, outer); 1594 } 1595 1596 return err; 1597 } 1598 1599 static extent_t * 1600 extent_try_coalesce_impl(tsdn_t *tsdn, arena_t *arena, 1601 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents, 1602 extent_t *extent, bool *coalesced, bool growing_retained, 1603 bool inactive_only) { 1604 /* 1605 * We avoid checking / locking inactive neighbors for large size 1606 * classes, since they are eagerly coalesced on deallocation which can 1607 * cause lock contention. 1608 */ 1609 /* 1610 * Continue attempting to coalesce until failure, to protect against 1611 * races with other threads that are thwarted by this one. 1612 */ 1613 bool again; 1614 do { 1615 again = false; 1616 1617 /* Try to coalesce forward. */ 1618 extent_t *next = extent_lock_from_addr(tsdn, rtree_ctx, 1619 extent_past_get(extent), inactive_only); 1620 if (next != NULL) { 1621 /* 1622 * extents->mtx only protects against races for 1623 * like-state extents, so call extent_can_coalesce() 1624 * before releasing next's pool lock. 1625 */ 1626 bool can_coalesce = extent_can_coalesce(arena, extents, 1627 extent, next); 1628 1629 extent_unlock(tsdn, next); 1630 1631 if (can_coalesce && !extent_coalesce(tsdn, arena, 1632 r_extent_hooks, extents, extent, next, true, 1633 growing_retained)) { 1634 if (extents->delay_coalesce) { 1635 /* Do minimal coalescing. */ 1636 *coalesced = true; 1637 return extent; 1638 } 1639 again = true; 1640 } 1641 } 1642 1643 /* Try to coalesce backward. */ 1644 extent_t *prev = extent_lock_from_addr(tsdn, rtree_ctx, 1645 extent_before_get(extent), inactive_only); 1646 if (prev != NULL) { 1647 bool can_coalesce = extent_can_coalesce(arena, extents, 1648 extent, prev); 1649 extent_unlock(tsdn, prev); 1650 1651 if (can_coalesce && !extent_coalesce(tsdn, arena, 1652 r_extent_hooks, extents, extent, prev, false, 1653 growing_retained)) { 1654 extent = prev; 1655 if (extents->delay_coalesce) { 1656 /* Do minimal coalescing. */ 1657 *coalesced = true; 1658 return extent; 1659 } 1660 again = true; 1661 } 1662 } 1663 } while (again); 1664 1665 if (extents->delay_coalesce) { 1666 *coalesced = false; 1667 } 1668 return extent; 1669 } 1670 1671 static extent_t * 1672 extent_try_coalesce(tsdn_t *tsdn, arena_t *arena, 1673 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents, 1674 extent_t *extent, bool *coalesced, bool growing_retained) { 1675 return extent_try_coalesce_impl(tsdn, arena, r_extent_hooks, rtree_ctx, 1676 extents, extent, coalesced, growing_retained, false); 1677 } 1678 1679 static extent_t * 1680 extent_try_coalesce_large(tsdn_t *tsdn, arena_t *arena, 1681 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents, 1682 extent_t *extent, bool *coalesced, bool growing_retained) { 1683 return extent_try_coalesce_impl(tsdn, arena, r_extent_hooks, rtree_ctx, 1684 extents, extent, coalesced, growing_retained, true); 1685 } 1686 1687 /* 1688 * Does the metadata management portions of putting an unused extent into the 1689 * given extents_t (coalesces, deregisters slab interiors, the heap operations). 1690 */ 1691 static void 1692 extent_record(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks, 1693 extents_t *extents, extent_t *extent, bool growing_retained) { 1694 rtree_ctx_t rtree_ctx_fallback; 1695 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 1696 1697 assert((extents_state_get(extents) != extent_state_dirty && 1698 extents_state_get(extents) != extent_state_muzzy) || 1699 !extent_zeroed_get(extent)); 1700 1701 malloc_mutex_lock(tsdn, &extents->mtx); 1702 extent_hooks_assure_initialized(arena, r_extent_hooks); 1703 1704 extent_szind_set(extent, SC_NSIZES); 1705 if (extent_slab_get(extent)) { 1706 extent_interior_deregister(tsdn, rtree_ctx, extent); 1707 extent_slab_set(extent, false); 1708 } 1709 1710 assert(rtree_extent_read(tsdn, &extents_rtree, rtree_ctx, 1711 (uintptr_t)extent_base_get(extent), true) == extent); 1712 1713 if (!extents->delay_coalesce) { 1714 extent = extent_try_coalesce(tsdn, arena, r_extent_hooks, 1715 rtree_ctx, extents, extent, NULL, growing_retained); 1716 } else if (extent_size_get(extent) >= SC_LARGE_MINCLASS) { 1717 assert(extents == &arena->extents_dirty); 1718 /* Always coalesce large extents eagerly. */ 1719 bool coalesced; 1720 do { 1721 assert(extent_state_get(extent) == extent_state_active); 1722 extent = extent_try_coalesce_large(tsdn, arena, 1723 r_extent_hooks, rtree_ctx, extents, extent, 1724 &coalesced, growing_retained); 1725 } while (coalesced); 1726 if (extent_size_get(extent) >= oversize_threshold) { 1727 /* Shortcut to purge the oversize extent eagerly. */ 1728 malloc_mutex_unlock(tsdn, &extents->mtx); 1729 arena_decay_extent(tsdn, arena, r_extent_hooks, extent); 1730 return; 1731 } 1732 } 1733 extent_deactivate_locked(tsdn, arena, extents, extent); 1734 1735 malloc_mutex_unlock(tsdn, &extents->mtx); 1736 } 1737 1738 void 1739 extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent) { 1740 extent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER; 1741 1742 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1743 WITNESS_RANK_CORE, 0); 1744 1745 if (extent_register(tsdn, extent)) { 1746 extent_dalloc(tsdn, arena, extent); 1747 return; 1748 } 1749 extent_dalloc_wrapper(tsdn, arena, &extent_hooks, extent); 1750 } 1751 1752 static bool 1753 extent_may_dalloc(void) { 1754 /* With retain enabled, the default dalloc always fails. */ 1755 return !opt_retain; 1756 } 1757 1758 static bool 1759 extent_dalloc_default_impl(void *addr, size_t size) { 1760 if (!have_dss || !extent_in_dss(addr)) { 1761 return extent_dalloc_mmap(addr, size); 1762 } 1763 return true; 1764 } 1765 1766 static bool 1767 extent_dalloc_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1768 bool committed, unsigned arena_ind) { 1769 return extent_dalloc_default_impl(addr, size); 1770 } 1771 1772 static bool 1773 extent_dalloc_wrapper_try(tsdn_t *tsdn, arena_t *arena, 1774 extent_hooks_t **r_extent_hooks, extent_t *extent) { 1775 bool err; 1776 1777 assert(extent_base_get(extent) != NULL); 1778 assert(extent_size_get(extent) != 0); 1779 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1780 WITNESS_RANK_CORE, 0); 1781 1782 extent_addr_set(extent, extent_base_get(extent)); 1783 1784 extent_hooks_assure_initialized(arena, r_extent_hooks); 1785 /* Try to deallocate. */ 1786 if (*r_extent_hooks == &extent_hooks_default) { 1787 /* Call directly to propagate tsdn. */ 1788 err = extent_dalloc_default_impl(extent_base_get(extent), 1789 extent_size_get(extent)); 1790 } else { 1791 extent_hook_pre_reentrancy(tsdn, arena); 1792 err = ((*r_extent_hooks)->dalloc == NULL || 1793 (*r_extent_hooks)->dalloc(*r_extent_hooks, 1794 extent_base_get(extent), extent_size_get(extent), 1795 extent_committed_get(extent), arena_ind_get(arena))); 1796 extent_hook_post_reentrancy(tsdn); 1797 } 1798 1799 if (!err) { 1800 extent_dalloc(tsdn, arena, extent); 1801 } 1802 1803 return err; 1804 } 1805 1806 void 1807 extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena, 1808 extent_hooks_t **r_extent_hooks, extent_t *extent) { 1809 assert(extent_dumpable_get(extent)); 1810 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1811 WITNESS_RANK_CORE, 0); 1812 1813 /* Avoid calling the default extent_dalloc unless have to. */ 1814 if (*r_extent_hooks != &extent_hooks_default || extent_may_dalloc()) { 1815 /* 1816 * Deregister first to avoid a race with other allocating 1817 * threads, and reregister if deallocation fails. 1818 */ 1819 extent_deregister(tsdn, extent); 1820 if (!extent_dalloc_wrapper_try(tsdn, arena, r_extent_hooks, 1821 extent)) { 1822 return; 1823 } 1824 extent_reregister(tsdn, extent); 1825 } 1826 1827 if (*r_extent_hooks != &extent_hooks_default) { 1828 extent_hook_pre_reentrancy(tsdn, arena); 1829 } 1830 /* Try to decommit; purge if that fails. */ 1831 bool zeroed; 1832 if (!extent_committed_get(extent)) { 1833 zeroed = true; 1834 } else if (!extent_decommit_wrapper(tsdn, arena, r_extent_hooks, extent, 1835 0, extent_size_get(extent))) { 1836 zeroed = true; 1837 } else if ((*r_extent_hooks)->purge_forced != NULL && 1838 !(*r_extent_hooks)->purge_forced(*r_extent_hooks, 1839 extent_base_get(extent), extent_size_get(extent), 0, 1840 extent_size_get(extent), arena_ind_get(arena))) { 1841 zeroed = true; 1842 } else if (extent_state_get(extent) == extent_state_muzzy || 1843 ((*r_extent_hooks)->purge_lazy != NULL && 1844 !(*r_extent_hooks)->purge_lazy(*r_extent_hooks, 1845 extent_base_get(extent), extent_size_get(extent), 0, 1846 extent_size_get(extent), arena_ind_get(arena)))) { 1847 zeroed = false; 1848 } else { 1849 zeroed = false; 1850 } 1851 if (*r_extent_hooks != &extent_hooks_default) { 1852 extent_hook_post_reentrancy(tsdn); 1853 } 1854 extent_zeroed_set(extent, zeroed); 1855 1856 if (config_prof) { 1857 extent_gdump_sub(tsdn, extent); 1858 } 1859 1860 extent_record(tsdn, arena, r_extent_hooks, &arena->extents_retained, 1861 extent, false); 1862 } 1863 1864 static void 1865 extent_destroy_default_impl(void *addr, size_t size) { 1866 if (!have_dss || !extent_in_dss(addr)) { 1867 pages_unmap(addr, size); 1868 } 1869 } 1870 1871 static void 1872 extent_destroy_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1873 bool committed, unsigned arena_ind) { 1874 extent_destroy_default_impl(addr, size); 1875 } 1876 1877 void 1878 extent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena, 1879 extent_hooks_t **r_extent_hooks, extent_t *extent) { 1880 assert(extent_base_get(extent) != NULL); 1881 assert(extent_size_get(extent) != 0); 1882 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1883 WITNESS_RANK_CORE, 0); 1884 1885 /* Deregister first to avoid a race with other allocating threads. */ 1886 extent_deregister(tsdn, extent); 1887 1888 extent_addr_set(extent, extent_base_get(extent)); 1889 1890 extent_hooks_assure_initialized(arena, r_extent_hooks); 1891 /* Try to destroy; silently fail otherwise. */ 1892 if (*r_extent_hooks == &extent_hooks_default) { 1893 /* Call directly to propagate tsdn. */ 1894 extent_destroy_default_impl(extent_base_get(extent), 1895 extent_size_get(extent)); 1896 } else if ((*r_extent_hooks)->destroy != NULL) { 1897 extent_hook_pre_reentrancy(tsdn, arena); 1898 (*r_extent_hooks)->destroy(*r_extent_hooks, 1899 extent_base_get(extent), extent_size_get(extent), 1900 extent_committed_get(extent), arena_ind_get(arena)); 1901 extent_hook_post_reentrancy(tsdn); 1902 } 1903 1904 extent_dalloc(tsdn, arena, extent); 1905 } 1906 1907 static bool 1908 extent_commit_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1909 size_t offset, size_t length, unsigned arena_ind) { 1910 return pages_commit((void *)((uintptr_t)addr + (uintptr_t)offset), 1911 length); 1912 } 1913 1914 static bool 1915 extent_commit_impl(tsdn_t *tsdn, arena_t *arena, 1916 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 1917 size_t length, bool growing_retained) { 1918 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1919 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 1920 1921 extent_hooks_assure_initialized(arena, r_extent_hooks); 1922 if (*r_extent_hooks != &extent_hooks_default) { 1923 extent_hook_pre_reentrancy(tsdn, arena); 1924 } 1925 bool err = ((*r_extent_hooks)->commit == NULL || 1926 (*r_extent_hooks)->commit(*r_extent_hooks, extent_base_get(extent), 1927 extent_size_get(extent), offset, length, arena_ind_get(arena))); 1928 if (*r_extent_hooks != &extent_hooks_default) { 1929 extent_hook_post_reentrancy(tsdn); 1930 } 1931 extent_committed_set(extent, extent_committed_get(extent) || !err); 1932 return err; 1933 } 1934 1935 bool 1936 extent_commit_wrapper(tsdn_t *tsdn, arena_t *arena, 1937 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 1938 size_t length) { 1939 return extent_commit_impl(tsdn, arena, r_extent_hooks, extent, offset, 1940 length, false); 1941 } 1942 1943 static bool 1944 extent_decommit_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1945 size_t offset, size_t length, unsigned arena_ind) { 1946 return pages_decommit((void *)((uintptr_t)addr + (uintptr_t)offset), 1947 length); 1948 } 1949 1950 bool 1951 extent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena, 1952 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 1953 size_t length) { 1954 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1955 WITNESS_RANK_CORE, 0); 1956 1957 extent_hooks_assure_initialized(arena, r_extent_hooks); 1958 1959 if (*r_extent_hooks != &extent_hooks_default) { 1960 extent_hook_pre_reentrancy(tsdn, arena); 1961 } 1962 bool err = ((*r_extent_hooks)->decommit == NULL || 1963 (*r_extent_hooks)->decommit(*r_extent_hooks, 1964 extent_base_get(extent), extent_size_get(extent), offset, length, 1965 arena_ind_get(arena))); 1966 if (*r_extent_hooks != &extent_hooks_default) { 1967 extent_hook_post_reentrancy(tsdn); 1968 } 1969 extent_committed_set(extent, extent_committed_get(extent) && err); 1970 return err; 1971 } 1972 1973 #ifdef PAGES_CAN_PURGE_LAZY 1974 static bool 1975 extent_purge_lazy_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1976 size_t offset, size_t length, unsigned arena_ind) { 1977 assert(addr != NULL); 1978 assert((offset & PAGE_MASK) == 0); 1979 assert(length != 0); 1980 assert((length & PAGE_MASK) == 0); 1981 1982 return pages_purge_lazy((void *)((uintptr_t)addr + (uintptr_t)offset), 1983 length); 1984 } 1985 #endif 1986 1987 static bool 1988 extent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena, 1989 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 1990 size_t length, bool growing_retained) { 1991 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1992 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 1993 1994 extent_hooks_assure_initialized(arena, r_extent_hooks); 1995 1996 if ((*r_extent_hooks)->purge_lazy == NULL) { 1997 return true; 1998 } 1999 if (*r_extent_hooks != &extent_hooks_default) { 2000 extent_hook_pre_reentrancy(tsdn, arena); 2001 } 2002 bool err = (*r_extent_hooks)->purge_lazy(*r_extent_hooks, 2003 extent_base_get(extent), extent_size_get(extent), offset, length, 2004 arena_ind_get(arena)); 2005 if (*r_extent_hooks != &extent_hooks_default) { 2006 extent_hook_post_reentrancy(tsdn); 2007 } 2008 2009 return err; 2010 } 2011 2012 bool 2013 extent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena, 2014 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 2015 size_t length) { 2016 return extent_purge_lazy_impl(tsdn, arena, r_extent_hooks, extent, 2017 offset, length, false); 2018 } 2019 2020 #ifdef PAGES_CAN_PURGE_FORCED 2021 static bool 2022 extent_purge_forced_default(extent_hooks_t *extent_hooks, void *addr, 2023 size_t size, size_t offset, size_t length, unsigned arena_ind) { 2024 assert(addr != NULL); 2025 assert((offset & PAGE_MASK) == 0); 2026 assert(length != 0); 2027 assert((length & PAGE_MASK) == 0); 2028 2029 return pages_purge_forced((void *)((uintptr_t)addr + 2030 (uintptr_t)offset), length); 2031 } 2032 #endif 2033 2034 static bool 2035 extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena, 2036 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 2037 size_t length, bool growing_retained) { 2038 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 2039 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 2040 2041 extent_hooks_assure_initialized(arena, r_extent_hooks); 2042 2043 if ((*r_extent_hooks)->purge_forced == NULL) { 2044 return true; 2045 } 2046 if (*r_extent_hooks != &extent_hooks_default) { 2047 extent_hook_pre_reentrancy(tsdn, arena); 2048 } 2049 bool err = (*r_extent_hooks)->purge_forced(*r_extent_hooks, 2050 extent_base_get(extent), extent_size_get(extent), offset, length, 2051 arena_ind_get(arena)); 2052 if (*r_extent_hooks != &extent_hooks_default) { 2053 extent_hook_post_reentrancy(tsdn); 2054 } 2055 return err; 2056 } 2057 2058 bool 2059 extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena, 2060 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 2061 size_t length) { 2062 return extent_purge_forced_impl(tsdn, arena, r_extent_hooks, extent, 2063 offset, length, false); 2064 } 2065 2066 static bool 2067 extent_split_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 2068 size_t size_a, size_t size_b, bool committed, unsigned arena_ind) { 2069 if (!maps_coalesce) { 2070 /* 2071 * Without retain, only whole regions can be purged (required by 2072 * MEM_RELEASE on Windows) -- therefore disallow splitting. See 2073 * comments in extent_head_no_merge(). 2074 */ 2075 return !opt_retain; 2076 } 2077 2078 return false; 2079 } 2080 2081 /* 2082 * Accepts the extent to split, and the characteristics of each side of the 2083 * split. The 'a' parameters go with the 'lead' of the resulting pair of 2084 * extents (the lower addressed portion of the split), and the 'b' parameters go 2085 * with the trail (the higher addressed portion). This makes 'extent' the lead, 2086 * and returns the trail (except in case of error). 2087 */ 2088 static extent_t * 2089 extent_split_impl(tsdn_t *tsdn, arena_t *arena, 2090 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a, 2091 szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b, 2092 bool growing_retained) { 2093 assert(extent_size_get(extent) == size_a + size_b); 2094 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 2095 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 2096 2097 extent_hooks_assure_initialized(arena, r_extent_hooks); 2098 2099 if ((*r_extent_hooks)->split == NULL) { 2100 return NULL; 2101 } 2102 2103 extent_t *trail = extent_alloc(tsdn, arena); 2104 if (trail == NULL) { 2105 goto label_error_a; 2106 } 2107 2108 extent_init(trail, arena, (void *)((uintptr_t)extent_base_get(extent) + 2109 size_a), size_b, slab_b, szind_b, extent_sn_get(extent), 2110 extent_state_get(extent), extent_zeroed_get(extent), 2111 extent_committed_get(extent), extent_dumpable_get(extent), 2112 EXTENT_NOT_HEAD); 2113 2114 rtree_ctx_t rtree_ctx_fallback; 2115 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 2116 rtree_leaf_elm_t *lead_elm_a, *lead_elm_b; 2117 { 2118 extent_t lead; 2119 2120 extent_init(&lead, arena, extent_addr_get(extent), size_a, 2121 slab_a, szind_a, extent_sn_get(extent), 2122 extent_state_get(extent), extent_zeroed_get(extent), 2123 extent_committed_get(extent), extent_dumpable_get(extent), 2124 EXTENT_NOT_HEAD); 2125 2126 extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, &lead, false, 2127 true, &lead_elm_a, &lead_elm_b); 2128 } 2129 rtree_leaf_elm_t *trail_elm_a, *trail_elm_b; 2130 extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, trail, false, true, 2131 &trail_elm_a, &trail_elm_b); 2132 2133 if (lead_elm_a == NULL || lead_elm_b == NULL || trail_elm_a == NULL 2134 || trail_elm_b == NULL) { 2135 goto label_error_b; 2136 } 2137 2138 extent_lock2(tsdn, extent, trail); 2139 2140 if (*r_extent_hooks != &extent_hooks_default) { 2141 extent_hook_pre_reentrancy(tsdn, arena); 2142 } 2143 bool err = (*r_extent_hooks)->split(*r_extent_hooks, extent_base_get(extent), 2144 size_a + size_b, size_a, size_b, extent_committed_get(extent), 2145 arena_ind_get(arena)); 2146 if (*r_extent_hooks != &extent_hooks_default) { 2147 extent_hook_post_reentrancy(tsdn); 2148 } 2149 if (err) { 2150 goto label_error_c; 2151 } 2152 2153 extent_size_set(extent, size_a); 2154 extent_szind_set(extent, szind_a); 2155 2156 extent_rtree_write_acquired(tsdn, lead_elm_a, lead_elm_b, extent, 2157 szind_a, slab_a); 2158 extent_rtree_write_acquired(tsdn, trail_elm_a, trail_elm_b, trail, 2159 szind_b, slab_b); 2160 2161 extent_unlock2(tsdn, extent, trail); 2162 2163 return trail; 2164 label_error_c: 2165 extent_unlock2(tsdn, extent, trail); 2166 label_error_b: 2167 extent_dalloc(tsdn, arena, trail); 2168 label_error_a: 2169 return NULL; 2170 } 2171 2172 extent_t * 2173 extent_split_wrapper(tsdn_t *tsdn, arena_t *arena, 2174 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a, 2175 szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b) { 2176 return extent_split_impl(tsdn, arena, r_extent_hooks, extent, size_a, 2177 szind_a, slab_a, size_b, szind_b, slab_b, false); 2178 } 2179 2180 static bool 2181 extent_merge_default_impl(void *addr_a, void *addr_b) { 2182 if (!maps_coalesce && !opt_retain) { 2183 return true; 2184 } 2185 if (have_dss && !extent_dss_mergeable(addr_a, addr_b)) { 2186 return true; 2187 } 2188 2189 return false; 2190 } 2191 2192 /* 2193 * Returns true if the given extents can't be merged because of their head bit 2194 * settings. Assumes the second extent has the higher address. 2195 */ 2196 static bool 2197 extent_head_no_merge(extent_t *a, extent_t *b) { 2198 assert(extent_base_get(a) < extent_base_get(b)); 2199 /* 2200 * When coalesce is not always allowed (Windows), only merge extents 2201 * from the same VirtualAlloc region under opt.retain (in which case 2202 * MEM_DECOMMIT is utilized for purging). 2203 */ 2204 if (maps_coalesce) { 2205 return false; 2206 } 2207 if (!opt_retain) { 2208 return true; 2209 } 2210 /* If b is a head extent, disallow the cross-region merge. */ 2211 if (extent_is_head_get(b)) { 2212 /* 2213 * Additionally, sn should not overflow with retain; sanity 2214 * check that different regions have unique sn. 2215 */ 2216 assert(extent_sn_comp(a, b) != 0); 2217 return true; 2218 } 2219 assert(extent_sn_comp(a, b) == 0); 2220 2221 return false; 2222 } 2223 2224 static bool 2225 extent_merge_default(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a, 2226 void *addr_b, size_t size_b, bool committed, unsigned arena_ind) { 2227 if (!maps_coalesce) { 2228 tsdn_t *tsdn = tsdn_fetch(); 2229 extent_t *a = iealloc(tsdn, addr_a); 2230 extent_t *b = iealloc(tsdn, addr_b); 2231 if (extent_head_no_merge(a, b)) { 2232 return true; 2233 } 2234 } 2235 return extent_merge_default_impl(addr_a, addr_b); 2236 } 2237 2238 static bool 2239 extent_merge_impl(tsdn_t *tsdn, arena_t *arena, 2240 extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b, 2241 bool growing_retained) { 2242 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 2243 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 2244 assert(extent_base_get(a) < extent_base_get(b)); 2245 2246 extent_hooks_assure_initialized(arena, r_extent_hooks); 2247 2248 if ((*r_extent_hooks)->merge == NULL || extent_head_no_merge(a, b)) { 2249 return true; 2250 } 2251 2252 bool err; 2253 if (*r_extent_hooks == &extent_hooks_default) { 2254 /* Call directly to propagate tsdn. */ 2255 err = extent_merge_default_impl(extent_base_get(a), 2256 extent_base_get(b)); 2257 } else { 2258 extent_hook_pre_reentrancy(tsdn, arena); 2259 err = (*r_extent_hooks)->merge(*r_extent_hooks, 2260 extent_base_get(a), extent_size_get(a), extent_base_get(b), 2261 extent_size_get(b), extent_committed_get(a), 2262 arena_ind_get(arena)); 2263 extent_hook_post_reentrancy(tsdn); 2264 } 2265 2266 if (err) { 2267 return true; 2268 } 2269 2270 /* 2271 * The rtree writes must happen while all the relevant elements are 2272 * owned, so the following code uses decomposed helper functions rather 2273 * than extent_{,de}register() to do things in the right order. 2274 */ 2275 rtree_ctx_t rtree_ctx_fallback; 2276 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 2277 rtree_leaf_elm_t *a_elm_a, *a_elm_b, *b_elm_a, *b_elm_b; 2278 extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, a, true, false, &a_elm_a, 2279 &a_elm_b); 2280 extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, b, true, false, &b_elm_a, 2281 &b_elm_b); 2282 2283 extent_lock2(tsdn, a, b); 2284 2285 if (a_elm_b != NULL) { 2286 rtree_leaf_elm_write(tsdn, &extents_rtree, a_elm_b, NULL, 2287 SC_NSIZES, false); 2288 } 2289 if (b_elm_b != NULL) { 2290 rtree_leaf_elm_write(tsdn, &extents_rtree, b_elm_a, NULL, 2291 SC_NSIZES, false); 2292 } else { 2293 b_elm_b = b_elm_a; 2294 } 2295 2296 extent_size_set(a, extent_size_get(a) + extent_size_get(b)); 2297 extent_szind_set(a, SC_NSIZES); 2298 extent_sn_set(a, (extent_sn_get(a) < extent_sn_get(b)) ? 2299 extent_sn_get(a) : extent_sn_get(b)); 2300 extent_zeroed_set(a, extent_zeroed_get(a) && extent_zeroed_get(b)); 2301 2302 extent_rtree_write_acquired(tsdn, a_elm_a, b_elm_b, a, SC_NSIZES, 2303 false); 2304 2305 extent_unlock2(tsdn, a, b); 2306 2307 extent_dalloc(tsdn, extent_arena_get(b), b); 2308 2309 return false; 2310 } 2311 2312 bool 2313 extent_merge_wrapper(tsdn_t *tsdn, arena_t *arena, 2314 extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b) { 2315 return extent_merge_impl(tsdn, arena, r_extent_hooks, a, b, false); 2316 } 2317 2318 bool 2319 extent_boot(void) { 2320 if (rtree_new(&extents_rtree, true)) { 2321 return true; 2322 } 2323 2324 if (mutex_pool_init(&extent_mutex_pool, "extent_mutex_pool", 2325 WITNESS_RANK_EXTENT_POOL)) { 2326 return true; 2327 } 2328 2329 if (have_dss) { 2330 extent_dss_boot(); 2331 } 2332 2333 return false; 2334 } 2335 2336 void 2337 extent_util_stats_get(tsdn_t *tsdn, const void *ptr, 2338 size_t *nfree, size_t *nregs, size_t *size) { 2339 assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL); 2340 2341 const extent_t *extent = iealloc(tsdn, ptr); 2342 if (unlikely(extent == NULL)) { 2343 *nfree = *nregs = *size = 0; 2344 return; 2345 } 2346 2347 *size = extent_size_get(extent); 2348 if (!extent_slab_get(extent)) { 2349 *nfree = 0; 2350 *nregs = 1; 2351 } else { 2352 *nfree = extent_nfree_get(extent); 2353 *nregs = bin_infos[extent_szind_get(extent)].nregs; 2354 assert(*nfree <= *nregs); 2355 assert(*nfree * extent_usize_get(extent) <= *size); 2356 } 2357 } 2358 2359 void 2360 extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr, 2361 size_t *nfree, size_t *nregs, size_t *size, 2362 size_t *bin_nfree, size_t *bin_nregs, void **slabcur_addr) { 2363 assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL 2364 && bin_nfree != NULL && bin_nregs != NULL && slabcur_addr != NULL); 2365 2366 const extent_t *extent = iealloc(tsdn, ptr); 2367 if (unlikely(extent == NULL)) { 2368 *nfree = *nregs = *size = *bin_nfree = *bin_nregs = 0; 2369 *slabcur_addr = NULL; 2370 return; 2371 } 2372 2373 *size = extent_size_get(extent); 2374 if (!extent_slab_get(extent)) { 2375 *nfree = *bin_nfree = *bin_nregs = 0; 2376 *nregs = 1; 2377 *slabcur_addr = NULL; 2378 return; 2379 } 2380 2381 *nfree = extent_nfree_get(extent); 2382 const szind_t szind = extent_szind_get(extent); 2383 *nregs = bin_infos[szind].nregs; 2384 assert(*nfree <= *nregs); 2385 assert(*nfree * extent_usize_get(extent) <= *size); 2386 2387 const arena_t *arena = extent_arena_get(extent); 2388 assert(arena != NULL); 2389 const unsigned binshard = extent_binshard_get(extent); 2390 bin_t *bin = &arena->bins[szind].bin_shards[binshard]; 2391 2392 malloc_mutex_lock(tsdn, &bin->lock); 2393 if (config_stats) { 2394 *bin_nregs = *nregs * bin->stats.curslabs; 2395 assert(*bin_nregs >= bin->stats.curregs); 2396 *bin_nfree = *bin_nregs - bin->stats.curregs; 2397 } else { 2398 *bin_nfree = *bin_nregs = 0; 2399 } 2400 *slabcur_addr = extent_addr_get(bin->slabcur); 2401 assert(*slabcur_addr != NULL); 2402 malloc_mutex_unlock(tsdn, &bin->lock); 2403 } 2404