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
120 u8_t data
[DATABYTES
];
124 static struct slabheader
{
125 struct slabdata
*list_head
;
128 static int objstats(void *, int, struct slabheader
**, struct slabdata
131 #define GETSLAB(b, s) { \
133 assert((b) >= MINSIZE); \
134 _gsi = (b) - MINSIZE; \
135 assert((_gsi) < SLABSIZES); \
136 assert((_gsi) >= 0); \
140 /* move slabdata nw to slabheader sl under list number l. */
141 #define ADDHEAD(nw, sl) { \
143 (nw)->sdh.next = sl->list_head; \
144 (nw)->sdh.prev = NULL;); \
145 sl->list_head = nw; \
146 if((nw)->sdh.next) { \
147 SLABDATAUSE((nw)->sdh.next, \
148 (nw)->sdh.next->sdh.prev = (nw);); \
152 #define UNLINKNODE(node) { \
153 struct slabdata *next, *prev; \
154 prev = (node)->sdh.prev; \
155 next = (node)->sdh.next; \
156 if(prev) { SLABDATAUSE(prev, prev->sdh.next = next;); } \
157 if(next) { SLABDATAUSE(next, next->sdh.prev = prev;); } \
160 static struct slabdata
*newslabdata(void)
165 assert(sizeof(*n
) == VM_PAGE_SIZE
);
167 if(!(n
= vm_allocpage(&p
, VMP_SLAB
))) {
168 printf("newslabdata: vm_allocpage failed\n");
171 memset(n
->sdh
.usebits
, 0, sizeof(n
->sdh
.usebits
));
176 n
->sdh
.magic1
= MAGIC1
;
177 n
->sdh
.magic2
= MAGIC2
;
180 n
->sdh
.freeguess
= 0;
183 n
->sdh
.writable
= WRITABLE_HEADER
;
184 SLABDATAUNWRITABLE(n
);
192 /*===========================================================================*
194 *===========================================================================*/
195 static int checklist(const char *file
, int line
,
196 struct slabheader
*s
, int bytes
)
198 struct slabdata
*n
= s
->list_head
;
204 MYASSERT(n
->sdh
.magic1
== MAGIC1
);
205 MYASSERT(n
->sdh
.magic2
== MAGIC2
);
207 MYASSERT(usedpages_add(n
->sdh
.phys
, VM_PAGE_SIZE
) == OK
);
209 MYASSERT(n
->sdh
.prev
->sdh
.next
== n
);
211 MYASSERT(s
->list_head
== n
);
212 if(n
->sdh
.next
) MYASSERT(n
->sdh
.next
->sdh
.prev
== n
);
213 for(i
= 0; i
< USEELEMENTS
*8; i
++)
214 if(i
>= ITEMSPERPAGE(bytes
))
215 MYASSERT(!GETBIT(n
, i
));
219 MYASSERT(count
== n
->sdh
.nused
);
227 /*===========================================================================*
228 * void slab_sanitycheck *
229 *===========================================================================*/
230 void slab_sanitycheck(const char *file
, int line
)
233 for(s
= 0; s
< SLABSIZES
; s
++) {
234 checklist(file
, line
, &slabs
[s
], s
+ MINSIZE
);
238 /*===========================================================================*
240 *===========================================================================*/
241 int slabsane_f(const char *file
, int line
, void *mem
, int bytes
)
243 struct slabheader
*s
;
247 bytes
= roundup(bytes
, OBJALIGN
);
249 return (objstats(mem
, bytes
, &s
, &f
, &i
) == OK
);
254 static int nojunkwarning
= 0;
257 /*===========================================================================*
259 *===========================================================================*/
260 void *slaballoc(int bytes
)
264 struct slabheader
*s
;
265 struct slabdata
*newslab
;
268 bytes
= roundup(bytes
, OBJALIGN
);
270 SLABSANITYCHECK(SCL_FUNCTIONS
);
272 /* Retrieve entry in slabs[]. */
276 if(!(newslab
= s
->list_head
)) {
277 /* Make sure there is something on the freelist. */
278 newslab
= newslabdata();
279 if(!newslab
) return NULL
;
281 assert(newslab
->sdh
.nused
== 0);
282 } else assert(newslab
->sdh
.nused
> 0);
283 assert(newslab
->sdh
.nused
< ITEMSPERPAGE(bytes
));
285 SLABSANITYCHECK(SCL_DETAIL
);
288 assert(newslab
->sdh
.magic1
== MAGIC1
);
289 assert(newslab
->sdh
.magic2
== MAGIC2
);
292 for(i
= newslab
->sdh
.freeguess
;
293 count
< ITEMSPERPAGE(bytes
); count
++, i
++) {
294 i
= i
% ITEMSPERPAGE(bytes
);
296 if(!GETBIT(newslab
, i
))
300 SLABSANITYCHECK(SCL_FUNCTIONS
);
302 assert(count
< ITEMSPERPAGE(bytes
));
303 assert(i
>= 0 && i
< ITEMSPERPAGE(bytes
));
306 if(newslab
->sdh
.nused
== ITEMSPERPAGE(bytes
)) {
308 s
->list_head
= newslab
->sdh
.next
;
311 ret
= ((char *) newslab
) + i
*bytes
;
316 slabunlock(ret
, bytes
);
318 assert(!nojunkwarning
);
320 *(u32_t
*) ret
= NOJUNK
;
322 slablock(ret
, bytes
);
326 SLABDATAUSE(newslab
, newslab
->sdh
.freeguess
= i
+1;);
329 if(bytes
>= SLABSIZES
+MINSIZE
) {
330 printf("slaballoc: odd, bytes %d?\n", bytes
);
333 if(!slabsane_f(__FILE__
, __LINE__
, ret
, bytes
))
334 panic("slaballoc: slabsane failed");
337 assert(!((vir_bytes
) ret
% OBJALIGN
));
342 /*===========================================================================*
344 *===========================================================================*/
345 static inline int objstats(void *mem
, int bytes
,
346 struct slabheader
**sp
, struct slabdata
**fp
, int *ip
)
349 #define OBJSTATSCHECK(cond) \
351 printf("VM: objstats: %s failed for ptr %p, %d bytes\n", \
352 #cond, mem, bytes); \
356 #define OBJSTATSCHECK(cond)
359 struct slabheader
*s
;
363 assert(!(bytes
% OBJALIGN
));
365 OBJSTATSCHECK((char *) mem
>= (char *) VM_PAGE_SIZE
);
368 if(*(u32_t
*) mem
== JUNK
&& !nojunkwarning
) {
370 printf("VM: WARNING: JUNK seen in slab object, likely freed\n");
373 /* Retrieve entry in slabs[]. */
376 /* Round address down to VM_PAGE_SIZE boundary to get header. */
377 f
= (struct slabdata
*) ((char *) mem
- (vir_bytes
) mem
% VM_PAGE_SIZE
);
380 OBJSTATSCHECK(f
->sdh
.magic1
== MAGIC1
);
381 OBJSTATSCHECK(f
->sdh
.magic2
== MAGIC2
);
384 /* Make sure it's in range. */
385 OBJSTATSCHECK((char *) mem
>= (char *) f
->data
);
386 OBJSTATSCHECK((char *) mem
< (char *) f
->data
+ sizeof(f
->data
));
389 i
= (char *) mem
- (char *) f
->data
;
390 OBJSTATSCHECK(!(i
% bytes
));
393 /* Make sure it is marked as allocated. */
394 OBJSTATSCHECK(GETBIT(f
, i
));
404 /*===========================================================================*
406 *===========================================================================*/
407 void slabfree(void *mem
, int bytes
)
410 struct slabheader
*s
;
413 bytes
= roundup(bytes
, OBJALIGN
);
415 SLABSANITYCHECK(SCL_FUNCTIONS
);
417 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
) {
418 panic("slabfree objstats failed");
422 if(*(u32_t
*) mem
== JUNK
) {
423 printf("VM: WARNING: likely double free, JUNK seen\n");
429 slabunlock(mem
, bytes
);
432 memset(mem
, 0xa6, bytes
);
434 *(u32_t
*) mem
= JUNK
;
437 slablock(mem
, bytes
);
440 assert(!nojunkwarning
);
443 /* Free this data. */
446 /* Check if this slab changes lists. */
447 if(f
->sdh
.nused
== 0) {
449 if(f
== s
->list_head
) s
->list_head
= f
->sdh
.next
;
450 vm_freepages((vir_bytes
) f
, 1);
451 SLABSANITYCHECK(SCL_DETAIL
);
452 } else if(f
->sdh
.nused
== ITEMSPERPAGE(bytes
)-1) {
456 SLABSANITYCHECK(SCL_FUNCTIONS
);
462 /*===========================================================================*
464 *===========================================================================*/
465 void slablock(void *mem
, int bytes
)
468 struct slabheader
*s
;
471 bytes
= roundup(bytes
, OBJALIGN
);
473 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
)
474 panic("slablock objstats failed");
476 SLABDATAUNWRITABLE(f
);
481 /*===========================================================================*
483 *===========================================================================*/
484 void slabunlock(void *mem
, int bytes
)
487 struct slabheader
*s
;
490 bytes
= roundup(bytes
, OBJALIGN
);
492 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
)
493 panic("slabunlock objstats failed");
495 SLABDATAWRITABLE(f
, i
);
502 /*===========================================================================*
504 *===========================================================================*/
507 int s
, totalbytes
= 0;
511 for(s
= 0; s
< SLABSIZES
; s
++) {
514 t
= checklist(__FILE__
, __LINE__
, &slabs
[s
], b
);
518 printf("VMSTATS: %2d slabs: %d (%dkB)\n", b
, t
, bytes
/1024);
524 printf("VMSTATS: %dK net used in slab objects in %d pages (%dkB): %d%% utilization\n",
525 totalbytes
/1024, pages
, pages
*VM_PAGE_SIZE
/1024,
526 100 * totalbytes
/ (pages
*VM_PAGE_SIZE
));