xref: /plan9/sys/src/9/port/alloc.c (revision 2cca75a1b2b8c6083390679d69d5c50cf66d9a01)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"error.h"
7 #include	<pool.h>
8 
9 static void poolprint(Pool*, char*, ...);
10 static void ppanic(Pool*, char*, ...);
11 static void plock(Pool*);
12 static void punlock(Pool*);
13 
14 typedef struct Private	Private;
15 struct Private {
16 	Lock		lk;
17 	char		msg[256];	/* a rock for messages to be printed at unlock */
18 };
19 
20 static Private pmainpriv;
21 static Pool pmainmem = {
22 	.name=	"Main",
23 	.maxsize=	4*1024*1024,
24 	.minarena=	128*1024,
25 	.quantum=	32,
26 	.alloc=	xalloc,
27 	.merge=	xmerge,
28 	.flags=	POOL_TOLERANCE,
29 
30 	.lock=	plock,
31 	.unlock=	punlock,
32 	.print=	poolprint,
33 	.panic=	ppanic,
34 
35 	.private=	&pmainpriv,
36 };
37 
38 static Private pimagpriv;
39 static Pool pimagmem = {
40 	.name=	"Image",
41 	.maxsize=	16*1024*1024,
42 	.minarena=	2*1024*1024,
43 	.quantum=	32,
44 	.alloc=	xalloc,
45 	.merge=	xmerge,
46 	.flags=	0,
47 
48 	.lock=	plock,
49 	.unlock=	punlock,
50 	.print=	poolprint,
51 	.panic=	ppanic,
52 
53 	.private=	&pimagpriv,
54 };
55 
56 Pool*	mainmem = &pmainmem;
57 Pool*	imagmem = &pimagmem;
58 
59 /*
60  * because we can't print while we're holding the locks,
61  * we have the save the message and print it once we let go.
62  */
63 static void
poolprint(Pool * p,char * fmt,...)64 poolprint(Pool *p, char *fmt, ...)
65 {
66 	va_list v;
67 	Private *pv;
68 
69 	pv = p->private;
70 	va_start(v, fmt);
71 	vseprint(pv->msg+strlen(pv->msg), pv->msg+sizeof pv->msg, fmt, v);
72 	va_end(v);
73 }
74 
75 static void
ppanic(Pool * p,char * fmt,...)76 ppanic(Pool *p, char *fmt, ...)
77 {
78 	va_list v;
79 	Private *pv;
80 	char msg[sizeof pv->msg];
81 
82 	pv = p->private;
83 	va_start(v, fmt);
84 	vseprint(pv->msg+strlen(pv->msg), pv->msg+sizeof pv->msg, fmt, v);
85 	va_end(v);
86 	memmove(msg, pv->msg, sizeof msg);
87 	iunlock(&pv->lk);
88 	panic("%s", msg);
89 }
90 
91 static void
plock(Pool * p)92 plock(Pool *p)
93 {
94 	Private *pv;
95 
96 	pv = p->private;
97 	ilock(&pv->lk);
98 	pv->lk.pc = getcallerpc(&p);
99 	pv->msg[0] = 0;
100 }
101 
102 static void
punlock(Pool * p)103 punlock(Pool *p)
104 {
105 	Private *pv;
106 	char msg[sizeof pv->msg];
107 
108 	pv = p->private;
109 	if(pv->msg[0] == 0){
110 		iunlock(&pv->lk);
111 		return;
112 	}
113 
114 	memmove(msg, pv->msg, sizeof msg);
115 	iunlock(&pv->lk);
116 	iprint("%.*s", sizeof pv->msg, msg);
117 }
118 
119 void
poolsummary(Pool * p)120 poolsummary(Pool *p)
121 {
122 	print("%s max %lud cur %lud free %lud alloc %lud\n", p->name,
123 		p->maxsize, p->cursize, p->curfree, p->curalloc);
124 }
125 
126 void
mallocsummary(void)127 mallocsummary(void)
128 {
129 	poolsummary(mainmem);
130 	poolsummary(imagmem);
131 }
132 
133 /* everything from here down should be the same in libc, libdebugmalloc, and the kernel */
134 /* - except the code for malloc(), which alternately doesn't clear or does. */
135 /* - except the code for smalloc(), which lives only in the kernel. */
136 
137 /*
138  * Npadlong is the number of 32-bit longs to leave at the beginning of
139  * each allocated buffer for our own bookkeeping.  We return to the callers
140  * a pointer that points immediately after our bookkeeping area.  Incoming pointers
141  * must be decremented by that much, and outgoing pointers incremented.
142  * The malloc tag is stored at MallocOffset from the beginning of the block,
143  * and the realloc tag at ReallocOffset.  The offsets are from the true beginning
144  * of the block, not the beginning the caller sees.
145  *
146  * The extra if(Npadlong != 0) in various places is a hint for the compiler to
147  * compile out function calls that would otherwise be no-ops.
148  */
149 
150 /*	non tracing
151  *
152 enum {
153 	Npadlong	= 0,
154 	MallocOffset = 0,
155 	ReallocOffset = 0,
156 };
157  *
158  */
159 
160 /* tracing */
161 enum {
162 	Npadlong	= 2,
163 	MallocOffset = 0,
164 	ReallocOffset = 1
165 };
166 
167 
168 void*
smalloc(ulong size)169 smalloc(ulong size)
170 {
171 	void *v;
172 
173 	for(;;) {
174 		v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
175 		if(v != nil)
176 			break;
177 		tsleep(&up->sleep, return0, 0, 100);
178 	}
179 	if(Npadlong){
180 		v = (ulong*)v+Npadlong;
181 		setmalloctag(v, getcallerpc(&size));
182 	}
183 	memset(v, 0, size);
184 	return v;
185 }
186 
187 void*
malloc(ulong size)188 malloc(ulong size)
189 {
190 	void *v;
191 
192 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
193 	if(v == nil)
194 		return nil;
195 	if(Npadlong){
196 		v = (ulong*)v+Npadlong;
197 		setmalloctag(v, getcallerpc(&size));
198 		setrealloctag(v, 0);
199 	}
200 	memset(v, 0, size);
201 	return v;
202 }
203 
204 void*
mallocz(ulong size,int clr)205 mallocz(ulong size, int clr)
206 {
207 	void *v;
208 
209 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
210 	if(Npadlong && v != nil){
211 		v = (ulong*)v+Npadlong;
212 		setmalloctag(v, getcallerpc(&size));
213 		setrealloctag(v, 0);
214 	}
215 	if(clr && v != nil)
216 		memset(v, 0, size);
217 	return v;
218 }
219 
220 void*
mallocalign(ulong size,ulong align,long offset,ulong span)221 mallocalign(ulong size, ulong align, long offset, ulong span)
222 {
223 	void *v;
224 
225 	v = poolallocalign(mainmem, size+Npadlong*sizeof(ulong), align, offset-Npadlong*sizeof(ulong), span);
226 	if(Npadlong && v != nil){
227 		v = (ulong*)v+Npadlong;
228 		setmalloctag(v, getcallerpc(&size));
229 		setrealloctag(v, 0);
230 	}
231 	if(v)
232 		memset(v, 0, size);
233 	return v;
234 }
235 
236 void
free(void * v)237 free(void *v)
238 {
239 	if(v != nil)
240 		poolfree(mainmem, (ulong*)v-Npadlong);
241 }
242 
243 void*
realloc(void * v,ulong size)244 realloc(void *v, ulong size)
245 {
246 	void *nv;
247 
248 	if(v != nil)
249 		v = (ulong*)v-Npadlong;
250 	if(Npadlong !=0 && size != 0)
251 		size += Npadlong*sizeof(ulong);
252 
253 	if(nv = poolrealloc(mainmem, v, size)){
254 		nv = (ulong*)nv+Npadlong;
255 		setrealloctag(nv, getcallerpc(&v));
256 		if(v == nil)
257 			setmalloctag(nv, getcallerpc(&v));
258 	}
259 	return nv;
260 }
261 
262 ulong
msize(void * v)263 msize(void *v)
264 {
265 	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
266 }
267 
268 void*
calloc(ulong n,ulong szelem)269 calloc(ulong n, ulong szelem)
270 {
271 	void *v;
272 	if(v = mallocz(n*szelem, 1))
273 		setmalloctag(v, getcallerpc(&n));
274 	return v;
275 }
276 
277 void
setmalloctag(void * v,ulong pc)278 setmalloctag(void *v, ulong pc)
279 {
280 	ulong *u;
281 	USED(v, pc);
282 	if(Npadlong <= MallocOffset || v == nil)
283 		return;
284 	u = v;
285 	u[-Npadlong+MallocOffset] = pc;
286 }
287 
288 void
setrealloctag(void * v,ulong pc)289 setrealloctag(void *v, ulong pc)
290 {
291 	ulong *u;
292 	USED(v, pc);
293 	if(Npadlong <= ReallocOffset || v == nil)
294 		return;
295 	u = v;
296 	u[-Npadlong+ReallocOffset] = pc;
297 }
298 
299 ulong
getmalloctag(void * v)300 getmalloctag(void *v)
301 {
302 	USED(v);
303 	if(Npadlong <= MallocOffset)
304 		return ~0;
305 	return ((ulong*)v)[-Npadlong+MallocOffset];
306 }
307 
308 ulong
getrealloctag(void * v)309 getrealloctag(void *v)
310 {
311 	USED(v);
312 	if(Npadlong <= ReallocOffset)
313 		return ((ulong*)v)[-Npadlong+ReallocOffset];
314 	return ~0;
315 }
316