361 lines
8.3 KiB
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");
|
|
}
|
|
}
|
|
}
|