github.com/afumu/libc@v0.0.6/musl/src/malloc/mallocng/malloc.c (about) 1 #include <stdlib.h> 2 #include <stdint.h> 3 #include <limits.h> 4 #include <string.h> 5 #include <sys/mman.h> 6 #include <errno.h> 7 8 #include "meta.h" 9 10 LOCK_OBJ_DEF; 11 12 const uint16_t size_classes[] = { 13 1, 2, 3, 4, 5, 6, 7, 8, 14 9, 10, 12, 15, 15 18, 20, 25, 31, 16 36, 42, 50, 63, 17 72, 84, 102, 127, 18 146, 170, 204, 255, 19 292, 340, 409, 511, 20 584, 682, 818, 1023, 21 1169, 1364, 1637, 2047, 22 2340, 2730, 3276, 4095, 23 4680, 5460, 6552, 8191, 24 }; 25 26 static const uint8_t small_cnt_tab[][3] = { 27 { 30, 30, 30 }, 28 { 31, 15, 15 }, 29 { 20, 10, 10 }, 30 { 31, 15, 7 }, 31 { 25, 12, 6 }, 32 { 21, 10, 5 }, 33 { 18, 8, 4 }, 34 { 31, 15, 7 }, 35 { 28, 14, 6 }, 36 }; 37 38 static const uint8_t med_cnt_tab[4] = { 28, 24, 20, 32 }; 39 40 struct malloc_context ctx = { 0 }; 41 42 struct meta *alloc_meta(void) 43 { 44 struct meta *m; 45 unsigned char *p; 46 if (!ctx.init_done) { 47 #ifndef PAGESIZE 48 ctx.pagesize = get_page_size(); 49 #endif 50 ctx.secret = get_random_secret(); 51 ctx.init_done = 1; 52 } 53 size_t pagesize = PGSZ; 54 if (pagesize < 4096) pagesize = 4096; 55 if ((m = dequeue_head(&ctx.free_meta_head))) return m; 56 if (!ctx.avail_meta_count) { 57 int need_unprotect = 1; 58 if (!ctx.avail_meta_area_count && ctx.brk!=-1) { 59 uintptr_t new = ctx.brk + pagesize; 60 int need_guard = 0; 61 if (!ctx.brk) { 62 need_guard = 1; 63 ctx.brk = brk(0); 64 // some ancient kernels returned _ebss 65 // instead of next page as initial brk. 66 ctx.brk += -ctx.brk & (pagesize-1); 67 new = ctx.brk + 2*pagesize; 68 } 69 if (brk(new) != new) { 70 ctx.brk = -1; 71 } else { 72 if (need_guard) mmap((void *)ctx.brk, pagesize, 73 PROT_NONE, MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0); 74 ctx.brk = new; 75 ctx.avail_meta_areas = (void *)(new - pagesize); 76 ctx.avail_meta_area_count = pagesize>>12; 77 need_unprotect = 0; 78 } 79 } 80 if (!ctx.avail_meta_area_count) { 81 size_t n = 2UL << ctx.meta_alloc_shift; 82 p = mmap(0, n*pagesize, PROT_NONE, 83 MAP_PRIVATE|MAP_ANON, -1, 0); 84 if (p==MAP_FAILED) return 0; 85 ctx.avail_meta_areas = p + pagesize; 86 ctx.avail_meta_area_count = (n-1)*(pagesize>>12); 87 ctx.meta_alloc_shift++; 88 } 89 p = ctx.avail_meta_areas; 90 if ((uintptr_t)p & (pagesize-1)) need_unprotect = 0; 91 if (need_unprotect) 92 if (mprotect(p, pagesize, PROT_READ|PROT_WRITE) 93 && errno != ENOSYS) 94 return 0; 95 ctx.avail_meta_area_count--; 96 ctx.avail_meta_areas = p + 4096; 97 if (ctx.meta_area_tail) { 98 ctx.meta_area_tail->next = (void *)p; 99 } else { 100 ctx.meta_area_head = (void *)p; 101 } 102 ctx.meta_area_tail = (void *)p; 103 ctx.meta_area_tail->check = ctx.secret; 104 ctx.avail_meta_count = ctx.meta_area_tail->nslots 105 = (4096-sizeof(struct meta_area))/sizeof *m; 106 ctx.avail_meta = ctx.meta_area_tail->slots; 107 } 108 ctx.avail_meta_count--; 109 m = ctx.avail_meta++; 110 m->prev = m->next = 0; 111 return m; 112 } 113 114 static uint32_t try_avail(struct meta **pm) 115 { 116 struct meta *m = *pm; 117 uint32_t first; 118 if (!m) return 0; 119 uint32_t mask = m->avail_mask; 120 if (!mask) { 121 if (!m) return 0; 122 if (!m->freed_mask) { 123 dequeue(pm, m); 124 m = *pm; 125 if (!m) return 0; 126 } else { 127 m = m->next; 128 *pm = m; 129 } 130 131 mask = m->freed_mask; 132 133 // skip fully-free group unless it's the only one 134 // or it's a permanently non-freeable group 135 if (mask == (2u<<m->last_idx)-1 && m->freeable) { 136 m = m->next; 137 *pm = m; 138 mask = m->freed_mask; 139 } 140 141 // activate more slots in a not-fully-active group 142 // if needed, but only as a last resort. prefer using 143 // any other group with free slots. this avoids 144 // touching & dirtying as-yet-unused pages. 145 if (!(mask & ((2u<<m->mem->active_idx)-1))) { 146 if (m->next != m) { 147 m = m->next; 148 *pm = m; 149 } else { 150 int cnt = m->mem->active_idx + 2; 151 int size = size_classes[m->sizeclass]*UNIT; 152 int span = UNIT + size*cnt; 153 // activate up to next 4k boundary 154 while ((span^(span+size-1)) < 4096) { 155 cnt++; 156 span += size; 157 } 158 if (cnt > m->last_idx+1) 159 cnt = m->last_idx+1; 160 m->mem->active_idx = cnt-1; 161 } 162 } 163 mask = activate_group(m); 164 assert(mask); 165 decay_bounces(m->sizeclass); 166 } 167 first = mask&-mask; 168 m->avail_mask = mask-first; 169 return first; 170 } 171 172 static int alloc_slot(int, size_t); 173 174 static struct meta *alloc_group(int sc, size_t req) 175 { 176 size_t size = UNIT*size_classes[sc]; 177 int i = 0, cnt; 178 unsigned char *p; 179 struct meta *m = alloc_meta(); 180 if (!m) return 0; 181 size_t usage = ctx.usage_by_class[sc]; 182 size_t pagesize = PGSZ; 183 int active_idx; 184 if (sc < 9) { 185 while (i<2 && 4*small_cnt_tab[sc][i] > usage) 186 i++; 187 cnt = small_cnt_tab[sc][i]; 188 } else { 189 // lookup max number of slots fitting in power-of-two size 190 // from a table, along with number of factors of two we 191 // can divide out without a remainder or reaching 1. 192 cnt = med_cnt_tab[sc&3]; 193 194 // reduce cnt to avoid excessive eagar allocation. 195 while (!(cnt&1) && 4*cnt > usage) 196 cnt >>= 1; 197 198 // data structures don't support groups whose slot offsets 199 // in units don't fit in 16 bits. 200 while (size*cnt >= 65536*UNIT) 201 cnt >>= 1; 202 } 203 204 // If we selected a count of 1 above but it's not sufficient to use 205 // mmap, increase to 2. Then it might be; if not it will nest. 206 if (cnt==1 && size*cnt+UNIT <= pagesize/2) cnt = 2; 207 208 // All choices of size*cnt are "just below" a power of two, so anything 209 // larger than half the page size should be allocated as whole pages. 210 if (size*cnt+UNIT > pagesize/2) { 211 // check/update bounce counter to start/increase retention 212 // of freed maps, and inhibit use of low-count, odd-size 213 // small mappings and single-slot groups if activated. 214 int nosmall = is_bouncing(sc); 215 account_bounce(sc); 216 step_seq(); 217 218 // since the following count reduction opportunities have 219 // an absolute memory usage cost, don't overdo them. count 220 // coarse usage as part of usage. 221 if (!(sc&1) && sc<32) usage += ctx.usage_by_class[sc+1]; 222 223 // try to drop to a lower count if the one found above 224 // increases usage by more than 25%. these reduced counts 225 // roughly fill an integral number of pages, just not a 226 // power of two, limiting amount of unusable space. 227 if (4*cnt > usage && !nosmall) { 228 if (0); 229 else if ((sc&3)==1 && size*cnt>8*pagesize) cnt = 2; 230 else if ((sc&3)==2 && size*cnt>4*pagesize) cnt = 3; 231 else if ((sc&3)==0 && size*cnt>8*pagesize) cnt = 3; 232 else if ((sc&3)==0 && size*cnt>2*pagesize) cnt = 5; 233 } 234 size_t needed = size*cnt + UNIT; 235 needed += -needed & (pagesize-1); 236 237 // produce an individually-mmapped allocation if usage is low, 238 // bounce counter hasn't triggered, and either it saves memory 239 // or it avoids eagar slot allocation without wasting too much. 240 if (!nosmall && cnt<=7) { 241 req += IB + UNIT; 242 req += -req & (pagesize-1); 243 if (req<size+UNIT || (req>=4*pagesize && 2*cnt>usage)) { 244 cnt = 1; 245 needed = req; 246 } 247 } 248 249 p = mmap(0, needed, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); 250 if (p==MAP_FAILED) { 251 free_meta(m); 252 return 0; 253 } 254 m->maplen = needed>>12; 255 ctx.mmap_counter++; 256 active_idx = (4096-UNIT)/size-1; 257 if (active_idx > cnt-1) active_idx = cnt-1; 258 if (active_idx < 0) active_idx = 0; 259 } else { 260 int j = size_to_class(UNIT+cnt*size-IB); 261 int idx = alloc_slot(j, UNIT+cnt*size-IB); 262 if (idx < 0) { 263 free_meta(m); 264 return 0; 265 } 266 struct meta *g = ctx.active[j]; 267 p = enframe(g, idx, UNIT*size_classes[j]-IB, ctx.mmap_counter); 268 m->maplen = 0; 269 p[-3] = (p[-3]&31) | (6<<5); 270 for (int i=0; i<=cnt; i++) 271 p[UNIT+i*size-4] = 0; 272 active_idx = cnt-1; 273 } 274 ctx.usage_by_class[sc] += cnt; 275 m->avail_mask = (2u<<active_idx)-1; 276 m->freed_mask = (2u<<(cnt-1))-1 - m->avail_mask; 277 m->mem = (void *)p; 278 m->mem->meta = m; 279 m->mem->active_idx = active_idx; 280 m->last_idx = cnt-1; 281 m->freeable = 1; 282 m->sizeclass = sc; 283 return m; 284 } 285 286 static int alloc_slot(int sc, size_t req) 287 { 288 uint32_t first = try_avail(&ctx.active[sc]); 289 if (first) return a_ctz_32(first); 290 291 struct meta *g = alloc_group(sc, req); 292 if (!g) return -1; 293 294 g->avail_mask--; 295 queue(&ctx.active[sc], g); 296 return 0; 297 } 298 299 void *malloc(size_t n) 300 { 301 if (size_overflows(n)) return 0; 302 struct meta *g; 303 uint32_t mask, first; 304 int sc; 305 int idx; 306 int ctr; 307 308 if (n >= MMAP_THRESHOLD) { 309 size_t needed = n + IB + UNIT; 310 void *p = mmap(0, needed, PROT_READ|PROT_WRITE, 311 MAP_PRIVATE|MAP_ANON, -1, 0); 312 if (p==MAP_FAILED) return 0; 313 wrlock(); 314 step_seq(); 315 g = alloc_meta(); 316 if (!g) { 317 unlock(); 318 munmap(p, needed); 319 return 0; 320 } 321 g->mem = p; 322 g->mem->meta = g; 323 g->last_idx = 0; 324 g->freeable = 1; 325 g->sizeclass = 63; 326 g->maplen = (needed+4095)/4096; 327 g->avail_mask = g->freed_mask = 0; 328 // use a global counter to cycle offset in 329 // individually-mmapped allocations. 330 ctx.mmap_counter++; 331 idx = 0; 332 goto success; 333 } 334 335 sc = size_to_class(n); 336 337 rdlock(); 338 g = ctx.active[sc]; 339 340 // use coarse size classes initially when there are not yet 341 // any groups of desired size. this allows counts of 2 or 3 342 // to be allocated at first rather than having to start with 343 // 7 or 5, the min counts for even size classes. 344 if (!g && sc>=4 && sc<32 && sc!=6 && !(sc&1) && !ctx.usage_by_class[sc]) { 345 size_t usage = ctx.usage_by_class[sc|1]; 346 // if a new group may be allocated, count it toward 347 // usage in deciding if we can use coarse class. 348 if (!ctx.active[sc|1] || (!ctx.active[sc|1]->avail_mask 349 && !ctx.active[sc|1]->freed_mask)) 350 usage += 3; 351 if (usage <= 12) 352 sc |= 1; 353 g = ctx.active[sc]; 354 } 355 356 for (;;) { 357 mask = g ? g->avail_mask : 0; 358 first = mask&-mask; 359 if (!first) break; 360 if (RDLOCK_IS_EXCLUSIVE || !MT) 361 g->avail_mask = mask-first; 362 else if (a_cas(&g->avail_mask, mask, mask-first)!=mask) 363 continue; 364 idx = a_ctz_32(first); 365 goto success; 366 } 367 upgradelock(); 368 369 idx = alloc_slot(sc, n); 370 if (idx < 0) { 371 unlock(); 372 return 0; 373 } 374 g = ctx.active[sc]; 375 376 success: 377 ctr = ctx.mmap_counter; 378 unlock(); 379 return enframe(g, idx, n, ctr); 380 } 381 382 int is_allzero(void *p) 383 { 384 struct meta *g = get_meta(p); 385 return g->sizeclass >= 48 || 386 get_stride(g) < UNIT*size_classes[g->sizeclass]; 387 }