diff options
author | rsc <devnull@localhost> | 2003-11-23 17:55:34 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2003-11-23 17:55:34 +0000 |
commit | 7763a61a3582ef330bca54f225e8ec5325fbd35e (patch) | |
tree | 952957eef4d70ecbd30c58e3a0dacd6b3a753a54 /src/cmd/vac/cache.c | |
parent | 7a4ee46d253e291044bba2d0c54b818b67ac013c (diff) | |
download | plan9port-7763a61a3582ef330bca54f225e8ec5325fbd35e.tar.gz plan9port-7763a61a3582ef330bca54f225e8ec5325fbd35e.tar.bz2 plan9port-7763a61a3582ef330bca54f225e8ec5325fbd35e.zip |
start thinking about vac -- doesn't build yet
Diffstat (limited to 'src/cmd/vac/cache.c')
-rw-r--r-- | src/cmd/vac/cache.c | 876 |
1 files changed, 876 insertions, 0 deletions
diff --git a/src/cmd/vac/cache.c b/src/cmd/vac/cache.c new file mode 100644 index 00000000..fc34d688 --- /dev/null +++ b/src/cmd/vac/cache.c @@ -0,0 +1,876 @@ +#include "stdinc.h" +#include "vac.h" +#include "dat.h" +#include "fns.h" + +typedef struct Label Label; + +enum { + BadHeap = ~0, +}; + +/* + * the plan is to store data to the cache in c->size blocks + * with the block zero extended to fill it out. When writing to + * venti, the block will be zero truncated. The walker will also check + * that the block fits within psize or dsize as the case may be. + */ + +struct Cache +{ + VtLock *lk; + VtSession *z; + u32int now; /* ticks for usage timestamps */ + int size; /* max. size of any block; allocated to each block */ + Lump **heads; /* hash table for finding address */ + int nheap; /* number of available victims */ + Lump **heap; /* heap for locating victims */ + long nblocks; /* number of blocks allocated */ + Lump *blocks; /* array of block descriptors */ + u8int *mem; /* memory for all block descriptors */ + Lump *free; /* free list of lumps */ + + long hashSize; +}; + +/* + * the tag for a block is hash(index, parent tag) + */ + +struct Label { + uchar gen[4]; + uchar state; + uchar type; /* top bit indicates it is part of a directory */ + uchar tag[4]; /* tag of file it is in */ +}; + + +static char ENoDir[] = "directory entry is not allocated"; + +static void fixHeap(int si, Lump *b); +static int upHeap(int i, Lump *b); +static int downHeap(int i, Lump *b); +static char *lumpState(int); +static void lumpSetState(Lump *u, int state); + +Cache * +cacheAlloc(VtSession *z, int blockSize, long nblocks) +{ + int i; + Cache *c; + Lump *b; + + c = vtMemAllocZ(sizeof(Cache)); + + c->lk = vtLockAlloc(); + c->z = z; + c->size = blockSize; + c->nblocks = nblocks; + c->hashSize = nblocks; + c->heads = vtMemAllocZ(c->hashSize*sizeof(Lump*)); + c->heap = vtMemAllocZ(nblocks*sizeof(Lump*)); + c->blocks = vtMemAllocZ(nblocks*sizeof(Lump)); + c->mem = vtMemAllocZ(nblocks * blockSize); + for(i = 0; i < nblocks; i++){ + b = &c->blocks[i]; + b->lk = vtLockAlloc(); + b->c = c; + b->data = &c->mem[i * blockSize]; + b->addr = i+1; + b->state = LumpFree; + b->heap = BadHeap; + b->next = c->free; + c->free = b; + } + c->nheap = 0; + + return c; +} + +long +cacheGetSize(Cache *c) +{ + return c->nblocks; +} + +int +cacheGetBlockSize(Cache *c) +{ + return c->size; +} + +int +cacheSetSize(Cache *c, long nblocks) +{ + USED(c); + USED(nblocks); + return 0; +} + +void +cacheFree(Cache *c) +{ + int i; + + for(i = 0; i < c->nblocks; i++){ + assert(c->blocks[i].ref == 0); + vtLockFree(c->blocks[i].lk); + } + vtMemFree(c->heads); + vtMemFree(c->blocks); + vtMemFree(c->mem); + vtMemFree(c); +} + +static u32int +hash(Cache *c, uchar score[VtScoreSize], int type) +{ + u32int h; + uchar *p = score + VtScoreSize-4; + + h = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + h += type; + return h % c->hashSize; +} + +static void +findLump(Cache *c, Lump *bb) +{ + Lump *b, *last; + int h; + + last = nil; + h = hash(c, bb->score, bb->type); + for(b = c->heads[h]; b != nil; b = b->next){ + if(last != b->prev) + vtFatal("bad prev link"); + if(b == bb) + return; + last = b; + } + vtFatal("block missing from hash table"); +} + +void +cacheCheck(Cache *c) +{ + u32int size, now; + int i, k, refed, free; + static uchar zero[VtScoreSize]; + Lump *p; + + size = c->size; + now = c->now; + + free = 0; + for(p=c->free; p; p=p->next) + free++; + for(i = 0; i < c->nheap; i++){ + if(c->heap[i]->heap != i) + vtFatal("mis-heaped at %d: %d", i, c->heap[i]->heap); + if(i > 0 && c->heap[(i - 1) >> 1]->used2 - now > c->heap[i]->used2 - now) + vtFatal("bad heap ordering"); + k = (i << 1) + 1; + if(k < c->nheap && c->heap[i]->used2 - now > c->heap[k]->used2 - now) + vtFatal("bad heap ordering"); + k++; + if(k < c->nheap && c->heap[i]->used2 - now > c->heap[k]->used2 - now) + vtFatal("bad heap ordering"); + } + + refed = 0; + for(i = 0; i < c->nblocks; i++){ + if(c->blocks[i].data != &c->mem[i * size]) + vtFatal("mis-blocked at %d", i); + if(c->blocks[i].ref && c->blocks[i].heap == BadHeap){ + refed++; + } + if(memcmp(zero, c->blocks[i].score, VtScoreSize)) + findLump(c, &c->blocks[i]); + } +if(refed > 0)fprint(2, "cacheCheck: nheap %d refed %d free %d\n", c->nheap, refed, free); + assert(c->nheap + refed + free == c->nblocks); + refed = 0; + for(i = 0; i < c->nblocks; i++){ + if(c->blocks[i].ref) { +if(1)fprint(2, "%d %V %d %s\n", c->blocks[i].type, c->blocks[i].score, c->blocks[i].ref, lumpState(c->blocks[i].state)); + refed++; + } + } +if(refed > 0)fprint(2, "cacheCheck: in used %d\n", refed); +} + +/* + * delete an arbitrary block from the heap + */ +static void +delHeap(Lump *db) +{ + fixHeap(db->heap, db->c->heap[--db->c->nheap]); + db->heap = BadHeap; +} + +static void +fixHeap(int si, Lump *b) +{ + int i; + + i = upHeap(si, b); + if(i == si) + downHeap(i, b); +} + +static int +upHeap(int i, Lump *b) +{ + Lump *bb; + u32int now; + int p; + Cache *c; + + c = b->c; + now = c->now; + for(; i != 0; i = p){ + p = (i - 1) >> 1; + bb = c->heap[p]; + if(b->used2 - now >= bb->used2 - now) + break; + c->heap[i] = bb; + bb->heap = i; + } + c->heap[i] = b; + b->heap = i; + + return i; +} + +static int +downHeap(int i, Lump *b) +{ + Lump *bb; + u32int now; + int k; + Cache *c; + + c = b->c; + now = c->now; + for(; ; i = k){ + k = (i << 1) + 1; + if(k >= c->nheap) + break; + if(k + 1 < c->nheap && c->heap[k]->used2 - now > c->heap[k + 1]->used2 - now) + k++; + bb = c->heap[k]; + if(b->used2 - now <= bb->used2 - now) + break; + c->heap[i] = bb; + bb->heap = i; + } + c->heap[i] = b; + b->heap = i; + return i; +} + + +/* called with c->lk held */ +Lump * +cacheBumpLump(Cache *c) +{ + Lump *b; + + /* + * missed: locate the block with the oldest second to last use. + * remove it from the heap, and fix up the heap. + */ + if(c->free) { + b = c->free; + c->free = b->next; + } else { + for(;;){ + if(c->nheap == 0) { + cacheCheck(c); + assert(0); + return nil; + } + b = c->heap[0]; + delHeap(b); + if(b->ref == 0) + break; + } + + /* + * unchain the block from hash chain + */ + if(b->prev == nil) + c->heads[hash(c, b->score, b->type)] = b->next; + else + b->prev->next = b->next; + if(b->next != nil) + b->next->prev = b->prev; + + } + + /* + * the new block has no last use, so assume it happens sometime in the middle + */ + b->used = (b->used2 + c->now) / 2; + b->asize = 0; + + return b; +} + +Lump * +cacheAllocLump(Cache *c, int type, int size, int dir) +{ + Lump *b; + ulong h; + + assert(size <= c->size); + +again: + vtLock(c->lk); + b = cacheBumpLump(c); + if(b == nil) { + vtUnlock(c->lk); +fprint(2, "cache is full\n"); + /* XXX should be better */ + sleep(100); + goto again; + } + + vtLock(b->lk); + + assert(b->ref == 0); + b->ref++; + b->used2 = b->used; + b->used = c->now++; + + /* convert addr into score */ + memset(b->score, 0, VtScoreSize-4); + b->score[VtScoreSize-4] = b->addr>>24; + b->score[VtScoreSize-3] = b->addr>>16; + b->score[VtScoreSize-2] = b->addr>>8; + b->score[VtScoreSize-1] = b->addr; + + b->dir = dir; + b->type = type; + b->gen = 0; + b->asize = size; + b->state = LumpFree; + + h = hash(c, b->score, b->type); + + /* chain onto correct hash */ + b->next = c->heads[h]; + c->heads[h] = b; + if(b->next != nil) + b->next->prev = b; + b->prev = nil; + + vtUnlock(c->lk); + + vtZeroExtend(type, b->data, 0, size); + lumpSetState(b, LumpActive); + + return b; +} + +int +scoreIsLocal(uchar score[VtScoreSize]) +{ + static uchar zero[VtScoreSize]; + + return memcmp(score, zero, VtScoreSize-4) == 0; +} + +Lump * +cacheGetLump(Cache *c, uchar score[VtScoreSize], int type, int size) +{ + Lump *b; + ulong h; + int n; + static uchar zero[VtScoreSize]; + + assert(size <= c->size); + + h = hash(c, score, type); + +again: + /* + * look for the block in the cache + */ + vtLock(c->lk); + for(b = c->heads[h]; b != nil; b = b->next){ + if(memcmp(b->score, score, VtScoreSize) == 0 && b->type == type) + goto found; + } + + /* should not be looking for a temp block */ + if(scoreIsLocal(score)) { + if(memcmp(score, zero, VtScoreSize) == 0) + vtSetError("looking for zero score"); + else + vtSetError("missing local block"); + vtUnlock(c->lk); + return nil; + } + + b = cacheBumpLump(c); + if(b == nil) { + vtUnlock(c->lk); + sleep(100); + goto again; + } + + /* chain onto correct hash */ + b->next = c->heads[h]; + c->heads[h] = b; + if(b->next != nil) + b->next->prev = b; + b->prev = nil; + + memmove(b->score, score, VtScoreSize); + b->type = type; + b->state = LumpFree; + +found: + b->ref++; + b->used2 = b->used; + b->used = c->now++; + if(b->heap != BadHeap) + fixHeap(b->heap, b); + + vtUnlock(c->lk); + + vtLock(b->lk); + if(b->state != LumpFree) + return b; + + n = vtRead(c->z, score, type, b->data, size); + if(n < 0) { + lumpDecRef(b, 1); + return nil; + } + if(!vtSha1Check(score, b->data, n)) { + vtSetError("vtSha1Check failed"); + lumpDecRef(b, 1); + return nil; + } + vtZeroExtend(type, b->data, n, size); + b->asize = size; + lumpSetState(b, LumpVenti); + + return b; +} + +static char * +lumpState(int state) +{ + switch(state) { + default: + return "Unknown!!"; + case LumpFree: + return "Free"; + case LumpActive: + return "Active"; + case LumpSnap: + return "Snap"; + case LumpZombie: + return "Zombie"; + case LumpVenti: + return "Venti"; + } +} + +static void +lumpSetState(Lump *u, int state) +{ +// if(u->state != LumpFree) +// fprint(2, "%V: %s -> %s\n", u->score, lumpState(u->state), lumpState(state)); + u->state = state; +} + +int +lumpGetScore(Lump *u, int offset, uchar score[VtScoreSize]) +{ + uchar *sp; + VtRoot root; + VtEntry dir; + + vtLock(u->lk); + + switch(u->type) { + default: + vtSetError("bad type"); + goto Err; + case VtPointerType0: + case VtPointerType1: + case VtPointerType2: + case VtPointerType3: + case VtPointerType4: + case VtPointerType5: + case VtPointerType6: + if((offset+1)*VtScoreSize > u->asize) + sp = nil; + else + sp = u->data + offset*VtScoreSize; + break; + case VtRootType: + if(u->asize < VtRootSize) { + vtSetError("runt root block"); + goto Err; + } + if(!vtRootUnpack(&root, u->data)) + goto Err; + sp = root.score; + break; + case VtDirType: + if((offset+1)*VtEntrySize > u->asize) { + vtSetError(ENoDir); + goto Err; + } + if(!vtEntryUnpack(&dir, u->data, offset)) + goto Err; + if(!dir.flags & VtEntryActive) { + vtSetError(ENoDir); + goto Err; + } + sp = dir.score; + break; + } + + if(sp == nil) + memmove(score, vtZeroScore, VtScoreSize); + else + memmove(score, sp, VtScoreSize); + + vtUnlock(u->lk); + return !scoreIsLocal(score); +Err: + vtUnlock(u->lk); + return 0; +} + +Lump * +lumpWalk(Lump *u, int offset, int type, int size, int readOnly, int lock) +{ + Lump *v, *vv; + Cache *c; + uchar score[VtScoreSize], *sp; + VtRoot root; + VtEntry dir; + int split, isdir; + + c = u->c; + vtLock(u->lk); + +Again: + v = nil; + vv = nil; + + isdir = u->dir; + switch(u->type) { + default: + vtSetError("bad type"); + goto Err; + case VtPointerType0: + case VtPointerType1: + case VtPointerType2: + case VtPointerType3: + case VtPointerType4: + case VtPointerType5: + case VtPointerType6: + if((offset+1)*VtScoreSize > u->asize) + sp = nil; + else + sp = u->data + offset*VtScoreSize; + break; + case VtRootType: + if(u->asize < VtRootSize) { + vtSetError("runt root block"); + goto Err; + } + if(!vtRootUnpack(&root, u->data)) + goto Err; + sp = root.score; + break; + case VtDirType: + if((offset+1)*VtEntrySize > u->asize) { + vtSetError(ENoDir); + goto Err; + } + if(!vtEntryUnpack(&dir, u->data, offset)) + goto Err; + if(!(dir.flags & VtEntryActive)) { + vtSetError(ENoDir); + goto Err; + } + isdir = (dir.flags & VtEntryDir) != 0; +// sp = dir.score; + sp = u->data + offset*VtEntrySize + 20; + break; + } + + if(sp == nil) + memmove(score, vtZeroScore, VtScoreSize); + else + memmove(score, sp, VtScoreSize); + + vtUnlock(u->lk); + + +if(0)fprint(2, "lumpWalk: %V:%s %d:%d-> %V:%d\n", u->score, lumpState(u->state), u->type, offset, score, type); + v = cacheGetLump(c, score, type, size); + if(v == nil) + return nil; + + split = 1; + if(readOnly) + split = 0; + + switch(v->state) { + default: + assert(0); + case LumpFree: +fprint(2, "block is free %V!\n", v->score); + vtSetError("phase error"); + goto Err2; + case LumpActive: + if(v->gen < u->gen) { +print("LumpActive gen\n"); + lumpSetState(v, LumpSnap); + v->gen = u->gen; + } else + split = 0; + break; + case LumpSnap: + case LumpVenti: + break; + } + + /* easy case */ + if(!split) { + if(!lock) + vtUnlock(v->lk); + return v; + } + + if(sp == nil) { + vtSetError("bad offset"); + goto Err2; + } + + vv = cacheAllocLump(c, v->type, size, isdir); + /* vv is locked */ + vv->gen = u->gen; + memmove(vv->data, v->data, v->asize); +if(0)fprint(2, "split %V into %V\n", v->score, vv->score); + + lumpDecRef(v, 1); + v = nil; + + vtLock(u->lk); + if(u->state != LumpActive) { + vtSetError("bad parent state: can not happen"); + goto Err; + } + + /* check that nothing changed underfoot */ + if(memcmp(sp, score, VtScoreSize) != 0) { + lumpDecRef(vv, 1); +fprint(2, "lumpWalk: parent changed under foot\n"); + goto Again; + } + + /* XXX - hold Active blocks up - will go eventually */ + lumpIncRef(vv); + + /* change the parent */ + memmove(sp, vv->score, VtScoreSize); + + vtUnlock(u->lk); + + if(!lock) + vtUnlock(vv->lk); + return vv; +Err: + vtUnlock(u->lk); + lumpDecRef(v, 0); + lumpDecRef(vv, 1); + return nil; +Err2: + lumpDecRef(v, 1); + return nil; + +} + +void +lumpFreeEntry(Lump *u, int entry) +{ + uchar score[VtScoreSize]; + int type; + ulong gen; + VtEntry dir; + Cache *c; + + c = u->c; + vtLock(u->lk); + if(u->state == LumpVenti) + goto Exit; + + switch(u->type) { + default: + fprint(2, "freeing bad lump type: %d\n", u->type); + return; + case VtPointerType0: + if((entry+1)*VtScoreSize > u->asize) + goto Exit; + memmove(score, u->data + entry*VtScoreSize, VtScoreSize); + memmove(u->data + entry*VtScoreSize, vtZeroScore, VtScoreSize); + type = u->dir?VtDirType:VtDataType; + break; + case VtPointerType1: + case VtPointerType2: + case VtPointerType3: + case VtPointerType4: + case VtPointerType5: + case VtPointerType6: + if((entry+1)*VtScoreSize > u->asize) + goto Exit; + memmove(score, u->data + entry*VtScoreSize, VtScoreSize); + memmove(u->data + entry*VtScoreSize, vtZeroScore, VtScoreSize); + type = u->type-1; + break; + case VtDirType: + if((entry+1)*VtEntrySize > u->asize) + goto Exit; + if(!vtEntryUnpack(&dir, u->data, entry)) + goto Exit; + if(!dir.flags & VtEntryActive) + goto Exit; + gen = dir.gen; + if(gen != ~0) + gen++; + if(dir.depth == 0) + type = (dir.flags&VtEntryDir)?VtDirType:VtDataType; + else + type = VtPointerType0 + dir.depth - 1; + memmove(score, dir.score, VtScoreSize); + memset(&dir, 0, sizeof(dir)); + dir.gen = gen; + vtEntryPack(&dir, u->data, entry); + break; + case VtDataType: + type = VtErrType; + break; + } + vtUnlock(u->lk); + if(type == VtErrType || !scoreIsLocal(score)) + return; + + u = cacheGetLump(c, score, type, c->size); + if(u == nil) + return; + lumpDecRef(u, 1); + /* XXX remove extra reference */ + lumpDecRef(u, 0); + return; +Exit: + vtUnlock(u->lk); + return; + +} + +void +lumpCleanup(Lump *u) +{ + int i, n; + + switch(u->type) { + default: + return; + case VtPointerType0: + case VtPointerType1: + case VtPointerType2: + case VtPointerType3: + case VtPointerType4: + case VtPointerType5: + case VtPointerType6: + n = u->asize/VtScoreSize; + break; + case VtDirType: + n = u->asize/VtEntrySize; + break; + } + + for(i=0; i<n; i++) + lumpFreeEntry(u, i); +} + + +void +lumpDecRef(Lump *b, int unlock) +{ + int i; + Cache *c; + + if(b == nil) + return; + + if(unlock) + vtUnlock(b->lk); + + c = b->c; + vtLock(c->lk); + if(--b->ref > 0) { + vtUnlock(c->lk); + return; + } + assert(b->ref == 0); + + switch(b->state) { + default: + fprint(2, "bad state: %s\n", lumpState(b->state)); + assert(0); + case LumpActive: + /* hack - but will do for now */ + b->ref++; + vtUnlock(c->lk); + lumpCleanup(b); + vtLock(c->lk); + b->ref--; + lumpSetState(b, LumpFree); + break; + case LumpZombie: + lumpSetState(b, LumpFree); + break; + case LumpFree: + case LumpVenti: + break; + } + + /* + * reinsert in the free heap + */ + if(b->heap == BadHeap) { + i = upHeap(c->nheap++, b); + c->heap[i] = b; + b->heap = i; + } + + vtUnlock(c->lk); +} + +Lump * +lumpIncRef(Lump *b) +{ + Cache *c; + + c = b->c; + + vtLock(c->lk); + assert(b->ref > 0); + b->ref++; + vtUnlock(c->lk); + return b; +} |