6 typedef struct Label Label;
13 * the plan is to store data to the cache in c->size blocks
14 * with the block zero extended to fill it out. When writing to
15 * venti, the block will be zero truncated. The walker will also check
16 * that the block fits within psize or dsize as the case may be.
23 u32int now; /* ticks for usage timestamps */
24 int size; /* max. size of any block; allocated to each block */
25 Lump **heads; /* hash table for finding address */
26 int nheap; /* number of available victims */
27 Lump **heap; /* heap for locating victims */
28 long nblocks; /* number of blocks allocated */
29 Lump *blocks; /* array of block descriptors */
30 u8int *mem; /* memory for all block descriptors */
31 Lump *free; /* free list of lumps */
37 * the tag for a block is hash(index, parent tag)
43 uchar type; /* top bit indicates it is part of a directory */
44 uchar tag[4]; /* tag of file it is in */
48 static char ENoDir[] = "directory entry is not allocated";
50 static void fixHeap(int si, Lump *b);
51 static int upHeap(int i, Lump *b);
52 static int downHeap(int i, Lump *b);
53 static char *lumpState(int);
54 static void lumpSetState(Lump *u, int state);
57 cacheAlloc(VtSession *z, int blockSize, long nblocks)
63 c = vtMemAllocZ(sizeof(Cache));
65 c->lk = vtLockAlloc();
69 c->hashSize = nblocks;
70 c->heads = vtMemAllocZ(c->hashSize*sizeof(Lump*));
71 c->heap = vtMemAllocZ(nblocks*sizeof(Lump*));
72 c->blocks = vtMemAllocZ(nblocks*sizeof(Lump));
73 c->mem = vtMemAllocZ(nblocks * blockSize);
74 for(i = 0; i < nblocks; i++){
76 b->lk = vtLockAlloc();
78 b->data = &c->mem[i * blockSize];
91 cacheGetSize(Cache *c)
97 cacheGetBlockSize(Cache *c)
103 cacheSetSize(Cache *c, long nblocks)
115 for(i = 0; i < c->nblocks; i++){
116 assert(c->blocks[i].ref == 0);
117 vtLockFree(c->blocks[i].lk);
120 vtMemFree(c->blocks);
126 hash(Cache *c, uchar score[VtScoreSize], int type)
129 uchar *p = score + VtScoreSize-4;
131 h = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
133 return h % c->hashSize;
137 findLump(Cache *c, Lump *bb)
143 h = hash(c, bb->score, bb->type);
144 for(b = c->heads[h]; b != nil; b = b->next){
146 vtFatal("bad prev link");
151 vtFatal("block missing from hash table");
158 int i, k, refed, free;
159 static uchar zero[VtScoreSize];
166 for(p=c->free; p; p=p->next)
168 for(i = 0; i < c->nheap; i++){
169 if(c->heap[i]->heap != i)
170 vtFatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
171 if(i > 0 && c->heap[(i - 1) >> 1]->used2 - now > c->heap[i]->used2 - now)
172 vtFatal("bad heap ordering");
174 if(k < c->nheap && c->heap[i]->used2 - now > c->heap[k]->used2 - now)
175 vtFatal("bad heap ordering");
177 if(k < c->nheap && c->heap[i]->used2 - now > c->heap[k]->used2 - now)
178 vtFatal("bad heap ordering");
182 for(i = 0; i < c->nblocks; i++){
183 if(c->blocks[i].data != &c->mem[i * size])
184 vtFatal("mis-blocked at %d", i);
185 if(c->blocks[i].ref && c->blocks[i].heap == BadHeap){
188 if(memcmp(zero, c->blocks[i].score, VtScoreSize))
189 findLump(c, &c->blocks[i]);
191 if(refed > 0)fprint(2, "cacheCheck: nheap %d refed %d free %d\n", c->nheap, refed, free);
192 assert(c->nheap + refed + free == c->nblocks);
194 for(i = 0; i < c->nblocks; i++){
195 if(c->blocks[i].ref) {
196 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));
200 if(refed > 0)fprint(2, "cacheCheck: in used %d\n", refed);
204 * delete an arbitrary block from the heap
209 fixHeap(db->heap, db->c->heap[--db->c->nheap]);
214 fixHeap(int si, Lump *b)
224 upHeap(int i, Lump *b)
233 for(; i != 0; i = p){
236 if(b->used2 - now >= bb->used2 - now)
248 downHeap(int i, Lump *b)
261 if(k + 1 < c->nheap && c->heap[k]->used2 - now > c->heap[k + 1]->used2 - now)
264 if(b->used2 - now <= bb->used2 - now)
275 /* called with c->lk held */
277 cacheBumpLump(Cache *c)
282 * missed: locate the block with the oldest second to last use.
283 * remove it from the heap, and fix up the heap.
302 * unchain the block from hash chain
305 c->heads[hash(c, b->score, b->type)] = b->next;
307 b->prev->next = b->next;
309 b->next->prev = b->prev;
314 * the new block has no last use, so assume it happens sometime in the middle
316 b->used = (b->used2 + c->now) / 2;
323 cacheAllocLump(Cache *c, int type, int size, int dir)
328 assert(size <= c->size);
332 b = cacheBumpLump(c);
335 fprint(2, "cache is full\n");
336 /* XXX should be better */
348 /* convert addr into score */
349 memset(b->score, 0, VtScoreSize-4);
350 b->score[VtScoreSize-4] = b->addr>>24;
351 b->score[VtScoreSize-3] = b->addr>>16;
352 b->score[VtScoreSize-2] = b->addr>>8;
353 b->score[VtScoreSize-1] = b->addr;
361 h = hash(c, b->score, b->type);
363 /* chain onto correct hash */
364 b->next = c->heads[h];
372 vtZeroExtend(type, b->data, 0, size);
373 lumpSetState(b, LumpActive);
379 scoreIsLocal(uchar score[VtScoreSize])
381 static uchar zero[VtScoreSize];
383 return memcmp(score, zero, VtScoreSize-4) == 0;
387 cacheGetLump(Cache *c, uchar score[VtScoreSize], int type, int size)
392 static uchar zero[VtScoreSize];
394 assert(size <= c->size);
396 h = hash(c, score, type);
400 * look for the block in the cache
403 for(b = c->heads[h]; b != nil; b = b->next){
404 if(memcmp(b->score, score, VtScoreSize) == 0 && b->type == type)
408 /* should not be looking for a temp block */
409 if(scoreIsLocal(score)) {
410 if(memcmp(score, zero, VtScoreSize) == 0)
411 vtSetError("looking for zero score");
413 vtSetError("missing local block");
418 b = cacheBumpLump(c);
425 /* chain onto correct hash */
426 b->next = c->heads[h];
432 memmove(b->score, score, VtScoreSize);
440 if(b->heap != BadHeap)
446 if(b->state != LumpFree)
449 n = vtRead(c->z, score, type, b->data, size);
454 if(!vtSha1Check(score, b->data, n)) {
455 vtSetError("vtSha1Check failed");
459 vtZeroExtend(type, b->data, n, size);
461 lumpSetState(b, LumpVenti);
486 lumpSetState(Lump *u, int state)
488 // if(u->state != LumpFree)
489 // fprint(2, "%V: %s -> %s\n", u->score, lumpState(u->state), lumpState(state));
494 lumpGetScore(Lump *u, int offset, uchar score[VtScoreSize])
504 vtSetError("bad type");
513 if((offset+1)*VtScoreSize > u->asize)
516 sp = u->data + offset*VtScoreSize;
519 if(u->asize < VtRootSize) {
520 vtSetError("runt root block");
523 if(!vtRootUnpack(&root, u->data))
528 if((offset+1)*VtEntrySize > u->asize) {
532 if(!vtEntryUnpack(&dir, u->data, offset))
534 if(!dir.flags & VtEntryActive) {
543 memmove(score, vtZeroScore, VtScoreSize);
545 memmove(score, sp, VtScoreSize);
548 return !scoreIsLocal(score);
555 lumpWalk(Lump *u, int offset, int type, int size, int readOnly, int lock)
559 uchar score[VtScoreSize], *sp;
574 vtSetError("bad type");
583 if((offset+1)*VtScoreSize > u->asize)
586 sp = u->data + offset*VtScoreSize;
589 if(u->asize < VtRootSize) {
590 vtSetError("runt root block");
593 if(!vtRootUnpack(&root, u->data))
598 if((offset+1)*VtEntrySize > u->asize) {
602 if(!vtEntryUnpack(&dir, u->data, offset))
604 if(!(dir.flags & VtEntryActive)) {
608 isdir = (dir.flags & VtEntryDir) != 0;
610 sp = u->data + offset*VtEntrySize + 20;
615 memmove(score, vtZeroScore, VtScoreSize);
617 memmove(score, sp, VtScoreSize);
622 if(0)fprint(2, "lumpWalk: %V:%s %d:%d-> %V:%d\n", u->score, lumpState(u->state), u->type, offset, score, type);
623 v = cacheGetLump(c, score, type, size);
635 fprint(2, "block is free %V!\n", v->score);
636 vtSetError("phase error");
639 if(v->gen < u->gen) {
640 print("LumpActive gen\n");
641 lumpSetState(v, LumpSnap);
659 vtSetError("bad offset");
663 vv = cacheAllocLump(c, v->type, size, isdir);
666 memmove(vv->data, v->data, v->asize);
667 if(0)fprint(2, "split %V into %V\n", v->score, vv->score);
673 if(u->state != LumpActive) {
674 vtSetError("bad parent state: can not happen");
678 /* check that nothing changed underfoot */
679 if(memcmp(sp, score, VtScoreSize) != 0) {
681 fprint(2, "lumpWalk: parent changed under foot\n");
685 /* XXX - hold Active blocks up - will go eventually */
688 /* change the parent */
689 memmove(sp, vv->score, VtScoreSize);
708 lumpFreeEntry(Lump *u, int entry)
710 uchar score[VtScoreSize];
718 if(u->state == LumpVenti)
723 fprint(2, "freeing bad lump type: %d\n", u->type);
726 if((entry+1)*VtScoreSize > u->asize)
728 memmove(score, u->data + entry*VtScoreSize, VtScoreSize);
729 memmove(u->data + entry*VtScoreSize, vtZeroScore, VtScoreSize);
730 type = u->dir?VtDirType:VtDataType;
738 if((entry+1)*VtScoreSize > u->asize)
740 memmove(score, u->data + entry*VtScoreSize, VtScoreSize);
741 memmove(u->data + entry*VtScoreSize, vtZeroScore, VtScoreSize);
745 if((entry+1)*VtEntrySize > u->asize)
747 if(!vtEntryUnpack(&dir, u->data, entry))
749 if(!dir.flags & VtEntryActive)
755 type = (dir.flags&VtEntryDir)?VtDirType:VtDataType;
757 type = VtPointerType0 + dir.depth - 1;
758 memmove(score, dir.score, VtScoreSize);
759 memset(&dir, 0, sizeof(dir));
761 vtEntryPack(&dir, u->data, entry);
768 if(type == VtErrType || !scoreIsLocal(score))
771 u = cacheGetLump(c, score, type, c->size);
775 /* XXX remove extra reference */
799 n = u->asize/VtScoreSize;
802 n = u->asize/VtEntrySize;
812 lumpDecRef(Lump *b, int unlock)
833 fprint(2, "bad state: %s\n", lumpState(b->state));
836 /* hack - but will do for now */
842 lumpSetState(b, LumpFree);
845 lumpSetState(b, LumpFree);
853 * reinsert in the free heap
855 if(b->heap == BadHeap) {
856 i = upHeap(c->nheap++, b);