Blob


1 /*
2 * Memory-only VtBlock cache.
3 *
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.
9 */
11 #include <u.h>
12 #include <libc.h>
13 #include <venti.h>
15 int nread, ncopy, nwrite;
17 enum {
18 BioLocal = 1,
19 BioVenti,
20 BioReading,
21 BioWriting,
22 BioEmpty,
23 BioVentiError,
24 };
25 enum {
26 BadHeap = ~0,
27 };
28 struct VtCache
29 {
30 QLock lk;
31 VtConn *z;
32 u32int blocksize;
33 u32int now; /* ticks for usage time stamps */
34 VtBlock **hash; /* hash table for finding addresses */
35 int nhash;
36 VtBlock **heap; /* heap for finding victims */
37 int nheap;
38 VtBlock *block; /* all allocated blocks */
39 int nblock;
40 uchar *mem; /* memory for all blocks and data */
41 int (*write)(VtConn*, uchar[VtScoreSize], uint, uchar*, int);
42 };
44 static void cachecheck(VtCache*);
46 VtCache*
47 vtcachealloc(VtConn *z, int blocksize, ulong nblock)
48 {
49 uchar *p;
50 VtCache *c;
51 int i;
52 VtBlock *b;
54 c = vtmallocz(sizeof(VtCache));
56 c->z = z;
57 c->blocksize = (blocksize + 127) & ~127;
58 c->nblock = nblock;
59 c->nhash = nblock;
60 c->hash = vtmallocz(nblock*sizeof(VtBlock*));
61 c->heap = vtmallocz(nblock*sizeof(VtBlock*));
62 c->block = vtmallocz(nblock*sizeof(VtBlock));
63 c->mem = vtmallocz(nblock*c->blocksize);
64 c->write = vtwrite;
66 p = c->mem;
67 for(i=0; i<nblock; i++){
68 b = &c->block[i];
69 b->addr = NilBlock;
70 b->c = c;
71 b->data = p;
72 b->heap = i;
73 c->heap[i] = b;
74 p += c->blocksize;
75 }
76 c->nheap = nblock;
77 cachecheck(c);
78 return c;
79 }
81 /*
82 * BUG This is here so that vbackup can override it and do some
83 * pipelining of writes. Arguably vtwrite or vtwritepacket or the
84 * cache itself should be providing this functionality.
85 */
86 void
87 vtcachesetwrite(VtCache *c, int (*write)(VtConn*, uchar[VtScoreSize], uint, uchar*, int))
88 {
89 if(write == nil)
90 write = vtwrite;
91 c->write = write;
92 }
94 void
95 vtcachefree(VtCache *c)
96 {
97 int i;
99 qlock(&c->lk);
101 cachecheck(c);
102 for(i=0; i<c->nblock; i++)
103 assert(c->block[i].ref == 0);
105 vtfree(c->hash);
106 vtfree(c->heap);
107 vtfree(c->block);
108 vtfree(c->mem);
109 vtfree(c);
112 static void
113 vtcachedump(VtCache *c)
115 int i;
116 VtBlock *b;
118 for(i=0; i<c->nblock; i++){
119 b = &c->block[i];
120 print("cache block %d: type %d score %V iostate %d addr %d ref %d nlock %d\n",
121 i, b->type, b->score, b->iostate, b->addr, b->ref, b->nlock);
125 static void
126 cachecheck(VtCache *c)
128 u32int size, now;
129 int i, k, refed;
130 VtBlock *b;
132 size = c->blocksize;
133 now = c->now;
135 for(i = 0; i < c->nheap; i++){
136 if(c->heap[i]->heap != i)
137 sysfatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
138 if(i > 0 && c->heap[(i - 1) >> 1]->used - now > c->heap[i]->used - now)
139 sysfatal("bad heap ordering");
140 k = (i << 1) + 1;
141 if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
142 sysfatal("bad heap ordering");
143 k++;
144 if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
145 sysfatal("bad heap ordering");
148 refed = 0;
149 for(i = 0; i < c->nblock; i++){
150 b = &c->block[i];
151 if(b->data != &c->mem[i * size])
152 sysfatal("mis-blocked at %d", i);
153 if(b->ref && b->heap == BadHeap)
154 refed++;
155 else if(b->addr != NilBlock)
156 refed++;
158 if(c->nheap + refed != c->nblock){
159 fprint(2, "cachecheck: nheap %d refed %d nblocks %d\n", c->nheap, refed, c->nblock);
160 //vtcachedump(c);
162 assert(c->nheap + refed == c->nblock);
163 refed = 0;
164 for(i = 0; i < c->nblock; i++){
165 b = &c->block[i];
166 if(b->ref){
167 if(1)fprint(2, "a=%ud %V ref=%d\n", b->addr, b->score, b->ref);
168 refed++;
171 if(refed > 0)fprint(2, "cachecheck: in used %d\n", refed);
174 static int
175 upheap(int i, VtBlock *b)
177 VtBlock *bb;
178 u32int now;
179 int p;
180 VtCache *c;
182 c = b->c;
183 now = c->now;
184 for(; i != 0; i = p){
185 p = (i - 1) >> 1;
186 bb = c->heap[p];
187 if(b->used - now >= bb->used - now)
188 break;
189 c->heap[i] = bb;
190 bb->heap = i;
192 c->heap[i] = b;
193 b->heap = i;
195 return i;
198 static int
199 downheap(int i, VtBlock *b)
201 VtBlock *bb;
202 u32int now;
203 int k;
204 VtCache *c;
206 c = b->c;
207 now = c->now;
208 for(; ; i = k){
209 k = (i << 1) + 1;
210 if(k >= c->nheap)
211 break;
212 if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now)
213 k++;
214 bb = c->heap[k];
215 if(b->used - now <= bb->used - now)
216 break;
217 c->heap[i] = bb;
218 bb->heap = i;
220 c->heap[i] = b;
221 b->heap = i;
222 return i;
225 /*
226 * Delete a block from the heap.
227 * Called with c->lk held.
228 */
229 static void
230 heapdel(VtBlock *b)
232 int i, si;
233 VtCache *c;
235 c = b->c;
237 si = b->heap;
238 if(si == BadHeap)
239 return;
240 b->heap = BadHeap;
241 c->nheap--;
242 if(si == c->nheap)
243 return;
244 b = c->heap[c->nheap];
245 i = upheap(si, b);
246 if(i == si)
247 downheap(i, b);
250 /*
251 * Insert a block into the heap.
252 * Called with c->lk held.
253 */
254 static void
255 heapins(VtBlock *b)
257 assert(b->heap == BadHeap);
258 upheap(b->c->nheap++, b);
261 /*
262 * locate the vtBlock with the oldest second to last use.
263 * remove it from the heap, and fix up the heap.
264 */
265 /* called with c->lk held */
266 static VtBlock*
267 vtcachebumpblock(VtCache *c)
269 VtBlock *b;
271 /*
272 * locate the vtBlock with the oldest second to last use.
273 * remove it from the heap, and fix up the heap.
274 */
275 if(c->nheap == 0){
276 vtcachedump(c);
277 fprint(2, "vtcachebumpblock: no free blocks in vtCache");
278 abort();
280 b = c->heap[0];
281 heapdel(b);
283 assert(b->heap == BadHeap);
284 assert(b->ref == 0);
286 /*
287 * unchain the vtBlock from hash chain if any
288 */
289 if(b->prev){
290 *(b->prev) = b->next;
291 if(b->next)
292 b->next->prev = b->prev;
293 b->prev = nil;
297 if(0)fprint(2, "droping %x:%V\n", b->addr, b->score);
298 /* set vtBlock to a reasonable state */
299 b->ref = 1;
300 b->iostate = BioEmpty;
301 return b;
304 /*
305 * fetch a local block from the memory cache.
306 * if it's not there, load it, bumping some other Block.
307 * if we're out of free blocks, we're screwed.
308 */
309 VtBlock*
310 vtcachelocal(VtCache *c, u32int addr, int type)
312 VtBlock *b;
314 if(addr >= c->nblock)
315 sysfatal("vtcachelocal: asked for block #%ud; only %d blocks\n",
316 addr, c->nblock);
318 b = &c->block[addr];
319 if(b->addr == NilBlock || b->iostate != BioLocal)
320 sysfatal("vtcachelocal: block is not local");
322 if(b->type != type)
323 sysfatal("vtcachelocal: block has wrong type %d != %d", b->type, type);
325 qlock(&c->lk);
326 b->ref++;
327 qunlock(&c->lk);
329 qlock(&b->lk);
330 b->nlock = 1;
331 return b;
334 VtBlock*
335 vtcacheallocblock(VtCache *c, int type)
337 VtBlock *b;
339 qlock(&c->lk);
340 b = vtcachebumpblock(c);
341 b->iostate = BioLocal;
342 b->type = type;
343 b->addr = b - c->block;
344 vtzeroextend(type, b->data, 0, c->blocksize);
345 vtlocaltoglobal(b->addr, b->score);
346 qunlock(&c->lk);
348 qlock(&b->lk);
349 b->nlock = 1;
351 return b;
354 /*
355 * fetch a global (Venti) block from the memory cache.
356 * if it's not there, load it, bumping some other block.
357 */
358 VtBlock*
359 vtcacheglobal(VtCache *c, uchar score[VtScoreSize], int type)
361 VtBlock *b;
362 ulong h;
363 int n;
364 u32int addr;
366 addr = vtglobaltolocal(score);
367 if(addr != NilBlock)
368 return vtcachelocal(c, addr, type);
370 h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
372 /*
373 * look for the block in the cache
374 */
375 qlock(&c->lk);
376 for(b = c->hash[h]; b != nil; b = b->next){
377 if(b->addr != NilBlock || memcmp(b->score, score, VtScoreSize) != 0 || b->type != type)
378 continue;
379 heapdel(b);
380 b->ref++;
381 qunlock(&c->lk);
382 qlock(&b->lk);
383 b->nlock = 1;
384 if(b->iostate == BioVentiError){
385 if(chattyventi)
386 fprint(2, "cached read error for %V\n", score);
387 werrstr("venti i/o error");
388 vtblockput(b);
389 return nil;
391 return b;
394 /*
395 * not found
396 */
397 b = vtcachebumpblock(c);
398 b->addr = NilBlock;
399 b->type = type;
400 memmove(b->score, score, VtScoreSize);
401 /* chain onto correct hash */
402 b->next = c->hash[h];
403 c->hash[h] = b;
404 if(b->next != nil)
405 b->next->prev = &b->next;
406 b->prev = &c->hash[h];
408 /*
409 * Lock b before unlocking c, so that others wait while we read.
411 * You might think there is a race between this qlock(b) before qunlock(c)
412 * and the qlock(c) while holding a qlock(b) in vtblockwrite. However,
413 * the block here can never be the block in a vtblockwrite, so we're safe.
414 * We're certainly living on the edge.
415 */
416 qlock(&b->lk);
417 b->nlock = 1;
418 qunlock(&c->lk);
420 n = vtread(c->z, score, type, b->data, c->blocksize);
421 if(n < 0){
422 werrstr("vtread %V: %r", score);
423 if(chattyventi)
424 fprint(2, "read %V: %r\n", score);
425 b->iostate = BioVentiError;
426 vtblockput(b);
427 return nil;
429 vtzeroextend(type, b->data, n, c->blocksize);
430 b->iostate = BioVenti;
431 b->nlock = 1;
432 return b;
435 /*
436 * The thread that has locked b may refer to it by
437 * multiple names. Nlock counts the number of
438 * references the locking thread holds. It will call
439 * vtblockput once per reference.
440 */
441 void
442 vtblockduplock(VtBlock *b)
444 assert(b->nlock > 0);
445 b->nlock++;
448 /*
449 * we're done with the block.
450 * unlock it. can't use it after calling this.
451 */
452 void
453 vtblockput(VtBlock* b)
455 VtCache *c;
457 if(b == nil)
458 return;
460 if(0)fprint(2, "vtblockput: %d: %x %d %d\n", getpid(), b->addr, c->nheap, b->iostate);
462 if(--b->nlock > 0)
463 return;
465 /*
466 * b->nlock should probably stay at zero while
467 * the vtBlock is unlocked, but diskThread and vtSleep
468 * conspire to assume that they can just qlock(&b->lk); vtblockput(b),
469 * so we have to keep b->nlock set to 1 even
470 * when the vtBlock is unlocked.
471 */
472 assert(b->nlock == 0);
473 b->nlock = 1;
475 qunlock(&b->lk);
476 c = b->c;
477 qlock(&c->lk);
479 if(--b->ref > 0){
480 qunlock(&c->lk);
481 return;
484 assert(b->ref == 0);
485 switch(b->iostate){
486 case BioVenti:
487 //if(b->addr != NilBlock) print("blockput %d\n", b->addr);
488 b->used = c->now++;
489 case BioVentiError:
490 heapins(b);
491 break;
492 case BioLocal:
493 break;
495 qunlock(&c->lk);
498 int
499 vtblockwrite(VtBlock *b)
501 uchar score[VtScoreSize];
502 VtCache *c;
503 uint h;
504 int n;
506 if(b->iostate != BioLocal){
507 werrstr("vtblockwrite: not a local block");
508 return -1;
511 c = b->c;
512 n = vtzerotruncate(b->type, b->data, c->blocksize);
513 if(c->write(c->z, score, b->type, b->data, n) < 0)
514 return -1;
516 memmove(b->score, score, VtScoreSize);
518 qlock(&c->lk);
519 b->iostate = BioVenti;
520 h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
521 b->next = c->hash[h];
522 c->hash[h] = b;
523 if(b->next != nil)
524 b->next->prev = &b->next;
525 b->prev = &c->hash[h];
526 qunlock(&c->lk);
527 return 0;
530 uint
531 vtcacheblocksize(VtCache *c)
533 return c->blocksize;
536 VtBlock*
537 vtblockcopy(VtBlock *b)
539 VtBlock *bb;
541 ncopy++;
542 bb = vtcacheallocblock(b->c, b->type);
543 if(bb == nil){
544 vtblockput(b);
545 return nil;
547 memmove(bb->data, b->data, b->c->blocksize);
548 vtblockput(b);
549 return bb;
552 void
553 vtlocaltoglobal(u32int addr, uchar score[VtScoreSize])
555 memset(score, 0, 16);
556 score[16] = addr>>24;
557 score[17] = addr>>16;
558 score[18] = addr>>8;
559 score[19] = addr;
563 u32int
564 vtglobaltolocal(uchar score[VtScoreSize])
566 static uchar zero[16];
567 if(memcmp(score, zero, 16) != 0)
568 return NilBlock;
569 return (score[16]<<24)|(score[17]<<16)|(score[18]<<8)|score[19];