blob: 5dff044b4e01a55a2dcb84b00c6583ca2a3b460a [file] [log] [blame]
#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));
}