| // INFERNO |
| #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> |
| #include <ip.h> |
| #include <process.h> |
| |
| /* Note that Hdrspc is only available via padblock (to the 'left' of the rp). */ |
| enum { |
| Hdrspc = 128, /* leave room for high-level headers */ |
| Bdead = 0x51494F42, /* "QIOB" */ |
| BLOCKALIGN = 32, /* was the old BY2V in inferno, which was 8 */ |
| }; |
| |
| static atomic_t ialloc_bytes = 0; |
| |
| /* |
| * allocate blocks (round data base address to 64 bit boundary). |
| * if mallocz gives us more than we asked for, leave room at the front |
| * for header. |
| */ |
| static struct block *_allocb(int size, int mem_flags) |
| { |
| struct block *b; |
| uintptr_t addr; |
| int n; |
| |
| b = kmalloc(sizeof(struct block) + size + Hdrspc + (BLOCKALIGN - 1), |
| mem_flags); |
| if (b == NULL) |
| return NULL; |
| |
| b->next = NULL; |
| b->list = NULL; |
| b->free = NULL; |
| b->flag = 0; |
| b->extra_len = 0; |
| b->nr_extra_bufs = 0; |
| b->extra_data = 0; |
| |
| addr = (uintptr_t) b; |
| addr = ROUNDUP(addr + sizeof(struct block), BLOCKALIGN); |
| b->base = (uint8_t *) addr; |
| /* TODO: support this */ |
| /* interesting. We can ask the allocator, after allocating, |
| * the *real* size of the block we got. Very nice. |
| * Not on akaros yet. |
| b->lim = ((uint8_t*)b) + msize(b); |
| */ |
| b->lim = |
| ((uint8_t *) b) + sizeof(struct block) + size + Hdrspc + (BLOCKALIGN - |
| 1); |
| b->rp = b->base; |
| n = b->lim - b->base - size; |
| b->rp += n & ~(BLOCKALIGN - 1); |
| b->wp = b->rp; |
| /* b->base is aligned, rounded up from b |
| * b->lim is the upper bound on our malloc |
| * b->rp is advanced by some aligned amount, based on how much extra we |
| * received from kmalloc and the Hdrspc. */ |
| return b; |
| } |
| |
| struct block *allocb(int size) |
| { |
| return _allocb(size, KMALLOC_WAIT); |
| } |
| |
| /* Makes sure b has nr_bufs extra_data. Will grow, but not shrink, an existing |
| * extra_data array. When growing, it'll copy over the old entries. All new |
| * entries will be zeroed. mem_flags determines if we'll block on kmallocs. |
| * |
| * Caller is responsible for concurrent access to the block's metadata. */ |
| void block_add_extd(struct block *b, unsigned int nr_bufs, int mem_flags) |
| { |
| unsigned int old_nr_bufs = b->nr_extra_bufs; |
| size_t old_amt = sizeof(struct extra_bdata) * old_nr_bufs; |
| size_t new_amt = sizeof(struct extra_bdata) * nr_bufs; |
| void *new_bdata; |
| |
| if (old_nr_bufs >= nr_bufs) |
| return; |
| if (b->extra_data) { |
| new_bdata = krealloc(b->extra_data, new_amt, mem_flags); |
| if (!new_bdata) |
| return; |
| memset(new_bdata + old_amt, 0, new_amt - old_amt); |
| } else { |
| new_bdata = kzmalloc(new_amt, mem_flags); |
| if (!new_bdata) |
| return; |
| } |
| b->extra_data = new_bdata; |
| b->nr_extra_bufs = nr_bufs; |
| } |
| |
| /* |
| * interrupt time allocation |
| */ |
| struct block *iallocb(int size) |
| { |
| struct block *b; |
| |
| #if 0 /* conf is some inferno global config */ |
| if (atomic_read(&ialloc_bytes) > conf.ialloc) { |
| //printk("iallocb: limited %lu/%lu\n", atomic_read(&ialloc_bytes), |
| // conf.ialloc); |
| return NULL; |
| } |
| #endif |
| |
| b = _allocb(size, 0); /* no KMALLOC_WAIT */ |
| if (b == NULL) { |
| //printk("iallocb: no memory %lu/%lu\n", atomic_read(&ialloc_bytes), |
| // conf.ialloc); |
| return NULL; |
| } |
| b->flag = BINTR; |
| |
| atomic_add(&ialloc_bytes, b->lim - b->base); |
| |
| return b; |
| } |
| |
| void freeb(struct block *b) |
| { |
| void *dead = (void *)Bdead; |
| struct extra_bdata *ebd; |
| |
| if (b == NULL) |
| return; |
| |
| /* assuming our release method is kfree, which will change when we support |
| * user buffers */ |
| for (int i = 0; i < b->nr_extra_bufs; i++) { |
| ebd = &b->extra_data[i]; |
| if (ebd->base) |
| kfree((void*)ebd->base); |
| } |
| kfree(b->extra_data); /* harmless if it is 0 */ |
| b->extra_data = 0; /* in case the block is reused by a free override */ |
| /* |
| * 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)); |
| } |
| |
| /* 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(b); |
| } |
| |
| void checkb(struct block *b, char *msg) |
| { |
| void *dead = (void *)Bdead; |
| struct extra_bdata *ebd; |
| |
| if (b == dead) |
| panic("checkb b %s 0x%lx", msg, b); |
| if (b->base == dead || b->lim == dead || b->next == dead |
| || b->rp == dead || b->wp == dead) { |
| printd("checkb: base 0x%8.8lx lim 0x%8.8lx next 0x%8.8lx\n", |
| b->base, b->lim, b->next); |
| printd("checkb: rp 0x%8.8lx wp 0x%8.8lx\n", b->rp, b->wp); |
| panic("checkb dead: %s\n", msg); |
| } |
| |
| if (b->base > b->lim) |
| panic("checkb 0 %s 0x%lx 0x%lx", msg, b->base, b->lim); |
| if (b->rp < b->base) |
| panic("checkb 1 %s 0x%lx 0x%lx", msg, b->base, b->rp); |
| if (b->wp < b->base) |
| panic("checkb 2 %s 0x%lx 0x%lx", msg, b->base, b->wp); |
| if (b->rp > b->lim) |
| panic("checkb 3 %s 0x%lx 0x%lx", msg, b->rp, b->lim); |
| if (b->wp > b->lim) |
| panic("checkb 4 %s 0x%lx 0x%lx", msg, b->wp, b->lim); |
| if (b->nr_extra_bufs && !b->extra_data) |
| panic("checkb 5 %s missing extra_data", msg); |
| |
| for (int i = 0; i < b->nr_extra_bufs; i++) { |
| ebd = &b->extra_data[i]; |
| if (ebd->base) { |
| if (!kmalloc_refcnt((void*)ebd->base)) |
| panic("checkb buf %d, base %p has no refcnt!\n", i, ebd->base); |
| } |
| } |
| |
| } |
| |
| void iallocsummary(void) |
| { |
| printd("ialloc %lu/%lu\n", atomic_read(&ialloc_bytes), 0 /*conf.ialloc */ ); |
| } |
| |
| void printblock(struct block *b) |
| { |
| unsigned char *c; |
| unsigned int off, elen; |
| struct extra_bdata *e; |
| |
| if (b == NULL) { |
| printk("block is null\n"); |
| return; |
| } |
| |
| printk("block of BLEN = %d, with %d header and %d data in %d extras\n", |
| BLEN(b), BHLEN(b), b->extra_len, b->nr_extra_bufs); |
| |
| printk("header:\n"); |
| printk("%2x:\t", 0); |
| off = 0; |
| for (c = b->rp; c < b->wp; c++) { |
| printk(" %02x", *c & 0xff); |
| off++; |
| if (off % 8 == 0) { |
| printk("\n"); |
| printk("%2x:\t", off); |
| } |
| } |
| printk("\n"); |
| elen = b->extra_len; |
| for (int i = 0; (i < b->nr_extra_bufs) && elen; i++) { |
| e = &b->extra_data[i]; |
| if (e->len == 0) |
| continue; |
| elen -= e->len; |
| printk("data %d:\n", i); |
| printk("%2x:\t", 0); |
| for (off = 0; off < e->len; off++) { |
| c = (unsigned char *)e->base + e->off + off; |
| printk(" %02x", *c & 0xff); |
| if ((off + 1) % 8 == 0 && off +1 < e->len) { |
| printk("\n"); |
| printk("%2x:\t", off + 1); |
| } |
| } |
| } |
| printk("\n"); |
| } |