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 = NULL; 1645 if (extent_before_get(extent) != NULL) { 1646 prev = extent_lock_from_addr(tsdn, rtree_ctx, 1647 extent_before_get(extent), inactive_only); 1648 } 1649 if (prev != NULL) { 1650 bool can_coalesce = extent_can_coalesce(arena, extents, 1651 extent, prev); 1652 extent_unlock(tsdn, prev); 1653 1654 if (can_coalesce && !extent_coalesce(tsdn, arena, 1655 r_extent_hooks, extents, extent, prev, false, 1656 growing_retained)) { 1657 extent = prev; 1658 if (extents->delay_coalesce) { 1659 /* Do minimal coalescing. */ 1660 *coalesced = true; 1661 return extent; 1662 } 1663 again = true; 1664 } 1665 } 1666 } while (again); 1667 1668 if (extents->delay_coalesce) { 1669 *coalesced = false; 1670 } 1671 return extent; 1672 } 1673 1674 static extent_t * 1675 extent_try_coalesce(tsdn_t *tsdn, arena_t *arena, 1676 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents, 1677 extent_t *extent, bool *coalesced, bool growing_retained) { 1678 return extent_try_coalesce_impl(tsdn, arena, r_extent_hooks, rtree_ctx, 1679 extents, extent, coalesced, growing_retained, false); 1680 } 1681 1682 static extent_t * 1683 extent_try_coalesce_large(tsdn_t *tsdn, arena_t *arena, 1684 extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents, 1685 extent_t *extent, bool *coalesced, bool growing_retained) { 1686 return extent_try_coalesce_impl(tsdn, arena, r_extent_hooks, rtree_ctx, 1687 extents, extent, coalesced, growing_retained, true); 1688 } 1689 1690 /* 1691 * Does the metadata management portions of putting an unused extent into the 1692 * given extents_t (coalesces, deregisters slab interiors, the heap operations). 1693 */ 1694 static void 1695 extent_record(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks, 1696 extents_t *extents, extent_t *extent, bool growing_retained) { 1697 rtree_ctx_t rtree_ctx_fallback; 1698 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 1699 1700 assert((extents_state_get(extents) != extent_state_dirty && 1701 extents_state_get(extents) != extent_state_muzzy) || 1702 !extent_zeroed_get(extent)); 1703 1704 malloc_mutex_lock(tsdn, &extents->mtx); 1705 extent_hooks_assure_initialized(arena, r_extent_hooks); 1706 1707 extent_szind_set(extent, SC_NSIZES); 1708 if (extent_slab_get(extent)) { 1709 extent_interior_deregister(tsdn, rtree_ctx, extent); 1710 extent_slab_set(extent, false); 1711 } 1712 1713 assert(rtree_extent_read(tsdn, &extents_rtree, rtree_ctx, 1714 (uintptr_t)extent_base_get(extent), true) == extent); 1715 1716 if (!extents->delay_coalesce) { 1717 extent = extent_try_coalesce(tsdn, arena, r_extent_hooks, 1718 rtree_ctx, extents, extent, NULL, growing_retained); 1719 } else if (extent_size_get(extent) >= SC_LARGE_MINCLASS) { 1720 assert(extents == &arena->extents_dirty); 1721 /* Always coalesce large extents eagerly. */ 1722 bool coalesced; 1723 do { 1724 assert(extent_state_get(extent) == extent_state_active); 1725 extent = extent_try_coalesce_large(tsdn, arena, 1726 r_extent_hooks, rtree_ctx, extents, extent, 1727 &coalesced, growing_retained); 1728 } while (coalesced); 1729 if (extent_size_get(extent) >= oversize_threshold) { 1730 /* Shortcut to purge the oversize extent eagerly. */ 1731 malloc_mutex_unlock(tsdn, &extents->mtx); 1732 arena_decay_extent(tsdn, arena, r_extent_hooks, extent); 1733 return; 1734 } 1735 } 1736 extent_deactivate_locked(tsdn, arena, extents, extent); 1737 1738 malloc_mutex_unlock(tsdn, &extents->mtx); 1739 } 1740 1741 void 1742 extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent) { 1743 extent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER; 1744 1745 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1746 WITNESS_RANK_CORE, 0); 1747 1748 if (extent_register(tsdn, extent)) { 1749 extent_dalloc(tsdn, arena, extent); 1750 return; 1751 } 1752 extent_dalloc_wrapper(tsdn, arena, &extent_hooks, extent); 1753 } 1754 1755 static bool 1756 extent_may_dalloc(void) { 1757 /* With retain enabled, the default dalloc always fails. */ 1758 return !opt_retain; 1759 } 1760 1761 static bool 1762 extent_dalloc_default_impl(void *addr, size_t size) { 1763 if (!have_dss || !extent_in_dss(addr)) { 1764 return extent_dalloc_mmap(addr, size); 1765 } 1766 return true; 1767 } 1768 1769 static bool 1770 extent_dalloc_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1771 bool committed, unsigned arena_ind) { 1772 return extent_dalloc_default_impl(addr, size); 1773 } 1774 1775 static bool 1776 extent_dalloc_wrapper_try(tsdn_t *tsdn, arena_t *arena, 1777 extent_hooks_t **r_extent_hooks, extent_t *extent) { 1778 bool err; 1779 1780 assert(extent_base_get(extent) != NULL); 1781 assert(extent_size_get(extent) != 0); 1782 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1783 WITNESS_RANK_CORE, 0); 1784 1785 extent_addr_set(extent, extent_base_get(extent)); 1786 1787 extent_hooks_assure_initialized(arena, r_extent_hooks); 1788 /* Try to deallocate. */ 1789 if (*r_extent_hooks == &extent_hooks_default) { 1790 /* Call directly to propagate tsdn. */ 1791 err = extent_dalloc_default_impl(extent_base_get(extent), 1792 extent_size_get(extent)); 1793 } else { 1794 extent_hook_pre_reentrancy(tsdn, arena); 1795 err = ((*r_extent_hooks)->dalloc == NULL || 1796 (*r_extent_hooks)->dalloc(*r_extent_hooks, 1797 extent_base_get(extent), extent_size_get(extent), 1798 extent_committed_get(extent), arena_ind_get(arena))); 1799 extent_hook_post_reentrancy(tsdn); 1800 } 1801 1802 if (!err) { 1803 extent_dalloc(tsdn, arena, extent); 1804 } 1805 1806 return err; 1807 } 1808 1809 void 1810 extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena, 1811 extent_hooks_t **r_extent_hooks, extent_t *extent) { 1812 assert(extent_dumpable_get(extent)); 1813 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1814 WITNESS_RANK_CORE, 0); 1815 1816 /* Avoid calling the default extent_dalloc unless have to. */ 1817 if (*r_extent_hooks != &extent_hooks_default || extent_may_dalloc()) { 1818 /* 1819 * Deregister first to avoid a race with other allocating 1820 * threads, and reregister if deallocation fails. 1821 */ 1822 extent_deregister(tsdn, extent); 1823 if (!extent_dalloc_wrapper_try(tsdn, arena, r_extent_hooks, 1824 extent)) { 1825 return; 1826 } 1827 extent_reregister(tsdn, extent); 1828 } 1829 1830 if (*r_extent_hooks != &extent_hooks_default) { 1831 extent_hook_pre_reentrancy(tsdn, arena); 1832 } 1833 /* Try to decommit; purge if that fails. */ 1834 bool zeroed; 1835 if (!extent_committed_get(extent)) { 1836 zeroed = true; 1837 } else if (!extent_decommit_wrapper(tsdn, arena, r_extent_hooks, extent, 1838 0, extent_size_get(extent))) { 1839 zeroed = true; 1840 } else if ((*r_extent_hooks)->purge_forced != NULL && 1841 !(*r_extent_hooks)->purge_forced(*r_extent_hooks, 1842 extent_base_get(extent), extent_size_get(extent), 0, 1843 extent_size_get(extent), arena_ind_get(arena))) { 1844 zeroed = true; 1845 } else if (extent_state_get(extent) == extent_state_muzzy || 1846 ((*r_extent_hooks)->purge_lazy != NULL && 1847 !(*r_extent_hooks)->purge_lazy(*r_extent_hooks, 1848 extent_base_get(extent), extent_size_get(extent), 0, 1849 extent_size_get(extent), arena_ind_get(arena)))) { 1850 zeroed = false; 1851 } else { 1852 zeroed = false; 1853 } 1854 if (*r_extent_hooks != &extent_hooks_default) { 1855 extent_hook_post_reentrancy(tsdn); 1856 } 1857 extent_zeroed_set(extent, zeroed); 1858 1859 if (config_prof) { 1860 extent_gdump_sub(tsdn, extent); 1861 } 1862 1863 extent_record(tsdn, arena, r_extent_hooks, &arena->extents_retained, 1864 extent, false); 1865 } 1866 1867 static void 1868 extent_destroy_default_impl(void *addr, size_t size) { 1869 if (!have_dss || !extent_in_dss(addr)) { 1870 pages_unmap(addr, size); 1871 } 1872 } 1873 1874 static void 1875 extent_destroy_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1876 bool committed, unsigned arena_ind) { 1877 extent_destroy_default_impl(addr, size); 1878 } 1879 1880 void 1881 extent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena, 1882 extent_hooks_t **r_extent_hooks, extent_t *extent) { 1883 assert(extent_base_get(extent) != NULL); 1884 assert(extent_size_get(extent) != 0); 1885 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1886 WITNESS_RANK_CORE, 0); 1887 1888 /* Deregister first to avoid a race with other allocating threads. */ 1889 extent_deregister(tsdn, extent); 1890 1891 extent_addr_set(extent, extent_base_get(extent)); 1892 1893 extent_hooks_assure_initialized(arena, r_extent_hooks); 1894 /* Try to destroy; silently fail otherwise. */ 1895 if (*r_extent_hooks == &extent_hooks_default) { 1896 /* Call directly to propagate tsdn. */ 1897 extent_destroy_default_impl(extent_base_get(extent), 1898 extent_size_get(extent)); 1899 } else if ((*r_extent_hooks)->destroy != NULL) { 1900 extent_hook_pre_reentrancy(tsdn, arena); 1901 (*r_extent_hooks)->destroy(*r_extent_hooks, 1902 extent_base_get(extent), extent_size_get(extent), 1903 extent_committed_get(extent), arena_ind_get(arena)); 1904 extent_hook_post_reentrancy(tsdn); 1905 } 1906 1907 extent_dalloc(tsdn, arena, extent); 1908 } 1909 1910 static bool 1911 extent_commit_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1912 size_t offset, size_t length, unsigned arena_ind) { 1913 return pages_commit((void *)((uintptr_t)addr + (uintptr_t)offset), 1914 length); 1915 } 1916 1917 static bool 1918 extent_commit_impl(tsdn_t *tsdn, arena_t *arena, 1919 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 1920 size_t length, bool growing_retained) { 1921 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1922 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 1923 1924 extent_hooks_assure_initialized(arena, r_extent_hooks); 1925 if (*r_extent_hooks != &extent_hooks_default) { 1926 extent_hook_pre_reentrancy(tsdn, arena); 1927 } 1928 bool err = ((*r_extent_hooks)->commit == NULL || 1929 (*r_extent_hooks)->commit(*r_extent_hooks, extent_base_get(extent), 1930 extent_size_get(extent), offset, length, arena_ind_get(arena))); 1931 if (*r_extent_hooks != &extent_hooks_default) { 1932 extent_hook_post_reentrancy(tsdn); 1933 } 1934 extent_committed_set(extent, extent_committed_get(extent) || !err); 1935 return err; 1936 } 1937 1938 bool 1939 extent_commit_wrapper(tsdn_t *tsdn, arena_t *arena, 1940 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 1941 size_t length) { 1942 return extent_commit_impl(tsdn, arena, r_extent_hooks, extent, offset, 1943 length, false); 1944 } 1945 1946 static bool 1947 extent_decommit_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1948 size_t offset, size_t length, unsigned arena_ind) { 1949 return pages_decommit((void *)((uintptr_t)addr + (uintptr_t)offset), 1950 length); 1951 } 1952 1953 bool 1954 extent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena, 1955 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 1956 size_t length) { 1957 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1958 WITNESS_RANK_CORE, 0); 1959 1960 extent_hooks_assure_initialized(arena, r_extent_hooks); 1961 1962 if (*r_extent_hooks != &extent_hooks_default) { 1963 extent_hook_pre_reentrancy(tsdn, arena); 1964 } 1965 bool err = ((*r_extent_hooks)->decommit == NULL || 1966 (*r_extent_hooks)->decommit(*r_extent_hooks, 1967 extent_base_get(extent), extent_size_get(extent), offset, length, 1968 arena_ind_get(arena))); 1969 if (*r_extent_hooks != &extent_hooks_default) { 1970 extent_hook_post_reentrancy(tsdn); 1971 } 1972 extent_committed_set(extent, extent_committed_get(extent) && err); 1973 return err; 1974 } 1975 1976 #ifdef PAGES_CAN_PURGE_LAZY 1977 static bool 1978 extent_purge_lazy_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 1979 size_t offset, size_t length, unsigned arena_ind) { 1980 assert(addr != NULL); 1981 assert((offset & PAGE_MASK) == 0); 1982 assert(length != 0); 1983 assert((length & PAGE_MASK) == 0); 1984 1985 return pages_purge_lazy((void *)((uintptr_t)addr + (uintptr_t)offset), 1986 length); 1987 } 1988 #endif 1989 1990 static bool 1991 extent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena, 1992 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 1993 size_t length, bool growing_retained) { 1994 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 1995 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 1996 1997 extent_hooks_assure_initialized(arena, r_extent_hooks); 1998 1999 if ((*r_extent_hooks)->purge_lazy == NULL) { 2000 return true; 2001 } 2002 if (*r_extent_hooks != &extent_hooks_default) { 2003 extent_hook_pre_reentrancy(tsdn, arena); 2004 } 2005 bool err = (*r_extent_hooks)->purge_lazy(*r_extent_hooks, 2006 extent_base_get(extent), extent_size_get(extent), offset, length, 2007 arena_ind_get(arena)); 2008 if (*r_extent_hooks != &extent_hooks_default) { 2009 extent_hook_post_reentrancy(tsdn); 2010 } 2011 2012 return err; 2013 } 2014 2015 bool 2016 extent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena, 2017 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 2018 size_t length) { 2019 return extent_purge_lazy_impl(tsdn, arena, r_extent_hooks, extent, 2020 offset, length, false); 2021 } 2022 2023 #ifdef PAGES_CAN_PURGE_FORCED 2024 static bool 2025 extent_purge_forced_default(extent_hooks_t *extent_hooks, void *addr, 2026 size_t size, size_t offset, size_t length, unsigned arena_ind) { 2027 assert(addr != NULL); 2028 assert((offset & PAGE_MASK) == 0); 2029 assert(length != 0); 2030 assert((length & PAGE_MASK) == 0); 2031 2032 return pages_purge_forced((void *)((uintptr_t)addr + 2033 (uintptr_t)offset), length); 2034 } 2035 #endif 2036 2037 static bool 2038 extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena, 2039 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 2040 size_t length, bool growing_retained) { 2041 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 2042 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 2043 2044 extent_hooks_assure_initialized(arena, r_extent_hooks); 2045 2046 if ((*r_extent_hooks)->purge_forced == NULL) { 2047 return true; 2048 } 2049 if (*r_extent_hooks != &extent_hooks_default) { 2050 extent_hook_pre_reentrancy(tsdn, arena); 2051 } 2052 bool err = (*r_extent_hooks)->purge_forced(*r_extent_hooks, 2053 extent_base_get(extent), extent_size_get(extent), offset, length, 2054 arena_ind_get(arena)); 2055 if (*r_extent_hooks != &extent_hooks_default) { 2056 extent_hook_post_reentrancy(tsdn); 2057 } 2058 return err; 2059 } 2060 2061 bool 2062 extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena, 2063 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset, 2064 size_t length) { 2065 return extent_purge_forced_impl(tsdn, arena, r_extent_hooks, extent, 2066 offset, length, false); 2067 } 2068 2069 static bool 2070 extent_split_default(extent_hooks_t *extent_hooks, void *addr, size_t size, 2071 size_t size_a, size_t size_b, bool committed, unsigned arena_ind) { 2072 if (!maps_coalesce) { 2073 /* 2074 * Without retain, only whole regions can be purged (required by 2075 * MEM_RELEASE on Windows) -- therefore disallow splitting. See 2076 * comments in extent_head_no_merge(). 2077 */ 2078 return !opt_retain; 2079 } 2080 2081 return false; 2082 } 2083 2084 /* 2085 * Accepts the extent to split, and the characteristics of each side of the 2086 * split. The 'a' parameters go with the 'lead' of the resulting pair of 2087 * extents (the lower addressed portion of the split), and the 'b' parameters go 2088 * with the trail (the higher addressed portion). This makes 'extent' the lead, 2089 * and returns the trail (except in case of error). 2090 */ 2091 static extent_t * 2092 extent_split_impl(tsdn_t *tsdn, arena_t *arena, 2093 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a, 2094 szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b, 2095 bool growing_retained) { 2096 assert(extent_size_get(extent) == size_a + size_b); 2097 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 2098 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 2099 2100 extent_hooks_assure_initialized(arena, r_extent_hooks); 2101 2102 if ((*r_extent_hooks)->split == NULL) { 2103 return NULL; 2104 } 2105 2106 extent_t *trail = extent_alloc(tsdn, arena); 2107 if (trail == NULL) { 2108 goto label_error_a; 2109 } 2110 2111 extent_init(trail, arena, (void *)((uintptr_t)extent_base_get(extent) + 2112 size_a), size_b, slab_b, szind_b, extent_sn_get(extent), 2113 extent_state_get(extent), extent_zeroed_get(extent), 2114 extent_committed_get(extent), extent_dumpable_get(extent), 2115 EXTENT_NOT_HEAD); 2116 2117 rtree_ctx_t rtree_ctx_fallback; 2118 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 2119 rtree_leaf_elm_t *lead_elm_a, *lead_elm_b; 2120 { 2121 extent_t lead; 2122 2123 extent_init(&lead, arena, extent_addr_get(extent), size_a, 2124 slab_a, szind_a, extent_sn_get(extent), 2125 extent_state_get(extent), extent_zeroed_get(extent), 2126 extent_committed_get(extent), extent_dumpable_get(extent), 2127 EXTENT_NOT_HEAD); 2128 2129 extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, &lead, false, 2130 true, &lead_elm_a, &lead_elm_b); 2131 } 2132 rtree_leaf_elm_t *trail_elm_a, *trail_elm_b; 2133 extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, trail, false, true, 2134 &trail_elm_a, &trail_elm_b); 2135 2136 if (lead_elm_a == NULL || lead_elm_b == NULL || trail_elm_a == NULL 2137 || trail_elm_b == NULL) { 2138 goto label_error_b; 2139 } 2140 2141 extent_lock2(tsdn, extent, trail); 2142 2143 if (*r_extent_hooks != &extent_hooks_default) { 2144 extent_hook_pre_reentrancy(tsdn, arena); 2145 } 2146 bool err = (*r_extent_hooks)->split(*r_extent_hooks, extent_base_get(extent), 2147 size_a + size_b, size_a, size_b, extent_committed_get(extent), 2148 arena_ind_get(arena)); 2149 if (*r_extent_hooks != &extent_hooks_default) { 2150 extent_hook_post_reentrancy(tsdn); 2151 } 2152 if (err) { 2153 goto label_error_c; 2154 } 2155 2156 extent_size_set(extent, size_a); 2157 extent_szind_set(extent, szind_a); 2158 2159 extent_rtree_write_acquired(tsdn, lead_elm_a, lead_elm_b, extent, 2160 szind_a, slab_a); 2161 extent_rtree_write_acquired(tsdn, trail_elm_a, trail_elm_b, trail, 2162 szind_b, slab_b); 2163 2164 extent_unlock2(tsdn, extent, trail); 2165 2166 return trail; 2167 label_error_c: 2168 extent_unlock2(tsdn, extent, trail); 2169 label_error_b: 2170 extent_dalloc(tsdn, arena, trail); 2171 label_error_a: 2172 return NULL; 2173 } 2174 2175 extent_t * 2176 extent_split_wrapper(tsdn_t *tsdn, arena_t *arena, 2177 extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a, 2178 szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b) { 2179 return extent_split_impl(tsdn, arena, r_extent_hooks, extent, size_a, 2180 szind_a, slab_a, size_b, szind_b, slab_b, false); 2181 } 2182 2183 static bool 2184 extent_merge_default_impl(void *addr_a, void *addr_b) { 2185 if (!maps_coalesce && !opt_retain) { 2186 return true; 2187 } 2188 if (have_dss && !extent_dss_mergeable(addr_a, addr_b)) { 2189 return true; 2190 } 2191 2192 return false; 2193 } 2194 2195 /* 2196 * Returns true if the given extents can't be merged because of their head bit 2197 * settings. Assumes the second extent has the higher address. 2198 */ 2199 static bool 2200 extent_head_no_merge(extent_t *a, extent_t *b) { 2201 assert(extent_base_get(a) < extent_base_get(b)); 2202 /* 2203 * When coalesce is not always allowed (Windows), only merge extents 2204 * from the same VirtualAlloc region under opt.retain (in which case 2205 * MEM_DECOMMIT is utilized for purging). 2206 */ 2207 if (maps_coalesce) { 2208 return false; 2209 } 2210 if (!opt_retain) { 2211 return true; 2212 } 2213 /* If b is a head extent, disallow the cross-region merge. */ 2214 if (extent_is_head_get(b)) { 2215 /* 2216 * Additionally, sn should not overflow with retain; sanity 2217 * check that different regions have unique sn. 2218 */ 2219 assert(extent_sn_comp(a, b) != 0); 2220 return true; 2221 } 2222 assert(extent_sn_comp(a, b) == 0); 2223 2224 return false; 2225 } 2226 2227 static bool 2228 extent_merge_default(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a, 2229 void *addr_b, size_t size_b, bool committed, unsigned arena_ind) { 2230 if (!maps_coalesce) { 2231 tsdn_t *tsdn = tsdn_fetch(); 2232 extent_t *a = iealloc(tsdn, addr_a); 2233 extent_t *b = iealloc(tsdn, addr_b); 2234 if (extent_head_no_merge(a, b)) { 2235 return true; 2236 } 2237 } 2238 return extent_merge_default_impl(addr_a, addr_b); 2239 } 2240 2241 static bool 2242 extent_merge_impl(tsdn_t *tsdn, arena_t *arena, 2243 extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b, 2244 bool growing_retained) { 2245 witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 2246 WITNESS_RANK_CORE, growing_retained ? 1 : 0); 2247 assert(extent_base_get(a) < extent_base_get(b)); 2248 2249 extent_hooks_assure_initialized(arena, r_extent_hooks); 2250 2251 if ((*r_extent_hooks)->merge == NULL || extent_head_no_merge(a, b)) { 2252 return true; 2253 } 2254 2255 bool err; 2256 if (*r_extent_hooks == &extent_hooks_default) { 2257 /* Call directly to propagate tsdn. */ 2258 err = extent_merge_default_impl(extent_base_get(a), 2259 extent_base_get(b)); 2260 } else { 2261 extent_hook_pre_reentrancy(tsdn, arena); 2262 err = (*r_extent_hooks)->merge(*r_extent_hooks, 2263 extent_base_get(a), extent_size_get(a), extent_base_get(b), 2264 extent_size_get(b), extent_committed_get(a), 2265 arena_ind_get(arena)); 2266 extent_hook_post_reentrancy(tsdn); 2267 } 2268 2269 if (err) { 2270 return true; 2271 } 2272 2273 /* 2274 * The rtree writes must happen while all the relevant elements are 2275 * owned, so the following code uses decomposed helper functions rather 2276 * than extent_{,de}register() to do things in the right order. 2277 */ 2278 rtree_ctx_t rtree_ctx_fallback; 2279 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 2280 rtree_leaf_elm_t *a_elm_a, *a_elm_b, *b_elm_a, *b_elm_b; 2281 extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, a, true, false, &a_elm_a, 2282 &a_elm_b); 2283 extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, b, true, false, &b_elm_a, 2284 &b_elm_b); 2285 2286 extent_lock2(tsdn, a, b); 2287 2288 if (a_elm_b != NULL) { 2289 rtree_leaf_elm_write(tsdn, &extents_rtree, a_elm_b, NULL, 2290 SC_NSIZES, false); 2291 } 2292 if (b_elm_b != NULL) { 2293 rtree_leaf_elm_write(tsdn, &extents_rtree, b_elm_a, NULL, 2294 SC_NSIZES, false); 2295 } else { 2296 b_elm_b = b_elm_a; 2297 } 2298 2299 extent_size_set(a, extent_size_get(a) + extent_size_get(b)); 2300 extent_szind_set(a, SC_NSIZES); 2301 extent_sn_set(a, (extent_sn_get(a) < extent_sn_get(b)) ? 2302 extent_sn_get(a) : extent_sn_get(b)); 2303 extent_zeroed_set(a, extent_zeroed_get(a) && extent_zeroed_get(b)); 2304 2305 extent_rtree_write_acquired(tsdn, a_elm_a, b_elm_b, a, SC_NSIZES, 2306 false); 2307 2308 extent_unlock2(tsdn, a, b); 2309 2310 extent_dalloc(tsdn, extent_arena_get(b), b); 2311 2312 return false; 2313 } 2314 2315 bool 2316 extent_merge_wrapper(tsdn_t *tsdn, arena_t *arena, 2317 extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b) { 2318 return extent_merge_impl(tsdn, arena, r_extent_hooks, a, b, false); 2319 } 2320 2321 bool 2322 extent_boot(void) { 2323 if (rtree_new(&extents_rtree, true)) { 2324 return true; 2325 } 2326 2327 if (mutex_pool_init(&extent_mutex_pool, "extent_mutex_pool", 2328 WITNESS_RANK_EXTENT_POOL)) { 2329 return true; 2330 } 2331 2332 if (have_dss) { 2333 extent_dss_boot(); 2334 } 2335 2336 return false; 2337 } 2338 2339 void 2340 extent_util_stats_get(tsdn_t *tsdn, const void *ptr, 2341 size_t *nfree, size_t *nregs, size_t *size) { 2342 assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL); 2343 2344 const extent_t *extent = iealloc(tsdn, ptr); 2345 if (unlikely(extent == NULL)) { 2346 *nfree = *nregs = *size = 0; 2347 return; 2348 } 2349 2350 *size = extent_size_get(extent); 2351 if (!extent_slab_get(extent)) { 2352 *nfree = 0; 2353 *nregs = 1; 2354 } else { 2355 *nfree = extent_nfree_get(extent); 2356 *nregs = bin_infos[extent_szind_get(extent)].nregs; 2357 assert(*nfree <= *nregs); 2358 assert(*nfree * extent_usize_get(extent) <= *size); 2359 } 2360 } 2361 2362 void 2363 extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr, 2364 size_t *nfree, size_t *nregs, size_t *size, 2365 size_t *bin_nfree, size_t *bin_nregs, void **slabcur_addr) { 2366 assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL 2367 && bin_nfree != NULL && bin_nregs != NULL && slabcur_addr != NULL); 2368 2369 const extent_t *extent = iealloc(tsdn, ptr); 2370 if (unlikely(extent == NULL)) { 2371 *nfree = *nregs = *size = *bin_nfree = *bin_nregs = 0; 2372 *slabcur_addr = NULL; 2373 return; 2374 } 2375 2376 *size = extent_size_get(extent); 2377 if (!extent_slab_get(extent)) { 2378 *nfree = *bin_nfree = *bin_nregs = 0; 2379 *nregs = 1; 2380 *slabcur_addr = NULL; 2381 return; 2382 } 2383 2384 *nfree = extent_nfree_get(extent); 2385 const szind_t szind = extent_szind_get(extent); 2386 *nregs = bin_infos[szind].nregs; 2387 assert(*nfree <= *nregs); 2388 assert(*nfree * extent_usize_get(extent) <= *size); 2389 2390 const arena_t *arena = extent_arena_get(extent); 2391 assert(arena != NULL); 2392 const unsigned binshard = extent_binshard_get(extent); 2393 bin_t *bin = &arena->bins[szind].bin_shards[binshard]; 2394 2395 malloc_mutex_lock(tsdn, &bin->lock); 2396 if (config_stats) { 2397 *bin_nregs = *nregs * bin->stats.curslabs; 2398 assert(*bin_nregs >= bin->stats.curregs); 2399 *bin_nfree = *bin_nregs - bin->stats.curregs; 2400 } else { 2401 *bin_nfree = *bin_nregs = 0; 2402 } 2403 *slabcur_addr = extent_addr_get(bin->slabcur); 2404 assert(*slabcur_addr != NULL); 2405 malloc_mutex_unlock(tsdn, &bin->lock); 2406 } 2407