jehanne/sys/src/kern/port/bud.c

361 lines
8.3 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
/*
* - locks
* - could instead coalesce free items on demand (cf. Wulf)
* - or lazy buddy (cf. Barkley)
*/
enum{
MinK= PGSHFT, /* default minimum size (one page) */
Nbits= sizeof(uintmem)*8,
MaxK= Nbits-1, /* last usable k (largest block is 2^k) */
Busy= 0x80, /* bit set in byte map if block busy (low order bits are block size, 0=unavailable) */
};
//#define usize uintmem
typedef struct Blk Blk;
struct Blk{
Blk* forw; /* free list */
Blk* back;
};
typedef struct Bfree Bfree;
struct Bfree{
Blk h; /* header */
Lock;
uint32_t avail;
};
struct Bpool{
Lock lk; /* TO DO: localise lock (need CAS update of pool->kofb) (also see Johnson & Davis 1992) */
Bfree blist[Nbits]; /* increasing powers of two */
uint8_t* kofb; /* k(block_index) with top bit set if busy */
uint32_t mink;
uint32_t maxk;
uint32_t maxb; /* limit to block index, in minbsize blocks (pool size) */
Blk* blocks; /* free list pointers */
uintmem base;
uintmem minbsize;
uintmem limit;
};
static uint8_t lg2table[256] = {
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
};
#define BI(a) ((a)>>pool->mink)
#define IB(x) ((uintmem)(x)<<pool->mink)
static int
lg2ceil(uintmem m)
{
uint32_t n, h;
int r;
r = (m & (m-1)) != 0; /* not a power of two => round up */
n = (uint32_t)m;
if(sizeof(uintmem)>sizeof(uint32_t)){
h = (uint64_t)m>>32;
if(h != 0){
n = h;
r += 32;
}
}
if((n>>8) == 0)
return lg2table[n] + r;
if((n>>16) == 0)
return 8 + lg2table[n>>8] + r;
if((n>>24) == 0)
return 16 + lg2table[n>>16] + r;
return 24 + lg2table[n>>24] + r;
}
Bpool*
bpoolcreate(uint32_t mink, uint32_t maxk, uintmem base, uintmem top, void* (*alloc)(usize, int))
{
int k;
Blk *b;
Bpool *pool;
if(mink == 0)
mink = MinK;
if(maxk > MaxK)
panic("bpoolcreate");
pool = alloc(sizeof(Bpool), 1);
if(pool == nil)
panic("bpoolcreate alloc");
pool->mink = mink;
pool->maxk = maxk;
pool->base = base;
pool->limit = top;
pool->maxb = BI(top-base);
pool->minbsize = (uintmem)1<<mink;
pool->blocks = alloc((pool->maxb+1)*sizeof(*pool->blocks), 0);
if(pool->blocks == nil)
panic("bpoolinit: can't allocate %ud blocks", pool->maxb+1);
for(k = 0; k < nelem(pool->blist); k++){
b = &pool->blist[k].h;
b->forw = b->back = b;
}
pool->kofb = alloc((pool->maxb+1)*sizeof(*pool->kofb), 1);
if(pool->kofb == nil)
panic("physinit: can't allocate %ud kofb", pool->maxb+1);
jehanne_print("pool %#p space base %#P top=%#P maxb=%#ux (%d)\n", pool, base, top, pool->maxb, pool->maxb);
return pool;
}
uintmem
bpoolalloc(Bpool *pool, usize size)
{
int j, k;
Blk *b, *b2;
uintmem a, a2;
uint32_t bi;
k = lg2ceil(size);
if(k < pool->mink)
k = pool->mink;
if(k > pool->maxk)
return 0;
DBG("%#p size=%#P k=%d\n", pool, (uintmem)size, k);
lock(&pool->lk);
for(j = k;;){
b = pool->blist[j].h.forw;
if(b != &pool->blist[j].h)
break;
if(++j > pool->maxk){
unlock(&pool->lk);
return 0; /* out of space */
}
}
if(b == nil)
panic("physalloc: nil");
/* set busy state */
bi = b - pool->blocks;
if(pool->kofb[bi] & Busy || b->forw == nil || b->back == nil)
panic("physalloc: inval k=%d j=%d %#p %d %#ux %#p %#p", k, j, b, bi, pool->kofb[bi], b->forw, b->back);
pool->kofb[bi] = k | Busy;
pool->blist[j].avail--;
b->forw->back = b->back;
b->back->forw = b->forw;
a = IB(bi);
while(j != k){
/* split */
j--;
a2 = a+((uintmem)1<<j);
bi = BI(a2);
DBG("split %#llux %#llux k=%d %#llux pool->kofb=%#ux\n", a, a2, j, (uintmem)1<<j, pool->kofb[bi]);
if(pool->kofb[bi] & Busy){
if(pool->kofb[bi] & ~Busy)
panic("bal: busy block %#llux k=%d\n", a, pool->kofb[bi] & ~Busy);
}
pool->kofb[bi] = j; /* new size, not busy */
b2 = &pool->blocks[bi];
b2->forw = &pool->blist[j].h;
b2->back = pool->blist[j].h.back;
pool->blist[j].h.back = b2;
b2->back->forw = b2;
pool->blist[j].avail++;
}
unlock(&pool->lk);
return a + pool->base;
}
void
bpoolfree(Bpool *pool, uintmem a, usize size)
{
int k;
Blk *b, *b2;
uintmem a2;
uint32_t bi, bi2;
k = lg2ceil(size); /* could look it up in pool->kofb */
if(k < pool->mink)
return;
if(k > pool->maxk)
k = pool->maxk;
DBG("%#p free %#llux %#P k%d\n", pool, a, (uintmem)size, k);
if(a < pool->base)
panic("bpoolfree");
a -= pool->base;
bi = BI(a);
lock(&pool->lk);
if(pool->kofb[bi] != 0 && pool->kofb[bi] != (Busy|k)){
unlock(&pool->lk);
panic("balfree: busy %#llux odd k k=%d kofb=%#ux\n", a, k, pool->kofb[bi]);
}
for(; k != pool->maxk; k++){
pool->kofb[bi] = Busy;
a2 = a ^ ((uintmem)1<<k); /* buddy */
bi2 = BI(a2);
b2 = &pool->blocks[bi2];
if(bi2 >= pool->maxb || pool->kofb[bi2] != k)
break;
/* valid, not busy or empty, size k */
DBG("combine %#llux %#llux %d %#llux\n", a, a2, k, (uintmem)1<<k);
b2->back->forw = b2->forw;
b2->forw->back = b2->back;
pool->kofb[bi2] = Busy;
pool->blist[k].avail--;
if(a2 < a){
a = a2;
bi = bi2;
}
}
pool->kofb[bi] = k; /* sets size and resets Busy */
b = &pool->blocks[bi];
b->forw = &pool->blist[k].h;
b->back = pool->blist[k].h.back;
pool->blist[k].h.back = b;
b->back->forw = b;
pool->blist[k].avail++;
unlock(&pool->lk);
}
void
bpoolallocrange(Bpool *pool, usize *low, usize *high)
{
*low = (usize)1<<pool->mink;
*high = (usize)1<<pool->maxk;
}
static void
ibpoolfree(Bpool *pool, uintmem base, usize size)
{
bpoolfree(pool, base+pool->base, size);
}
void
bpoolinitfree(Bpool *pool, uintmem base, uintmem lim)
{
uintmem m, size;
int i;
/* chop limit to min block alignment */
if(base >= pool->limit)
return;
if(pool->base > base)
base = pool->base;
if(lim > pool->limit)
lim = pool->limit;
base -= pool->base;
lim -= pool->base;
lim &= ~(pool->minbsize-1);
if(BI(lim) > pool->maxb){
jehanne_print("physinitfree: address space too large");
lim = IB(pool->maxb);
}
/* round base to min block alignment */
base = (base + pool->minbsize-1) & ~(pool->minbsize-1);
size = lim - base;
if(size < pool->minbsize)
return;
DBG("bpoolinitfree %#p %#P-%#P [%#P]\n", pool, pool->base+base, pool->base+lim, size);
/* move up from base in largest blocks that remain aligned */
for(i=pool->mink; i<pool->maxk; i++){
m = (uintmem)1 << i;
if(base & m){
if(size < m)
break;
if(base & (m-1)){
jehanne_print(" ** error: %#P %#P\n", base, m);
return;
}
ibpoolfree(pool, base, m);
base += m;
size -= m;
}
}
/* largest chunks, aligned */
m = (uintmem)1<<pool->maxk;
while(size >= m){
if(base & (m-1)){
jehanne_print(" ** error: %#P %#P\n", base, m);
return;
}
ibpoolfree(pool, base, m);
base += m;
size -= m;
}
/* free remaining chunks, decreasing alignment */
for(; size >= pool->minbsize; m >>= 1){
if(size & m){
DBG("\t%#P %#P\n", base, m);
if(base & (m-1)){
jehanne_print(" ** error: %#P %#P\n", base, m);
return;
}
ibpoolfree(pool, base, m);
base += m;
size &= ~m;
}
}
}
char*
seprintbpoolstats(Bpool *pool, char *s, char *e)
{
Bfree *b;
int i;
lock(&pool->lk);
for(i = 0; i < nelem(pool->blist); i++){
b = &pool->blist[i];
if(b->avail != 0)
s = jehanne_seprint(s, e, "%ud %ulldK blocks avail\n",
b->avail, (1ull<<i)/KiB);
}
unlock(&pool->lk);
return s;
}
void
bpooldump(Bpool *pool)
{
uintmem a;
uint32_t bi;
int i, k;
Blk *b;
for(i=0; i<nelem(pool->blist); i++){
b = pool->blist[i].h.forw;
if(b != &pool->blist[i].h){
jehanne_print("%d ", i);
for(; b != &pool->blist[i].h; b = b->forw){
bi = b-pool->blocks;
a = IB(bi);
k = pool->kofb[bi];
jehanne_print(" [%#llux %d %#ux b=%#llux]", a, k, 1<<k, a^((uintmem)1<<k));
}
jehanne_print("\n");
}
}
}