xref: /plan9/sys/src/libventi/cache.c (revision 368c31ab13393dea083228fdd1c3445076f83a4b)
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