1 #define	JEMALLOC_ARENA_C_
2 #include "jemalloc/internal/jemalloc_internal.h"
3 
4 /******************************************************************************/
5 /* Data. */
6 
7 ssize_t		opt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT;
8 static ssize_t	lg_dirty_mult_default;
9 arena_bin_info_t	arena_bin_info[NBINS];
10 
11 size_t		map_bias;
12 size_t		map_misc_offset;
13 size_t		arena_maxrun; /* Max run size for arenas. */
14 size_t		arena_maxclass; /* Max size class for arenas. */
15 static size_t	small_maxrun; /* Max run size used for small size classes. */
16 static bool	*small_run_tab; /* Valid small run page multiples. */
17 unsigned	nlclasses; /* Number of large size classes. */
18 unsigned	nhclasses; /* Number of huge size classes. */
19 
20 /******************************************************************************/
21 /*
22  * Function prototypes for static functions that are referenced prior to
23  * definition.
24  */
25 
26 static void	arena_purge(arena_t *arena, bool all);
27 static void	arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty,
28     bool cleaned);
29 static void	arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk,
30     arena_run_t *run, arena_bin_t *bin);
31 static void	arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk,
32     arena_run_t *run, arena_bin_t *bin);
33 
34 /******************************************************************************/
35 
36 JEMALLOC_INLINE_C size_t
arena_miscelm_to_bits(arena_chunk_map_misc_t * miscelm)37 arena_miscelm_to_bits(arena_chunk_map_misc_t *miscelm)
38 {
39 	arena_chunk_t *chunk = CHUNK_ADDR2BASE(miscelm);
40 	size_t pageind = arena_miscelm_to_pageind(miscelm);
41 
42 	return (arena_mapbits_get(chunk, pageind));
43 }
44 
45 JEMALLOC_INLINE_C int
arena_run_comp(arena_chunk_map_misc_t * a,arena_chunk_map_misc_t * b)46 arena_run_comp(arena_chunk_map_misc_t *a, arena_chunk_map_misc_t *b)
47 {
48 	uintptr_t a_miscelm = (uintptr_t)a;
49 	uintptr_t b_miscelm = (uintptr_t)b;
50 
51 	assert(a != NULL);
52 	assert(b != NULL);
53 
54 	return ((a_miscelm > b_miscelm) - (a_miscelm < b_miscelm));
55 }
56 
57 /* Generate red-black tree functions. */
rb_gen(static UNUSED,arena_run_tree_,arena_run_tree_t,arena_chunk_map_misc_t,rb_link,arena_run_comp)58 rb_gen(static UNUSED, arena_run_tree_, arena_run_tree_t, arena_chunk_map_misc_t,
59     rb_link, arena_run_comp)
60 
61 static size_t
62 run_quantize(size_t size)
63 {
64 	size_t qsize;
65 
66 	assert(size != 0);
67 	assert(size == PAGE_CEILING(size));
68 
69 	/* Don't change sizes that are valid small run sizes. */
70 	if (size <= small_maxrun && small_run_tab[size >> LG_PAGE])
71 		return (size);
72 
73 	/*
74 	 * Round down to the nearest run size that can actually be requested
75 	 * during normal large allocation.  Add large_pad so that cache index
76 	 * randomization can offset the allocation from the page boundary.
77 	 */
78 	qsize = index2size(size2index(size - large_pad + 1) - 1) + large_pad;
79 	if (qsize <= SMALL_MAXCLASS + large_pad)
80 		return (run_quantize(size - large_pad));
81 	assert(qsize <= size);
82 	return (qsize);
83 }
84 
85 static size_t
run_quantize_next(size_t size)86 run_quantize_next(size_t size)
87 {
88 	size_t large_run_size_next;
89 
90 	assert(size != 0);
91 	assert(size == PAGE_CEILING(size));
92 
93 	/*
94 	 * Return the next quantized size greater than the input size.
95 	 * Quantized sizes comprise the union of run sizes that back small
96 	 * region runs, and run sizes that back large regions with no explicit
97 	 * alignment constraints.
98 	 */
99 
100 	if (size > SMALL_MAXCLASS) {
101 		large_run_size_next = PAGE_CEILING(index2size(size2index(size -
102 		    large_pad) + 1) + large_pad);
103 	} else
104 		large_run_size_next = SIZE_T_MAX;
105 	if (size >= small_maxrun)
106 		return (large_run_size_next);
107 
108 	while (true) {
109 		size += PAGE;
110 		assert(size <= small_maxrun);
111 		if (small_run_tab[size >> LG_PAGE]) {
112 			if (large_run_size_next < size)
113 				return (large_run_size_next);
114 			return (size);
115 		}
116 	}
117 }
118 
119 static size_t
run_quantize_first(size_t size)120 run_quantize_first(size_t size)
121 {
122 	size_t qsize = run_quantize(size);
123 
124 	if (qsize < size) {
125 		/*
126 		 * Skip a quantization that may have an adequately large run,
127 		 * because under-sized runs may be mixed in.  This only happens
128 		 * when an unusual size is requested, i.e. for aligned
129 		 * allocation, and is just one of several places where linear
130 		 * search would potentially find sufficiently aligned available
131 		 * memory somewhere lower.
132 		 */
133 		qsize = run_quantize_next(size);
134 	}
135 	return (qsize);
136 }
137 
138 JEMALLOC_INLINE_C int
arena_avail_comp(arena_chunk_map_misc_t * a,arena_chunk_map_misc_t * b)139 arena_avail_comp(arena_chunk_map_misc_t *a, arena_chunk_map_misc_t *b)
140 {
141 	int ret;
142 	uintptr_t a_miscelm = (uintptr_t)a;
143 	size_t a_qsize;
144 	size_t b_qsize = run_quantize(arena_miscelm_to_bits(b) & ~PAGE_MASK);
145 
146 	if (a_miscelm & CHUNK_MAP_KEY) {
147 		size_t a_size = a_miscelm & ~PAGE_MASK;
148 		a_qsize = run_quantize(a_size);
149 	} else
150 		a_qsize = run_quantize(arena_miscelm_to_bits(a) & ~PAGE_MASK);
151 
152 	/*
153 	 * Compare based on quantized size rather than size, in order to sort
154 	 * equally useful runs only by address.
155 	 */
156 	ret = (a_qsize > b_qsize) - (a_qsize < b_qsize);
157 	if (ret == 0) {
158 		if (!(a_miscelm & CHUNK_MAP_KEY)) {
159 			uintptr_t b_miscelm = (uintptr_t)b;
160 
161 			ret = (a_miscelm > b_miscelm) - (a_miscelm < b_miscelm);
162 		} else {
163 			/*
164 			 * Treat keys as if they are lower than anything else.
165 			 */
166 			ret = -1;
167 		}
168 	}
169 
170 	return (ret);
171 }
172 
173 /* Generate red-black tree functions. */
rb_gen(static UNUSED,arena_avail_tree_,arena_avail_tree_t,arena_chunk_map_misc_t,rb_link,arena_avail_comp)174 rb_gen(static UNUSED, arena_avail_tree_, arena_avail_tree_t,
175     arena_chunk_map_misc_t, rb_link, arena_avail_comp)
176 
177 static void
178 arena_avail_insert(arena_t *arena, arena_chunk_t *chunk, size_t pageind,
179     size_t npages)
180 {
181 
182 	assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >>
183 	    LG_PAGE));
184 	arena_avail_tree_insert(&arena->runs_avail, arena_miscelm_get(chunk,
185 	    pageind));
186 }
187 
188 static void
arena_avail_remove(arena_t * arena,arena_chunk_t * chunk,size_t pageind,size_t npages)189 arena_avail_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind,
190     size_t npages)
191 {
192 
193 	assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >>
194 	    LG_PAGE));
195 	arena_avail_tree_remove(&arena->runs_avail, arena_miscelm_get(chunk,
196 	    pageind));
197 }
198 
199 static void
arena_run_dirty_insert(arena_t * arena,arena_chunk_t * chunk,size_t pageind,size_t npages)200 arena_run_dirty_insert(arena_t *arena, arena_chunk_t *chunk, size_t pageind,
201     size_t npages)
202 {
203 	arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind);
204 
205 	assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >>
206 	    LG_PAGE));
207 	assert(arena_mapbits_dirty_get(chunk, pageind) == CHUNK_MAP_DIRTY);
208 	assert(arena_mapbits_dirty_get(chunk, pageind+npages-1) ==
209 	    CHUNK_MAP_DIRTY);
210 
211 	qr_new(&miscelm->rd, rd_link);
212 	qr_meld(&arena->runs_dirty, &miscelm->rd, rd_link);
213 	arena->ndirty += npages;
214 }
215 
216 static void
arena_run_dirty_remove(arena_t * arena,arena_chunk_t * chunk,size_t pageind,size_t npages)217 arena_run_dirty_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind,
218     size_t npages)
219 {
220 	arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind);
221 
222 	assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >>
223 	    LG_PAGE));
224 	assert(arena_mapbits_dirty_get(chunk, pageind) == CHUNK_MAP_DIRTY);
225 	assert(arena_mapbits_dirty_get(chunk, pageind+npages-1) ==
226 	    CHUNK_MAP_DIRTY);
227 
228 	qr_remove(&miscelm->rd, rd_link);
229 	assert(arena->ndirty >= npages);
230 	arena->ndirty -= npages;
231 }
232 
233 static size_t
arena_chunk_dirty_npages(const extent_node_t * node)234 arena_chunk_dirty_npages(const extent_node_t *node)
235 {
236 
237 	return (extent_node_size_get(node) >> LG_PAGE);
238 }
239 
240 void
arena_chunk_cache_maybe_insert(arena_t * arena,extent_node_t * node,bool cache)241 arena_chunk_cache_maybe_insert(arena_t *arena, extent_node_t *node, bool cache)
242 {
243 
244 	if (cache) {
245 		extent_node_dirty_linkage_init(node);
246 		extent_node_dirty_insert(node, &arena->runs_dirty,
247 		    &arena->chunks_cache);
248 		arena->ndirty += arena_chunk_dirty_npages(node);
249 	}
250 }
251 
252 void
arena_chunk_cache_maybe_remove(arena_t * arena,extent_node_t * node,bool dirty)253 arena_chunk_cache_maybe_remove(arena_t *arena, extent_node_t *node, bool dirty)
254 {
255 
256 	if (dirty) {
257 		extent_node_dirty_remove(node);
258 		assert(arena->ndirty >= arena_chunk_dirty_npages(node));
259 		arena->ndirty -= arena_chunk_dirty_npages(node);
260 	}
261 }
262 
263 JEMALLOC_INLINE_C void *
arena_run_reg_alloc(arena_run_t * run,arena_bin_info_t * bin_info)264 arena_run_reg_alloc(arena_run_t *run, arena_bin_info_t *bin_info)
265 {
266 	void *ret;
267 	unsigned regind;
268 	arena_chunk_map_misc_t *miscelm;
269 	void *rpages;
270 
271 	assert(run->nfree > 0);
272 	assert(!bitmap_full(run->bitmap, &bin_info->bitmap_info));
273 
274 	regind = bitmap_sfu(run->bitmap, &bin_info->bitmap_info);
275 	miscelm = arena_run_to_miscelm(run);
276 	rpages = arena_miscelm_to_rpages(miscelm);
277 	ret = (void *)((uintptr_t)rpages + (uintptr_t)bin_info->reg0_offset +
278 	    (uintptr_t)(bin_info->reg_interval * regind));
279 	run->nfree--;
280 	return (ret);
281 }
282 
283 JEMALLOC_INLINE_C void
arena_run_reg_dalloc(arena_run_t * run,void * ptr)284 arena_run_reg_dalloc(arena_run_t *run, void *ptr)
285 {
286 	arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
287 	size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
288 	size_t mapbits = arena_mapbits_get(chunk, pageind);
289 	index_t binind = arena_ptr_small_binind_get(ptr, mapbits);
290 	arena_bin_info_t *bin_info = &arena_bin_info[binind];
291 	unsigned regind = arena_run_regind(run, bin_info, ptr);
292 
293 	assert(run->nfree < bin_info->nregs);
294 	/* Freeing an interior pointer can cause assertion failure. */
295 	assert(((uintptr_t)ptr -
296 	    ((uintptr_t)arena_miscelm_to_rpages(arena_run_to_miscelm(run)) +
297 	    (uintptr_t)bin_info->reg0_offset)) %
298 	    (uintptr_t)bin_info->reg_interval == 0);
299 	assert((uintptr_t)ptr >=
300 	    (uintptr_t)arena_miscelm_to_rpages(arena_run_to_miscelm(run)) +
301 	    (uintptr_t)bin_info->reg0_offset);
302 	/* Freeing an unallocated pointer can cause assertion failure. */
303 	assert(bitmap_get(run->bitmap, &bin_info->bitmap_info, regind));
304 
305 	bitmap_unset(run->bitmap, &bin_info->bitmap_info, regind);
306 	run->nfree++;
307 }
308 
309 JEMALLOC_INLINE_C void
arena_run_zero(arena_chunk_t * chunk,size_t run_ind,size_t npages)310 arena_run_zero(arena_chunk_t *chunk, size_t run_ind, size_t npages)
311 {
312 
313 	JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk +
314 	    (run_ind << LG_PAGE)), (npages << LG_PAGE));
315 	memset((void *)((uintptr_t)chunk + (run_ind << LG_PAGE)), 0,
316 	    (npages << LG_PAGE));
317 }
318 
319 JEMALLOC_INLINE_C void
arena_run_page_mark_zeroed(arena_chunk_t * chunk,size_t run_ind)320 arena_run_page_mark_zeroed(arena_chunk_t *chunk, size_t run_ind)
321 {
322 
323 	JEMALLOC_VALGRIND_MAKE_MEM_DEFINED((void *)((uintptr_t)chunk + (run_ind
324 	    << LG_PAGE)), PAGE);
325 }
326 
327 JEMALLOC_INLINE_C void
arena_run_page_validate_zeroed(arena_chunk_t * chunk,size_t run_ind)328 arena_run_page_validate_zeroed(arena_chunk_t *chunk, size_t run_ind)
329 {
330 	size_t i;
331 	UNUSED size_t *p = (size_t *)((uintptr_t)chunk + (run_ind << LG_PAGE));
332 
333 	arena_run_page_mark_zeroed(chunk, run_ind);
334 	for (i = 0; i < PAGE / sizeof(size_t); i++)
335 		assert(p[i] == 0);
336 }
337 
338 static void
arena_cactive_update(arena_t * arena,size_t add_pages,size_t sub_pages)339 arena_cactive_update(arena_t *arena, size_t add_pages, size_t sub_pages)
340 {
341 
342 	if (config_stats) {
343 		ssize_t cactive_diff = CHUNK_CEILING((arena->nactive + add_pages
344 		    - sub_pages) << LG_PAGE) - CHUNK_CEILING(arena->nactive <<
345 		    LG_PAGE);
346 		if (cactive_diff != 0)
347 			stats_cactive_add(cactive_diff);
348 	}
349 }
350 
351 static void
arena_run_split_remove(arena_t * arena,arena_chunk_t * chunk,size_t run_ind,size_t flag_dirty,size_t need_pages)352 arena_run_split_remove(arena_t *arena, arena_chunk_t *chunk, size_t run_ind,
353     size_t flag_dirty, size_t need_pages)
354 {
355 	size_t total_pages, rem_pages;
356 
357 	total_pages = arena_mapbits_unallocated_size_get(chunk, run_ind) >>
358 	    LG_PAGE;
359 	assert(arena_mapbits_dirty_get(chunk, run_ind+total_pages-1) ==
360 	    flag_dirty);
361 	assert(need_pages <= total_pages);
362 	rem_pages = total_pages - need_pages;
363 
364 	arena_avail_remove(arena, chunk, run_ind, total_pages);
365 	if (flag_dirty != 0)
366 		arena_run_dirty_remove(arena, chunk, run_ind, total_pages);
367 	arena_cactive_update(arena, need_pages, 0);
368 	arena->nactive += need_pages;
369 
370 	/* Keep track of trailing unused pages for later use. */
371 	if (rem_pages > 0) {
372 		if (flag_dirty != 0) {
373 			arena_mapbits_unallocated_set(chunk,
374 			    run_ind+need_pages, (rem_pages << LG_PAGE),
375 			    flag_dirty);
376 			arena_mapbits_unallocated_set(chunk,
377 			    run_ind+total_pages-1, (rem_pages << LG_PAGE),
378 			    flag_dirty);
379 			arena_run_dirty_insert(arena, chunk, run_ind+need_pages,
380 			    rem_pages);
381 		} else {
382 			arena_mapbits_unallocated_set(chunk, run_ind+need_pages,
383 			    (rem_pages << LG_PAGE),
384 			    arena_mapbits_unzeroed_get(chunk,
385 			    run_ind+need_pages));
386 			arena_mapbits_unallocated_set(chunk,
387 			    run_ind+total_pages-1, (rem_pages << LG_PAGE),
388 			    arena_mapbits_unzeroed_get(chunk,
389 			    run_ind+total_pages-1));
390 		}
391 		arena_avail_insert(arena, chunk, run_ind+need_pages, rem_pages);
392 	}
393 }
394 
395 static void
arena_run_split_large_helper(arena_t * arena,arena_run_t * run,size_t size,bool remove,bool zero)396 arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size,
397     bool remove, bool zero)
398 {
399 	arena_chunk_t *chunk;
400 	arena_chunk_map_misc_t *miscelm;
401 	size_t flag_dirty, run_ind, need_pages, i;
402 
403 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
404 	miscelm = arena_run_to_miscelm(run);
405 	run_ind = arena_miscelm_to_pageind(miscelm);
406 	flag_dirty = arena_mapbits_dirty_get(chunk, run_ind);
407 	need_pages = (size >> LG_PAGE);
408 	assert(need_pages > 0);
409 
410 	if (remove) {
411 		arena_run_split_remove(arena, chunk, run_ind, flag_dirty,
412 		    need_pages);
413 	}
414 
415 	if (zero) {
416 		if (flag_dirty == 0) {
417 			/*
418 			 * The run is clean, so some pages may be zeroed (i.e.
419 			 * never before touched).
420 			 */
421 			for (i = 0; i < need_pages; i++) {
422 				if (arena_mapbits_unzeroed_get(chunk, run_ind+i)
423 				    != 0)
424 					arena_run_zero(chunk, run_ind+i, 1);
425 				else if (config_debug) {
426 					arena_run_page_validate_zeroed(chunk,
427 					    run_ind+i);
428 				} else {
429 					arena_run_page_mark_zeroed(chunk,
430 					    run_ind+i);
431 				}
432 			}
433 		} else {
434 			/* The run is dirty, so all pages must be zeroed. */
435 			arena_run_zero(chunk, run_ind, need_pages);
436 		}
437 	} else {
438 		JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk +
439 		    (run_ind << LG_PAGE)), (need_pages << LG_PAGE));
440 	}
441 
442 	/*
443 	 * Set the last element first, in case the run only contains one page
444 	 * (i.e. both statements set the same element).
445 	 */
446 	arena_mapbits_large_set(chunk, run_ind+need_pages-1, 0, flag_dirty);
447 	arena_mapbits_large_set(chunk, run_ind, size, flag_dirty);
448 }
449 
450 static void
arena_run_split_large(arena_t * arena,arena_run_t * run,size_t size,bool zero)451 arena_run_split_large(arena_t *arena, arena_run_t *run, size_t size, bool zero)
452 {
453 
454 	arena_run_split_large_helper(arena, run, size, true, zero);
455 }
456 
457 static void
arena_run_init_large(arena_t * arena,arena_run_t * run,size_t size,bool zero)458 arena_run_init_large(arena_t *arena, arena_run_t *run, size_t size, bool zero)
459 {
460 
461 	arena_run_split_large_helper(arena, run, size, false, zero);
462 }
463 
464 static void
arena_run_split_small(arena_t * arena,arena_run_t * run,size_t size,index_t binind)465 arena_run_split_small(arena_t *arena, arena_run_t *run, size_t size,
466     index_t binind)
467 {
468 	arena_chunk_t *chunk;
469 	arena_chunk_map_misc_t *miscelm;
470 	size_t flag_dirty, run_ind, need_pages, i;
471 
472 	assert(binind != BININD_INVALID);
473 
474 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
475 	miscelm = arena_run_to_miscelm(run);
476 	run_ind = arena_miscelm_to_pageind(miscelm);
477 	flag_dirty = arena_mapbits_dirty_get(chunk, run_ind);
478 	need_pages = (size >> LG_PAGE);
479 	assert(need_pages > 0);
480 
481 	arena_run_split_remove(arena, chunk, run_ind, flag_dirty, need_pages);
482 
483 	for (i = 0; i < need_pages; i++) {
484 		arena_mapbits_small_set(chunk, run_ind+i, i, binind, 0);
485 		if (config_debug && flag_dirty == 0 &&
486 		    arena_mapbits_unzeroed_get(chunk, run_ind+i) == 0)
487 			arena_run_page_validate_zeroed(chunk, run_ind+i);
488 	}
489 	JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk +
490 	    (run_ind << LG_PAGE)), (need_pages << LG_PAGE));
491 }
492 
493 static arena_chunk_t *
arena_chunk_init_spare(arena_t * arena)494 arena_chunk_init_spare(arena_t *arena)
495 {
496 	arena_chunk_t *chunk;
497 
498 	assert(arena->spare != NULL);
499 
500 	chunk = arena->spare;
501 	arena->spare = NULL;
502 
503 	assert(arena_mapbits_allocated_get(chunk, map_bias) == 0);
504 	assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0);
505 	assert(arena_mapbits_unallocated_size_get(chunk, map_bias) ==
506 	    arena_maxrun);
507 	assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) ==
508 	    arena_maxrun);
509 	assert(arena_mapbits_dirty_get(chunk, map_bias) ==
510 	    arena_mapbits_dirty_get(chunk, chunk_npages-1));
511 
512 	return (chunk);
513 }
514 
515 static bool
arena_chunk_register(arena_t * arena,arena_chunk_t * chunk,bool zero)516 arena_chunk_register(arena_t *arena, arena_chunk_t *chunk, bool zero)
517 {
518 
519 	extent_node_init(&chunk->node, arena, chunk, chunksize, zero);
520 	extent_node_achunk_set(&chunk->node, true);
521 	return (chunk_register(chunk, &chunk->node));
522 }
523 
524 static arena_chunk_t *
arena_chunk_alloc_internal_hard(arena_t * arena,bool * zero)525 arena_chunk_alloc_internal_hard(arena_t *arena, bool *zero)
526 {
527 	arena_chunk_t *chunk;
528 	chunk_alloc_t *chunk_alloc = arena->chunk_alloc;
529 	chunk_dalloc_t *chunk_dalloc = arena->chunk_dalloc;
530 
531 	malloc_mutex_unlock(&arena->lock);
532 	chunk = (arena_chunk_t *)chunk_alloc_wrapper(arena, chunk_alloc, NULL,
533 	    chunksize, chunksize, zero);
534 	if (chunk != NULL && arena_chunk_register(arena, chunk, *zero)) {
535 		chunk_dalloc_wrapper(arena, chunk_dalloc, (void *)chunk,
536 		    chunksize);
537 		chunk = NULL;
538 	}
539 	malloc_mutex_lock(&arena->lock);
540 
541 	return (chunk);
542 }
543 
544 static arena_chunk_t *
arena_chunk_alloc_internal(arena_t * arena,bool * zero)545 arena_chunk_alloc_internal(arena_t *arena, bool *zero)
546 {
547 	arena_chunk_t *chunk;
548 
549 	if (likely(arena->chunk_alloc == chunk_alloc_default)) {
550 		chunk = chunk_alloc_cache(arena, NULL, chunksize, chunksize,
551 		    zero, true);
552 		if (chunk != NULL && arena_chunk_register(arena, chunk,
553 		    *zero)) {
554 			chunk_dalloc_cache(arena, chunk, chunksize);
555 			return (NULL);
556 		}
557 	} else
558 		chunk = NULL;
559 	if (chunk == NULL)
560 		chunk = arena_chunk_alloc_internal_hard(arena, zero);
561 
562 	if (config_stats && chunk != NULL) {
563 		arena->stats.mapped += chunksize;
564 		arena->stats.metadata_mapped += (map_bias << LG_PAGE);
565 	}
566 
567 	return (chunk);
568 }
569 
570 static arena_chunk_t *
arena_chunk_init_hard(arena_t * arena)571 arena_chunk_init_hard(arena_t *arena)
572 {
573 	arena_chunk_t *chunk;
574 	bool zero;
575 	size_t unzeroed, i;
576 
577 	assert(arena->spare == NULL);
578 
579 	zero = false;
580 	chunk = arena_chunk_alloc_internal(arena, &zero);
581 	if (chunk == NULL)
582 		return (NULL);
583 
584 	/*
585 	 * Initialize the map to contain one maximal free untouched run.  Mark
586 	 * the pages as zeroed iff chunk_alloc() returned a zeroed chunk.
587 	 */
588 	unzeroed = zero ? 0 : CHUNK_MAP_UNZEROED;
589 	arena_mapbits_unallocated_set(chunk, map_bias, arena_maxrun, unzeroed);
590 	/*
591 	 * There is no need to initialize the internal page map entries unless
592 	 * the chunk is not zeroed.
593 	 */
594 	if (!zero) {
595 		JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(
596 		    (void *)arena_bitselm_get(chunk, map_bias+1),
597 		    (size_t)((uintptr_t) arena_bitselm_get(chunk,
598 		    chunk_npages-1) - (uintptr_t)arena_bitselm_get(chunk,
599 		    map_bias+1)));
600 		for (i = map_bias+1; i < chunk_npages-1; i++)
601 			arena_mapbits_unzeroed_set(chunk, i, unzeroed);
602 	} else {
603 		JEMALLOC_VALGRIND_MAKE_MEM_DEFINED((void
604 		    *)arena_bitselm_get(chunk, map_bias+1), (size_t)((uintptr_t)
605 		    arena_bitselm_get(chunk, chunk_npages-1) -
606 		    (uintptr_t)arena_bitselm_get(chunk, map_bias+1)));
607 		if (config_debug) {
608 			for (i = map_bias+1; i < chunk_npages-1; i++) {
609 				assert(arena_mapbits_unzeroed_get(chunk, i) ==
610 				    unzeroed);
611 			}
612 		}
613 	}
614 	arena_mapbits_unallocated_set(chunk, chunk_npages-1, arena_maxrun,
615 	    unzeroed);
616 
617 	return (chunk);
618 }
619 
620 static arena_chunk_t *
arena_chunk_alloc(arena_t * arena)621 arena_chunk_alloc(arena_t *arena)
622 {
623 	arena_chunk_t *chunk;
624 
625 	if (arena->spare != NULL)
626 		chunk = arena_chunk_init_spare(arena);
627 	else {
628 		chunk = arena_chunk_init_hard(arena);
629 		if (chunk == NULL)
630 			return (NULL);
631 	}
632 
633 	/* Insert the run into the runs_avail tree. */
634 	arena_avail_insert(arena, chunk, map_bias, chunk_npages-map_bias);
635 
636 	return (chunk);
637 }
638 
639 static void
arena_chunk_dalloc(arena_t * arena,arena_chunk_t * chunk)640 arena_chunk_dalloc(arena_t *arena, arena_chunk_t *chunk)
641 {
642 
643 	assert(arena_mapbits_allocated_get(chunk, map_bias) == 0);
644 	assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0);
645 	assert(arena_mapbits_unallocated_size_get(chunk, map_bias) ==
646 	    arena_maxrun);
647 	assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) ==
648 	    arena_maxrun);
649 	assert(arena_mapbits_dirty_get(chunk, map_bias) ==
650 	    arena_mapbits_dirty_get(chunk, chunk_npages-1));
651 
652 	/*
653 	 * Remove run from the runs_avail tree, so that the arena does not use
654 	 * it.
655 	 */
656 	arena_avail_remove(arena, chunk, map_bias, chunk_npages-map_bias);
657 
658 	if (arena->spare != NULL) {
659 		arena_chunk_t *spare = arena->spare;
660 		chunk_dalloc_t *chunk_dalloc;
661 
662 		arena->spare = chunk;
663 		if (arena_mapbits_dirty_get(spare, map_bias) != 0) {
664 			arena_run_dirty_remove(arena, spare, map_bias,
665 			    chunk_npages-map_bias);
666 		}
667 
668 		chunk_deregister(spare, &spare->node);
669 
670 		chunk_dalloc = arena->chunk_dalloc;
671 		if (likely(chunk_dalloc == chunk_dalloc_default))
672 			chunk_dalloc_cache(arena, (void *)spare, chunksize);
673 		else {
674 			malloc_mutex_unlock(&arena->lock);
675 			chunk_dalloc_wrapper(arena, chunk_dalloc, (void *)spare,
676 			    chunksize);
677 			malloc_mutex_lock(&arena->lock);
678 		}
679 
680 		if (config_stats) {
681 			arena->stats.mapped -= chunksize;
682 			arena->stats.metadata_mapped -= (map_bias << LG_PAGE);
683 		}
684 	} else
685 		arena->spare = chunk;
686 }
687 
688 static void
arena_huge_malloc_stats_update(arena_t * arena,size_t usize)689 arena_huge_malloc_stats_update(arena_t *arena, size_t usize)
690 {
691 	index_t index = size2index(usize) - nlclasses - NBINS;
692 
693 	cassert(config_stats);
694 
695 	arena->stats.nmalloc_huge++;
696 	arena->stats.allocated_huge += usize;
697 	arena->stats.hstats[index].nmalloc++;
698 	arena->stats.hstats[index].curhchunks++;
699 }
700 
701 static void
arena_huge_malloc_stats_update_undo(arena_t * arena,size_t usize)702 arena_huge_malloc_stats_update_undo(arena_t *arena, size_t usize)
703 {
704 	index_t index = size2index(usize) - nlclasses - NBINS;
705 
706 	cassert(config_stats);
707 
708 	arena->stats.nmalloc_huge--;
709 	arena->stats.allocated_huge -= usize;
710 	arena->stats.hstats[index].nmalloc--;
711 	arena->stats.hstats[index].curhchunks--;
712 }
713 
714 static void
arena_huge_dalloc_stats_update(arena_t * arena,size_t usize)715 arena_huge_dalloc_stats_update(arena_t *arena, size_t usize)
716 {
717 	index_t index = size2index(usize) - nlclasses - NBINS;
718 
719 	cassert(config_stats);
720 
721 	arena->stats.ndalloc_huge++;
722 	arena->stats.allocated_huge -= usize;
723 	arena->stats.hstats[index].ndalloc++;
724 	arena->stats.hstats[index].curhchunks--;
725 }
726 
727 static void
arena_huge_dalloc_stats_update_undo(arena_t * arena,size_t usize)728 arena_huge_dalloc_stats_update_undo(arena_t *arena, size_t usize)
729 {
730 	index_t index = size2index(usize) - nlclasses - NBINS;
731 
732 	cassert(config_stats);
733 
734 	arena->stats.ndalloc_huge--;
735 	arena->stats.allocated_huge += usize;
736 	arena->stats.hstats[index].ndalloc--;
737 	arena->stats.hstats[index].curhchunks++;
738 }
739 
740 static void
arena_huge_ralloc_stats_update(arena_t * arena,size_t oldsize,size_t usize)741 arena_huge_ralloc_stats_update(arena_t *arena, size_t oldsize, size_t usize)
742 {
743 
744 	arena_huge_dalloc_stats_update(arena, oldsize);
745 	arena_huge_malloc_stats_update(arena, usize);
746 }
747 
748 static void
arena_huge_ralloc_stats_update_undo(arena_t * arena,size_t oldsize,size_t usize)749 arena_huge_ralloc_stats_update_undo(arena_t *arena, size_t oldsize,
750     size_t usize)
751 {
752 
753 	arena_huge_dalloc_stats_update_undo(arena, oldsize);
754 	arena_huge_malloc_stats_update_undo(arena, usize);
755 }
756 
757 extent_node_t *
arena_node_alloc(arena_t * arena)758 arena_node_alloc(arena_t *arena)
759 {
760 	extent_node_t *node;
761 
762 	malloc_mutex_lock(&arena->node_cache_mtx);
763 	node = ql_last(&arena->node_cache, ql_link);
764 	if (node == NULL) {
765 		malloc_mutex_unlock(&arena->node_cache_mtx);
766 		return (base_alloc(sizeof(extent_node_t)));
767 	}
768 	ql_tail_remove(&arena->node_cache, extent_node_t, ql_link);
769 	malloc_mutex_unlock(&arena->node_cache_mtx);
770 	return (node);
771 }
772 
773 void
arena_node_dalloc(arena_t * arena,extent_node_t * node)774 arena_node_dalloc(arena_t *arena, extent_node_t *node)
775 {
776 
777 	malloc_mutex_lock(&arena->node_cache_mtx);
778 	ql_elm_new(node, ql_link);
779 	ql_tail_insert(&arena->node_cache, node, ql_link);
780 	malloc_mutex_unlock(&arena->node_cache_mtx);
781 }
782 
783 static void *
arena_chunk_alloc_huge_hard(arena_t * arena,chunk_alloc_t * chunk_alloc,size_t usize,size_t alignment,bool * zero,size_t csize)784 arena_chunk_alloc_huge_hard(arena_t *arena, chunk_alloc_t *chunk_alloc,
785     size_t usize, size_t alignment, bool *zero, size_t csize)
786 {
787 	void *ret;
788 
789 	ret = chunk_alloc_wrapper(arena, chunk_alloc, NULL, csize, alignment,
790 	    zero);
791 	if (ret == NULL) {
792 		/* Revert optimistic stats updates. */
793 		malloc_mutex_lock(&arena->lock);
794 		if (config_stats) {
795 			arena_huge_malloc_stats_update_undo(arena, usize);
796 			arena->stats.mapped -= usize;
797 		}
798 		arena->nactive -= (usize >> LG_PAGE);
799 		malloc_mutex_unlock(&arena->lock);
800 	}
801 
802 	return (ret);
803 }
804 
805 void *
arena_chunk_alloc_huge(arena_t * arena,size_t usize,size_t alignment,bool * zero)806 arena_chunk_alloc_huge(arena_t *arena, size_t usize, size_t alignment,
807     bool *zero)
808 {
809 	void *ret;
810 	chunk_alloc_t *chunk_alloc;
811 	size_t csize = CHUNK_CEILING(usize);
812 
813 	malloc_mutex_lock(&arena->lock);
814 
815 	/* Optimistically update stats. */
816 	if (config_stats) {
817 		arena_huge_malloc_stats_update(arena, usize);
818 		arena->stats.mapped += usize;
819 	}
820 	arena->nactive += (usize >> LG_PAGE);
821 
822 	chunk_alloc = arena->chunk_alloc;
823 	if (likely(chunk_alloc == chunk_alloc_default)) {
824 		ret = chunk_alloc_cache(arena, NULL, csize, alignment, zero,
825 		    true);
826 	} else
827 		ret = NULL;
828 	malloc_mutex_unlock(&arena->lock);
829 	if (ret == NULL) {
830 		ret = arena_chunk_alloc_huge_hard(arena, chunk_alloc, usize,
831 		    alignment, zero, csize);
832 	}
833 
834 	if (config_stats && ret != NULL)
835 		stats_cactive_add(usize);
836 	return (ret);
837 }
838 
839 void
arena_chunk_dalloc_huge(arena_t * arena,void * chunk,size_t usize)840 arena_chunk_dalloc_huge(arena_t *arena, void *chunk, size_t usize)
841 {
842 	chunk_dalloc_t *chunk_dalloc;
843 	size_t csize;
844 
845 	csize = CHUNK_CEILING(usize);
846 	malloc_mutex_lock(&arena->lock);
847 	chunk_dalloc = arena->chunk_dalloc;
848 	if (config_stats) {
849 		arena_huge_dalloc_stats_update(arena, usize);
850 		arena->stats.mapped -= usize;
851 		stats_cactive_sub(usize);
852 	}
853 	arena->nactive -= (usize >> LG_PAGE);
854 
855 	if (likely(chunk_dalloc == chunk_dalloc_default)) {
856 		chunk_dalloc_cache(arena, chunk, csize);
857 		malloc_mutex_unlock(&arena->lock);
858 	} else {
859 		malloc_mutex_unlock(&arena->lock);
860 		chunk_dalloc_wrapper(arena, chunk_dalloc, chunk, csize);
861 	}
862 }
863 
864 void
arena_chunk_ralloc_huge_similar(arena_t * arena,void * chunk,size_t oldsize,size_t usize)865 arena_chunk_ralloc_huge_similar(arena_t *arena, void *chunk, size_t oldsize,
866     size_t usize)
867 {
868 
869 	assert(CHUNK_CEILING(oldsize) == CHUNK_CEILING(usize));
870 	assert(oldsize != usize);
871 
872 	malloc_mutex_lock(&arena->lock);
873 	if (config_stats)
874 		arena_huge_ralloc_stats_update(arena, oldsize, usize);
875 	if (oldsize < usize) {
876 		size_t udiff = usize - oldsize;
877 		arena->nactive += udiff >> LG_PAGE;
878 		if (config_stats)
879 			stats_cactive_add(udiff);
880 	} else {
881 		size_t udiff = oldsize - usize;
882 		arena->nactive -= udiff >> LG_PAGE;
883 		if (config_stats)
884 			stats_cactive_sub(udiff);
885 	}
886 	malloc_mutex_unlock(&arena->lock);
887 }
888 
889 void
arena_chunk_ralloc_huge_shrink(arena_t * arena,void * chunk,size_t oldsize,size_t usize)890 arena_chunk_ralloc_huge_shrink(arena_t *arena, void *chunk, size_t oldsize,
891     size_t usize)
892 {
893 	size_t udiff = oldsize - usize;
894 	size_t cdiff = CHUNK_CEILING(oldsize) - CHUNK_CEILING(usize);
895 
896 	malloc_mutex_lock(&arena->lock);
897 	if (config_stats) {
898 		arena_huge_ralloc_stats_update(arena, oldsize, usize);
899 		if (cdiff != 0) {
900 			arena->stats.mapped -= cdiff;
901 			stats_cactive_sub(udiff);
902 		}
903 	}
904 	arena->nactive -= udiff >> LG_PAGE;
905 
906 	if (cdiff != 0) {
907 		chunk_dalloc_t *chunk_dalloc = arena->chunk_dalloc;
908 		void *nchunk = (void *)((uintptr_t)chunk +
909 		    CHUNK_CEILING(usize));
910 
911 		if (likely(chunk_dalloc == chunk_dalloc_default)) {
912 			chunk_dalloc_cache(arena, nchunk, cdiff);
913 			malloc_mutex_unlock(&arena->lock);
914 		} else {
915 			malloc_mutex_unlock(&arena->lock);
916 			chunk_dalloc_wrapper(arena, chunk_dalloc, nchunk,
917 			    cdiff);
918 		}
919 	} else
920 		malloc_mutex_unlock(&arena->lock);
921 }
922 
923 bool
arena_chunk_ralloc_huge_expand_hard(arena_t * arena,chunk_alloc_t * chunk_alloc,size_t oldsize,size_t usize,bool * zero,void * nchunk,size_t udiff,size_t cdiff)924 arena_chunk_ralloc_huge_expand_hard(arena_t *arena, chunk_alloc_t *chunk_alloc,
925     size_t oldsize, size_t usize, bool *zero, void *nchunk, size_t udiff,
926     size_t cdiff)
927 {
928 	bool err;
929 
930 	err = (chunk_alloc_wrapper(arena, chunk_alloc, nchunk, cdiff, chunksize,
931 	    zero) == NULL);
932 	if (err) {
933 		/* Revert optimistic stats updates. */
934 		malloc_mutex_lock(&arena->lock);
935 		if (config_stats) {
936 			arena_huge_ralloc_stats_update_undo(arena, oldsize,
937 			    usize);
938 			arena->stats.mapped -= cdiff;
939 		}
940 		arena->nactive -= (udiff >> LG_PAGE);
941 		malloc_mutex_unlock(&arena->lock);
942 	}
943 	return (err);
944 }
945 
946 bool
arena_chunk_ralloc_huge_expand(arena_t * arena,void * chunk,size_t oldsize,size_t usize,bool * zero)947 arena_chunk_ralloc_huge_expand(arena_t *arena, void *chunk, size_t oldsize,
948     size_t usize, bool *zero)
949 {
950 	bool err;
951 	chunk_alloc_t *chunk_alloc;
952 	void *nchunk = (void *)((uintptr_t)chunk + CHUNK_CEILING(oldsize));
953 	size_t udiff = usize - oldsize;
954 	size_t cdiff = CHUNK_CEILING(usize) - CHUNK_CEILING(oldsize);
955 
956 	malloc_mutex_lock(&arena->lock);
957 
958 	/* Optimistically update stats. */
959 	if (config_stats) {
960 		arena_huge_ralloc_stats_update(arena, oldsize, usize);
961 		arena->stats.mapped += cdiff;
962 	}
963 	arena->nactive += (udiff >> LG_PAGE);
964 
965 	chunk_alloc = arena->chunk_alloc;
966 	if (likely(chunk_alloc == chunk_alloc_default)) {
967 		err = (chunk_alloc_cache(arena, nchunk, cdiff, chunksize, zero,
968 		    true) == NULL);
969 	} else
970 		err = true;
971 	malloc_mutex_unlock(&arena->lock);
972 	if (err) {
973 		err = arena_chunk_ralloc_huge_expand_hard(arena, chunk_alloc,
974 		    oldsize, usize, zero, nchunk, udiff, cdiff);
975 	}
976 
977 	if (config_stats && !err)
978 		stats_cactive_add(udiff);
979 	return (err);
980 }
981 
982 /*
983  * Do first-best-fit run selection, i.e. select the lowest run that best fits.
984  * Run sizes are quantized, so not all candidate runs are necessarily exactly
985  * the same size.
986  */
987 static arena_run_t *
arena_run_first_best_fit(arena_t * arena,size_t size)988 arena_run_first_best_fit(arena_t *arena, size_t size)
989 {
990 	size_t search_size = run_quantize_first(size);
991 	arena_chunk_map_misc_t *key = (arena_chunk_map_misc_t *)
992 	    (search_size | CHUNK_MAP_KEY);
993 	arena_chunk_map_misc_t *miscelm =
994 	    arena_avail_tree_nsearch(&arena->runs_avail, key);
995 	if (miscelm == NULL)
996 		return (NULL);
997 	return (&miscelm->run);
998 }
999 
1000 static arena_run_t *
arena_run_alloc_large_helper(arena_t * arena,size_t size,bool zero)1001 arena_run_alloc_large_helper(arena_t *arena, size_t size, bool zero)
1002 {
1003 	arena_run_t *run = arena_run_first_best_fit(arena, s2u(size));
1004 	if (run != NULL)
1005 		arena_run_split_large(arena, run, size, zero);
1006 	return (run);
1007 }
1008 
1009 static arena_run_t *
arena_run_alloc_large(arena_t * arena,size_t size,bool zero)1010 arena_run_alloc_large(arena_t *arena, size_t size, bool zero)
1011 {
1012 	arena_chunk_t *chunk;
1013 	arena_run_t *run;
1014 
1015 	assert(size <= arena_maxrun);
1016 	assert(size == PAGE_CEILING(size));
1017 
1018 	/* Search the arena's chunks for the lowest best fit. */
1019 	run = arena_run_alloc_large_helper(arena, size, zero);
1020 	if (run != NULL)
1021 		return (run);
1022 
1023 	/*
1024 	 * No usable runs.  Create a new chunk from which to allocate the run.
1025 	 */
1026 	chunk = arena_chunk_alloc(arena);
1027 	if (chunk != NULL) {
1028 		run = &arena_miscelm_get(chunk, map_bias)->run;
1029 		arena_run_split_large(arena, run, size, zero);
1030 		return (run);
1031 	}
1032 
1033 	/*
1034 	 * arena_chunk_alloc() failed, but another thread may have made
1035 	 * sufficient memory available while this one dropped arena->lock in
1036 	 * arena_chunk_alloc(), so search one more time.
1037 	 */
1038 	return (arena_run_alloc_large_helper(arena, size, zero));
1039 }
1040 
1041 static arena_run_t *
arena_run_alloc_small_helper(arena_t * arena,size_t size,index_t binind)1042 arena_run_alloc_small_helper(arena_t *arena, size_t size, index_t binind)
1043 {
1044 	arena_run_t *run = arena_run_first_best_fit(arena, size);
1045 	if (run != NULL)
1046 		arena_run_split_small(arena, run, size, binind);
1047 	return (run);
1048 }
1049 
1050 static arena_run_t *
arena_run_alloc_small(arena_t * arena,size_t size,index_t binind)1051 arena_run_alloc_small(arena_t *arena, size_t size, index_t binind)
1052 {
1053 	arena_chunk_t *chunk;
1054 	arena_run_t *run;
1055 
1056 	assert(size <= arena_maxrun);
1057 	assert(size == PAGE_CEILING(size));
1058 	assert(binind != BININD_INVALID);
1059 
1060 	/* Search the arena's chunks for the lowest best fit. */
1061 	run = arena_run_alloc_small_helper(arena, size, binind);
1062 	if (run != NULL)
1063 		return (run);
1064 
1065 	/*
1066 	 * No usable runs.  Create a new chunk from which to allocate the run.
1067 	 */
1068 	chunk = arena_chunk_alloc(arena);
1069 	if (chunk != NULL) {
1070 		run = &arena_miscelm_get(chunk, map_bias)->run;
1071 		arena_run_split_small(arena, run, size, binind);
1072 		return (run);
1073 	}
1074 
1075 	/*
1076 	 * arena_chunk_alloc() failed, but another thread may have made
1077 	 * sufficient memory available while this one dropped arena->lock in
1078 	 * arena_chunk_alloc(), so search one more time.
1079 	 */
1080 	return (arena_run_alloc_small_helper(arena, size, binind));
1081 }
1082 
1083 static bool
arena_lg_dirty_mult_valid(ssize_t lg_dirty_mult)1084 arena_lg_dirty_mult_valid(ssize_t lg_dirty_mult)
1085 {
1086 
1087 	return (lg_dirty_mult >= -1 && lg_dirty_mult < (ssize_t)(sizeof(size_t)
1088 	    << 3));
1089 }
1090 
1091 ssize_t
arena_lg_dirty_mult_get(arena_t * arena)1092 arena_lg_dirty_mult_get(arena_t *arena)
1093 {
1094 	ssize_t lg_dirty_mult;
1095 
1096 	malloc_mutex_lock(&arena->lock);
1097 	lg_dirty_mult = arena->lg_dirty_mult;
1098 	malloc_mutex_unlock(&arena->lock);
1099 
1100 	return (lg_dirty_mult);
1101 }
1102 
1103 bool
arena_lg_dirty_mult_set(arena_t * arena,ssize_t lg_dirty_mult)1104 arena_lg_dirty_mult_set(arena_t *arena, ssize_t lg_dirty_mult)
1105 {
1106 
1107 	if (!arena_lg_dirty_mult_valid(lg_dirty_mult))
1108 		return (true);
1109 
1110 	malloc_mutex_lock(&arena->lock);
1111 	arena->lg_dirty_mult = lg_dirty_mult;
1112 	arena_maybe_purge(arena);
1113 	malloc_mutex_unlock(&arena->lock);
1114 
1115 	return (false);
1116 }
1117 
1118 void
arena_maybe_purge(arena_t * arena)1119 arena_maybe_purge(arena_t *arena)
1120 {
1121 #if !defined(ANDROID_ALWAYS_PURGE)
1122 	size_t threshold;
1123 
1124 	/* Don't purge if the option is disabled. */
1125 	if (arena->lg_dirty_mult < 0)
1126 		return;
1127 	threshold = (arena->nactive >> arena->lg_dirty_mult);
1128 	threshold = threshold < chunk_npages ? chunk_npages : threshold;
1129 	/*
1130 	 * Don't purge unless the number of purgeable pages exceeds the
1131 	 * threshold.
1132 	 */
1133 	if (arena->ndirty <= threshold)
1134 		return;
1135 #endif
1136 
1137 	arena_purge(arena, false);
1138 }
1139 
1140 static size_t
arena_dirty_count(arena_t * arena)1141 arena_dirty_count(arena_t *arena)
1142 {
1143 	size_t ndirty = 0;
1144 	arena_runs_dirty_link_t *rdelm;
1145 	extent_node_t *chunkselm;
1146 
1147 	for (rdelm = qr_next(&arena->runs_dirty, rd_link),
1148 	    chunkselm = qr_next(&arena->chunks_cache, cc_link);
1149 	    rdelm != &arena->runs_dirty; rdelm = qr_next(rdelm, rd_link)) {
1150 		size_t npages;
1151 
1152 		if (rdelm == &chunkselm->rd) {
1153 			npages = extent_node_size_get(chunkselm) >> LG_PAGE;
1154 			chunkselm = qr_next(chunkselm, cc_link);
1155 		} else {
1156 			arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(
1157 			    rdelm);
1158 			arena_chunk_map_misc_t *miscelm =
1159 			    arena_rd_to_miscelm(rdelm);
1160 			size_t pageind = arena_miscelm_to_pageind(miscelm);
1161 			assert(arena_mapbits_allocated_get(chunk, pageind) ==
1162 			    0);
1163 			assert(arena_mapbits_large_get(chunk, pageind) == 0);
1164 			assert(arena_mapbits_dirty_get(chunk, pageind) != 0);
1165 			npages = arena_mapbits_unallocated_size_get(chunk,
1166 			    pageind) >> LG_PAGE;
1167 		}
1168 		ndirty += npages;
1169 	}
1170 
1171 	return (ndirty);
1172 }
1173 
1174 static size_t
arena_compute_npurge(arena_t * arena,bool all)1175 arena_compute_npurge(arena_t *arena, bool all)
1176 {
1177 	size_t npurge;
1178 
1179 	/*
1180 	 * Compute the minimum number of pages that this thread should try to
1181 	 * purge.
1182 	 */
1183 	if (!all) {
1184 		size_t threshold = (arena->nactive >> arena->lg_dirty_mult);
1185 		threshold = threshold < chunk_npages ? chunk_npages : threshold;
1186 
1187 		npurge = arena->ndirty - threshold;
1188 	} else
1189 		npurge = arena->ndirty;
1190 
1191 	return (npurge);
1192 }
1193 
1194 static size_t
arena_stash_dirty(arena_t * arena,bool all,size_t npurge,arena_runs_dirty_link_t * purge_runs_sentinel,extent_node_t * purge_chunks_sentinel)1195 arena_stash_dirty(arena_t *arena, bool all, size_t npurge,
1196     arena_runs_dirty_link_t *purge_runs_sentinel,
1197     extent_node_t *purge_chunks_sentinel)
1198 {
1199 	arena_runs_dirty_link_t *rdelm, *rdelm_next;
1200 	extent_node_t *chunkselm;
1201 	size_t nstashed = 0;
1202 
1203 	/* Stash at least npurge pages. */
1204 	for (rdelm = qr_next(&arena->runs_dirty, rd_link),
1205 	    chunkselm = qr_next(&arena->chunks_cache, cc_link);
1206 	    rdelm != &arena->runs_dirty; rdelm = rdelm_next) {
1207 		size_t npages;
1208 		rdelm_next = qr_next(rdelm, rd_link);
1209 
1210 		if (rdelm == &chunkselm->rd) {
1211 			extent_node_t *chunkselm_next;
1212 			bool zero;
1213 			UNUSED void *chunk;
1214 
1215 			chunkselm_next = qr_next(chunkselm, cc_link);
1216 			/*
1217 			 * Allocate.  chunkselm remains valid due to the
1218 			 * dalloc_node=false argument to chunk_alloc_cache().
1219 			 */
1220 			zero = false;
1221 			chunk = chunk_alloc_cache(arena,
1222 			    extent_node_addr_get(chunkselm),
1223 			    extent_node_size_get(chunkselm), chunksize, &zero,
1224 			    false);
1225 			assert(chunk == extent_node_addr_get(chunkselm));
1226 			assert(zero == extent_node_zeroed_get(chunkselm));
1227 			extent_node_dirty_insert(chunkselm, purge_runs_sentinel,
1228 			    purge_chunks_sentinel);
1229 			npages = extent_node_size_get(chunkselm) >> LG_PAGE;
1230 			chunkselm = chunkselm_next;
1231 		} else {
1232 			arena_chunk_t *chunk =
1233 			    (arena_chunk_t *)CHUNK_ADDR2BASE(rdelm);
1234 			arena_chunk_map_misc_t *miscelm =
1235 			    arena_rd_to_miscelm(rdelm);
1236 			size_t pageind = arena_miscelm_to_pageind(miscelm);
1237 			arena_run_t *run = &miscelm->run;
1238 			size_t run_size =
1239 			    arena_mapbits_unallocated_size_get(chunk, pageind);
1240 
1241 			npages = run_size >> LG_PAGE;
1242 
1243 			assert(pageind + npages <= chunk_npages);
1244 			assert(arena_mapbits_dirty_get(chunk, pageind) ==
1245 			    arena_mapbits_dirty_get(chunk, pageind+npages-1));
1246 
1247 			/*
1248 			 * If purging the spare chunk's run, make it available
1249 			 * prior to allocation.
1250 			 */
1251 			if (chunk == arena->spare)
1252 				arena_chunk_alloc(arena);
1253 
1254 			/* Temporarily allocate the free dirty run. */
1255 			arena_run_split_large(arena, run, run_size, false);
1256 			/* Stash. */
1257 			if (false)
1258 				qr_new(rdelm, rd_link); /* Redundant. */
1259 			else {
1260 				assert(qr_next(rdelm, rd_link) == rdelm);
1261 				assert(qr_prev(rdelm, rd_link) == rdelm);
1262 			}
1263 			qr_meld(purge_runs_sentinel, rdelm, rd_link);
1264 		}
1265 
1266 		nstashed += npages;
1267 		if (!all && nstashed >= npurge)
1268 			break;
1269 	}
1270 
1271 	return (nstashed);
1272 }
1273 
1274 static size_t
arena_purge_stashed(arena_t * arena,arena_runs_dirty_link_t * purge_runs_sentinel,extent_node_t * purge_chunks_sentinel)1275 arena_purge_stashed(arena_t *arena,
1276     arena_runs_dirty_link_t *purge_runs_sentinel,
1277     extent_node_t *purge_chunks_sentinel)
1278 {
1279 	size_t npurged, nmadvise;
1280 	chunk_purge_t *chunk_purge;
1281 	arena_runs_dirty_link_t *rdelm;
1282 	extent_node_t *chunkselm;
1283 
1284 	if (config_stats)
1285 		nmadvise = 0;
1286 	npurged = 0;
1287 
1288 	chunk_purge = arena->chunk_purge;
1289 	malloc_mutex_unlock(&arena->lock);
1290 	for (rdelm = qr_next(purge_runs_sentinel, rd_link),
1291 	    chunkselm = qr_next(purge_chunks_sentinel, cc_link);
1292 	    rdelm != purge_runs_sentinel; rdelm = qr_next(rdelm, rd_link)) {
1293 		size_t npages;
1294 
1295 		if (rdelm == &chunkselm->rd) {
1296 			size_t size = extent_node_size_get(chunkselm);
1297 			bool unzeroed;
1298 
1299 			npages = size >> LG_PAGE;
1300 			unzeroed = chunk_purge_wrapper(arena, chunk_purge,
1301 			    extent_node_addr_get(chunkselm), 0, size);
1302 			extent_node_zeroed_set(chunkselm, !unzeroed);
1303 			chunkselm = qr_next(chunkselm, cc_link);
1304 		} else {
1305 			size_t pageind, run_size, flag_unzeroed, i;
1306 			bool unzeroed;
1307 			arena_chunk_t *chunk = (arena_chunk_t
1308 			    *)CHUNK_ADDR2BASE(rdelm);
1309 			arena_chunk_map_misc_t *miscelm =
1310 			    arena_rd_to_miscelm(rdelm);
1311 			pageind = arena_miscelm_to_pageind(miscelm);
1312 			run_size = arena_mapbits_large_size_get(chunk, pageind);
1313 			npages = run_size >> LG_PAGE;
1314 
1315 			assert(pageind + npages <= chunk_npages);
1316 			unzeroed = chunk_purge_wrapper(arena, chunk_purge,
1317 			    chunk, pageind << LG_PAGE, run_size);
1318 			flag_unzeroed = unzeroed ? CHUNK_MAP_UNZEROED : 0;
1319 
1320 			/*
1321 			 * Set the unzeroed flag for all pages, now that
1322 			 * chunk_purge_wrapper() has returned whether the pages
1323 			 * were zeroed as a side effect of purging.  This chunk
1324 			 * map modification is safe even though the arena mutex
1325 			 * isn't currently owned by this thread, because the run
1326 			 * is marked as allocated, thus protecting it from being
1327 			 * modified by any other thread.  As long as these
1328 			 * writes don't perturb the first and last elements'
1329 			 * CHUNK_MAP_ALLOCATED bits, behavior is well defined.
1330 			 */
1331 			for (i = 0; i < npages; i++) {
1332 				arena_mapbits_unzeroed_set(chunk, pageind+i,
1333 				    flag_unzeroed);
1334 			}
1335 		}
1336 
1337 		npurged += npages;
1338 		if (config_stats)
1339 			nmadvise++;
1340 	}
1341 	malloc_mutex_lock(&arena->lock);
1342 
1343 	if (config_stats) {
1344 		arena->stats.nmadvise += nmadvise;
1345 		arena->stats.purged += npurged;
1346 	}
1347 
1348 	return (npurged);
1349 }
1350 
1351 static void
arena_unstash_purged(arena_t * arena,arena_runs_dirty_link_t * purge_runs_sentinel,extent_node_t * purge_chunks_sentinel)1352 arena_unstash_purged(arena_t *arena,
1353     arena_runs_dirty_link_t *purge_runs_sentinel,
1354     extent_node_t *purge_chunks_sentinel)
1355 {
1356 	arena_runs_dirty_link_t *rdelm, *rdelm_next;
1357 	extent_node_t *chunkselm;
1358 
1359 	/* Deallocate runs. */
1360 	for (rdelm = qr_next(purge_runs_sentinel, rd_link),
1361 	    chunkselm = qr_next(purge_chunks_sentinel, cc_link);
1362 	    rdelm != purge_runs_sentinel; rdelm = rdelm_next) {
1363 		rdelm_next = qr_next(rdelm, rd_link);
1364 		if (rdelm == &chunkselm->rd) {
1365 			extent_node_t *chunkselm_next = qr_next(chunkselm,
1366 			    cc_link);
1367 			void *addr = extent_node_addr_get(chunkselm);
1368 			size_t size = extent_node_size_get(chunkselm);
1369 			bool zeroed = extent_node_zeroed_get(chunkselm);
1370 			extent_node_dirty_remove(chunkselm);
1371 			arena_node_dalloc(arena, chunkselm);
1372 			chunkselm = chunkselm_next;
1373 			chunk_dalloc_arena(arena, addr, size, zeroed);
1374 		} else {
1375 			arena_chunk_map_misc_t *miscelm =
1376 			    arena_rd_to_miscelm(rdelm);
1377 			arena_run_t *run = &miscelm->run;
1378 			qr_remove(rdelm, rd_link);
1379 			arena_run_dalloc(arena, run, false, true);
1380 		}
1381 	}
1382 }
1383 
1384 static void
arena_purge(arena_t * arena,bool all)1385 arena_purge(arena_t *arena, bool all)
1386 {
1387 	size_t npurge, npurgeable, npurged;
1388 	arena_runs_dirty_link_t purge_runs_sentinel;
1389 	extent_node_t purge_chunks_sentinel;
1390 
1391 	/*
1392 	 * Calls to arena_dirty_count() are disabled even for debug builds
1393 	 * because overhead grows nonlinearly as memory usage increases.
1394 	 */
1395 	if (false && config_debug) {
1396 		size_t ndirty = arena_dirty_count(arena);
1397 		assert(ndirty == arena->ndirty);
1398 	}
1399 	assert((arena->nactive >> arena->lg_dirty_mult) < arena->ndirty || all);
1400 
1401 	if (config_stats)
1402 		arena->stats.npurge++;
1403 
1404 	npurge = arena_compute_npurge(arena, all);
1405 	qr_new(&purge_runs_sentinel, rd_link);
1406 	extent_node_dirty_linkage_init(&purge_chunks_sentinel);
1407 
1408 	npurgeable = arena_stash_dirty(arena, all, npurge, &purge_runs_sentinel,
1409 	    &purge_chunks_sentinel);
1410 	assert(npurgeable >= npurge);
1411 	npurged = arena_purge_stashed(arena, &purge_runs_sentinel,
1412 	    &purge_chunks_sentinel);
1413 	assert(npurged == npurgeable);
1414 	arena_unstash_purged(arena, &purge_runs_sentinel,
1415 	    &purge_chunks_sentinel);
1416 }
1417 
1418 void
arena_purge_all(arena_t * arena)1419 arena_purge_all(arena_t *arena)
1420 {
1421 
1422 	malloc_mutex_lock(&arena->lock);
1423 	arena_purge(arena, true);
1424 	malloc_mutex_unlock(&arena->lock);
1425 }
1426 
1427 static void
arena_run_coalesce(arena_t * arena,arena_chunk_t * chunk,size_t * p_size,size_t * p_run_ind,size_t * p_run_pages,size_t flag_dirty)1428 arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size,
1429     size_t *p_run_ind, size_t *p_run_pages, size_t flag_dirty)
1430 {
1431 	size_t size = *p_size;
1432 	size_t run_ind = *p_run_ind;
1433 	size_t run_pages = *p_run_pages;
1434 
1435 	/* Try to coalesce forward. */
1436 	if (run_ind + run_pages < chunk_npages &&
1437 	    arena_mapbits_allocated_get(chunk, run_ind+run_pages) == 0 &&
1438 	    arena_mapbits_dirty_get(chunk, run_ind+run_pages) == flag_dirty) {
1439 		size_t nrun_size = arena_mapbits_unallocated_size_get(chunk,
1440 		    run_ind+run_pages);
1441 		size_t nrun_pages = nrun_size >> LG_PAGE;
1442 
1443 		/*
1444 		 * Remove successor from runs_avail; the coalesced run is
1445 		 * inserted later.
1446 		 */
1447 		assert(arena_mapbits_unallocated_size_get(chunk,
1448 		    run_ind+run_pages+nrun_pages-1) == nrun_size);
1449 		assert(arena_mapbits_dirty_get(chunk,
1450 		    run_ind+run_pages+nrun_pages-1) == flag_dirty);
1451 		arena_avail_remove(arena, chunk, run_ind+run_pages, nrun_pages);
1452 
1453 		/*
1454 		 * If the successor is dirty, remove it from the set of dirty
1455 		 * pages.
1456 		 */
1457 		if (flag_dirty != 0) {
1458 			arena_run_dirty_remove(arena, chunk, run_ind+run_pages,
1459 			    nrun_pages);
1460 		}
1461 
1462 		size += nrun_size;
1463 		run_pages += nrun_pages;
1464 
1465 		arena_mapbits_unallocated_size_set(chunk, run_ind, size);
1466 		arena_mapbits_unallocated_size_set(chunk, run_ind+run_pages-1,
1467 		    size);
1468 	}
1469 
1470 	/* Try to coalesce backward. */
1471 	if (run_ind > map_bias && arena_mapbits_allocated_get(chunk,
1472 	    run_ind-1) == 0 && arena_mapbits_dirty_get(chunk, run_ind-1) ==
1473 	    flag_dirty) {
1474 		size_t prun_size = arena_mapbits_unallocated_size_get(chunk,
1475 		    run_ind-1);
1476 		size_t prun_pages = prun_size >> LG_PAGE;
1477 
1478 		run_ind -= prun_pages;
1479 
1480 		/*
1481 		 * Remove predecessor from runs_avail; the coalesced run is
1482 		 * inserted later.
1483 		 */
1484 		assert(arena_mapbits_unallocated_size_get(chunk, run_ind) ==
1485 		    prun_size);
1486 		assert(arena_mapbits_dirty_get(chunk, run_ind) == flag_dirty);
1487 		arena_avail_remove(arena, chunk, run_ind, prun_pages);
1488 
1489 		/*
1490 		 * If the predecessor is dirty, remove it from the set of dirty
1491 		 * pages.
1492 		 */
1493 		if (flag_dirty != 0) {
1494 			arena_run_dirty_remove(arena, chunk, run_ind,
1495 			    prun_pages);
1496 		}
1497 
1498 		size += prun_size;
1499 		run_pages += prun_pages;
1500 
1501 		arena_mapbits_unallocated_size_set(chunk, run_ind, size);
1502 		arena_mapbits_unallocated_size_set(chunk, run_ind+run_pages-1,
1503 		    size);
1504 	}
1505 
1506 	*p_size = size;
1507 	*p_run_ind = run_ind;
1508 	*p_run_pages = run_pages;
1509 }
1510 
1511 static void
arena_run_dalloc(arena_t * arena,arena_run_t * run,bool dirty,bool cleaned)1512 arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned)
1513 {
1514 	arena_chunk_t *chunk;
1515 	arena_chunk_map_misc_t *miscelm;
1516 	size_t size, run_ind, run_pages, flag_dirty;
1517 
1518 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
1519 	miscelm = arena_run_to_miscelm(run);
1520 	run_ind = arena_miscelm_to_pageind(miscelm);
1521 	assert(run_ind >= map_bias);
1522 	assert(run_ind < chunk_npages);
1523 	if (arena_mapbits_large_get(chunk, run_ind) != 0) {
1524 		size = arena_mapbits_large_size_get(chunk, run_ind);
1525 		assert(size == PAGE ||
1526 		    arena_mapbits_large_size_get(chunk,
1527 		    run_ind+(size>>LG_PAGE)-1) == 0);
1528 	} else {
1529 		arena_bin_info_t *bin_info = &arena_bin_info[run->binind];
1530 		size = bin_info->run_size;
1531 	}
1532 	run_pages = (size >> LG_PAGE);
1533 	arena_cactive_update(arena, 0, run_pages);
1534 	arena->nactive -= run_pages;
1535 
1536 	/*
1537 	 * The run is dirty if the caller claims to have dirtied it, as well as
1538 	 * if it was already dirty before being allocated and the caller
1539 	 * doesn't claim to have cleaned it.
1540 	 */
1541 	assert(arena_mapbits_dirty_get(chunk, run_ind) ==
1542 	    arena_mapbits_dirty_get(chunk, run_ind+run_pages-1));
1543 	if (!cleaned && arena_mapbits_dirty_get(chunk, run_ind) != 0)
1544 		dirty = true;
1545 	flag_dirty = dirty ? CHUNK_MAP_DIRTY : 0;
1546 
1547 	/* Mark pages as unallocated in the chunk map. */
1548 	if (dirty) {
1549 		arena_mapbits_unallocated_set(chunk, run_ind, size,
1550 		    CHUNK_MAP_DIRTY);
1551 		arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size,
1552 		    CHUNK_MAP_DIRTY);
1553 	} else {
1554 		arena_mapbits_unallocated_set(chunk, run_ind, size,
1555 		    arena_mapbits_unzeroed_get(chunk, run_ind));
1556 		arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size,
1557 		    arena_mapbits_unzeroed_get(chunk, run_ind+run_pages-1));
1558 	}
1559 
1560 	arena_run_coalesce(arena, chunk, &size, &run_ind, &run_pages, flag_dirty);
1561 
1562 	/* Insert into runs_avail, now that coalescing is complete. */
1563 	assert(arena_mapbits_unallocated_size_get(chunk, run_ind) ==
1564 	    arena_mapbits_unallocated_size_get(chunk, run_ind+run_pages-1));
1565 	assert(arena_mapbits_dirty_get(chunk, run_ind) ==
1566 	    arena_mapbits_dirty_get(chunk, run_ind+run_pages-1));
1567 	arena_avail_insert(arena, chunk, run_ind, run_pages);
1568 
1569 	if (dirty)
1570 		arena_run_dirty_insert(arena, chunk, run_ind, run_pages);
1571 
1572 	/* Deallocate chunk if it is now completely unused. */
1573 	if (size == arena_maxrun) {
1574 		assert(run_ind == map_bias);
1575 		assert(run_pages == (arena_maxrun >> LG_PAGE));
1576 		arena_chunk_dalloc(arena, chunk);
1577 	}
1578 
1579 	/*
1580 	 * It is okay to do dirty page processing here even if the chunk was
1581 	 * deallocated above, since in that case it is the spare.  Waiting
1582 	 * until after possible chunk deallocation to do dirty processing
1583 	 * allows for an old spare to be fully deallocated, thus decreasing the
1584 	 * chances of spuriously crossing the dirty page purging threshold.
1585 	 */
1586 	if (dirty)
1587 		arena_maybe_purge(arena);
1588 }
1589 
1590 static void
arena_run_trim_head(arena_t * arena,arena_chunk_t * chunk,arena_run_t * run,size_t oldsize,size_t newsize)1591 arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
1592     size_t oldsize, size_t newsize)
1593 {
1594 	arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run);
1595 	size_t pageind = arena_miscelm_to_pageind(miscelm);
1596 	size_t head_npages = (oldsize - newsize) >> LG_PAGE;
1597 	size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind);
1598 
1599 	assert(oldsize > newsize);
1600 
1601 	/*
1602 	 * Update the chunk map so that arena_run_dalloc() can treat the
1603 	 * leading run as separately allocated.  Set the last element of each
1604 	 * run first, in case of single-page runs.
1605 	 */
1606 	assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize);
1607 	arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty);
1608 	arena_mapbits_large_set(chunk, pageind, oldsize-newsize, flag_dirty);
1609 
1610 	if (config_debug) {
1611 		UNUSED size_t tail_npages = newsize >> LG_PAGE;
1612 		assert(arena_mapbits_large_size_get(chunk,
1613 		    pageind+head_npages+tail_npages-1) == 0);
1614 		assert(arena_mapbits_dirty_get(chunk,
1615 		    pageind+head_npages+tail_npages-1) == flag_dirty);
1616 	}
1617 	arena_mapbits_large_set(chunk, pageind+head_npages, newsize,
1618 	    flag_dirty);
1619 
1620 	arena_run_dalloc(arena, run, false, false);
1621 }
1622 
1623 static void
arena_run_trim_tail(arena_t * arena,arena_chunk_t * chunk,arena_run_t * run,size_t oldsize,size_t newsize,bool dirty)1624 arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
1625     size_t oldsize, size_t newsize, bool dirty)
1626 {
1627 	arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run);
1628 	size_t pageind = arena_miscelm_to_pageind(miscelm);
1629 	size_t head_npages = newsize >> LG_PAGE;
1630 	size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind);
1631 	arena_chunk_map_misc_t *tail_miscelm;
1632 	arena_run_t *tail_run;
1633 
1634 	assert(oldsize > newsize);
1635 
1636 	/*
1637 	 * Update the chunk map so that arena_run_dalloc() can treat the
1638 	 * trailing run as separately allocated.  Set the last element of each
1639 	 * run first, in case of single-page runs.
1640 	 */
1641 	assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize);
1642 	arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty);
1643 	arena_mapbits_large_set(chunk, pageind, newsize, flag_dirty);
1644 
1645 	if (config_debug) {
1646 		UNUSED size_t tail_npages = (oldsize - newsize) >> LG_PAGE;
1647 		assert(arena_mapbits_large_size_get(chunk,
1648 		    pageind+head_npages+tail_npages-1) == 0);
1649 		assert(arena_mapbits_dirty_get(chunk,
1650 		    pageind+head_npages+tail_npages-1) == flag_dirty);
1651 	}
1652 	arena_mapbits_large_set(chunk, pageind+head_npages, oldsize-newsize,
1653 	    flag_dirty);
1654 
1655 	tail_miscelm = arena_miscelm_get(chunk, pageind + head_npages);
1656 	tail_run = &tail_miscelm->run;
1657 	arena_run_dalloc(arena, tail_run, dirty, false);
1658 }
1659 
1660 static arena_run_t *
arena_bin_runs_first(arena_bin_t * bin)1661 arena_bin_runs_first(arena_bin_t *bin)
1662 {
1663 	arena_chunk_map_misc_t *miscelm = arena_run_tree_first(&bin->runs);
1664 	if (miscelm != NULL)
1665 		return (&miscelm->run);
1666 
1667 	return (NULL);
1668 }
1669 
1670 static void
arena_bin_runs_insert(arena_bin_t * bin,arena_run_t * run)1671 arena_bin_runs_insert(arena_bin_t *bin, arena_run_t *run)
1672 {
1673 	arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run);
1674 
1675 	assert(arena_run_tree_search(&bin->runs, miscelm) == NULL);
1676 
1677 	arena_run_tree_insert(&bin->runs, miscelm);
1678 }
1679 
1680 static void
arena_bin_runs_remove(arena_bin_t * bin,arena_run_t * run)1681 arena_bin_runs_remove(arena_bin_t *bin, arena_run_t *run)
1682 {
1683 	arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run);
1684 
1685 	assert(arena_run_tree_search(&bin->runs, miscelm) != NULL);
1686 
1687 	arena_run_tree_remove(&bin->runs, miscelm);
1688 }
1689 
1690 static arena_run_t *
arena_bin_nonfull_run_tryget(arena_bin_t * bin)1691 arena_bin_nonfull_run_tryget(arena_bin_t *bin)
1692 {
1693 	arena_run_t *run = arena_bin_runs_first(bin);
1694 	if (run != NULL) {
1695 		arena_bin_runs_remove(bin, run);
1696 		if (config_stats)
1697 			bin->stats.reruns++;
1698 	}
1699 	return (run);
1700 }
1701 
1702 static arena_run_t *
arena_bin_nonfull_run_get(arena_t * arena,arena_bin_t * bin)1703 arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)
1704 {
1705 	arena_run_t *run;
1706 	index_t binind;
1707 	arena_bin_info_t *bin_info;
1708 
1709 	/* Look for a usable run. */
1710 	run = arena_bin_nonfull_run_tryget(bin);
1711 	if (run != NULL)
1712 		return (run);
1713 	/* No existing runs have any space available. */
1714 
1715 	binind = arena_bin_index(arena, bin);
1716 	bin_info = &arena_bin_info[binind];
1717 
1718 	/* Allocate a new run. */
1719 	malloc_mutex_unlock(&bin->lock);
1720 	/******************************/
1721 	malloc_mutex_lock(&arena->lock);
1722 	run = arena_run_alloc_small(arena, bin_info->run_size, binind);
1723 	if (run != NULL) {
1724 		/* Initialize run internals. */
1725 		run->binind = binind;
1726 		run->nfree = bin_info->nregs;
1727 		bitmap_init(run->bitmap, &bin_info->bitmap_info);
1728 	}
1729 	malloc_mutex_unlock(&arena->lock);
1730 	/********************************/
1731 	malloc_mutex_lock(&bin->lock);
1732 	if (run != NULL) {
1733 		if (config_stats) {
1734 			bin->stats.nruns++;
1735 			bin->stats.curruns++;
1736 		}
1737 		return (run);
1738 	}
1739 
1740 	/*
1741 	 * arena_run_alloc_small() failed, but another thread may have made
1742 	 * sufficient memory available while this one dropped bin->lock above,
1743 	 * so search one more time.
1744 	 */
1745 	run = arena_bin_nonfull_run_tryget(bin);
1746 	if (run != NULL)
1747 		return (run);
1748 
1749 	return (NULL);
1750 }
1751 
1752 /* Re-fill bin->runcur, then call arena_run_reg_alloc(). */
1753 static void *
arena_bin_malloc_hard(arena_t * arena,arena_bin_t * bin)1754 arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin)
1755 {
1756 	void *ret;
1757 	index_t binind;
1758 	arena_bin_info_t *bin_info;
1759 	arena_run_t *run;
1760 
1761 	binind = arena_bin_index(arena, bin);
1762 	bin_info = &arena_bin_info[binind];
1763 	bin->runcur = NULL;
1764 	run = arena_bin_nonfull_run_get(arena, bin);
1765 	if (bin->runcur != NULL && bin->runcur->nfree > 0) {
1766 		/*
1767 		 * Another thread updated runcur while this one ran without the
1768 		 * bin lock in arena_bin_nonfull_run_get().
1769 		 */
1770 		assert(bin->runcur->nfree > 0);
1771 		ret = arena_run_reg_alloc(bin->runcur, bin_info);
1772 		if (run != NULL) {
1773 			arena_chunk_t *chunk;
1774 
1775 			/*
1776 			 * arena_run_alloc_small() may have allocated run, or
1777 			 * it may have pulled run from the bin's run tree.
1778 			 * Therefore it is unsafe to make any assumptions about
1779 			 * how run has previously been used, and
1780 			 * arena_bin_lower_run() must be called, as if a region
1781 			 * were just deallocated from the run.
1782 			 */
1783 			chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
1784 			if (run->nfree == bin_info->nregs)
1785 				arena_dalloc_bin_run(arena, chunk, run, bin);
1786 			else
1787 				arena_bin_lower_run(arena, chunk, run, bin);
1788 		}
1789 		return (ret);
1790 	}
1791 
1792 	if (run == NULL)
1793 		return (NULL);
1794 
1795 	bin->runcur = run;
1796 
1797 	assert(bin->runcur->nfree > 0);
1798 
1799 	return (arena_run_reg_alloc(bin->runcur, bin_info));
1800 }
1801 
1802 void
arena_tcache_fill_small(arena_t * arena,tcache_bin_t * tbin,index_t binind,uint64_t prof_accumbytes)1803 arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, index_t binind,
1804     uint64_t prof_accumbytes)
1805 {
1806 	unsigned i, nfill;
1807 	arena_bin_t *bin;
1808 	arena_run_t *run;
1809 	void *ptr;
1810 
1811 	assert(tbin->ncached == 0);
1812 
1813 	if (config_prof && arena_prof_accum(arena, prof_accumbytes))
1814 		prof_idump();
1815 	bin = &arena->bins[binind];
1816 	malloc_mutex_lock(&bin->lock);
1817 	for (i = 0, nfill = (tcache_bin_info[binind].ncached_max >>
1818 	    tbin->lg_fill_div); i < nfill; i++) {
1819 		if ((run = bin->runcur) != NULL && run->nfree > 0)
1820 			ptr = arena_run_reg_alloc(run, &arena_bin_info[binind]);
1821 		else
1822 			ptr = arena_bin_malloc_hard(arena, bin);
1823 		if (ptr == NULL) {
1824 			/*
1825 			 * OOM.  tbin->avail isn't yet filled down to its first
1826 			 * element, so the successful allocations (if any) must
1827 			 * be moved to the base of tbin->avail before bailing
1828 			 * out.
1829 			 */
1830 			if (i > 0) {
1831 				memmove(tbin->avail, &tbin->avail[nfill - i],
1832 				    i * sizeof(void *));
1833 			}
1834 			break;
1835 		}
1836 		if (config_fill && unlikely(opt_junk_alloc)) {
1837 			arena_alloc_junk_small(ptr, &arena_bin_info[binind],
1838 			    true);
1839 		}
1840 		/* Insert such that low regions get used first. */
1841 		tbin->avail[nfill - 1 - i] = ptr;
1842 	}
1843 	if (config_stats) {
1844 		bin->stats.nmalloc += i;
1845 		bin->stats.nrequests += tbin->tstats.nrequests;
1846 		bin->stats.curregs += i;
1847 		bin->stats.nfills++;
1848 		tbin->tstats.nrequests = 0;
1849 	}
1850 	malloc_mutex_unlock(&bin->lock);
1851 	tbin->ncached = i;
1852 }
1853 
1854 void
arena_alloc_junk_small(void * ptr,arena_bin_info_t * bin_info,bool zero)1855 arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info, bool zero)
1856 {
1857 
1858 	if (zero) {
1859 		size_t redzone_size = bin_info->redzone_size;
1860 		memset((void *)((uintptr_t)ptr - redzone_size), 0xa5,
1861 		    redzone_size);
1862 		memset((void *)((uintptr_t)ptr + bin_info->reg_size), 0xa5,
1863 		    redzone_size);
1864 	} else {
1865 		memset((void *)((uintptr_t)ptr - bin_info->redzone_size), 0xa5,
1866 		    bin_info->reg_interval);
1867 	}
1868 }
1869 
1870 #ifdef JEMALLOC_JET
1871 #undef arena_redzone_corruption
1872 #define	arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption_impl)
1873 #endif
1874 static void
arena_redzone_corruption(void * ptr,size_t usize,bool after,size_t offset,uint8_t byte)1875 arena_redzone_corruption(void *ptr, size_t usize, bool after,
1876     size_t offset, uint8_t byte)
1877 {
1878 
1879 	malloc_printf("<jemalloc>: Corrupt redzone %zu byte%s %s %p "
1880 	    "(size %zu), byte=%#x\n", offset, (offset == 1) ? "" : "s",
1881 	    after ? "after" : "before", ptr, usize, byte);
1882 }
1883 #ifdef JEMALLOC_JET
1884 #undef arena_redzone_corruption
1885 #define	arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption)
1886 arena_redzone_corruption_t *arena_redzone_corruption =
1887     JEMALLOC_N(arena_redzone_corruption_impl);
1888 #endif
1889 
1890 static void
arena_redzones_validate(void * ptr,arena_bin_info_t * bin_info,bool reset)1891 arena_redzones_validate(void *ptr, arena_bin_info_t *bin_info, bool reset)
1892 {
1893 	size_t size = bin_info->reg_size;
1894 	size_t redzone_size = bin_info->redzone_size;
1895 	size_t i;
1896 	bool error = false;
1897 
1898 	if (opt_junk_alloc) {
1899 		for (i = 1; i <= redzone_size; i++) {
1900 			uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i);
1901 			if (*byte != 0xa5) {
1902 				error = true;
1903 				arena_redzone_corruption(ptr, size, false, i, *byte);
1904 				if (reset)
1905 					*byte = 0xa5;
1906 			}
1907 		}
1908 		for (i = 0; i < redzone_size; i++) {
1909 			uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i);
1910 			if (*byte != 0xa5) {
1911 				error = true;
1912 				arena_redzone_corruption(ptr, size, true, i, *byte);
1913 				if (reset)
1914 					*byte = 0xa5;
1915 			}
1916 		}
1917 	}
1918 
1919 	if (opt_abort && error)
1920 		abort();
1921 }
1922 
1923 #ifdef JEMALLOC_JET
1924 #undef arena_dalloc_junk_small
1925 #define	arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small_impl)
1926 #endif
1927 void
arena_dalloc_junk_small(void * ptr,arena_bin_info_t * bin_info)1928 arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info)
1929 {
1930 	size_t redzone_size = bin_info->redzone_size;
1931 
1932 	arena_redzones_validate(ptr, bin_info, false);
1933 	memset((void *)((uintptr_t)ptr - redzone_size), 0x5a,
1934 	    bin_info->reg_interval);
1935 }
1936 #ifdef JEMALLOC_JET
1937 #undef arena_dalloc_junk_small
1938 #define	arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small)
1939 arena_dalloc_junk_small_t *arena_dalloc_junk_small =
1940     JEMALLOC_N(arena_dalloc_junk_small_impl);
1941 #endif
1942 
1943 void
arena_quarantine_junk_small(void * ptr,size_t usize)1944 arena_quarantine_junk_small(void *ptr, size_t usize)
1945 {
1946 	index_t binind;
1947 	arena_bin_info_t *bin_info;
1948 	cassert(config_fill);
1949 	assert(opt_junk_free);
1950 	assert(opt_quarantine);
1951 	assert(usize <= SMALL_MAXCLASS);
1952 
1953 	binind = size2index(usize);
1954 	bin_info = &arena_bin_info[binind];
1955 	arena_redzones_validate(ptr, bin_info, true);
1956 }
1957 
1958 void *
arena_malloc_small(arena_t * arena,size_t size,bool zero)1959 arena_malloc_small(arena_t *arena, size_t size, bool zero)
1960 {
1961 	void *ret;
1962 	arena_bin_t *bin;
1963 	arena_run_t *run;
1964 	index_t binind;
1965 
1966 	binind = size2index(size);
1967 	assert(binind < NBINS);
1968 	bin = &arena->bins[binind];
1969 	size = index2size(binind);
1970 
1971 	malloc_mutex_lock(&bin->lock);
1972 	if ((run = bin->runcur) != NULL && run->nfree > 0)
1973 		ret = arena_run_reg_alloc(run, &arena_bin_info[binind]);
1974 	else
1975 		ret = arena_bin_malloc_hard(arena, bin);
1976 
1977 	if (ret == NULL) {
1978 		malloc_mutex_unlock(&bin->lock);
1979 		return (NULL);
1980 	}
1981 
1982 	if (config_stats) {
1983 		bin->stats.nmalloc++;
1984 		bin->stats.nrequests++;
1985 		bin->stats.curregs++;
1986 	}
1987 	malloc_mutex_unlock(&bin->lock);
1988 	if (config_prof && !isthreaded && arena_prof_accum(arena, size))
1989 		prof_idump();
1990 
1991 	if (!zero) {
1992 		if (config_fill) {
1993 			if (unlikely(opt_junk_alloc)) {
1994 				arena_alloc_junk_small(ret,
1995 				    &arena_bin_info[binind], false);
1996 			} else if (unlikely(opt_zero))
1997 				memset(ret, 0, size);
1998 		}
1999 		JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
2000 	} else {
2001 		if (config_fill && unlikely(opt_junk_alloc)) {
2002 			arena_alloc_junk_small(ret, &arena_bin_info[binind],
2003 			    true);
2004 		}
2005 		JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
2006 		memset(ret, 0, size);
2007 	}
2008 
2009 	return (ret);
2010 }
2011 
2012 void *
arena_malloc_large(arena_t * arena,size_t size,bool zero)2013 arena_malloc_large(arena_t *arena, size_t size, bool zero)
2014 {
2015 	void *ret;
2016 	size_t usize;
2017 	uint64_t r;
2018 	uintptr_t random_offset;
2019 	arena_run_t *run;
2020 	arena_chunk_map_misc_t *miscelm;
2021 	UNUSED bool idump;
2022 
2023 	/* Large allocation. */
2024 	usize = s2u(size);
2025 	malloc_mutex_lock(&arena->lock);
2026 	if (config_cache_oblivious) {
2027 		/*
2028 		 * Compute a uniformly distributed offset within the first page
2029 		 * that is a multiple of the cacheline size, e.g. [0 .. 63) * 64
2030 		 * for 4 KiB pages and 64-byte cachelines.
2031 		 */
2032 		prng64(r, LG_PAGE - LG_CACHELINE, arena->offset_state,
2033 		    UINT64_C(6364136223846793009), UINT64_C(1442695040888963409));
2034 		random_offset = ((uintptr_t)r) << LG_CACHELINE;
2035 	} else
2036 		random_offset = 0;
2037 	run = arena_run_alloc_large(arena, usize + large_pad, zero);
2038 	if (run == NULL) {
2039 		malloc_mutex_unlock(&arena->lock);
2040 		return (NULL);
2041 	}
2042 	miscelm = arena_run_to_miscelm(run);
2043 	ret = (void *)((uintptr_t)arena_miscelm_to_rpages(miscelm) +
2044 	    random_offset);
2045 	if (config_stats) {
2046 		index_t index = size2index(usize) - NBINS;
2047 
2048 		arena->stats.nmalloc_large++;
2049 		arena->stats.nrequests_large++;
2050 		arena->stats.allocated_large += usize;
2051 		arena->stats.lstats[index].nmalloc++;
2052 		arena->stats.lstats[index].nrequests++;
2053 		arena->stats.lstats[index].curruns++;
2054 	}
2055 	if (config_prof)
2056 		idump = arena_prof_accum_locked(arena, usize);
2057 	malloc_mutex_unlock(&arena->lock);
2058 	if (config_prof && idump)
2059 		prof_idump();
2060 
2061 	if (!zero) {
2062 		if (config_fill) {
2063 			if (unlikely(opt_junk_alloc))
2064 				memset(ret, 0xa5, usize);
2065 			else if (unlikely(opt_zero))
2066 				memset(ret, 0, usize);
2067 		}
2068 	}
2069 
2070 	return (ret);
2071 }
2072 
2073 /* Only handles large allocations that require more than page alignment. */
2074 static void *
arena_palloc_large(tsd_t * tsd,arena_t * arena,size_t size,size_t alignment,bool zero)2075 arena_palloc_large(tsd_t *tsd, arena_t *arena, size_t size, size_t alignment,
2076     bool zero)
2077 {
2078 	void *ret;
2079 	size_t alloc_size, leadsize, trailsize;
2080 	arena_run_t *run;
2081 	arena_chunk_t *chunk;
2082 	arena_chunk_map_misc_t *miscelm;
2083 	void *rpages;
2084 
2085 	assert(size == PAGE_CEILING(size));
2086 
2087 	arena = arena_choose(tsd, arena);
2088 	if (unlikely(arena == NULL))
2089 		return (NULL);
2090 
2091 	alignment = PAGE_CEILING(alignment);
2092 	alloc_size = size + large_pad + alignment - PAGE;
2093 
2094 	malloc_mutex_lock(&arena->lock);
2095 	run = arena_run_alloc_large(arena, alloc_size, false);
2096 	if (run == NULL) {
2097 		malloc_mutex_unlock(&arena->lock);
2098 		return (NULL);
2099 	}
2100 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
2101 	miscelm = arena_run_to_miscelm(run);
2102 	rpages = arena_miscelm_to_rpages(miscelm);
2103 
2104 	leadsize = ALIGNMENT_CEILING((uintptr_t)rpages, alignment) -
2105 	    (uintptr_t)rpages;
2106 	assert(alloc_size >= leadsize + size);
2107 	trailsize = alloc_size - leadsize - size - large_pad;
2108 	if (leadsize != 0) {
2109 		arena_chunk_map_misc_t *head_miscelm = miscelm;
2110 		arena_run_t *head_run = run;
2111 
2112 		miscelm = arena_miscelm_get(chunk,
2113 		    arena_miscelm_to_pageind(head_miscelm) + (leadsize >>
2114 		    LG_PAGE));
2115 		run = &miscelm->run;
2116 
2117 		arena_run_trim_head(arena, chunk, head_run, alloc_size,
2118 		    alloc_size - leadsize);
2119 	}
2120 	if (trailsize != 0) {
2121 		arena_run_trim_tail(arena, chunk, run, size + large_pad +
2122 		    trailsize, size + large_pad, false);
2123 	}
2124 	arena_run_init_large(arena, run, size + large_pad, zero);
2125 	ret = arena_miscelm_to_rpages(miscelm);
2126 
2127 	if (config_stats) {
2128 		index_t index = size2index(size) - NBINS;
2129 
2130 		arena->stats.nmalloc_large++;
2131 		arena->stats.nrequests_large++;
2132 		arena->stats.allocated_large += size;
2133 		arena->stats.lstats[index].nmalloc++;
2134 		arena->stats.lstats[index].nrequests++;
2135 		arena->stats.lstats[index].curruns++;
2136 	}
2137 	malloc_mutex_unlock(&arena->lock);
2138 
2139 	if (config_fill && !zero) {
2140 		if (unlikely(opt_junk_alloc))
2141 			memset(ret, 0xa5, size);
2142 		else if (unlikely(opt_zero))
2143 			memset(ret, 0, size);
2144 	}
2145 	return (ret);
2146 }
2147 
2148 void *
arena_palloc(tsd_t * tsd,arena_t * arena,size_t usize,size_t alignment,bool zero,tcache_t * tcache)2149 arena_palloc(tsd_t *tsd, arena_t *arena, size_t usize, size_t alignment,
2150     bool zero, tcache_t *tcache)
2151 {
2152 	void *ret;
2153 
2154 	if (usize <= SMALL_MAXCLASS && (alignment < PAGE || (alignment == PAGE
2155 	    && (usize & PAGE_MASK) == 0))) {
2156 		/* Small; alignment doesn't require special run placement. */
2157 		ret = arena_malloc(tsd, arena, usize, zero, tcache);
2158 	} else if (usize <= arena_maxclass && alignment <= PAGE) {
2159 		/*
2160 		 * Large; alignment doesn't require special run placement.
2161 		 * However, the cached pointer may be at a random offset from
2162 		 * the base of the run, so do some bit manipulation to retrieve
2163 		 * the base.
2164 		 */
2165 		ret = arena_malloc(tsd, arena, usize, zero, tcache);
2166 		if (config_cache_oblivious)
2167 			ret = (void *)((uintptr_t)ret & ~PAGE_MASK);
2168 	} else {
2169 		if (likely(usize <= arena_maxclass)) {
2170 			ret = arena_palloc_large(tsd, arena, usize, alignment,
2171 			    zero);
2172 		} else if (likely(alignment <= chunksize))
2173 			ret = huge_malloc(tsd, arena, usize, zero, tcache);
2174 		else {
2175 			ret = huge_palloc(tsd, arena, usize, alignment, zero,
2176 			    tcache);
2177 		}
2178 	}
2179 	return (ret);
2180 }
2181 
2182 void
arena_prof_promoted(const void * ptr,size_t size)2183 arena_prof_promoted(const void *ptr, size_t size)
2184 {
2185 	arena_chunk_t *chunk;
2186 	size_t pageind;
2187 	index_t binind;
2188 
2189 	cassert(config_prof);
2190 	assert(ptr != NULL);
2191 	assert(CHUNK_ADDR2BASE(ptr) != ptr);
2192 	assert(isalloc(ptr, false) == LARGE_MINCLASS);
2193 	assert(isalloc(ptr, true) == LARGE_MINCLASS);
2194 	assert(size <= SMALL_MAXCLASS);
2195 
2196 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
2197 	pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
2198 	binind = size2index(size);
2199 	assert(binind < NBINS);
2200 	arena_mapbits_large_binind_set(chunk, pageind, binind);
2201 
2202 	assert(isalloc(ptr, false) == LARGE_MINCLASS);
2203 	assert(isalloc(ptr, true) == size);
2204 }
2205 
2206 static void
arena_dissociate_bin_run(arena_chunk_t * chunk,arena_run_t * run,arena_bin_t * bin)2207 arena_dissociate_bin_run(arena_chunk_t *chunk, arena_run_t *run,
2208     arena_bin_t *bin)
2209 {
2210 
2211 	/* Dissociate run from bin. */
2212 	if (run == bin->runcur)
2213 		bin->runcur = NULL;
2214 	else {
2215 		index_t binind = arena_bin_index(extent_node_arena_get(
2216 		    &chunk->node), bin);
2217 		arena_bin_info_t *bin_info = &arena_bin_info[binind];
2218 
2219 		if (bin_info->nregs != 1) {
2220 			/*
2221 			 * This block's conditional is necessary because if the
2222 			 * run only contains one region, then it never gets
2223 			 * inserted into the non-full runs tree.
2224 			 */
2225 			arena_bin_runs_remove(bin, run);
2226 		}
2227 	}
2228 }
2229 
2230 static void
arena_dalloc_bin_run(arena_t * arena,arena_chunk_t * chunk,arena_run_t * run,arena_bin_t * bin)2231 arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
2232     arena_bin_t *bin)
2233 {
2234 
2235 	assert(run != bin->runcur);
2236 	assert(arena_run_tree_search(&bin->runs, arena_run_to_miscelm(run)) ==
2237 	    NULL);
2238 
2239 	malloc_mutex_unlock(&bin->lock);
2240 	/******************************/
2241 	malloc_mutex_lock(&arena->lock);
2242 	arena_run_dalloc(arena, run, true, false);
2243 	malloc_mutex_unlock(&arena->lock);
2244 	/****************************/
2245 	malloc_mutex_lock(&bin->lock);
2246 	if (config_stats)
2247 		bin->stats.curruns--;
2248 }
2249 
2250 static void
arena_bin_lower_run(arena_t * arena,arena_chunk_t * chunk,arena_run_t * run,arena_bin_t * bin)2251 arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
2252     arena_bin_t *bin)
2253 {
2254 
2255 	/*
2256 	 * Make sure that if bin->runcur is non-NULL, it refers to the lowest
2257 	 * non-full run.  It is okay to NULL runcur out rather than proactively
2258 	 * keeping it pointing at the lowest non-full run.
2259 	 */
2260 	if ((uintptr_t)run < (uintptr_t)bin->runcur) {
2261 		/* Switch runcur. */
2262 		if (bin->runcur->nfree > 0)
2263 			arena_bin_runs_insert(bin, bin->runcur);
2264 		bin->runcur = run;
2265 		if (config_stats)
2266 			bin->stats.reruns++;
2267 	} else
2268 		arena_bin_runs_insert(bin, run);
2269 }
2270 
2271 static void
arena_dalloc_bin_locked_impl(arena_t * arena,arena_chunk_t * chunk,void * ptr,arena_chunk_map_bits_t * bitselm,bool junked)2272 arena_dalloc_bin_locked_impl(arena_t *arena, arena_chunk_t *chunk, void *ptr,
2273     arena_chunk_map_bits_t *bitselm, bool junked)
2274 {
2275 	size_t pageind, rpages_ind;
2276 	arena_run_t *run;
2277 	arena_bin_t *bin;
2278 	arena_bin_info_t *bin_info;
2279 	index_t binind;
2280 
2281 	pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
2282 	rpages_ind = pageind - arena_mapbits_small_runind_get(chunk, pageind);
2283 	run = &arena_miscelm_get(chunk, rpages_ind)->run;
2284 	binind = run->binind;
2285 	bin = &arena->bins[binind];
2286 	bin_info = &arena_bin_info[binind];
2287 
2288 	if (!junked && config_fill && unlikely(opt_junk_free))
2289 		arena_dalloc_junk_small(ptr, bin_info);
2290 
2291 	arena_run_reg_dalloc(run, ptr);
2292 	if (run->nfree == bin_info->nregs) {
2293 		arena_dissociate_bin_run(chunk, run, bin);
2294 		arena_dalloc_bin_run(arena, chunk, run, bin);
2295 	} else if (run->nfree == 1 && run != bin->runcur)
2296 		arena_bin_lower_run(arena, chunk, run, bin);
2297 
2298 	if (config_stats) {
2299 		bin->stats.ndalloc++;
2300 		bin->stats.curregs--;
2301 	}
2302 }
2303 
2304 void
arena_dalloc_bin_junked_locked(arena_t * arena,arena_chunk_t * chunk,void * ptr,arena_chunk_map_bits_t * bitselm)2305 arena_dalloc_bin_junked_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr,
2306     arena_chunk_map_bits_t *bitselm)
2307 {
2308 
2309 	arena_dalloc_bin_locked_impl(arena, chunk, ptr, bitselm, true);
2310 }
2311 
2312 void
arena_dalloc_bin(arena_t * arena,arena_chunk_t * chunk,void * ptr,size_t pageind,arena_chunk_map_bits_t * bitselm)2313 arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,
2314     size_t pageind, arena_chunk_map_bits_t *bitselm)
2315 {
2316 	arena_run_t *run;
2317 	arena_bin_t *bin;
2318 	size_t rpages_ind;
2319 
2320 	rpages_ind = pageind - arena_mapbits_small_runind_get(chunk, pageind);
2321 	run = &arena_miscelm_get(chunk, rpages_ind)->run;
2322 	bin = &arena->bins[run->binind];
2323 	malloc_mutex_lock(&bin->lock);
2324 	arena_dalloc_bin_locked_impl(arena, chunk, ptr, bitselm, false);
2325 	malloc_mutex_unlock(&bin->lock);
2326 }
2327 
2328 void
arena_dalloc_small(arena_t * arena,arena_chunk_t * chunk,void * ptr,size_t pageind)2329 arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr,
2330     size_t pageind)
2331 {
2332 	arena_chunk_map_bits_t *bitselm;
2333 
2334 	if (config_debug) {
2335 		/* arena_ptr_small_binind_get() does extra sanity checking. */
2336 		assert(arena_ptr_small_binind_get(ptr, arena_mapbits_get(chunk,
2337 		    pageind)) != BININD_INVALID);
2338 	}
2339 	bitselm = arena_bitselm_get(chunk, pageind);
2340 	arena_dalloc_bin(arena, chunk, ptr, pageind, bitselm);
2341 }
2342 
2343 #ifdef JEMALLOC_JET
2344 #undef arena_dalloc_junk_large
2345 #define	arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large_impl)
2346 #endif
2347 void
arena_dalloc_junk_large(void * ptr,size_t usize)2348 arena_dalloc_junk_large(void *ptr, size_t usize)
2349 {
2350 
2351 	if (config_fill && unlikely(opt_junk_free))
2352 		memset(ptr, 0x5a, usize);
2353 }
2354 #ifdef JEMALLOC_JET
2355 #undef arena_dalloc_junk_large
2356 #define	arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large)
2357 arena_dalloc_junk_large_t *arena_dalloc_junk_large =
2358     JEMALLOC_N(arena_dalloc_junk_large_impl);
2359 #endif
2360 
2361 void
arena_dalloc_large_locked_impl(arena_t * arena,arena_chunk_t * chunk,void * ptr,bool junked)2362 arena_dalloc_large_locked_impl(arena_t *arena, arena_chunk_t *chunk,
2363     void *ptr, bool junked)
2364 {
2365 	size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
2366 	arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind);
2367 	arena_run_t *run = &miscelm->run;
2368 
2369 	if (config_fill || config_stats) {
2370 		size_t usize = arena_mapbits_large_size_get(chunk, pageind) -
2371 		    large_pad;
2372 
2373 		if (!junked)
2374 			arena_dalloc_junk_large(ptr, usize);
2375 		if (config_stats) {
2376 			index_t index = size2index(usize) - NBINS;
2377 
2378 			arena->stats.ndalloc_large++;
2379 			arena->stats.allocated_large -= usize;
2380 			arena->stats.lstats[index].ndalloc++;
2381 			arena->stats.lstats[index].curruns--;
2382 		}
2383 	}
2384 
2385 	arena_run_dalloc(arena, run, true, false);
2386 }
2387 
2388 void
arena_dalloc_large_junked_locked(arena_t * arena,arena_chunk_t * chunk,void * ptr)2389 arena_dalloc_large_junked_locked(arena_t *arena, arena_chunk_t *chunk,
2390     void *ptr)
2391 {
2392 
2393 	arena_dalloc_large_locked_impl(arena, chunk, ptr, true);
2394 }
2395 
2396 void
arena_dalloc_large(arena_t * arena,arena_chunk_t * chunk,void * ptr)2397 arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr)
2398 {
2399 
2400 	malloc_mutex_lock(&arena->lock);
2401 	arena_dalloc_large_locked_impl(arena, chunk, ptr, false);
2402 	malloc_mutex_unlock(&arena->lock);
2403 }
2404 
2405 static void
arena_ralloc_large_shrink(arena_t * arena,arena_chunk_t * chunk,void * ptr,size_t oldsize,size_t size)2406 arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr,
2407     size_t oldsize, size_t size)
2408 {
2409 	size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
2410 	arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind);
2411 	arena_run_t *run = &miscelm->run;
2412 
2413 	assert(size < oldsize);
2414 
2415 	/*
2416 	 * Shrink the run, and make trailing pages available for other
2417 	 * allocations.
2418 	 */
2419 	malloc_mutex_lock(&arena->lock);
2420 	arena_run_trim_tail(arena, chunk, run, oldsize + large_pad, size +
2421 	    large_pad, true);
2422 	if (config_stats) {
2423 		index_t oldindex = size2index(oldsize) - NBINS;
2424 		index_t index = size2index(size) - NBINS;
2425 
2426 		arena->stats.ndalloc_large++;
2427 		arena->stats.allocated_large -= oldsize;
2428 		arena->stats.lstats[oldindex].ndalloc++;
2429 		arena->stats.lstats[oldindex].curruns--;
2430 
2431 		arena->stats.nmalloc_large++;
2432 		arena->stats.nrequests_large++;
2433 		arena->stats.allocated_large += size;
2434 		arena->stats.lstats[index].nmalloc++;
2435 		arena->stats.lstats[index].nrequests++;
2436 		arena->stats.lstats[index].curruns++;
2437 	}
2438 	malloc_mutex_unlock(&arena->lock);
2439 }
2440 
2441 static bool
arena_ralloc_large_grow(arena_t * arena,arena_chunk_t * chunk,void * ptr,size_t oldsize,size_t size,size_t extra,bool zero)2442 arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr,
2443     size_t oldsize, size_t size, size_t extra, bool zero)
2444 {
2445 	size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
2446 	size_t npages = oldsize >> LG_PAGE;
2447 	size_t followsize;
2448 	size_t usize_min = s2u(size);
2449 
2450 	assert(oldsize == arena_mapbits_large_size_get(chunk, pageind) -
2451 	    large_pad);
2452 
2453 	/* Try to extend the run. */
2454 	assert(usize_min > oldsize);
2455 	malloc_mutex_lock(&arena->lock);
2456 	if (pageind + npages < chunk_npages &&
2457 	    arena_mapbits_allocated_get(chunk, pageind+npages) == 0 &&
2458 	    (followsize = arena_mapbits_unallocated_size_get(chunk,
2459 	    pageind+npages)) >= usize_min - oldsize) {
2460 		/*
2461 		 * The next run is available and sufficiently large.  Split the
2462 		 * following run, then merge the first part with the existing
2463 		 * allocation.
2464 		 */
2465 		arena_run_t *run;
2466 		size_t flag_dirty, splitsize, usize;
2467 
2468 		usize = s2u(size + extra);
2469 		while (oldsize + followsize < usize)
2470 			usize = index2size(size2index(usize)-1);
2471 		assert(usize >= usize_min);
2472 		splitsize = usize - oldsize + large_pad;
2473 
2474 		run = &arena_miscelm_get(chunk, pageind+npages)->run;
2475 		arena_run_split_large(arena, run, splitsize, zero);
2476 
2477 		size = oldsize + splitsize;
2478 		npages = size >> LG_PAGE;
2479 
2480 		/*
2481 		 * Mark the extended run as dirty if either portion of the run
2482 		 * was dirty before allocation.  This is rather pedantic,
2483 		 * because there's not actually any sequence of events that
2484 		 * could cause the resulting run to be passed to
2485 		 * arena_run_dalloc() with the dirty argument set to false
2486 		 * (which is when dirty flag consistency would really matter).
2487 		 */
2488 		flag_dirty = arena_mapbits_dirty_get(chunk, pageind) |
2489 		    arena_mapbits_dirty_get(chunk, pageind+npages-1);
2490 		arena_mapbits_large_set(chunk, pageind, size, flag_dirty);
2491 		arena_mapbits_large_set(chunk, pageind+npages-1, 0, flag_dirty);
2492 
2493 		if (config_stats) {
2494 			index_t oldindex = size2index(oldsize) - NBINS;
2495 			index_t index = size2index(size) - NBINS;
2496 
2497 			arena->stats.ndalloc_large++;
2498 			arena->stats.allocated_large -= oldsize;
2499 			arena->stats.lstats[oldindex].ndalloc++;
2500 			arena->stats.lstats[oldindex].curruns--;
2501 
2502 			arena->stats.nmalloc_large++;
2503 			arena->stats.nrequests_large++;
2504 			arena->stats.allocated_large += size;
2505 			arena->stats.lstats[index].nmalloc++;
2506 			arena->stats.lstats[index].nrequests++;
2507 			arena->stats.lstats[index].curruns++;
2508 		}
2509 		malloc_mutex_unlock(&arena->lock);
2510 		return (false);
2511 	}
2512 	malloc_mutex_unlock(&arena->lock);
2513 
2514 	return (true);
2515 }
2516 
2517 #ifdef JEMALLOC_JET
2518 #undef arena_ralloc_junk_large
2519 #define	arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large_impl)
2520 #endif
2521 static void
arena_ralloc_junk_large(void * ptr,size_t old_usize,size_t usize)2522 arena_ralloc_junk_large(void *ptr, size_t old_usize, size_t usize)
2523 {
2524 
2525 	if (config_fill && unlikely(opt_junk_free)) {
2526 		memset((void *)((uintptr_t)ptr + usize), 0x5a,
2527 		    old_usize - usize);
2528 	}
2529 }
2530 #ifdef JEMALLOC_JET
2531 #undef arena_ralloc_junk_large
2532 #define	arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large)
2533 arena_ralloc_junk_large_t *arena_ralloc_junk_large =
2534     JEMALLOC_N(arena_ralloc_junk_large_impl);
2535 #endif
2536 
2537 /*
2538  * Try to resize a large allocation, in order to avoid copying.  This will
2539  * always fail if growing an object, and the following run is already in use.
2540  */
2541 static bool
arena_ralloc_large(void * ptr,size_t oldsize,size_t size,size_t extra,bool zero)2542 arena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra,
2543     bool zero)
2544 {
2545 	size_t usize;
2546 
2547 	/* Make sure extra can't cause size_t overflow. */
2548 	if (unlikely(extra >= arena_maxclass))
2549 		return (true);
2550 
2551 	usize = s2u(size + extra);
2552 	if (usize == oldsize) {
2553 		/* Same size class. */
2554 		return (false);
2555 	} else {
2556 		arena_chunk_t *chunk;
2557 		arena_t *arena;
2558 
2559 		chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
2560 		arena = extent_node_arena_get(&chunk->node);
2561 
2562 		if (usize < oldsize) {
2563 			/* Fill before shrinking in order avoid a race. */
2564 			arena_ralloc_junk_large(ptr, oldsize, usize);
2565 			arena_ralloc_large_shrink(arena, chunk, ptr, oldsize,
2566 			    usize);
2567 			return (false);
2568 		} else {
2569 			bool ret = arena_ralloc_large_grow(arena, chunk, ptr,
2570 			    oldsize, size, extra, zero);
2571 			if (config_fill && !ret && !zero) {
2572 				if (unlikely(opt_junk_alloc)) {
2573 					memset((void *)((uintptr_t)ptr +
2574 					    oldsize), 0xa5, isalloc(ptr,
2575 					    config_prof) - oldsize);
2576 				} else if (unlikely(opt_zero)) {
2577 					memset((void *)((uintptr_t)ptr +
2578 					    oldsize), 0, isalloc(ptr,
2579 					    config_prof) - oldsize);
2580 				}
2581 			}
2582 			return (ret);
2583 		}
2584 	}
2585 }
2586 
2587 bool
arena_ralloc_no_move(void * ptr,size_t oldsize,size_t size,size_t extra,bool zero)2588 arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra,
2589     bool zero)
2590 {
2591 
2592 	if (likely(size <= arena_maxclass)) {
2593 		/*
2594 		 * Avoid moving the allocation if the size class can be left the
2595 		 * same.
2596 		 */
2597 		if (likely(oldsize <= arena_maxclass)) {
2598 			if (oldsize <= SMALL_MAXCLASS) {
2599 				assert(
2600 				    arena_bin_info[size2index(oldsize)].reg_size
2601 				    == oldsize);
2602 				if ((size + extra <= SMALL_MAXCLASS &&
2603 				    size2index(size + extra) ==
2604 				    size2index(oldsize)) || (size <= oldsize &&
2605 				    size + extra >= oldsize))
2606 					return (false);
2607 			} else {
2608 				assert(size <= arena_maxclass);
2609 				if (size + extra > SMALL_MAXCLASS) {
2610 					if (!arena_ralloc_large(ptr, oldsize,
2611 					    size, extra, zero))
2612 						return (false);
2613 				}
2614 			}
2615 		}
2616 
2617 		/* Reallocation would require a move. */
2618 		return (true);
2619 	} else
2620 		return (huge_ralloc_no_move(ptr, oldsize, size, extra, zero));
2621 }
2622 
2623 void *
arena_ralloc(tsd_t * tsd,arena_t * arena,void * ptr,size_t oldsize,size_t size,size_t extra,size_t alignment,bool zero,tcache_t * tcache)2624 arena_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t size,
2625     size_t extra, size_t alignment, bool zero, tcache_t *tcache)
2626 {
2627 	void *ret;
2628 
2629 	if (likely(size <= arena_maxclass)) {
2630 		size_t copysize;
2631 
2632 		/* Try to avoid moving the allocation. */
2633 		if (!arena_ralloc_no_move(ptr, oldsize, size, extra, zero))
2634 			return (ptr);
2635 
2636 		/*
2637 		 * size and oldsize are different enough that we need to move
2638 		 * the object.  In that case, fall back to allocating new space
2639 		 * and copying.
2640 		 */
2641 		if (alignment != 0) {
2642 			size_t usize = sa2u(size + extra, alignment);
2643 			if (usize == 0)
2644 				return (NULL);
2645 			ret = ipalloct(tsd, usize, alignment, zero, tcache,
2646 			    arena);
2647 		} else {
2648 			ret = arena_malloc(tsd, arena, size + extra, zero,
2649 			    tcache);
2650 		}
2651 
2652 		if (ret == NULL) {
2653 			if (extra == 0)
2654 				return (NULL);
2655 			/* Try again, this time without extra. */
2656 			if (alignment != 0) {
2657 				size_t usize = sa2u(size, alignment);
2658 				if (usize == 0)
2659 					return (NULL);
2660 				ret = ipalloct(tsd, usize, alignment, zero,
2661 				    tcache, arena);
2662 			} else {
2663 				ret = arena_malloc(tsd, arena, size, zero,
2664 				    tcache);
2665 			}
2666 
2667 			if (ret == NULL)
2668 				return (NULL);
2669 		}
2670 
2671 		/*
2672 		 * Junk/zero-filling were already done by
2673 		 * ipalloc()/arena_malloc().
2674 		 */
2675 
2676 		/*
2677 		 * Copy at most size bytes (not size+extra), since the caller
2678 		 * has no expectation that the extra bytes will be reliably
2679 		 * preserved.
2680 		 */
2681 		copysize = (size < oldsize) ? size : oldsize;
2682 		JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, copysize);
2683 		memcpy(ret, ptr, copysize);
2684 		isqalloc(tsd, ptr, oldsize, tcache);
2685 	} else {
2686 		ret = huge_ralloc(tsd, arena, ptr, oldsize, size, extra,
2687 		    alignment, zero, tcache);
2688 	}
2689 	return (ret);
2690 }
2691 
2692 dss_prec_t
arena_dss_prec_get(arena_t * arena)2693 arena_dss_prec_get(arena_t *arena)
2694 {
2695 	dss_prec_t ret;
2696 
2697 	malloc_mutex_lock(&arena->lock);
2698 	ret = arena->dss_prec;
2699 	malloc_mutex_unlock(&arena->lock);
2700 	return (ret);
2701 }
2702 
2703 bool
arena_dss_prec_set(arena_t * arena,dss_prec_t dss_prec)2704 arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec)
2705 {
2706 
2707 	if (!have_dss)
2708 		return (dss_prec != dss_prec_disabled);
2709 	malloc_mutex_lock(&arena->lock);
2710 	arena->dss_prec = dss_prec;
2711 	malloc_mutex_unlock(&arena->lock);
2712 	return (false);
2713 }
2714 
2715 ssize_t
arena_lg_dirty_mult_default_get(void)2716 arena_lg_dirty_mult_default_get(void)
2717 {
2718 
2719 	return ((ssize_t)atomic_read_z((size_t *)&lg_dirty_mult_default));
2720 }
2721 
2722 bool
arena_lg_dirty_mult_default_set(ssize_t lg_dirty_mult)2723 arena_lg_dirty_mult_default_set(ssize_t lg_dirty_mult)
2724 {
2725 
2726 	if (!arena_lg_dirty_mult_valid(lg_dirty_mult))
2727 		return (true);
2728 	atomic_write_z((size_t *)&lg_dirty_mult_default, (size_t)lg_dirty_mult);
2729 	return (false);
2730 }
2731 
2732 void
arena_stats_merge(arena_t * arena,const char ** dss,ssize_t * lg_dirty_mult,size_t * nactive,size_t * ndirty,arena_stats_t * astats,malloc_bin_stats_t * bstats,malloc_large_stats_t * lstats,malloc_huge_stats_t * hstats)2733 arena_stats_merge(arena_t *arena, const char **dss, ssize_t *lg_dirty_mult,
2734     size_t *nactive, size_t *ndirty, arena_stats_t *astats,
2735     malloc_bin_stats_t *bstats, malloc_large_stats_t *lstats,
2736     malloc_huge_stats_t *hstats)
2737 {
2738 	unsigned i;
2739 
2740 	malloc_mutex_lock(&arena->lock);
2741 	*dss = dss_prec_names[arena->dss_prec];
2742 	*lg_dirty_mult = arena->lg_dirty_mult;
2743 	*nactive += arena->nactive;
2744 	*ndirty += arena->ndirty;
2745 
2746 	astats->mapped += arena->stats.mapped;
2747 	astats->npurge += arena->stats.npurge;
2748 	astats->nmadvise += arena->stats.nmadvise;
2749 	astats->purged += arena->stats.purged;
2750 	astats->metadata_mapped += arena->stats.metadata_mapped;
2751 	astats->metadata_allocated += arena_metadata_allocated_get(arena);
2752 	astats->allocated_large += arena->stats.allocated_large;
2753 	astats->nmalloc_large += arena->stats.nmalloc_large;
2754 	astats->ndalloc_large += arena->stats.ndalloc_large;
2755 	astats->nrequests_large += arena->stats.nrequests_large;
2756 	astats->allocated_huge += arena->stats.allocated_huge;
2757 	astats->nmalloc_huge += arena->stats.nmalloc_huge;
2758 	astats->ndalloc_huge += arena->stats.ndalloc_huge;
2759 
2760 	for (i = 0; i < nlclasses; i++) {
2761 		lstats[i].nmalloc += arena->stats.lstats[i].nmalloc;
2762 		lstats[i].ndalloc += arena->stats.lstats[i].ndalloc;
2763 		lstats[i].nrequests += arena->stats.lstats[i].nrequests;
2764 		lstats[i].curruns += arena->stats.lstats[i].curruns;
2765 	}
2766 
2767 	for (i = 0; i < nhclasses; i++) {
2768 		hstats[i].nmalloc += arena->stats.hstats[i].nmalloc;
2769 		hstats[i].ndalloc += arena->stats.hstats[i].ndalloc;
2770 		hstats[i].curhchunks += arena->stats.hstats[i].curhchunks;
2771 	}
2772 	malloc_mutex_unlock(&arena->lock);
2773 
2774 	for (i = 0; i < NBINS; i++) {
2775 		arena_bin_t *bin = &arena->bins[i];
2776 
2777 		malloc_mutex_lock(&bin->lock);
2778 		bstats[i].nmalloc += bin->stats.nmalloc;
2779 		bstats[i].ndalloc += bin->stats.ndalloc;
2780 		bstats[i].nrequests += bin->stats.nrequests;
2781 		bstats[i].curregs += bin->stats.curregs;
2782 		if (config_tcache) {
2783 			bstats[i].nfills += bin->stats.nfills;
2784 			bstats[i].nflushes += bin->stats.nflushes;
2785 		}
2786 		bstats[i].nruns += bin->stats.nruns;
2787 		bstats[i].reruns += bin->stats.reruns;
2788 		bstats[i].curruns += bin->stats.curruns;
2789 		malloc_mutex_unlock(&bin->lock);
2790 	}
2791 }
2792 
2793 arena_t *
arena_new(unsigned ind)2794 arena_new(unsigned ind)
2795 {
2796 	arena_t *arena;
2797 	unsigned i;
2798 	arena_bin_t *bin;
2799 
2800 	/*
2801 	 * Allocate arena, arena->lstats, and arena->hstats contiguously, mainly
2802 	 * because there is no way to clean up if base_alloc() OOMs.
2803 	 */
2804 	if (config_stats) {
2805 		arena = (arena_t *)base_alloc(CACHELINE_CEILING(sizeof(arena_t))
2806 		    + QUANTUM_CEILING(nlclasses * sizeof(malloc_large_stats_t) +
2807 		    nhclasses) * sizeof(malloc_huge_stats_t));
2808 	} else
2809 		arena = (arena_t *)base_alloc(sizeof(arena_t));
2810 	if (arena == NULL)
2811 		return (NULL);
2812 
2813 	arena->ind = ind;
2814 	arena->nthreads = 0;
2815 	if (malloc_mutex_init(&arena->lock))
2816 		return (NULL);
2817 
2818 	if (config_stats) {
2819 		memset(&arena->stats, 0, sizeof(arena_stats_t));
2820 		arena->stats.lstats = (malloc_large_stats_t *)((uintptr_t)arena
2821 		    + CACHELINE_CEILING(sizeof(arena_t)));
2822 		memset(arena->stats.lstats, 0, nlclasses *
2823 		    sizeof(malloc_large_stats_t));
2824 		arena->stats.hstats = (malloc_huge_stats_t *)((uintptr_t)arena
2825 		    + CACHELINE_CEILING(sizeof(arena_t)) +
2826 		    QUANTUM_CEILING(nlclasses * sizeof(malloc_large_stats_t)));
2827 		memset(arena->stats.hstats, 0, nhclasses *
2828 		    sizeof(malloc_huge_stats_t));
2829 		if (config_tcache)
2830 			ql_new(&arena->tcache_ql);
2831 	}
2832 
2833 	if (config_prof)
2834 		arena->prof_accumbytes = 0;
2835 
2836 	if (config_cache_oblivious) {
2837 		/*
2838 		 * A nondeterministic seed based on the address of arena reduces
2839 		 * the likelihood of lockstep non-uniform cache index
2840 		 * utilization among identical concurrent processes, but at the
2841 		 * cost of test repeatability.  For debug builds, instead use a
2842 		 * deterministic seed.
2843 		 */
2844 		arena->offset_state = config_debug ? ind :
2845 		    (uint64_t)(uintptr_t)arena;
2846 	}
2847 
2848 	arena->dss_prec = chunk_dss_prec_get();
2849 
2850 	arena->spare = NULL;
2851 
2852 	arena->lg_dirty_mult = arena_lg_dirty_mult_default_get();
2853 	arena->nactive = 0;
2854 	arena->ndirty = 0;
2855 
2856 	arena_avail_tree_new(&arena->runs_avail);
2857 	qr_new(&arena->runs_dirty, rd_link);
2858 	qr_new(&arena->chunks_cache, cc_link);
2859 
2860 	ql_new(&arena->huge);
2861 	if (malloc_mutex_init(&arena->huge_mtx))
2862 		return (NULL);
2863 
2864 	extent_tree_szad_new(&arena->chunks_szad_cache);
2865 	extent_tree_ad_new(&arena->chunks_ad_cache);
2866 	extent_tree_szad_new(&arena->chunks_szad_mmap);
2867 	extent_tree_ad_new(&arena->chunks_ad_mmap);
2868 	extent_tree_szad_new(&arena->chunks_szad_dss);
2869 	extent_tree_ad_new(&arena->chunks_ad_dss);
2870 	if (malloc_mutex_init(&arena->chunks_mtx))
2871 		return (NULL);
2872 	ql_new(&arena->node_cache);
2873 	if (malloc_mutex_init(&arena->node_cache_mtx))
2874 		return (NULL);
2875 
2876 	arena->chunk_alloc = chunk_alloc_default;
2877 	arena->chunk_dalloc = chunk_dalloc_default;
2878 	arena->chunk_purge = chunk_purge_default;
2879 
2880 	/* Initialize bins. */
2881 	for (i = 0; i < NBINS; i++) {
2882 		bin = &arena->bins[i];
2883 		if (malloc_mutex_init(&bin->lock))
2884 			return (NULL);
2885 		bin->runcur = NULL;
2886 		arena_run_tree_new(&bin->runs);
2887 		if (config_stats)
2888 			memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
2889 	}
2890 
2891 	return (arena);
2892 }
2893 
2894 /*
2895  * Calculate bin_info->run_size such that it meets the following constraints:
2896  *
2897  *   *) bin_info->run_size <= arena_maxrun
2898  *   *) bin_info->nregs <= RUN_MAXREGS
2899  *
2900  * bin_info->nregs and bin_info->reg0_offset are also calculated here, since
2901  * these settings are all interdependent.
2902  */
2903 static void
bin_info_run_size_calc(arena_bin_info_t * bin_info)2904 bin_info_run_size_calc(arena_bin_info_t *bin_info)
2905 {
2906 	size_t pad_size;
2907 	size_t try_run_size, perfect_run_size, actual_run_size;
2908 	uint32_t try_nregs, perfect_nregs, actual_nregs;
2909 
2910 	/*
2911 	 * Determine redzone size based on minimum alignment and minimum
2912 	 * redzone size.  Add padding to the end of the run if it is needed to
2913 	 * align the regions.  The padding allows each redzone to be half the
2914 	 * minimum alignment; without the padding, each redzone would have to
2915 	 * be twice as large in order to maintain alignment.
2916 	 */
2917 	if (config_fill && unlikely(opt_redzone)) {
2918 		size_t align_min = ZU(1) << (jemalloc_ffs(bin_info->reg_size) -
2919 		    1);
2920 		if (align_min <= REDZONE_MINSIZE) {
2921 			bin_info->redzone_size = REDZONE_MINSIZE;
2922 			pad_size = 0;
2923 		} else {
2924 			bin_info->redzone_size = align_min >> 1;
2925 			pad_size = bin_info->redzone_size;
2926 		}
2927 	} else {
2928 		bin_info->redzone_size = 0;
2929 		pad_size = 0;
2930 	}
2931 	bin_info->reg_interval = bin_info->reg_size +
2932 	    (bin_info->redzone_size << 1);
2933 
2934 	/*
2935 	 * Compute run size under ideal conditions (no redzones, no limit on run
2936 	 * size).
2937 	 */
2938 	try_run_size = PAGE;
2939 	try_nregs = try_run_size / bin_info->reg_size;
2940 	do {
2941 		perfect_run_size = try_run_size;
2942 		perfect_nregs = try_nregs;
2943 
2944 		try_run_size += PAGE;
2945 		try_nregs = try_run_size / bin_info->reg_size;
2946 	} while (perfect_run_size != perfect_nregs * bin_info->reg_size);
2947 	assert(perfect_nregs <= RUN_MAXREGS);
2948 
2949 	actual_run_size = perfect_run_size;
2950 	actual_nregs = (actual_run_size - pad_size) / bin_info->reg_interval;
2951 
2952 	/*
2953 	 * Redzones can require enough padding that not even a single region can
2954 	 * fit within the number of pages that would normally be dedicated to a
2955 	 * run for this size class.  Increase the run size until at least one
2956 	 * region fits.
2957 	 */
2958 	while (actual_nregs == 0) {
2959 		assert(config_fill && unlikely(opt_redzone));
2960 
2961 		actual_run_size += PAGE;
2962 		actual_nregs = (actual_run_size - pad_size) /
2963 		    bin_info->reg_interval;
2964 	}
2965 
2966 	/*
2967 	 * Make sure that the run will fit within an arena chunk.
2968 	 */
2969 	while (actual_run_size > arena_maxrun) {
2970 		actual_run_size -= PAGE;
2971 		actual_nregs = (actual_run_size - pad_size) /
2972 		    bin_info->reg_interval;
2973 	}
2974 	assert(actual_nregs > 0);
2975 	assert(actual_run_size == s2u(actual_run_size));
2976 
2977 	/* Copy final settings. */
2978 	bin_info->run_size = actual_run_size;
2979 	bin_info->nregs = actual_nregs;
2980 	bin_info->reg0_offset = actual_run_size - (actual_nregs *
2981 	    bin_info->reg_interval) - pad_size + bin_info->redzone_size;
2982 
2983 	if (actual_run_size > small_maxrun)
2984 		small_maxrun = actual_run_size;
2985 
2986 	assert(bin_info->reg0_offset - bin_info->redzone_size + (bin_info->nregs
2987 	    * bin_info->reg_interval) + pad_size == bin_info->run_size);
2988 }
2989 
2990 static void
bin_info_init(void)2991 bin_info_init(void)
2992 {
2993 	arena_bin_info_t *bin_info;
2994 
2995 #define	BIN_INFO_INIT_bin_yes(index, size)				\
2996 	bin_info = &arena_bin_info[index];				\
2997 	bin_info->reg_size = size;					\
2998 	bin_info_run_size_calc(bin_info);				\
2999 	bitmap_info_init(&bin_info->bitmap_info, bin_info->nregs);
3000 #define	BIN_INFO_INIT_bin_no(index, size)
3001 #define	SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup)	\
3002 	BIN_INFO_INIT_bin_##bin(index, (ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta))
3003 	SIZE_CLASSES
3004 #undef BIN_INFO_INIT_bin_yes
3005 #undef BIN_INFO_INIT_bin_no
3006 #undef SC
3007 }
3008 
3009 static bool
small_run_size_init(void)3010 small_run_size_init(void)
3011 {
3012 
3013 	assert(small_maxrun != 0);
3014 
3015 	small_run_tab = (bool *)base_alloc(sizeof(bool) * (small_maxrun >>
3016 	    LG_PAGE));
3017 	if (small_run_tab == NULL)
3018 		return (true);
3019 
3020 #define	TAB_INIT_bin_yes(index, size) {					\
3021 		arena_bin_info_t *bin_info = &arena_bin_info[index];	\
3022 		small_run_tab[bin_info->run_size >> LG_PAGE] = true;	\
3023 	}
3024 #define	TAB_INIT_bin_no(index, size)
3025 #define	SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup)	\
3026 	TAB_INIT_bin_##bin(index, (ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta))
3027 	SIZE_CLASSES
3028 #undef TAB_INIT_bin_yes
3029 #undef TAB_INIT_bin_no
3030 #undef SC
3031 
3032 	return (false);
3033 }
3034 
3035 bool
arena_boot(void)3036 arena_boot(void)
3037 {
3038 	size_t header_size;
3039 	unsigned i;
3040 
3041 	arena_lg_dirty_mult_default_set(opt_lg_dirty_mult);
3042 
3043 	/*
3044 	 * Compute the header size such that it is large enough to contain the
3045 	 * page map.  The page map is biased to omit entries for the header
3046 	 * itself, so some iteration is necessary to compute the map bias.
3047 	 *
3048 	 * 1) Compute safe header_size and map_bias values that include enough
3049 	 *    space for an unbiased page map.
3050 	 * 2) Refine map_bias based on (1) to omit the header pages in the page
3051 	 *    map.  The resulting map_bias may be one too small.
3052 	 * 3) Refine map_bias based on (2).  The result will be >= the result
3053 	 *    from (2), and will always be correct.
3054 	 */
3055 	map_bias = 0;
3056 	for (i = 0; i < 3; i++) {
3057 		header_size = offsetof(arena_chunk_t, map_bits) +
3058 		    ((sizeof(arena_chunk_map_bits_t) +
3059 		    sizeof(arena_chunk_map_misc_t)) * (chunk_npages-map_bias));
3060 		map_bias = (header_size + PAGE_MASK) >> LG_PAGE;
3061 	}
3062 	assert(map_bias > 0);
3063 
3064 	map_misc_offset = offsetof(arena_chunk_t, map_bits) +
3065 	    sizeof(arena_chunk_map_bits_t) * (chunk_npages-map_bias);
3066 
3067 	arena_maxrun = chunksize - (map_bias << LG_PAGE);
3068 	assert(arena_maxrun > 0);
3069 	arena_maxclass = index2size(size2index(chunksize)-1);
3070 	if (arena_maxclass > arena_maxrun) {
3071 		/*
3072 		 * For small chunk sizes it's possible for there to be fewer
3073 		 * non-header pages available than are necessary to serve the
3074 		 * size classes just below chunksize.
3075 		 */
3076 		arena_maxclass = arena_maxrun;
3077 	}
3078 	assert(arena_maxclass > 0);
3079 	nlclasses = size2index(arena_maxclass) - size2index(SMALL_MAXCLASS);
3080 	nhclasses = NSIZES - nlclasses - NBINS;
3081 
3082 	bin_info_init();
3083 	return (small_run_size_init());
3084 }
3085 
3086 void
arena_prefork(arena_t * arena)3087 arena_prefork(arena_t *arena)
3088 {
3089 	unsigned i;
3090 
3091 	malloc_mutex_prefork(&arena->lock);
3092 	malloc_mutex_prefork(&arena->huge_mtx);
3093 	malloc_mutex_prefork(&arena->chunks_mtx);
3094 	malloc_mutex_prefork(&arena->node_cache_mtx);
3095 	for (i = 0; i < NBINS; i++)
3096 		malloc_mutex_prefork(&arena->bins[i].lock);
3097 }
3098 
3099 void
arena_postfork_parent(arena_t * arena)3100 arena_postfork_parent(arena_t *arena)
3101 {
3102 	unsigned i;
3103 
3104 	for (i = 0; i < NBINS; i++)
3105 		malloc_mutex_postfork_parent(&arena->bins[i].lock);
3106 	malloc_mutex_postfork_parent(&arena->node_cache_mtx);
3107 	malloc_mutex_postfork_parent(&arena->chunks_mtx);
3108 	malloc_mutex_postfork_parent(&arena->huge_mtx);
3109 	malloc_mutex_postfork_parent(&arena->lock);
3110 }
3111 
3112 void
arena_postfork_child(arena_t * arena)3113 arena_postfork_child(arena_t *arena)
3114 {
3115 	unsigned i;
3116 
3117 	for (i = 0; i < NBINS; i++)
3118 		malloc_mutex_postfork_child(&arena->bins[i].lock);
3119 	malloc_mutex_postfork_child(&arena->node_cache_mtx);
3120 	malloc_mutex_postfork_child(&arena->chunks_mtx);
3121 	malloc_mutex_postfork_child(&arena->huge_mtx);
3122 	malloc_mutex_postfork_child(&arena->lock);
3123 }
3124