1#define JEMALLOC_ARENA_C_ 2#include "jemalloc/internal/jemalloc_internal.h" 3 4/******************************************************************************/ 5/* Data. */ 6 7ssize_t opt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT; 8arena_bin_info_t arena_bin_info[NBINS]; 9 10JEMALLOC_ALIGNED(CACHELINE) 11const uint8_t small_size2bin[] = { 12#define S2B_8(i) i, 13#define S2B_16(i) S2B_8(i) S2B_8(i) 14#define S2B_32(i) S2B_16(i) S2B_16(i) 15#define S2B_64(i) S2B_32(i) S2B_32(i) 16#define S2B_128(i) S2B_64(i) S2B_64(i) 17#define S2B_256(i) S2B_128(i) S2B_128(i) 18#define S2B_512(i) S2B_256(i) S2B_256(i) 19#define S2B_1024(i) S2B_512(i) S2B_512(i) 20#define S2B_2048(i) S2B_1024(i) S2B_1024(i) 21#define S2B_4096(i) S2B_2048(i) S2B_2048(i) 22#define S2B_8192(i) S2B_4096(i) S2B_4096(i) 23#define SIZE_CLASS(bin, delta, size) \ 24 S2B_##delta(bin) 25 SIZE_CLASSES 26#undef S2B_8 27#undef S2B_16 28#undef S2B_32 29#undef S2B_64 30#undef S2B_128 31#undef S2B_256 32#undef S2B_512 33#undef S2B_1024 34#undef S2B_2048 35#undef S2B_4096 36#undef S2B_8192 37#undef SIZE_CLASS 38}; 39 40/******************************************************************************/ 41/* 42 * Function prototypes for static functions that are referenced prior to 43 * definition. 44 */ 45 46static void arena_purge(arena_t *arena, bool all); 47static void arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, 48 bool cleaned); 49static void arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, 50 arena_run_t *run, arena_bin_t *bin); 51static void arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, 52 arena_run_t *run, arena_bin_t *bin); 53 54/******************************************************************************/ 55 56static inline int 57arena_run_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) 58{ 59 uintptr_t a_mapelm = (uintptr_t)a; 60 uintptr_t b_mapelm = (uintptr_t)b; 61 62 assert(a != NULL); 63 assert(b != NULL); 64 65 return ((a_mapelm > b_mapelm) - (a_mapelm < b_mapelm)); 66} 67 68/* Generate red-black tree functions. */ 69rb_gen(static UNUSED, arena_run_tree_, arena_run_tree_t, arena_chunk_map_t, 70 u.rb_link, arena_run_comp) 71 72static inline int 73arena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) 74{ 75 int ret; 76 size_t a_size = a->bits & ~PAGE_MASK; 77 size_t b_size = b->bits & ~PAGE_MASK; 78 79 ret = (a_size > b_size) - (a_size < b_size); 80 if (ret == 0) { 81 uintptr_t a_mapelm, b_mapelm; 82 83 if ((a->bits & CHUNK_MAP_KEY) != CHUNK_MAP_KEY) 84 a_mapelm = (uintptr_t)a; 85 else { 86 /* 87 * Treat keys as though they are lower than anything 88 * else. 89 */ 90 a_mapelm = 0; 91 } 92 b_mapelm = (uintptr_t)b; 93 94 ret = (a_mapelm > b_mapelm) - (a_mapelm < b_mapelm); 95 } 96 97 return (ret); 98} 99 100/* Generate red-black tree functions. */ 101rb_gen(static UNUSED, arena_avail_tree_, arena_avail_tree_t, arena_chunk_map_t, 102 u.rb_link, arena_avail_comp) 103 104static inline int 105arena_chunk_dirty_comp(arena_chunk_t *a, arena_chunk_t *b) 106{ 107 108 assert(a != NULL); 109 assert(b != NULL); 110 111 /* 112 * Short-circuit for self comparison. The following comparison code 113 * would come to the same result, but at the cost of executing the slow 114 * path. 115 */ 116 if (a == b) 117 return (0); 118 119 /* 120 * Order such that chunks with higher fragmentation are "less than" 121 * those with lower fragmentation -- purging order is from "least" to 122 * "greatest". Fragmentation is measured as: 123 * 124 * mean current avail run size 125 * -------------------------------- 126 * mean defragmented avail run size 127 * 128 * navail 129 * ----------- 130 * nruns_avail nruns_avail-nruns_adjac 131 * = ========================= = ----------------------- 132 * navail nruns_avail 133 * ----------------------- 134 * nruns_avail-nruns_adjac 135 * 136 * The following code multiplies away the denominator prior to 137 * comparison, in order to avoid division. 138 * 139 */ 140 { 141 size_t a_val = (a->nruns_avail - a->nruns_adjac) * 142 b->nruns_avail; 143 size_t b_val = (b->nruns_avail - b->nruns_adjac) * 144 a->nruns_avail; 145 146 if (a_val < b_val) 147 return (1); 148 if (a_val > b_val) 149 return (-1); 150 } 151 /* 152 * Break ties by chunk address. For fragmented chunks, report lower 153 * addresses as "lower", so that fragmentation reduction happens first 154 * at lower addresses. However, use the opposite ordering for 155 * unfragmented chunks, in order to increase the chances of 156 * re-allocating dirty runs. 157 */ 158 { 159 uintptr_t a_chunk = (uintptr_t)a; 160 uintptr_t b_chunk = (uintptr_t)b; 161 int ret = ((a_chunk > b_chunk) - (a_chunk < b_chunk)); 162 if (a->nruns_adjac == 0) { 163 assert(b->nruns_adjac == 0); 164 ret = -ret; 165 } 166 return (ret); 167 } 168} 169 170/* Generate red-black tree functions. */ 171rb_gen(static UNUSED, arena_chunk_dirty_, arena_chunk_tree_t, arena_chunk_t, 172 dirty_link, arena_chunk_dirty_comp) 173 174static inline bool 175arena_avail_adjac_pred(arena_chunk_t *chunk, size_t pageind) 176{ 177 bool ret; 178 179 if (pageind-1 < map_bias) 180 ret = false; 181 else { 182 ret = (arena_mapbits_allocated_get(chunk, pageind-1) == 0); 183 assert(ret == false || arena_mapbits_dirty_get(chunk, 184 pageind-1) != arena_mapbits_dirty_get(chunk, pageind)); 185 } 186 return (ret); 187} 188 189static inline bool 190arena_avail_adjac_succ(arena_chunk_t *chunk, size_t pageind, size_t npages) 191{ 192 bool ret; 193 194 if (pageind+npages == chunk_npages) 195 ret = false; 196 else { 197 assert(pageind+npages < chunk_npages); 198 ret = (arena_mapbits_allocated_get(chunk, pageind+npages) == 0); 199 assert(ret == false || arena_mapbits_dirty_get(chunk, pageind) 200 != arena_mapbits_dirty_get(chunk, pageind+npages)); 201 } 202 return (ret); 203} 204 205static inline bool 206arena_avail_adjac(arena_chunk_t *chunk, size_t pageind, size_t npages) 207{ 208 209 return (arena_avail_adjac_pred(chunk, pageind) || 210 arena_avail_adjac_succ(chunk, pageind, npages)); 211} 212 213static void 214arena_avail_insert(arena_t *arena, arena_chunk_t *chunk, size_t pageind, 215 size_t npages, bool maybe_adjac_pred, bool maybe_adjac_succ) 216{ 217 218 assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >> 219 LG_PAGE)); 220 221 /* 222 * chunks_dirty is keyed by nruns_{avail,adjac}, so the chunk must be 223 * removed and reinserted even if the run to be inserted is clean. 224 */ 225 if (chunk->ndirty != 0) 226 arena_chunk_dirty_remove(&arena->chunks_dirty, chunk); 227 228 if (maybe_adjac_pred && arena_avail_adjac_pred(chunk, pageind)) 229 chunk->nruns_adjac++; 230 if (maybe_adjac_succ && arena_avail_adjac_succ(chunk, pageind, npages)) 231 chunk->nruns_adjac++; 232 chunk->nruns_avail++; 233 assert(chunk->nruns_avail > chunk->nruns_adjac); 234 235 if (arena_mapbits_dirty_get(chunk, pageind) != 0) { 236 arena->ndirty += npages; 237 chunk->ndirty += npages; 238 } 239 if (chunk->ndirty != 0) 240 arena_chunk_dirty_insert(&arena->chunks_dirty, chunk); 241 242 arena_avail_tree_insert(&arena->runs_avail, arena_mapp_get(chunk, 243 pageind)); 244} 245 246static void 247arena_avail_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind, 248 size_t npages, bool maybe_adjac_pred, bool maybe_adjac_succ) 249{ 250 251 assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >> 252 LG_PAGE)); 253 254 /* 255 * chunks_dirty is keyed by nruns_{avail,adjac}, so the chunk must be 256 * removed and reinserted even if the run to be removed is clean. 257 */ 258 if (chunk->ndirty != 0) 259 arena_chunk_dirty_remove(&arena->chunks_dirty, chunk); 260 261 if (maybe_adjac_pred && arena_avail_adjac_pred(chunk, pageind)) 262 chunk->nruns_adjac--; 263 if (maybe_adjac_succ && arena_avail_adjac_succ(chunk, pageind, npages)) 264 chunk->nruns_adjac--; 265 chunk->nruns_avail--; 266 assert(chunk->nruns_avail > chunk->nruns_adjac || (chunk->nruns_avail 267 == 0 && chunk->nruns_adjac == 0)); 268 269 if (arena_mapbits_dirty_get(chunk, pageind) != 0) { 270 arena->ndirty -= npages; 271 chunk->ndirty -= npages; 272 } 273 if (chunk->ndirty != 0) 274 arena_chunk_dirty_insert(&arena->chunks_dirty, chunk); 275 276 arena_avail_tree_remove(&arena->runs_avail, arena_mapp_get(chunk, 277 pageind)); 278} 279 280static inline void * 281arena_run_reg_alloc(arena_run_t *run, arena_bin_info_t *bin_info) 282{ 283 void *ret; 284 unsigned regind; 285 bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + 286 (uintptr_t)bin_info->bitmap_offset); 287 288 assert(run->nfree > 0); 289 assert(bitmap_full(bitmap, &bin_info->bitmap_info) == false); 290 291 regind = bitmap_sfu(bitmap, &bin_info->bitmap_info); 292 ret = (void *)((uintptr_t)run + (uintptr_t)bin_info->reg0_offset + 293 (uintptr_t)(bin_info->reg_interval * regind)); 294 run->nfree--; 295 if (regind == run->nextind) 296 run->nextind++; 297 assert(regind < run->nextind); 298 return (ret); 299} 300 301static inline void 302arena_run_reg_dalloc(arena_run_t *run, void *ptr) 303{ 304 arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 305 size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 306 size_t mapbits = arena_mapbits_get(chunk, pageind); 307 size_t binind = arena_ptr_small_binind_get(ptr, mapbits); 308 arena_bin_info_t *bin_info = &arena_bin_info[binind]; 309 unsigned regind = arena_run_regind(run, bin_info, ptr); 310 bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + 311 (uintptr_t)bin_info->bitmap_offset); 312 313 assert(run->nfree < bin_info->nregs); 314 /* Freeing an interior pointer can cause assertion failure. */ 315 assert(((uintptr_t)ptr - ((uintptr_t)run + 316 (uintptr_t)bin_info->reg0_offset)) % 317 (uintptr_t)bin_info->reg_interval == 0); 318 assert((uintptr_t)ptr >= (uintptr_t)run + 319 (uintptr_t)bin_info->reg0_offset); 320 /* Freeing an unallocated pointer can cause assertion failure. */ 321 assert(bitmap_get(bitmap, &bin_info->bitmap_info, regind)); 322 323 bitmap_unset(bitmap, &bin_info->bitmap_info, regind); 324 run->nfree++; 325} 326 327static inline void 328arena_run_zero(arena_chunk_t *chunk, size_t run_ind, size_t npages) 329{ 330 331 VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + (run_ind << 332 LG_PAGE)), (npages << LG_PAGE)); 333 memset((void *)((uintptr_t)chunk + (run_ind << LG_PAGE)), 0, 334 (npages << LG_PAGE)); 335} 336 337static inline void 338arena_run_page_mark_zeroed(arena_chunk_t *chunk, size_t run_ind) 339{ 340 341 VALGRIND_MAKE_MEM_DEFINED((void *)((uintptr_t)chunk + (run_ind << 342 LG_PAGE)), PAGE); 343} 344 345static inline void 346arena_run_page_validate_zeroed(arena_chunk_t *chunk, size_t run_ind) 347{ 348 size_t i; 349 UNUSED size_t *p = (size_t *)((uintptr_t)chunk + (run_ind << LG_PAGE)); 350 351 arena_run_page_mark_zeroed(chunk, run_ind); 352 for (i = 0; i < PAGE / sizeof(size_t); i++) 353 assert(p[i] == 0); 354} 355 356static void 357arena_cactive_update(arena_t *arena, size_t add_pages, size_t sub_pages) 358{ 359 360 if (config_stats) { 361 ssize_t cactive_diff = CHUNK_CEILING((arena->nactive + 362 add_pages) << LG_PAGE) - CHUNK_CEILING((arena->nactive - 363 sub_pages) << LG_PAGE); 364 if (cactive_diff != 0) 365 stats_cactive_add(cactive_diff); 366 } 367} 368 369static void 370arena_run_split_remove(arena_t *arena, arena_chunk_t *chunk, size_t run_ind, 371 size_t flag_dirty, size_t need_pages) 372{ 373 size_t total_pages, rem_pages; 374 375 total_pages = arena_mapbits_unallocated_size_get(chunk, run_ind) >> 376 LG_PAGE; 377 assert(arena_mapbits_dirty_get(chunk, run_ind+total_pages-1) == 378 flag_dirty); 379 assert(need_pages <= total_pages); 380 rem_pages = total_pages - need_pages; 381 382 arena_avail_remove(arena, chunk, run_ind, total_pages, true, true); 383 arena_cactive_update(arena, need_pages, 0); 384 arena->nactive += need_pages; 385 386 /* Keep track of trailing unused pages for later use. */ 387 if (rem_pages > 0) { 388 if (flag_dirty != 0) { 389 arena_mapbits_unallocated_set(chunk, 390 run_ind+need_pages, (rem_pages << LG_PAGE), 391 flag_dirty); 392 arena_mapbits_unallocated_set(chunk, 393 run_ind+total_pages-1, (rem_pages << LG_PAGE), 394 flag_dirty); 395 } else { 396 arena_mapbits_unallocated_set(chunk, run_ind+need_pages, 397 (rem_pages << LG_PAGE), 398 arena_mapbits_unzeroed_get(chunk, 399 run_ind+need_pages)); 400 arena_mapbits_unallocated_set(chunk, 401 run_ind+total_pages-1, (rem_pages << LG_PAGE), 402 arena_mapbits_unzeroed_get(chunk, 403 run_ind+total_pages-1)); 404 } 405 arena_avail_insert(arena, chunk, run_ind+need_pages, rem_pages, 406 false, true); 407 } 408} 409 410static void 411arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size, 412 bool remove, bool zero) 413{ 414 arena_chunk_t *chunk; 415 size_t flag_dirty, run_ind, need_pages, i; 416 417 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 418 run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); 419 flag_dirty = arena_mapbits_dirty_get(chunk, run_ind); 420 need_pages = (size >> LG_PAGE); 421 assert(need_pages > 0); 422 423 if (remove) { 424 arena_run_split_remove(arena, chunk, run_ind, flag_dirty, 425 need_pages); 426 } 427 428 if (zero) { 429 if (flag_dirty == 0) { 430 /* 431 * The run is clean, so some pages may be zeroed (i.e. 432 * never before touched). 433 */ 434 for (i = 0; i < need_pages; i++) { 435 if (arena_mapbits_unzeroed_get(chunk, run_ind+i) 436 != 0) 437 arena_run_zero(chunk, run_ind+i, 1); 438 else if (config_debug) { 439 arena_run_page_validate_zeroed(chunk, 440 run_ind+i); 441 } else { 442 arena_run_page_mark_zeroed(chunk, 443 run_ind+i); 444 } 445 } 446 } else { 447 /* The run is dirty, so all pages must be zeroed. */ 448 arena_run_zero(chunk, run_ind, need_pages); 449 } 450 } else { 451 VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + 452 (run_ind << LG_PAGE)), (need_pages << LG_PAGE)); 453 } 454 455 /* 456 * Set the last element first, in case the run only contains one page 457 * (i.e. both statements set the same element). 458 */ 459 arena_mapbits_large_set(chunk, run_ind+need_pages-1, 0, flag_dirty); 460 arena_mapbits_large_set(chunk, run_ind, size, flag_dirty); 461} 462 463static void 464arena_run_split_large(arena_t *arena, arena_run_t *run, size_t size, bool zero) 465{ 466 467 arena_run_split_large_helper(arena, run, size, true, zero); 468} 469 470static void 471arena_run_init_large(arena_t *arena, arena_run_t *run, size_t size, bool zero) 472{ 473 474 arena_run_split_large_helper(arena, run, size, false, zero); 475} 476 477static void 478arena_run_split_small(arena_t *arena, arena_run_t *run, size_t size, 479 size_t binind) 480{ 481 arena_chunk_t *chunk; 482 size_t flag_dirty, run_ind, need_pages, i; 483 484 assert(binind != BININD_INVALID); 485 486 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 487 run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); 488 flag_dirty = arena_mapbits_dirty_get(chunk, run_ind); 489 need_pages = (size >> LG_PAGE); 490 assert(need_pages > 0); 491 492 arena_run_split_remove(arena, chunk, run_ind, flag_dirty, need_pages); 493 494 /* 495 * Propagate the dirty and unzeroed flags to the allocated small run, 496 * so that arena_dalloc_bin_run() has the ability to conditionally trim 497 * clean pages. 498 */ 499 arena_mapbits_small_set(chunk, run_ind, 0, binind, flag_dirty); 500 /* 501 * The first page will always be dirtied during small run 502 * initialization, so a validation failure here would not actually 503 * cause an observable failure. 504 */ 505 if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk, 506 run_ind) == 0) 507 arena_run_page_validate_zeroed(chunk, run_ind); 508 for (i = 1; i < need_pages - 1; i++) { 509 arena_mapbits_small_set(chunk, run_ind+i, i, binind, 0); 510 if (config_debug && flag_dirty == 0 && 511 arena_mapbits_unzeroed_get(chunk, run_ind+i) == 0) 512 arena_run_page_validate_zeroed(chunk, run_ind+i); 513 } 514 arena_mapbits_small_set(chunk, run_ind+need_pages-1, need_pages-1, 515 binind, flag_dirty); 516 if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk, 517 run_ind+need_pages-1) == 0) 518 arena_run_page_validate_zeroed(chunk, run_ind+need_pages-1); 519 VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + 520 (run_ind << LG_PAGE)), (need_pages << LG_PAGE)); 521} 522 523static arena_chunk_t * 524arena_chunk_init_spare(arena_t *arena) 525{ 526 arena_chunk_t *chunk; 527 528 assert(arena->spare != NULL); 529 530 chunk = arena->spare; 531 arena->spare = NULL; 532 533 assert(arena_mapbits_allocated_get(chunk, map_bias) == 0); 534 assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0); 535 assert(arena_mapbits_unallocated_size_get(chunk, map_bias) == 536 arena_maxclass); 537 assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) == 538 arena_maxclass); 539 assert(arena_mapbits_dirty_get(chunk, map_bias) == 540 arena_mapbits_dirty_get(chunk, chunk_npages-1)); 541 542 return (chunk); 543} 544 545static arena_chunk_t * 546arena_chunk_init_hard(arena_t *arena) 547{ 548 arena_chunk_t *chunk; 549 bool zero; 550 size_t unzeroed, i; 551 552 assert(arena->spare == NULL); 553 554 zero = false; 555 malloc_mutex_unlock(&arena->lock); 556 chunk = (arena_chunk_t *)chunk_alloc(chunksize, chunksize, false, 557 &zero, arena->dss_prec); 558 malloc_mutex_lock(&arena->lock); 559 if (chunk == NULL) 560 return (NULL); 561 if (config_stats) 562 arena->stats.mapped += chunksize; 563 564 chunk->arena = arena; 565 566 /* 567 * Claim that no pages are in use, since the header is merely overhead. 568 */ 569 chunk->ndirty = 0; 570 571 chunk->nruns_avail = 0; 572 chunk->nruns_adjac = 0; 573 574 /* 575 * Initialize the map to contain one maximal free untouched run. Mark 576 * the pages as zeroed iff chunk_alloc() returned a zeroed chunk. 577 */ 578 unzeroed = zero ? 0 : CHUNK_MAP_UNZEROED; 579 arena_mapbits_unallocated_set(chunk, map_bias, arena_maxclass, 580 unzeroed); 581 /* 582 * There is no need to initialize the internal page map entries unless 583 * the chunk is not zeroed. 584 */ 585 if (zero == false) { 586 VALGRIND_MAKE_MEM_UNDEFINED((void *)arena_mapp_get(chunk, 587 map_bias+1), (size_t)((uintptr_t) arena_mapp_get(chunk, 588 chunk_npages-1) - (uintptr_t)arena_mapp_get(chunk, 589 map_bias+1))); 590 for (i = map_bias+1; i < chunk_npages-1; i++) 591 arena_mapbits_unzeroed_set(chunk, i, unzeroed); 592 } else { 593 VALGRIND_MAKE_MEM_DEFINED((void *)arena_mapp_get(chunk, 594 map_bias+1), (size_t)((uintptr_t) arena_mapp_get(chunk, 595 chunk_npages-1) - (uintptr_t)arena_mapp_get(chunk, 596 map_bias+1))); 597 if (config_debug) { 598 for (i = map_bias+1; i < chunk_npages-1; i++) { 599 assert(arena_mapbits_unzeroed_get(chunk, i) == 600 unzeroed); 601 } 602 } 603 } 604 arena_mapbits_unallocated_set(chunk, chunk_npages-1, arena_maxclass, 605 unzeroed); 606 607 return (chunk); 608} 609 610static arena_chunk_t * 611arena_chunk_alloc(arena_t *arena) 612{ 613 arena_chunk_t *chunk; 614 615 if (arena->spare != NULL) 616 chunk = arena_chunk_init_spare(arena);
| 1#define JEMALLOC_ARENA_C_ 2#include "jemalloc/internal/jemalloc_internal.h" 3 4/******************************************************************************/ 5/* Data. */ 6 7ssize_t opt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT; 8arena_bin_info_t arena_bin_info[NBINS]; 9 10JEMALLOC_ALIGNED(CACHELINE) 11const uint8_t small_size2bin[] = { 12#define S2B_8(i) i, 13#define S2B_16(i) S2B_8(i) S2B_8(i) 14#define S2B_32(i) S2B_16(i) S2B_16(i) 15#define S2B_64(i) S2B_32(i) S2B_32(i) 16#define S2B_128(i) S2B_64(i) S2B_64(i) 17#define S2B_256(i) S2B_128(i) S2B_128(i) 18#define S2B_512(i) S2B_256(i) S2B_256(i) 19#define S2B_1024(i) S2B_512(i) S2B_512(i) 20#define S2B_2048(i) S2B_1024(i) S2B_1024(i) 21#define S2B_4096(i) S2B_2048(i) S2B_2048(i) 22#define S2B_8192(i) S2B_4096(i) S2B_4096(i) 23#define SIZE_CLASS(bin, delta, size) \ 24 S2B_##delta(bin) 25 SIZE_CLASSES 26#undef S2B_8 27#undef S2B_16 28#undef S2B_32 29#undef S2B_64 30#undef S2B_128 31#undef S2B_256 32#undef S2B_512 33#undef S2B_1024 34#undef S2B_2048 35#undef S2B_4096 36#undef S2B_8192 37#undef SIZE_CLASS 38}; 39 40/******************************************************************************/ 41/* 42 * Function prototypes for static functions that are referenced prior to 43 * definition. 44 */ 45 46static void arena_purge(arena_t *arena, bool all); 47static void arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, 48 bool cleaned); 49static void arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, 50 arena_run_t *run, arena_bin_t *bin); 51static void arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, 52 arena_run_t *run, arena_bin_t *bin); 53 54/******************************************************************************/ 55 56static inline int 57arena_run_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) 58{ 59 uintptr_t a_mapelm = (uintptr_t)a; 60 uintptr_t b_mapelm = (uintptr_t)b; 61 62 assert(a != NULL); 63 assert(b != NULL); 64 65 return ((a_mapelm > b_mapelm) - (a_mapelm < b_mapelm)); 66} 67 68/* Generate red-black tree functions. */ 69rb_gen(static UNUSED, arena_run_tree_, arena_run_tree_t, arena_chunk_map_t, 70 u.rb_link, arena_run_comp) 71 72static inline int 73arena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) 74{ 75 int ret; 76 size_t a_size = a->bits & ~PAGE_MASK; 77 size_t b_size = b->bits & ~PAGE_MASK; 78 79 ret = (a_size > b_size) - (a_size < b_size); 80 if (ret == 0) { 81 uintptr_t a_mapelm, b_mapelm; 82 83 if ((a->bits & CHUNK_MAP_KEY) != CHUNK_MAP_KEY) 84 a_mapelm = (uintptr_t)a; 85 else { 86 /* 87 * Treat keys as though they are lower than anything 88 * else. 89 */ 90 a_mapelm = 0; 91 } 92 b_mapelm = (uintptr_t)b; 93 94 ret = (a_mapelm > b_mapelm) - (a_mapelm < b_mapelm); 95 } 96 97 return (ret); 98} 99 100/* Generate red-black tree functions. */ 101rb_gen(static UNUSED, arena_avail_tree_, arena_avail_tree_t, arena_chunk_map_t, 102 u.rb_link, arena_avail_comp) 103 104static inline int 105arena_chunk_dirty_comp(arena_chunk_t *a, arena_chunk_t *b) 106{ 107 108 assert(a != NULL); 109 assert(b != NULL); 110 111 /* 112 * Short-circuit for self comparison. The following comparison code 113 * would come to the same result, but at the cost of executing the slow 114 * path. 115 */ 116 if (a == b) 117 return (0); 118 119 /* 120 * Order such that chunks with higher fragmentation are "less than" 121 * those with lower fragmentation -- purging order is from "least" to 122 * "greatest". Fragmentation is measured as: 123 * 124 * mean current avail run size 125 * -------------------------------- 126 * mean defragmented avail run size 127 * 128 * navail 129 * ----------- 130 * nruns_avail nruns_avail-nruns_adjac 131 * = ========================= = ----------------------- 132 * navail nruns_avail 133 * ----------------------- 134 * nruns_avail-nruns_adjac 135 * 136 * The following code multiplies away the denominator prior to 137 * comparison, in order to avoid division. 138 * 139 */ 140 { 141 size_t a_val = (a->nruns_avail - a->nruns_adjac) * 142 b->nruns_avail; 143 size_t b_val = (b->nruns_avail - b->nruns_adjac) * 144 a->nruns_avail; 145 146 if (a_val < b_val) 147 return (1); 148 if (a_val > b_val) 149 return (-1); 150 } 151 /* 152 * Break ties by chunk address. For fragmented chunks, report lower 153 * addresses as "lower", so that fragmentation reduction happens first 154 * at lower addresses. However, use the opposite ordering for 155 * unfragmented chunks, in order to increase the chances of 156 * re-allocating dirty runs. 157 */ 158 { 159 uintptr_t a_chunk = (uintptr_t)a; 160 uintptr_t b_chunk = (uintptr_t)b; 161 int ret = ((a_chunk > b_chunk) - (a_chunk < b_chunk)); 162 if (a->nruns_adjac == 0) { 163 assert(b->nruns_adjac == 0); 164 ret = -ret; 165 } 166 return (ret); 167 } 168} 169 170/* Generate red-black tree functions. */ 171rb_gen(static UNUSED, arena_chunk_dirty_, arena_chunk_tree_t, arena_chunk_t, 172 dirty_link, arena_chunk_dirty_comp) 173 174static inline bool 175arena_avail_adjac_pred(arena_chunk_t *chunk, size_t pageind) 176{ 177 bool ret; 178 179 if (pageind-1 < map_bias) 180 ret = false; 181 else { 182 ret = (arena_mapbits_allocated_get(chunk, pageind-1) == 0); 183 assert(ret == false || arena_mapbits_dirty_get(chunk, 184 pageind-1) != arena_mapbits_dirty_get(chunk, pageind)); 185 } 186 return (ret); 187} 188 189static inline bool 190arena_avail_adjac_succ(arena_chunk_t *chunk, size_t pageind, size_t npages) 191{ 192 bool ret; 193 194 if (pageind+npages == chunk_npages) 195 ret = false; 196 else { 197 assert(pageind+npages < chunk_npages); 198 ret = (arena_mapbits_allocated_get(chunk, pageind+npages) == 0); 199 assert(ret == false || arena_mapbits_dirty_get(chunk, pageind) 200 != arena_mapbits_dirty_get(chunk, pageind+npages)); 201 } 202 return (ret); 203} 204 205static inline bool 206arena_avail_adjac(arena_chunk_t *chunk, size_t pageind, size_t npages) 207{ 208 209 return (arena_avail_adjac_pred(chunk, pageind) || 210 arena_avail_adjac_succ(chunk, pageind, npages)); 211} 212 213static void 214arena_avail_insert(arena_t *arena, arena_chunk_t *chunk, size_t pageind, 215 size_t npages, bool maybe_adjac_pred, bool maybe_adjac_succ) 216{ 217 218 assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >> 219 LG_PAGE)); 220 221 /* 222 * chunks_dirty is keyed by nruns_{avail,adjac}, so the chunk must be 223 * removed and reinserted even if the run to be inserted is clean. 224 */ 225 if (chunk->ndirty != 0) 226 arena_chunk_dirty_remove(&arena->chunks_dirty, chunk); 227 228 if (maybe_adjac_pred && arena_avail_adjac_pred(chunk, pageind)) 229 chunk->nruns_adjac++; 230 if (maybe_adjac_succ && arena_avail_adjac_succ(chunk, pageind, npages)) 231 chunk->nruns_adjac++; 232 chunk->nruns_avail++; 233 assert(chunk->nruns_avail > chunk->nruns_adjac); 234 235 if (arena_mapbits_dirty_get(chunk, pageind) != 0) { 236 arena->ndirty += npages; 237 chunk->ndirty += npages; 238 } 239 if (chunk->ndirty != 0) 240 arena_chunk_dirty_insert(&arena->chunks_dirty, chunk); 241 242 arena_avail_tree_insert(&arena->runs_avail, arena_mapp_get(chunk, 243 pageind)); 244} 245 246static void 247arena_avail_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind, 248 size_t npages, bool maybe_adjac_pred, bool maybe_adjac_succ) 249{ 250 251 assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >> 252 LG_PAGE)); 253 254 /* 255 * chunks_dirty is keyed by nruns_{avail,adjac}, so the chunk must be 256 * removed and reinserted even if the run to be removed is clean. 257 */ 258 if (chunk->ndirty != 0) 259 arena_chunk_dirty_remove(&arena->chunks_dirty, chunk); 260 261 if (maybe_adjac_pred && arena_avail_adjac_pred(chunk, pageind)) 262 chunk->nruns_adjac--; 263 if (maybe_adjac_succ && arena_avail_adjac_succ(chunk, pageind, npages)) 264 chunk->nruns_adjac--; 265 chunk->nruns_avail--; 266 assert(chunk->nruns_avail > chunk->nruns_adjac || (chunk->nruns_avail 267 == 0 && chunk->nruns_adjac == 0)); 268 269 if (arena_mapbits_dirty_get(chunk, pageind) != 0) { 270 arena->ndirty -= npages; 271 chunk->ndirty -= npages; 272 } 273 if (chunk->ndirty != 0) 274 arena_chunk_dirty_insert(&arena->chunks_dirty, chunk); 275 276 arena_avail_tree_remove(&arena->runs_avail, arena_mapp_get(chunk, 277 pageind)); 278} 279 280static inline void * 281arena_run_reg_alloc(arena_run_t *run, arena_bin_info_t *bin_info) 282{ 283 void *ret; 284 unsigned regind; 285 bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + 286 (uintptr_t)bin_info->bitmap_offset); 287 288 assert(run->nfree > 0); 289 assert(bitmap_full(bitmap, &bin_info->bitmap_info) == false); 290 291 regind = bitmap_sfu(bitmap, &bin_info->bitmap_info); 292 ret = (void *)((uintptr_t)run + (uintptr_t)bin_info->reg0_offset + 293 (uintptr_t)(bin_info->reg_interval * regind)); 294 run->nfree--; 295 if (regind == run->nextind) 296 run->nextind++; 297 assert(regind < run->nextind); 298 return (ret); 299} 300 301static inline void 302arena_run_reg_dalloc(arena_run_t *run, void *ptr) 303{ 304 arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 305 size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 306 size_t mapbits = arena_mapbits_get(chunk, pageind); 307 size_t binind = arena_ptr_small_binind_get(ptr, mapbits); 308 arena_bin_info_t *bin_info = &arena_bin_info[binind]; 309 unsigned regind = arena_run_regind(run, bin_info, ptr); 310 bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + 311 (uintptr_t)bin_info->bitmap_offset); 312 313 assert(run->nfree < bin_info->nregs); 314 /* Freeing an interior pointer can cause assertion failure. */ 315 assert(((uintptr_t)ptr - ((uintptr_t)run + 316 (uintptr_t)bin_info->reg0_offset)) % 317 (uintptr_t)bin_info->reg_interval == 0); 318 assert((uintptr_t)ptr >= (uintptr_t)run + 319 (uintptr_t)bin_info->reg0_offset); 320 /* Freeing an unallocated pointer can cause assertion failure. */ 321 assert(bitmap_get(bitmap, &bin_info->bitmap_info, regind)); 322 323 bitmap_unset(bitmap, &bin_info->bitmap_info, regind); 324 run->nfree++; 325} 326 327static inline void 328arena_run_zero(arena_chunk_t *chunk, size_t run_ind, size_t npages) 329{ 330 331 VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + (run_ind << 332 LG_PAGE)), (npages << LG_PAGE)); 333 memset((void *)((uintptr_t)chunk + (run_ind << LG_PAGE)), 0, 334 (npages << LG_PAGE)); 335} 336 337static inline void 338arena_run_page_mark_zeroed(arena_chunk_t *chunk, size_t run_ind) 339{ 340 341 VALGRIND_MAKE_MEM_DEFINED((void *)((uintptr_t)chunk + (run_ind << 342 LG_PAGE)), PAGE); 343} 344 345static inline void 346arena_run_page_validate_zeroed(arena_chunk_t *chunk, size_t run_ind) 347{ 348 size_t i; 349 UNUSED size_t *p = (size_t *)((uintptr_t)chunk + (run_ind << LG_PAGE)); 350 351 arena_run_page_mark_zeroed(chunk, run_ind); 352 for (i = 0; i < PAGE / sizeof(size_t); i++) 353 assert(p[i] == 0); 354} 355 356static void 357arena_cactive_update(arena_t *arena, size_t add_pages, size_t sub_pages) 358{ 359 360 if (config_stats) { 361 ssize_t cactive_diff = CHUNK_CEILING((arena->nactive + 362 add_pages) << LG_PAGE) - CHUNK_CEILING((arena->nactive - 363 sub_pages) << LG_PAGE); 364 if (cactive_diff != 0) 365 stats_cactive_add(cactive_diff); 366 } 367} 368 369static void 370arena_run_split_remove(arena_t *arena, arena_chunk_t *chunk, size_t run_ind, 371 size_t flag_dirty, size_t need_pages) 372{ 373 size_t total_pages, rem_pages; 374 375 total_pages = arena_mapbits_unallocated_size_get(chunk, run_ind) >> 376 LG_PAGE; 377 assert(arena_mapbits_dirty_get(chunk, run_ind+total_pages-1) == 378 flag_dirty); 379 assert(need_pages <= total_pages); 380 rem_pages = total_pages - need_pages; 381 382 arena_avail_remove(arena, chunk, run_ind, total_pages, true, true); 383 arena_cactive_update(arena, need_pages, 0); 384 arena->nactive += need_pages; 385 386 /* Keep track of trailing unused pages for later use. */ 387 if (rem_pages > 0) { 388 if (flag_dirty != 0) { 389 arena_mapbits_unallocated_set(chunk, 390 run_ind+need_pages, (rem_pages << LG_PAGE), 391 flag_dirty); 392 arena_mapbits_unallocated_set(chunk, 393 run_ind+total_pages-1, (rem_pages << LG_PAGE), 394 flag_dirty); 395 } else { 396 arena_mapbits_unallocated_set(chunk, run_ind+need_pages, 397 (rem_pages << LG_PAGE), 398 arena_mapbits_unzeroed_get(chunk, 399 run_ind+need_pages)); 400 arena_mapbits_unallocated_set(chunk, 401 run_ind+total_pages-1, (rem_pages << LG_PAGE), 402 arena_mapbits_unzeroed_get(chunk, 403 run_ind+total_pages-1)); 404 } 405 arena_avail_insert(arena, chunk, run_ind+need_pages, rem_pages, 406 false, true); 407 } 408} 409 410static void 411arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size, 412 bool remove, bool zero) 413{ 414 arena_chunk_t *chunk; 415 size_t flag_dirty, run_ind, need_pages, i; 416 417 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 418 run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); 419 flag_dirty = arena_mapbits_dirty_get(chunk, run_ind); 420 need_pages = (size >> LG_PAGE); 421 assert(need_pages > 0); 422 423 if (remove) { 424 arena_run_split_remove(arena, chunk, run_ind, flag_dirty, 425 need_pages); 426 } 427 428 if (zero) { 429 if (flag_dirty == 0) { 430 /* 431 * The run is clean, so some pages may be zeroed (i.e. 432 * never before touched). 433 */ 434 for (i = 0; i < need_pages; i++) { 435 if (arena_mapbits_unzeroed_get(chunk, run_ind+i) 436 != 0) 437 arena_run_zero(chunk, run_ind+i, 1); 438 else if (config_debug) { 439 arena_run_page_validate_zeroed(chunk, 440 run_ind+i); 441 } else { 442 arena_run_page_mark_zeroed(chunk, 443 run_ind+i); 444 } 445 } 446 } else { 447 /* The run is dirty, so all pages must be zeroed. */ 448 arena_run_zero(chunk, run_ind, need_pages); 449 } 450 } else { 451 VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + 452 (run_ind << LG_PAGE)), (need_pages << LG_PAGE)); 453 } 454 455 /* 456 * Set the last element first, in case the run only contains one page 457 * (i.e. both statements set the same element). 458 */ 459 arena_mapbits_large_set(chunk, run_ind+need_pages-1, 0, flag_dirty); 460 arena_mapbits_large_set(chunk, run_ind, size, flag_dirty); 461} 462 463static void 464arena_run_split_large(arena_t *arena, arena_run_t *run, size_t size, bool zero) 465{ 466 467 arena_run_split_large_helper(arena, run, size, true, zero); 468} 469 470static void 471arena_run_init_large(arena_t *arena, arena_run_t *run, size_t size, bool zero) 472{ 473 474 arena_run_split_large_helper(arena, run, size, false, zero); 475} 476 477static void 478arena_run_split_small(arena_t *arena, arena_run_t *run, size_t size, 479 size_t binind) 480{ 481 arena_chunk_t *chunk; 482 size_t flag_dirty, run_ind, need_pages, i; 483 484 assert(binind != BININD_INVALID); 485 486 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 487 run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); 488 flag_dirty = arena_mapbits_dirty_get(chunk, run_ind); 489 need_pages = (size >> LG_PAGE); 490 assert(need_pages > 0); 491 492 arena_run_split_remove(arena, chunk, run_ind, flag_dirty, need_pages); 493 494 /* 495 * Propagate the dirty and unzeroed flags to the allocated small run, 496 * so that arena_dalloc_bin_run() has the ability to conditionally trim 497 * clean pages. 498 */ 499 arena_mapbits_small_set(chunk, run_ind, 0, binind, flag_dirty); 500 /* 501 * The first page will always be dirtied during small run 502 * initialization, so a validation failure here would not actually 503 * cause an observable failure. 504 */ 505 if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk, 506 run_ind) == 0) 507 arena_run_page_validate_zeroed(chunk, run_ind); 508 for (i = 1; i < need_pages - 1; i++) { 509 arena_mapbits_small_set(chunk, run_ind+i, i, binind, 0); 510 if (config_debug && flag_dirty == 0 && 511 arena_mapbits_unzeroed_get(chunk, run_ind+i) == 0) 512 arena_run_page_validate_zeroed(chunk, run_ind+i); 513 } 514 arena_mapbits_small_set(chunk, run_ind+need_pages-1, need_pages-1, 515 binind, flag_dirty); 516 if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk, 517 run_ind+need_pages-1) == 0) 518 arena_run_page_validate_zeroed(chunk, run_ind+need_pages-1); 519 VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + 520 (run_ind << LG_PAGE)), (need_pages << LG_PAGE)); 521} 522 523static arena_chunk_t * 524arena_chunk_init_spare(arena_t *arena) 525{ 526 arena_chunk_t *chunk; 527 528 assert(arena->spare != NULL); 529 530 chunk = arena->spare; 531 arena->spare = NULL; 532 533 assert(arena_mapbits_allocated_get(chunk, map_bias) == 0); 534 assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0); 535 assert(arena_mapbits_unallocated_size_get(chunk, map_bias) == 536 arena_maxclass); 537 assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) == 538 arena_maxclass); 539 assert(arena_mapbits_dirty_get(chunk, map_bias) == 540 arena_mapbits_dirty_get(chunk, chunk_npages-1)); 541 542 return (chunk); 543} 544 545static arena_chunk_t * 546arena_chunk_init_hard(arena_t *arena) 547{ 548 arena_chunk_t *chunk; 549 bool zero; 550 size_t unzeroed, i; 551 552 assert(arena->spare == NULL); 553 554 zero = false; 555 malloc_mutex_unlock(&arena->lock); 556 chunk = (arena_chunk_t *)chunk_alloc(chunksize, chunksize, false, 557 &zero, arena->dss_prec); 558 malloc_mutex_lock(&arena->lock); 559 if (chunk == NULL) 560 return (NULL); 561 if (config_stats) 562 arena->stats.mapped += chunksize; 563 564 chunk->arena = arena; 565 566 /* 567 * Claim that no pages are in use, since the header is merely overhead. 568 */ 569 chunk->ndirty = 0; 570 571 chunk->nruns_avail = 0; 572 chunk->nruns_adjac = 0; 573 574 /* 575 * Initialize the map to contain one maximal free untouched run. Mark 576 * the pages as zeroed iff chunk_alloc() returned a zeroed chunk. 577 */ 578 unzeroed = zero ? 0 : CHUNK_MAP_UNZEROED; 579 arena_mapbits_unallocated_set(chunk, map_bias, arena_maxclass, 580 unzeroed); 581 /* 582 * There is no need to initialize the internal page map entries unless 583 * the chunk is not zeroed. 584 */ 585 if (zero == false) { 586 VALGRIND_MAKE_MEM_UNDEFINED((void *)arena_mapp_get(chunk, 587 map_bias+1), (size_t)((uintptr_t) arena_mapp_get(chunk, 588 chunk_npages-1) - (uintptr_t)arena_mapp_get(chunk, 589 map_bias+1))); 590 for (i = map_bias+1; i < chunk_npages-1; i++) 591 arena_mapbits_unzeroed_set(chunk, i, unzeroed); 592 } else { 593 VALGRIND_MAKE_MEM_DEFINED((void *)arena_mapp_get(chunk, 594 map_bias+1), (size_t)((uintptr_t) arena_mapp_get(chunk, 595 chunk_npages-1) - (uintptr_t)arena_mapp_get(chunk, 596 map_bias+1))); 597 if (config_debug) { 598 for (i = map_bias+1; i < chunk_npages-1; i++) { 599 assert(arena_mapbits_unzeroed_get(chunk, i) == 600 unzeroed); 601 } 602 } 603 } 604 arena_mapbits_unallocated_set(chunk, chunk_npages-1, arena_maxclass, 605 unzeroed); 606 607 return (chunk); 608} 609 610static arena_chunk_t * 611arena_chunk_alloc(arena_t *arena) 612{ 613 arena_chunk_t *chunk; 614 615 if (arena->spare != NULL) 616 chunk = arena_chunk_init_spare(arena);
|
619 620 /* Insert the run into the runs_avail tree. */ 621 arena_avail_insert(arena, chunk, map_bias, chunk_npages-map_bias, 622 false, false); 623 624 return (chunk); 625} 626 627static void 628arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) 629{ 630 assert(arena_mapbits_allocated_get(chunk, map_bias) == 0); 631 assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0); 632 assert(arena_mapbits_unallocated_size_get(chunk, map_bias) == 633 arena_maxclass); 634 assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) == 635 arena_maxclass); 636 assert(arena_mapbits_dirty_get(chunk, map_bias) == 637 arena_mapbits_dirty_get(chunk, chunk_npages-1)); 638 639 /* 640 * Remove run from the runs_avail tree, so that the arena does not use 641 * it. 642 */ 643 arena_avail_remove(arena, chunk, map_bias, chunk_npages-map_bias, 644 false, false); 645 646 if (arena->spare != NULL) { 647 arena_chunk_t *spare = arena->spare; 648 649 arena->spare = chunk; 650 malloc_mutex_unlock(&arena->lock); 651 chunk_dealloc((void *)spare, chunksize, true); 652 malloc_mutex_lock(&arena->lock); 653 if (config_stats) 654 arena->stats.mapped -= chunksize; 655 } else 656 arena->spare = chunk; 657} 658 659static arena_run_t * 660arena_run_alloc_large_helper(arena_t *arena, size_t size, bool zero) 661{ 662 arena_run_t *run; 663 arena_chunk_map_t *mapelm, key; 664 665 key.bits = size | CHUNK_MAP_KEY; 666 mapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key); 667 if (mapelm != NULL) { 668 arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); 669 size_t pageind = (((uintptr_t)mapelm - 670 (uintptr_t)run_chunk->map) / sizeof(arena_chunk_map_t)) 671 + map_bias; 672 673 run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << 674 LG_PAGE)); 675 arena_run_split_large(arena, run, size, zero); 676 return (run); 677 } 678 679 return (NULL); 680} 681 682static arena_run_t * 683arena_run_alloc_large(arena_t *arena, size_t size, bool zero) 684{ 685 arena_chunk_t *chunk; 686 arena_run_t *run; 687 688 assert(size <= arena_maxclass); 689 assert((size & PAGE_MASK) == 0); 690 691 /* Search the arena's chunks for the lowest best fit. */ 692 run = arena_run_alloc_large_helper(arena, size, zero); 693 if (run != NULL) 694 return (run); 695 696 /* 697 * No usable runs. Create a new chunk from which to allocate the run. 698 */ 699 chunk = arena_chunk_alloc(arena); 700 if (chunk != NULL) { 701 run = (arena_run_t *)((uintptr_t)chunk + (map_bias << LG_PAGE)); 702 arena_run_split_large(arena, run, size, zero); 703 return (run); 704 } 705 706 /* 707 * arena_chunk_alloc() failed, but another thread may have made 708 * sufficient memory available while this one dropped arena->lock in 709 * arena_chunk_alloc(), so search one more time. 710 */ 711 return (arena_run_alloc_large_helper(arena, size, zero)); 712} 713 714static arena_run_t * 715arena_run_alloc_small_helper(arena_t *arena, size_t size, size_t binind) 716{ 717 arena_run_t *run; 718 arena_chunk_map_t *mapelm, key; 719 720 key.bits = size | CHUNK_MAP_KEY; 721 mapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key); 722 if (mapelm != NULL) { 723 arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); 724 size_t pageind = (((uintptr_t)mapelm - 725 (uintptr_t)run_chunk->map) / sizeof(arena_chunk_map_t)) 726 + map_bias; 727 728 run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << 729 LG_PAGE)); 730 arena_run_split_small(arena, run, size, binind); 731 return (run); 732 } 733 734 return (NULL); 735} 736 737static arena_run_t * 738arena_run_alloc_small(arena_t *arena, size_t size, size_t binind) 739{ 740 arena_chunk_t *chunk; 741 arena_run_t *run; 742 743 assert(size <= arena_maxclass); 744 assert((size & PAGE_MASK) == 0); 745 assert(binind != BININD_INVALID); 746 747 /* Search the arena's chunks for the lowest best fit. */ 748 run = arena_run_alloc_small_helper(arena, size, binind); 749 if (run != NULL) 750 return (run); 751 752 /* 753 * No usable runs. Create a new chunk from which to allocate the run. 754 */ 755 chunk = arena_chunk_alloc(arena); 756 if (chunk != NULL) { 757 run = (arena_run_t *)((uintptr_t)chunk + (map_bias << LG_PAGE)); 758 arena_run_split_small(arena, run, size, binind); 759 return (run); 760 } 761 762 /* 763 * arena_chunk_alloc() failed, but another thread may have made 764 * sufficient memory available while this one dropped arena->lock in 765 * arena_chunk_alloc(), so search one more time. 766 */ 767 return (arena_run_alloc_small_helper(arena, size, binind)); 768} 769 770static inline void 771arena_maybe_purge(arena_t *arena) 772{ 773 size_t npurgeable, threshold; 774 775 /* Don't purge if the option is disabled. */ 776 if (opt_lg_dirty_mult < 0) 777 return; 778 /* Don't purge if all dirty pages are already being purged. */ 779 if (arena->ndirty <= arena->npurgatory) 780 return; 781 npurgeable = arena->ndirty - arena->npurgatory; 782 threshold = (arena->nactive >> opt_lg_dirty_mult); 783 /* 784 * Don't purge unless the number of purgeable pages exceeds the 785 * threshold. 786 */ 787 if (npurgeable <= threshold) 788 return; 789 790 arena_purge(arena, false); 791} 792 793static arena_chunk_t * 794chunks_dirty_iter_cb(arena_chunk_tree_t *tree, arena_chunk_t *chunk, void *arg) 795{ 796 size_t *ndirty = (size_t *)arg; 797 798 assert(chunk->ndirty != 0); 799 *ndirty += chunk->ndirty; 800 return (NULL); 801} 802 803static size_t 804arena_compute_npurgatory(arena_t *arena, bool all) 805{ 806 size_t npurgatory, npurgeable; 807 808 /* 809 * Compute the minimum number of pages that this thread should try to 810 * purge. 811 */ 812 npurgeable = arena->ndirty - arena->npurgatory; 813 814 if (all == false) { 815 size_t threshold = (arena->nactive >> opt_lg_dirty_mult); 816 817 npurgatory = npurgeable - threshold; 818 } else 819 npurgatory = npurgeable; 820 821 return (npurgatory); 822} 823 824static void 825arena_chunk_stash_dirty(arena_t *arena, arena_chunk_t *chunk, bool all, 826 arena_chunk_mapelms_t *mapelms) 827{ 828 size_t pageind, npages; 829 830 /* 831 * Temporarily allocate free dirty runs within chunk. If all is false, 832 * only operate on dirty runs that are fragments; otherwise operate on 833 * all dirty runs. 834 */ 835 for (pageind = map_bias; pageind < chunk_npages; pageind += npages) { 836 arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); 837 if (arena_mapbits_allocated_get(chunk, pageind) == 0) { 838 size_t run_size = 839 arena_mapbits_unallocated_size_get(chunk, pageind); 840 841 npages = run_size >> LG_PAGE; 842 assert(pageind + npages <= chunk_npages); 843 assert(arena_mapbits_dirty_get(chunk, pageind) == 844 arena_mapbits_dirty_get(chunk, pageind+npages-1)); 845 846 if (arena_mapbits_dirty_get(chunk, pageind) != 0 && 847 (all || arena_avail_adjac(chunk, pageind, 848 npages))) { 849 arena_run_t *run = (arena_run_t *)((uintptr_t) 850 chunk + (uintptr_t)(pageind << LG_PAGE)); 851 852 arena_run_split_large(arena, run, run_size, 853 false); 854 /* Append to list for later processing. */ 855 ql_elm_new(mapelm, u.ql_link); 856 ql_tail_insert(mapelms, mapelm, u.ql_link); 857 } 858 } else { 859 /* Skip run. */ 860 if (arena_mapbits_large_get(chunk, pageind) != 0) { 861 npages = arena_mapbits_large_size_get(chunk, 862 pageind) >> LG_PAGE; 863 } else { 864 size_t binind; 865 arena_bin_info_t *bin_info; 866 arena_run_t *run = (arena_run_t *)((uintptr_t) 867 chunk + (uintptr_t)(pageind << LG_PAGE)); 868 869 assert(arena_mapbits_small_runind_get(chunk, 870 pageind) == 0); 871 binind = arena_bin_index(arena, run->bin); 872 bin_info = &arena_bin_info[binind]; 873 npages = bin_info->run_size >> LG_PAGE; 874 } 875 } 876 } 877 assert(pageind == chunk_npages); 878 assert(chunk->ndirty == 0 || all == false); 879 assert(chunk->nruns_adjac == 0); 880} 881 882static size_t 883arena_chunk_purge_stashed(arena_t *arena, arena_chunk_t *chunk, 884 arena_chunk_mapelms_t *mapelms) 885{ 886 size_t npurged, pageind, npages, nmadvise; 887 arena_chunk_map_t *mapelm; 888 889 malloc_mutex_unlock(&arena->lock); 890 if (config_stats) 891 nmadvise = 0; 892 npurged = 0; 893 ql_foreach(mapelm, mapelms, u.ql_link) { 894 bool unzeroed; 895 size_t flag_unzeroed, i; 896 897 pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / 898 sizeof(arena_chunk_map_t)) + map_bias; 899 npages = arena_mapbits_large_size_get(chunk, pageind) >> 900 LG_PAGE; 901 assert(pageind + npages <= chunk_npages); 902 unzeroed = pages_purge((void *)((uintptr_t)chunk + (pageind << 903 LG_PAGE)), (npages << LG_PAGE)); 904 flag_unzeroed = unzeroed ? CHUNK_MAP_UNZEROED : 0; 905 /* 906 * Set the unzeroed flag for all pages, now that pages_purge() 907 * has returned whether the pages were zeroed as a side effect 908 * of purging. This chunk map modification is safe even though 909 * the arena mutex isn't currently owned by this thread, 910 * because the run is marked as allocated, thus protecting it 911 * from being modified by any other thread. As long as these 912 * writes don't perturb the first and last elements' 913 * CHUNK_MAP_ALLOCATED bits, behavior is well defined. 914 */ 915 for (i = 0; i < npages; i++) { 916 arena_mapbits_unzeroed_set(chunk, pageind+i, 917 flag_unzeroed); 918 } 919 npurged += npages; 920 if (config_stats) 921 nmadvise++; 922 } 923 malloc_mutex_lock(&arena->lock); 924 if (config_stats) 925 arena->stats.nmadvise += nmadvise; 926 927 return (npurged); 928} 929 930static void 931arena_chunk_unstash_purged(arena_t *arena, arena_chunk_t *chunk, 932 arena_chunk_mapelms_t *mapelms) 933{ 934 arena_chunk_map_t *mapelm; 935 size_t pageind; 936 937 /* Deallocate runs. */ 938 for (mapelm = ql_first(mapelms); mapelm != NULL; 939 mapelm = ql_first(mapelms)) { 940 arena_run_t *run; 941 942 pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / 943 sizeof(arena_chunk_map_t)) + map_bias; 944 run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)(pageind << 945 LG_PAGE)); 946 ql_remove(mapelms, mapelm, u.ql_link); 947 arena_run_dalloc(arena, run, false, true); 948 } 949} 950 951static inline size_t 952arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all) 953{ 954 size_t npurged; 955 arena_chunk_mapelms_t mapelms; 956 957 ql_new(&mapelms); 958 959 /* 960 * If chunk is the spare, temporarily re-allocate it, 1) so that its 961 * run is reinserted into runs_avail, and 2) so that it cannot be 962 * completely discarded by another thread while arena->lock is dropped 963 * by this thread. Note that the arena_run_dalloc() call will 964 * implicitly deallocate the chunk, so no explicit action is required 965 * in this function to deallocate the chunk. 966 * 967 * Note that once a chunk contains dirty pages, it cannot again contain 968 * a single run unless 1) it is a dirty run, or 2) this function purges 969 * dirty pages and causes the transition to a single clean run. Thus 970 * (chunk == arena->spare) is possible, but it is not possible for 971 * this function to be called on the spare unless it contains a dirty 972 * run. 973 */ 974 if (chunk == arena->spare) { 975 assert(arena_mapbits_dirty_get(chunk, map_bias) != 0); 976 assert(arena_mapbits_dirty_get(chunk, chunk_npages-1) != 0); 977 978 arena_chunk_alloc(arena); 979 } 980 981 if (config_stats) 982 arena->stats.purged += chunk->ndirty; 983 984 /* 985 * Operate on all dirty runs if there is no clean/dirty run 986 * fragmentation. 987 */ 988 if (chunk->nruns_adjac == 0) 989 all = true; 990 991 arena_chunk_stash_dirty(arena, chunk, all, &mapelms); 992 npurged = arena_chunk_purge_stashed(arena, chunk, &mapelms); 993 arena_chunk_unstash_purged(arena, chunk, &mapelms); 994 995 return (npurged); 996} 997 998static void 999arena_purge(arena_t *arena, bool all) 1000{ 1001 arena_chunk_t *chunk; 1002 size_t npurgatory; 1003 if (config_debug) { 1004 size_t ndirty = 0; 1005 1006 arena_chunk_dirty_iter(&arena->chunks_dirty, NULL, 1007 chunks_dirty_iter_cb, (void *)&ndirty); 1008 assert(ndirty == arena->ndirty); 1009 } 1010 assert(arena->ndirty > arena->npurgatory || all); 1011 assert((arena->nactive >> opt_lg_dirty_mult) < (arena->ndirty - 1012 arena->npurgatory) || all); 1013 1014 if (config_stats) 1015 arena->stats.npurge++; 1016 1017 /* 1018 * Add the minimum number of pages this thread should try to purge to 1019 * arena->npurgatory. This will keep multiple threads from racing to 1020 * reduce ndirty below the threshold. 1021 */ 1022 npurgatory = arena_compute_npurgatory(arena, all); 1023 arena->npurgatory += npurgatory; 1024 1025 while (npurgatory > 0) { 1026 size_t npurgeable, npurged, nunpurged; 1027 1028 /* Get next chunk with dirty pages. */ 1029 chunk = arena_chunk_dirty_first(&arena->chunks_dirty); 1030 if (chunk == NULL) { 1031 /* 1032 * This thread was unable to purge as many pages as 1033 * originally intended, due to races with other threads 1034 * that either did some of the purging work, or re-used 1035 * dirty pages. 1036 */ 1037 arena->npurgatory -= npurgatory; 1038 return; 1039 } 1040 npurgeable = chunk->ndirty; 1041 assert(npurgeable != 0); 1042 1043 if (npurgeable > npurgatory && chunk->nruns_adjac == 0) { 1044 /* 1045 * This thread will purge all the dirty pages in chunk, 1046 * so set npurgatory to reflect this thread's intent to 1047 * purge the pages. This tends to reduce the chances 1048 * of the following scenario: 1049 * 1050 * 1) This thread sets arena->npurgatory such that 1051 * (arena->ndirty - arena->npurgatory) is at the 1052 * threshold. 1053 * 2) This thread drops arena->lock. 1054 * 3) Another thread causes one or more pages to be 1055 * dirtied, and immediately determines that it must 1056 * purge dirty pages. 1057 * 1058 * If this scenario *does* play out, that's okay, 1059 * because all of the purging work being done really 1060 * needs to happen. 1061 */ 1062 arena->npurgatory += npurgeable - npurgatory; 1063 npurgatory = npurgeable; 1064 } 1065 1066 /* 1067 * Keep track of how many pages are purgeable, versus how many 1068 * actually get purged, and adjust counters accordingly. 1069 */ 1070 arena->npurgatory -= npurgeable; 1071 npurgatory -= npurgeable; 1072 npurged = arena_chunk_purge(arena, chunk, all); 1073 nunpurged = npurgeable - npurged; 1074 arena->npurgatory += nunpurged; 1075 npurgatory += nunpurged; 1076 } 1077} 1078 1079void 1080arena_purge_all(arena_t *arena) 1081{ 1082 1083 malloc_mutex_lock(&arena->lock); 1084 arena_purge(arena, true); 1085 malloc_mutex_unlock(&arena->lock); 1086} 1087 1088static void 1089arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size, 1090 size_t *p_run_ind, size_t *p_run_pages, size_t flag_dirty) 1091{ 1092 size_t size = *p_size; 1093 size_t run_ind = *p_run_ind; 1094 size_t run_pages = *p_run_pages; 1095 1096 /* Try to coalesce forward. */ 1097 if (run_ind + run_pages < chunk_npages && 1098 arena_mapbits_allocated_get(chunk, run_ind+run_pages) == 0 && 1099 arena_mapbits_dirty_get(chunk, run_ind+run_pages) == flag_dirty) { 1100 size_t nrun_size = arena_mapbits_unallocated_size_get(chunk, 1101 run_ind+run_pages); 1102 size_t nrun_pages = nrun_size >> LG_PAGE; 1103 1104 /* 1105 * Remove successor from runs_avail; the coalesced run is 1106 * inserted later. 1107 */ 1108 assert(arena_mapbits_unallocated_size_get(chunk, 1109 run_ind+run_pages+nrun_pages-1) == nrun_size); 1110 assert(arena_mapbits_dirty_get(chunk, 1111 run_ind+run_pages+nrun_pages-1) == flag_dirty); 1112 arena_avail_remove(arena, chunk, run_ind+run_pages, nrun_pages, 1113 false, true); 1114 1115 size += nrun_size; 1116 run_pages += nrun_pages; 1117 1118 arena_mapbits_unallocated_size_set(chunk, run_ind, size); 1119 arena_mapbits_unallocated_size_set(chunk, run_ind+run_pages-1, 1120 size); 1121 } 1122 1123 /* Try to coalesce backward. */ 1124 if (run_ind > map_bias && arena_mapbits_allocated_get(chunk, 1125 run_ind-1) == 0 && arena_mapbits_dirty_get(chunk, run_ind-1) == 1126 flag_dirty) { 1127 size_t prun_size = arena_mapbits_unallocated_size_get(chunk, 1128 run_ind-1); 1129 size_t prun_pages = prun_size >> LG_PAGE; 1130 1131 run_ind -= prun_pages; 1132 1133 /* 1134 * Remove predecessor from runs_avail; the coalesced run is 1135 * inserted later. 1136 */ 1137 assert(arena_mapbits_unallocated_size_get(chunk, run_ind) == 1138 prun_size); 1139 assert(arena_mapbits_dirty_get(chunk, run_ind) == flag_dirty); 1140 arena_avail_remove(arena, chunk, run_ind, prun_pages, true, 1141 false); 1142 1143 size += prun_size; 1144 run_pages += prun_pages; 1145 1146 arena_mapbits_unallocated_size_set(chunk, run_ind, size); 1147 arena_mapbits_unallocated_size_set(chunk, run_ind+run_pages-1, 1148 size); 1149 } 1150 1151 *p_size = size; 1152 *p_run_ind = run_ind; 1153 *p_run_pages = run_pages; 1154} 1155 1156static void 1157arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) 1158{ 1159 arena_chunk_t *chunk; 1160 size_t size, run_ind, run_pages, flag_dirty; 1161 1162 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1163 run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); 1164 assert(run_ind >= map_bias); 1165 assert(run_ind < chunk_npages); 1166 if (arena_mapbits_large_get(chunk, run_ind) != 0) { 1167 size = arena_mapbits_large_size_get(chunk, run_ind); 1168 assert(size == PAGE || 1169 arena_mapbits_large_size_get(chunk, 1170 run_ind+(size>>LG_PAGE)-1) == 0); 1171 } else { 1172 size_t binind = arena_bin_index(arena, run->bin); 1173 arena_bin_info_t *bin_info = &arena_bin_info[binind]; 1174 size = bin_info->run_size; 1175 } 1176 run_pages = (size >> LG_PAGE); 1177 arena_cactive_update(arena, 0, run_pages); 1178 arena->nactive -= run_pages; 1179 1180 /* 1181 * The run is dirty if the caller claims to have dirtied it, as well as 1182 * if it was already dirty before being allocated and the caller 1183 * doesn't claim to have cleaned it. 1184 */ 1185 assert(arena_mapbits_dirty_get(chunk, run_ind) == 1186 arena_mapbits_dirty_get(chunk, run_ind+run_pages-1)); 1187 if (cleaned == false && arena_mapbits_dirty_get(chunk, run_ind) != 0) 1188 dirty = true; 1189 flag_dirty = dirty ? CHUNK_MAP_DIRTY : 0; 1190 1191 /* Mark pages as unallocated in the chunk map. */ 1192 if (dirty) { 1193 arena_mapbits_unallocated_set(chunk, run_ind, size, 1194 CHUNK_MAP_DIRTY); 1195 arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size, 1196 CHUNK_MAP_DIRTY); 1197 } else { 1198 arena_mapbits_unallocated_set(chunk, run_ind, size, 1199 arena_mapbits_unzeroed_get(chunk, run_ind)); 1200 arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size, 1201 arena_mapbits_unzeroed_get(chunk, run_ind+run_pages-1)); 1202 } 1203 1204 arena_run_coalesce(arena, chunk, &size, &run_ind, &run_pages, 1205 flag_dirty); 1206 1207 /* Insert into runs_avail, now that coalescing is complete. */ 1208 assert(arena_mapbits_unallocated_size_get(chunk, run_ind) == 1209 arena_mapbits_unallocated_size_get(chunk, run_ind+run_pages-1)); 1210 assert(arena_mapbits_dirty_get(chunk, run_ind) == 1211 arena_mapbits_dirty_get(chunk, run_ind+run_pages-1)); 1212 arena_avail_insert(arena, chunk, run_ind, run_pages, true, true); 1213 1214 /* Deallocate chunk if it is now completely unused. */ 1215 if (size == arena_maxclass) { 1216 assert(run_ind == map_bias); 1217 assert(run_pages == (arena_maxclass >> LG_PAGE)); 1218 arena_chunk_dealloc(arena, chunk); 1219 } 1220 1221 /* 1222 * It is okay to do dirty page processing here even if the chunk was 1223 * deallocated above, since in that case it is the spare. Waiting 1224 * until after possible chunk deallocation to do dirty processing 1225 * allows for an old spare to be fully deallocated, thus decreasing the 1226 * chances of spuriously crossing the dirty page purging threshold. 1227 */ 1228 if (dirty) 1229 arena_maybe_purge(arena); 1230} 1231 1232static void 1233arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1234 size_t oldsize, size_t newsize) 1235{ 1236 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; 1237 size_t head_npages = (oldsize - newsize) >> LG_PAGE; 1238 size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind); 1239 1240 assert(oldsize > newsize); 1241 1242 /* 1243 * Update the chunk map so that arena_run_dalloc() can treat the 1244 * leading run as separately allocated. Set the last element of each 1245 * run first, in case of single-page runs. 1246 */ 1247 assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize); 1248 arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty); 1249 arena_mapbits_large_set(chunk, pageind, oldsize-newsize, flag_dirty); 1250 1251 if (config_debug) { 1252 UNUSED size_t tail_npages = newsize >> LG_PAGE; 1253 assert(arena_mapbits_large_size_get(chunk, 1254 pageind+head_npages+tail_npages-1) == 0); 1255 assert(arena_mapbits_dirty_get(chunk, 1256 pageind+head_npages+tail_npages-1) == flag_dirty); 1257 } 1258 arena_mapbits_large_set(chunk, pageind+head_npages, newsize, 1259 flag_dirty); 1260 1261 arena_run_dalloc(arena, run, false, false); 1262} 1263 1264static void 1265arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1266 size_t oldsize, size_t newsize, bool dirty) 1267{ 1268 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; 1269 size_t head_npages = newsize >> LG_PAGE; 1270 size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind); 1271 1272 assert(oldsize > newsize); 1273 1274 /* 1275 * Update the chunk map so that arena_run_dalloc() can treat the 1276 * trailing run as separately allocated. Set the last element of each 1277 * run first, in case of single-page runs. 1278 */ 1279 assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize); 1280 arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty); 1281 arena_mapbits_large_set(chunk, pageind, newsize, flag_dirty); 1282 1283 if (config_debug) { 1284 UNUSED size_t tail_npages = (oldsize - newsize) >> LG_PAGE; 1285 assert(arena_mapbits_large_size_get(chunk, 1286 pageind+head_npages+tail_npages-1) == 0); 1287 assert(arena_mapbits_dirty_get(chunk, 1288 pageind+head_npages+tail_npages-1) == flag_dirty); 1289 } 1290 arena_mapbits_large_set(chunk, pageind+head_npages, oldsize-newsize, 1291 flag_dirty); 1292 1293 arena_run_dalloc(arena, (arena_run_t *)((uintptr_t)run + newsize), 1294 dirty, false); 1295} 1296 1297static arena_run_t * 1298arena_bin_runs_first(arena_bin_t *bin) 1299{ 1300 arena_chunk_map_t *mapelm = arena_run_tree_first(&bin->runs); 1301 if (mapelm != NULL) { 1302 arena_chunk_t *chunk; 1303 size_t pageind; 1304 arena_run_t *run; 1305 1306 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm); 1307 pageind = ((((uintptr_t)mapelm - (uintptr_t)chunk->map) / 1308 sizeof(arena_chunk_map_t))) + map_bias; 1309 run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - 1310 arena_mapbits_small_runind_get(chunk, pageind)) << 1311 LG_PAGE)); 1312 return (run); 1313 } 1314 1315 return (NULL); 1316} 1317 1318static void 1319arena_bin_runs_insert(arena_bin_t *bin, arena_run_t *run) 1320{ 1321 arena_chunk_t *chunk = CHUNK_ADDR2BASE(run); 1322 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; 1323 arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); 1324 1325 assert(arena_run_tree_search(&bin->runs, mapelm) == NULL); 1326 1327 arena_run_tree_insert(&bin->runs, mapelm); 1328} 1329 1330static void 1331arena_bin_runs_remove(arena_bin_t *bin, arena_run_t *run) 1332{ 1333 arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1334 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; 1335 arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); 1336 1337 assert(arena_run_tree_search(&bin->runs, mapelm) != NULL); 1338 1339 arena_run_tree_remove(&bin->runs, mapelm); 1340} 1341 1342static arena_run_t * 1343arena_bin_nonfull_run_tryget(arena_bin_t *bin) 1344{ 1345 arena_run_t *run = arena_bin_runs_first(bin); 1346 if (run != NULL) { 1347 arena_bin_runs_remove(bin, run); 1348 if (config_stats) 1349 bin->stats.reruns++; 1350 } 1351 return (run); 1352} 1353 1354static arena_run_t * 1355arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) 1356{ 1357 arena_run_t *run; 1358 size_t binind; 1359 arena_bin_info_t *bin_info; 1360 1361 /* Look for a usable run. */ 1362 run = arena_bin_nonfull_run_tryget(bin); 1363 if (run != NULL) 1364 return (run); 1365 /* No existing runs have any space available. */ 1366 1367 binind = arena_bin_index(arena, bin); 1368 bin_info = &arena_bin_info[binind]; 1369 1370 /* Allocate a new run. */ 1371 malloc_mutex_unlock(&bin->lock); 1372 /******************************/ 1373 malloc_mutex_lock(&arena->lock); 1374 run = arena_run_alloc_small(arena, bin_info->run_size, binind); 1375 if (run != NULL) { 1376 bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + 1377 (uintptr_t)bin_info->bitmap_offset); 1378 1379 /* Initialize run internals. */ 1380 run->bin = bin; 1381 run->nextind = 0; 1382 run->nfree = bin_info->nregs; 1383 bitmap_init(bitmap, &bin_info->bitmap_info); 1384 } 1385 malloc_mutex_unlock(&arena->lock); 1386 /********************************/ 1387 malloc_mutex_lock(&bin->lock); 1388 if (run != NULL) { 1389 if (config_stats) { 1390 bin->stats.nruns++; 1391 bin->stats.curruns++; 1392 } 1393 return (run); 1394 } 1395 1396 /* 1397 * arena_run_alloc_small() failed, but another thread may have made 1398 * sufficient memory available while this one dropped bin->lock above, 1399 * so search one more time. 1400 */ 1401 run = arena_bin_nonfull_run_tryget(bin); 1402 if (run != NULL) 1403 return (run); 1404 1405 return (NULL); 1406} 1407 1408/* Re-fill bin->runcur, then call arena_run_reg_alloc(). */ 1409static void * 1410arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin) 1411{ 1412 void *ret; 1413 size_t binind; 1414 arena_bin_info_t *bin_info; 1415 arena_run_t *run; 1416 1417 binind = arena_bin_index(arena, bin); 1418 bin_info = &arena_bin_info[binind]; 1419 bin->runcur = NULL; 1420 run = arena_bin_nonfull_run_get(arena, bin); 1421 if (bin->runcur != NULL && bin->runcur->nfree > 0) { 1422 /* 1423 * Another thread updated runcur while this one ran without the 1424 * bin lock in arena_bin_nonfull_run_get(). 1425 */ 1426 assert(bin->runcur->nfree > 0); 1427 ret = arena_run_reg_alloc(bin->runcur, bin_info); 1428 if (run != NULL) { 1429 arena_chunk_t *chunk; 1430 1431 /* 1432 * arena_run_alloc_small() may have allocated run, or 1433 * it may have pulled run from the bin's run tree. 1434 * Therefore it is unsafe to make any assumptions about 1435 * how run has previously been used, and 1436 * arena_bin_lower_run() must be called, as if a region 1437 * were just deallocated from the run. 1438 */ 1439 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1440 if (run->nfree == bin_info->nregs) 1441 arena_dalloc_bin_run(arena, chunk, run, bin); 1442 else 1443 arena_bin_lower_run(arena, chunk, run, bin); 1444 } 1445 return (ret); 1446 } 1447 1448 if (run == NULL) 1449 return (NULL); 1450 1451 bin->runcur = run; 1452 1453 assert(bin->runcur->nfree > 0); 1454 1455 return (arena_run_reg_alloc(bin->runcur, bin_info)); 1456} 1457 1458void 1459arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind, 1460 uint64_t prof_accumbytes) 1461{ 1462 unsigned i, nfill; 1463 arena_bin_t *bin; 1464 arena_run_t *run; 1465 void *ptr; 1466 1467 assert(tbin->ncached == 0); 1468 1469 if (config_prof && arena_prof_accum(arena, prof_accumbytes)) 1470 prof_idump(); 1471 bin = &arena->bins[binind]; 1472 malloc_mutex_lock(&bin->lock); 1473 for (i = 0, nfill = (tcache_bin_info[binind].ncached_max >> 1474 tbin->lg_fill_div); i < nfill; i++) { 1475 if ((run = bin->runcur) != NULL && run->nfree > 0) 1476 ptr = arena_run_reg_alloc(run, &arena_bin_info[binind]); 1477 else 1478 ptr = arena_bin_malloc_hard(arena, bin); 1479 if (ptr == NULL) 1480 break; 1481 if (config_fill && opt_junk) { 1482 arena_alloc_junk_small(ptr, &arena_bin_info[binind], 1483 true); 1484 } 1485 /* Insert such that low regions get used first. */ 1486 tbin->avail[nfill - 1 - i] = ptr; 1487 } 1488 if (config_stats) { 1489 bin->stats.allocated += i * arena_bin_info[binind].reg_size; 1490 bin->stats.nmalloc += i; 1491 bin->stats.nrequests += tbin->tstats.nrequests; 1492 bin->stats.nfills++; 1493 tbin->tstats.nrequests = 0; 1494 } 1495 malloc_mutex_unlock(&bin->lock); 1496 tbin->ncached = i; 1497} 1498 1499void 1500arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info, bool zero) 1501{ 1502 1503 if (zero) { 1504 size_t redzone_size = bin_info->redzone_size; 1505 memset((void *)((uintptr_t)ptr - redzone_size), 0xa5, 1506 redzone_size); 1507 memset((void *)((uintptr_t)ptr + bin_info->reg_size), 0xa5, 1508 redzone_size); 1509 } else { 1510 memset((void *)((uintptr_t)ptr - bin_info->redzone_size), 0xa5, 1511 bin_info->reg_interval); 1512 } 1513} 1514 1515#ifdef JEMALLOC_JET 1516#undef arena_redzone_corruption 1517#define arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption_impl) 1518#endif 1519static void 1520arena_redzone_corruption(void *ptr, size_t usize, bool after, 1521 size_t offset, uint8_t byte) 1522{ 1523 1524 malloc_printf("<jemalloc>: Corrupt redzone %zu byte%s %s %p " 1525 "(size %zu), byte=%#x\n", offset, (offset == 1) ? "" : "s", 1526 after ? "after" : "before", ptr, usize, byte); 1527} 1528#ifdef JEMALLOC_JET 1529#undef arena_redzone_corruption 1530#define arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption) 1531arena_redzone_corruption_t *arena_redzone_corruption = 1532 JEMALLOC_N(arena_redzone_corruption_impl); 1533#endif 1534 1535static void 1536arena_redzones_validate(void *ptr, arena_bin_info_t *bin_info, bool reset) 1537{ 1538 size_t size = bin_info->reg_size; 1539 size_t redzone_size = bin_info->redzone_size; 1540 size_t i; 1541 bool error = false; 1542 1543 for (i = 1; i <= redzone_size; i++) { 1544 uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i); 1545 if (*byte != 0xa5) { 1546 error = true; 1547 arena_redzone_corruption(ptr, size, false, i, *byte); 1548 if (reset) 1549 *byte = 0xa5; 1550 } 1551 } 1552 for (i = 0; i < redzone_size; i++) { 1553 uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i); 1554 if (*byte != 0xa5) { 1555 error = true; 1556 arena_redzone_corruption(ptr, size, true, i, *byte); 1557 if (reset) 1558 *byte = 0xa5; 1559 } 1560 } 1561 if (opt_abort && error) 1562 abort(); 1563} 1564 1565#ifdef JEMALLOC_JET 1566#undef arena_dalloc_junk_small 1567#define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small_impl) 1568#endif 1569void 1570arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info) 1571{ 1572 size_t redzone_size = bin_info->redzone_size; 1573 1574 arena_redzones_validate(ptr, bin_info, false); 1575 memset((void *)((uintptr_t)ptr - redzone_size), 0x5a, 1576 bin_info->reg_interval); 1577} 1578#ifdef JEMALLOC_JET 1579#undef arena_dalloc_junk_small 1580#define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small) 1581arena_dalloc_junk_small_t *arena_dalloc_junk_small = 1582 JEMALLOC_N(arena_dalloc_junk_small_impl); 1583#endif 1584 1585void 1586arena_quarantine_junk_small(void *ptr, size_t usize) 1587{ 1588 size_t binind; 1589 arena_bin_info_t *bin_info; 1590 cassert(config_fill); 1591 assert(opt_junk); 1592 assert(opt_quarantine); 1593 assert(usize <= SMALL_MAXCLASS); 1594 1595 binind = SMALL_SIZE2BIN(usize); 1596 bin_info = &arena_bin_info[binind]; 1597 arena_redzones_validate(ptr, bin_info, true); 1598} 1599 1600void * 1601arena_malloc_small(arena_t *arena, size_t size, bool zero) 1602{ 1603 void *ret; 1604 arena_bin_t *bin; 1605 arena_run_t *run; 1606 size_t binind; 1607 1608 binind = SMALL_SIZE2BIN(size); 1609 assert(binind < NBINS); 1610 bin = &arena->bins[binind]; 1611 size = arena_bin_info[binind].reg_size; 1612 1613 malloc_mutex_lock(&bin->lock); 1614 if ((run = bin->runcur) != NULL && run->nfree > 0) 1615 ret = arena_run_reg_alloc(run, &arena_bin_info[binind]); 1616 else 1617 ret = arena_bin_malloc_hard(arena, bin); 1618 1619 if (ret == NULL) { 1620 malloc_mutex_unlock(&bin->lock); 1621 return (NULL); 1622 } 1623 1624 if (config_stats) { 1625 bin->stats.allocated += size; 1626 bin->stats.nmalloc++; 1627 bin->stats.nrequests++; 1628 } 1629 malloc_mutex_unlock(&bin->lock); 1630 if (config_prof && isthreaded == false && arena_prof_accum(arena, size)) 1631 prof_idump(); 1632 1633 if (zero == false) { 1634 if (config_fill) { 1635 if (opt_junk) { 1636 arena_alloc_junk_small(ret, 1637 &arena_bin_info[binind], false); 1638 } else if (opt_zero) 1639 memset(ret, 0, size); 1640 } 1641 VALGRIND_MAKE_MEM_UNDEFINED(ret, size); 1642 } else { 1643 if (config_fill && opt_junk) { 1644 arena_alloc_junk_small(ret, &arena_bin_info[binind], 1645 true); 1646 } 1647 VALGRIND_MAKE_MEM_UNDEFINED(ret, size); 1648 memset(ret, 0, size); 1649 } 1650 1651 return (ret); 1652} 1653 1654void * 1655arena_malloc_large(arena_t *arena, size_t size, bool zero) 1656{ 1657 void *ret; 1658 UNUSED bool idump; 1659 1660 /* Large allocation. */ 1661 size = PAGE_CEILING(size); 1662 malloc_mutex_lock(&arena->lock); 1663 ret = (void *)arena_run_alloc_large(arena, size, zero); 1664 if (ret == NULL) { 1665 malloc_mutex_unlock(&arena->lock); 1666 return (NULL); 1667 } 1668 if (config_stats) { 1669 arena->stats.nmalloc_large++; 1670 arena->stats.nrequests_large++; 1671 arena->stats.allocated_large += size; 1672 arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; 1673 arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; 1674 arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; 1675 } 1676 if (config_prof) 1677 idump = arena_prof_accum_locked(arena, size); 1678 malloc_mutex_unlock(&arena->lock); 1679 if (config_prof && idump) 1680 prof_idump(); 1681 1682 if (zero == false) { 1683 if (config_fill) { 1684 if (opt_junk) 1685 memset(ret, 0xa5, size); 1686 else if (opt_zero) 1687 memset(ret, 0, size); 1688 } 1689 } 1690 1691 return (ret); 1692} 1693 1694/* Only handles large allocations that require more than page alignment. */ 1695void * 1696arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero) 1697{ 1698 void *ret; 1699 size_t alloc_size, leadsize, trailsize; 1700 arena_run_t *run; 1701 arena_chunk_t *chunk; 1702 1703 assert((size & PAGE_MASK) == 0); 1704 1705 alignment = PAGE_CEILING(alignment); 1706 alloc_size = size + alignment - PAGE; 1707 1708 malloc_mutex_lock(&arena->lock); 1709 run = arena_run_alloc_large(arena, alloc_size, false); 1710 if (run == NULL) { 1711 malloc_mutex_unlock(&arena->lock); 1712 return (NULL); 1713 } 1714 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1715 1716 leadsize = ALIGNMENT_CEILING((uintptr_t)run, alignment) - 1717 (uintptr_t)run; 1718 assert(alloc_size >= leadsize + size); 1719 trailsize = alloc_size - leadsize - size; 1720 ret = (void *)((uintptr_t)run + leadsize); 1721 if (leadsize != 0) { 1722 arena_run_trim_head(arena, chunk, run, alloc_size, alloc_size - 1723 leadsize); 1724 } 1725 if (trailsize != 0) { 1726 arena_run_trim_tail(arena, chunk, ret, size + trailsize, size, 1727 false); 1728 } 1729 arena_run_init_large(arena, (arena_run_t *)ret, size, zero); 1730 1731 if (config_stats) { 1732 arena->stats.nmalloc_large++; 1733 arena->stats.nrequests_large++; 1734 arena->stats.allocated_large += size; 1735 arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; 1736 arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; 1737 arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; 1738 } 1739 malloc_mutex_unlock(&arena->lock); 1740 1741 if (config_fill && zero == false) { 1742 if (opt_junk) 1743 memset(ret, 0xa5, size); 1744 else if (opt_zero) 1745 memset(ret, 0, size); 1746 } 1747 return (ret); 1748} 1749 1750void 1751arena_prof_promoted(const void *ptr, size_t size) 1752{ 1753 arena_chunk_t *chunk; 1754 size_t pageind, binind; 1755 1756 cassert(config_prof); 1757 assert(ptr != NULL); 1758 assert(CHUNK_ADDR2BASE(ptr) != ptr); 1759 assert(isalloc(ptr, false) == PAGE); 1760 assert(isalloc(ptr, true) == PAGE); 1761 assert(size <= SMALL_MAXCLASS); 1762 1763 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 1764 pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 1765 binind = SMALL_SIZE2BIN(size); 1766 assert(binind < NBINS); 1767 arena_mapbits_large_binind_set(chunk, pageind, binind); 1768 1769 assert(isalloc(ptr, false) == PAGE); 1770 assert(isalloc(ptr, true) == size); 1771} 1772 1773static void 1774arena_dissociate_bin_run(arena_chunk_t *chunk, arena_run_t *run, 1775 arena_bin_t *bin) 1776{ 1777 1778 /* Dissociate run from bin. */ 1779 if (run == bin->runcur) 1780 bin->runcur = NULL; 1781 else { 1782 size_t binind = arena_bin_index(chunk->arena, bin); 1783 arena_bin_info_t *bin_info = &arena_bin_info[binind]; 1784 1785 if (bin_info->nregs != 1) { 1786 /* 1787 * This block's conditional is necessary because if the 1788 * run only contains one region, then it never gets 1789 * inserted into the non-full runs tree. 1790 */ 1791 arena_bin_runs_remove(bin, run); 1792 } 1793 } 1794} 1795 1796static void 1797arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1798 arena_bin_t *bin) 1799{ 1800 size_t binind; 1801 arena_bin_info_t *bin_info; 1802 size_t npages, run_ind, past; 1803 1804 assert(run != bin->runcur); 1805 assert(arena_run_tree_search(&bin->runs, 1806 arena_mapp_get(chunk, ((uintptr_t)run-(uintptr_t)chunk)>>LG_PAGE)) 1807 == NULL); 1808 1809 binind = arena_bin_index(chunk->arena, run->bin); 1810 bin_info = &arena_bin_info[binind]; 1811 1812 malloc_mutex_unlock(&bin->lock); 1813 /******************************/ 1814 npages = bin_info->run_size >> LG_PAGE; 1815 run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); 1816 past = (size_t)(PAGE_CEILING((uintptr_t)run + 1817 (uintptr_t)bin_info->reg0_offset + (uintptr_t)(run->nextind * 1818 bin_info->reg_interval - bin_info->redzone_size) - 1819 (uintptr_t)chunk) >> LG_PAGE); 1820 malloc_mutex_lock(&arena->lock); 1821 1822 /* 1823 * If the run was originally clean, and some pages were never touched, 1824 * trim the clean pages before deallocating the dirty portion of the 1825 * run. 1826 */ 1827 assert(arena_mapbits_dirty_get(chunk, run_ind) == 1828 arena_mapbits_dirty_get(chunk, run_ind+npages-1)); 1829 if (arena_mapbits_dirty_get(chunk, run_ind) == 0 && past - run_ind < 1830 npages) { 1831 /* Trim clean pages. Convert to large run beforehand. */ 1832 assert(npages > 0); 1833 arena_mapbits_large_set(chunk, run_ind, bin_info->run_size, 0); 1834 arena_mapbits_large_set(chunk, run_ind+npages-1, 0, 0); 1835 arena_run_trim_tail(arena, chunk, run, (npages << LG_PAGE), 1836 ((past - run_ind) << LG_PAGE), false); 1837 /* npages = past - run_ind; */ 1838 } 1839 arena_run_dalloc(arena, run, true, false); 1840 malloc_mutex_unlock(&arena->lock); 1841 /****************************/ 1842 malloc_mutex_lock(&bin->lock); 1843 if (config_stats) 1844 bin->stats.curruns--; 1845} 1846 1847static void 1848arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1849 arena_bin_t *bin) 1850{ 1851 1852 /* 1853 * Make sure that if bin->runcur is non-NULL, it refers to the lowest 1854 * non-full run. It is okay to NULL runcur out rather than proactively 1855 * keeping it pointing at the lowest non-full run. 1856 */ 1857 if ((uintptr_t)run < (uintptr_t)bin->runcur) { 1858 /* Switch runcur. */ 1859 if (bin->runcur->nfree > 0) 1860 arena_bin_runs_insert(bin, bin->runcur); 1861 bin->runcur = run; 1862 if (config_stats) 1863 bin->stats.reruns++; 1864 } else 1865 arena_bin_runs_insert(bin, run); 1866} 1867 1868void 1869arena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1870 arena_chunk_map_t *mapelm) 1871{ 1872 size_t pageind; 1873 arena_run_t *run; 1874 arena_bin_t *bin; 1875 arena_bin_info_t *bin_info; 1876 size_t size, binind; 1877 1878 pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 1879 run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - 1880 arena_mapbits_small_runind_get(chunk, pageind)) << LG_PAGE)); 1881 bin = run->bin; 1882 binind = arena_ptr_small_binind_get(ptr, mapelm->bits); 1883 bin_info = &arena_bin_info[binind]; 1884 if (config_fill || config_stats) 1885 size = bin_info->reg_size; 1886 1887 if (config_fill && opt_junk) 1888 arena_dalloc_junk_small(ptr, bin_info); 1889 1890 arena_run_reg_dalloc(run, ptr); 1891 if (run->nfree == bin_info->nregs) { 1892 arena_dissociate_bin_run(chunk, run, bin); 1893 arena_dalloc_bin_run(arena, chunk, run, bin); 1894 } else if (run->nfree == 1 && run != bin->runcur) 1895 arena_bin_lower_run(arena, chunk, run, bin); 1896 1897 if (config_stats) { 1898 bin->stats.allocated -= size; 1899 bin->stats.ndalloc++; 1900 } 1901} 1902 1903void 1904arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1905 size_t pageind, arena_chunk_map_t *mapelm) 1906{ 1907 arena_run_t *run; 1908 arena_bin_t *bin; 1909 1910 run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - 1911 arena_mapbits_small_runind_get(chunk, pageind)) << LG_PAGE)); 1912 bin = run->bin; 1913 malloc_mutex_lock(&bin->lock); 1914 arena_dalloc_bin_locked(arena, chunk, ptr, mapelm); 1915 malloc_mutex_unlock(&bin->lock); 1916} 1917 1918void 1919arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1920 size_t pageind) 1921{ 1922 arena_chunk_map_t *mapelm; 1923 1924 if (config_debug) { 1925 /* arena_ptr_small_binind_get() does extra sanity checking. */ 1926 assert(arena_ptr_small_binind_get(ptr, arena_mapbits_get(chunk, 1927 pageind)) != BININD_INVALID); 1928 } 1929 mapelm = arena_mapp_get(chunk, pageind); 1930 arena_dalloc_bin(arena, chunk, ptr, pageind, mapelm); 1931} 1932 1933#ifdef JEMALLOC_JET 1934#undef arena_dalloc_junk_large 1935#define arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large_impl) 1936#endif 1937static void 1938arena_dalloc_junk_large(void *ptr, size_t usize) 1939{ 1940 1941 if (config_fill && opt_junk) 1942 memset(ptr, 0x5a, usize); 1943} 1944#ifdef JEMALLOC_JET 1945#undef arena_dalloc_junk_large 1946#define arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large) 1947arena_dalloc_junk_large_t *arena_dalloc_junk_large = 1948 JEMALLOC_N(arena_dalloc_junk_large_impl); 1949#endif 1950 1951void 1952arena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr) 1953{ 1954 1955 if (config_fill || config_stats) { 1956 size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 1957 size_t usize = arena_mapbits_large_size_get(chunk, pageind); 1958 1959 arena_dalloc_junk_large(ptr, usize); 1960 if (config_stats) { 1961 arena->stats.ndalloc_large++; 1962 arena->stats.allocated_large -= usize; 1963 arena->stats.lstats[(usize >> LG_PAGE) - 1].ndalloc++; 1964 arena->stats.lstats[(usize >> LG_PAGE) - 1].curruns--; 1965 } 1966 } 1967 1968 arena_run_dalloc(arena, (arena_run_t *)ptr, true, false); 1969} 1970 1971void 1972arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr) 1973{ 1974 1975 malloc_mutex_lock(&arena->lock); 1976 arena_dalloc_large_locked(arena, chunk, ptr); 1977 malloc_mutex_unlock(&arena->lock); 1978} 1979 1980static void 1981arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1982 size_t oldsize, size_t size) 1983{ 1984 1985 assert(size < oldsize); 1986 1987 /* 1988 * Shrink the run, and make trailing pages available for other 1989 * allocations. 1990 */ 1991 malloc_mutex_lock(&arena->lock); 1992 arena_run_trim_tail(arena, chunk, (arena_run_t *)ptr, oldsize, size, 1993 true); 1994 if (config_stats) { 1995 arena->stats.ndalloc_large++; 1996 arena->stats.allocated_large -= oldsize; 1997 arena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++; 1998 arena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--; 1999 2000 arena->stats.nmalloc_large++; 2001 arena->stats.nrequests_large++; 2002 arena->stats.allocated_large += size; 2003 arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; 2004 arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; 2005 arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; 2006 } 2007 malloc_mutex_unlock(&arena->lock); 2008} 2009 2010static bool 2011arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, 2012 size_t oldsize, size_t size, size_t extra, bool zero) 2013{ 2014 size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 2015 size_t npages = oldsize >> LG_PAGE; 2016 size_t followsize; 2017 2018 assert(oldsize == arena_mapbits_large_size_get(chunk, pageind)); 2019 2020 /* Try to extend the run. */ 2021 assert(size + extra > oldsize); 2022 malloc_mutex_lock(&arena->lock); 2023 if (pageind + npages < chunk_npages && 2024 arena_mapbits_allocated_get(chunk, pageind+npages) == 0 && 2025 (followsize = arena_mapbits_unallocated_size_get(chunk, 2026 pageind+npages)) >= size - oldsize) { 2027 /* 2028 * The next run is available and sufficiently large. Split the 2029 * following run, then merge the first part with the existing 2030 * allocation. 2031 */ 2032 size_t flag_dirty; 2033 size_t splitsize = (oldsize + followsize <= size + extra) 2034 ? followsize : size + extra - oldsize; 2035 arena_run_split_large(arena, (arena_run_t *)((uintptr_t)chunk + 2036 ((pageind+npages) << LG_PAGE)), splitsize, zero); 2037 2038 size = oldsize + splitsize; 2039 npages = size >> LG_PAGE; 2040 2041 /* 2042 * Mark the extended run as dirty if either portion of the run 2043 * was dirty before allocation. This is rather pedantic, 2044 * because there's not actually any sequence of events that 2045 * could cause the resulting run to be passed to 2046 * arena_run_dalloc() with the dirty argument set to false 2047 * (which is when dirty flag consistency would really matter). 2048 */ 2049 flag_dirty = arena_mapbits_dirty_get(chunk, pageind) | 2050 arena_mapbits_dirty_get(chunk, pageind+npages-1); 2051 arena_mapbits_large_set(chunk, pageind, size, flag_dirty); 2052 arena_mapbits_large_set(chunk, pageind+npages-1, 0, flag_dirty); 2053 2054 if (config_stats) { 2055 arena->stats.ndalloc_large++; 2056 arena->stats.allocated_large -= oldsize; 2057 arena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++; 2058 arena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--; 2059 2060 arena->stats.nmalloc_large++; 2061 arena->stats.nrequests_large++; 2062 arena->stats.allocated_large += size; 2063 arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; 2064 arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; 2065 arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; 2066 } 2067 malloc_mutex_unlock(&arena->lock); 2068 return (false); 2069 } 2070 malloc_mutex_unlock(&arena->lock); 2071 2072 return (true); 2073} 2074 2075#ifdef JEMALLOC_JET 2076#undef arena_ralloc_junk_large 2077#define arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large_impl) 2078#endif 2079static void 2080arena_ralloc_junk_large(void *ptr, size_t old_usize, size_t usize) 2081{ 2082 2083 if (config_fill && opt_junk) { 2084 memset((void *)((uintptr_t)ptr + usize), 0x5a, 2085 old_usize - usize); 2086 } 2087} 2088#ifdef JEMALLOC_JET 2089#undef arena_ralloc_junk_large 2090#define arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large) 2091arena_ralloc_junk_large_t *arena_ralloc_junk_large = 2092 JEMALLOC_N(arena_ralloc_junk_large_impl); 2093#endif 2094 2095/* 2096 * Try to resize a large allocation, in order to avoid copying. This will 2097 * always fail if growing an object, and the following run is already in use. 2098 */ 2099static bool 2100arena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra, 2101 bool zero) 2102{ 2103 size_t psize; 2104 2105 psize = PAGE_CEILING(size + extra); 2106 if (psize == oldsize) { 2107 /* Same size class. */ 2108 return (false); 2109 } else { 2110 arena_chunk_t *chunk; 2111 arena_t *arena; 2112 2113 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 2114 arena = chunk->arena; 2115 2116 if (psize < oldsize) { 2117 /* Fill before shrinking in order avoid a race. */ 2118 arena_ralloc_junk_large(ptr, oldsize, psize); 2119 arena_ralloc_large_shrink(arena, chunk, ptr, oldsize, 2120 psize); 2121 return (false); 2122 } else { 2123 bool ret = arena_ralloc_large_grow(arena, chunk, ptr, 2124 oldsize, PAGE_CEILING(size), 2125 psize - PAGE_CEILING(size), zero); 2126 if (config_fill && ret == false && zero == false) { 2127 if (opt_junk) { 2128 memset((void *)((uintptr_t)ptr + 2129 oldsize), 0xa5, isalloc(ptr, 2130 config_prof) - oldsize); 2131 } else if (opt_zero) { 2132 memset((void *)((uintptr_t)ptr + 2133 oldsize), 0, isalloc(ptr, 2134 config_prof) - oldsize); 2135 } 2136 } 2137 return (ret); 2138 } 2139 } 2140} 2141 2142bool 2143arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra, 2144 bool zero) 2145{ 2146 2147 /* 2148 * Avoid moving the allocation if the size class can be left the same. 2149 */ 2150 if (oldsize <= arena_maxclass) { 2151 if (oldsize <= SMALL_MAXCLASS) { 2152 assert(arena_bin_info[SMALL_SIZE2BIN(oldsize)].reg_size 2153 == oldsize); 2154 if ((size + extra <= SMALL_MAXCLASS && 2155 SMALL_SIZE2BIN(size + extra) == 2156 SMALL_SIZE2BIN(oldsize)) || (size <= oldsize && 2157 size + extra >= oldsize)) 2158 return (false); 2159 } else { 2160 assert(size <= arena_maxclass); 2161 if (size + extra > SMALL_MAXCLASS) { 2162 if (arena_ralloc_large(ptr, oldsize, size, 2163 extra, zero) == false) 2164 return (false); 2165 } 2166 } 2167 } 2168 2169 /* Reallocation would require a move. */ 2170 return (true); 2171} 2172 2173void * 2174arena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size, 2175 size_t extra, size_t alignment, bool zero, bool try_tcache_alloc, 2176 bool try_tcache_dalloc) 2177{ 2178 void *ret; 2179 size_t copysize; 2180 2181 /* Try to avoid moving the allocation. */ 2182 if (arena_ralloc_no_move(ptr, oldsize, size, extra, zero) == false) 2183 return (ptr); 2184 2185 /* 2186 * size and oldsize are different enough that we need to move the 2187 * object. In that case, fall back to allocating new space and 2188 * copying. 2189 */ 2190 if (alignment != 0) { 2191 size_t usize = sa2u(size + extra, alignment); 2192 if (usize == 0) 2193 return (NULL); 2194 ret = ipalloct(usize, alignment, zero, try_tcache_alloc, arena); 2195 } else 2196 ret = arena_malloc(arena, size + extra, zero, try_tcache_alloc); 2197 2198 if (ret == NULL) { 2199 if (extra == 0) 2200 return (NULL); 2201 /* Try again, this time without extra. */ 2202 if (alignment != 0) { 2203 size_t usize = sa2u(size, alignment); 2204 if (usize == 0) 2205 return (NULL); 2206 ret = ipalloct(usize, alignment, zero, try_tcache_alloc, 2207 arena); 2208 } else 2209 ret = arena_malloc(arena, size, zero, try_tcache_alloc); 2210 2211 if (ret == NULL) 2212 return (NULL); 2213 } 2214 2215 /* Junk/zero-filling were already done by ipalloc()/arena_malloc(). */ 2216 2217 /* 2218 * Copy at most size bytes (not size+extra), since the caller has no 2219 * expectation that the extra bytes will be reliably preserved. 2220 */ 2221 copysize = (size < oldsize) ? size : oldsize; 2222 VALGRIND_MAKE_MEM_UNDEFINED(ret, copysize); 2223 memcpy(ret, ptr, copysize); 2224 iqalloct(ptr, try_tcache_dalloc); 2225 return (ret); 2226} 2227 2228dss_prec_t 2229arena_dss_prec_get(arena_t *arena) 2230{ 2231 dss_prec_t ret; 2232 2233 malloc_mutex_lock(&arena->lock); 2234 ret = arena->dss_prec; 2235 malloc_mutex_unlock(&arena->lock); 2236 return (ret); 2237} 2238 2239void 2240arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec) 2241{ 2242 2243 malloc_mutex_lock(&arena->lock); 2244 arena->dss_prec = dss_prec; 2245 malloc_mutex_unlock(&arena->lock); 2246} 2247 2248void 2249arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive, 2250 size_t *ndirty, arena_stats_t *astats, malloc_bin_stats_t *bstats, 2251 malloc_large_stats_t *lstats) 2252{ 2253 unsigned i; 2254 2255 malloc_mutex_lock(&arena->lock); 2256 *dss = dss_prec_names[arena->dss_prec]; 2257 *nactive += arena->nactive; 2258 *ndirty += arena->ndirty; 2259 2260 astats->mapped += arena->stats.mapped; 2261 astats->npurge += arena->stats.npurge; 2262 astats->nmadvise += arena->stats.nmadvise; 2263 astats->purged += arena->stats.purged; 2264 astats->allocated_large += arena->stats.allocated_large; 2265 astats->nmalloc_large += arena->stats.nmalloc_large; 2266 astats->ndalloc_large += arena->stats.ndalloc_large; 2267 astats->nrequests_large += arena->stats.nrequests_large; 2268 2269 for (i = 0; i < nlclasses; i++) { 2270 lstats[i].nmalloc += arena->stats.lstats[i].nmalloc; 2271 lstats[i].ndalloc += arena->stats.lstats[i].ndalloc; 2272 lstats[i].nrequests += arena->stats.lstats[i].nrequests; 2273 lstats[i].curruns += arena->stats.lstats[i].curruns; 2274 } 2275 malloc_mutex_unlock(&arena->lock); 2276 2277 for (i = 0; i < NBINS; i++) { 2278 arena_bin_t *bin = &arena->bins[i]; 2279 2280 malloc_mutex_lock(&bin->lock); 2281 bstats[i].allocated += bin->stats.allocated; 2282 bstats[i].nmalloc += bin->stats.nmalloc; 2283 bstats[i].ndalloc += bin->stats.ndalloc; 2284 bstats[i].nrequests += bin->stats.nrequests; 2285 if (config_tcache) { 2286 bstats[i].nfills += bin->stats.nfills; 2287 bstats[i].nflushes += bin->stats.nflushes; 2288 } 2289 bstats[i].nruns += bin->stats.nruns; 2290 bstats[i].reruns += bin->stats.reruns; 2291 bstats[i].curruns += bin->stats.curruns; 2292 malloc_mutex_unlock(&bin->lock); 2293 } 2294} 2295 2296bool 2297arena_new(arena_t *arena, unsigned ind) 2298{ 2299 unsigned i; 2300 arena_bin_t *bin; 2301 2302 arena->ind = ind; 2303 arena->nthreads = 0; 2304 2305 if (malloc_mutex_init(&arena->lock)) 2306 return (true); 2307 2308 if (config_stats) { 2309 memset(&arena->stats, 0, sizeof(arena_stats_t)); 2310 arena->stats.lstats = 2311 (malloc_large_stats_t *)base_alloc(nlclasses * 2312 sizeof(malloc_large_stats_t)); 2313 if (arena->stats.lstats == NULL) 2314 return (true); 2315 memset(arena->stats.lstats, 0, nlclasses * 2316 sizeof(malloc_large_stats_t)); 2317 if (config_tcache) 2318 ql_new(&arena->tcache_ql); 2319 } 2320 2321 if (config_prof) 2322 arena->prof_accumbytes = 0; 2323 2324 arena->dss_prec = chunk_dss_prec_get(); 2325 2326 /* Initialize chunks. */ 2327 arena_chunk_dirty_new(&arena->chunks_dirty); 2328 arena->spare = NULL; 2329 2330 arena->nactive = 0; 2331 arena->ndirty = 0; 2332 arena->npurgatory = 0; 2333 2334 arena_avail_tree_new(&arena->runs_avail); 2335 2336 /* Initialize bins. */ 2337 for (i = 0; i < NBINS; i++) { 2338 bin = &arena->bins[i]; 2339 if (malloc_mutex_init(&bin->lock)) 2340 return (true); 2341 bin->runcur = NULL; 2342 arena_run_tree_new(&bin->runs); 2343 if (config_stats) 2344 memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); 2345 } 2346 2347 return (false); 2348} 2349 2350/* 2351 * Calculate bin_info->run_size such that it meets the following constraints: 2352 * 2353 * *) bin_info->run_size >= min_run_size 2354 * *) bin_info->run_size <= arena_maxclass 2355 * *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed). 2356 * *) bin_info->nregs <= RUN_MAXREGS 2357 * 2358 * bin_info->nregs, bin_info->bitmap_offset, and bin_info->reg0_offset are also 2359 * calculated here, since these settings are all interdependent. 2360 */ 2361static size_t 2362bin_info_run_size_calc(arena_bin_info_t *bin_info, size_t min_run_size) 2363{ 2364 size_t pad_size; 2365 size_t try_run_size, good_run_size; 2366 uint32_t try_nregs, good_nregs; 2367 uint32_t try_hdr_size, good_hdr_size; 2368 uint32_t try_bitmap_offset, good_bitmap_offset; 2369 uint32_t try_ctx0_offset, good_ctx0_offset; 2370 uint32_t try_redzone0_offset, good_redzone0_offset; 2371 2372 assert(min_run_size >= PAGE); 2373 assert(min_run_size <= arena_maxclass); 2374 2375 /* 2376 * Determine redzone size based on minimum alignment and minimum 2377 * redzone size. Add padding to the end of the run if it is needed to 2378 * align the regions. The padding allows each redzone to be half the 2379 * minimum alignment; without the padding, each redzone would have to 2380 * be twice as large in order to maintain alignment. 2381 */ 2382 if (config_fill && opt_redzone) { 2383 size_t align_min = ZU(1) << (ffs(bin_info->reg_size) - 1); 2384 if (align_min <= REDZONE_MINSIZE) { 2385 bin_info->redzone_size = REDZONE_MINSIZE; 2386 pad_size = 0; 2387 } else { 2388 bin_info->redzone_size = align_min >> 1; 2389 pad_size = bin_info->redzone_size; 2390 } 2391 } else { 2392 bin_info->redzone_size = 0; 2393 pad_size = 0; 2394 } 2395 bin_info->reg_interval = bin_info->reg_size + 2396 (bin_info->redzone_size << 1); 2397 2398 /* 2399 * Calculate known-valid settings before entering the run_size 2400 * expansion loop, so that the first part of the loop always copies 2401 * valid settings. 2402 * 2403 * The do..while loop iteratively reduces the number of regions until 2404 * the run header and the regions no longer overlap. A closed formula 2405 * would be quite messy, since there is an interdependency between the 2406 * header's mask length and the number of regions. 2407 */ 2408 try_run_size = min_run_size; 2409 try_nregs = ((try_run_size - sizeof(arena_run_t)) / 2410 bin_info->reg_interval) 2411 + 1; /* Counter-act try_nregs-- in loop. */ 2412 if (try_nregs > RUN_MAXREGS) { 2413 try_nregs = RUN_MAXREGS 2414 + 1; /* Counter-act try_nregs-- in loop. */ 2415 } 2416 do { 2417 try_nregs--; 2418 try_hdr_size = sizeof(arena_run_t); 2419 /* Pad to a long boundary. */ 2420 try_hdr_size = LONG_CEILING(try_hdr_size); 2421 try_bitmap_offset = try_hdr_size; 2422 /* Add space for bitmap. */ 2423 try_hdr_size += bitmap_size(try_nregs); 2424 if (config_prof && opt_prof && prof_promote == false) { 2425 /* Pad to a quantum boundary. */ 2426 try_hdr_size = QUANTUM_CEILING(try_hdr_size); 2427 try_ctx0_offset = try_hdr_size; 2428 /* Add space for one (prof_ctx_t *) per region. */ 2429 try_hdr_size += try_nregs * sizeof(prof_ctx_t *); 2430 } else 2431 try_ctx0_offset = 0; 2432 try_redzone0_offset = try_run_size - (try_nregs * 2433 bin_info->reg_interval) - pad_size; 2434 } while (try_hdr_size > try_redzone0_offset); 2435 2436 /* run_size expansion loop. */ 2437 do { 2438 /* 2439 * Copy valid settings before trying more aggressive settings. 2440 */ 2441 good_run_size = try_run_size; 2442 good_nregs = try_nregs; 2443 good_hdr_size = try_hdr_size; 2444 good_bitmap_offset = try_bitmap_offset; 2445 good_ctx0_offset = try_ctx0_offset; 2446 good_redzone0_offset = try_redzone0_offset; 2447 2448 /* Try more aggressive settings. */ 2449 try_run_size += PAGE; 2450 try_nregs = ((try_run_size - sizeof(arena_run_t) - pad_size) / 2451 bin_info->reg_interval) 2452 + 1; /* Counter-act try_nregs-- in loop. */ 2453 if (try_nregs > RUN_MAXREGS) { 2454 try_nregs = RUN_MAXREGS 2455 + 1; /* Counter-act try_nregs-- in loop. */ 2456 } 2457 do { 2458 try_nregs--; 2459 try_hdr_size = sizeof(arena_run_t); 2460 /* Pad to a long boundary. */ 2461 try_hdr_size = LONG_CEILING(try_hdr_size); 2462 try_bitmap_offset = try_hdr_size; 2463 /* Add space for bitmap. */ 2464 try_hdr_size += bitmap_size(try_nregs); 2465 if (config_prof && opt_prof && prof_promote == false) { 2466 /* Pad to a quantum boundary. */ 2467 try_hdr_size = QUANTUM_CEILING(try_hdr_size); 2468 try_ctx0_offset = try_hdr_size; 2469 /* 2470 * Add space for one (prof_ctx_t *) per region. 2471 */ 2472 try_hdr_size += try_nregs * 2473 sizeof(prof_ctx_t *); 2474 } 2475 try_redzone0_offset = try_run_size - (try_nregs * 2476 bin_info->reg_interval) - pad_size; 2477 } while (try_hdr_size > try_redzone0_offset); 2478 } while (try_run_size <= arena_maxclass 2479 && RUN_MAX_OVRHD * (bin_info->reg_interval << 3) > 2480 RUN_MAX_OVRHD_RELAX 2481 && (try_redzone0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size 2482 && try_nregs < RUN_MAXREGS); 2483 2484 assert(good_hdr_size <= good_redzone0_offset); 2485 2486 /* Copy final settings. */ 2487 bin_info->run_size = good_run_size; 2488 bin_info->nregs = good_nregs; 2489 bin_info->bitmap_offset = good_bitmap_offset; 2490 bin_info->ctx0_offset = good_ctx0_offset; 2491 bin_info->reg0_offset = good_redzone0_offset + bin_info->redzone_size; 2492 2493 assert(bin_info->reg0_offset - bin_info->redzone_size + (bin_info->nregs 2494 * bin_info->reg_interval) + pad_size == bin_info->run_size); 2495 2496 return (good_run_size); 2497} 2498 2499static void 2500bin_info_init(void) 2501{ 2502 arena_bin_info_t *bin_info; 2503 size_t prev_run_size = PAGE; 2504 2505#define SIZE_CLASS(bin, delta, size) \ 2506 bin_info = &arena_bin_info[bin]; \ 2507 bin_info->reg_size = size; \ 2508 prev_run_size = bin_info_run_size_calc(bin_info, prev_run_size);\ 2509 bitmap_info_init(&bin_info->bitmap_info, bin_info->nregs); 2510 SIZE_CLASSES 2511#undef SIZE_CLASS 2512} 2513 2514void 2515arena_boot(void) 2516{ 2517 size_t header_size; 2518 unsigned i; 2519 2520 /* 2521 * Compute the header size such that it is large enough to contain the 2522 * page map. The page map is biased to omit entries for the header 2523 * itself, so some iteration is necessary to compute the map bias. 2524 * 2525 * 1) Compute safe header_size and map_bias values that include enough 2526 * space for an unbiased page map. 2527 * 2) Refine map_bias based on (1) to omit the header pages in the page 2528 * map. The resulting map_bias may be one too small. 2529 * 3) Refine map_bias based on (2). The result will be >= the result 2530 * from (2), and will always be correct. 2531 */ 2532 map_bias = 0; 2533 for (i = 0; i < 3; i++) { 2534 header_size = offsetof(arena_chunk_t, map) + 2535 (sizeof(arena_chunk_map_t) * (chunk_npages-map_bias)); 2536 map_bias = (header_size >> LG_PAGE) + ((header_size & PAGE_MASK) 2537 != 0); 2538 } 2539 assert(map_bias > 0); 2540 2541 arena_maxclass = chunksize - (map_bias << LG_PAGE); 2542 2543 bin_info_init(); 2544} 2545 2546void 2547arena_prefork(arena_t *arena) 2548{ 2549 unsigned i; 2550 2551 malloc_mutex_prefork(&arena->lock); 2552 for (i = 0; i < NBINS; i++) 2553 malloc_mutex_prefork(&arena->bins[i].lock); 2554} 2555 2556void 2557arena_postfork_parent(arena_t *arena) 2558{ 2559 unsigned i; 2560 2561 for (i = 0; i < NBINS; i++) 2562 malloc_mutex_postfork_parent(&arena->bins[i].lock); 2563 malloc_mutex_postfork_parent(&arena->lock); 2564} 2565 2566void 2567arena_postfork_child(arena_t *arena) 2568{ 2569 unsigned i; 2570 2571 for (i = 0; i < NBINS; i++) 2572 malloc_mutex_postfork_child(&arena->bins[i].lock); 2573 malloc_mutex_postfork_child(&arena->lock); 2574}
| 622 623 /* Insert the run into the runs_avail tree. */ 624 arena_avail_insert(arena, chunk, map_bias, chunk_npages-map_bias, 625 false, false); 626 627 return (chunk); 628} 629 630static void 631arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) 632{ 633 assert(arena_mapbits_allocated_get(chunk, map_bias) == 0); 634 assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0); 635 assert(arena_mapbits_unallocated_size_get(chunk, map_bias) == 636 arena_maxclass); 637 assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) == 638 arena_maxclass); 639 assert(arena_mapbits_dirty_get(chunk, map_bias) == 640 arena_mapbits_dirty_get(chunk, chunk_npages-1)); 641 642 /* 643 * Remove run from the runs_avail tree, so that the arena does not use 644 * it. 645 */ 646 arena_avail_remove(arena, chunk, map_bias, chunk_npages-map_bias, 647 false, false); 648 649 if (arena->spare != NULL) { 650 arena_chunk_t *spare = arena->spare; 651 652 arena->spare = chunk; 653 malloc_mutex_unlock(&arena->lock); 654 chunk_dealloc((void *)spare, chunksize, true); 655 malloc_mutex_lock(&arena->lock); 656 if (config_stats) 657 arena->stats.mapped -= chunksize; 658 } else 659 arena->spare = chunk; 660} 661 662static arena_run_t * 663arena_run_alloc_large_helper(arena_t *arena, size_t size, bool zero) 664{ 665 arena_run_t *run; 666 arena_chunk_map_t *mapelm, key; 667 668 key.bits = size | CHUNK_MAP_KEY; 669 mapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key); 670 if (mapelm != NULL) { 671 arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); 672 size_t pageind = (((uintptr_t)mapelm - 673 (uintptr_t)run_chunk->map) / sizeof(arena_chunk_map_t)) 674 + map_bias; 675 676 run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << 677 LG_PAGE)); 678 arena_run_split_large(arena, run, size, zero); 679 return (run); 680 } 681 682 return (NULL); 683} 684 685static arena_run_t * 686arena_run_alloc_large(arena_t *arena, size_t size, bool zero) 687{ 688 arena_chunk_t *chunk; 689 arena_run_t *run; 690 691 assert(size <= arena_maxclass); 692 assert((size & PAGE_MASK) == 0); 693 694 /* Search the arena's chunks for the lowest best fit. */ 695 run = arena_run_alloc_large_helper(arena, size, zero); 696 if (run != NULL) 697 return (run); 698 699 /* 700 * No usable runs. Create a new chunk from which to allocate the run. 701 */ 702 chunk = arena_chunk_alloc(arena); 703 if (chunk != NULL) { 704 run = (arena_run_t *)((uintptr_t)chunk + (map_bias << LG_PAGE)); 705 arena_run_split_large(arena, run, size, zero); 706 return (run); 707 } 708 709 /* 710 * arena_chunk_alloc() failed, but another thread may have made 711 * sufficient memory available while this one dropped arena->lock in 712 * arena_chunk_alloc(), so search one more time. 713 */ 714 return (arena_run_alloc_large_helper(arena, size, zero)); 715} 716 717static arena_run_t * 718arena_run_alloc_small_helper(arena_t *arena, size_t size, size_t binind) 719{ 720 arena_run_t *run; 721 arena_chunk_map_t *mapelm, key; 722 723 key.bits = size | CHUNK_MAP_KEY; 724 mapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key); 725 if (mapelm != NULL) { 726 arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); 727 size_t pageind = (((uintptr_t)mapelm - 728 (uintptr_t)run_chunk->map) / sizeof(arena_chunk_map_t)) 729 + map_bias; 730 731 run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << 732 LG_PAGE)); 733 arena_run_split_small(arena, run, size, binind); 734 return (run); 735 } 736 737 return (NULL); 738} 739 740static arena_run_t * 741arena_run_alloc_small(arena_t *arena, size_t size, size_t binind) 742{ 743 arena_chunk_t *chunk; 744 arena_run_t *run; 745 746 assert(size <= arena_maxclass); 747 assert((size & PAGE_MASK) == 0); 748 assert(binind != BININD_INVALID); 749 750 /* Search the arena's chunks for the lowest best fit. */ 751 run = arena_run_alloc_small_helper(arena, size, binind); 752 if (run != NULL) 753 return (run); 754 755 /* 756 * No usable runs. Create a new chunk from which to allocate the run. 757 */ 758 chunk = arena_chunk_alloc(arena); 759 if (chunk != NULL) { 760 run = (arena_run_t *)((uintptr_t)chunk + (map_bias << LG_PAGE)); 761 arena_run_split_small(arena, run, size, binind); 762 return (run); 763 } 764 765 /* 766 * arena_chunk_alloc() failed, but another thread may have made 767 * sufficient memory available while this one dropped arena->lock in 768 * arena_chunk_alloc(), so search one more time. 769 */ 770 return (arena_run_alloc_small_helper(arena, size, binind)); 771} 772 773static inline void 774arena_maybe_purge(arena_t *arena) 775{ 776 size_t npurgeable, threshold; 777 778 /* Don't purge if the option is disabled. */ 779 if (opt_lg_dirty_mult < 0) 780 return; 781 /* Don't purge if all dirty pages are already being purged. */ 782 if (arena->ndirty <= arena->npurgatory) 783 return; 784 npurgeable = arena->ndirty - arena->npurgatory; 785 threshold = (arena->nactive >> opt_lg_dirty_mult); 786 /* 787 * Don't purge unless the number of purgeable pages exceeds the 788 * threshold. 789 */ 790 if (npurgeable <= threshold) 791 return; 792 793 arena_purge(arena, false); 794} 795 796static arena_chunk_t * 797chunks_dirty_iter_cb(arena_chunk_tree_t *tree, arena_chunk_t *chunk, void *arg) 798{ 799 size_t *ndirty = (size_t *)arg; 800 801 assert(chunk->ndirty != 0); 802 *ndirty += chunk->ndirty; 803 return (NULL); 804} 805 806static size_t 807arena_compute_npurgatory(arena_t *arena, bool all) 808{ 809 size_t npurgatory, npurgeable; 810 811 /* 812 * Compute the minimum number of pages that this thread should try to 813 * purge. 814 */ 815 npurgeable = arena->ndirty - arena->npurgatory; 816 817 if (all == false) { 818 size_t threshold = (arena->nactive >> opt_lg_dirty_mult); 819 820 npurgatory = npurgeable - threshold; 821 } else 822 npurgatory = npurgeable; 823 824 return (npurgatory); 825} 826 827static void 828arena_chunk_stash_dirty(arena_t *arena, arena_chunk_t *chunk, bool all, 829 arena_chunk_mapelms_t *mapelms) 830{ 831 size_t pageind, npages; 832 833 /* 834 * Temporarily allocate free dirty runs within chunk. If all is false, 835 * only operate on dirty runs that are fragments; otherwise operate on 836 * all dirty runs. 837 */ 838 for (pageind = map_bias; pageind < chunk_npages; pageind += npages) { 839 arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); 840 if (arena_mapbits_allocated_get(chunk, pageind) == 0) { 841 size_t run_size = 842 arena_mapbits_unallocated_size_get(chunk, pageind); 843 844 npages = run_size >> LG_PAGE; 845 assert(pageind + npages <= chunk_npages); 846 assert(arena_mapbits_dirty_get(chunk, pageind) == 847 arena_mapbits_dirty_get(chunk, pageind+npages-1)); 848 849 if (arena_mapbits_dirty_get(chunk, pageind) != 0 && 850 (all || arena_avail_adjac(chunk, pageind, 851 npages))) { 852 arena_run_t *run = (arena_run_t *)((uintptr_t) 853 chunk + (uintptr_t)(pageind << LG_PAGE)); 854 855 arena_run_split_large(arena, run, run_size, 856 false); 857 /* Append to list for later processing. */ 858 ql_elm_new(mapelm, u.ql_link); 859 ql_tail_insert(mapelms, mapelm, u.ql_link); 860 } 861 } else { 862 /* Skip run. */ 863 if (arena_mapbits_large_get(chunk, pageind) != 0) { 864 npages = arena_mapbits_large_size_get(chunk, 865 pageind) >> LG_PAGE; 866 } else { 867 size_t binind; 868 arena_bin_info_t *bin_info; 869 arena_run_t *run = (arena_run_t *)((uintptr_t) 870 chunk + (uintptr_t)(pageind << LG_PAGE)); 871 872 assert(arena_mapbits_small_runind_get(chunk, 873 pageind) == 0); 874 binind = arena_bin_index(arena, run->bin); 875 bin_info = &arena_bin_info[binind]; 876 npages = bin_info->run_size >> LG_PAGE; 877 } 878 } 879 } 880 assert(pageind == chunk_npages); 881 assert(chunk->ndirty == 0 || all == false); 882 assert(chunk->nruns_adjac == 0); 883} 884 885static size_t 886arena_chunk_purge_stashed(arena_t *arena, arena_chunk_t *chunk, 887 arena_chunk_mapelms_t *mapelms) 888{ 889 size_t npurged, pageind, npages, nmadvise; 890 arena_chunk_map_t *mapelm; 891 892 malloc_mutex_unlock(&arena->lock); 893 if (config_stats) 894 nmadvise = 0; 895 npurged = 0; 896 ql_foreach(mapelm, mapelms, u.ql_link) { 897 bool unzeroed; 898 size_t flag_unzeroed, i; 899 900 pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / 901 sizeof(arena_chunk_map_t)) + map_bias; 902 npages = arena_mapbits_large_size_get(chunk, pageind) >> 903 LG_PAGE; 904 assert(pageind + npages <= chunk_npages); 905 unzeroed = pages_purge((void *)((uintptr_t)chunk + (pageind << 906 LG_PAGE)), (npages << LG_PAGE)); 907 flag_unzeroed = unzeroed ? CHUNK_MAP_UNZEROED : 0; 908 /* 909 * Set the unzeroed flag for all pages, now that pages_purge() 910 * has returned whether the pages were zeroed as a side effect 911 * of purging. This chunk map modification is safe even though 912 * the arena mutex isn't currently owned by this thread, 913 * because the run is marked as allocated, thus protecting it 914 * from being modified by any other thread. As long as these 915 * writes don't perturb the first and last elements' 916 * CHUNK_MAP_ALLOCATED bits, behavior is well defined. 917 */ 918 for (i = 0; i < npages; i++) { 919 arena_mapbits_unzeroed_set(chunk, pageind+i, 920 flag_unzeroed); 921 } 922 npurged += npages; 923 if (config_stats) 924 nmadvise++; 925 } 926 malloc_mutex_lock(&arena->lock); 927 if (config_stats) 928 arena->stats.nmadvise += nmadvise; 929 930 return (npurged); 931} 932 933static void 934arena_chunk_unstash_purged(arena_t *arena, arena_chunk_t *chunk, 935 arena_chunk_mapelms_t *mapelms) 936{ 937 arena_chunk_map_t *mapelm; 938 size_t pageind; 939 940 /* Deallocate runs. */ 941 for (mapelm = ql_first(mapelms); mapelm != NULL; 942 mapelm = ql_first(mapelms)) { 943 arena_run_t *run; 944 945 pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / 946 sizeof(arena_chunk_map_t)) + map_bias; 947 run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)(pageind << 948 LG_PAGE)); 949 ql_remove(mapelms, mapelm, u.ql_link); 950 arena_run_dalloc(arena, run, false, true); 951 } 952} 953 954static inline size_t 955arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all) 956{ 957 size_t npurged; 958 arena_chunk_mapelms_t mapelms; 959 960 ql_new(&mapelms); 961 962 /* 963 * If chunk is the spare, temporarily re-allocate it, 1) so that its 964 * run is reinserted into runs_avail, and 2) so that it cannot be 965 * completely discarded by another thread while arena->lock is dropped 966 * by this thread. Note that the arena_run_dalloc() call will 967 * implicitly deallocate the chunk, so no explicit action is required 968 * in this function to deallocate the chunk. 969 * 970 * Note that once a chunk contains dirty pages, it cannot again contain 971 * a single run unless 1) it is a dirty run, or 2) this function purges 972 * dirty pages and causes the transition to a single clean run. Thus 973 * (chunk == arena->spare) is possible, but it is not possible for 974 * this function to be called on the spare unless it contains a dirty 975 * run. 976 */ 977 if (chunk == arena->spare) { 978 assert(arena_mapbits_dirty_get(chunk, map_bias) != 0); 979 assert(arena_mapbits_dirty_get(chunk, chunk_npages-1) != 0); 980 981 arena_chunk_alloc(arena); 982 } 983 984 if (config_stats) 985 arena->stats.purged += chunk->ndirty; 986 987 /* 988 * Operate on all dirty runs if there is no clean/dirty run 989 * fragmentation. 990 */ 991 if (chunk->nruns_adjac == 0) 992 all = true; 993 994 arena_chunk_stash_dirty(arena, chunk, all, &mapelms); 995 npurged = arena_chunk_purge_stashed(arena, chunk, &mapelms); 996 arena_chunk_unstash_purged(arena, chunk, &mapelms); 997 998 return (npurged); 999} 1000 1001static void 1002arena_purge(arena_t *arena, bool all) 1003{ 1004 arena_chunk_t *chunk; 1005 size_t npurgatory; 1006 if (config_debug) { 1007 size_t ndirty = 0; 1008 1009 arena_chunk_dirty_iter(&arena->chunks_dirty, NULL, 1010 chunks_dirty_iter_cb, (void *)&ndirty); 1011 assert(ndirty == arena->ndirty); 1012 } 1013 assert(arena->ndirty > arena->npurgatory || all); 1014 assert((arena->nactive >> opt_lg_dirty_mult) < (arena->ndirty - 1015 arena->npurgatory) || all); 1016 1017 if (config_stats) 1018 arena->stats.npurge++; 1019 1020 /* 1021 * Add the minimum number of pages this thread should try to purge to 1022 * arena->npurgatory. This will keep multiple threads from racing to 1023 * reduce ndirty below the threshold. 1024 */ 1025 npurgatory = arena_compute_npurgatory(arena, all); 1026 arena->npurgatory += npurgatory; 1027 1028 while (npurgatory > 0) { 1029 size_t npurgeable, npurged, nunpurged; 1030 1031 /* Get next chunk with dirty pages. */ 1032 chunk = arena_chunk_dirty_first(&arena->chunks_dirty); 1033 if (chunk == NULL) { 1034 /* 1035 * This thread was unable to purge as many pages as 1036 * originally intended, due to races with other threads 1037 * that either did some of the purging work, or re-used 1038 * dirty pages. 1039 */ 1040 arena->npurgatory -= npurgatory; 1041 return; 1042 } 1043 npurgeable = chunk->ndirty; 1044 assert(npurgeable != 0); 1045 1046 if (npurgeable > npurgatory && chunk->nruns_adjac == 0) { 1047 /* 1048 * This thread will purge all the dirty pages in chunk, 1049 * so set npurgatory to reflect this thread's intent to 1050 * purge the pages. This tends to reduce the chances 1051 * of the following scenario: 1052 * 1053 * 1) This thread sets arena->npurgatory such that 1054 * (arena->ndirty - arena->npurgatory) is at the 1055 * threshold. 1056 * 2) This thread drops arena->lock. 1057 * 3) Another thread causes one or more pages to be 1058 * dirtied, and immediately determines that it must 1059 * purge dirty pages. 1060 * 1061 * If this scenario *does* play out, that's okay, 1062 * because all of the purging work being done really 1063 * needs to happen. 1064 */ 1065 arena->npurgatory += npurgeable - npurgatory; 1066 npurgatory = npurgeable; 1067 } 1068 1069 /* 1070 * Keep track of how many pages are purgeable, versus how many 1071 * actually get purged, and adjust counters accordingly. 1072 */ 1073 arena->npurgatory -= npurgeable; 1074 npurgatory -= npurgeable; 1075 npurged = arena_chunk_purge(arena, chunk, all); 1076 nunpurged = npurgeable - npurged; 1077 arena->npurgatory += nunpurged; 1078 npurgatory += nunpurged; 1079 } 1080} 1081 1082void 1083arena_purge_all(arena_t *arena) 1084{ 1085 1086 malloc_mutex_lock(&arena->lock); 1087 arena_purge(arena, true); 1088 malloc_mutex_unlock(&arena->lock); 1089} 1090 1091static void 1092arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size, 1093 size_t *p_run_ind, size_t *p_run_pages, size_t flag_dirty) 1094{ 1095 size_t size = *p_size; 1096 size_t run_ind = *p_run_ind; 1097 size_t run_pages = *p_run_pages; 1098 1099 /* Try to coalesce forward. */ 1100 if (run_ind + run_pages < chunk_npages && 1101 arena_mapbits_allocated_get(chunk, run_ind+run_pages) == 0 && 1102 arena_mapbits_dirty_get(chunk, run_ind+run_pages) == flag_dirty) { 1103 size_t nrun_size = arena_mapbits_unallocated_size_get(chunk, 1104 run_ind+run_pages); 1105 size_t nrun_pages = nrun_size >> LG_PAGE; 1106 1107 /* 1108 * Remove successor from runs_avail; the coalesced run is 1109 * inserted later. 1110 */ 1111 assert(arena_mapbits_unallocated_size_get(chunk, 1112 run_ind+run_pages+nrun_pages-1) == nrun_size); 1113 assert(arena_mapbits_dirty_get(chunk, 1114 run_ind+run_pages+nrun_pages-1) == flag_dirty); 1115 arena_avail_remove(arena, chunk, run_ind+run_pages, nrun_pages, 1116 false, true); 1117 1118 size += nrun_size; 1119 run_pages += nrun_pages; 1120 1121 arena_mapbits_unallocated_size_set(chunk, run_ind, size); 1122 arena_mapbits_unallocated_size_set(chunk, run_ind+run_pages-1, 1123 size); 1124 } 1125 1126 /* Try to coalesce backward. */ 1127 if (run_ind > map_bias && arena_mapbits_allocated_get(chunk, 1128 run_ind-1) == 0 && arena_mapbits_dirty_get(chunk, run_ind-1) == 1129 flag_dirty) { 1130 size_t prun_size = arena_mapbits_unallocated_size_get(chunk, 1131 run_ind-1); 1132 size_t prun_pages = prun_size >> LG_PAGE; 1133 1134 run_ind -= prun_pages; 1135 1136 /* 1137 * Remove predecessor from runs_avail; the coalesced run is 1138 * inserted later. 1139 */ 1140 assert(arena_mapbits_unallocated_size_get(chunk, run_ind) == 1141 prun_size); 1142 assert(arena_mapbits_dirty_get(chunk, run_ind) == flag_dirty); 1143 arena_avail_remove(arena, chunk, run_ind, prun_pages, true, 1144 false); 1145 1146 size += prun_size; 1147 run_pages += prun_pages; 1148 1149 arena_mapbits_unallocated_size_set(chunk, run_ind, size); 1150 arena_mapbits_unallocated_size_set(chunk, run_ind+run_pages-1, 1151 size); 1152 } 1153 1154 *p_size = size; 1155 *p_run_ind = run_ind; 1156 *p_run_pages = run_pages; 1157} 1158 1159static void 1160arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) 1161{ 1162 arena_chunk_t *chunk; 1163 size_t size, run_ind, run_pages, flag_dirty; 1164 1165 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1166 run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); 1167 assert(run_ind >= map_bias); 1168 assert(run_ind < chunk_npages); 1169 if (arena_mapbits_large_get(chunk, run_ind) != 0) { 1170 size = arena_mapbits_large_size_get(chunk, run_ind); 1171 assert(size == PAGE || 1172 arena_mapbits_large_size_get(chunk, 1173 run_ind+(size>>LG_PAGE)-1) == 0); 1174 } else { 1175 size_t binind = arena_bin_index(arena, run->bin); 1176 arena_bin_info_t *bin_info = &arena_bin_info[binind]; 1177 size = bin_info->run_size; 1178 } 1179 run_pages = (size >> LG_PAGE); 1180 arena_cactive_update(arena, 0, run_pages); 1181 arena->nactive -= run_pages; 1182 1183 /* 1184 * The run is dirty if the caller claims to have dirtied it, as well as 1185 * if it was already dirty before being allocated and the caller 1186 * doesn't claim to have cleaned it. 1187 */ 1188 assert(arena_mapbits_dirty_get(chunk, run_ind) == 1189 arena_mapbits_dirty_get(chunk, run_ind+run_pages-1)); 1190 if (cleaned == false && arena_mapbits_dirty_get(chunk, run_ind) != 0) 1191 dirty = true; 1192 flag_dirty = dirty ? CHUNK_MAP_DIRTY : 0; 1193 1194 /* Mark pages as unallocated in the chunk map. */ 1195 if (dirty) { 1196 arena_mapbits_unallocated_set(chunk, run_ind, size, 1197 CHUNK_MAP_DIRTY); 1198 arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size, 1199 CHUNK_MAP_DIRTY); 1200 } else { 1201 arena_mapbits_unallocated_set(chunk, run_ind, size, 1202 arena_mapbits_unzeroed_get(chunk, run_ind)); 1203 arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size, 1204 arena_mapbits_unzeroed_get(chunk, run_ind+run_pages-1)); 1205 } 1206 1207 arena_run_coalesce(arena, chunk, &size, &run_ind, &run_pages, 1208 flag_dirty); 1209 1210 /* Insert into runs_avail, now that coalescing is complete. */ 1211 assert(arena_mapbits_unallocated_size_get(chunk, run_ind) == 1212 arena_mapbits_unallocated_size_get(chunk, run_ind+run_pages-1)); 1213 assert(arena_mapbits_dirty_get(chunk, run_ind) == 1214 arena_mapbits_dirty_get(chunk, run_ind+run_pages-1)); 1215 arena_avail_insert(arena, chunk, run_ind, run_pages, true, true); 1216 1217 /* Deallocate chunk if it is now completely unused. */ 1218 if (size == arena_maxclass) { 1219 assert(run_ind == map_bias); 1220 assert(run_pages == (arena_maxclass >> LG_PAGE)); 1221 arena_chunk_dealloc(arena, chunk); 1222 } 1223 1224 /* 1225 * It is okay to do dirty page processing here even if the chunk was 1226 * deallocated above, since in that case it is the spare. Waiting 1227 * until after possible chunk deallocation to do dirty processing 1228 * allows for an old spare to be fully deallocated, thus decreasing the 1229 * chances of spuriously crossing the dirty page purging threshold. 1230 */ 1231 if (dirty) 1232 arena_maybe_purge(arena); 1233} 1234 1235static void 1236arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1237 size_t oldsize, size_t newsize) 1238{ 1239 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; 1240 size_t head_npages = (oldsize - newsize) >> LG_PAGE; 1241 size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind); 1242 1243 assert(oldsize > newsize); 1244 1245 /* 1246 * Update the chunk map so that arena_run_dalloc() can treat the 1247 * leading run as separately allocated. Set the last element of each 1248 * run first, in case of single-page runs. 1249 */ 1250 assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize); 1251 arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty); 1252 arena_mapbits_large_set(chunk, pageind, oldsize-newsize, flag_dirty); 1253 1254 if (config_debug) { 1255 UNUSED size_t tail_npages = newsize >> LG_PAGE; 1256 assert(arena_mapbits_large_size_get(chunk, 1257 pageind+head_npages+tail_npages-1) == 0); 1258 assert(arena_mapbits_dirty_get(chunk, 1259 pageind+head_npages+tail_npages-1) == flag_dirty); 1260 } 1261 arena_mapbits_large_set(chunk, pageind+head_npages, newsize, 1262 flag_dirty); 1263 1264 arena_run_dalloc(arena, run, false, false); 1265} 1266 1267static void 1268arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1269 size_t oldsize, size_t newsize, bool dirty) 1270{ 1271 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; 1272 size_t head_npages = newsize >> LG_PAGE; 1273 size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind); 1274 1275 assert(oldsize > newsize); 1276 1277 /* 1278 * Update the chunk map so that arena_run_dalloc() can treat the 1279 * trailing run as separately allocated. Set the last element of each 1280 * run first, in case of single-page runs. 1281 */ 1282 assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize); 1283 arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty); 1284 arena_mapbits_large_set(chunk, pageind, newsize, flag_dirty); 1285 1286 if (config_debug) { 1287 UNUSED size_t tail_npages = (oldsize - newsize) >> LG_PAGE; 1288 assert(arena_mapbits_large_size_get(chunk, 1289 pageind+head_npages+tail_npages-1) == 0); 1290 assert(arena_mapbits_dirty_get(chunk, 1291 pageind+head_npages+tail_npages-1) == flag_dirty); 1292 } 1293 arena_mapbits_large_set(chunk, pageind+head_npages, oldsize-newsize, 1294 flag_dirty); 1295 1296 arena_run_dalloc(arena, (arena_run_t *)((uintptr_t)run + newsize), 1297 dirty, false); 1298} 1299 1300static arena_run_t * 1301arena_bin_runs_first(arena_bin_t *bin) 1302{ 1303 arena_chunk_map_t *mapelm = arena_run_tree_first(&bin->runs); 1304 if (mapelm != NULL) { 1305 arena_chunk_t *chunk; 1306 size_t pageind; 1307 arena_run_t *run; 1308 1309 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm); 1310 pageind = ((((uintptr_t)mapelm - (uintptr_t)chunk->map) / 1311 sizeof(arena_chunk_map_t))) + map_bias; 1312 run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - 1313 arena_mapbits_small_runind_get(chunk, pageind)) << 1314 LG_PAGE)); 1315 return (run); 1316 } 1317 1318 return (NULL); 1319} 1320 1321static void 1322arena_bin_runs_insert(arena_bin_t *bin, arena_run_t *run) 1323{ 1324 arena_chunk_t *chunk = CHUNK_ADDR2BASE(run); 1325 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; 1326 arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); 1327 1328 assert(arena_run_tree_search(&bin->runs, mapelm) == NULL); 1329 1330 arena_run_tree_insert(&bin->runs, mapelm); 1331} 1332 1333static void 1334arena_bin_runs_remove(arena_bin_t *bin, arena_run_t *run) 1335{ 1336 arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1337 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; 1338 arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); 1339 1340 assert(arena_run_tree_search(&bin->runs, mapelm) != NULL); 1341 1342 arena_run_tree_remove(&bin->runs, mapelm); 1343} 1344 1345static arena_run_t * 1346arena_bin_nonfull_run_tryget(arena_bin_t *bin) 1347{ 1348 arena_run_t *run = arena_bin_runs_first(bin); 1349 if (run != NULL) { 1350 arena_bin_runs_remove(bin, run); 1351 if (config_stats) 1352 bin->stats.reruns++; 1353 } 1354 return (run); 1355} 1356 1357static arena_run_t * 1358arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) 1359{ 1360 arena_run_t *run; 1361 size_t binind; 1362 arena_bin_info_t *bin_info; 1363 1364 /* Look for a usable run. */ 1365 run = arena_bin_nonfull_run_tryget(bin); 1366 if (run != NULL) 1367 return (run); 1368 /* No existing runs have any space available. */ 1369 1370 binind = arena_bin_index(arena, bin); 1371 bin_info = &arena_bin_info[binind]; 1372 1373 /* Allocate a new run. */ 1374 malloc_mutex_unlock(&bin->lock); 1375 /******************************/ 1376 malloc_mutex_lock(&arena->lock); 1377 run = arena_run_alloc_small(arena, bin_info->run_size, binind); 1378 if (run != NULL) { 1379 bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + 1380 (uintptr_t)bin_info->bitmap_offset); 1381 1382 /* Initialize run internals. */ 1383 run->bin = bin; 1384 run->nextind = 0; 1385 run->nfree = bin_info->nregs; 1386 bitmap_init(bitmap, &bin_info->bitmap_info); 1387 } 1388 malloc_mutex_unlock(&arena->lock); 1389 /********************************/ 1390 malloc_mutex_lock(&bin->lock); 1391 if (run != NULL) { 1392 if (config_stats) { 1393 bin->stats.nruns++; 1394 bin->stats.curruns++; 1395 } 1396 return (run); 1397 } 1398 1399 /* 1400 * arena_run_alloc_small() failed, but another thread may have made 1401 * sufficient memory available while this one dropped bin->lock above, 1402 * so search one more time. 1403 */ 1404 run = arena_bin_nonfull_run_tryget(bin); 1405 if (run != NULL) 1406 return (run); 1407 1408 return (NULL); 1409} 1410 1411/* Re-fill bin->runcur, then call arena_run_reg_alloc(). */ 1412static void * 1413arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin) 1414{ 1415 void *ret; 1416 size_t binind; 1417 arena_bin_info_t *bin_info; 1418 arena_run_t *run; 1419 1420 binind = arena_bin_index(arena, bin); 1421 bin_info = &arena_bin_info[binind]; 1422 bin->runcur = NULL; 1423 run = arena_bin_nonfull_run_get(arena, bin); 1424 if (bin->runcur != NULL && bin->runcur->nfree > 0) { 1425 /* 1426 * Another thread updated runcur while this one ran without the 1427 * bin lock in arena_bin_nonfull_run_get(). 1428 */ 1429 assert(bin->runcur->nfree > 0); 1430 ret = arena_run_reg_alloc(bin->runcur, bin_info); 1431 if (run != NULL) { 1432 arena_chunk_t *chunk; 1433 1434 /* 1435 * arena_run_alloc_small() may have allocated run, or 1436 * it may have pulled run from the bin's run tree. 1437 * Therefore it is unsafe to make any assumptions about 1438 * how run has previously been used, and 1439 * arena_bin_lower_run() must be called, as if a region 1440 * were just deallocated from the run. 1441 */ 1442 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1443 if (run->nfree == bin_info->nregs) 1444 arena_dalloc_bin_run(arena, chunk, run, bin); 1445 else 1446 arena_bin_lower_run(arena, chunk, run, bin); 1447 } 1448 return (ret); 1449 } 1450 1451 if (run == NULL) 1452 return (NULL); 1453 1454 bin->runcur = run; 1455 1456 assert(bin->runcur->nfree > 0); 1457 1458 return (arena_run_reg_alloc(bin->runcur, bin_info)); 1459} 1460 1461void 1462arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind, 1463 uint64_t prof_accumbytes) 1464{ 1465 unsigned i, nfill; 1466 arena_bin_t *bin; 1467 arena_run_t *run; 1468 void *ptr; 1469 1470 assert(tbin->ncached == 0); 1471 1472 if (config_prof && arena_prof_accum(arena, prof_accumbytes)) 1473 prof_idump(); 1474 bin = &arena->bins[binind]; 1475 malloc_mutex_lock(&bin->lock); 1476 for (i = 0, nfill = (tcache_bin_info[binind].ncached_max >> 1477 tbin->lg_fill_div); i < nfill; i++) { 1478 if ((run = bin->runcur) != NULL && run->nfree > 0) 1479 ptr = arena_run_reg_alloc(run, &arena_bin_info[binind]); 1480 else 1481 ptr = arena_bin_malloc_hard(arena, bin); 1482 if (ptr == NULL) 1483 break; 1484 if (config_fill && opt_junk) { 1485 arena_alloc_junk_small(ptr, &arena_bin_info[binind], 1486 true); 1487 } 1488 /* Insert such that low regions get used first. */ 1489 tbin->avail[nfill - 1 - i] = ptr; 1490 } 1491 if (config_stats) { 1492 bin->stats.allocated += i * arena_bin_info[binind].reg_size; 1493 bin->stats.nmalloc += i; 1494 bin->stats.nrequests += tbin->tstats.nrequests; 1495 bin->stats.nfills++; 1496 tbin->tstats.nrequests = 0; 1497 } 1498 malloc_mutex_unlock(&bin->lock); 1499 tbin->ncached = i; 1500} 1501 1502void 1503arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info, bool zero) 1504{ 1505 1506 if (zero) { 1507 size_t redzone_size = bin_info->redzone_size; 1508 memset((void *)((uintptr_t)ptr - redzone_size), 0xa5, 1509 redzone_size); 1510 memset((void *)((uintptr_t)ptr + bin_info->reg_size), 0xa5, 1511 redzone_size); 1512 } else { 1513 memset((void *)((uintptr_t)ptr - bin_info->redzone_size), 0xa5, 1514 bin_info->reg_interval); 1515 } 1516} 1517 1518#ifdef JEMALLOC_JET 1519#undef arena_redzone_corruption 1520#define arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption_impl) 1521#endif 1522static void 1523arena_redzone_corruption(void *ptr, size_t usize, bool after, 1524 size_t offset, uint8_t byte) 1525{ 1526 1527 malloc_printf("<jemalloc>: Corrupt redzone %zu byte%s %s %p " 1528 "(size %zu), byte=%#x\n", offset, (offset == 1) ? "" : "s", 1529 after ? "after" : "before", ptr, usize, byte); 1530} 1531#ifdef JEMALLOC_JET 1532#undef arena_redzone_corruption 1533#define arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption) 1534arena_redzone_corruption_t *arena_redzone_corruption = 1535 JEMALLOC_N(arena_redzone_corruption_impl); 1536#endif 1537 1538static void 1539arena_redzones_validate(void *ptr, arena_bin_info_t *bin_info, bool reset) 1540{ 1541 size_t size = bin_info->reg_size; 1542 size_t redzone_size = bin_info->redzone_size; 1543 size_t i; 1544 bool error = false; 1545 1546 for (i = 1; i <= redzone_size; i++) { 1547 uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i); 1548 if (*byte != 0xa5) { 1549 error = true; 1550 arena_redzone_corruption(ptr, size, false, i, *byte); 1551 if (reset) 1552 *byte = 0xa5; 1553 } 1554 } 1555 for (i = 0; i < redzone_size; i++) { 1556 uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i); 1557 if (*byte != 0xa5) { 1558 error = true; 1559 arena_redzone_corruption(ptr, size, true, i, *byte); 1560 if (reset) 1561 *byte = 0xa5; 1562 } 1563 } 1564 if (opt_abort && error) 1565 abort(); 1566} 1567 1568#ifdef JEMALLOC_JET 1569#undef arena_dalloc_junk_small 1570#define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small_impl) 1571#endif 1572void 1573arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info) 1574{ 1575 size_t redzone_size = bin_info->redzone_size; 1576 1577 arena_redzones_validate(ptr, bin_info, false); 1578 memset((void *)((uintptr_t)ptr - redzone_size), 0x5a, 1579 bin_info->reg_interval); 1580} 1581#ifdef JEMALLOC_JET 1582#undef arena_dalloc_junk_small 1583#define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small) 1584arena_dalloc_junk_small_t *arena_dalloc_junk_small = 1585 JEMALLOC_N(arena_dalloc_junk_small_impl); 1586#endif 1587 1588void 1589arena_quarantine_junk_small(void *ptr, size_t usize) 1590{ 1591 size_t binind; 1592 arena_bin_info_t *bin_info; 1593 cassert(config_fill); 1594 assert(opt_junk); 1595 assert(opt_quarantine); 1596 assert(usize <= SMALL_MAXCLASS); 1597 1598 binind = SMALL_SIZE2BIN(usize); 1599 bin_info = &arena_bin_info[binind]; 1600 arena_redzones_validate(ptr, bin_info, true); 1601} 1602 1603void * 1604arena_malloc_small(arena_t *arena, size_t size, bool zero) 1605{ 1606 void *ret; 1607 arena_bin_t *bin; 1608 arena_run_t *run; 1609 size_t binind; 1610 1611 binind = SMALL_SIZE2BIN(size); 1612 assert(binind < NBINS); 1613 bin = &arena->bins[binind]; 1614 size = arena_bin_info[binind].reg_size; 1615 1616 malloc_mutex_lock(&bin->lock); 1617 if ((run = bin->runcur) != NULL && run->nfree > 0) 1618 ret = arena_run_reg_alloc(run, &arena_bin_info[binind]); 1619 else 1620 ret = arena_bin_malloc_hard(arena, bin); 1621 1622 if (ret == NULL) { 1623 malloc_mutex_unlock(&bin->lock); 1624 return (NULL); 1625 } 1626 1627 if (config_stats) { 1628 bin->stats.allocated += size; 1629 bin->stats.nmalloc++; 1630 bin->stats.nrequests++; 1631 } 1632 malloc_mutex_unlock(&bin->lock); 1633 if (config_prof && isthreaded == false && arena_prof_accum(arena, size)) 1634 prof_idump(); 1635 1636 if (zero == false) { 1637 if (config_fill) { 1638 if (opt_junk) { 1639 arena_alloc_junk_small(ret, 1640 &arena_bin_info[binind], false); 1641 } else if (opt_zero) 1642 memset(ret, 0, size); 1643 } 1644 VALGRIND_MAKE_MEM_UNDEFINED(ret, size); 1645 } else { 1646 if (config_fill && opt_junk) { 1647 arena_alloc_junk_small(ret, &arena_bin_info[binind], 1648 true); 1649 } 1650 VALGRIND_MAKE_MEM_UNDEFINED(ret, size); 1651 memset(ret, 0, size); 1652 } 1653 1654 return (ret); 1655} 1656 1657void * 1658arena_malloc_large(arena_t *arena, size_t size, bool zero) 1659{ 1660 void *ret; 1661 UNUSED bool idump; 1662 1663 /* Large allocation. */ 1664 size = PAGE_CEILING(size); 1665 malloc_mutex_lock(&arena->lock); 1666 ret = (void *)arena_run_alloc_large(arena, size, zero); 1667 if (ret == NULL) { 1668 malloc_mutex_unlock(&arena->lock); 1669 return (NULL); 1670 } 1671 if (config_stats) { 1672 arena->stats.nmalloc_large++; 1673 arena->stats.nrequests_large++; 1674 arena->stats.allocated_large += size; 1675 arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; 1676 arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; 1677 arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; 1678 } 1679 if (config_prof) 1680 idump = arena_prof_accum_locked(arena, size); 1681 malloc_mutex_unlock(&arena->lock); 1682 if (config_prof && idump) 1683 prof_idump(); 1684 1685 if (zero == false) { 1686 if (config_fill) { 1687 if (opt_junk) 1688 memset(ret, 0xa5, size); 1689 else if (opt_zero) 1690 memset(ret, 0, size); 1691 } 1692 } 1693 1694 return (ret); 1695} 1696 1697/* Only handles large allocations that require more than page alignment. */ 1698void * 1699arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero) 1700{ 1701 void *ret; 1702 size_t alloc_size, leadsize, trailsize; 1703 arena_run_t *run; 1704 arena_chunk_t *chunk; 1705 1706 assert((size & PAGE_MASK) == 0); 1707 1708 alignment = PAGE_CEILING(alignment); 1709 alloc_size = size + alignment - PAGE; 1710 1711 malloc_mutex_lock(&arena->lock); 1712 run = arena_run_alloc_large(arena, alloc_size, false); 1713 if (run == NULL) { 1714 malloc_mutex_unlock(&arena->lock); 1715 return (NULL); 1716 } 1717 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1718 1719 leadsize = ALIGNMENT_CEILING((uintptr_t)run, alignment) - 1720 (uintptr_t)run; 1721 assert(alloc_size >= leadsize + size); 1722 trailsize = alloc_size - leadsize - size; 1723 ret = (void *)((uintptr_t)run + leadsize); 1724 if (leadsize != 0) { 1725 arena_run_trim_head(arena, chunk, run, alloc_size, alloc_size - 1726 leadsize); 1727 } 1728 if (trailsize != 0) { 1729 arena_run_trim_tail(arena, chunk, ret, size + trailsize, size, 1730 false); 1731 } 1732 arena_run_init_large(arena, (arena_run_t *)ret, size, zero); 1733 1734 if (config_stats) { 1735 arena->stats.nmalloc_large++; 1736 arena->stats.nrequests_large++; 1737 arena->stats.allocated_large += size; 1738 arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; 1739 arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; 1740 arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; 1741 } 1742 malloc_mutex_unlock(&arena->lock); 1743 1744 if (config_fill && zero == false) { 1745 if (opt_junk) 1746 memset(ret, 0xa5, size); 1747 else if (opt_zero) 1748 memset(ret, 0, size); 1749 } 1750 return (ret); 1751} 1752 1753void 1754arena_prof_promoted(const void *ptr, size_t size) 1755{ 1756 arena_chunk_t *chunk; 1757 size_t pageind, binind; 1758 1759 cassert(config_prof); 1760 assert(ptr != NULL); 1761 assert(CHUNK_ADDR2BASE(ptr) != ptr); 1762 assert(isalloc(ptr, false) == PAGE); 1763 assert(isalloc(ptr, true) == PAGE); 1764 assert(size <= SMALL_MAXCLASS); 1765 1766 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 1767 pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 1768 binind = SMALL_SIZE2BIN(size); 1769 assert(binind < NBINS); 1770 arena_mapbits_large_binind_set(chunk, pageind, binind); 1771 1772 assert(isalloc(ptr, false) == PAGE); 1773 assert(isalloc(ptr, true) == size); 1774} 1775 1776static void 1777arena_dissociate_bin_run(arena_chunk_t *chunk, arena_run_t *run, 1778 arena_bin_t *bin) 1779{ 1780 1781 /* Dissociate run from bin. */ 1782 if (run == bin->runcur) 1783 bin->runcur = NULL; 1784 else { 1785 size_t binind = arena_bin_index(chunk->arena, bin); 1786 arena_bin_info_t *bin_info = &arena_bin_info[binind]; 1787 1788 if (bin_info->nregs != 1) { 1789 /* 1790 * This block's conditional is necessary because if the 1791 * run only contains one region, then it never gets 1792 * inserted into the non-full runs tree. 1793 */ 1794 arena_bin_runs_remove(bin, run); 1795 } 1796 } 1797} 1798 1799static void 1800arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1801 arena_bin_t *bin) 1802{ 1803 size_t binind; 1804 arena_bin_info_t *bin_info; 1805 size_t npages, run_ind, past; 1806 1807 assert(run != bin->runcur); 1808 assert(arena_run_tree_search(&bin->runs, 1809 arena_mapp_get(chunk, ((uintptr_t)run-(uintptr_t)chunk)>>LG_PAGE)) 1810 == NULL); 1811 1812 binind = arena_bin_index(chunk->arena, run->bin); 1813 bin_info = &arena_bin_info[binind]; 1814 1815 malloc_mutex_unlock(&bin->lock); 1816 /******************************/ 1817 npages = bin_info->run_size >> LG_PAGE; 1818 run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); 1819 past = (size_t)(PAGE_CEILING((uintptr_t)run + 1820 (uintptr_t)bin_info->reg0_offset + (uintptr_t)(run->nextind * 1821 bin_info->reg_interval - bin_info->redzone_size) - 1822 (uintptr_t)chunk) >> LG_PAGE); 1823 malloc_mutex_lock(&arena->lock); 1824 1825 /* 1826 * If the run was originally clean, and some pages were never touched, 1827 * trim the clean pages before deallocating the dirty portion of the 1828 * run. 1829 */ 1830 assert(arena_mapbits_dirty_get(chunk, run_ind) == 1831 arena_mapbits_dirty_get(chunk, run_ind+npages-1)); 1832 if (arena_mapbits_dirty_get(chunk, run_ind) == 0 && past - run_ind < 1833 npages) { 1834 /* Trim clean pages. Convert to large run beforehand. */ 1835 assert(npages > 0); 1836 arena_mapbits_large_set(chunk, run_ind, bin_info->run_size, 0); 1837 arena_mapbits_large_set(chunk, run_ind+npages-1, 0, 0); 1838 arena_run_trim_tail(arena, chunk, run, (npages << LG_PAGE), 1839 ((past - run_ind) << LG_PAGE), false); 1840 /* npages = past - run_ind; */ 1841 } 1842 arena_run_dalloc(arena, run, true, false); 1843 malloc_mutex_unlock(&arena->lock); 1844 /****************************/ 1845 malloc_mutex_lock(&bin->lock); 1846 if (config_stats) 1847 bin->stats.curruns--; 1848} 1849 1850static void 1851arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1852 arena_bin_t *bin) 1853{ 1854 1855 /* 1856 * Make sure that if bin->runcur is non-NULL, it refers to the lowest 1857 * non-full run. It is okay to NULL runcur out rather than proactively 1858 * keeping it pointing at the lowest non-full run. 1859 */ 1860 if ((uintptr_t)run < (uintptr_t)bin->runcur) { 1861 /* Switch runcur. */ 1862 if (bin->runcur->nfree > 0) 1863 arena_bin_runs_insert(bin, bin->runcur); 1864 bin->runcur = run; 1865 if (config_stats) 1866 bin->stats.reruns++; 1867 } else 1868 arena_bin_runs_insert(bin, run); 1869} 1870 1871void 1872arena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1873 arena_chunk_map_t *mapelm) 1874{ 1875 size_t pageind; 1876 arena_run_t *run; 1877 arena_bin_t *bin; 1878 arena_bin_info_t *bin_info; 1879 size_t size, binind; 1880 1881 pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 1882 run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - 1883 arena_mapbits_small_runind_get(chunk, pageind)) << LG_PAGE)); 1884 bin = run->bin; 1885 binind = arena_ptr_small_binind_get(ptr, mapelm->bits); 1886 bin_info = &arena_bin_info[binind]; 1887 if (config_fill || config_stats) 1888 size = bin_info->reg_size; 1889 1890 if (config_fill && opt_junk) 1891 arena_dalloc_junk_small(ptr, bin_info); 1892 1893 arena_run_reg_dalloc(run, ptr); 1894 if (run->nfree == bin_info->nregs) { 1895 arena_dissociate_bin_run(chunk, run, bin); 1896 arena_dalloc_bin_run(arena, chunk, run, bin); 1897 } else if (run->nfree == 1 && run != bin->runcur) 1898 arena_bin_lower_run(arena, chunk, run, bin); 1899 1900 if (config_stats) { 1901 bin->stats.allocated -= size; 1902 bin->stats.ndalloc++; 1903 } 1904} 1905 1906void 1907arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1908 size_t pageind, arena_chunk_map_t *mapelm) 1909{ 1910 arena_run_t *run; 1911 arena_bin_t *bin; 1912 1913 run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - 1914 arena_mapbits_small_runind_get(chunk, pageind)) << LG_PAGE)); 1915 bin = run->bin; 1916 malloc_mutex_lock(&bin->lock); 1917 arena_dalloc_bin_locked(arena, chunk, ptr, mapelm); 1918 malloc_mutex_unlock(&bin->lock); 1919} 1920 1921void 1922arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1923 size_t pageind) 1924{ 1925 arena_chunk_map_t *mapelm; 1926 1927 if (config_debug) { 1928 /* arena_ptr_small_binind_get() does extra sanity checking. */ 1929 assert(arena_ptr_small_binind_get(ptr, arena_mapbits_get(chunk, 1930 pageind)) != BININD_INVALID); 1931 } 1932 mapelm = arena_mapp_get(chunk, pageind); 1933 arena_dalloc_bin(arena, chunk, ptr, pageind, mapelm); 1934} 1935 1936#ifdef JEMALLOC_JET 1937#undef arena_dalloc_junk_large 1938#define arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large_impl) 1939#endif 1940static void 1941arena_dalloc_junk_large(void *ptr, size_t usize) 1942{ 1943 1944 if (config_fill && opt_junk) 1945 memset(ptr, 0x5a, usize); 1946} 1947#ifdef JEMALLOC_JET 1948#undef arena_dalloc_junk_large 1949#define arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large) 1950arena_dalloc_junk_large_t *arena_dalloc_junk_large = 1951 JEMALLOC_N(arena_dalloc_junk_large_impl); 1952#endif 1953 1954void 1955arena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr) 1956{ 1957 1958 if (config_fill || config_stats) { 1959 size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 1960 size_t usize = arena_mapbits_large_size_get(chunk, pageind); 1961 1962 arena_dalloc_junk_large(ptr, usize); 1963 if (config_stats) { 1964 arena->stats.ndalloc_large++; 1965 arena->stats.allocated_large -= usize; 1966 arena->stats.lstats[(usize >> LG_PAGE) - 1].ndalloc++; 1967 arena->stats.lstats[(usize >> LG_PAGE) - 1].curruns--; 1968 } 1969 } 1970 1971 arena_run_dalloc(arena, (arena_run_t *)ptr, true, false); 1972} 1973 1974void 1975arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr) 1976{ 1977 1978 malloc_mutex_lock(&arena->lock); 1979 arena_dalloc_large_locked(arena, chunk, ptr); 1980 malloc_mutex_unlock(&arena->lock); 1981} 1982 1983static void 1984arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1985 size_t oldsize, size_t size) 1986{ 1987 1988 assert(size < oldsize); 1989 1990 /* 1991 * Shrink the run, and make trailing pages available for other 1992 * allocations. 1993 */ 1994 malloc_mutex_lock(&arena->lock); 1995 arena_run_trim_tail(arena, chunk, (arena_run_t *)ptr, oldsize, size, 1996 true); 1997 if (config_stats) { 1998 arena->stats.ndalloc_large++; 1999 arena->stats.allocated_large -= oldsize; 2000 arena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++; 2001 arena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--; 2002 2003 arena->stats.nmalloc_large++; 2004 arena->stats.nrequests_large++; 2005 arena->stats.allocated_large += size; 2006 arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; 2007 arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; 2008 arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; 2009 } 2010 malloc_mutex_unlock(&arena->lock); 2011} 2012 2013static bool 2014arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, 2015 size_t oldsize, size_t size, size_t extra, bool zero) 2016{ 2017 size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 2018 size_t npages = oldsize >> LG_PAGE; 2019 size_t followsize; 2020 2021 assert(oldsize == arena_mapbits_large_size_get(chunk, pageind)); 2022 2023 /* Try to extend the run. */ 2024 assert(size + extra > oldsize); 2025 malloc_mutex_lock(&arena->lock); 2026 if (pageind + npages < chunk_npages && 2027 arena_mapbits_allocated_get(chunk, pageind+npages) == 0 && 2028 (followsize = arena_mapbits_unallocated_size_get(chunk, 2029 pageind+npages)) >= size - oldsize) { 2030 /* 2031 * The next run is available and sufficiently large. Split the 2032 * following run, then merge the first part with the existing 2033 * allocation. 2034 */ 2035 size_t flag_dirty; 2036 size_t splitsize = (oldsize + followsize <= size + extra) 2037 ? followsize : size + extra - oldsize; 2038 arena_run_split_large(arena, (arena_run_t *)((uintptr_t)chunk + 2039 ((pageind+npages) << LG_PAGE)), splitsize, zero); 2040 2041 size = oldsize + splitsize; 2042 npages = size >> LG_PAGE; 2043 2044 /* 2045 * Mark the extended run as dirty if either portion of the run 2046 * was dirty before allocation. This is rather pedantic, 2047 * because there's not actually any sequence of events that 2048 * could cause the resulting run to be passed to 2049 * arena_run_dalloc() with the dirty argument set to false 2050 * (which is when dirty flag consistency would really matter). 2051 */ 2052 flag_dirty = arena_mapbits_dirty_get(chunk, pageind) | 2053 arena_mapbits_dirty_get(chunk, pageind+npages-1); 2054 arena_mapbits_large_set(chunk, pageind, size, flag_dirty); 2055 arena_mapbits_large_set(chunk, pageind+npages-1, 0, flag_dirty); 2056 2057 if (config_stats) { 2058 arena->stats.ndalloc_large++; 2059 arena->stats.allocated_large -= oldsize; 2060 arena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++; 2061 arena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--; 2062 2063 arena->stats.nmalloc_large++; 2064 arena->stats.nrequests_large++; 2065 arena->stats.allocated_large += size; 2066 arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; 2067 arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; 2068 arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; 2069 } 2070 malloc_mutex_unlock(&arena->lock); 2071 return (false); 2072 } 2073 malloc_mutex_unlock(&arena->lock); 2074 2075 return (true); 2076} 2077 2078#ifdef JEMALLOC_JET 2079#undef arena_ralloc_junk_large 2080#define arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large_impl) 2081#endif 2082static void 2083arena_ralloc_junk_large(void *ptr, size_t old_usize, size_t usize) 2084{ 2085 2086 if (config_fill && opt_junk) { 2087 memset((void *)((uintptr_t)ptr + usize), 0x5a, 2088 old_usize - usize); 2089 } 2090} 2091#ifdef JEMALLOC_JET 2092#undef arena_ralloc_junk_large 2093#define arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large) 2094arena_ralloc_junk_large_t *arena_ralloc_junk_large = 2095 JEMALLOC_N(arena_ralloc_junk_large_impl); 2096#endif 2097 2098/* 2099 * Try to resize a large allocation, in order to avoid copying. This will 2100 * always fail if growing an object, and the following run is already in use. 2101 */ 2102static bool 2103arena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra, 2104 bool zero) 2105{ 2106 size_t psize; 2107 2108 psize = PAGE_CEILING(size + extra); 2109 if (psize == oldsize) { 2110 /* Same size class. */ 2111 return (false); 2112 } else { 2113 arena_chunk_t *chunk; 2114 arena_t *arena; 2115 2116 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 2117 arena = chunk->arena; 2118 2119 if (psize < oldsize) { 2120 /* Fill before shrinking in order avoid a race. */ 2121 arena_ralloc_junk_large(ptr, oldsize, psize); 2122 arena_ralloc_large_shrink(arena, chunk, ptr, oldsize, 2123 psize); 2124 return (false); 2125 } else { 2126 bool ret = arena_ralloc_large_grow(arena, chunk, ptr, 2127 oldsize, PAGE_CEILING(size), 2128 psize - PAGE_CEILING(size), zero); 2129 if (config_fill && ret == false && zero == false) { 2130 if (opt_junk) { 2131 memset((void *)((uintptr_t)ptr + 2132 oldsize), 0xa5, isalloc(ptr, 2133 config_prof) - oldsize); 2134 } else if (opt_zero) { 2135 memset((void *)((uintptr_t)ptr + 2136 oldsize), 0, isalloc(ptr, 2137 config_prof) - oldsize); 2138 } 2139 } 2140 return (ret); 2141 } 2142 } 2143} 2144 2145bool 2146arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra, 2147 bool zero) 2148{ 2149 2150 /* 2151 * Avoid moving the allocation if the size class can be left the same. 2152 */ 2153 if (oldsize <= arena_maxclass) { 2154 if (oldsize <= SMALL_MAXCLASS) { 2155 assert(arena_bin_info[SMALL_SIZE2BIN(oldsize)].reg_size 2156 == oldsize); 2157 if ((size + extra <= SMALL_MAXCLASS && 2158 SMALL_SIZE2BIN(size + extra) == 2159 SMALL_SIZE2BIN(oldsize)) || (size <= oldsize && 2160 size + extra >= oldsize)) 2161 return (false); 2162 } else { 2163 assert(size <= arena_maxclass); 2164 if (size + extra > SMALL_MAXCLASS) { 2165 if (arena_ralloc_large(ptr, oldsize, size, 2166 extra, zero) == false) 2167 return (false); 2168 } 2169 } 2170 } 2171 2172 /* Reallocation would require a move. */ 2173 return (true); 2174} 2175 2176void * 2177arena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size, 2178 size_t extra, size_t alignment, bool zero, bool try_tcache_alloc, 2179 bool try_tcache_dalloc) 2180{ 2181 void *ret; 2182 size_t copysize; 2183 2184 /* Try to avoid moving the allocation. */ 2185 if (arena_ralloc_no_move(ptr, oldsize, size, extra, zero) == false) 2186 return (ptr); 2187 2188 /* 2189 * size and oldsize are different enough that we need to move the 2190 * object. In that case, fall back to allocating new space and 2191 * copying. 2192 */ 2193 if (alignment != 0) { 2194 size_t usize = sa2u(size + extra, alignment); 2195 if (usize == 0) 2196 return (NULL); 2197 ret = ipalloct(usize, alignment, zero, try_tcache_alloc, arena); 2198 } else 2199 ret = arena_malloc(arena, size + extra, zero, try_tcache_alloc); 2200 2201 if (ret == NULL) { 2202 if (extra == 0) 2203 return (NULL); 2204 /* Try again, this time without extra. */ 2205 if (alignment != 0) { 2206 size_t usize = sa2u(size, alignment); 2207 if (usize == 0) 2208 return (NULL); 2209 ret = ipalloct(usize, alignment, zero, try_tcache_alloc, 2210 arena); 2211 } else 2212 ret = arena_malloc(arena, size, zero, try_tcache_alloc); 2213 2214 if (ret == NULL) 2215 return (NULL); 2216 } 2217 2218 /* Junk/zero-filling were already done by ipalloc()/arena_malloc(). */ 2219 2220 /* 2221 * Copy at most size bytes (not size+extra), since the caller has no 2222 * expectation that the extra bytes will be reliably preserved. 2223 */ 2224 copysize = (size < oldsize) ? size : oldsize; 2225 VALGRIND_MAKE_MEM_UNDEFINED(ret, copysize); 2226 memcpy(ret, ptr, copysize); 2227 iqalloct(ptr, try_tcache_dalloc); 2228 return (ret); 2229} 2230 2231dss_prec_t 2232arena_dss_prec_get(arena_t *arena) 2233{ 2234 dss_prec_t ret; 2235 2236 malloc_mutex_lock(&arena->lock); 2237 ret = arena->dss_prec; 2238 malloc_mutex_unlock(&arena->lock); 2239 return (ret); 2240} 2241 2242void 2243arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec) 2244{ 2245 2246 malloc_mutex_lock(&arena->lock); 2247 arena->dss_prec = dss_prec; 2248 malloc_mutex_unlock(&arena->lock); 2249} 2250 2251void 2252arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive, 2253 size_t *ndirty, arena_stats_t *astats, malloc_bin_stats_t *bstats, 2254 malloc_large_stats_t *lstats) 2255{ 2256 unsigned i; 2257 2258 malloc_mutex_lock(&arena->lock); 2259 *dss = dss_prec_names[arena->dss_prec]; 2260 *nactive += arena->nactive; 2261 *ndirty += arena->ndirty; 2262 2263 astats->mapped += arena->stats.mapped; 2264 astats->npurge += arena->stats.npurge; 2265 astats->nmadvise += arena->stats.nmadvise; 2266 astats->purged += arena->stats.purged; 2267 astats->allocated_large += arena->stats.allocated_large; 2268 astats->nmalloc_large += arena->stats.nmalloc_large; 2269 astats->ndalloc_large += arena->stats.ndalloc_large; 2270 astats->nrequests_large += arena->stats.nrequests_large; 2271 2272 for (i = 0; i < nlclasses; i++) { 2273 lstats[i].nmalloc += arena->stats.lstats[i].nmalloc; 2274 lstats[i].ndalloc += arena->stats.lstats[i].ndalloc; 2275 lstats[i].nrequests += arena->stats.lstats[i].nrequests; 2276 lstats[i].curruns += arena->stats.lstats[i].curruns; 2277 } 2278 malloc_mutex_unlock(&arena->lock); 2279 2280 for (i = 0; i < NBINS; i++) { 2281 arena_bin_t *bin = &arena->bins[i]; 2282 2283 malloc_mutex_lock(&bin->lock); 2284 bstats[i].allocated += bin->stats.allocated; 2285 bstats[i].nmalloc += bin->stats.nmalloc; 2286 bstats[i].ndalloc += bin->stats.ndalloc; 2287 bstats[i].nrequests += bin->stats.nrequests; 2288 if (config_tcache) { 2289 bstats[i].nfills += bin->stats.nfills; 2290 bstats[i].nflushes += bin->stats.nflushes; 2291 } 2292 bstats[i].nruns += bin->stats.nruns; 2293 bstats[i].reruns += bin->stats.reruns; 2294 bstats[i].curruns += bin->stats.curruns; 2295 malloc_mutex_unlock(&bin->lock); 2296 } 2297} 2298 2299bool 2300arena_new(arena_t *arena, unsigned ind) 2301{ 2302 unsigned i; 2303 arena_bin_t *bin; 2304 2305 arena->ind = ind; 2306 arena->nthreads = 0; 2307 2308 if (malloc_mutex_init(&arena->lock)) 2309 return (true); 2310 2311 if (config_stats) { 2312 memset(&arena->stats, 0, sizeof(arena_stats_t)); 2313 arena->stats.lstats = 2314 (malloc_large_stats_t *)base_alloc(nlclasses * 2315 sizeof(malloc_large_stats_t)); 2316 if (arena->stats.lstats == NULL) 2317 return (true); 2318 memset(arena->stats.lstats, 0, nlclasses * 2319 sizeof(malloc_large_stats_t)); 2320 if (config_tcache) 2321 ql_new(&arena->tcache_ql); 2322 } 2323 2324 if (config_prof) 2325 arena->prof_accumbytes = 0; 2326 2327 arena->dss_prec = chunk_dss_prec_get(); 2328 2329 /* Initialize chunks. */ 2330 arena_chunk_dirty_new(&arena->chunks_dirty); 2331 arena->spare = NULL; 2332 2333 arena->nactive = 0; 2334 arena->ndirty = 0; 2335 arena->npurgatory = 0; 2336 2337 arena_avail_tree_new(&arena->runs_avail); 2338 2339 /* Initialize bins. */ 2340 for (i = 0; i < NBINS; i++) { 2341 bin = &arena->bins[i]; 2342 if (malloc_mutex_init(&bin->lock)) 2343 return (true); 2344 bin->runcur = NULL; 2345 arena_run_tree_new(&bin->runs); 2346 if (config_stats) 2347 memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); 2348 } 2349 2350 return (false); 2351} 2352 2353/* 2354 * Calculate bin_info->run_size such that it meets the following constraints: 2355 * 2356 * *) bin_info->run_size >= min_run_size 2357 * *) bin_info->run_size <= arena_maxclass 2358 * *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed). 2359 * *) bin_info->nregs <= RUN_MAXREGS 2360 * 2361 * bin_info->nregs, bin_info->bitmap_offset, and bin_info->reg0_offset are also 2362 * calculated here, since these settings are all interdependent. 2363 */ 2364static size_t 2365bin_info_run_size_calc(arena_bin_info_t *bin_info, size_t min_run_size) 2366{ 2367 size_t pad_size; 2368 size_t try_run_size, good_run_size; 2369 uint32_t try_nregs, good_nregs; 2370 uint32_t try_hdr_size, good_hdr_size; 2371 uint32_t try_bitmap_offset, good_bitmap_offset; 2372 uint32_t try_ctx0_offset, good_ctx0_offset; 2373 uint32_t try_redzone0_offset, good_redzone0_offset; 2374 2375 assert(min_run_size >= PAGE); 2376 assert(min_run_size <= arena_maxclass); 2377 2378 /* 2379 * Determine redzone size based on minimum alignment and minimum 2380 * redzone size. Add padding to the end of the run if it is needed to 2381 * align the regions. The padding allows each redzone to be half the 2382 * minimum alignment; without the padding, each redzone would have to 2383 * be twice as large in order to maintain alignment. 2384 */ 2385 if (config_fill && opt_redzone) { 2386 size_t align_min = ZU(1) << (ffs(bin_info->reg_size) - 1); 2387 if (align_min <= REDZONE_MINSIZE) { 2388 bin_info->redzone_size = REDZONE_MINSIZE; 2389 pad_size = 0; 2390 } else { 2391 bin_info->redzone_size = align_min >> 1; 2392 pad_size = bin_info->redzone_size; 2393 } 2394 } else { 2395 bin_info->redzone_size = 0; 2396 pad_size = 0; 2397 } 2398 bin_info->reg_interval = bin_info->reg_size + 2399 (bin_info->redzone_size << 1); 2400 2401 /* 2402 * Calculate known-valid settings before entering the run_size 2403 * expansion loop, so that the first part of the loop always copies 2404 * valid settings. 2405 * 2406 * The do..while loop iteratively reduces the number of regions until 2407 * the run header and the regions no longer overlap. A closed formula 2408 * would be quite messy, since there is an interdependency between the 2409 * header's mask length and the number of regions. 2410 */ 2411 try_run_size = min_run_size; 2412 try_nregs = ((try_run_size - sizeof(arena_run_t)) / 2413 bin_info->reg_interval) 2414 + 1; /* Counter-act try_nregs-- in loop. */ 2415 if (try_nregs > RUN_MAXREGS) { 2416 try_nregs = RUN_MAXREGS 2417 + 1; /* Counter-act try_nregs-- in loop. */ 2418 } 2419 do { 2420 try_nregs--; 2421 try_hdr_size = sizeof(arena_run_t); 2422 /* Pad to a long boundary. */ 2423 try_hdr_size = LONG_CEILING(try_hdr_size); 2424 try_bitmap_offset = try_hdr_size; 2425 /* Add space for bitmap. */ 2426 try_hdr_size += bitmap_size(try_nregs); 2427 if (config_prof && opt_prof && prof_promote == false) { 2428 /* Pad to a quantum boundary. */ 2429 try_hdr_size = QUANTUM_CEILING(try_hdr_size); 2430 try_ctx0_offset = try_hdr_size; 2431 /* Add space for one (prof_ctx_t *) per region. */ 2432 try_hdr_size += try_nregs * sizeof(prof_ctx_t *); 2433 } else 2434 try_ctx0_offset = 0; 2435 try_redzone0_offset = try_run_size - (try_nregs * 2436 bin_info->reg_interval) - pad_size; 2437 } while (try_hdr_size > try_redzone0_offset); 2438 2439 /* run_size expansion loop. */ 2440 do { 2441 /* 2442 * Copy valid settings before trying more aggressive settings. 2443 */ 2444 good_run_size = try_run_size; 2445 good_nregs = try_nregs; 2446 good_hdr_size = try_hdr_size; 2447 good_bitmap_offset = try_bitmap_offset; 2448 good_ctx0_offset = try_ctx0_offset; 2449 good_redzone0_offset = try_redzone0_offset; 2450 2451 /* Try more aggressive settings. */ 2452 try_run_size += PAGE; 2453 try_nregs = ((try_run_size - sizeof(arena_run_t) - pad_size) / 2454 bin_info->reg_interval) 2455 + 1; /* Counter-act try_nregs-- in loop. */ 2456 if (try_nregs > RUN_MAXREGS) { 2457 try_nregs = RUN_MAXREGS 2458 + 1; /* Counter-act try_nregs-- in loop. */ 2459 } 2460 do { 2461 try_nregs--; 2462 try_hdr_size = sizeof(arena_run_t); 2463 /* Pad to a long boundary. */ 2464 try_hdr_size = LONG_CEILING(try_hdr_size); 2465 try_bitmap_offset = try_hdr_size; 2466 /* Add space for bitmap. */ 2467 try_hdr_size += bitmap_size(try_nregs); 2468 if (config_prof && opt_prof && prof_promote == false) { 2469 /* Pad to a quantum boundary. */ 2470 try_hdr_size = QUANTUM_CEILING(try_hdr_size); 2471 try_ctx0_offset = try_hdr_size; 2472 /* 2473 * Add space for one (prof_ctx_t *) per region. 2474 */ 2475 try_hdr_size += try_nregs * 2476 sizeof(prof_ctx_t *); 2477 } 2478 try_redzone0_offset = try_run_size - (try_nregs * 2479 bin_info->reg_interval) - pad_size; 2480 } while (try_hdr_size > try_redzone0_offset); 2481 } while (try_run_size <= arena_maxclass 2482 && RUN_MAX_OVRHD * (bin_info->reg_interval << 3) > 2483 RUN_MAX_OVRHD_RELAX 2484 && (try_redzone0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size 2485 && try_nregs < RUN_MAXREGS); 2486 2487 assert(good_hdr_size <= good_redzone0_offset); 2488 2489 /* Copy final settings. */ 2490 bin_info->run_size = good_run_size; 2491 bin_info->nregs = good_nregs; 2492 bin_info->bitmap_offset = good_bitmap_offset; 2493 bin_info->ctx0_offset = good_ctx0_offset; 2494 bin_info->reg0_offset = good_redzone0_offset + bin_info->redzone_size; 2495 2496 assert(bin_info->reg0_offset - bin_info->redzone_size + (bin_info->nregs 2497 * bin_info->reg_interval) + pad_size == bin_info->run_size); 2498 2499 return (good_run_size); 2500} 2501 2502static void 2503bin_info_init(void) 2504{ 2505 arena_bin_info_t *bin_info; 2506 size_t prev_run_size = PAGE; 2507 2508#define SIZE_CLASS(bin, delta, size) \ 2509 bin_info = &arena_bin_info[bin]; \ 2510 bin_info->reg_size = size; \ 2511 prev_run_size = bin_info_run_size_calc(bin_info, prev_run_size);\ 2512 bitmap_info_init(&bin_info->bitmap_info, bin_info->nregs); 2513 SIZE_CLASSES 2514#undef SIZE_CLASS 2515} 2516 2517void 2518arena_boot(void) 2519{ 2520 size_t header_size; 2521 unsigned i; 2522 2523 /* 2524 * Compute the header size such that it is large enough to contain the 2525 * page map. The page map is biased to omit entries for the header 2526 * itself, so some iteration is necessary to compute the map bias. 2527 * 2528 * 1) Compute safe header_size and map_bias values that include enough 2529 * space for an unbiased page map. 2530 * 2) Refine map_bias based on (1) to omit the header pages in the page 2531 * map. The resulting map_bias may be one too small. 2532 * 3) Refine map_bias based on (2). The result will be >= the result 2533 * from (2), and will always be correct. 2534 */ 2535 map_bias = 0; 2536 for (i = 0; i < 3; i++) { 2537 header_size = offsetof(arena_chunk_t, map) + 2538 (sizeof(arena_chunk_map_t) * (chunk_npages-map_bias)); 2539 map_bias = (header_size >> LG_PAGE) + ((header_size & PAGE_MASK) 2540 != 0); 2541 } 2542 assert(map_bias > 0); 2543 2544 arena_maxclass = chunksize - (map_bias << LG_PAGE); 2545 2546 bin_info_init(); 2547} 2548 2549void 2550arena_prefork(arena_t *arena) 2551{ 2552 unsigned i; 2553 2554 malloc_mutex_prefork(&arena->lock); 2555 for (i = 0; i < NBINS; i++) 2556 malloc_mutex_prefork(&arena->bins[i].lock); 2557} 2558 2559void 2560arena_postfork_parent(arena_t *arena) 2561{ 2562 unsigned i; 2563 2564 for (i = 0; i < NBINS; i++) 2565 malloc_mutex_postfork_parent(&arena->bins[i].lock); 2566 malloc_mutex_postfork_parent(&arena->lock); 2567} 2568 2569void 2570arena_postfork_child(arena_t *arena) 2571{ 2572 unsigned i; 2573 2574 for (i = 0; i < NBINS; i++) 2575 malloc_mutex_postfork_child(&arena->bins[i].lock); 2576 malloc_mutex_postfork_child(&arena->lock); 2577}
|