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