| #include <vfs.h> |
| #include <kfs.h> |
| #include <slab.h> |
| #include <kmalloc.h> |
| #include <kref.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <error.h> |
| #include <cpio.h> |
| #include <pmap.h> |
| #include <smp.h> |
| |
| enum { |
| Hdrspc = 64, /* leave room for high-level headers */ |
| Bdead = 0x51494F42, /* "QIOB" */ |
| BLOCKALIGN = 32, /* known to be good for all systems. */ |
| }; |
| |
| static atomic_t ialloc_bytes = 0; |
| |
| static struct block *_allocb(int size) |
| { |
| struct block *b; |
| uint8_t *p; |
| int n; |
| |
| n = BLOCKALIGN + ROUNDUP(size + Hdrspc, BLOCKALIGN) + sizeof(struct block); |
| if ((p = kzmalloc(n, KMALLOC_WAIT)) == NULL) |
| return NULL; |
| |
| b = (struct block *)(p + n - sizeof(struct block)); /* block at end of allocated space */ |
| b->base = p; |
| |
| b->next = NULL; |
| b->list = NULL; |
| b->free = 0; |
| b->flag = 0; |
| |
| /* align base and bounds of data */ |
| b->lim = (uint8_t *) ((uintptr_t) b & ~(BLOCKALIGN - 1)); |
| |
| /* align start of writable data, leaving space below for added headers */ |
| b->rp = b->lim - ROUNDUP(size, BLOCKALIGN); |
| b->wp = b->rp; |
| |
| if (b->rp < b->base || b->lim - b->rp < size) |
| panic("_allocb"); |
| |
| return b; |
| } |
| |
| struct block *allocb(int size) |
| { |
| struct block *b; |
| |
| /* |
| * Check in a process and wait until successful. |
| * Can still error out of here, though. |
| * should only be called from user context. |
| */ |
| if ((b = _allocb(size)) == NULL) { |
| panic("allocb: no memory for %d bytes\n", size); |
| } |
| |
| return b; |
| } |
| |
| /* interrupt context allocb. */ |
| struct block *iallocb(int size) |
| { |
| struct block *b; |
| static int m1, m2, mp; |
| |
| if ((b = _allocb(size)) == NULL) { |
| if ((m2++ % 10000) == 0) { |
| if (mp++ > 1000) { |
| panic("iallocb"); |
| } |
| printd("iallocb: no memory \n"); |
| } |
| return NULL; |
| } |
| b->flag = BINTR; |
| |
| atomic_add(&ialloc_bytes, b->lim - b->base); |
| |
| return b; |
| } |
| |
| void freeb(struct block *b) |
| { |
| void *dead = (void *)Bdead; |
| uint8_t *p; |
| |
| if (b == NULL) |
| return; |
| |
| /* |
| * drivers which perform non cache coherent DMA manage their own buffer |
| * pool of uncached buffers and provide their own free routine. |
| */ |
| if (b->free) { |
| b->free(b); |
| return; |
| } |
| if (b->flag & BINTR) { |
| /* subtracting the size of b */ |
| atomic_add(&ialloc_bytes, -(b->lim - b->base)); |
| } |
| |
| p = b->base; |
| |
| /* poison the block in case someone is still holding onto it */ |
| b->next = dead; |
| b->rp = dead; |
| b->wp = dead; |
| b->lim = dead; |
| b->base = dead; |
| |
| kfree(p); |
| } |
| |
| void checkb(struct block *b, char *msg) |
| { |
| void *dead = (void *)Bdead; |
| |
| if (b == dead) |
| panic("checkb b %s %#p", msg, b); |
| if (b->base == dead || b->lim == dead || b->next == dead |
| || b->rp == dead || b->wp == dead) { |
| printd("checkb: base %#p lim %#p next %#p\n", b->base, b->lim, b->next); |
| printd("checkb: rp %#p wp %#p\n", b->rp, b->wp); |
| panic("checkb dead: %s\n", msg); |
| } |
| |
| if (b->base > b->lim) |
| panic("checkb 0 %s %#p %#p", msg, b->base, b->lim); |
| if (b->rp < b->base) |
| panic("checkb 1 %s %#p %#p", msg, b->base, b->rp); |
| if (b->wp < b->base) |
| panic("checkb 2 %s %#p %#p", msg, b->base, b->wp); |
| if (b->rp > b->lim) |
| panic("checkb 3 %s %#p %#p", msg, b->rp, b->lim); |
| if (b->wp > b->lim) |
| panic("checkb 4 %s %#p %#p", msg, b->wp, b->lim); |
| } |
| |
| void iallocsummary(void) |
| { |
| printd("ialloc %lu\n", atomic_read(&ialloc_bytes)); |
| } |