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