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>
26 #include <sys/param.h>
31 #include "sanitycheck.h"
35 #define ITEMSPERPAGE(bytes) (DATABYTES / (bytes))
37 #define ELBITS (sizeof(element_t)*8)
38 #define BITPAT(b) (1UL << ((b) % ELBITS))
39 #define BITEL(f, b) (f)->sdh.usebits[(b)/ELBITS]
42 #define OFF(f, b) assert(!GETBIT(f, b))
43 #define ON(f, b) assert(GETBIT(f, b))
46 #define SLABDATAWRITABLE(data, wr) do { \
47 assert(data->sdh.writable == WRITABLE_NONE); \
48 assert(wr != WRITABLE_NONE); \
49 vm_pagelock(data, 0); \
50 data->sdh.writable = wr; \
53 #define SLABDATAUNWRITABLE(data) do { \
54 assert(data->sdh.writable != WRITABLE_NONE); \
55 data->sdh.writable = WRITABLE_NONE; \
56 vm_pagelock(data, 1); \
59 #define SLABDATAUSE(data, code) do { \
60 SLABDATAWRITABLE(data, WRITABLE_HEADER); \
62 SLABDATAUNWRITABLE(data); \
67 #define SLABDATAWRITABLE(data, wr)
68 #define SLABDATAUNWRITABLE(data)
69 #define SLABDATAUSE(data, code) do { code } while(0)
73 #define GETBIT(f, b) (BITEL(f,b) & BITPAT(b))
74 #define SETBIT(f, b) {OFF(f,b); SLABDATAUSE(f, BITEL(f,b)|= BITPAT(b); (f)->sdh.nused++;); }
75 #define CLEARBIT(f, b) {ON(f, b); SLABDATAUSE(f, BITEL(f,b)&=~BITPAT(b); (f)->sdh.nused--; (f)->sdh.freeguess = (b);); }
80 #define MAXSIZE (SLABSIZES-1+MINSIZE)
81 #define USEELEMENTS (1+(VM_PAGE_SIZE/MINSIZE/8))
85 typedef u8_t element_t
;
86 #define BITS_FULL (~(element_t)0)
87 typedef element_t elements_t
[USEELEMENTS
];
89 /* This file is too low-level to have global SANITYCHECKs everywhere,
90 * as the (other) data structures are often necessarily in an
91 * inconsistent state during a slaballoc() / slabfree(). So only do
92 * our own sanity checks here, with SLABSANITYCHECK.
96 /* Special writable values. */
97 #define WRITABLE_NONE -2
98 #define WRITABLE_HEADER -1
105 struct slabdata
*next
, *prev
;
109 int writable
; /* data item number or WRITABLE_* */
112 u16_t nused
; /* Number of data items used in this slab. */
115 #define DATABYTES (VM_PAGE_SIZE-sizeof(struct sdh))
117 #define MAGIC1 0x1f5b842f
118 #define MAGIC2 0x8bb5a420
119 #define JUNK 0xdeadbeef
120 #define NOJUNK 0xc0ffee
122 static struct slabheader
{
124 u8_t data
[DATABYTES
];
129 static int objstats(void *, int, struct slabheader
**, struct slabdata
132 #define GETSLAB(b, s) { \
134 assert((b) >= MINSIZE); \
136 assert((i) < SLABSIZES); \
141 /* move slabdata nw to slabheader sl under list number l. */
142 #define ADDHEAD(nw, sl) { \
144 (nw)->sdh.next = sl->list_head; \
145 (nw)->sdh.prev = NULL;); \
146 sl->list_head = nw; \
147 if((nw)->sdh.next) { \
148 SLABDATAUSE((nw)->sdh.next, \
149 (nw)->sdh.next->sdh.prev = (nw);); \
153 #define UNLINKNODE(node) { \
154 struct slabdata *next, *prev; \
155 prev = (node)->sdh.prev; \
156 next = (node)->sdh.next; \
157 if(prev) { SLABDATAUSE(prev, prev->sdh.next = next;); } \
158 if(next) { SLABDATAUSE(next, next->sdh.prev = prev;); } \
161 static struct slabdata
*newslabdata()
166 assert(sizeof(*n
) == VM_PAGE_SIZE
);
168 if(!(n
= vm_allocpage(&p
, VMP_SLAB
))) {
169 printf("newslabdata: vm_allocpage failed\n");
172 memset(n
->sdh
.usebits
, 0, sizeof(n
->sdh
.usebits
));
177 n
->sdh
.magic1
= MAGIC1
;
178 n
->sdh
.magic2
= MAGIC2
;
181 n
->sdh
.freeguess
= 0;
184 n
->sdh
.writable
= WRITABLE_HEADER
;
185 SLABDATAUNWRITABLE(n
);
193 /*===========================================================================*
195 *===========================================================================*/
196 static int checklist(char *file
, int line
,
197 struct slabheader
*s
, int bytes
)
199 struct slabdata
*n
= s
->list_head
;
205 MYASSERT(n
->sdh
.magic1
== MAGIC1
);
206 MYASSERT(n
->sdh
.magic2
== MAGIC2
);
208 MYASSERT(usedpages_add(n
->sdh
.phys
, VM_PAGE_SIZE
) == OK
);
210 MYASSERT(n
->sdh
.prev
->sdh
.next
== n
);
212 MYASSERT(s
->list_head
== n
);
213 if(n
->sdh
.next
) MYASSERT(n
->sdh
.next
->sdh
.prev
== n
);
214 for(i
= 0; i
< USEELEMENTS
*8; i
++)
215 if(i
>= ITEMSPERPAGE(bytes
))
216 MYASSERT(!GETBIT(n
, i
));
220 MYASSERT(count
== n
->sdh
.nused
);
228 /*===========================================================================*
229 * void slab_sanitycheck *
230 *===========================================================================*/
231 void slab_sanitycheck(char *file
, int line
)
234 for(s
= 0; s
< SLABSIZES
; s
++) {
235 checklist(file
, line
, &slabs
[s
], s
+ MINSIZE
);
239 /*===========================================================================*
241 *===========================================================================*/
242 int slabsane_f(char *file
, int line
, void *mem
, int bytes
)
244 struct slabheader
*s
;
248 bytes
= roundup(bytes
, OBJALIGN
);
250 return (objstats(mem
, bytes
, &s
, &f
, &i
) == OK
);
255 static int nojunkwarning
= 0;
258 /*===========================================================================*
260 *===========================================================================*/
261 void *slaballoc(int bytes
)
265 struct slabheader
*s
;
266 struct slabdata
*newslab
;
269 bytes
= roundup(bytes
, OBJALIGN
);
271 SLABSANITYCHECK(SCL_FUNCTIONS
);
273 /* Retrieve entry in slabs[]. */
277 if(!(newslab
= s
->list_head
)) {
278 /* Make sure there is something on the freelist. */
279 newslab
= newslabdata();
280 if(!newslab
) return NULL
;
282 assert(newslab
->sdh
.nused
== 0);
283 } else assert(newslab
->sdh
.nused
> 0);
284 assert(newslab
->sdh
.nused
< ITEMSPERPAGE(bytes
));
286 SLABSANITYCHECK(SCL_DETAIL
);
289 assert(newslab
->sdh
.magic1
== MAGIC1
);
290 assert(newslab
->sdh
.magic2
== MAGIC2
);
293 for(i
= newslab
->sdh
.freeguess
;
294 count
< ITEMSPERPAGE(bytes
); count
++, i
++) {
295 i
= i
% ITEMSPERPAGE(bytes
);
297 if(!GETBIT(newslab
, i
))
301 SLABSANITYCHECK(SCL_FUNCTIONS
);
303 assert(count
< ITEMSPERPAGE(bytes
));
304 assert(i
>= 0 && i
< ITEMSPERPAGE(bytes
));
307 if(newslab
->sdh
.nused
== ITEMSPERPAGE(bytes
)) {
309 s
->list_head
= newslab
->sdh
.next
;
312 ret
= ((char *) newslab
) + i
*bytes
;
317 slabunlock(ret
, bytes
);
319 assert(!nojunkwarning
);
321 *(u32_t
*) ret
= NOJUNK
;
323 slablock(ret
, bytes
);
327 SLABDATAUSE(newslab
, newslab
->sdh
.freeguess
= i
+1;);
330 if(bytes
>= SLABSIZES
+MINSIZE
) {
331 printf("slaballoc: odd, bytes %d?\n", bytes
);
334 if(!slabsane_f(__FILE__
, __LINE__
, ret
, bytes
))
335 panic("slaballoc: slabsane failed");
338 assert(!((vir_bytes
) ret
% OBJALIGN
));
343 /*===========================================================================*
345 *===========================================================================*/
346 static inline int objstats(void *mem
, int bytes
,
347 struct slabheader
**sp
, struct slabdata
**fp
, int *ip
)
350 #define OBJSTATSCHECK(cond) \
352 printf("VM: objstats: %s failed for ptr %p, %d bytes\n", \
353 #cond, mem, bytes); \
357 #define OBJSTATSCHECK(cond)
360 struct slabheader
*s
;
364 assert(!(bytes
% OBJALIGN
));
366 OBJSTATSCHECK((char *) mem
>= (char *) VM_PAGE_SIZE
);
369 if(*(u32_t
*) mem
== JUNK
&& !nojunkwarning
) {
371 printf("VM: WARNING: JUNK seen in slab object, likely freed\n");
374 /* Retrieve entry in slabs[]. */
377 /* Round address down to VM_PAGE_SIZE boundary to get header. */
378 f
= (struct slabdata
*) ((char *) mem
- (vir_bytes
) mem
% VM_PAGE_SIZE
);
381 OBJSTATSCHECK(f
->sdh
.magic1
== MAGIC1
);
382 OBJSTATSCHECK(f
->sdh
.magic2
== MAGIC2
);
385 /* Make sure it's in range. */
386 OBJSTATSCHECK((char *) mem
>= (char *) f
->data
);
387 OBJSTATSCHECK((char *) mem
< (char *) f
->data
+ sizeof(f
->data
));
390 i
= (char *) mem
- (char *) f
->data
;
391 OBJSTATSCHECK(!(i
% bytes
));
394 /* Make sure it is marked as allocated. */
395 OBJSTATSCHECK(GETBIT(f
, i
));
405 /*===========================================================================*
407 *===========================================================================*/
408 void slabfree(void *mem
, int bytes
)
411 struct slabheader
*s
;
414 bytes
= roundup(bytes
, OBJALIGN
);
416 SLABSANITYCHECK(SCL_FUNCTIONS
);
418 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
) {
419 panic("slabfree objstats failed");
423 if(*(u32_t
*) mem
== JUNK
) {
424 printf("VM: WARNING: likely double free, JUNK seen\n");
430 slabunlock(mem
, bytes
);
433 memset(mem
, 0xa6, bytes
);
435 *(u32_t
*) mem
= JUNK
;
438 slablock(mem
, bytes
);
441 assert(!nojunkwarning
);
444 /* Free this data. */
447 /* Check if this slab changes lists. */
448 if(f
->sdh
.nused
== 0) {
450 if(f
== s
->list_head
) s
->list_head
= f
->sdh
.next
;
451 vm_freepages((vir_bytes
) f
, 1);
452 SLABSANITYCHECK(SCL_DETAIL
);
453 } else if(f
->sdh
.nused
== ITEMSPERPAGE(bytes
)-1) {
457 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
);
501 /*===========================================================================*
503 *===========================================================================*/
506 int s
, totalbytes
= 0;
510 for(s
= 0; s
< SLABSIZES
; s
++) {
513 t
= checklist(__FILE__
, __LINE__
, &slabs
[s
], b
);
517 printf("VMSTATS: %2d slabs: %d (%dkB)\n", b
, t
, bytes
/1024);
523 printf("VMSTATS: %dK net used in slab objects in %d pages (%dkB): %d%% utilization\n",
524 totalbytes
/1024, pages
, pages
*VM_PAGE_SIZE
/1024,
525 100 * totalbytes
/ (pages
*VM_PAGE_SIZE
));