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