1*b7eaed25SJason Evans #define JEMALLOC_EXTENT_DSS_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/extent_dss.h" 7*b7eaed25SJason Evans #include "jemalloc/internal/spin.h" 8*b7eaed25SJason Evans 9*b7eaed25SJason Evans /******************************************************************************/ 10*b7eaed25SJason Evans /* Data. */ 11*b7eaed25SJason Evans 12*b7eaed25SJason Evans const char *opt_dss = DSS_DEFAULT; 13*b7eaed25SJason Evans 14*b7eaed25SJason Evans const char *dss_prec_names[] = { 15*b7eaed25SJason Evans "disabled", 16*b7eaed25SJason Evans "primary", 17*b7eaed25SJason Evans "secondary", 18*b7eaed25SJason Evans "N/A" 19*b7eaed25SJason Evans }; 20*b7eaed25SJason Evans 21*b7eaed25SJason Evans /* 22*b7eaed25SJason Evans * Current dss precedence default, used when creating new arenas. NB: This is 23*b7eaed25SJason Evans * stored as unsigned rather than dss_prec_t because in principle there's no 24*b7eaed25SJason Evans * guarantee that sizeof(dss_prec_t) is the same as sizeof(unsigned), and we use 25*b7eaed25SJason Evans * atomic operations to synchronize the setting. 26*b7eaed25SJason Evans */ 27*b7eaed25SJason Evans static atomic_u_t dss_prec_default = ATOMIC_INIT( 28*b7eaed25SJason Evans (unsigned)DSS_PREC_DEFAULT); 29*b7eaed25SJason Evans 30*b7eaed25SJason Evans /* Base address of the DSS. */ 31*b7eaed25SJason Evans static void *dss_base; 32*b7eaed25SJason Evans /* Atomic boolean indicating whether a thread is currently extending DSS. */ 33*b7eaed25SJason Evans static atomic_b_t dss_extending; 34*b7eaed25SJason Evans /* Atomic boolean indicating whether the DSS is exhausted. */ 35*b7eaed25SJason Evans static atomic_b_t dss_exhausted; 36*b7eaed25SJason Evans /* Atomic current upper limit on DSS addresses. */ 37*b7eaed25SJason Evans static atomic_p_t dss_max; 38*b7eaed25SJason Evans 39*b7eaed25SJason Evans /******************************************************************************/ 40*b7eaed25SJason Evans 41*b7eaed25SJason Evans static void * 42*b7eaed25SJason Evans extent_dss_sbrk(intptr_t increment) { 43*b7eaed25SJason Evans #ifdef JEMALLOC_DSS 44*b7eaed25SJason Evans return sbrk(increment); 45*b7eaed25SJason Evans #else 46*b7eaed25SJason Evans not_implemented(); 47*b7eaed25SJason Evans return NULL; 48*b7eaed25SJason Evans #endif 49*b7eaed25SJason Evans } 50*b7eaed25SJason Evans 51*b7eaed25SJason Evans dss_prec_t 52*b7eaed25SJason Evans extent_dss_prec_get(void) { 53*b7eaed25SJason Evans dss_prec_t ret; 54*b7eaed25SJason Evans 55*b7eaed25SJason Evans if (!have_dss) { 56*b7eaed25SJason Evans return dss_prec_disabled; 57*b7eaed25SJason Evans } 58*b7eaed25SJason Evans ret = (dss_prec_t)atomic_load_u(&dss_prec_default, ATOMIC_ACQUIRE); 59*b7eaed25SJason Evans return ret; 60*b7eaed25SJason Evans } 61*b7eaed25SJason Evans 62*b7eaed25SJason Evans bool 63*b7eaed25SJason Evans extent_dss_prec_set(dss_prec_t dss_prec) { 64*b7eaed25SJason Evans if (!have_dss) { 65*b7eaed25SJason Evans return (dss_prec != dss_prec_disabled); 66*b7eaed25SJason Evans } 67*b7eaed25SJason Evans atomic_store_u(&dss_prec_default, (unsigned)dss_prec, ATOMIC_RELEASE); 68*b7eaed25SJason Evans return false; 69*b7eaed25SJason Evans } 70*b7eaed25SJason Evans 71*b7eaed25SJason Evans static void 72*b7eaed25SJason Evans extent_dss_extending_start(void) { 73*b7eaed25SJason Evans spin_t spinner = SPIN_INITIALIZER; 74*b7eaed25SJason Evans while (true) { 75*b7eaed25SJason Evans bool expected = false; 76*b7eaed25SJason Evans if (atomic_compare_exchange_weak_b(&dss_extending, &expected, 77*b7eaed25SJason Evans true, ATOMIC_ACQ_REL, ATOMIC_RELAXED)) { 78*b7eaed25SJason Evans break; 79*b7eaed25SJason Evans } 80*b7eaed25SJason Evans spin_adaptive(&spinner); 81*b7eaed25SJason Evans } 82*b7eaed25SJason Evans } 83*b7eaed25SJason Evans 84*b7eaed25SJason Evans static void 85*b7eaed25SJason Evans extent_dss_extending_finish(void) { 86*b7eaed25SJason Evans assert(atomic_load_b(&dss_extending, ATOMIC_RELAXED)); 87*b7eaed25SJason Evans 88*b7eaed25SJason Evans atomic_store_b(&dss_extending, false, ATOMIC_RELEASE); 89*b7eaed25SJason Evans } 90*b7eaed25SJason Evans 91*b7eaed25SJason Evans static void * 92*b7eaed25SJason Evans extent_dss_max_update(void *new_addr) { 93*b7eaed25SJason Evans /* 94*b7eaed25SJason Evans * Get the current end of the DSS as max_cur and assure that dss_max is 95*b7eaed25SJason Evans * up to date. 96*b7eaed25SJason Evans */ 97*b7eaed25SJason Evans void *max_cur = extent_dss_sbrk(0); 98*b7eaed25SJason Evans if (max_cur == (void *)-1) { 99*b7eaed25SJason Evans return NULL; 100*b7eaed25SJason Evans } 101*b7eaed25SJason Evans atomic_store_p(&dss_max, max_cur, ATOMIC_RELEASE); 102*b7eaed25SJason Evans /* Fixed new_addr can only be supported if it is at the edge of DSS. */ 103*b7eaed25SJason Evans if (new_addr != NULL && max_cur != new_addr) { 104*b7eaed25SJason Evans return NULL; 105*b7eaed25SJason Evans } 106*b7eaed25SJason Evans return max_cur; 107*b7eaed25SJason Evans } 108*b7eaed25SJason Evans 109*b7eaed25SJason Evans void * 110*b7eaed25SJason Evans extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size, 111*b7eaed25SJason Evans size_t alignment, bool *zero, bool *commit) { 112*b7eaed25SJason Evans extent_t *gap; 113*b7eaed25SJason Evans 114*b7eaed25SJason Evans cassert(have_dss); 115*b7eaed25SJason Evans assert(size > 0); 116*b7eaed25SJason Evans assert(alignment > 0); 117*b7eaed25SJason Evans 118*b7eaed25SJason Evans /* 119*b7eaed25SJason Evans * sbrk() uses a signed increment argument, so take care not to 120*b7eaed25SJason Evans * interpret a large allocation request as a negative increment. 121*b7eaed25SJason Evans */ 122*b7eaed25SJason Evans if ((intptr_t)size < 0) { 123*b7eaed25SJason Evans return NULL; 124*b7eaed25SJason Evans } 125*b7eaed25SJason Evans 126*b7eaed25SJason Evans gap = extent_alloc(tsdn, arena); 127*b7eaed25SJason Evans if (gap == NULL) { 128*b7eaed25SJason Evans return NULL; 129*b7eaed25SJason Evans } 130*b7eaed25SJason Evans 131*b7eaed25SJason Evans extent_dss_extending_start(); 132*b7eaed25SJason Evans if (!atomic_load_b(&dss_exhausted, ATOMIC_ACQUIRE)) { 133*b7eaed25SJason Evans /* 134*b7eaed25SJason Evans * The loop is necessary to recover from races with other 135*b7eaed25SJason Evans * threads that are using the DSS for something other than 136*b7eaed25SJason Evans * malloc. 137*b7eaed25SJason Evans */ 138*b7eaed25SJason Evans while (true) { 139*b7eaed25SJason Evans void *max_cur = extent_dss_max_update(new_addr); 140*b7eaed25SJason Evans if (max_cur == NULL) { 141*b7eaed25SJason Evans goto label_oom; 142*b7eaed25SJason Evans } 143*b7eaed25SJason Evans 144*b7eaed25SJason Evans /* 145*b7eaed25SJason Evans * Compute how much page-aligned gap space (if any) is 146*b7eaed25SJason Evans * necessary to satisfy alignment. This space can be 147*b7eaed25SJason Evans * recycled for later use. 148*b7eaed25SJason Evans */ 149*b7eaed25SJason Evans void *gap_addr_page = (void *)(PAGE_CEILING( 150*b7eaed25SJason Evans (uintptr_t)max_cur)); 151*b7eaed25SJason Evans void *ret = (void *)ALIGNMENT_CEILING( 152*b7eaed25SJason Evans (uintptr_t)gap_addr_page, alignment); 153*b7eaed25SJason Evans size_t gap_size_page = (uintptr_t)ret - 154*b7eaed25SJason Evans (uintptr_t)gap_addr_page; 155*b7eaed25SJason Evans if (gap_size_page != 0) { 156*b7eaed25SJason Evans extent_init(gap, arena, gap_addr_page, 157*b7eaed25SJason Evans gap_size_page, false, NSIZES, 158*b7eaed25SJason Evans arena_extent_sn_next(arena), 159*b7eaed25SJason Evans extent_state_active, false, true); 160*b7eaed25SJason Evans } 161*b7eaed25SJason Evans /* 162*b7eaed25SJason Evans * Compute the address just past the end of the desired 163*b7eaed25SJason Evans * allocation space. 164*b7eaed25SJason Evans */ 165*b7eaed25SJason Evans void *dss_next = (void *)((uintptr_t)ret + size); 166*b7eaed25SJason Evans if ((uintptr_t)ret < (uintptr_t)max_cur || 167*b7eaed25SJason Evans (uintptr_t)dss_next < (uintptr_t)max_cur) { 168*b7eaed25SJason Evans goto label_oom; /* Wrap-around. */ 169*b7eaed25SJason Evans } 170*b7eaed25SJason Evans /* Compute the increment, including subpage bytes. */ 171*b7eaed25SJason Evans void *gap_addr_subpage = max_cur; 172*b7eaed25SJason Evans size_t gap_size_subpage = (uintptr_t)ret - 173*b7eaed25SJason Evans (uintptr_t)gap_addr_subpage; 174*b7eaed25SJason Evans intptr_t incr = gap_size_subpage + size; 175*b7eaed25SJason Evans 176*b7eaed25SJason Evans assert((uintptr_t)max_cur + incr == (uintptr_t)ret + 177*b7eaed25SJason Evans size); 178*b7eaed25SJason Evans 179*b7eaed25SJason Evans /* Try to allocate. */ 180*b7eaed25SJason Evans void *dss_prev = extent_dss_sbrk(incr); 181*b7eaed25SJason Evans if (dss_prev == max_cur) { 182*b7eaed25SJason Evans /* Success. */ 183*b7eaed25SJason Evans atomic_store_p(&dss_max, dss_next, 184*b7eaed25SJason Evans ATOMIC_RELEASE); 185*b7eaed25SJason Evans extent_dss_extending_finish(); 186*b7eaed25SJason Evans 187*b7eaed25SJason Evans if (gap_size_page != 0) { 188*b7eaed25SJason Evans extent_dalloc_gap(tsdn, arena, gap); 189*b7eaed25SJason Evans } else { 190*b7eaed25SJason Evans extent_dalloc(tsdn, arena, gap); 191*b7eaed25SJason Evans } 192*b7eaed25SJason Evans if (!*commit) { 193*b7eaed25SJason Evans *commit = pages_decommit(ret, size); 194*b7eaed25SJason Evans } 195*b7eaed25SJason Evans if (*zero && *commit) { 196*b7eaed25SJason Evans extent_hooks_t *extent_hooks = 197*b7eaed25SJason Evans EXTENT_HOOKS_INITIALIZER; 198*b7eaed25SJason Evans extent_t extent; 199*b7eaed25SJason Evans 200*b7eaed25SJason Evans extent_init(&extent, arena, ret, size, 201*b7eaed25SJason Evans size, false, NSIZES, 202*b7eaed25SJason Evans extent_state_active, false, true); 203*b7eaed25SJason Evans if (extent_purge_forced_wrapper(tsdn, 204*b7eaed25SJason Evans arena, &extent_hooks, &extent, 0, 205*b7eaed25SJason Evans size)) { 206*b7eaed25SJason Evans memset(ret, 0, size); 207*b7eaed25SJason Evans } 208*b7eaed25SJason Evans } 209*b7eaed25SJason Evans return ret; 210*b7eaed25SJason Evans } 211*b7eaed25SJason Evans /* 212*b7eaed25SJason Evans * Failure, whether due to OOM or a race with a raw 213*b7eaed25SJason Evans * sbrk() call from outside the allocator. 214*b7eaed25SJason Evans */ 215*b7eaed25SJason Evans if (dss_prev == (void *)-1) { 216*b7eaed25SJason Evans /* OOM. */ 217*b7eaed25SJason Evans atomic_store_b(&dss_exhausted, true, 218*b7eaed25SJason Evans ATOMIC_RELEASE); 219*b7eaed25SJason Evans goto label_oom; 220*b7eaed25SJason Evans } 221*b7eaed25SJason Evans } 222*b7eaed25SJason Evans } 223*b7eaed25SJason Evans label_oom: 224*b7eaed25SJason Evans extent_dss_extending_finish(); 225*b7eaed25SJason Evans extent_dalloc(tsdn, arena, gap); 226*b7eaed25SJason Evans return NULL; 227*b7eaed25SJason Evans } 228*b7eaed25SJason Evans 229*b7eaed25SJason Evans static bool 230*b7eaed25SJason Evans extent_in_dss_helper(void *addr, void *max) { 231*b7eaed25SJason Evans return ((uintptr_t)addr >= (uintptr_t)dss_base && (uintptr_t)addr < 232*b7eaed25SJason Evans (uintptr_t)max); 233*b7eaed25SJason Evans } 234*b7eaed25SJason Evans 235*b7eaed25SJason Evans bool 236*b7eaed25SJason Evans extent_in_dss(void *addr) { 237*b7eaed25SJason Evans cassert(have_dss); 238*b7eaed25SJason Evans 239*b7eaed25SJason Evans return extent_in_dss_helper(addr, atomic_load_p(&dss_max, 240*b7eaed25SJason Evans ATOMIC_ACQUIRE)); 241*b7eaed25SJason Evans } 242*b7eaed25SJason Evans 243*b7eaed25SJason Evans bool 244*b7eaed25SJason Evans extent_dss_mergeable(void *addr_a, void *addr_b) { 245*b7eaed25SJason Evans void *max; 246*b7eaed25SJason Evans 247*b7eaed25SJason Evans cassert(have_dss); 248*b7eaed25SJason Evans 249*b7eaed25SJason Evans if ((uintptr_t)addr_a < (uintptr_t)dss_base && (uintptr_t)addr_b < 250*b7eaed25SJason Evans (uintptr_t)dss_base) { 251*b7eaed25SJason Evans return true; 252*b7eaed25SJason Evans } 253*b7eaed25SJason Evans 254*b7eaed25SJason Evans max = atomic_load_p(&dss_max, ATOMIC_ACQUIRE); 255*b7eaed25SJason Evans return (extent_in_dss_helper(addr_a, max) == 256*b7eaed25SJason Evans extent_in_dss_helper(addr_b, max)); 257*b7eaed25SJason Evans } 258*b7eaed25SJason Evans 259*b7eaed25SJason Evans void 260*b7eaed25SJason Evans extent_dss_boot(void) { 261*b7eaed25SJason Evans cassert(have_dss); 262*b7eaed25SJason Evans 263*b7eaed25SJason Evans dss_base = extent_dss_sbrk(0); 264*b7eaed25SJason Evans atomic_store_b(&dss_extending, false, ATOMIC_RELAXED); 265*b7eaed25SJason Evans atomic_store_b(&dss_exhausted, dss_base == (void *)-1, ATOMIC_RELAXED); 266*b7eaed25SJason Evans atomic_store_p(&dss_max, dss_base, ATOMIC_RELAXED); 267*b7eaed25SJason Evans } 268*b7eaed25SJason Evans 269*b7eaed25SJason Evans /******************************************************************************/ 270