blob: 28879fcc1fd0cf6ab2d9646a75e45bd21e6ff36d [file] [log] [blame]
// 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");
}