1a4bd5210SJason Evans #define JEMALLOC_TCACHE_C_ 2*b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_preamble.h" 3*b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_internal_includes.h" 4*b7eaed25SJason Evans 5*b7eaed25SJason Evans #include "jemalloc/internal/assert.h" 6*b7eaed25SJason Evans #include "jemalloc/internal/mutex.h" 7*b7eaed25SJason Evans #include "jemalloc/internal/size_classes.h" 8a4bd5210SJason Evans 9a4bd5210SJason Evans /******************************************************************************/ 10a4bd5210SJason Evans /* Data. */ 11a4bd5210SJason Evans 12a4bd5210SJason Evans bool opt_tcache = true; 13a4bd5210SJason Evans ssize_t opt_lg_tcache_max = LG_TCACHE_MAXCLASS_DEFAULT; 14a4bd5210SJason Evans 15a4bd5210SJason Evans tcache_bin_info_t *tcache_bin_info; 16a4bd5210SJason Evans static unsigned stack_nelms; /* Total stack elms per tcache. */ 17a4bd5210SJason Evans 18df0d881dSJason Evans unsigned nhbins; 19a4bd5210SJason Evans size_t tcache_maxclass; 20a4bd5210SJason Evans 21d0e79aa3SJason Evans tcaches_t *tcaches; 22d0e79aa3SJason Evans 23d0e79aa3SJason Evans /* Index of first element within tcaches that has never been used. */ 24d0e79aa3SJason Evans static unsigned tcaches_past; 25d0e79aa3SJason Evans 26d0e79aa3SJason Evans /* Head of singly linked list tracking available tcaches elements. */ 27d0e79aa3SJason Evans static tcaches_t *tcaches_avail; 28d0e79aa3SJason Evans 298244f2aaSJason Evans /* Protects tcaches{,_past,_avail}. */ 308244f2aaSJason Evans static malloc_mutex_t tcaches_mtx; 318244f2aaSJason Evans 32a4bd5210SJason Evans /******************************************************************************/ 33a4bd5210SJason Evans 341f0a49e8SJason Evans size_t 35*b7eaed25SJason Evans tcache_salloc(tsdn_t *tsdn, const void *ptr) { 36*b7eaed25SJason Evans return arena_salloc(tsdn, ptr); 378ed34ab0SJason Evans } 388ed34ab0SJason Evans 39e722f8f8SJason Evans void 40*b7eaed25SJason Evans tcache_event_hard(tsd_t *tsd, tcache_t *tcache) { 41536b3538SJason Evans szind_t binind = tcache->next_gc_bin; 42e722f8f8SJason Evans 43*b7eaed25SJason Evans tcache_bin_t *tbin; 44*b7eaed25SJason Evans if (binind < NBINS) { 45*b7eaed25SJason Evans tbin = tcache_small_bin_get(tcache, binind); 46*b7eaed25SJason Evans } else { 47*b7eaed25SJason Evans tbin = tcache_large_bin_get(tcache, binind); 48*b7eaed25SJason Evans } 49e722f8f8SJason Evans if (tbin->low_water > 0) { 50e722f8f8SJason Evans /* 51e722f8f8SJason Evans * Flush (ceiling) 3/4 of the objects below the low water mark. 52e722f8f8SJason Evans */ 53e722f8f8SJason Evans if (binind < NBINS) { 54d0e79aa3SJason Evans tcache_bin_flush_small(tsd, tcache, tbin, binind, 55d0e79aa3SJason Evans tbin->ncached - tbin->low_water + (tbin->low_water 56d0e79aa3SJason Evans >> 2)); 57*b7eaed25SJason Evans /* 58*b7eaed25SJason Evans * Reduce fill count by 2X. Limit lg_fill_div such that 59*b7eaed25SJason Evans * the fill count is always at least 1. 60*b7eaed25SJason Evans */ 61*b7eaed25SJason Evans tcache_bin_info_t *tbin_info = &tcache_bin_info[binind]; 62*b7eaed25SJason Evans if ((tbin_info->ncached_max >> 63*b7eaed25SJason Evans (tcache->lg_fill_div[binind] + 1)) >= 1) { 64*b7eaed25SJason Evans tcache->lg_fill_div[binind]++; 65*b7eaed25SJason Evans } 66e722f8f8SJason Evans } else { 67d0e79aa3SJason Evans tcache_bin_flush_large(tsd, tbin, binind, tbin->ncached 68d0e79aa3SJason Evans - tbin->low_water + (tbin->low_water >> 2), tcache); 69e722f8f8SJason Evans } 70e722f8f8SJason Evans } else if (tbin->low_water < 0) { 71e722f8f8SJason Evans /* 72*b7eaed25SJason Evans * Increase fill count by 2X for small bins. Make sure 73*b7eaed25SJason Evans * lg_fill_div stays greater than 0. 74e722f8f8SJason Evans */ 75*b7eaed25SJason Evans if (binind < NBINS && tcache->lg_fill_div[binind] > 1) { 76*b7eaed25SJason Evans tcache->lg_fill_div[binind]--; 77*b7eaed25SJason Evans } 78e722f8f8SJason Evans } 79e722f8f8SJason Evans tbin->low_water = tbin->ncached; 80e722f8f8SJason Evans 81e722f8f8SJason Evans tcache->next_gc_bin++; 82*b7eaed25SJason Evans if (tcache->next_gc_bin == nhbins) { 83e722f8f8SJason Evans tcache->next_gc_bin = 0; 84e722f8f8SJason Evans } 85*b7eaed25SJason Evans } 86e722f8f8SJason Evans 87a4bd5210SJason Evans void * 881f0a49e8SJason Evans tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache, 89*b7eaed25SJason Evans tcache_bin_t *tbin, szind_t binind, bool *tcache_success) { 90a4bd5210SJason Evans void *ret; 91a4bd5210SJason Evans 92*b7eaed25SJason Evans assert(tcache->arena != NULL); 93*b7eaed25SJason Evans arena_tcache_fill_small(tsdn, arena, tcache, tbin, binind, 94*b7eaed25SJason Evans config_prof ? tcache->prof_accumbytes : 0); 95*b7eaed25SJason Evans if (config_prof) { 96a4bd5210SJason Evans tcache->prof_accumbytes = 0; 97*b7eaed25SJason Evans } 98df0d881dSJason Evans ret = tcache_alloc_easy(tbin, tcache_success); 99a4bd5210SJason Evans 100*b7eaed25SJason Evans return ret; 101a4bd5210SJason Evans } 102a4bd5210SJason Evans 103a4bd5210SJason Evans void 104d0e79aa3SJason Evans tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin, 105*b7eaed25SJason Evans szind_t binind, unsigned rem) { 106a4bd5210SJason Evans bool merged_stats = false; 107a4bd5210SJason Evans 108a4bd5210SJason Evans assert(binind < NBINS); 109a4bd5210SJason Evans assert(rem <= tbin->ncached); 110a4bd5210SJason Evans 111*b7eaed25SJason Evans arena_t *arena = tcache->arena; 112d0e79aa3SJason Evans assert(arena != NULL); 113*b7eaed25SJason Evans unsigned nflush = tbin->ncached - rem; 114*b7eaed25SJason Evans VARIABLE_ARRAY(extent_t *, item_extent, nflush); 115*b7eaed25SJason Evans /* Look up extent once per item. */ 116*b7eaed25SJason Evans for (unsigned i = 0 ; i < nflush; i++) { 117*b7eaed25SJason Evans item_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i)); 118*b7eaed25SJason Evans } 119*b7eaed25SJason Evans 120*b7eaed25SJason Evans while (nflush > 0) { 121a4bd5210SJason Evans /* Lock the arena bin associated with the first object. */ 122*b7eaed25SJason Evans extent_t *extent = item_extent[0]; 123*b7eaed25SJason Evans arena_t *bin_arena = extent_arena_get(extent); 124d0e79aa3SJason Evans arena_bin_t *bin = &bin_arena->bins[binind]; 125a4bd5210SJason Evans 126d0e79aa3SJason Evans if (config_prof && bin_arena == arena) { 1271f0a49e8SJason Evans if (arena_prof_accum(tsd_tsdn(tsd), arena, 128*b7eaed25SJason Evans tcache->prof_accumbytes)) { 1291f0a49e8SJason Evans prof_idump(tsd_tsdn(tsd)); 130*b7eaed25SJason Evans } 131a4bd5210SJason Evans tcache->prof_accumbytes = 0; 132a4bd5210SJason Evans } 133a4bd5210SJason Evans 1341f0a49e8SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock); 135d0e79aa3SJason Evans if (config_stats && bin_arena == arena) { 136d0e79aa3SJason Evans assert(!merged_stats); 137a4bd5210SJason Evans merged_stats = true; 138a4bd5210SJason Evans bin->stats.nflushes++; 139a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 140a4bd5210SJason Evans tbin->tstats.nrequests = 0; 141a4bd5210SJason Evans } 142*b7eaed25SJason Evans unsigned ndeferred = 0; 143*b7eaed25SJason Evans for (unsigned i = 0; i < nflush; i++) { 144*b7eaed25SJason Evans void *ptr = *(tbin->avail - 1 - i); 145*b7eaed25SJason Evans extent = item_extent[i]; 146*b7eaed25SJason Evans assert(ptr != NULL && extent != NULL); 147*b7eaed25SJason Evans 148*b7eaed25SJason Evans if (extent_arena_get(extent) == bin_arena) { 1491f0a49e8SJason Evans arena_dalloc_bin_junked_locked(tsd_tsdn(tsd), 150*b7eaed25SJason Evans bin_arena, extent, ptr); 151a4bd5210SJason Evans } else { 152a4bd5210SJason Evans /* 153a4bd5210SJason Evans * This object was allocated via a different 154a4bd5210SJason Evans * arena bin than the one that is currently 155a4bd5210SJason Evans * locked. Stash the object, so that it can be 156a4bd5210SJason Evans * handled in a future pass. 157a4bd5210SJason Evans */ 158df0d881dSJason Evans *(tbin->avail - 1 - ndeferred) = ptr; 159*b7eaed25SJason Evans item_extent[ndeferred] = extent; 160a4bd5210SJason Evans ndeferred++; 161a4bd5210SJason Evans } 162a4bd5210SJason Evans } 1631f0a49e8SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock); 1641f0a49e8SJason Evans arena_decay_ticks(tsd_tsdn(tsd), bin_arena, nflush - ndeferred); 165*b7eaed25SJason Evans nflush = ndeferred; 166a4bd5210SJason Evans } 167d0e79aa3SJason Evans if (config_stats && !merged_stats) { 168a4bd5210SJason Evans /* 169a4bd5210SJason Evans * The flush loop didn't happen to flush to this thread's 170a4bd5210SJason Evans * arena, so the stats didn't get merged. Manually do so now. 171a4bd5210SJason Evans */ 172d0e79aa3SJason Evans arena_bin_t *bin = &arena->bins[binind]; 1731f0a49e8SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock); 174a4bd5210SJason Evans bin->stats.nflushes++; 175a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 176a4bd5210SJason Evans tbin->tstats.nrequests = 0; 1771f0a49e8SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock); 178a4bd5210SJason Evans } 179a4bd5210SJason Evans 180df0d881dSJason Evans memmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem * 181df0d881dSJason Evans sizeof(void *)); 182a4bd5210SJason Evans tbin->ncached = rem; 183*b7eaed25SJason Evans if ((low_water_t)tbin->ncached < tbin->low_water) { 184a4bd5210SJason Evans tbin->low_water = tbin->ncached; 185a4bd5210SJason Evans } 186*b7eaed25SJason Evans } 187a4bd5210SJason Evans 188a4bd5210SJason Evans void 189536b3538SJason Evans tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, szind_t binind, 190*b7eaed25SJason Evans unsigned rem, tcache_t *tcache) { 191a4bd5210SJason Evans bool merged_stats = false; 192a4bd5210SJason Evans 193a4bd5210SJason Evans assert(binind < nhbins); 194a4bd5210SJason Evans assert(rem <= tbin->ncached); 195a4bd5210SJason Evans 196*b7eaed25SJason Evans arena_t *arena = tcache->arena; 197d0e79aa3SJason Evans assert(arena != NULL); 198*b7eaed25SJason Evans unsigned nflush = tbin->ncached - rem; 199*b7eaed25SJason Evans VARIABLE_ARRAY(extent_t *, item_extent, nflush); 200*b7eaed25SJason Evans /* Look up extent once per item. */ 201*b7eaed25SJason Evans for (unsigned i = 0 ; i < nflush; i++) { 202*b7eaed25SJason Evans item_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i)); 203*b7eaed25SJason Evans } 204*b7eaed25SJason Evans 205*b7eaed25SJason Evans while (nflush > 0) { 206a4bd5210SJason Evans /* Lock the arena associated with the first object. */ 207*b7eaed25SJason Evans extent_t *extent = item_extent[0]; 208*b7eaed25SJason Evans arena_t *locked_arena = extent_arena_get(extent); 209f8ca2db1SJason Evans UNUSED bool idump; 210a4bd5210SJason Evans 211*b7eaed25SJason Evans if (config_prof) { 212f8ca2db1SJason Evans idump = false; 213*b7eaed25SJason Evans } 214*b7eaed25SJason Evans 215*b7eaed25SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &locked_arena->large_mtx); 216*b7eaed25SJason Evans for (unsigned i = 0; i < nflush; i++) { 217*b7eaed25SJason Evans void *ptr = *(tbin->avail - 1 - i); 218*b7eaed25SJason Evans assert(ptr != NULL); 219*b7eaed25SJason Evans extent = item_extent[i]; 220*b7eaed25SJason Evans if (extent_arena_get(extent) == locked_arena) { 221*b7eaed25SJason Evans large_dalloc_prep_junked_locked(tsd_tsdn(tsd), 222*b7eaed25SJason Evans extent); 223*b7eaed25SJason Evans } 224*b7eaed25SJason Evans } 225d0e79aa3SJason Evans if ((config_prof || config_stats) && locked_arena == arena) { 226a4bd5210SJason Evans if (config_prof) { 227*b7eaed25SJason Evans idump = arena_prof_accum(tsd_tsdn(tsd), arena, 228a4bd5210SJason Evans tcache->prof_accumbytes); 229a4bd5210SJason Evans tcache->prof_accumbytes = 0; 230a4bd5210SJason Evans } 231a4bd5210SJason Evans if (config_stats) { 232a4bd5210SJason Evans merged_stats = true; 233*b7eaed25SJason Evans arena_stats_large_nrequests_add(tsd_tsdn(tsd), 234*b7eaed25SJason Evans &arena->stats, binind, 235*b7eaed25SJason Evans tbin->tstats.nrequests); 236a4bd5210SJason Evans tbin->tstats.nrequests = 0; 237a4bd5210SJason Evans } 238a4bd5210SJason Evans } 239*b7eaed25SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &locked_arena->large_mtx); 240*b7eaed25SJason Evans 241*b7eaed25SJason Evans unsigned ndeferred = 0; 242*b7eaed25SJason Evans for (unsigned i = 0; i < nflush; i++) { 243*b7eaed25SJason Evans void *ptr = *(tbin->avail - 1 - i); 244*b7eaed25SJason Evans extent = item_extent[i]; 245*b7eaed25SJason Evans assert(ptr != NULL && extent != NULL); 246*b7eaed25SJason Evans 247*b7eaed25SJason Evans if (extent_arena_get(extent) == locked_arena) { 248*b7eaed25SJason Evans large_dalloc_finish(tsd_tsdn(tsd), extent); 249d0e79aa3SJason Evans } else { 250a4bd5210SJason Evans /* 251a4bd5210SJason Evans * This object was allocated via a different 252a4bd5210SJason Evans * arena than the one that is currently locked. 253a4bd5210SJason Evans * Stash the object, so that it can be handled 254a4bd5210SJason Evans * in a future pass. 255a4bd5210SJason Evans */ 256df0d881dSJason Evans *(tbin->avail - 1 - ndeferred) = ptr; 257*b7eaed25SJason Evans item_extent[ndeferred] = extent; 258a4bd5210SJason Evans ndeferred++; 259a4bd5210SJason Evans } 260a4bd5210SJason Evans } 261*b7eaed25SJason Evans if (config_prof && idump) { 2621f0a49e8SJason Evans prof_idump(tsd_tsdn(tsd)); 263*b7eaed25SJason Evans } 2641f0a49e8SJason Evans arena_decay_ticks(tsd_tsdn(tsd), locked_arena, nflush - 2651f0a49e8SJason Evans ndeferred); 266*b7eaed25SJason Evans nflush = ndeferred; 267a4bd5210SJason Evans } 268d0e79aa3SJason Evans if (config_stats && !merged_stats) { 269a4bd5210SJason Evans /* 270a4bd5210SJason Evans * The flush loop didn't happen to flush to this thread's 271a4bd5210SJason Evans * arena, so the stats didn't get merged. Manually do so now. 272a4bd5210SJason Evans */ 273*b7eaed25SJason Evans arena_stats_large_nrequests_add(tsd_tsdn(tsd), &arena->stats, 274*b7eaed25SJason Evans binind, tbin->tstats.nrequests); 275a4bd5210SJason Evans tbin->tstats.nrequests = 0; 276a4bd5210SJason Evans } 277a4bd5210SJason Evans 278df0d881dSJason Evans memmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem * 279df0d881dSJason Evans sizeof(void *)); 280a4bd5210SJason Evans tbin->ncached = rem; 281*b7eaed25SJason Evans if ((low_water_t)tbin->ncached < tbin->low_water) { 282a4bd5210SJason Evans tbin->low_water = tbin->ncached; 283a4bd5210SJason Evans } 284*b7eaed25SJason Evans } 285a4bd5210SJason Evans 286*b7eaed25SJason Evans void 287*b7eaed25SJason Evans tcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) { 288*b7eaed25SJason Evans assert(tcache->arena == NULL); 289*b7eaed25SJason Evans tcache->arena = arena; 290a4bd5210SJason Evans 291a4bd5210SJason Evans if (config_stats) { 292a4bd5210SJason Evans /* Link into list of extant tcaches. */ 293*b7eaed25SJason Evans malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx); 294a4bd5210SJason Evans ql_elm_new(tcache, link); 295a4bd5210SJason Evans ql_tail_insert(&arena->tcache_ql, tcache, link); 296*b7eaed25SJason Evans malloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx); 297a4bd5210SJason Evans } 298a4bd5210SJason Evans } 299a4bd5210SJason Evans 3001f0a49e8SJason Evans static void 301*b7eaed25SJason Evans tcache_arena_dissociate(tsdn_t *tsdn, tcache_t *tcache) { 302*b7eaed25SJason Evans arena_t *arena = tcache->arena; 303*b7eaed25SJason Evans assert(arena != NULL); 304a4bd5210SJason Evans if (config_stats) { 305a4bd5210SJason Evans /* Unlink from list of extant tcaches. */ 306*b7eaed25SJason Evans malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx); 307d0e79aa3SJason Evans if (config_debug) { 308d0e79aa3SJason Evans bool in_ql = false; 309d0e79aa3SJason Evans tcache_t *iter; 310d0e79aa3SJason Evans ql_foreach(iter, &arena->tcache_ql, link) { 311d0e79aa3SJason Evans if (iter == tcache) { 312d0e79aa3SJason Evans in_ql = true; 313d0e79aa3SJason Evans break; 314d0e79aa3SJason Evans } 315d0e79aa3SJason Evans } 316d0e79aa3SJason Evans assert(in_ql); 317d0e79aa3SJason Evans } 318d0e79aa3SJason Evans ql_remove(&arena->tcache_ql, tcache, link); 3191f0a49e8SJason Evans tcache_stats_merge(tsdn, tcache, arena); 320*b7eaed25SJason Evans malloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx); 321a4bd5210SJason Evans } 322*b7eaed25SJason Evans tcache->arena = NULL; 323a4bd5210SJason Evans } 324a4bd5210SJason Evans 3251f0a49e8SJason Evans void 326*b7eaed25SJason Evans tcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) { 327*b7eaed25SJason Evans tcache_arena_dissociate(tsdn, tcache); 3281f0a49e8SJason Evans tcache_arena_associate(tsdn, tcache, arena); 329*b7eaed25SJason Evans } 330*b7eaed25SJason Evans 331*b7eaed25SJason Evans bool 332*b7eaed25SJason Evans tsd_tcache_enabled_data_init(tsd_t *tsd) { 333*b7eaed25SJason Evans /* Called upon tsd initialization. */ 334*b7eaed25SJason Evans tsd_tcache_enabled_set(tsd, opt_tcache); 335*b7eaed25SJason Evans tsd_slow_update(tsd); 336*b7eaed25SJason Evans 337*b7eaed25SJason Evans if (opt_tcache) { 338*b7eaed25SJason Evans /* Trigger tcache init. */ 339*b7eaed25SJason Evans tsd_tcache_data_init(tsd); 340*b7eaed25SJason Evans } 341*b7eaed25SJason Evans 342*b7eaed25SJason Evans return false; 343*b7eaed25SJason Evans } 344*b7eaed25SJason Evans 345*b7eaed25SJason Evans /* Initialize auto tcache (embedded in TSD). */ 346*b7eaed25SJason Evans static void 347*b7eaed25SJason Evans tcache_init(tsd_t *tsd, tcache_t *tcache, void *avail_stack) { 348*b7eaed25SJason Evans memset(&tcache->link, 0, sizeof(ql_elm(tcache_t))); 349*b7eaed25SJason Evans tcache->prof_accumbytes = 0; 350*b7eaed25SJason Evans tcache->next_gc_bin = 0; 351*b7eaed25SJason Evans tcache->arena = NULL; 352a4bd5210SJason Evans 353df0d881dSJason Evans ticker_init(&tcache->gc_ticker, TCACHE_GC_INCR); 354df0d881dSJason Evans 355*b7eaed25SJason Evans size_t stack_offset = 0; 356a4bd5210SJason Evans assert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0); 357*b7eaed25SJason Evans memset(tcache->tbins_small, 0, sizeof(tcache_bin_t) * NBINS); 358*b7eaed25SJason Evans memset(tcache->tbins_large, 0, sizeof(tcache_bin_t) * (nhbins - NBINS)); 359*b7eaed25SJason Evans unsigned i = 0; 360*b7eaed25SJason Evans for (; i < NBINS; i++) { 361*b7eaed25SJason Evans tcache->lg_fill_div[i] = 1; 362df0d881dSJason Evans stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *); 363df0d881dSJason Evans /* 364df0d881dSJason Evans * avail points past the available space. Allocations will 365df0d881dSJason Evans * access the slots toward higher addresses (for the benefit of 366df0d881dSJason Evans * prefetch). 367df0d881dSJason Evans */ 368*b7eaed25SJason Evans tcache_small_bin_get(tcache, i)->avail = 369*b7eaed25SJason Evans (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset); 370*b7eaed25SJason Evans } 371*b7eaed25SJason Evans for (; i < nhbins; i++) { 372*b7eaed25SJason Evans stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *); 373*b7eaed25SJason Evans tcache_large_bin_get(tcache, i)->avail = 374*b7eaed25SJason Evans (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset); 375*b7eaed25SJason Evans } 376*b7eaed25SJason Evans assert(stack_offset == stack_nelms * sizeof(void *)); 377a4bd5210SJason Evans } 378a4bd5210SJason Evans 379*b7eaed25SJason Evans /* Initialize auto tcache (embedded in TSD). */ 380*b7eaed25SJason Evans bool 381*b7eaed25SJason Evans tsd_tcache_data_init(tsd_t *tsd) { 382*b7eaed25SJason Evans tcache_t *tcache = tsd_tcachep_get_unsafe(tsd); 383*b7eaed25SJason Evans assert(tcache_small_bin_get(tcache, 0)->avail == NULL); 384*b7eaed25SJason Evans size_t size = stack_nelms * sizeof(void *); 385*b7eaed25SJason Evans /* Avoid false cacheline sharing. */ 386*b7eaed25SJason Evans size = sz_sa2u(size, CACHELINE); 387*b7eaed25SJason Evans 388*b7eaed25SJason Evans void *avail_array = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true, 389*b7eaed25SJason Evans NULL, true, arena_get(TSDN_NULL, 0, true)); 390*b7eaed25SJason Evans if (avail_array == NULL) { 391*b7eaed25SJason Evans return true; 392*b7eaed25SJason Evans } 393*b7eaed25SJason Evans 394*b7eaed25SJason Evans tcache_init(tsd, tcache, avail_array); 395*b7eaed25SJason Evans /* 396*b7eaed25SJason Evans * Initialization is a bit tricky here. After malloc init is done, all 397*b7eaed25SJason Evans * threads can rely on arena_choose and associate tcache accordingly. 398*b7eaed25SJason Evans * However, the thread that does actual malloc bootstrapping relies on 399*b7eaed25SJason Evans * functional tsd, and it can only rely on a0. In that case, we 400*b7eaed25SJason Evans * associate its tcache to a0 temporarily, and later on 401*b7eaed25SJason Evans * arena_choose_hard() will re-associate properly. 402*b7eaed25SJason Evans */ 403*b7eaed25SJason Evans tcache->arena = NULL; 404*b7eaed25SJason Evans arena_t *arena; 405*b7eaed25SJason Evans if (!malloc_initialized()) { 406*b7eaed25SJason Evans /* If in initialization, assign to a0. */ 407*b7eaed25SJason Evans arena = arena_get(tsd_tsdn(tsd), 0, false); 408*b7eaed25SJason Evans tcache_arena_associate(tsd_tsdn(tsd), tcache, arena); 409*b7eaed25SJason Evans } else { 410*b7eaed25SJason Evans arena = arena_choose(tsd, NULL); 411*b7eaed25SJason Evans /* This may happen if thread.tcache.enabled is used. */ 412*b7eaed25SJason Evans if (tcache->arena == NULL) { 413*b7eaed25SJason Evans tcache_arena_associate(tsd_tsdn(tsd), tcache, arena); 414*b7eaed25SJason Evans } 415*b7eaed25SJason Evans } 416*b7eaed25SJason Evans assert(arena == tcache->arena); 417*b7eaed25SJason Evans 418*b7eaed25SJason Evans return false; 419*b7eaed25SJason Evans } 420*b7eaed25SJason Evans 421*b7eaed25SJason Evans /* Created manual tcache for tcache.create mallctl. */ 422*b7eaed25SJason Evans tcache_t * 423*b7eaed25SJason Evans tcache_create_explicit(tsd_t *tsd) { 424*b7eaed25SJason Evans tcache_t *tcache; 425*b7eaed25SJason Evans size_t size, stack_offset; 426*b7eaed25SJason Evans 427*b7eaed25SJason Evans size = sizeof(tcache_t); 428*b7eaed25SJason Evans /* Naturally align the pointer stacks. */ 429*b7eaed25SJason Evans size = PTR_CEILING(size); 430*b7eaed25SJason Evans stack_offset = size; 431*b7eaed25SJason Evans size += stack_nelms * sizeof(void *); 432*b7eaed25SJason Evans /* Avoid false cacheline sharing. */ 433*b7eaed25SJason Evans size = sz_sa2u(size, CACHELINE); 434*b7eaed25SJason Evans 435*b7eaed25SJason Evans tcache = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true, NULL, true, 436*b7eaed25SJason Evans arena_get(TSDN_NULL, 0, true)); 437*b7eaed25SJason Evans if (tcache == NULL) { 438*b7eaed25SJason Evans return NULL; 439*b7eaed25SJason Evans } 440*b7eaed25SJason Evans 441*b7eaed25SJason Evans tcache_init(tsd, tcache, 442*b7eaed25SJason Evans (void *)((uintptr_t)tcache + (uintptr_t)stack_offset)); 443*b7eaed25SJason Evans tcache_arena_associate(tsd_tsdn(tsd), tcache, arena_ichoose(tsd, NULL)); 444*b7eaed25SJason Evans 445*b7eaed25SJason Evans return tcache; 446a4bd5210SJason Evans } 447a4bd5210SJason Evans 448d0e79aa3SJason Evans static void 449*b7eaed25SJason Evans tcache_flush_cache(tsd_t *tsd, tcache_t *tcache) { 450*b7eaed25SJason Evans assert(tcache->arena != NULL); 451a4bd5210SJason Evans 452*b7eaed25SJason Evans for (unsigned i = 0; i < NBINS; i++) { 453*b7eaed25SJason Evans tcache_bin_t *tbin = tcache_small_bin_get(tcache, i); 454d0e79aa3SJason Evans tcache_bin_flush_small(tsd, tcache, tbin, i, 0); 455a4bd5210SJason Evans 456*b7eaed25SJason Evans if (config_stats) { 457*b7eaed25SJason Evans assert(tbin->tstats.nrequests == 0); 458a4bd5210SJason Evans } 459a4bd5210SJason Evans } 460*b7eaed25SJason Evans for (unsigned i = NBINS; i < nhbins; i++) { 461*b7eaed25SJason Evans tcache_bin_t *tbin = tcache_large_bin_get(tcache, i); 462d0e79aa3SJason Evans tcache_bin_flush_large(tsd, tbin, i, 0, tcache); 463a4bd5210SJason Evans 464*b7eaed25SJason Evans if (config_stats) { 465*b7eaed25SJason Evans assert(tbin->tstats.nrequests == 0); 466a4bd5210SJason Evans } 467a4bd5210SJason Evans } 468a4bd5210SJason Evans 469f8ca2db1SJason Evans if (config_prof && tcache->prof_accumbytes > 0 && 470*b7eaed25SJason Evans arena_prof_accum(tsd_tsdn(tsd), tcache->arena, 471*b7eaed25SJason Evans tcache->prof_accumbytes)) { 4721f0a49e8SJason Evans prof_idump(tsd_tsdn(tsd)); 473*b7eaed25SJason Evans } 474a4bd5210SJason Evans } 475a4bd5210SJason Evans 476a4bd5210SJason Evans void 477*b7eaed25SJason Evans tcache_flush(void) { 478*b7eaed25SJason Evans tsd_t *tsd = tsd_fetch(); 479*b7eaed25SJason Evans assert(tcache_available(tsd)); 480*b7eaed25SJason Evans tcache_flush_cache(tsd, tsd_tcachep_get(tsd)); 481*b7eaed25SJason Evans } 482a4bd5210SJason Evans 483*b7eaed25SJason Evans static void 484*b7eaed25SJason Evans tcache_destroy(tsd_t *tsd, tcache_t *tcache, bool tsd_tcache) { 485*b7eaed25SJason Evans tcache_flush_cache(tsd, tcache); 486*b7eaed25SJason Evans tcache_arena_dissociate(tsd_tsdn(tsd), tcache); 487*b7eaed25SJason Evans 488*b7eaed25SJason Evans if (tsd_tcache) { 489*b7eaed25SJason Evans /* Release the avail array for the TSD embedded auto tcache. */ 490*b7eaed25SJason Evans void *avail_array = 491*b7eaed25SJason Evans (void *)((uintptr_t)tcache_small_bin_get(tcache, 0)->avail - 492*b7eaed25SJason Evans (uintptr_t)tcache_bin_info[0].ncached_max * sizeof(void *)); 493*b7eaed25SJason Evans idalloctm(tsd_tsdn(tsd), avail_array, NULL, NULL, true, true); 494*b7eaed25SJason Evans } else { 495*b7eaed25SJason Evans /* Release both the tcache struct and avail array. */ 496*b7eaed25SJason Evans idalloctm(tsd_tsdn(tsd), tcache, NULL, NULL, true, true); 497*b7eaed25SJason Evans } 498*b7eaed25SJason Evans } 499*b7eaed25SJason Evans 500*b7eaed25SJason Evans /* For auto tcache (embedded in TSD) only. */ 501*b7eaed25SJason Evans void 502*b7eaed25SJason Evans tcache_cleanup(tsd_t *tsd) { 503*b7eaed25SJason Evans tcache_t *tcache = tsd_tcachep_get(tsd); 504*b7eaed25SJason Evans if (!tcache_available(tsd)) { 505*b7eaed25SJason Evans assert(tsd_tcache_enabled_get(tsd) == false); 506*b7eaed25SJason Evans if (config_debug) { 507*b7eaed25SJason Evans assert(tcache_small_bin_get(tcache, 0)->avail == NULL); 508*b7eaed25SJason Evans } 509d0e79aa3SJason Evans return; 510*b7eaed25SJason Evans } 511*b7eaed25SJason Evans assert(tsd_tcache_enabled_get(tsd)); 512*b7eaed25SJason Evans assert(tcache_small_bin_get(tcache, 0)->avail != NULL); 513d0e79aa3SJason Evans 514*b7eaed25SJason Evans tcache_destroy(tsd, tcache, true); 515*b7eaed25SJason Evans if (config_debug) { 516*b7eaed25SJason Evans tcache_small_bin_get(tcache, 0)->avail = NULL; 517a4bd5210SJason Evans } 518a4bd5210SJason Evans } 519a4bd5210SJason Evans 520d0e79aa3SJason Evans void 521*b7eaed25SJason Evans tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) { 522a4bd5210SJason Evans unsigned i; 523a4bd5210SJason Evans 524f921d10fSJason Evans cassert(config_stats); 525f921d10fSJason Evans 526a4bd5210SJason Evans /* Merge and reset tcache stats. */ 527a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 528a4bd5210SJason Evans arena_bin_t *bin = &arena->bins[i]; 529*b7eaed25SJason Evans tcache_bin_t *tbin = tcache_small_bin_get(tcache, i); 5301f0a49e8SJason Evans malloc_mutex_lock(tsdn, &bin->lock); 531a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 5321f0a49e8SJason Evans malloc_mutex_unlock(tsdn, &bin->lock); 533a4bd5210SJason Evans tbin->tstats.nrequests = 0; 534a4bd5210SJason Evans } 535a4bd5210SJason Evans 536a4bd5210SJason Evans for (; i < nhbins; i++) { 537*b7eaed25SJason Evans tcache_bin_t *tbin = tcache_large_bin_get(tcache, i); 538*b7eaed25SJason Evans arena_stats_large_nrequests_add(tsdn, &arena->stats, i, 539*b7eaed25SJason Evans tbin->tstats.nrequests); 540a4bd5210SJason Evans tbin->tstats.nrequests = 0; 541a4bd5210SJason Evans } 542a4bd5210SJason Evans } 543a4bd5210SJason Evans 5448244f2aaSJason Evans static bool 5458244f2aaSJason Evans tcaches_create_prep(tsd_t *tsd) { 5468244f2aaSJason Evans bool err; 5478244f2aaSJason Evans 5488244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 549d0e79aa3SJason Evans 550d0e79aa3SJason Evans if (tcaches == NULL) { 551*b7eaed25SJason Evans tcaches = base_alloc(tsd_tsdn(tsd), b0get(), sizeof(tcache_t *) 552*b7eaed25SJason Evans * (MALLOCX_TCACHE_MAX+1), CACHELINE); 5538244f2aaSJason Evans if (tcaches == NULL) { 5548244f2aaSJason Evans err = true; 5558244f2aaSJason Evans goto label_return; 5568244f2aaSJason Evans } 557d0e79aa3SJason Evans } 558d0e79aa3SJason Evans 5598244f2aaSJason Evans if (tcaches_avail == NULL && tcaches_past > MALLOCX_TCACHE_MAX) { 5608244f2aaSJason Evans err = true; 5618244f2aaSJason Evans goto label_return; 5628244f2aaSJason Evans } 563d0e79aa3SJason Evans 5648244f2aaSJason Evans err = false; 5658244f2aaSJason Evans label_return: 5668244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 5678244f2aaSJason Evans return err; 5688244f2aaSJason Evans } 5698244f2aaSJason Evans 5708244f2aaSJason Evans bool 5718244f2aaSJason Evans tcaches_create(tsd_t *tsd, unsigned *r_ind) { 572*b7eaed25SJason Evans witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0); 573*b7eaed25SJason Evans 5748244f2aaSJason Evans bool err; 5758244f2aaSJason Evans 5768244f2aaSJason Evans if (tcaches_create_prep(tsd)) { 5778244f2aaSJason Evans err = true; 5788244f2aaSJason Evans goto label_return; 5798244f2aaSJason Evans } 5808244f2aaSJason Evans 581*b7eaed25SJason Evans tcache_t *tcache = tcache_create_explicit(tsd); 5828244f2aaSJason Evans if (tcache == NULL) { 5838244f2aaSJason Evans err = true; 5848244f2aaSJason Evans goto label_return; 5858244f2aaSJason Evans } 5868244f2aaSJason Evans 587*b7eaed25SJason Evans tcaches_t *elm; 5888244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 589d0e79aa3SJason Evans if (tcaches_avail != NULL) { 590d0e79aa3SJason Evans elm = tcaches_avail; 591d0e79aa3SJason Evans tcaches_avail = tcaches_avail->next; 592d0e79aa3SJason Evans elm->tcache = tcache; 593df0d881dSJason Evans *r_ind = (unsigned)(elm - tcaches); 594d0e79aa3SJason Evans } else { 595d0e79aa3SJason Evans elm = &tcaches[tcaches_past]; 596d0e79aa3SJason Evans elm->tcache = tcache; 597d0e79aa3SJason Evans *r_ind = tcaches_past; 598d0e79aa3SJason Evans tcaches_past++; 599d0e79aa3SJason Evans } 6008244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 601d0e79aa3SJason Evans 6028244f2aaSJason Evans err = false; 6038244f2aaSJason Evans label_return: 604*b7eaed25SJason Evans witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0); 6058244f2aaSJason Evans return err; 606d0e79aa3SJason Evans } 607d0e79aa3SJason Evans 608*b7eaed25SJason Evans static tcache_t * 609*b7eaed25SJason Evans tcaches_elm_remove(tsd_t *tsd, tcaches_t *elm) { 6108244f2aaSJason Evans malloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx); 611d0e79aa3SJason Evans 6128244f2aaSJason Evans if (elm->tcache == NULL) { 613*b7eaed25SJason Evans return NULL; 6148244f2aaSJason Evans } 615*b7eaed25SJason Evans tcache_t *tcache = elm->tcache; 616d0e79aa3SJason Evans elm->tcache = NULL; 617*b7eaed25SJason Evans return tcache; 618d0e79aa3SJason Evans } 619d0e79aa3SJason Evans 620d0e79aa3SJason Evans void 6218244f2aaSJason Evans tcaches_flush(tsd_t *tsd, unsigned ind) { 6228244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 623*b7eaed25SJason Evans tcache_t *tcache = tcaches_elm_remove(tsd, &tcaches[ind]); 6248244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 625*b7eaed25SJason Evans if (tcache != NULL) { 626*b7eaed25SJason Evans tcache_destroy(tsd, tcache, false); 627*b7eaed25SJason Evans } 628d0e79aa3SJason Evans } 629d0e79aa3SJason Evans 630d0e79aa3SJason Evans void 6318244f2aaSJason Evans tcaches_destroy(tsd_t *tsd, unsigned ind) { 6328244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 633*b7eaed25SJason Evans tcaches_t *elm = &tcaches[ind]; 634*b7eaed25SJason Evans tcache_t *tcache = tcaches_elm_remove(tsd, elm); 635d0e79aa3SJason Evans elm->next = tcaches_avail; 636d0e79aa3SJason Evans tcaches_avail = elm; 6378244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 638*b7eaed25SJason Evans if (tcache != NULL) { 639*b7eaed25SJason Evans tcache_destroy(tsd, tcache, false); 640*b7eaed25SJason Evans } 641d0e79aa3SJason Evans } 642d0e79aa3SJason Evans 643d0e79aa3SJason Evans bool 6448244f2aaSJason Evans tcache_boot(tsdn_t *tsdn) { 645*b7eaed25SJason Evans /* If necessary, clamp opt_lg_tcache_max. */ 646*b7eaed25SJason Evans if (opt_lg_tcache_max < 0 || (ZU(1) << opt_lg_tcache_max) < 647*b7eaed25SJason Evans SMALL_MAXCLASS) { 648a4bd5210SJason Evans tcache_maxclass = SMALL_MAXCLASS; 649*b7eaed25SJason Evans } else { 6507fa7f12fSJason Evans tcache_maxclass = (ZU(1) << opt_lg_tcache_max); 651*b7eaed25SJason Evans } 652a4bd5210SJason Evans 653*b7eaed25SJason Evans if (malloc_mutex_init(&tcaches_mtx, "tcaches", WITNESS_RANK_TCACHES, 654*b7eaed25SJason Evans malloc_mutex_rank_exclusive)) { 6558244f2aaSJason Evans return true; 6568244f2aaSJason Evans } 6578244f2aaSJason Evans 658*b7eaed25SJason Evans nhbins = sz_size2index(tcache_maxclass) + 1; 659a4bd5210SJason Evans 660a4bd5210SJason Evans /* Initialize tcache_bin_info. */ 661*b7eaed25SJason Evans tcache_bin_info = (tcache_bin_info_t *)base_alloc(tsdn, b0get(), nhbins 662*b7eaed25SJason Evans * sizeof(tcache_bin_info_t), CACHELINE); 663*b7eaed25SJason Evans if (tcache_bin_info == NULL) { 664*b7eaed25SJason Evans return true; 665*b7eaed25SJason Evans } 666a4bd5210SJason Evans stack_nelms = 0; 667*b7eaed25SJason Evans unsigned i; 668a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 669d0e79aa3SJason Evans if ((arena_bin_info[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MIN) { 670d0e79aa3SJason Evans tcache_bin_info[i].ncached_max = 671d0e79aa3SJason Evans TCACHE_NSLOTS_SMALL_MIN; 672d0e79aa3SJason Evans } else if ((arena_bin_info[i].nregs << 1) <= 673d0e79aa3SJason Evans TCACHE_NSLOTS_SMALL_MAX) { 674a4bd5210SJason Evans tcache_bin_info[i].ncached_max = 675a4bd5210SJason Evans (arena_bin_info[i].nregs << 1); 676a4bd5210SJason Evans } else { 677a4bd5210SJason Evans tcache_bin_info[i].ncached_max = 678a4bd5210SJason Evans TCACHE_NSLOTS_SMALL_MAX; 679a4bd5210SJason Evans } 680a4bd5210SJason Evans stack_nelms += tcache_bin_info[i].ncached_max; 681a4bd5210SJason Evans } 682a4bd5210SJason Evans for (; i < nhbins; i++) { 683a4bd5210SJason Evans tcache_bin_info[i].ncached_max = TCACHE_NSLOTS_LARGE; 684a4bd5210SJason Evans stack_nelms += tcache_bin_info[i].ncached_max; 685a4bd5210SJason Evans } 686a4bd5210SJason Evans 687*b7eaed25SJason Evans return false; 688a4bd5210SJason Evans } 6898244f2aaSJason Evans 6908244f2aaSJason Evans void 6918244f2aaSJason Evans tcache_prefork(tsdn_t *tsdn) { 6928244f2aaSJason Evans if (!config_prof && opt_tcache) { 6938244f2aaSJason Evans malloc_mutex_prefork(tsdn, &tcaches_mtx); 6948244f2aaSJason Evans } 6958244f2aaSJason Evans } 6968244f2aaSJason Evans 6978244f2aaSJason Evans void 6988244f2aaSJason Evans tcache_postfork_parent(tsdn_t *tsdn) { 6998244f2aaSJason Evans if (!config_prof && opt_tcache) { 7008244f2aaSJason Evans malloc_mutex_postfork_parent(tsdn, &tcaches_mtx); 7018244f2aaSJason Evans } 7028244f2aaSJason Evans } 7038244f2aaSJason Evans 7048244f2aaSJason Evans void 7058244f2aaSJason Evans tcache_postfork_child(tsdn_t *tsdn) { 7068244f2aaSJason Evans if (!config_prof && opt_tcache) { 7078244f2aaSJason Evans malloc_mutex_postfork_child(tsdn, &tcaches_mtx); 7088244f2aaSJason Evans } 7098244f2aaSJason Evans } 710