xref: /aosp_15_r20/external/musl/src/malloc/mallocng/free.c (revision c9945492fdd68bbe62686c5b452b4dc1be3f8453)
1*c9945492SAndroid Build Coastguard Worker #define _BSD_SOURCE
2*c9945492SAndroid Build Coastguard Worker #include <stdlib.h>
3*c9945492SAndroid Build Coastguard Worker #include <sys/mman.h>
4*c9945492SAndroid Build Coastguard Worker 
5*c9945492SAndroid Build Coastguard Worker #include "meta.h"
6*c9945492SAndroid Build Coastguard Worker 
7*c9945492SAndroid Build Coastguard Worker struct mapinfo {
8*c9945492SAndroid Build Coastguard Worker 	void *base;
9*c9945492SAndroid Build Coastguard Worker 	size_t len;
10*c9945492SAndroid Build Coastguard Worker };
11*c9945492SAndroid Build Coastguard Worker 
12*c9945492SAndroid Build Coastguard Worker static struct mapinfo nontrivial_free(struct meta *, int);
13*c9945492SAndroid Build Coastguard Worker 
free_group(struct meta * g)14*c9945492SAndroid Build Coastguard Worker static struct mapinfo free_group(struct meta *g)
15*c9945492SAndroid Build Coastguard Worker {
16*c9945492SAndroid Build Coastguard Worker 	struct mapinfo mi = { 0 };
17*c9945492SAndroid Build Coastguard Worker 	int sc = g->sizeclass;
18*c9945492SAndroid Build Coastguard Worker 	if (sc < 48) {
19*c9945492SAndroid Build Coastguard Worker 		ctx.usage_by_class[sc] -= g->last_idx+1;
20*c9945492SAndroid Build Coastguard Worker 	}
21*c9945492SAndroid Build Coastguard Worker 	if (g->maplen) {
22*c9945492SAndroid Build Coastguard Worker 		step_seq();
23*c9945492SAndroid Build Coastguard Worker 		record_seq(sc);
24*c9945492SAndroid Build Coastguard Worker 		mi.base = g->mem;
25*c9945492SAndroid Build Coastguard Worker 		mi.len = g->maplen*4096UL;
26*c9945492SAndroid Build Coastguard Worker 	} else {
27*c9945492SAndroid Build Coastguard Worker 		void *p = g->mem;
28*c9945492SAndroid Build Coastguard Worker 		struct meta *m = get_meta(p);
29*c9945492SAndroid Build Coastguard Worker 		int idx = get_slot_index(p);
30*c9945492SAndroid Build Coastguard Worker 		g->mem->meta = 0;
31*c9945492SAndroid Build Coastguard Worker 		// not checking size/reserved here; it's intentionally invalid
32*c9945492SAndroid Build Coastguard Worker 		mi = nontrivial_free(m, idx);
33*c9945492SAndroid Build Coastguard Worker 	}
34*c9945492SAndroid Build Coastguard Worker 	free_meta(g);
35*c9945492SAndroid Build Coastguard Worker 	return mi;
36*c9945492SAndroid Build Coastguard Worker }
37*c9945492SAndroid Build Coastguard Worker 
okay_to_free(struct meta * g)38*c9945492SAndroid Build Coastguard Worker static int okay_to_free(struct meta *g)
39*c9945492SAndroid Build Coastguard Worker {
40*c9945492SAndroid Build Coastguard Worker 	int sc = g->sizeclass;
41*c9945492SAndroid Build Coastguard Worker 
42*c9945492SAndroid Build Coastguard Worker 	if (!g->freeable) return 0;
43*c9945492SAndroid Build Coastguard Worker 
44*c9945492SAndroid Build Coastguard Worker 	// always free individual mmaps not suitable for reuse
45*c9945492SAndroid Build Coastguard Worker 	if (sc >= 48 || get_stride(g) < UNIT*size_classes[sc])
46*c9945492SAndroid Build Coastguard Worker 		return 1;
47*c9945492SAndroid Build Coastguard Worker 
48*c9945492SAndroid Build Coastguard Worker 	// always free groups allocated inside another group's slot
49*c9945492SAndroid Build Coastguard Worker 	// since recreating them should not be expensive and they
50*c9945492SAndroid Build Coastguard Worker 	// might be blocking freeing of a much larger group.
51*c9945492SAndroid Build Coastguard Worker 	if (!g->maplen) return 1;
52*c9945492SAndroid Build Coastguard Worker 
53*c9945492SAndroid Build Coastguard Worker 	// if there is another non-full group, free this one to
54*c9945492SAndroid Build Coastguard Worker 	// consolidate future allocations, reduce fragmentation.
55*c9945492SAndroid Build Coastguard Worker 	if (g->next != g) return 1;
56*c9945492SAndroid Build Coastguard Worker 
57*c9945492SAndroid Build Coastguard Worker 	// free any group in a size class that's not bouncing
58*c9945492SAndroid Build Coastguard Worker 	if (!is_bouncing(sc)) return 1;
59*c9945492SAndroid Build Coastguard Worker 
60*c9945492SAndroid Build Coastguard Worker 	size_t cnt = g->last_idx+1;
61*c9945492SAndroid Build Coastguard Worker 	size_t usage = ctx.usage_by_class[sc];
62*c9945492SAndroid Build Coastguard Worker 
63*c9945492SAndroid Build Coastguard Worker 	// if usage is high enough that a larger count should be
64*c9945492SAndroid Build Coastguard Worker 	// used, free the low-count group so a new one will be made.
65*c9945492SAndroid Build Coastguard Worker 	if (9*cnt <= usage && cnt < 20)
66*c9945492SAndroid Build Coastguard Worker 		return 1;
67*c9945492SAndroid Build Coastguard Worker 
68*c9945492SAndroid Build Coastguard Worker 	// otherwise, keep the last group in a bouncing class.
69*c9945492SAndroid Build Coastguard Worker 	return 0;
70*c9945492SAndroid Build Coastguard Worker }
71*c9945492SAndroid Build Coastguard Worker 
nontrivial_free(struct meta * g,int i)72*c9945492SAndroid Build Coastguard Worker static struct mapinfo nontrivial_free(struct meta *g, int i)
73*c9945492SAndroid Build Coastguard Worker {
74*c9945492SAndroid Build Coastguard Worker 	uint32_t self = 1u<<i;
75*c9945492SAndroid Build Coastguard Worker 	int sc = g->sizeclass;
76*c9945492SAndroid Build Coastguard Worker 	uint32_t mask = g->freed_mask | g->avail_mask;
77*c9945492SAndroid Build Coastguard Worker 
78*c9945492SAndroid Build Coastguard Worker 	if (mask+self == (2u<<g->last_idx)-1 && okay_to_free(g)) {
79*c9945492SAndroid Build Coastguard Worker 		// any multi-slot group is necessarily on an active list
80*c9945492SAndroid Build Coastguard Worker 		// here, but single-slot groups might or might not be.
81*c9945492SAndroid Build Coastguard Worker 		if (g->next) {
82*c9945492SAndroid Build Coastguard Worker 			assert(sc < 48);
83*c9945492SAndroid Build Coastguard Worker 			int activate_new = (ctx.active[sc]==g);
84*c9945492SAndroid Build Coastguard Worker 			dequeue(&ctx.active[sc], g);
85*c9945492SAndroid Build Coastguard Worker 			if (activate_new && ctx.active[sc])
86*c9945492SAndroid Build Coastguard Worker 				activate_group(ctx.active[sc]);
87*c9945492SAndroid Build Coastguard Worker 		}
88*c9945492SAndroid Build Coastguard Worker 		return free_group(g);
89*c9945492SAndroid Build Coastguard Worker 	} else if (!mask) {
90*c9945492SAndroid Build Coastguard Worker 		assert(sc < 48);
91*c9945492SAndroid Build Coastguard Worker 		// might still be active if there were no allocations
92*c9945492SAndroid Build Coastguard Worker 		// after last available slot was taken.
93*c9945492SAndroid Build Coastguard Worker 		if (ctx.active[sc] != g) {
94*c9945492SAndroid Build Coastguard Worker 			queue(&ctx.active[sc], g);
95*c9945492SAndroid Build Coastguard Worker 		}
96*c9945492SAndroid Build Coastguard Worker 	}
97*c9945492SAndroid Build Coastguard Worker 	a_or(&g->freed_mask, self);
98*c9945492SAndroid Build Coastguard Worker 	return (struct mapinfo){ 0 };
99*c9945492SAndroid Build Coastguard Worker }
100*c9945492SAndroid Build Coastguard Worker 
free(void * p)101*c9945492SAndroid Build Coastguard Worker void free(void *p)
102*c9945492SAndroid Build Coastguard Worker {
103*c9945492SAndroid Build Coastguard Worker 	if (!p) return;
104*c9945492SAndroid Build Coastguard Worker 
105*c9945492SAndroid Build Coastguard Worker 	struct meta *g = get_meta(p);
106*c9945492SAndroid Build Coastguard Worker 	int idx = get_slot_index(p);
107*c9945492SAndroid Build Coastguard Worker 	size_t stride = get_stride(g);
108*c9945492SAndroid Build Coastguard Worker 	unsigned char *start = g->mem->storage + stride*idx;
109*c9945492SAndroid Build Coastguard Worker 	unsigned char *end = start + stride - IB;
110*c9945492SAndroid Build Coastguard Worker 	get_nominal_size(p, end);
111*c9945492SAndroid Build Coastguard Worker 	uint32_t self = 1u<<idx, all = (2u<<g->last_idx)-1;
112*c9945492SAndroid Build Coastguard Worker 	((unsigned char *)p)[-3] = 255;
113*c9945492SAndroid Build Coastguard Worker 	// invalidate offset to group header, and cycle offset of
114*c9945492SAndroid Build Coastguard Worker 	// used region within slot if current offset is zero.
115*c9945492SAndroid Build Coastguard Worker 	*(uint16_t *)((char *)p-2) = 0;
116*c9945492SAndroid Build Coastguard Worker 
117*c9945492SAndroid Build Coastguard Worker 	// release any whole pages contained in the slot to be freed
118*c9945492SAndroid Build Coastguard Worker 	// unless it's a single-slot group that will be unmapped.
119*c9945492SAndroid Build Coastguard Worker 	if (((uintptr_t)(start-1) ^ (uintptr_t)end) >= 2*PGSZ && g->last_idx) {
120*c9945492SAndroid Build Coastguard Worker 		unsigned char *base = start + (-(uintptr_t)start & (PGSZ-1));
121*c9945492SAndroid Build Coastguard Worker 		size_t len = (end-base) & -PGSZ;
122*c9945492SAndroid Build Coastguard Worker 		if (len && USE_MADV_FREE) {
123*c9945492SAndroid Build Coastguard Worker 			int e = errno;
124*c9945492SAndroid Build Coastguard Worker 			madvise(base, len, MADV_FREE);
125*c9945492SAndroid Build Coastguard Worker 			errno = e;
126*c9945492SAndroid Build Coastguard Worker 		}
127*c9945492SAndroid Build Coastguard Worker 	}
128*c9945492SAndroid Build Coastguard Worker 
129*c9945492SAndroid Build Coastguard Worker 	// atomic free without locking if this is neither first or last slot
130*c9945492SAndroid Build Coastguard Worker 	for (;;) {
131*c9945492SAndroid Build Coastguard Worker 		uint32_t freed = g->freed_mask;
132*c9945492SAndroid Build Coastguard Worker 		uint32_t avail = g->avail_mask;
133*c9945492SAndroid Build Coastguard Worker 		uint32_t mask = freed | avail;
134*c9945492SAndroid Build Coastguard Worker 		assert(!(mask&self));
135*c9945492SAndroid Build Coastguard Worker 		if (!freed || mask+self==all) break;
136*c9945492SAndroid Build Coastguard Worker 		if (!MT)
137*c9945492SAndroid Build Coastguard Worker 			g->freed_mask = freed+self;
138*c9945492SAndroid Build Coastguard Worker 		else if (a_cas(&g->freed_mask, freed, freed+self)!=freed)
139*c9945492SAndroid Build Coastguard Worker 			continue;
140*c9945492SAndroid Build Coastguard Worker 		return;
141*c9945492SAndroid Build Coastguard Worker 	}
142*c9945492SAndroid Build Coastguard Worker 
143*c9945492SAndroid Build Coastguard Worker 	wrlock();
144*c9945492SAndroid Build Coastguard Worker 	struct mapinfo mi = nontrivial_free(g, idx);
145*c9945492SAndroid Build Coastguard Worker 	unlock();
146*c9945492SAndroid Build Coastguard Worker 	if (mi.len) {
147*c9945492SAndroid Build Coastguard Worker 		int e = errno;
148*c9945492SAndroid Build Coastguard Worker 		munmap(mi.base, mi.len);
149*c9945492SAndroid Build Coastguard Worker 		errno = e;
150*c9945492SAndroid Build Coastguard Worker 	}
151*c9945492SAndroid Build Coastguard Worker }
152