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 vtcachenread;
16 int vtcachencopy;
17 int vtcachenwrite;
18 int vttracelevel;
20 enum {
21 BioLocal = 1,
22 BioVenti,
23 BioReading,
24 BioWriting,
25 BioEmpty,
26 BioVentiError
27 };
28 enum {
29 BadHeap = ~0
30 };
31 struct VtCache
32 {
33 QLock lk;
34 VtConn *z;
35 u32int blocksize;
36 u32int now; /* ticks for usage time stamps */
37 VtBlock **hash; /* hash table for finding addresses */
38 int nhash;
39 VtBlock **heap; /* heap for finding victims */
40 int nheap;
41 VtBlock *block; /* all allocated blocks */
42 int nblock;
43 uchar *mem; /* memory for all blocks and data */
44 int (*write)(VtConn*, uchar[VtScoreSize], uint, uchar*, int);
45 };
47 static void cachecheck(VtCache*);
49 VtCache*
50 vtcachealloc(VtConn *z, int blocksize, ulong nblock)
51 {
52 uchar *p;
53 VtCache *c;
54 int i;
55 VtBlock *b;
57 c = vtmallocz(sizeof(VtCache));
59 c->z = z;
60 c->blocksize = (blocksize + 127) & ~127;
61 c->nblock = nblock;
62 c->nhash = nblock;
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);
67 c->write = vtwrite;
69 p = c->mem;
70 for(i=0; i<nblock; i++){
71 b = &c->block[i];
72 b->addr = NilBlock;
73 b->c = c;
74 b->data = p;
75 b->heap = i;
76 c->heap[i] = b;
77 p += c->blocksize;
78 }
79 c->nheap = nblock;
80 cachecheck(c);
81 return c;
82 }
84 /*
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.
88 */
89 void
90 vtcachesetwrite(VtCache *c, int (*write)(VtConn*, uchar[VtScoreSize], uint, uchar*, int))
91 {
92 if(write == nil)
93 write = vtwrite;
94 c->write = write;
95 }
97 void
98 vtcachefree(VtCache *c)
99 {
100 int i;
102 qlock(&c->lk);
104 cachecheck(c);
105 for(i=0; i<c->nblock; i++)
106 assert(c->block[i].ref == 0);
108 vtfree(c->hash);
109 vtfree(c->heap);
110 vtfree(c->block);
111 vtfree(c->mem);
112 vtfree(c);
115 static void
116 vtcachedump(VtCache *c)
118 int i;
119 VtBlock *b;
121 for(i=0; i<c->nblock; i++){
122 b = &c->block[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);
128 static void
129 cachecheck(VtCache *c)
131 u32int size, now;
132 int i, k, refed;
133 VtBlock *b;
135 size = c->blocksize;
136 now = c->now;
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");
143 k = (i << 1) + 1;
144 if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
145 sysfatal("bad heap ordering");
146 k++;
147 if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
148 sysfatal("bad heap ordering");
151 refed = 0;
152 for(i = 0; i < c->nblock; i++){
153 b = &c->block[i];
154 if(b->data != &c->mem[i * size])
155 sysfatal("mis-blocked at %d", i);
156 if(b->ref && b->heap == BadHeap)
157 refed++;
158 else if(b->addr != NilBlock)
159 refed++;
161 assert(c->nheap + refed == c->nblock);
162 refed = 0;
163 for(i = 0; i < c->nblock; i++){
164 b = &c->block[i];
165 if(b->ref){
166 refed++;
171 static int
172 upheap(int i, VtBlock *b)
174 VtBlock *bb;
175 u32int now;
176 int p;
177 VtCache *c;
179 c = b->c;
180 now = c->now;
181 for(; i != 0; i = p){
182 p = (i - 1) >> 1;
183 bb = c->heap[p];
184 if(b->used - now >= bb->used - now)
185 break;
186 c->heap[i] = bb;
187 bb->heap = i;
189 c->heap[i] = b;
190 b->heap = i;
192 return i;
195 static int
196 downheap(int i, VtBlock *b)
198 VtBlock *bb;
199 u32int now;
200 int k;
201 VtCache *c;
203 c = b->c;
204 now = c->now;
205 for(; ; i = k){
206 k = (i << 1) + 1;
207 if(k >= c->nheap)
208 break;
209 if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now)
210 k++;
211 bb = c->heap[k];
212 if(b->used - now <= bb->used - now)
213 break;
214 c->heap[i] = bb;
215 bb->heap = i;
217 c->heap[i] = b;
218 b->heap = i;
219 return i;
222 /*
223 * Delete a block from the heap.
224 * Called with c->lk held.
225 */
226 static void
227 heapdel(VtBlock *b)
229 int i, si;
230 VtCache *c;
232 c = b->c;
234 si = b->heap;
235 if(si == BadHeap)
236 return;
237 b->heap = BadHeap;
238 c->nheap--;
239 if(si == c->nheap)
240 return;
241 b = c->heap[c->nheap];
242 i = upheap(si, b);
243 if(i == si)
244 downheap(i, b);
247 /*
248 * Insert a block into the heap.
249 * Called with c->lk held.
250 */
251 static void
252 heapins(VtBlock *b)
254 assert(b->heap == BadHeap);
255 upheap(b->c->nheap++, b);
258 /*
259 * locate the vtBlock with the oldest second to last use.
260 * remove it from the heap, and fix up the heap.
261 */
262 /* called with c->lk held */
263 static VtBlock*
264 vtcachebumpblock(VtCache *c)
266 VtBlock *b;
268 /*
269 * locate the vtBlock with the oldest second to last use.
270 * remove it from the heap, and fix up the heap.
271 */
272 if(c->nheap == 0){
273 vtcachedump(c);
274 fprint(2, "vtcachebumpblock: no free blocks in vtCache");
275 abort();
277 b = c->heap[0];
278 heapdel(b);
280 assert(b->heap == BadHeap);
281 assert(b->ref == 0);
283 /*
284 * unchain the vtBlock from hash chain if any
285 */
286 if(b->prev){
287 *(b->prev) = b->next;
288 if(b->next)
289 b->next->prev = b->prev;
290 b->prev = nil;
294 if(0)fprint(2, "droping %x:%V\n", b->addr, b->score);
295 /* set vtBlock to a reasonable state */
296 b->ref = 1;
297 b->iostate = BioEmpty;
298 return b;
301 /*
302 * fetch a local block from the memory cache.
303 * if it's not there, load it, bumping some other Block.
304 * if we're out of free blocks, we're screwed.
305 */
306 VtBlock*
307 vtcachelocal(VtCache *c, u32int addr, int type)
309 VtBlock *b;
311 if(addr == 0)
312 sysfatal("vtcachelocal: asked for nonexistent block 0");
313 if(addr > c->nblock)
314 sysfatal("vtcachelocal: asked for block #%ud; only %d blocks",
315 addr, c->nblock);
317 b = &c->block[addr-1];
318 if(b->addr == NilBlock || b->iostate != BioLocal)
319 sysfatal("vtcachelocal: block is not local");
321 if(b->type != type)
322 sysfatal("vtcachelocal: block has wrong type %d != %d", b->type, type);
324 qlock(&c->lk);
325 b->ref++;
326 qunlock(&c->lk);
328 qlock(&b->lk);
329 b->nlock = 1;
330 b->pc = getcallerpc(&c);
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)+1;
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;
350 b->pc = getcallerpc(&c);
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 if(vttracelevel)
367 fprint(2, "vtcacheglobal %V %d from %p\n", score, type, getcallerpc(&c));
368 addr = vtglobaltolocal(score);
369 if(addr != NilBlock){
370 if(vttracelevel)
371 fprint(2, "vtcacheglobal %V %d => local\n", score, type);
372 b = vtcachelocal(c, addr, type);
373 if(b)
374 b->pc = getcallerpc(&c);
375 return b;
378 h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
380 /*
381 * look for the block in the cache
382 */
383 qlock(&c->lk);
384 for(b = c->hash[h]; b != nil; b = b->next){
385 if(b->addr != NilBlock || memcmp(b->score, score, VtScoreSize) != 0 || b->type != type)
386 continue;
387 heapdel(b);
388 b->ref++;
389 qunlock(&c->lk);
390 if(vttracelevel)
391 fprint(2, "vtcacheglobal %V %d => found in cache %p; locking\n", score, type, b);
392 qlock(&b->lk);
393 b->nlock = 1;
394 if(b->iostate == BioVentiError){
395 if(chattyventi)
396 fprint(2, "cached read error for %V\n", score);
397 if(vttracelevel)
398 fprint(2, "vtcacheglobal %V %d => cache read error\n", score, type);
399 vtblockput(b);
400 werrstr("venti i/o error");
401 return nil;
403 if(vttracelevel)
404 fprint(2, "vtcacheglobal %V %d => found in cache; returning\n", score, type);
405 b->pc = getcallerpc(&c);
406 return b;
409 /*
410 * not found
411 */
412 b = vtcachebumpblock(c);
413 b->addr = NilBlock;
414 b->type = type;
415 memmove(b->score, score, VtScoreSize);
416 /* chain onto correct hash */
417 b->next = c->hash[h];
418 c->hash[h] = b;
419 if(b->next != nil)
420 b->next->prev = &b->next;
421 b->prev = &c->hash[h];
423 /*
424 * Lock b before unlocking c, so that others wait while we read.
426 * You might think there is a race between this qlock(b) before qunlock(c)
427 * and the qlock(c) while holding a qlock(b) in vtblockwrite. However,
428 * the block here can never be the block in a vtblockwrite, so we're safe.
429 * We're certainly living on the edge.
430 */
431 if(vttracelevel)
432 fprint(2, "vtcacheglobal %V %d => bumped; locking %p\n", score, type, b);
433 qlock(&b->lk);
434 b->nlock = 1;
435 qunlock(&c->lk);
437 vtcachenread++;
438 n = vtread(c->z, score, type, b->data, c->blocksize);
439 if(n < 0){
440 if(chattyventi)
441 fprint(2, "read %V: %r\n", score);
442 if(vttracelevel)
443 fprint(2, "vtcacheglobal %V %d => bumped; read error\n", score, type);
444 b->iostate = BioVentiError;
445 vtblockput(b);
446 return nil;
448 vtzeroextend(type, b->data, n, c->blocksize);
449 b->iostate = BioVenti;
450 b->nlock = 1;
451 if(vttracelevel)
452 fprint(2, "vtcacheglobal %V %d => loaded into cache; returning\n", score, type);
453 b->pc = getcallerpc(&b);
454 return b;
457 /*
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.
462 */
463 void
464 vtblockduplock(VtBlock *b)
466 assert(b->nlock > 0);
467 b->nlock++;
470 /*
471 * we're done with the block.
472 * unlock it. can't use it after calling this.
473 */
474 void
475 vtblockput(VtBlock* b)
477 VtCache *c;
479 if(b == nil)
480 return;
482 if(0)fprint(2, "vtblockput: %d: %x %d %d\n", getpid(), b->addr, c->nheap, b->iostate);
483 if(vttracelevel)
484 fprint(2, "vtblockput %p from %p\n", b, getcallerpc(&b));
486 if(--b->nlock > 0)
487 return;
489 /*
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.
495 */
496 assert(b->nlock == 0);
497 b->nlock = 1;
499 qunlock(&b->lk);
500 c = b->c;
501 qlock(&c->lk);
503 if(--b->ref > 0){
504 qunlock(&c->lk);
505 return;
508 assert(b->ref == 0);
509 switch(b->iostate){
510 case BioVenti:
511 /*if(b->addr != NilBlock) print("blockput %d\n", b->addr); */
512 b->used = c->now++;
513 /* fall through */
514 case BioVentiError:
515 heapins(b);
516 break;
517 case BioLocal:
518 break;
520 qunlock(&c->lk);
523 int
524 vtblockwrite(VtBlock *b)
526 uchar score[VtScoreSize];
527 VtCache *c;
528 uint h;
529 int n;
531 if(b->iostate != BioLocal){
532 werrstr("vtblockwrite: not a local block");
533 return -1;
536 c = b->c;
537 n = vtzerotruncate(b->type, b->data, c->blocksize);
538 vtcachenwrite++;
539 if(c->write(c->z, score, b->type, b->data, n) < 0)
540 return -1;
542 memmove(b->score, score, VtScoreSize);
544 qlock(&c->lk);
545 b->addr = NilBlock; /* now on venti */
546 b->iostate = BioVenti;
547 h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
548 b->next = c->hash[h];
549 c->hash[h] = b;
550 if(b->next != nil)
551 b->next->prev = &b->next;
552 b->prev = &c->hash[h];
553 qunlock(&c->lk);
554 return 0;
557 uint
558 vtcacheblocksize(VtCache *c)
560 return c->blocksize;
563 VtBlock*
564 vtblockcopy(VtBlock *b)
566 VtBlock *bb;
568 vtcachencopy++;
569 bb = vtcacheallocblock(b->c, b->type);
570 if(bb == nil){
571 vtblockput(b);
572 return nil;
574 memmove(bb->data, b->data, b->c->blocksize);
575 vtblockput(b);
576 bb->pc = getcallerpc(&b);
577 return bb;
580 void
581 vtlocaltoglobal(u32int addr, uchar score[VtScoreSize])
583 memset(score, 0, 16);
584 score[16] = addr>>24;
585 score[17] = addr>>16;
586 score[18] = addr>>8;
587 score[19] = addr;
591 u32int
592 vtglobaltolocal(uchar score[VtScoreSize])
594 static uchar zero[16];
595 if(memcmp(score, zero, 16) != 0)
596 return NilBlock;
597 return (score[16]<<24)|(score[17]<<16)|(score[18]<<8)|score[19];