4 #include <minix/callnr.h>
6 #include <minix/config.h>
7 #include <minix/const.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>
23 #include <sys/param.h>
28 #include "sanitycheck.h"
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))
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; \
50 #define SLABDATAUNWRITABLE(data) do { \
51 assert(data->sdh.writable != WRITABLE_NONE); \
52 data->sdh.writable = WRITABLE_NONE; \
53 vm_pagelock(data, 1); \
56 #define SLABDATAUSE(data, code) do { \
57 SLABDATAWRITABLE(data, WRITABLE_HEADER); \
59 SLABDATAUNWRITABLE(data); \
64 #define SLABDATAWRITABLE(data, wr)
65 #define SLABDATAUNWRITABLE(data)
66 #define SLABDATAUSE(data, code) do { code } while(0)
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);); }
77 #define MAXSIZE (SLABSIZES-1+MINSIZE)
78 #define USEELEMENTS (1+(VM_PAGE_SIZE/MINSIZE/8))
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
102 struct slabdata
*next
, *prev
;
106 int writable
; /* data item number or WRITABLE_* */
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
{
121 u8_t data
[DATABYTES
];
126 static int objstats(void *, int, struct slabheader
**, struct slabdata
129 #define GETSLAB(b, s) { \
131 assert((b) >= MINSIZE); \
132 _gsi = (b) - MINSIZE; \
133 assert((_gsi) < SLABSIZES); \
134 assert((_gsi) >= 0); \
138 /* move slabdata nw to slabheader sl under list number l. */
139 #define ADDHEAD(nw, sl) { \
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)
163 assert(sizeof(*n
) == VM_PAGE_SIZE
);
165 if(!(n
= vm_allocpage(&p
, VMP_SLAB
))) {
166 printf("newslabdata: vm_allocpage failed\n");
169 memset(n
->sdh
.usebits
, 0, sizeof(n
->sdh
.usebits
));
174 n
->sdh
.magic1
= MAGIC1
;
175 n
->sdh
.magic2
= MAGIC2
;
178 n
->sdh
.freeguess
= 0;
181 n
->sdh
.writable
= WRITABLE_HEADER
;
182 SLABDATAUNWRITABLE(n
);
190 /*===========================================================================*
192 *===========================================================================*/
193 static int checklist(const char *file
, int line
,
194 struct slabheader
*s
, int bytes
)
196 struct slabdata
*n
= s
->list_head
;
202 MYASSERT(n
->sdh
.magic1
== MAGIC1
);
203 MYASSERT(n
->sdh
.magic2
== MAGIC2
);
205 MYASSERT(usedpages_add(n
->sdh
.phys
, VM_PAGE_SIZE
) == OK
);
207 MYASSERT(n
->sdh
.prev
->sdh
.next
== n
);
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
));
217 MYASSERT(count
== n
->sdh
.nused
);
225 /*===========================================================================*
226 * void slab_sanitycheck *
227 *===========================================================================*/
228 void slab_sanitycheck(const char *file
, int line
)
231 for(s
= 0; s
< SLABSIZES
; s
++) {
232 checklist(file
, line
, &slabs
[s
], s
+ MINSIZE
);
236 /*===========================================================================*
238 *===========================================================================*/
239 int slabsane_f(const char *file
, int line
, void *mem
, int bytes
)
241 struct slabheader
*s
;
245 bytes
= roundup(bytes
, OBJALIGN
);
247 return (objstats(mem
, bytes
, &s
, &f
, &i
) == OK
);
252 static int nojunkwarning
= 0;
255 /*===========================================================================*
257 *===========================================================================*/
258 void *slaballoc(int bytes
)
262 struct slabheader
*s
;
263 struct slabdata
*newslab
;
266 bytes
= roundup(bytes
, OBJALIGN
);
268 SLABSANITYCHECK(SCL_FUNCTIONS
);
270 /* Retrieve entry in slabs[]. */
274 if(!(newslab
= s
->list_head
)) {
275 /* Make sure there is something on the freelist. */
276 newslab
= newslabdata();
277 if(!newslab
) return NULL
;
279 assert(newslab
->sdh
.nused
== 0);
280 } else assert(newslab
->sdh
.nused
> 0);
281 assert(newslab
->sdh
.nused
< ITEMSPERPAGE(bytes
));
283 SLABSANITYCHECK(SCL_DETAIL
);
286 assert(newslab
->sdh
.magic1
== MAGIC1
);
287 assert(newslab
->sdh
.magic2
== MAGIC2
);
290 for(i
= newslab
->sdh
.freeguess
;
291 count
< ITEMSPERPAGE(bytes
); count
++, i
++) {
292 i
= i
% ITEMSPERPAGE(bytes
);
294 if(!GETBIT(newslab
, i
))
298 SLABSANITYCHECK(SCL_FUNCTIONS
);
300 assert(count
< ITEMSPERPAGE(bytes
));
301 assert(i
>= 0 && i
< ITEMSPERPAGE(bytes
));
304 if(newslab
->sdh
.nused
== ITEMSPERPAGE(bytes
)) {
306 s
->list_head
= newslab
->sdh
.next
;
309 ret
= ((char *) newslab
) + i
*bytes
;
314 slabunlock(ret
, bytes
);
316 assert(!nojunkwarning
);
318 *(u32_t
*) ret
= NOJUNK
;
320 slablock(ret
, bytes
);
324 SLABDATAUSE(newslab
, newslab
->sdh
.freeguess
= i
+1;);
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");
335 assert(!((vir_bytes
) ret
% OBJALIGN
));
340 /*===========================================================================*
342 *===========================================================================*/
343 static inline int objstats(void *mem
, int bytes
,
344 struct slabheader
**sp
, struct slabdata
**fp
, int *ip
)
347 #define OBJSTATSCHECK(cond) \
349 printf("VM: objstats: %s failed for ptr %p, %d bytes\n", \
350 #cond, mem, bytes); \
354 #define OBJSTATSCHECK(cond)
357 struct slabheader
*s
;
361 assert(!(bytes
% OBJALIGN
));
363 OBJSTATSCHECK((char *) mem
>= (char *) VM_PAGE_SIZE
);
366 if(*(u32_t
*) mem
== JUNK
&& !nojunkwarning
) {
368 printf("VM: WARNING: JUNK seen in slab object, likely freed\n");
371 /* Retrieve entry in slabs[]. */
374 /* Round address down to VM_PAGE_SIZE boundary to get header. */
375 f
= (struct slabdata
*) ((char *) mem
- (vir_bytes
) mem
% VM_PAGE_SIZE
);
378 OBJSTATSCHECK(f
->sdh
.magic1
== MAGIC1
);
379 OBJSTATSCHECK(f
->sdh
.magic2
== MAGIC2
);
382 /* Make sure it's in range. */
383 OBJSTATSCHECK((char *) mem
>= (char *) f
->data
);
384 OBJSTATSCHECK((char *) mem
< (char *) f
->data
+ sizeof(f
->data
));
387 i
= (char *) mem
- (char *) f
->data
;
388 OBJSTATSCHECK(!(i
% bytes
));
391 /* Make sure it is marked as allocated. */
392 OBJSTATSCHECK(GETBIT(f
, i
));
402 /*===========================================================================*
404 *===========================================================================*/
405 void slabfree(void *mem
, int bytes
)
408 struct slabheader
*s
;
411 bytes
= roundup(bytes
, OBJALIGN
);
413 SLABSANITYCHECK(SCL_FUNCTIONS
);
415 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
) {
416 panic("slabfree objstats failed");
420 if(*(u32_t
*) mem
== JUNK
) {
421 printf("VM: WARNING: likely double free, JUNK seen\n");
427 slabunlock(mem
, bytes
);
430 memset(mem
, 0xa6, bytes
);
432 *(u32_t
*) mem
= JUNK
;
435 slablock(mem
, bytes
);
438 assert(!nojunkwarning
);
441 /* Free this data. */
444 /* Check if this slab changes lists. */
445 if(f
->sdh
.nused
== 0) {
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) {
454 SLABSANITYCHECK(SCL_FUNCTIONS
);
460 /*===========================================================================*
462 *===========================================================================*/
463 void slablock(void *mem
, int bytes
)
466 struct slabheader
*s
;
469 bytes
= roundup(bytes
, OBJALIGN
);
471 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
)
472 panic("slablock objstats failed");
474 SLABDATAUNWRITABLE(f
);
479 /*===========================================================================*
481 *===========================================================================*/
482 void slabunlock(void *mem
, int bytes
)
485 struct slabheader
*s
;
488 bytes
= roundup(bytes
, OBJALIGN
);
490 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
)
491 panic("slabunlock objstats failed");
493 SLABDATAWRITABLE(f
, i
);
500 /*===========================================================================*
502 *===========================================================================*/
505 int s
, totalbytes
= 0;
509 for(s
= 0; s
< SLABSIZES
; s
++) {
512 t
= checklist(__FILE__
, __LINE__
, &slabs
[s
], b
);
516 printf("VMSTATS: %2d slabs: %d (%dkB)\n", b
, t
, bytes
/1024);
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
));