4 #include <minix/callnr.h>
6 #include <minix/config.h>
7 #include <minix/const.h>
9 #include <minix/endpoint.h>
10 #include <minix/keymap.h>
11 #include <minix/minlib.h>
12 #include <minix/type.h>
13 #include <minix/ipc.h>
14 #include <minix/sysutil.h>
15 #include <minix/syslib.h>
16 #include <minix/bitmap.h>
17 #include <minix/debug.h>
29 #include "sanitycheck.h"
33 #define ITEMSPERPAGE(bytes) (DATABYTES / (bytes))
35 #define ELBITS (sizeof(element_t)*8)
36 #define BITPAT(b) (1UL << ((b) % ELBITS))
37 #define BITEL(f, b) (f)->sdh.usebits[(b)/ELBITS]
40 #define OFF(f, b) assert(!GETBIT(f, b))
41 #define ON(f, b) assert(GETBIT(f, b))
44 #define SLABDATAWRITABLE(data, wr) do { \
45 assert(data->sdh.writable == WRITABLE_NONE); \
46 assert(wr != WRITABLE_NONE); \
47 vm_pagelock(data, 0); \
48 data->sdh.writable = wr; \
51 #define SLABDATAUNWRITABLE(data) do { \
52 assert(data->sdh.writable != WRITABLE_NONE); \
53 data->sdh.writable = WRITABLE_NONE; \
54 vm_pagelock(data, 1); \
57 #define SLABDATAUSE(data, code) do { \
58 SLABDATAWRITABLE(data, WRITABLE_HEADER); \
60 SLABDATAUNWRITABLE(data); \
65 #define SLABDATAWRITABLE(data, wr)
66 #define SLABDATAUNWRITABLE(data)
67 #define SLABDATAUSE(data, code) do { code } while(0)
71 #define GETBIT(f, b) (BITEL(f,b) & BITPAT(b))
72 #define SETBIT(f, b) {OFF(f,b); SLABDATAUSE(f, BITEL(f,b)|= BITPAT(b); (f)->sdh.nused++;); }
73 #define CLEARBIT(f, b) {ON(f, b); SLABDATAUSE(f, BITEL(f,b)&=~BITPAT(b); (f)->sdh.nused--; (f)->sdh.freeguess = (b);); }
76 #define MAXSIZE (SLABSIZES-1+MINSIZE)
77 #define USEELEMENTS (1+(VM_PAGE_SIZE/MINSIZE/8))
79 PRIVATE
int pages
= 0;
81 typedef u8_t element_t
;
82 #define BITS_FULL (~(element_t)0)
83 typedef element_t elements_t
[USEELEMENTS
];
85 /* This file is too low-level to have global SANITYCHECKs everywhere,
86 * as the (other) data structures are often necessarily in an
87 * inconsistent state during a slaballoc() / slabfree(). So only do
88 * our own sanity checks here, with SLABSANITYCHECK.
92 /* Special writable values. */
93 #define WRITABLE_NONE -2
94 #define WRITABLE_HEADER -1
101 u16_t nused
; /* Number of data items used in this slab. */
103 struct slabdata
*next
, *prev
;
107 int writable
; /* data item number or WRITABLE_* */
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 #define LIST_UNUSED 0
123 #define LIST_NUMBER 4
125 PRIVATE
struct slabheader
{
128 u8_t data
[DATABYTES
];
129 } *list_head
[LIST_NUMBER
];
132 FORWARD
_PROTOTYPE( int objstats
, (void *, int, struct slabheader
**, struct slabdata
**, int *));
134 #define GETSLAB(b, s) { \
136 assert((b) >= MINSIZE); \
138 assert((i) < SLABSIZES); \
143 #define LH(sl, l) (sl)->list_head[l]
145 /* move head of list l1 to list of l2 in slabheader sl. */
146 #define MOVEHEAD(sl, l1, l2) { \
147 struct slabdata *t; \
149 REMOVEHEAD(sl, l1, t); \
150 ADDHEAD(t, sl, l2); \
153 /* remove head of list 'list' in sl, assign it unlinked to 'to'. */
154 #define REMOVEHEAD(sl, list, to) { \
155 struct slabdata *dat; \
156 dat = (to) = LH(sl, list); \
158 LH(sl, list) = dat->sdh.next; \
162 /* move slabdata nw to slabheader sl under list number l. */
163 #define ADDHEAD(nw, sl, l) { \
165 (nw)->sdh.next = LH(sl, l); \
166 (nw)->sdh.prev = NULL; \
167 (nw)->sdh.list = l;); \
169 if((nw)->sdh.next) { \
170 SLABDATAUSE((nw)->sdh.next, \
171 (nw)->sdh.next->sdh.prev = (nw);); \
175 #define UNLINKNODE(node) { \
176 struct slabdata *next, *prev; \
177 prev = (node)->sdh.prev; \
178 next = (node)->sdh.next; \
179 if(prev) { SLABDATAUSE(prev, prev->sdh.next = next;); } \
180 if(next) { SLABDATAUSE(next, next->sdh.prev = prev;); } \
183 struct slabdata
*newslabdata(int list
)
188 assert(sizeof(*n
) == VM_PAGE_SIZE
);
190 if(!(n
= vm_allocpage(&p
, VMP_SLAB
))) {
191 printf("newslabdata: vm_allocpage failed\n");
194 memset(n
->sdh
.usebits
, 0, sizeof(n
->sdh
.usebits
));
199 n
->sdh
.magic1
= MAGIC1
;
200 n
->sdh
.magic2
= MAGIC2
;
203 n
->sdh
.freeguess
= 0;
207 n
->sdh
.writable
= WRITABLE_HEADER
;
208 SLABDATAUNWRITABLE(n
);
216 /*===========================================================================*
218 *===========================================================================*/
219 PRIVATE
int checklist(char *file
, int line
,
220 struct slabheader
*s
, int l
, int bytes
)
222 struct slabdata
*n
= s
->list_head
[l
];
227 MYASSERT(n
->sdh
.magic1
== MAGIC1
);
228 MYASSERT(n
->sdh
.magic2
== MAGIC2
);
229 MYASSERT(n
->sdh
.list
== l
);
230 MYASSERT(usedpages_add(n
->sdh
.phys
, VM_PAGE_SIZE
) == OK
);
232 MYASSERT(n
->sdh
.prev
->sdh
.next
== n
);
234 MYASSERT(s
->list_head
[l
] == n
);
235 if(n
->sdh
.next
) MYASSERT(n
->sdh
.next
->sdh
.prev
== n
);
236 for(i
= 0; i
< USEELEMENTS
*8; i
++)
237 if(i
>= ITEMSPERPAGE(bytes
))
238 MYASSERT(!GETBIT(n
, i
));
242 MYASSERT(count
== n
->sdh
.nused
);
250 /*===========================================================================*
251 * void slab_sanitycheck *
252 *===========================================================================*/
253 PUBLIC
void slab_sanitycheck(char *file
, int line
)
256 for(s
= 0; s
< SLABSIZES
; s
++) {
258 for(l
= 0; l
< LIST_NUMBER
; l
++) {
259 checklist(file
, line
, &slabs
[s
], l
, s
+ MINSIZE
);
264 /*===========================================================================*
266 *===========================================================================*/
267 PUBLIC
int slabsane_f(char *file
, int line
, void *mem
, int bytes
)
269 struct slabheader
*s
;
273 return (objstats(mem
, bytes
, &s
, &f
, &i
) == OK
);
277 static int nojunkwarning
= 0;
279 /*===========================================================================*
281 *===========================================================================*/
282 PUBLIC
void *slaballoc(int bytes
)
286 struct slabheader
*s
;
287 struct slabdata
*firstused
;
289 SLABSANITYCHECK(SCL_FUNCTIONS
);
291 /* Retrieve entry in slabs[]. */
295 /* To make the common case more common, make space in the 'used'
298 if(!LH(s
, LIST_USED
)) {
299 /* Make sure there is something on the freelist. */
300 SLABSANITYCHECK(SCL_DETAIL
);
301 if(!LH(s
, LIST_FREE
)) {
302 struct slabdata
*nd
= newslabdata(LIST_FREE
);
303 SLABSANITYCHECK(SCL_DETAIL
);
305 ADDHEAD(nd
, s
, LIST_FREE
);
306 SLABSANITYCHECK(SCL_DETAIL
);
310 SLABSANITYCHECK(SCL_DETAIL
);
311 MOVEHEAD(s
, LIST_FREE
, LIST_USED
);
312 SLABSANITYCHECK(SCL_DETAIL
);
315 SLABSANITYCHECK(SCL_DETAIL
);
318 firstused
= LH(s
, LIST_USED
);
321 assert(firstused
->sdh
.magic1
== MAGIC1
);
322 assert(firstused
->sdh
.magic2
== MAGIC2
);
324 assert(firstused
->sdh
.nused
< ITEMSPERPAGE(bytes
));
326 for(i
= firstused
->sdh
.freeguess
;
327 count
< ITEMSPERPAGE(bytes
); count
++, i
++) {
328 SLABSANITYCHECK(SCL_DETAIL
);
329 i
= i
% ITEMSPERPAGE(bytes
);
331 if(!GETBIT(firstused
, i
)) {
334 SETBIT(firstused
, i
);
335 SLABSANITYCHECK(SCL_DETAIL
);
336 if(firstused
->sdh
.nused
== ITEMSPERPAGE(bytes
)) {
337 SLABSANITYCHECK(SCL_DETAIL
);
338 MOVEHEAD(s
, LIST_USED
, LIST_FULL
);
339 SLABSANITYCHECK(SCL_DETAIL
);
341 SLABSANITYCHECK(SCL_DETAIL
);
342 ret
= ((char *) firstused
->data
) + i
*bytes
;
346 slabunlock(ret
, bytes
);
348 assert(!nojunkwarning
);
349 *(u32_t
*) ret
= NOJUNK
;
350 slablock(ret
, bytes
);
352 SLABSANITYCHECK(SCL_FUNCTIONS
);
353 SLABDATAUSE(firstused
, firstused
->sdh
.freeguess
= i
+1;);
356 if(bytes
>= SLABSIZES
+MINSIZE
) {
357 printf("slaballoc: odd, bytes %d?\n", bytes
);
359 if(!slabsane_f(__FILE__
, __LINE__
, ret
, bytes
))
360 panic("slaballoc: slabsane failed");
366 SLABSANITYCHECK(SCL_DETAIL
);
369 SLABSANITYCHECK(SCL_FUNCTIONS
);
371 panic("slaballoc: no space in 'used' slabdata");
377 /*===========================================================================*
379 *===========================================================================*/
380 PRIVATE
int objstats(void *mem
, int bytes
,
381 struct slabheader
**sp
, struct slabdata
**fp
, int *ip
)
384 #define OBJSTATSCHECK(cond) \
386 printf("VM: objstats: %s failed for ptr 0x%p, %d bytes\n", \
387 #cond, mem, bytes); \
391 #define OBJSTATSCHECK(cond)
394 struct slabheader
*s
;
398 OBJSTATSCHECK((char *) mem
>= (char *) VM_PAGE_SIZE
);
401 if(*(u32_t
*) mem
== JUNK
&& !nojunkwarning
) {
403 printf("VM: WARNING: JUNK seen in slab object\n");
406 /* Retrieve entry in slabs[]. */
409 /* Round address down to VM_PAGE_SIZE boundary to get header. */
410 f
= (struct slabdata
*) ((char *) mem
- (vir_bytes
) mem
% VM_PAGE_SIZE
);
412 OBJSTATSCHECK(f
->sdh
.magic1
== MAGIC1
);
413 OBJSTATSCHECK(f
->sdh
.magic2
== MAGIC2
);
414 OBJSTATSCHECK(f
->sdh
.list
== LIST_USED
|| f
->sdh
.list
== LIST_FULL
);
416 /* Make sure it's in range. */
417 OBJSTATSCHECK((char *) mem
>= (char *) f
->data
);
418 OBJSTATSCHECK((char *) mem
< (char *) f
->data
+ sizeof(f
->data
));
421 i
= (char *) mem
- (char *) f
->data
;
422 OBJSTATSCHECK(!(i
% bytes
));
425 /* Make sure it is marked as allocated. */
426 OBJSTATSCHECK(GETBIT(f
, i
));
436 /*===========================================================================*
438 *===========================================================================*/
439 PUBLIC
void slabfree(void *mem
, int bytes
)
442 struct slabheader
*s
;
445 SLABSANITYCHECK(SCL_FUNCTIONS
);
447 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
) {
448 panic("slabfree objstats failed");
452 if(*(u32_t
*) mem
== JUNK
) {
453 printf("VM: WARNING: likely double free, JUNK seen\n");
456 slabunlock(mem
, bytes
);
457 *(u32_t
*) mem
= JUNK
;
459 slablock(mem
, bytes
);
461 assert(!nojunkwarning
);
464 /* Free this data. */
467 /* Check if this slab changes lists. */
468 if(f
->sdh
.nused
== 0) {
469 /* Now become FREE; must've been USED */
470 assert(f
->sdh
.list
== LIST_USED
);
472 if(f
== LH(s
, LIST_USED
))
473 LH(s
, LIST_USED
) = f
->sdh
.next
;
474 ADDHEAD(f
, s
, LIST_FREE
);
475 SLABSANITYCHECK(SCL_DETAIL
);
476 } else if(f
->sdh
.nused
== ITEMSPERPAGE(bytes
)-1) {
477 /* Now become USED; must've been FULL */
478 assert(f
->sdh
.list
== LIST_FULL
);
480 if(f
== LH(s
, LIST_FULL
))
481 LH(s
, LIST_FULL
) = f
->sdh
.next
;
482 ADDHEAD(f
, s
, LIST_USED
);
483 SLABSANITYCHECK(SCL_DETAIL
);
486 assert(f
->sdh
.list
== LIST_USED
);
489 SLABSANITYCHECK(SCL_FUNCTIONS
);
494 /*===========================================================================*
496 *===========================================================================*/
497 PUBLIC
void slablock(void *mem
, int bytes
)
500 struct slabheader
*s
;
503 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
)
504 panic("slablock objstats failed");
506 SLABDATAUNWRITABLE(f
);
511 /*===========================================================================*
513 *===========================================================================*/
514 PUBLIC
void slabunlock(void *mem
, int bytes
)
517 struct slabheader
*s
;
520 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
)
521 panic("slablock objstats failed");
523 SLABDATAWRITABLE(f
, i
);
529 /*===========================================================================*
531 *===========================================================================*/
532 PUBLIC
void slabstats(void)
534 int s
, total
= 0, totalbytes
= 0;
538 for(s
= 0; s
< SLABSIZES
; s
++) {
540 for(l
= 0; l
< LIST_NUMBER
; l
++) {
543 t
= checklist(__FILE__
, __LINE__
, &slabs
[s
], l
, b
);
547 printf("VMSTATS: %2d slabs: %d (%dkB)\n", b
, t
, bytes
/1024);
554 printf("VMSTATS: %dK net used in slab objects in %d pages (%dkB): %d%% utilization\n",
555 totalbytes
/1024, pages
, pages
*VM_PAGE_SIZE
/1024,
556 100 * totalbytes
/ (pages
*VM_PAGE_SIZE
));