1a4bd5210SJason Evans #define JEMALLOC_TCACHE_C_ 2b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_preamble.h" 3b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_internal_includes.h" 4b7eaed25SJason Evans 5b7eaed25SJason Evans #include "jemalloc/internal/assert.h" 6b7eaed25SJason Evans #include "jemalloc/internal/mutex.h" 7b7eaed25SJason 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 15*0ef50b4eSJason Evans cache_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 35b7eaed25SJason Evans tcache_salloc(tsdn_t *tsdn, const void *ptr) { 36b7eaed25SJason Evans return arena_salloc(tsdn, ptr); 378ed34ab0SJason Evans } 388ed34ab0SJason Evans 39e722f8f8SJason Evans void 40b7eaed25SJason Evans tcache_event_hard(tsd_t *tsd, tcache_t *tcache) { 41536b3538SJason Evans szind_t binind = tcache->next_gc_bin; 42e722f8f8SJason Evans 43*0ef50b4eSJason Evans cache_bin_t *tbin; 44b7eaed25SJason Evans if (binind < NBINS) { 45b7eaed25SJason Evans tbin = tcache_small_bin_get(tcache, binind); 46b7eaed25SJason Evans } else { 47b7eaed25SJason Evans tbin = tcache_large_bin_get(tcache, binind); 48b7eaed25SJason 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)); 57b7eaed25SJason Evans /* 58b7eaed25SJason Evans * Reduce fill count by 2X. Limit lg_fill_div such that 59b7eaed25SJason Evans * the fill count is always at least 1. 60b7eaed25SJason Evans */ 61*0ef50b4eSJason Evans cache_bin_info_t *tbin_info = &tcache_bin_info[binind]; 62b7eaed25SJason Evans if ((tbin_info->ncached_max >> 63b7eaed25SJason Evans (tcache->lg_fill_div[binind] + 1)) >= 1) { 64b7eaed25SJason Evans tcache->lg_fill_div[binind]++; 65b7eaed25SJason 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 /* 72b7eaed25SJason Evans * Increase fill count by 2X for small bins. Make sure 73b7eaed25SJason Evans * lg_fill_div stays greater than 0. 74e722f8f8SJason Evans */ 75b7eaed25SJason Evans if (binind < NBINS && tcache->lg_fill_div[binind] > 1) { 76b7eaed25SJason Evans tcache->lg_fill_div[binind]--; 77b7eaed25SJason Evans } 78e722f8f8SJason Evans } 79e722f8f8SJason Evans tbin->low_water = tbin->ncached; 80e722f8f8SJason Evans 81e722f8f8SJason Evans tcache->next_gc_bin++; 82b7eaed25SJason Evans if (tcache->next_gc_bin == nhbins) { 83e722f8f8SJason Evans tcache->next_gc_bin = 0; 84e722f8f8SJason Evans } 85b7eaed25SJason Evans } 86e722f8f8SJason Evans 87a4bd5210SJason Evans void * 881f0a49e8SJason Evans tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache, 89*0ef50b4eSJason Evans cache_bin_t *tbin, szind_t binind, bool *tcache_success) { 90a4bd5210SJason Evans void *ret; 91a4bd5210SJason Evans 92b7eaed25SJason Evans assert(tcache->arena != NULL); 93b7eaed25SJason Evans arena_tcache_fill_small(tsdn, arena, tcache, tbin, binind, 94b7eaed25SJason Evans config_prof ? tcache->prof_accumbytes : 0); 95b7eaed25SJason Evans if (config_prof) { 96a4bd5210SJason Evans tcache->prof_accumbytes = 0; 97b7eaed25SJason Evans } 98*0ef50b4eSJason Evans ret = cache_bin_alloc_easy(tbin, tcache_success); 99a4bd5210SJason Evans 100b7eaed25SJason Evans return ret; 101a4bd5210SJason Evans } 102a4bd5210SJason Evans 103a4bd5210SJason Evans void 104*0ef50b4eSJason Evans tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin, 105b7eaed25SJason Evans szind_t binind, unsigned rem) { 106a4bd5210SJason Evans bool merged_stats = false; 107a4bd5210SJason Evans 108a4bd5210SJason Evans assert(binind < NBINS); 109*0ef50b4eSJason Evans assert((cache_bin_sz_t)rem <= tbin->ncached); 110a4bd5210SJason Evans 111b7eaed25SJason Evans arena_t *arena = tcache->arena; 112d0e79aa3SJason Evans assert(arena != NULL); 113b7eaed25SJason Evans unsigned nflush = tbin->ncached - rem; 114b7eaed25SJason Evans VARIABLE_ARRAY(extent_t *, item_extent, nflush); 115b7eaed25SJason Evans /* Look up extent once per item. */ 116b7eaed25SJason Evans for (unsigned i = 0 ; i < nflush; i++) { 117b7eaed25SJason Evans item_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i)); 118b7eaed25SJason Evans } 119b7eaed25SJason Evans 120b7eaed25SJason Evans while (nflush > 0) { 121a4bd5210SJason Evans /* Lock the arena bin associated with the first object. */ 122b7eaed25SJason Evans extent_t *extent = item_extent[0]; 123b7eaed25SJason Evans arena_t *bin_arena = extent_arena_get(extent); 124*0ef50b4eSJason Evans 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, 128b7eaed25SJason Evans tcache->prof_accumbytes)) { 1291f0a49e8SJason Evans prof_idump(tsd_tsdn(tsd)); 130b7eaed25SJason 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 } 142b7eaed25SJason Evans unsigned ndeferred = 0; 143b7eaed25SJason Evans for (unsigned i = 0; i < nflush; i++) { 144b7eaed25SJason Evans void *ptr = *(tbin->avail - 1 - i); 145b7eaed25SJason Evans extent = item_extent[i]; 146b7eaed25SJason Evans assert(ptr != NULL && extent != NULL); 147b7eaed25SJason Evans 148b7eaed25SJason Evans if (extent_arena_get(extent) == bin_arena) { 1491f0a49e8SJason Evans arena_dalloc_bin_junked_locked(tsd_tsdn(tsd), 150b7eaed25SJason 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; 159b7eaed25SJason 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); 165b7eaed25SJason 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 */ 172*0ef50b4eSJason Evans 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*0ef50b4eSJason Evans if (tbin->ncached < tbin->low_water) { 184a4bd5210SJason Evans tbin->low_water = tbin->ncached; 185a4bd5210SJason Evans } 186b7eaed25SJason Evans } 187a4bd5210SJason Evans 188a4bd5210SJason Evans void 189*0ef50b4eSJason Evans tcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind, 190b7eaed25SJason Evans unsigned rem, tcache_t *tcache) { 191a4bd5210SJason Evans bool merged_stats = false; 192a4bd5210SJason Evans 193a4bd5210SJason Evans assert(binind < nhbins); 194*0ef50b4eSJason Evans assert((cache_bin_sz_t)rem <= tbin->ncached); 195a4bd5210SJason Evans 196b7eaed25SJason Evans arena_t *arena = tcache->arena; 197d0e79aa3SJason Evans assert(arena != NULL); 198b7eaed25SJason Evans unsigned nflush = tbin->ncached - rem; 199b7eaed25SJason Evans VARIABLE_ARRAY(extent_t *, item_extent, nflush); 200b7eaed25SJason Evans /* Look up extent once per item. */ 201b7eaed25SJason Evans for (unsigned i = 0 ; i < nflush; i++) { 202b7eaed25SJason Evans item_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i)); 203b7eaed25SJason Evans } 204b7eaed25SJason Evans 205b7eaed25SJason Evans while (nflush > 0) { 206a4bd5210SJason Evans /* Lock the arena associated with the first object. */ 207b7eaed25SJason Evans extent_t *extent = item_extent[0]; 208b7eaed25SJason Evans arena_t *locked_arena = extent_arena_get(extent); 209f8ca2db1SJason Evans UNUSED bool idump; 210a4bd5210SJason Evans 211b7eaed25SJason Evans if (config_prof) { 212f8ca2db1SJason Evans idump = false; 213b7eaed25SJason Evans } 214b7eaed25SJason Evans 215b7eaed25SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &locked_arena->large_mtx); 216b7eaed25SJason Evans for (unsigned i = 0; i < nflush; i++) { 217b7eaed25SJason Evans void *ptr = *(tbin->avail - 1 - i); 218b7eaed25SJason Evans assert(ptr != NULL); 219b7eaed25SJason Evans extent = item_extent[i]; 220b7eaed25SJason Evans if (extent_arena_get(extent) == locked_arena) { 221b7eaed25SJason Evans large_dalloc_prep_junked_locked(tsd_tsdn(tsd), 222b7eaed25SJason Evans extent); 223b7eaed25SJason Evans } 224b7eaed25SJason Evans } 225d0e79aa3SJason Evans if ((config_prof || config_stats) && locked_arena == arena) { 226a4bd5210SJason Evans if (config_prof) { 227b7eaed25SJason 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; 233b7eaed25SJason Evans arena_stats_large_nrequests_add(tsd_tsdn(tsd), 234b7eaed25SJason Evans &arena->stats, binind, 235b7eaed25SJason Evans tbin->tstats.nrequests); 236a4bd5210SJason Evans tbin->tstats.nrequests = 0; 237a4bd5210SJason Evans } 238a4bd5210SJason Evans } 239b7eaed25SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &locked_arena->large_mtx); 240b7eaed25SJason Evans 241b7eaed25SJason Evans unsigned ndeferred = 0; 242b7eaed25SJason Evans for (unsigned i = 0; i < nflush; i++) { 243b7eaed25SJason Evans void *ptr = *(tbin->avail - 1 - i); 244b7eaed25SJason Evans extent = item_extent[i]; 245b7eaed25SJason Evans assert(ptr != NULL && extent != NULL); 246b7eaed25SJason Evans 247b7eaed25SJason Evans if (extent_arena_get(extent) == locked_arena) { 248b7eaed25SJason 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; 257b7eaed25SJason Evans item_extent[ndeferred] = extent; 258a4bd5210SJason Evans ndeferred++; 259a4bd5210SJason Evans } 260a4bd5210SJason Evans } 261b7eaed25SJason Evans if (config_prof && idump) { 2621f0a49e8SJason Evans prof_idump(tsd_tsdn(tsd)); 263b7eaed25SJason Evans } 2641f0a49e8SJason Evans arena_decay_ticks(tsd_tsdn(tsd), locked_arena, nflush - 2651f0a49e8SJason Evans ndeferred); 266b7eaed25SJason 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 */ 273b7eaed25SJason Evans arena_stats_large_nrequests_add(tsd_tsdn(tsd), &arena->stats, 274b7eaed25SJason 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*0ef50b4eSJason Evans if (tbin->ncached < tbin->low_water) { 282a4bd5210SJason Evans tbin->low_water = tbin->ncached; 283a4bd5210SJason Evans } 284b7eaed25SJason Evans } 285a4bd5210SJason Evans 286b7eaed25SJason Evans void 287b7eaed25SJason Evans tcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) { 288b7eaed25SJason Evans assert(tcache->arena == NULL); 289b7eaed25SJason Evans tcache->arena = arena; 290a4bd5210SJason Evans 291a4bd5210SJason Evans if (config_stats) { 292a4bd5210SJason Evans /* Link into list of extant tcaches. */ 293b7eaed25SJason Evans malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx); 294*0ef50b4eSJason Evans 295a4bd5210SJason Evans ql_elm_new(tcache, link); 296a4bd5210SJason Evans ql_tail_insert(&arena->tcache_ql, tcache, link); 297*0ef50b4eSJason Evans cache_bin_array_descriptor_init( 298*0ef50b4eSJason Evans &tcache->cache_bin_array_descriptor, tcache->bins_small, 299*0ef50b4eSJason Evans tcache->bins_large); 300*0ef50b4eSJason Evans ql_tail_insert(&arena->cache_bin_array_descriptor_ql, 301*0ef50b4eSJason Evans &tcache->cache_bin_array_descriptor, link); 302*0ef50b4eSJason Evans 303b7eaed25SJason Evans malloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx); 304a4bd5210SJason Evans } 305a4bd5210SJason Evans } 306a4bd5210SJason Evans 3071f0a49e8SJason Evans static void 308b7eaed25SJason Evans tcache_arena_dissociate(tsdn_t *tsdn, tcache_t *tcache) { 309b7eaed25SJason Evans arena_t *arena = tcache->arena; 310b7eaed25SJason Evans assert(arena != NULL); 311a4bd5210SJason Evans if (config_stats) { 312a4bd5210SJason Evans /* Unlink from list of extant tcaches. */ 313b7eaed25SJason Evans malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx); 314d0e79aa3SJason Evans if (config_debug) { 315d0e79aa3SJason Evans bool in_ql = false; 316d0e79aa3SJason Evans tcache_t *iter; 317d0e79aa3SJason Evans ql_foreach(iter, &arena->tcache_ql, link) { 318d0e79aa3SJason Evans if (iter == tcache) { 319d0e79aa3SJason Evans in_ql = true; 320d0e79aa3SJason Evans break; 321d0e79aa3SJason Evans } 322d0e79aa3SJason Evans } 323d0e79aa3SJason Evans assert(in_ql); 324d0e79aa3SJason Evans } 325d0e79aa3SJason Evans ql_remove(&arena->tcache_ql, tcache, link); 326*0ef50b4eSJason Evans ql_remove(&arena->cache_bin_array_descriptor_ql, 327*0ef50b4eSJason Evans &tcache->cache_bin_array_descriptor, link); 3281f0a49e8SJason Evans tcache_stats_merge(tsdn, tcache, arena); 329b7eaed25SJason Evans malloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx); 330a4bd5210SJason Evans } 331b7eaed25SJason Evans tcache->arena = NULL; 332a4bd5210SJason Evans } 333a4bd5210SJason Evans 3341f0a49e8SJason Evans void 335b7eaed25SJason Evans tcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) { 336b7eaed25SJason Evans tcache_arena_dissociate(tsdn, tcache); 3371f0a49e8SJason Evans tcache_arena_associate(tsdn, tcache, arena); 338b7eaed25SJason Evans } 339b7eaed25SJason Evans 340b7eaed25SJason Evans bool 341b7eaed25SJason Evans tsd_tcache_enabled_data_init(tsd_t *tsd) { 342b7eaed25SJason Evans /* Called upon tsd initialization. */ 343b7eaed25SJason Evans tsd_tcache_enabled_set(tsd, opt_tcache); 344b7eaed25SJason Evans tsd_slow_update(tsd); 345b7eaed25SJason Evans 346b7eaed25SJason Evans if (opt_tcache) { 347b7eaed25SJason Evans /* Trigger tcache init. */ 348b7eaed25SJason Evans tsd_tcache_data_init(tsd); 349b7eaed25SJason Evans } 350b7eaed25SJason Evans 351b7eaed25SJason Evans return false; 352b7eaed25SJason Evans } 353b7eaed25SJason Evans 354b7eaed25SJason Evans /* Initialize auto tcache (embedded in TSD). */ 355b7eaed25SJason Evans static void 356b7eaed25SJason Evans tcache_init(tsd_t *tsd, tcache_t *tcache, void *avail_stack) { 357b7eaed25SJason Evans memset(&tcache->link, 0, sizeof(ql_elm(tcache_t))); 358b7eaed25SJason Evans tcache->prof_accumbytes = 0; 359b7eaed25SJason Evans tcache->next_gc_bin = 0; 360b7eaed25SJason Evans tcache->arena = NULL; 361a4bd5210SJason Evans 362df0d881dSJason Evans ticker_init(&tcache->gc_ticker, TCACHE_GC_INCR); 363df0d881dSJason Evans 364b7eaed25SJason Evans size_t stack_offset = 0; 365a4bd5210SJason Evans assert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0); 366*0ef50b4eSJason Evans memset(tcache->bins_small, 0, sizeof(cache_bin_t) * NBINS); 367*0ef50b4eSJason Evans memset(tcache->bins_large, 0, sizeof(cache_bin_t) * (nhbins - NBINS)); 368b7eaed25SJason Evans unsigned i = 0; 369b7eaed25SJason Evans for (; i < NBINS; i++) { 370b7eaed25SJason Evans tcache->lg_fill_div[i] = 1; 371df0d881dSJason Evans stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *); 372df0d881dSJason Evans /* 373df0d881dSJason Evans * avail points past the available space. Allocations will 374df0d881dSJason Evans * access the slots toward higher addresses (for the benefit of 375df0d881dSJason Evans * prefetch). 376df0d881dSJason Evans */ 377b7eaed25SJason Evans tcache_small_bin_get(tcache, i)->avail = 378b7eaed25SJason Evans (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset); 379b7eaed25SJason Evans } 380b7eaed25SJason Evans for (; i < nhbins; i++) { 381b7eaed25SJason Evans stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *); 382b7eaed25SJason Evans tcache_large_bin_get(tcache, i)->avail = 383b7eaed25SJason Evans (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset); 384b7eaed25SJason Evans } 385b7eaed25SJason Evans assert(stack_offset == stack_nelms * sizeof(void *)); 386a4bd5210SJason Evans } 387a4bd5210SJason Evans 388b7eaed25SJason Evans /* Initialize auto tcache (embedded in TSD). */ 389b7eaed25SJason Evans bool 390b7eaed25SJason Evans tsd_tcache_data_init(tsd_t *tsd) { 391b7eaed25SJason Evans tcache_t *tcache = tsd_tcachep_get_unsafe(tsd); 392b7eaed25SJason Evans assert(tcache_small_bin_get(tcache, 0)->avail == NULL); 393b7eaed25SJason Evans size_t size = stack_nelms * sizeof(void *); 394b7eaed25SJason Evans /* Avoid false cacheline sharing. */ 395b7eaed25SJason Evans size = sz_sa2u(size, CACHELINE); 396b7eaed25SJason Evans 397b7eaed25SJason Evans void *avail_array = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true, 398b7eaed25SJason Evans NULL, true, arena_get(TSDN_NULL, 0, true)); 399b7eaed25SJason Evans if (avail_array == NULL) { 400b7eaed25SJason Evans return true; 401b7eaed25SJason Evans } 402b7eaed25SJason Evans 403b7eaed25SJason Evans tcache_init(tsd, tcache, avail_array); 404b7eaed25SJason Evans /* 405b7eaed25SJason Evans * Initialization is a bit tricky here. After malloc init is done, all 406b7eaed25SJason Evans * threads can rely on arena_choose and associate tcache accordingly. 407b7eaed25SJason Evans * However, the thread that does actual malloc bootstrapping relies on 408b7eaed25SJason Evans * functional tsd, and it can only rely on a0. In that case, we 409b7eaed25SJason Evans * associate its tcache to a0 temporarily, and later on 410b7eaed25SJason Evans * arena_choose_hard() will re-associate properly. 411b7eaed25SJason Evans */ 412b7eaed25SJason Evans tcache->arena = NULL; 413b7eaed25SJason Evans arena_t *arena; 414b7eaed25SJason Evans if (!malloc_initialized()) { 415b7eaed25SJason Evans /* If in initialization, assign to a0. */ 416b7eaed25SJason Evans arena = arena_get(tsd_tsdn(tsd), 0, false); 417b7eaed25SJason Evans tcache_arena_associate(tsd_tsdn(tsd), tcache, arena); 418b7eaed25SJason Evans } else { 419b7eaed25SJason Evans arena = arena_choose(tsd, NULL); 420b7eaed25SJason Evans /* This may happen if thread.tcache.enabled is used. */ 421b7eaed25SJason Evans if (tcache->arena == NULL) { 422b7eaed25SJason Evans tcache_arena_associate(tsd_tsdn(tsd), tcache, arena); 423b7eaed25SJason Evans } 424b7eaed25SJason Evans } 425b7eaed25SJason Evans assert(arena == tcache->arena); 426b7eaed25SJason Evans 427b7eaed25SJason Evans return false; 428b7eaed25SJason Evans } 429b7eaed25SJason Evans 430b7eaed25SJason Evans /* Created manual tcache for tcache.create mallctl. */ 431b7eaed25SJason Evans tcache_t * 432b7eaed25SJason Evans tcache_create_explicit(tsd_t *tsd) { 433b7eaed25SJason Evans tcache_t *tcache; 434b7eaed25SJason Evans size_t size, stack_offset; 435b7eaed25SJason Evans 436b7eaed25SJason Evans size = sizeof(tcache_t); 437b7eaed25SJason Evans /* Naturally align the pointer stacks. */ 438b7eaed25SJason Evans size = PTR_CEILING(size); 439b7eaed25SJason Evans stack_offset = size; 440b7eaed25SJason Evans size += stack_nelms * sizeof(void *); 441b7eaed25SJason Evans /* Avoid false cacheline sharing. */ 442b7eaed25SJason Evans size = sz_sa2u(size, CACHELINE); 443b7eaed25SJason Evans 444b7eaed25SJason Evans tcache = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true, NULL, true, 445b7eaed25SJason Evans arena_get(TSDN_NULL, 0, true)); 446b7eaed25SJason Evans if (tcache == NULL) { 447b7eaed25SJason Evans return NULL; 448b7eaed25SJason Evans } 449b7eaed25SJason Evans 450b7eaed25SJason Evans tcache_init(tsd, tcache, 451b7eaed25SJason Evans (void *)((uintptr_t)tcache + (uintptr_t)stack_offset)); 452b7eaed25SJason Evans tcache_arena_associate(tsd_tsdn(tsd), tcache, arena_ichoose(tsd, NULL)); 453b7eaed25SJason Evans 454b7eaed25SJason Evans return tcache; 455a4bd5210SJason Evans } 456a4bd5210SJason Evans 457d0e79aa3SJason Evans static void 458b7eaed25SJason Evans tcache_flush_cache(tsd_t *tsd, tcache_t *tcache) { 459b7eaed25SJason Evans assert(tcache->arena != NULL); 460a4bd5210SJason Evans 461b7eaed25SJason Evans for (unsigned i = 0; i < NBINS; i++) { 462*0ef50b4eSJason Evans cache_bin_t *tbin = tcache_small_bin_get(tcache, i); 463d0e79aa3SJason Evans tcache_bin_flush_small(tsd, tcache, tbin, i, 0); 464a4bd5210SJason Evans 465b7eaed25SJason Evans if (config_stats) { 466b7eaed25SJason Evans assert(tbin->tstats.nrequests == 0); 467a4bd5210SJason Evans } 468a4bd5210SJason Evans } 469b7eaed25SJason Evans for (unsigned i = NBINS; i < nhbins; i++) { 470*0ef50b4eSJason Evans cache_bin_t *tbin = tcache_large_bin_get(tcache, i); 471d0e79aa3SJason Evans tcache_bin_flush_large(tsd, tbin, i, 0, tcache); 472a4bd5210SJason Evans 473b7eaed25SJason Evans if (config_stats) { 474b7eaed25SJason Evans assert(tbin->tstats.nrequests == 0); 475a4bd5210SJason Evans } 476a4bd5210SJason Evans } 477a4bd5210SJason Evans 478f8ca2db1SJason Evans if (config_prof && tcache->prof_accumbytes > 0 && 479b7eaed25SJason Evans arena_prof_accum(tsd_tsdn(tsd), tcache->arena, 480b7eaed25SJason Evans tcache->prof_accumbytes)) { 4811f0a49e8SJason Evans prof_idump(tsd_tsdn(tsd)); 482b7eaed25SJason Evans } 483a4bd5210SJason Evans } 484a4bd5210SJason Evans 485a4bd5210SJason Evans void 4868b2f5aafSJason Evans tcache_flush(tsd_t *tsd) { 487b7eaed25SJason Evans assert(tcache_available(tsd)); 488b7eaed25SJason Evans tcache_flush_cache(tsd, tsd_tcachep_get(tsd)); 489b7eaed25SJason Evans } 490a4bd5210SJason Evans 491b7eaed25SJason Evans static void 492b7eaed25SJason Evans tcache_destroy(tsd_t *tsd, tcache_t *tcache, bool tsd_tcache) { 493b7eaed25SJason Evans tcache_flush_cache(tsd, tcache); 494b7eaed25SJason Evans tcache_arena_dissociate(tsd_tsdn(tsd), tcache); 495b7eaed25SJason Evans 496b7eaed25SJason Evans if (tsd_tcache) { 497b7eaed25SJason Evans /* Release the avail array for the TSD embedded auto tcache. */ 498b7eaed25SJason Evans void *avail_array = 499b7eaed25SJason Evans (void *)((uintptr_t)tcache_small_bin_get(tcache, 0)->avail - 500b7eaed25SJason Evans (uintptr_t)tcache_bin_info[0].ncached_max * sizeof(void *)); 501b7eaed25SJason Evans idalloctm(tsd_tsdn(tsd), avail_array, NULL, NULL, true, true); 502b7eaed25SJason Evans } else { 503b7eaed25SJason Evans /* Release both the tcache struct and avail array. */ 504b7eaed25SJason Evans idalloctm(tsd_tsdn(tsd), tcache, NULL, NULL, true, true); 505b7eaed25SJason Evans } 506b7eaed25SJason Evans } 507b7eaed25SJason Evans 508b7eaed25SJason Evans /* For auto tcache (embedded in TSD) only. */ 509b7eaed25SJason Evans void 510b7eaed25SJason Evans tcache_cleanup(tsd_t *tsd) { 511b7eaed25SJason Evans tcache_t *tcache = tsd_tcachep_get(tsd); 512b7eaed25SJason Evans if (!tcache_available(tsd)) { 513b7eaed25SJason Evans assert(tsd_tcache_enabled_get(tsd) == false); 514b7eaed25SJason Evans if (config_debug) { 515b7eaed25SJason Evans assert(tcache_small_bin_get(tcache, 0)->avail == NULL); 516b7eaed25SJason Evans } 517d0e79aa3SJason Evans return; 518b7eaed25SJason Evans } 519b7eaed25SJason Evans assert(tsd_tcache_enabled_get(tsd)); 520b7eaed25SJason Evans assert(tcache_small_bin_get(tcache, 0)->avail != NULL); 521d0e79aa3SJason Evans 522b7eaed25SJason Evans tcache_destroy(tsd, tcache, true); 523b7eaed25SJason Evans if (config_debug) { 524b7eaed25SJason Evans tcache_small_bin_get(tcache, 0)->avail = NULL; 525a4bd5210SJason Evans } 526a4bd5210SJason Evans } 527a4bd5210SJason Evans 528d0e79aa3SJason Evans void 529b7eaed25SJason Evans tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) { 530a4bd5210SJason Evans unsigned i; 531a4bd5210SJason Evans 532f921d10fSJason Evans cassert(config_stats); 533f921d10fSJason Evans 534a4bd5210SJason Evans /* Merge and reset tcache stats. */ 535a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 536*0ef50b4eSJason Evans bin_t *bin = &arena->bins[i]; 537*0ef50b4eSJason Evans cache_bin_t *tbin = tcache_small_bin_get(tcache, i); 5381f0a49e8SJason Evans malloc_mutex_lock(tsdn, &bin->lock); 539a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 5401f0a49e8SJason Evans malloc_mutex_unlock(tsdn, &bin->lock); 541a4bd5210SJason Evans tbin->tstats.nrequests = 0; 542a4bd5210SJason Evans } 543a4bd5210SJason Evans 544a4bd5210SJason Evans for (; i < nhbins; i++) { 545*0ef50b4eSJason Evans cache_bin_t *tbin = tcache_large_bin_get(tcache, i); 546b7eaed25SJason Evans arena_stats_large_nrequests_add(tsdn, &arena->stats, i, 547b7eaed25SJason Evans tbin->tstats.nrequests); 548a4bd5210SJason Evans tbin->tstats.nrequests = 0; 549a4bd5210SJason Evans } 550a4bd5210SJason Evans } 551a4bd5210SJason Evans 5528244f2aaSJason Evans static bool 5538244f2aaSJason Evans tcaches_create_prep(tsd_t *tsd) { 5548244f2aaSJason Evans bool err; 5558244f2aaSJason Evans 5568244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 557d0e79aa3SJason Evans 558d0e79aa3SJason Evans if (tcaches == NULL) { 559b7eaed25SJason Evans tcaches = base_alloc(tsd_tsdn(tsd), b0get(), sizeof(tcache_t *) 560b7eaed25SJason Evans * (MALLOCX_TCACHE_MAX+1), CACHELINE); 5618244f2aaSJason Evans if (tcaches == NULL) { 5628244f2aaSJason Evans err = true; 5638244f2aaSJason Evans goto label_return; 5648244f2aaSJason Evans } 565d0e79aa3SJason Evans } 566d0e79aa3SJason Evans 5678244f2aaSJason Evans if (tcaches_avail == NULL && tcaches_past > MALLOCX_TCACHE_MAX) { 5688244f2aaSJason Evans err = true; 5698244f2aaSJason Evans goto label_return; 5708244f2aaSJason Evans } 571d0e79aa3SJason Evans 5728244f2aaSJason Evans err = false; 5738244f2aaSJason Evans label_return: 5748244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 5758244f2aaSJason Evans return err; 5768244f2aaSJason Evans } 5778244f2aaSJason Evans 5788244f2aaSJason Evans bool 5798244f2aaSJason Evans tcaches_create(tsd_t *tsd, unsigned *r_ind) { 580b7eaed25SJason Evans witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0); 581b7eaed25SJason Evans 5828244f2aaSJason Evans bool err; 5838244f2aaSJason Evans 5848244f2aaSJason Evans if (tcaches_create_prep(tsd)) { 5858244f2aaSJason Evans err = true; 5868244f2aaSJason Evans goto label_return; 5878244f2aaSJason Evans } 5888244f2aaSJason Evans 589b7eaed25SJason Evans tcache_t *tcache = tcache_create_explicit(tsd); 5908244f2aaSJason Evans if (tcache == NULL) { 5918244f2aaSJason Evans err = true; 5928244f2aaSJason Evans goto label_return; 5938244f2aaSJason Evans } 5948244f2aaSJason Evans 595b7eaed25SJason Evans tcaches_t *elm; 5968244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 597d0e79aa3SJason Evans if (tcaches_avail != NULL) { 598d0e79aa3SJason Evans elm = tcaches_avail; 599d0e79aa3SJason Evans tcaches_avail = tcaches_avail->next; 600d0e79aa3SJason Evans elm->tcache = tcache; 601df0d881dSJason Evans *r_ind = (unsigned)(elm - tcaches); 602d0e79aa3SJason Evans } else { 603d0e79aa3SJason Evans elm = &tcaches[tcaches_past]; 604d0e79aa3SJason Evans elm->tcache = tcache; 605d0e79aa3SJason Evans *r_ind = tcaches_past; 606d0e79aa3SJason Evans tcaches_past++; 607d0e79aa3SJason Evans } 6088244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 609d0e79aa3SJason Evans 6108244f2aaSJason Evans err = false; 6118244f2aaSJason Evans label_return: 612b7eaed25SJason Evans witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0); 6138244f2aaSJason Evans return err; 614d0e79aa3SJason Evans } 615d0e79aa3SJason Evans 616b7eaed25SJason Evans static tcache_t * 617b7eaed25SJason Evans tcaches_elm_remove(tsd_t *tsd, tcaches_t *elm) { 6188244f2aaSJason Evans malloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx); 619d0e79aa3SJason Evans 6208244f2aaSJason Evans if (elm->tcache == NULL) { 621b7eaed25SJason Evans return NULL; 6228244f2aaSJason Evans } 623b7eaed25SJason Evans tcache_t *tcache = elm->tcache; 624d0e79aa3SJason Evans elm->tcache = NULL; 625b7eaed25SJason Evans return tcache; 626d0e79aa3SJason Evans } 627d0e79aa3SJason Evans 628d0e79aa3SJason Evans void 6298244f2aaSJason Evans tcaches_flush(tsd_t *tsd, unsigned ind) { 6308244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 631b7eaed25SJason Evans tcache_t *tcache = tcaches_elm_remove(tsd, &tcaches[ind]); 6328244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 633b7eaed25SJason Evans if (tcache != NULL) { 634b7eaed25SJason Evans tcache_destroy(tsd, tcache, false); 635b7eaed25SJason Evans } 636d0e79aa3SJason Evans } 637d0e79aa3SJason Evans 638d0e79aa3SJason Evans void 6398244f2aaSJason Evans tcaches_destroy(tsd_t *tsd, unsigned ind) { 6408244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 641b7eaed25SJason Evans tcaches_t *elm = &tcaches[ind]; 642b7eaed25SJason Evans tcache_t *tcache = tcaches_elm_remove(tsd, elm); 643d0e79aa3SJason Evans elm->next = tcaches_avail; 644d0e79aa3SJason Evans tcaches_avail = elm; 6458244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 646b7eaed25SJason Evans if (tcache != NULL) { 647b7eaed25SJason Evans tcache_destroy(tsd, tcache, false); 648b7eaed25SJason Evans } 649d0e79aa3SJason Evans } 650d0e79aa3SJason Evans 651d0e79aa3SJason Evans bool 6528244f2aaSJason Evans tcache_boot(tsdn_t *tsdn) { 653b7eaed25SJason Evans /* If necessary, clamp opt_lg_tcache_max. */ 654b7eaed25SJason Evans if (opt_lg_tcache_max < 0 || (ZU(1) << opt_lg_tcache_max) < 655b7eaed25SJason Evans SMALL_MAXCLASS) { 656a4bd5210SJason Evans tcache_maxclass = SMALL_MAXCLASS; 657b7eaed25SJason Evans } else { 6587fa7f12fSJason Evans tcache_maxclass = (ZU(1) << opt_lg_tcache_max); 659b7eaed25SJason Evans } 660a4bd5210SJason Evans 661b7eaed25SJason Evans if (malloc_mutex_init(&tcaches_mtx, "tcaches", WITNESS_RANK_TCACHES, 662b7eaed25SJason Evans malloc_mutex_rank_exclusive)) { 6638244f2aaSJason Evans return true; 6648244f2aaSJason Evans } 6658244f2aaSJason Evans 666b7eaed25SJason Evans nhbins = sz_size2index(tcache_maxclass) + 1; 667a4bd5210SJason Evans 668a4bd5210SJason Evans /* Initialize tcache_bin_info. */ 669*0ef50b4eSJason Evans tcache_bin_info = (cache_bin_info_t *)base_alloc(tsdn, b0get(), nhbins 670*0ef50b4eSJason Evans * sizeof(cache_bin_info_t), CACHELINE); 671b7eaed25SJason Evans if (tcache_bin_info == NULL) { 672b7eaed25SJason Evans return true; 673b7eaed25SJason Evans } 674a4bd5210SJason Evans stack_nelms = 0; 675b7eaed25SJason Evans unsigned i; 676a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 677*0ef50b4eSJason Evans if ((bin_infos[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MIN) { 678d0e79aa3SJason Evans tcache_bin_info[i].ncached_max = 679d0e79aa3SJason Evans TCACHE_NSLOTS_SMALL_MIN; 680*0ef50b4eSJason Evans } else if ((bin_infos[i].nregs << 1) <= 681d0e79aa3SJason Evans TCACHE_NSLOTS_SMALL_MAX) { 682a4bd5210SJason Evans tcache_bin_info[i].ncached_max = 683*0ef50b4eSJason Evans (bin_infos[i].nregs << 1); 684a4bd5210SJason Evans } else { 685a4bd5210SJason Evans tcache_bin_info[i].ncached_max = 686a4bd5210SJason Evans TCACHE_NSLOTS_SMALL_MAX; 687a4bd5210SJason Evans } 688a4bd5210SJason Evans stack_nelms += tcache_bin_info[i].ncached_max; 689a4bd5210SJason Evans } 690a4bd5210SJason Evans for (; i < nhbins; i++) { 691a4bd5210SJason Evans tcache_bin_info[i].ncached_max = TCACHE_NSLOTS_LARGE; 692a4bd5210SJason Evans stack_nelms += tcache_bin_info[i].ncached_max; 693a4bd5210SJason Evans } 694a4bd5210SJason Evans 695b7eaed25SJason Evans return false; 696a4bd5210SJason Evans } 6978244f2aaSJason Evans 6988244f2aaSJason Evans void 6998244f2aaSJason Evans tcache_prefork(tsdn_t *tsdn) { 7008244f2aaSJason Evans if (!config_prof && opt_tcache) { 7018244f2aaSJason Evans malloc_mutex_prefork(tsdn, &tcaches_mtx); 7028244f2aaSJason Evans } 7038244f2aaSJason Evans } 7048244f2aaSJason Evans 7058244f2aaSJason Evans void 7068244f2aaSJason Evans tcache_postfork_parent(tsdn_t *tsdn) { 7078244f2aaSJason Evans if (!config_prof && opt_tcache) { 7088244f2aaSJason Evans malloc_mutex_postfork_parent(tsdn, &tcaches_mtx); 7098244f2aaSJason Evans } 7108244f2aaSJason Evans } 7118244f2aaSJason Evans 7128244f2aaSJason Evans void 7138244f2aaSJason Evans tcache_postfork_child(tsdn_t *tsdn) { 7148244f2aaSJason Evans if (!config_prof && opt_tcache) { 7158244f2aaSJason Evans malloc_mutex_postfork_child(tsdn, &tcaches_mtx); 7168244f2aaSJason Evans } 7178244f2aaSJason Evans } 718