#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)<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<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<kofb=%#ux\n", a, a2, j, (uintmem)1<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<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<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<mink; *high = (usize)1<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; imaxk; 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<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<lk); return s; } void bpooldump(Bpool *pool) { uintmem a; uint32_t bi; int i, k; Blk *b; for(i=0; iblist); 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<