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>
22 #include <sys/param.h>
27 #include "sanitycheck.h"
31 #define ITEMSPERPAGE(bytes) (int)(DATABYTES / (bytes))
33 #define ELBITS (sizeof(element_t)*8)
34 #define BITPAT(b) (1UL << ((b) % ELBITS))
35 #define BITEL(f, b) (f)->sdh.usebits[(b)/ELBITS]
38 #define OFF(f, b) assert(!GETBIT(f, b))
39 #define ON(f, b) assert(GETBIT(f, b))
42 #define SLABDATAWRITABLE(data, wr) do { \
43 assert(data->sdh.writable == WRITABLE_NONE); \
44 assert(wr != WRITABLE_NONE); \
45 vm_pagelock(data, 0); \
46 data->sdh.writable = wr; \
49 #define SLABDATAUNWRITABLE(data) do { \
50 assert(data->sdh.writable != WRITABLE_NONE); \
51 data->sdh.writable = WRITABLE_NONE; \
52 vm_pagelock(data, 1); \
55 #define SLABDATAUSE(data, code) do { \
56 SLABDATAWRITABLE(data, WRITABLE_HEADER); \
58 SLABDATAUNWRITABLE(data); \
63 #define SLABDATAWRITABLE(data, wr)
64 #define SLABDATAUNWRITABLE(data)
65 #define SLABDATAUSE(data, code) do { code } while(0)
69 #define GETBIT(f, b) (BITEL(f,b) & BITPAT(b))
70 #define SETBIT(f, b) {OFF(f,b); SLABDATAUSE(f, BITEL(f,b)|= BITPAT(b); (f)->sdh.nused++;); }
71 #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))
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 struct slabdata
*next
, *prev
;
105 int writable
; /* data item number or WRITABLE_* */
108 u16_t nused
; /* Number of data items used in this slab. */
111 #define DATABYTES (VM_PAGE_SIZE-sizeof(struct sdh))
113 #define MAGIC1 0x1f5b842f
114 #define MAGIC2 0x8bb5a420
115 #define JUNK 0xdeadbeef
116 #define NOJUNK 0xc0ffee
119 u8_t data
[DATABYTES
];
123 static struct slabheader
{
124 struct slabdata
*list_head
;
127 static int objstats(void *, int, struct slabheader
**, struct slabdata
130 #define GETSLAB(b, s) { \
132 assert((b) >= MINSIZE); \
133 _gsi = (b) - MINSIZE; \
134 assert((_gsi) < SLABSIZES); \
135 assert((_gsi) >= 0); \
139 /* move slabdata nw to slabheader sl under list number l. */
140 #define ADDHEAD(nw, sl) { \
142 (nw)->sdh.next = sl->list_head; \
143 (nw)->sdh.prev = NULL;); \
144 sl->list_head = nw; \
145 if((nw)->sdh.next) { \
146 SLABDATAUSE((nw)->sdh.next, \
147 (nw)->sdh.next->sdh.prev = (nw);); \
151 #define UNLINKNODE(node) { \
152 struct slabdata *next, *prev; \
153 prev = (node)->sdh.prev; \
154 next = (node)->sdh.next; \
155 if(prev) { SLABDATAUSE(prev, prev->sdh.next = next;); } \
156 if(next) { SLABDATAUSE(next, next->sdh.prev = prev;); } \
159 static struct slabdata
*newslabdata(void)
164 assert(sizeof(*n
) == VM_PAGE_SIZE
);
166 if(!(n
= vm_allocpage(&p
, VMP_SLAB
))) {
167 printf("newslabdata: vm_allocpage failed\n");
170 memset(n
->sdh
.usebits
, 0, sizeof(n
->sdh
.usebits
));
175 n
->sdh
.magic1
= MAGIC1
;
176 n
->sdh
.magic2
= MAGIC2
;
179 n
->sdh
.freeguess
= 0;
182 n
->sdh
.writable
= WRITABLE_HEADER
;
183 SLABDATAUNWRITABLE(n
);
191 /*===========================================================================*
193 *===========================================================================*/
194 static int checklist(const char *file
, int line
,
195 struct slabheader
*s
, int bytes
)
197 struct slabdata
*n
= s
->list_head
;
203 MYASSERT(n
->sdh
.magic1
== MAGIC1
);
204 MYASSERT(n
->sdh
.magic2
== MAGIC2
);
206 MYASSERT(usedpages_add(n
->sdh
.phys
, VM_PAGE_SIZE
) == OK
);
208 MYASSERT(n
->sdh
.prev
->sdh
.next
== n
);
210 MYASSERT(s
->list_head
== n
);
211 if(n
->sdh
.next
) MYASSERT(n
->sdh
.next
->sdh
.prev
== n
);
212 for(i
= 0; i
< USEELEMENTS
*8; i
++)
213 if(i
>= ITEMSPERPAGE(bytes
))
214 MYASSERT(!GETBIT(n
, i
));
218 MYASSERT(count
== n
->sdh
.nused
);
226 /*===========================================================================*
227 * void slab_sanitycheck *
228 *===========================================================================*/
229 void slab_sanitycheck(const char *file
, int line
)
232 for(s
= 0; s
< SLABSIZES
; s
++) {
233 checklist(file
, line
, &slabs
[s
], s
+ MINSIZE
);
237 /*===========================================================================*
239 *===========================================================================*/
240 int slabsane_f(const char *file
, int line
, void *mem
, int bytes
)
242 struct slabheader
*s
;
246 bytes
= roundup(bytes
, OBJALIGN
);
248 return (objstats(mem
, bytes
, &s
, &f
, &i
) == OK
);
253 static int nojunkwarning
= 0;
256 /*===========================================================================*
258 *===========================================================================*/
259 void *slaballoc(int bytes
)
263 struct slabheader
*s
;
264 struct slabdata
*newslab
;
267 bytes
= roundup(bytes
, OBJALIGN
);
269 SLABSANITYCHECK(SCL_FUNCTIONS
);
271 /* Retrieve entry in slabs[]. */
275 if(!(newslab
= s
->list_head
)) {
276 /* Make sure there is something on the freelist. */
277 newslab
= newslabdata();
278 if(!newslab
) return NULL
;
280 assert(newslab
->sdh
.nused
== 0);
281 } else assert(newslab
->sdh
.nused
> 0);
282 assert(newslab
->sdh
.nused
< ITEMSPERPAGE(bytes
));
284 SLABSANITYCHECK(SCL_DETAIL
);
287 assert(newslab
->sdh
.magic1
== MAGIC1
);
288 assert(newslab
->sdh
.magic2
== MAGIC2
);
291 for(i
= newslab
->sdh
.freeguess
;
292 count
< ITEMSPERPAGE(bytes
); count
++, i
++) {
293 i
= i
% ITEMSPERPAGE(bytes
);
295 if(!GETBIT(newslab
, i
))
299 SLABSANITYCHECK(SCL_FUNCTIONS
);
301 assert(count
< ITEMSPERPAGE(bytes
));
302 assert(i
>= 0 && i
< ITEMSPERPAGE(bytes
));
305 if(newslab
->sdh
.nused
== ITEMSPERPAGE(bytes
)) {
307 s
->list_head
= newslab
->sdh
.next
;
310 ret
= ((char *) newslab
) + i
*bytes
;
315 slabunlock(ret
, bytes
);
317 assert(!nojunkwarning
);
319 *(u32_t
*) ret
= NOJUNK
;
321 slablock(ret
, bytes
);
325 SLABDATAUSE(newslab
, newslab
->sdh
.freeguess
= i
+1;);
328 if(bytes
>= SLABSIZES
+MINSIZE
) {
329 printf("slaballoc: odd, bytes %d?\n", bytes
);
332 if(!slabsane_f(__FILE__
, __LINE__
, ret
, bytes
))
333 panic("slaballoc: slabsane failed");
336 assert(!((vir_bytes
) ret
% OBJALIGN
));
341 /*===========================================================================*
343 *===========================================================================*/
344 static inline int objstats(void *mem
, int bytes
,
345 struct slabheader
**sp
, struct slabdata
**fp
, int *ip
)
348 #define OBJSTATSCHECK(cond) \
350 printf("VM: objstats: %s failed for ptr %p, %d bytes\n", \
351 #cond, mem, bytes); \
355 #define OBJSTATSCHECK(cond)
358 struct slabheader
*s
;
362 assert(!(bytes
% OBJALIGN
));
364 OBJSTATSCHECK((char *) mem
>= (char *) VM_PAGE_SIZE
);
367 if(*(u32_t
*) mem
== JUNK
&& !nojunkwarning
) {
369 printf("VM: WARNING: JUNK seen in slab object, likely freed\n");
372 /* Retrieve entry in slabs[]. */
375 /* Round address down to VM_PAGE_SIZE boundary to get header. */
376 f
= (struct slabdata
*) ((char *) mem
- (vir_bytes
) mem
% VM_PAGE_SIZE
);
379 OBJSTATSCHECK(f
->sdh
.magic1
== MAGIC1
);
380 OBJSTATSCHECK(f
->sdh
.magic2
== MAGIC2
);
383 /* Make sure it's in range. */
384 OBJSTATSCHECK((char *) mem
>= (char *) f
->data
);
385 OBJSTATSCHECK((char *) mem
< (char *) f
->data
+ sizeof(f
->data
));
388 i
= (char *) mem
- (char *) f
->data
;
389 OBJSTATSCHECK(!(i
% bytes
));
392 /* Make sure it is marked as allocated. */
393 OBJSTATSCHECK(GETBIT(f
, i
));
403 /*===========================================================================*
405 *===========================================================================*/
406 void slabfree(void *mem
, int bytes
)
409 struct slabheader
*s
;
412 bytes
= roundup(bytes
, OBJALIGN
);
414 SLABSANITYCHECK(SCL_FUNCTIONS
);
416 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
) {
417 panic("slabfree objstats failed");
421 if(*(u32_t
*) mem
== JUNK
) {
422 printf("VM: WARNING: likely double free, JUNK seen\n");
428 slabunlock(mem
, bytes
);
431 memset(mem
, 0xa6, bytes
);
433 *(u32_t
*) mem
= JUNK
;
436 slablock(mem
, bytes
);
439 assert(!nojunkwarning
);
442 /* Free this data. */
445 /* Check if this slab changes lists. */
446 if(f
->sdh
.nused
== 0) {
448 if(f
== s
->list_head
) s
->list_head
= f
->sdh
.next
;
449 vm_freepages((vir_bytes
) f
, 1);
450 SLABSANITYCHECK(SCL_DETAIL
);
451 } else if(f
->sdh
.nused
== ITEMSPERPAGE(bytes
)-1) {
455 SLABSANITYCHECK(SCL_FUNCTIONS
);
461 /*===========================================================================*
463 *===========================================================================*/
464 void slablock(void *mem
, int bytes
)
467 struct slabheader
*s
;
470 bytes
= roundup(bytes
, OBJALIGN
);
472 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
)
473 panic("slablock objstats failed");
475 SLABDATAUNWRITABLE(f
);
480 /*===========================================================================*
482 *===========================================================================*/
483 void slabunlock(void *mem
, int bytes
)
486 struct slabheader
*s
;
489 bytes
= roundup(bytes
, OBJALIGN
);
491 if(objstats(mem
, bytes
, &s
, &f
, &i
) != OK
)
492 panic("slabunlock objstats failed");
494 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
));