malloc.c raw

   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  }
 388