2 * Memory-only VtBlock cache.
4 * The cached Venti blocks are in the hash chains.
5 * The cached local blocks are only in the blocks array.
6 * The free blocks are in the heap, which is supposed to
7 * be indexed by second-to-last use but actually
8 * appears to be last use.
36 u32int now; /* ticks for usage time stamps */
37 VtBlock **hash; /* hash table for finding addresses */
39 VtBlock **heap; /* heap for finding victims */
41 VtBlock *block; /* all allocated blocks */
43 uchar *mem; /* memory for all blocks and data */
44 int (*write)(VtConn*, uchar[VtScoreSize], uint, uchar*, int);
47 static void cachecheck(VtCache*);
50 vtcachealloc(VtConn *z, int blocksize, ulong nblock)
57 c = vtmallocz(sizeof(VtCache));
60 c->blocksize = (blocksize + 127) & ~127;
63 c->hash = vtmallocz(nblock*sizeof(VtBlock*));
64 c->heap = vtmallocz(nblock*sizeof(VtBlock*));
65 c->block = vtmallocz(nblock*sizeof(VtBlock));
66 c->mem = vtmallocz(nblock*c->blocksize);
70 for(i=0; i<nblock; i++){
85 * BUG This is here so that vbackup can override it and do some
86 * pipelining of writes. Arguably vtwrite or vtwritepacket or the
87 * cache itself should be providing this functionality.
90 vtcachesetwrite(VtCache *c, int (*write)(VtConn*, uchar[VtScoreSize], uint, uchar*, int))
98 vtcachefree(VtCache *c)
105 for(i=0; i<c->nblock; i++)
106 assert(c->block[i].ref == 0);
116 vtcachedump(VtCache *c)
121 for(i=0; i<c->nblock; i++){
123 print("cache block %d: type %d score %V iostate %d addr %d ref %d nlock %d\n",
124 i, b->type, b->score, b->iostate, b->addr, b->ref, b->nlock);
129 cachecheck(VtCache *c)
138 for(i = 0; i < c->nheap; i++){
139 if(c->heap[i]->heap != i)
140 sysfatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
141 if(i > 0 && c->heap[(i - 1) >> 1]->used - now > c->heap[i]->used - now)
142 sysfatal("bad heap ordering");
144 if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
145 sysfatal("bad heap ordering");
147 if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
148 sysfatal("bad heap ordering");
152 for(i = 0; i < c->nblock; i++){
154 if(b->data != &c->mem[i * size])
155 sysfatal("mis-blocked at %d", i);
156 if(b->ref && b->heap == BadHeap)
158 else if(b->addr != NilBlock)
161 if(c->nheap + refed != c->nblock){
162 fprint(2, "cachecheck: nheap %d refed %d nblocks %d\n", c->nheap, refed, c->nblock);
165 assert(c->nheap + refed == c->nblock);
167 for(i = 0; i < c->nblock; i++){
170 if(1)fprint(2, "a=%ud %V ref=%d\n", b->addr, b->score, b->ref);
174 if(refed > 0)fprint(2, "cachecheck: in used %d\n", refed);
178 upheap(int i, VtBlock *b)
187 for(; i != 0; i = p){
190 if(b->used - now >= bb->used - now)
202 downheap(int i, VtBlock *b)
215 if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now)
218 if(b->used - now <= bb->used - now)
229 * Delete a block from the heap.
230 * Called with c->lk held.
247 b = c->heap[c->nheap];
254 * Insert a block into the heap.
255 * Called with c->lk held.
260 assert(b->heap == BadHeap);
261 upheap(b->c->nheap++, b);
265 * locate the vtBlock with the oldest second to last use.
266 * remove it from the heap, and fix up the heap.
268 /* called with c->lk held */
270 vtcachebumpblock(VtCache *c)
275 * locate the vtBlock with the oldest second to last use.
276 * remove it from the heap, and fix up the heap.
280 fprint(2, "vtcachebumpblock: no free blocks in vtCache");
286 assert(b->heap == BadHeap);
290 * unchain the vtBlock from hash chain if any
293 *(b->prev) = b->next;
295 b->next->prev = b->prev;
300 if(0)fprint(2, "droping %x:%V\n", b->addr, b->score);
301 /* set vtBlock to a reasonable state */
303 b->iostate = BioEmpty;
308 * fetch a local block from the memory cache.
309 * if it's not there, load it, bumping some other Block.
310 * if we're out of free blocks, we're screwed.
313 vtcachelocal(VtCache *c, u32int addr, int type)
318 sysfatal("vtcachelocal: asked for nonexistent block 0");
320 sysfatal("vtcachelocal: asked for block #%ud; only %d blocks",
323 b = &c->block[addr-1];
324 if(b->addr == NilBlock || b->iostate != BioLocal)
325 sysfatal("vtcachelocal: block is not local");
328 sysfatal("vtcachelocal: block has wrong type %d != %d", b->type, type);
340 vtcacheallocblock(VtCache *c, int type)
345 b = vtcachebumpblock(c);
346 b->iostate = BioLocal;
348 b->addr = (b - c->block)+1;
349 vtzeroextend(type, b->data, 0, c->blocksize);
350 vtlocaltoglobal(b->addr, b->score);
360 * fetch a global (Venti) block from the memory cache.
361 * if it's not there, load it, bumping some other block.
364 vtcacheglobal(VtCache *c, uchar score[VtScoreSize], int type)
372 fprint(2, "vtcacheglobal %V %d from %p\n", score, type, getcallerpc(&c));
373 addr = vtglobaltolocal(score);
374 if(addr != NilBlock){
376 fprint(2, "vtcacheglobal %V %d => local\n", score, type);
377 return vtcachelocal(c, addr, type);
380 h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
383 * look for the block in the cache
386 for(b = c->hash[h]; b != nil; b = b->next){
387 if(b->addr != NilBlock || memcmp(b->score, score, VtScoreSize) != 0 || b->type != type)
393 fprint(2, "vtcacheglobal %V %d => found in cache %p; locking\n", score, type, b);
396 if(b->iostate == BioVentiError){
398 fprint(2, "cached read error for %V\n", score);
400 fprint(2, "vtcacheglobal %V %d => cache read error\n", score, type);
402 werrstr("venti i/o error");
406 fprint(2, "vtcacheglobal %V %d => found in cache; returning\n", score, type);
413 b = vtcachebumpblock(c);
416 memmove(b->score, score, VtScoreSize);
417 /* chain onto correct hash */
418 b->next = c->hash[h];
421 b->next->prev = &b->next;
422 b->prev = &c->hash[h];
425 * Lock b before unlocking c, so that others wait while we read.
427 * You might think there is a race between this qlock(b) before qunlock(c)
428 * and the qlock(c) while holding a qlock(b) in vtblockwrite. However,
429 * the block here can never be the block in a vtblockwrite, so we're safe.
430 * We're certainly living on the edge.
433 fprint(2, "vtcacheglobal %V %d => bumped; locking %p\n", score, type, b);
439 n = vtread(c->z, score, type, b->data, c->blocksize);
442 fprint(2, "read %V: %r\n", score);
444 fprint(2, "vtcacheglobal %V %d => bumped; read error\n", score, type);
445 b->iostate = BioVentiError;
449 vtzeroextend(type, b->data, n, c->blocksize);
450 b->iostate = BioVenti;
453 fprint(2, "vtcacheglobal %V %d => loaded into cache; returning\n", score, type);
458 * The thread that has locked b may refer to it by
459 * multiple names. Nlock counts the number of
460 * references the locking thread holds. It will call
461 * vtblockput once per reference.
464 vtblockduplock(VtBlock *b)
466 assert(b->nlock > 0);
471 * we're done with the block.
472 * unlock it. can't use it after calling this.
475 vtblockput(VtBlock* b)
482 if(0)fprint(2, "vtblockput: %d: %x %d %d\n", getpid(), b->addr, c->nheap, b->iostate);
484 fprint(2, "vtblockput %p from %p\n", b, getcallerpc(&b));
490 * b->nlock should probably stay at zero while
491 * the vtBlock is unlocked, but diskThread and vtSleep
492 * conspire to assume that they can just qlock(&b->lk); vtblockput(b),
493 * so we have to keep b->nlock set to 1 even
494 * when the vtBlock is unlocked.
496 assert(b->nlock == 0);
511 /*if(b->addr != NilBlock) print("blockput %d\n", b->addr); */
523 vtblockwrite(VtBlock *b)
525 uchar score[VtScoreSize];
530 if(b->iostate != BioLocal){
531 werrstr("vtblockwrite: not a local block");
536 n = vtzerotruncate(b->type, b->data, c->blocksize);
538 if(c->write(c->z, score, b->type, b->data, n) < 0)
541 memmove(b->score, score, VtScoreSize);
544 b->iostate = BioVenti;
545 h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
546 b->next = c->hash[h];
549 b->next->prev = &b->next;
550 b->prev = &c->hash[h];
556 vtcacheblocksize(VtCache *c)
562 vtblockcopy(VtBlock *b)
567 bb = vtcacheallocblock(b->c, b->type);
572 memmove(bb->data, b->data, b->c->blocksize);
578 vtlocaltoglobal(u32int addr, uchar score[VtScoreSize])
580 memset(score, 0, 16);
581 score[16] = addr>>24;
582 score[17] = addr>>16;
589 vtglobaltolocal(uchar score[VtScoreSize])
591 static uchar zero[16];
592 if(memcmp(score, zero, 16) != 0)
594 return (score[16]<<24)|(score[17]<<16)|(score[18]<<8)|score[19];