make vfs & filesystems use failable copying
[minix3.git] / servers / vm / slaballoc.c
blob6b19e61f5bcc3c7d83dba53b445bc8f1ac2d66c7
2 #define _SYSTEM 1
4 #include <minix/callnr.h>
5 #include <minix/com.h>
6 #include <minix/config.h>
7 #include <minix/const.h>
8 #include <minix/ds.h>
9 #include <minix/endpoint.h>
10 #include <minix/minlib.h>
11 #include <minix/type.h>
12 #include <minix/ipc.h>
13 #include <minix/sysutil.h>
14 #include <minix/syslib.h>
15 #include <minix/bitmap.h>
16 #include <minix/debug.h>
18 #include <assert.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <env.h>
23 #include <sys/param.h>
25 #include "glo.h"
26 #include "proto.h"
27 #include "util.h"
28 #include "sanitycheck.h"
30 #define SLABSIZES 200
32 #define ITEMSPERPAGE(bytes) (int)(DATABYTES / (bytes))
34 #define ELBITS (sizeof(element_t)*8)
35 #define BITPAT(b) (1UL << ((b) % ELBITS))
36 #define BITEL(f, b) (f)->sdh.usebits[(b)/ELBITS]
39 #define OFF(f, b) assert(!GETBIT(f, b))
40 #define ON(f, b) assert(GETBIT(f, b))
42 #if MEMPROTECT
43 #define SLABDATAWRITABLE(data, wr) do { \
44 assert(data->sdh.writable == WRITABLE_NONE); \
45 assert(wr != WRITABLE_NONE); \
46 vm_pagelock(data, 0); \
47 data->sdh.writable = wr; \
48 } while(0)
50 #define SLABDATAUNWRITABLE(data) do { \
51 assert(data->sdh.writable != WRITABLE_NONE); \
52 data->sdh.writable = WRITABLE_NONE; \
53 vm_pagelock(data, 1); \
54 } while(0)
56 #define SLABDATAUSE(data, code) do { \
57 SLABDATAWRITABLE(data, WRITABLE_HEADER); \
58 code \
59 SLABDATAUNWRITABLE(data); \
60 } while(0)
62 #else
64 #define SLABDATAWRITABLE(data, wr)
65 #define SLABDATAUNWRITABLE(data)
66 #define SLABDATAUSE(data, code) do { code } while(0)
68 #endif
70 #define GETBIT(f, b) (BITEL(f,b) & BITPAT(b))
71 #define SETBIT(f, b) {OFF(f,b); SLABDATAUSE(f, BITEL(f,b)|= BITPAT(b); (f)->sdh.nused++;); }
72 #define CLEARBIT(f, b) {ON(f, b); SLABDATAUSE(f, BITEL(f,b)&=~BITPAT(b); (f)->sdh.nused--; (f)->sdh.freeguess = (b);); }
74 #define OBJALIGN 8
76 #define MINSIZE 8
77 #define MAXSIZE (SLABSIZES-1+MINSIZE)
78 #define USEELEMENTS (1+(VM_PAGE_SIZE/MINSIZE/8))
80 static int pages = 0;
82 typedef u8_t element_t;
83 #define BITS_FULL (~(element_t)0)
84 typedef element_t elements_t[USEELEMENTS];
86 /* This file is too low-level to have global SANITYCHECKs everywhere,
87 * as the (other) data structures are often necessarily in an
88 * inconsistent state during a slaballoc() / slabfree(). So only do
89 * our own sanity checks here, with SLABSANITYCHECK.
93 /* Special writable values. */
94 #define WRITABLE_NONE -2
95 #define WRITABLE_HEADER -1
97 struct sdh {
98 #if SANITYCHECKS
99 u32_t magic1;
100 #endif
101 int freeguess;
102 struct slabdata *next, *prev;
103 elements_t usebits;
104 phys_bytes phys;
105 #if SANITYCHECKS
106 int writable; /* data item number or WRITABLE_* */
107 u32_t magic2;
108 #endif
109 u16_t nused; /* Number of data items used in this slab. */
112 #define DATABYTES (VM_PAGE_SIZE-sizeof(struct sdh))
114 #define MAGIC1 0x1f5b842f
115 #define MAGIC2 0x8bb5a420
116 #define JUNK 0xdeadbeef
117 #define NOJUNK 0xc0ffee
119 static struct slabheader {
120 struct slabdata {
121 u8_t data[DATABYTES];
122 struct sdh sdh;
123 } *list_head;
124 } slabs[SLABSIZES];
126 static int objstats(void *, int, struct slabheader **, struct slabdata
127 **, int *);
129 #define GETSLAB(b, s) { \
130 int _gsi; \
131 assert((b) >= MINSIZE); \
132 _gsi = (b) - MINSIZE; \
133 assert((_gsi) < SLABSIZES); \
134 assert((_gsi) >= 0); \
135 s = &slabs[_gsi]; \
138 /* move slabdata nw to slabheader sl under list number l. */
139 #define ADDHEAD(nw, sl) { \
140 SLABDATAUSE(nw, \
141 (nw)->sdh.next = sl->list_head; \
142 (nw)->sdh.prev = NULL;); \
143 sl->list_head = nw; \
144 if((nw)->sdh.next) { \
145 SLABDATAUSE((nw)->sdh.next, \
146 (nw)->sdh.next->sdh.prev = (nw);); \
150 #define UNLINKNODE(node) { \
151 struct slabdata *next, *prev; \
152 prev = (node)->sdh.prev; \
153 next = (node)->sdh.next; \
154 if(prev) { SLABDATAUSE(prev, prev->sdh.next = next;); } \
155 if(next) { SLABDATAUSE(next, next->sdh.prev = prev;); } \
158 static struct slabdata *newslabdata(void)
160 struct slabdata *n;
161 phys_bytes p;
163 assert(sizeof(*n) == VM_PAGE_SIZE);
165 if(!(n = vm_allocpage(&p, VMP_SLAB))) {
166 printf("newslabdata: vm_allocpage failed\n");
167 return NULL;
169 memset(n->sdh.usebits, 0, sizeof(n->sdh.usebits));
170 pages++;
172 n->sdh.phys = p;
173 #if SANITYCHECKS
174 n->sdh.magic1 = MAGIC1;
175 n->sdh.magic2 = MAGIC2;
176 #endif
177 n->sdh.nused = 0;
178 n->sdh.freeguess = 0;
180 #if SANITYCHECKS
181 n->sdh.writable = WRITABLE_HEADER;
182 SLABDATAUNWRITABLE(n);
183 #endif
185 return n;
188 #if SANITYCHECKS
190 /*===========================================================================*
191 * checklist *
192 *===========================================================================*/
193 static int checklist(const char *file, int line,
194 struct slabheader *s, int bytes)
196 struct slabdata *n = s->list_head;
197 int ch = 0;
199 while(n) {
200 int count = 0, i;
201 #if SANITYCHECKS
202 MYASSERT(n->sdh.magic1 == MAGIC1);
203 MYASSERT(n->sdh.magic2 == MAGIC2);
204 #endif
205 MYASSERT(usedpages_add(n->sdh.phys, VM_PAGE_SIZE) == OK);
206 if(n->sdh.prev)
207 MYASSERT(n->sdh.prev->sdh.next == n);
208 else
209 MYASSERT(s->list_head == n);
210 if(n->sdh.next) MYASSERT(n->sdh.next->sdh.prev == n);
211 for(i = 0; i < USEELEMENTS*8; i++)
212 if(i >= ITEMSPERPAGE(bytes))
213 MYASSERT(!GETBIT(n, i));
214 else
215 if(GETBIT(n,i))
216 count++;
217 MYASSERT(count == n->sdh.nused);
218 ch += count;
219 n = n->sdh.next;
222 return ch;
225 /*===========================================================================*
226 * void slab_sanitycheck *
227 *===========================================================================*/
228 void slab_sanitycheck(const char *file, int line)
230 int s;
231 for(s = 0; s < SLABSIZES; s++) {
232 checklist(file, line, &slabs[s], s + MINSIZE);
236 /*===========================================================================*
237 * int slabsane *
238 *===========================================================================*/
239 int slabsane_f(const char *file, int line, void *mem, int bytes)
241 struct slabheader *s;
242 struct slabdata *f;
243 int i;
245 bytes = roundup(bytes, OBJALIGN);
247 return (objstats(mem, bytes, &s, &f, &i) == OK);
249 #endif
251 #if SANITYCHECKS
252 static int nojunkwarning = 0;
253 #endif
255 /*===========================================================================*
256 * void *slaballoc *
257 *===========================================================================*/
258 void *slaballoc(int bytes)
260 int i;
261 int count = 0;
262 struct slabheader *s;
263 struct slabdata *newslab;
264 char *ret;
266 bytes = roundup(bytes, OBJALIGN);
268 SLABSANITYCHECK(SCL_FUNCTIONS);
270 /* Retrieve entry in slabs[]. */
271 GETSLAB(bytes, s);
272 assert(s);
274 if(!(newslab = s->list_head)) {
275 /* Make sure there is something on the freelist. */
276 newslab = newslabdata();
277 if(!newslab) return NULL;
278 ADDHEAD(newslab, s);
279 assert(newslab->sdh.nused == 0);
280 } else assert(newslab->sdh.nused > 0);
281 assert(newslab->sdh.nused < ITEMSPERPAGE(bytes));
283 SLABSANITYCHECK(SCL_DETAIL);
285 #if SANITYCHECKS
286 assert(newslab->sdh.magic1 == MAGIC1);
287 assert(newslab->sdh.magic2 == MAGIC2);
288 #endif
290 for(i = newslab->sdh.freeguess;
291 count < ITEMSPERPAGE(bytes); count++, i++) {
292 i = i % ITEMSPERPAGE(bytes);
294 if(!GETBIT(newslab, i))
295 break;
298 SLABSANITYCHECK(SCL_FUNCTIONS);
300 assert(count < ITEMSPERPAGE(bytes));
301 assert(i >= 0 && i < ITEMSPERPAGE(bytes));
303 SETBIT(newslab, i);
304 if(newslab->sdh.nused == ITEMSPERPAGE(bytes)) {
305 UNLINKNODE(newslab);
306 s->list_head = newslab->sdh.next;
309 ret = ((char *) newslab) + i*bytes;
311 #if SANITYCHECKS
312 #if MEMPROTECT
313 nojunkwarning++;
314 slabunlock(ret, bytes);
315 nojunkwarning--;
316 assert(!nojunkwarning);
317 #endif
318 *(u32_t *) ret = NOJUNK;
319 #if MEMPROTECT
320 slablock(ret, bytes);
321 #endif
322 #endif
324 SLABDATAUSE(newslab, newslab->sdh.freeguess = i+1;);
326 #if SANITYCHECKS
327 if(bytes >= SLABSIZES+MINSIZE) {
328 printf("slaballoc: odd, bytes %d?\n", bytes);
331 if(!slabsane_f(__FILE__, __LINE__, ret, bytes))
332 panic("slaballoc: slabsane failed");
333 #endif
335 assert(!((vir_bytes) ret % OBJALIGN));
337 return ret;
340 /*===========================================================================*
341 * int objstats *
342 *===========================================================================*/
343 static inline int objstats(void *mem, int bytes,
344 struct slabheader **sp, struct slabdata **fp, int *ip)
346 #if SANITYCHECKS
347 #define OBJSTATSCHECK(cond) \
348 if(!(cond)) { \
349 printf("VM: objstats: %s failed for ptr %p, %d bytes\n", \
350 #cond, mem, bytes); \
351 return EINVAL; \
353 #else
354 #define OBJSTATSCHECK(cond)
355 #endif
357 struct slabheader *s;
358 struct slabdata *f;
359 int i;
361 assert(!(bytes % OBJALIGN));
363 OBJSTATSCHECK((char *) mem >= (char *) VM_PAGE_SIZE);
365 #if SANITYCHECKS
366 if(*(u32_t *) mem == JUNK && !nojunkwarning) {
367 util_stacktrace();
368 printf("VM: WARNING: JUNK seen in slab object, likely freed\n");
370 #endif
371 /* Retrieve entry in slabs[]. */
372 GETSLAB(bytes, s);
374 /* Round address down to VM_PAGE_SIZE boundary to get header. */
375 f = (struct slabdata *) ((char *) mem - (vir_bytes) mem % VM_PAGE_SIZE);
377 #if SANITYCHECKS
378 OBJSTATSCHECK(f->sdh.magic1 == MAGIC1);
379 OBJSTATSCHECK(f->sdh.magic2 == MAGIC2);
380 #endif
382 /* Make sure it's in range. */
383 OBJSTATSCHECK((char *) mem >= (char *) f->data);
384 OBJSTATSCHECK((char *) mem < (char *) f->data + sizeof(f->data));
386 /* Get position. */
387 i = (char *) mem - (char *) f->data;
388 OBJSTATSCHECK(!(i % bytes));
389 i = i / bytes;
391 /* Make sure it is marked as allocated. */
392 OBJSTATSCHECK(GETBIT(f, i));
394 /* return values */
395 *ip = i;
396 *fp = f;
397 *sp = s;
399 return OK;
402 /*===========================================================================*
403 * void *slabfree *
404 *===========================================================================*/
405 void slabfree(void *mem, int bytes)
407 int i;
408 struct slabheader *s;
409 struct slabdata *f;
411 bytes = roundup(bytes, OBJALIGN);
413 SLABSANITYCHECK(SCL_FUNCTIONS);
415 if(objstats(mem, bytes, &s, &f, &i) != OK) {
416 panic("slabfree objstats failed");
419 #if SANITYCHECKS
420 if(*(u32_t *) mem == JUNK) {
421 printf("VM: WARNING: likely double free, JUNK seen\n");
423 #endif
425 #if SANITYCHECKS
426 #if MEMPROTECT
427 slabunlock(mem, bytes);
428 #endif
429 #if JUNKFREE
430 memset(mem, 0xa6, bytes);
431 #endif
432 *(u32_t *) mem = JUNK;
433 nojunkwarning++;
434 #if MEMPROTECT
435 slablock(mem, bytes);
436 #endif
437 nojunkwarning--;
438 assert(!nojunkwarning);
439 #endif
441 /* Free this data. */
442 CLEARBIT(f, i);
444 /* Check if this slab changes lists. */
445 if(f->sdh.nused == 0) {
446 UNLINKNODE(f);
447 if(f == s->list_head) s->list_head = f->sdh.next;
448 vm_freepages((vir_bytes) f, 1);
449 SLABSANITYCHECK(SCL_DETAIL);
450 } else if(f->sdh.nused == ITEMSPERPAGE(bytes)-1) {
451 ADDHEAD(f, s);
454 SLABSANITYCHECK(SCL_FUNCTIONS);
456 return;
459 #if MEMPROTECT
460 /*===========================================================================*
461 * void *slablock *
462 *===========================================================================*/
463 void slablock(void *mem, int bytes)
465 int i;
466 struct slabheader *s;
467 struct slabdata *f;
469 bytes = roundup(bytes, OBJALIGN);
471 if(objstats(mem, bytes, &s, &f, &i) != OK)
472 panic("slablock objstats failed");
474 SLABDATAUNWRITABLE(f);
476 return;
479 /*===========================================================================*
480 * void *slabunlock *
481 *===========================================================================*/
482 void slabunlock(void *mem, int bytes)
484 int i;
485 struct slabheader *s;
486 struct slabdata *f;
488 bytes = roundup(bytes, OBJALIGN);
490 if(objstats(mem, bytes, &s, &f, &i) != OK)
491 panic("slabunlock objstats failed");
493 SLABDATAWRITABLE(f, i);
495 return;
497 #endif
499 #if SANITYCHECKS
500 /*===========================================================================*
501 * void slabstats *
502 *===========================================================================*/
503 void slabstats(void)
505 int s, totalbytes = 0;
506 static int n;
507 n++;
508 if(n%1000) return;
509 for(s = 0; s < SLABSIZES; s++) {
510 int b, t;
511 b = s + MINSIZE;
512 t = checklist(__FILE__, __LINE__, &slabs[s], b);
514 if(t > 0) {
515 int bytes = t * b;
516 printf("VMSTATS: %2d slabs: %d (%dkB)\n", b, t, bytes/1024);
517 totalbytes += bytes;
521 if(pages > 0) {
522 printf("VMSTATS: %dK net used in slab objects in %d pages (%dkB): %d%% utilization\n",
523 totalbytes/1024, pages, pages*VM_PAGE_SIZE/1024,
524 100 * totalbytes / (pages*VM_PAGE_SIZE));
527 #endif