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