xref: /plan9-contrib/sys/src/cmd/fossil/cache.c (revision 5e96a66c77eb9140492ca53f857cbbf108e128ed)
1*5e96a66cSDavid du Colombier #include "stdinc.h"
2*5e96a66cSDavid du Colombier #include "dat.h"
3*5e96a66cSDavid du Colombier #include "fns.h"
4*5e96a66cSDavid du Colombier #include "error.h"
5*5e96a66cSDavid du Colombier 
6*5e96a66cSDavid du Colombier #include "9.h"	/* for cacheFlush */
7*5e96a66cSDavid du Colombier 
8*5e96a66cSDavid du Colombier typedef struct FreeList FreeList;
9*5e96a66cSDavid du Colombier typedef struct BAddr BAddr;
10*5e96a66cSDavid du Colombier 
11*5e96a66cSDavid du Colombier enum {
12*5e96a66cSDavid du Colombier 	BadHeap = ~0,
13*5e96a66cSDavid du Colombier };
14*5e96a66cSDavid du Colombier 
15*5e96a66cSDavid du Colombier /*
16*5e96a66cSDavid du Colombier  * Store data to the memory cache in c->size blocks
17*5e96a66cSDavid du Colombier  * with the block zero extended to fill it out.  When writing to
18*5e96a66cSDavid du Colombier  * Venti, the block will be zero truncated.  The walker will also check
19*5e96a66cSDavid du Colombier  * that the block fits within psize or dsize as the case may be.
20*5e96a66cSDavid du Colombier  */
21*5e96a66cSDavid du Colombier 
22*5e96a66cSDavid du Colombier struct Cache
23*5e96a66cSDavid du Colombier {
24*5e96a66cSDavid du Colombier 	VtLock	*lk;
25*5e96a66cSDavid du Colombier 	int 	ref;
26*5e96a66cSDavid du Colombier 	int	mode;
27*5e96a66cSDavid du Colombier 
28*5e96a66cSDavid du Colombier 	Disk 	*disk;
29*5e96a66cSDavid du Colombier 	int	size;			/* block size */
30*5e96a66cSDavid du Colombier 	VtSession *z;
31*5e96a66cSDavid du Colombier 	u32int	now;			/* ticks for usage timestamps */
32*5e96a66cSDavid du Colombier 	Block	**heads;		/* hash table for finding address */
33*5e96a66cSDavid du Colombier 	int	nheap;			/* number of available victims */
34*5e96a66cSDavid du Colombier 	Block	**heap;			/* heap for locating victims */
35*5e96a66cSDavid du Colombier 	long	nblocks;		/* number of blocks allocated */
36*5e96a66cSDavid du Colombier 	Block	*blocks;		/* array of block descriptors */
37*5e96a66cSDavid du Colombier 	u8int	*mem;			/* memory for all block data & blists */
38*5e96a66cSDavid du Colombier 
39*5e96a66cSDavid du Colombier 	BList	*blfree;
40*5e96a66cSDavid du Colombier 	VtRendez *blrend;
41*5e96a66cSDavid du Colombier 
42*5e96a66cSDavid du Colombier 	int 	ndirty;			/* number of dirty blocks in the cache */
43*5e96a66cSDavid du Colombier 	int 	maxdirty;		/* max number of dirty blocks */
44*5e96a66cSDavid du Colombier 	u32int	vers;
45*5e96a66cSDavid du Colombier 
46*5e96a66cSDavid du Colombier 	long hashSize;
47*5e96a66cSDavid du Colombier 
48*5e96a66cSDavid du Colombier 	FreeList *fl;
49*5e96a66cSDavid du Colombier 
50*5e96a66cSDavid du Colombier 	VtRendez *die;			/* daemon threads should die when != nil */
51*5e96a66cSDavid du Colombier 
52*5e96a66cSDavid du Colombier 	VtRendez *flush;
53*5e96a66cSDavid du Colombier 	VtRendez *flushwait;
54*5e96a66cSDavid du Colombier 	BAddr *baddr;
55*5e96a66cSDavid du Colombier 	int bw, br, be;
56*5e96a66cSDavid du Colombier 	int nflush;
57*5e96a66cSDavid du Colombier 
58*5e96a66cSDavid du Colombier 	/* unlink daemon */
59*5e96a66cSDavid du Colombier 	BList *uhead;
60*5e96a66cSDavid du Colombier 	BList *utail;
61*5e96a66cSDavid du Colombier 	VtRendez *unlink;
62*5e96a66cSDavid du Colombier };
63*5e96a66cSDavid du Colombier 
64*5e96a66cSDavid du Colombier struct BList {
65*5e96a66cSDavid du Colombier 	int part;
66*5e96a66cSDavid du Colombier 	u32int addr;
67*5e96a66cSDavid du Colombier 	uchar type;
68*5e96a66cSDavid du Colombier 	u32int tag;
69*5e96a66cSDavid du Colombier 	u32int epoch;
70*5e96a66cSDavid du Colombier 	u32int vers;
71*5e96a66cSDavid du Colombier 
72*5e96a66cSDavid du Colombier 	/* for roll back */
73*5e96a66cSDavid du Colombier 	int index;			/* -1 indicates not valid */
74*5e96a66cSDavid du Colombier 	uchar score[VtScoreSize];
75*5e96a66cSDavid du Colombier 
76*5e96a66cSDavid du Colombier 	BList *next;
77*5e96a66cSDavid du Colombier };
78*5e96a66cSDavid du Colombier 
79*5e96a66cSDavid du Colombier struct BAddr {
80*5e96a66cSDavid du Colombier 	int part;
81*5e96a66cSDavid du Colombier 	u32int addr;
82*5e96a66cSDavid du Colombier 	u32int vers;
83*5e96a66cSDavid du Colombier };
84*5e96a66cSDavid du Colombier 
85*5e96a66cSDavid du Colombier struct FreeList {
86*5e96a66cSDavid du Colombier 	VtLock *lk;
87*5e96a66cSDavid du Colombier 	u32int last;	/* last block allocated */
88*5e96a66cSDavid du Colombier 	u32int end;	/* end of data partition */
89*5e96a66cSDavid du Colombier };
90*5e96a66cSDavid du Colombier 
91*5e96a66cSDavid du Colombier static FreeList *flAlloc(u32int end);
92*5e96a66cSDavid du Colombier static void flFree(FreeList *fl);
93*5e96a66cSDavid du Colombier 
94*5e96a66cSDavid du Colombier static Block *cacheBumpBlock(Cache *c);
95*5e96a66cSDavid du Colombier static void heapDel(Block*);
96*5e96a66cSDavid du Colombier static void heapIns(Block*);
97*5e96a66cSDavid du Colombier static void cacheCheck(Cache*);
98*5e96a66cSDavid du Colombier static int readLabel(Cache*, Label*, u32int addr);
99*5e96a66cSDavid du Colombier static void unlinkThread(void *a);
100*5e96a66cSDavid du Colombier static void flushThread(void *a);
101*5e96a66cSDavid du Colombier static void flushBody(Cache *c);
102*5e96a66cSDavid du Colombier static void unlinkBody(Cache *c);
103*5e96a66cSDavid du Colombier static int cacheFlushBlock(Cache *c);
104*5e96a66cSDavid du Colombier 
105*5e96a66cSDavid du Colombier /*
106*5e96a66cSDavid du Colombier  * Mapping from local block type to Venti type
107*5e96a66cSDavid du Colombier  */
108*5e96a66cSDavid du Colombier int vtType[BtMax] = {
109*5e96a66cSDavid du Colombier 	VtDataType,		/* BtData | 0  */
110*5e96a66cSDavid du Colombier 	VtPointerType0,		/* BtData | 1  */
111*5e96a66cSDavid du Colombier 	VtPointerType1,		/* BtData | 2  */
112*5e96a66cSDavid du Colombier 	VtPointerType2,		/* BtData | 3  */
113*5e96a66cSDavid du Colombier 	VtPointerType3,		/* BtData | 4  */
114*5e96a66cSDavid du Colombier 	VtPointerType4,		/* BtData | 5  */
115*5e96a66cSDavid du Colombier 	VtPointerType5,		/* BtData | 6  */
116*5e96a66cSDavid du Colombier 	VtPointerType6,		/* BtData | 7  */
117*5e96a66cSDavid du Colombier 	VtDirType,		/* BtDir | 0  */
118*5e96a66cSDavid du Colombier 	VtPointerType0,		/* BtDir | 1  */
119*5e96a66cSDavid du Colombier 	VtPointerType1,		/* BtDir | 2  */
120*5e96a66cSDavid du Colombier 	VtPointerType2,		/* BtDir | 3  */
121*5e96a66cSDavid du Colombier 	VtPointerType3,		/* BtDir | 4  */
122*5e96a66cSDavid du Colombier 	VtPointerType4,		/* BtDir | 5  */
123*5e96a66cSDavid du Colombier 	VtPointerType5,		/* BtDir | 6  */
124*5e96a66cSDavid du Colombier 	VtPointerType6,		/* BtDir | 7  */
125*5e96a66cSDavid du Colombier };
126*5e96a66cSDavid du Colombier 
127*5e96a66cSDavid du Colombier /*
128*5e96a66cSDavid du Colombier  * Allocate the memory cache.
129*5e96a66cSDavid du Colombier  */
130*5e96a66cSDavid du Colombier Cache *
131*5e96a66cSDavid du Colombier cacheAlloc(Disk *disk, VtSession *z, ulong nblocks, int mode)
132*5e96a66cSDavid du Colombier {
133*5e96a66cSDavid du Colombier 	int i;
134*5e96a66cSDavid du Colombier 	Cache *c;
135*5e96a66cSDavid du Colombier 	Block *b;
136*5e96a66cSDavid du Colombier 	BList *bl;
137*5e96a66cSDavid du Colombier 	u8int *p;
138*5e96a66cSDavid du Colombier 	int nbl;
139*5e96a66cSDavid du Colombier 
140*5e96a66cSDavid du Colombier 	c = vtMemAllocZ(sizeof(Cache));
141*5e96a66cSDavid du Colombier 
142*5e96a66cSDavid du Colombier 	/* reasonable number of BList elements */
143*5e96a66cSDavid du Colombier 	nbl = nblocks * 4;
144*5e96a66cSDavid du Colombier 
145*5e96a66cSDavid du Colombier 	c->lk = vtLockAlloc();
146*5e96a66cSDavid du Colombier 	c->ref = 1;
147*5e96a66cSDavid du Colombier 	c->disk = disk;
148*5e96a66cSDavid du Colombier 	c->z = z;
149*5e96a66cSDavid du Colombier 	c->size = diskBlockSize(disk);
150*5e96a66cSDavid du Colombier bwatchSetBlockSize(c->size);
151*5e96a66cSDavid du Colombier 	/* round c->size up to be a nice multiple */
152*5e96a66cSDavid du Colombier 	c->size = (c->size + 127) & ~127;
153*5e96a66cSDavid du Colombier 	c->nblocks = nblocks;
154*5e96a66cSDavid du Colombier 	c->hashSize = nblocks;
155*5e96a66cSDavid du Colombier 	c->heads = vtMemAllocZ(c->hashSize*sizeof(Block*));
156*5e96a66cSDavid du Colombier 	c->heap = vtMemAllocZ(nblocks*sizeof(Block*));
157*5e96a66cSDavid du Colombier 	c->blocks = vtMemAllocZ(nblocks*sizeof(Block));
158*5e96a66cSDavid du Colombier 	c->mem = vtMemAllocZ(nblocks * c->size + nbl * sizeof(BList));
159*5e96a66cSDavid du Colombier 	c->baddr = vtMemAllocZ(nblocks * sizeof(BAddr));
160*5e96a66cSDavid du Colombier 	c->mode = mode;
161*5e96a66cSDavid du Colombier 	c->vers++;
162*5e96a66cSDavid du Colombier 	p = c->mem;
163*5e96a66cSDavid du Colombier 	for(i = 0; i < nblocks; i++){
164*5e96a66cSDavid du Colombier 		b = &c->blocks[i];
165*5e96a66cSDavid du Colombier 		b->lk = vtLockAlloc();
166*5e96a66cSDavid du Colombier 		b->c = c;
167*5e96a66cSDavid du Colombier 		b->data = p;
168*5e96a66cSDavid du Colombier 		b->heap = i;
169*5e96a66cSDavid du Colombier 		b->ioready = vtRendezAlloc(b->lk);
170*5e96a66cSDavid du Colombier 		c->heap[i] = b;
171*5e96a66cSDavid du Colombier 		p += c->size;
172*5e96a66cSDavid du Colombier 	}
173*5e96a66cSDavid du Colombier 	c->nheap = nblocks;
174*5e96a66cSDavid du Colombier 
175*5e96a66cSDavid du Colombier 	for(i=0; i<nbl; i++){
176*5e96a66cSDavid du Colombier 		bl = (BList*)p;
177*5e96a66cSDavid du Colombier 		bl->next = c->blfree;
178*5e96a66cSDavid du Colombier 		c->blfree = bl;
179*5e96a66cSDavid du Colombier 		p += sizeof(BList);
180*5e96a66cSDavid du Colombier 	}
181*5e96a66cSDavid du Colombier 	c->blrend = vtRendezAlloc(c->lk);
182*5e96a66cSDavid du Colombier 
183*5e96a66cSDavid du Colombier 	c->maxdirty = nblocks*(DirtyPercentage*0.01);
184*5e96a66cSDavid du Colombier 
185*5e96a66cSDavid du Colombier 	c->fl = flAlloc(diskSize(disk, PartData));
186*5e96a66cSDavid du Colombier 
187*5e96a66cSDavid du Colombier 	c->unlink = vtRendezAlloc(c->lk);
188*5e96a66cSDavid du Colombier 	c->flush = vtRendezAlloc(c->lk);
189*5e96a66cSDavid du Colombier 	c->flushwait = vtRendezAlloc(c->lk);
190*5e96a66cSDavid du Colombier 
191*5e96a66cSDavid du Colombier 	if(mode == OReadWrite){
192*5e96a66cSDavid du Colombier 		c->ref += 2;
193*5e96a66cSDavid du Colombier 		vtThread(unlinkThread, c);
194*5e96a66cSDavid du Colombier 		vtThread(flushThread, c);
195*5e96a66cSDavid du Colombier 	}
196*5e96a66cSDavid du Colombier 	cacheCheck(c);
197*5e96a66cSDavid du Colombier 
198*5e96a66cSDavid du Colombier 	return c;
199*5e96a66cSDavid du Colombier }
200*5e96a66cSDavid du Colombier 
201*5e96a66cSDavid du Colombier /*
202*5e96a66cSDavid du Colombier  * Free the whole memory cache, flushing all dirty blocks to the disk.
203*5e96a66cSDavid du Colombier  */
204*5e96a66cSDavid du Colombier void
205*5e96a66cSDavid du Colombier cacheFree(Cache *c)
206*5e96a66cSDavid du Colombier {
207*5e96a66cSDavid du Colombier 	int i;
208*5e96a66cSDavid du Colombier 
209*5e96a66cSDavid du Colombier 	/* kill off daemon threads */
210*5e96a66cSDavid du Colombier 	vtLock(c->lk);
211*5e96a66cSDavid du Colombier 	c->die = vtRendezAlloc(c->lk);
212*5e96a66cSDavid du Colombier 	vtWakeup(c->flush);
213*5e96a66cSDavid du Colombier 	vtWakeup(c->unlink);
214*5e96a66cSDavid du Colombier 	while(c->ref > 1)
215*5e96a66cSDavid du Colombier 		vtSleep(c->die);
216*5e96a66cSDavid du Colombier 
217*5e96a66cSDavid du Colombier 	/* flush everything out */
218*5e96a66cSDavid du Colombier 	do {
219*5e96a66cSDavid du Colombier 		unlinkBody(c);
220*5e96a66cSDavid du Colombier 		vtUnlock(c->lk);
221*5e96a66cSDavid du Colombier 		while(cacheFlushBlock(c))
222*5e96a66cSDavid du Colombier 			;
223*5e96a66cSDavid du Colombier 		diskFlush(c->disk);
224*5e96a66cSDavid du Colombier 		vtLock(c->lk);
225*5e96a66cSDavid du Colombier 	} while(c->uhead || c->ndirty);
226*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
227*5e96a66cSDavid du Colombier 
228*5e96a66cSDavid du Colombier 	cacheCheck(c);
229*5e96a66cSDavid du Colombier 
230*5e96a66cSDavid du Colombier 	for(i = 0; i < c->nblocks; i++){
231*5e96a66cSDavid du Colombier 		assert(c->blocks[i].ref == 0);
232*5e96a66cSDavid du Colombier 		vtRendezFree(c->blocks[i].ioready);
233*5e96a66cSDavid du Colombier 		vtLockFree(c->blocks[i].lk);
234*5e96a66cSDavid du Colombier 	}
235*5e96a66cSDavid du Colombier 	flFree(c->fl);
236*5e96a66cSDavid du Colombier 	vtMemFree(c->baddr);
237*5e96a66cSDavid du Colombier 	vtMemFree(c->heads);
238*5e96a66cSDavid du Colombier 	vtMemFree(c->blocks);
239*5e96a66cSDavid du Colombier 	vtMemFree(c->mem);
240*5e96a66cSDavid du Colombier 	vtLockFree(c->lk);
241*5e96a66cSDavid du Colombier 	diskFree(c->disk);
242*5e96a66cSDavid du Colombier 	vtRendezFree(c->blrend);
243*5e96a66cSDavid du Colombier 	/* don't close vtSession */
244*5e96a66cSDavid du Colombier 	vtMemFree(c);
245*5e96a66cSDavid du Colombier }
246*5e96a66cSDavid du Colombier 
247*5e96a66cSDavid du Colombier static void
248*5e96a66cSDavid du Colombier cacheDump(Cache *c)
249*5e96a66cSDavid du Colombier {
250*5e96a66cSDavid du Colombier 	int i;
251*5e96a66cSDavid du Colombier 	Block *b;
252*5e96a66cSDavid du Colombier 
253*5e96a66cSDavid du Colombier 	for(i = 0; i < c->nblocks; i++){
254*5e96a66cSDavid du Colombier 		b = &c->blocks[i];
255*5e96a66cSDavid du Colombier 		fprint(2, "p=%d a=%ud %V t=%d ref=%d state=%s io=%s\n",
256*5e96a66cSDavid du Colombier 			b->part, b->addr, b->score, b->l.type, b->ref,
257*5e96a66cSDavid du Colombier 			bsStr(b->l.state), bioStr(b->iostate));
258*5e96a66cSDavid du Colombier 	}
259*5e96a66cSDavid du Colombier }
260*5e96a66cSDavid du Colombier 
261*5e96a66cSDavid du Colombier static void
262*5e96a66cSDavid du Colombier cacheCheck(Cache *c)
263*5e96a66cSDavid du Colombier {
264*5e96a66cSDavid du Colombier 	u32int size, now;
265*5e96a66cSDavid du Colombier 	int i, k, refed;
266*5e96a66cSDavid du Colombier 	static uchar zero[VtScoreSize];
267*5e96a66cSDavid du Colombier 	Block *b;
268*5e96a66cSDavid du Colombier 
269*5e96a66cSDavid du Colombier 	size = c->size;
270*5e96a66cSDavid du Colombier 	now = c->now;
271*5e96a66cSDavid du Colombier 
272*5e96a66cSDavid du Colombier 	for(i = 0; i < c->nheap; i++){
273*5e96a66cSDavid du Colombier 		if(c->heap[i]->heap != i)
274*5e96a66cSDavid du Colombier 			vtFatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
275*5e96a66cSDavid du Colombier 		if(i > 0 && c->heap[(i - 1) >> 1]->used - now > c->heap[i]->used - now)
276*5e96a66cSDavid du Colombier 			vtFatal("bad heap ordering");
277*5e96a66cSDavid du Colombier 		k = (i << 1) + 1;
278*5e96a66cSDavid du Colombier 		if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
279*5e96a66cSDavid du Colombier 			vtFatal("bad heap ordering");
280*5e96a66cSDavid du Colombier 		k++;
281*5e96a66cSDavid du Colombier 		if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
282*5e96a66cSDavid du Colombier 			vtFatal("bad heap ordering");
283*5e96a66cSDavid du Colombier 	}
284*5e96a66cSDavid du Colombier 
285*5e96a66cSDavid du Colombier 	refed = 0;
286*5e96a66cSDavid du Colombier 	for(i = 0; i < c->nblocks; i++){
287*5e96a66cSDavid du Colombier 		b = &c->blocks[i];
288*5e96a66cSDavid du Colombier 		if(b->data != &c->mem[i * size])
289*5e96a66cSDavid du Colombier 			vtFatal("mis-blocked at %d", i);
290*5e96a66cSDavid du Colombier 		if(b->ref && b->heap == BadHeap){
291*5e96a66cSDavid du Colombier 			refed++;
292*5e96a66cSDavid du Colombier 		}
293*5e96a66cSDavid du Colombier 	}
294*5e96a66cSDavid du Colombier if(c->nheap + refed != c->nblocks){
295*5e96a66cSDavid du Colombier fprint(2, "cacheCheck: nheap %d refed %d nblocks %ld\n", c->nheap, refed, c->nblocks);
296*5e96a66cSDavid du Colombier cacheDump(c);
297*5e96a66cSDavid du Colombier }
298*5e96a66cSDavid du Colombier 	assert(c->nheap + refed == c->nblocks);
299*5e96a66cSDavid du Colombier 	refed = 0;
300*5e96a66cSDavid du Colombier 	for(i = 0; i < c->nblocks; i++){
301*5e96a66cSDavid du Colombier 		b = &c->blocks[i];
302*5e96a66cSDavid du Colombier 		if(b->ref){
303*5e96a66cSDavid du Colombier if(1)fprint(2, "p=%d a=%ud %V ref=%d %L\n", b->part, b->addr, b->score, b->ref, &b->l);
304*5e96a66cSDavid du Colombier 			refed++;
305*5e96a66cSDavid du Colombier 		}
306*5e96a66cSDavid du Colombier 	}
307*5e96a66cSDavid du Colombier if(refed > 0)fprint(2, "cacheCheck: in used %d\n", refed);
308*5e96a66cSDavid du Colombier }
309*5e96a66cSDavid du Colombier 
310*5e96a66cSDavid du Colombier 
311*5e96a66cSDavid du Colombier /*
312*5e96a66cSDavid du Colombier  * locate the block with the oldest second to last use.
313*5e96a66cSDavid du Colombier  * remove it from the heap, and fix up the heap.
314*5e96a66cSDavid du Colombier  */
315*5e96a66cSDavid du Colombier /* called with c->lk held */
316*5e96a66cSDavid du Colombier static Block *
317*5e96a66cSDavid du Colombier cacheBumpBlock(Cache *c)
318*5e96a66cSDavid du Colombier {
319*5e96a66cSDavid du Colombier 	Block *b;
320*5e96a66cSDavid du Colombier 
321*5e96a66cSDavid du Colombier 	/*
322*5e96a66cSDavid du Colombier 	 * locate the block with the oldest second to last use.
323*5e96a66cSDavid du Colombier 	 * remove it from the heap, and fix up the heap.
324*5e96a66cSDavid du Colombier 	 */
325*5e96a66cSDavid du Colombier 	if(c->nheap == 0)
326*5e96a66cSDavid du Colombier 		vtFatal("cacheBumpBlock: no free blocks in cache");
327*5e96a66cSDavid du Colombier 	b = c->heap[0];
328*5e96a66cSDavid du Colombier 	heapDel(b);
329*5e96a66cSDavid du Colombier 
330*5e96a66cSDavid du Colombier 	assert(b->heap == BadHeap);
331*5e96a66cSDavid du Colombier 	assert(b->ref == 0);
332*5e96a66cSDavid du Colombier 	assert(b->iostate == BioEmpty || b->iostate == BioLabel || b->iostate == BioClean);
333*5e96a66cSDavid du Colombier 	assert(b->prior == nil);
334*5e96a66cSDavid du Colombier 	assert(b->uhead == nil);
335*5e96a66cSDavid du Colombier 
336*5e96a66cSDavid du Colombier 	/*
337*5e96a66cSDavid du Colombier 	 * unchain the block from hash chain
338*5e96a66cSDavid du Colombier 	 */
339*5e96a66cSDavid du Colombier 	if(b->prev){
340*5e96a66cSDavid du Colombier 		*(b->prev) = b->next;
341*5e96a66cSDavid du Colombier 		if(b->next)
342*5e96a66cSDavid du Colombier 			b->next->prev = b->prev;
343*5e96a66cSDavid du Colombier 		b->prev = nil;
344*5e96a66cSDavid du Colombier 	}
345*5e96a66cSDavid du Colombier 
346*5e96a66cSDavid du Colombier 
347*5e96a66cSDavid du Colombier if(0)fprint(2, "droping %d:%x:%V\n", b->part, b->addr, b->score);
348*5e96a66cSDavid du Colombier 	/* set block to a reasonable state */
349*5e96a66cSDavid du Colombier 	b->ref = 1;
350*5e96a66cSDavid du Colombier 	b->part = PartError;
351*5e96a66cSDavid du Colombier 	memset(&b->l, 0, sizeof(b->l));
352*5e96a66cSDavid du Colombier 	b->iostate = BioEmpty;
353*5e96a66cSDavid du Colombier 
354*5e96a66cSDavid du Colombier 	return b;
355*5e96a66cSDavid du Colombier }
356*5e96a66cSDavid du Colombier 
357*5e96a66cSDavid du Colombier /*
358*5e96a66cSDavid du Colombier  * look for a particular version of the block in the memory cache.
359*5e96a66cSDavid du Colombier  */
360*5e96a66cSDavid du Colombier static Block *
361*5e96a66cSDavid du Colombier _cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers,
362*5e96a66cSDavid du Colombier 	int waitlock, int *lockfailure)
363*5e96a66cSDavid du Colombier {
364*5e96a66cSDavid du Colombier 	Block *b;
365*5e96a66cSDavid du Colombier 	ulong h;
366*5e96a66cSDavid du Colombier 
367*5e96a66cSDavid du Colombier 	h = addr % c->hashSize;
368*5e96a66cSDavid du Colombier 
369*5e96a66cSDavid du Colombier 	if(lockfailure)
370*5e96a66cSDavid du Colombier 		*lockfailure = 0;
371*5e96a66cSDavid du Colombier 
372*5e96a66cSDavid du Colombier 	/*
373*5e96a66cSDavid du Colombier 	 * look for the block in the cache
374*5e96a66cSDavid du Colombier 	 */
375*5e96a66cSDavid du Colombier 	vtLock(c->lk);
376*5e96a66cSDavid du Colombier 	for(b = c->heads[h]; b != nil; b = b->next){
377*5e96a66cSDavid du Colombier 		if(b->part == part && b->addr == addr)
378*5e96a66cSDavid du Colombier 			break;
379*5e96a66cSDavid du Colombier 	}
380*5e96a66cSDavid du Colombier 	if(b == nil || b->vers != vers){
381*5e96a66cSDavid du Colombier 		vtUnlock(c->lk);
382*5e96a66cSDavid du Colombier 		return nil;
383*5e96a66cSDavid du Colombier 	}
384*5e96a66cSDavid du Colombier 	if(!waitlock && !vtCanLock(b->lk)){
385*5e96a66cSDavid du Colombier 		*lockfailure = 1;
386*5e96a66cSDavid du Colombier 		vtUnlock(c->lk);
387*5e96a66cSDavid du Colombier 		return nil;
388*5e96a66cSDavid du Colombier 	}
389*5e96a66cSDavid du Colombier 	heapDel(b);
390*5e96a66cSDavid du Colombier 	b->ref++;
391*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
392*5e96a66cSDavid du Colombier 
393*5e96a66cSDavid du Colombier 	bwatchLock(b);
394*5e96a66cSDavid du Colombier 	if(waitlock)
395*5e96a66cSDavid du Colombier 		vtLock(b->lk);
396*5e96a66cSDavid du Colombier 	b->nlock = 1;
397*5e96a66cSDavid du Colombier 
398*5e96a66cSDavid du Colombier 	for(;;){
399*5e96a66cSDavid du Colombier 		switch(b->iostate){
400*5e96a66cSDavid du Colombier 		default:
401*5e96a66cSDavid du Colombier 			abort();
402*5e96a66cSDavid du Colombier 		case BioEmpty:
403*5e96a66cSDavid du Colombier 		case BioLabel:
404*5e96a66cSDavid du Colombier 		case BioClean:
405*5e96a66cSDavid du Colombier 		case BioDirty:
406*5e96a66cSDavid du Colombier 			if(b->vers != vers){
407*5e96a66cSDavid du Colombier 				blockPut(b);
408*5e96a66cSDavid du Colombier 				return nil;
409*5e96a66cSDavid du Colombier 			}
410*5e96a66cSDavid du Colombier 			return b;
411*5e96a66cSDavid du Colombier 		case BioReading:
412*5e96a66cSDavid du Colombier 		case BioWriting:
413*5e96a66cSDavid du Colombier 			vtSleep(b->ioready);
414*5e96a66cSDavid du Colombier 			break;
415*5e96a66cSDavid du Colombier 		case BioVentiError:
416*5e96a66cSDavid du Colombier 		case BioReadError:
417*5e96a66cSDavid du Colombier 			blockSetIOState(b, BioEmpty);
418*5e96a66cSDavid du Colombier 			blockPut(b);
419*5e96a66cSDavid du Colombier 			vtSetError(EIO);
420*5e96a66cSDavid du Colombier 			return nil;
421*5e96a66cSDavid du Colombier 		}
422*5e96a66cSDavid du Colombier 	}
423*5e96a66cSDavid du Colombier 	/* NOT REACHED */
424*5e96a66cSDavid du Colombier }
425*5e96a66cSDavid du Colombier static Block*
426*5e96a66cSDavid du Colombier cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers)
427*5e96a66cSDavid du Colombier {
428*5e96a66cSDavid du Colombier 	return _cacheLocalLookup(c, part, addr, vers, 1, 0);
429*5e96a66cSDavid du Colombier }
430*5e96a66cSDavid du Colombier 
431*5e96a66cSDavid du Colombier 
432*5e96a66cSDavid du Colombier /*
433*5e96a66cSDavid du Colombier  * fetch a local (on-disk) block from the memory cache.
434*5e96a66cSDavid du Colombier  * if it's not there, load it, bumping some other block.
435*5e96a66cSDavid du Colombier  */
436*5e96a66cSDavid du Colombier Block *
437*5e96a66cSDavid du Colombier _cacheLocal(Cache *c, int part, u32int addr, int mode, u32int epoch)
438*5e96a66cSDavid du Colombier {
439*5e96a66cSDavid du Colombier 	Block *b;
440*5e96a66cSDavid du Colombier 	ulong h;
441*5e96a66cSDavid du Colombier 
442*5e96a66cSDavid du Colombier 	assert(part != PartVenti);
443*5e96a66cSDavid du Colombier 
444*5e96a66cSDavid du Colombier 	h = addr % c->hashSize;
445*5e96a66cSDavid du Colombier 
446*5e96a66cSDavid du Colombier 	/*
447*5e96a66cSDavid du Colombier 	 * look for the block in the cache
448*5e96a66cSDavid du Colombier 	 */
449*5e96a66cSDavid du Colombier 	vtLock(c->lk);
450*5e96a66cSDavid du Colombier 	for(b = c->heads[h]; b != nil; b = b->next){
451*5e96a66cSDavid du Colombier 		if(b->part != part || b->addr != addr)
452*5e96a66cSDavid du Colombier 			continue;
453*5e96a66cSDavid du Colombier 		if(epoch && b->l.epoch != epoch){
454*5e96a66cSDavid du Colombier fprint(2, "_cacheLocal want epoch %ud got %ud\n", epoch, b->l.epoch);
455*5e96a66cSDavid du Colombier 			vtUnlock(c->lk);
456*5e96a66cSDavid du Colombier 			vtSetError(ELabelMismatch);
457*5e96a66cSDavid du Colombier 			return nil;
458*5e96a66cSDavid du Colombier 		}
459*5e96a66cSDavid du Colombier 		heapDel(b);
460*5e96a66cSDavid du Colombier 		b->ref++;
461*5e96a66cSDavid du Colombier 		break;
462*5e96a66cSDavid du Colombier 	}
463*5e96a66cSDavid du Colombier 
464*5e96a66cSDavid du Colombier 	if(b == nil){
465*5e96a66cSDavid du Colombier 		b = cacheBumpBlock(c);
466*5e96a66cSDavid du Colombier 
467*5e96a66cSDavid du Colombier 		b->part = part;
468*5e96a66cSDavid du Colombier 		b->addr = addr;
469*5e96a66cSDavid du Colombier 		localToGlobal(addr, b->score);
470*5e96a66cSDavid du Colombier 
471*5e96a66cSDavid du Colombier 		/* chain onto correct hash */
472*5e96a66cSDavid du Colombier 		b->next = c->heads[h];
473*5e96a66cSDavid du Colombier 		c->heads[h] = b;
474*5e96a66cSDavid du Colombier 		if(b->next != nil)
475*5e96a66cSDavid du Colombier 			b->next->prev = &b->next;
476*5e96a66cSDavid du Colombier 		b->prev = &c->heads[h];
477*5e96a66cSDavid du Colombier 	}
478*5e96a66cSDavid du Colombier 
479*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
480*5e96a66cSDavid du Colombier 
481*5e96a66cSDavid du Colombier 	/*
482*5e96a66cSDavid du Colombier 	 * BUG: what if the epoch changes right here?
483*5e96a66cSDavid du Colombier 	 * In the worst case, we could end up in some weird
484*5e96a66cSDavid du Colombier 	 * lock loop, because the block we want no longer exists,
485*5e96a66cSDavid du Colombier 	 * and instead we're trying to lock a block we have no
486*5e96a66cSDavid du Colombier 	 * business grabbing.
487*5e96a66cSDavid du Colombier 	 *
488*5e96a66cSDavid du Colombier 	 * For now, I'm not going to worry about it.
489*5e96a66cSDavid du Colombier 	 */
490*5e96a66cSDavid du Colombier 
491*5e96a66cSDavid du Colombier if(0)fprint(2, "cacheLocal: %d: %d %x\n", getpid(), b->part, b->addr);
492*5e96a66cSDavid du Colombier 	bwatchLock(b);
493*5e96a66cSDavid du Colombier 	vtLock(b->lk);
494*5e96a66cSDavid du Colombier 	b->nlock = 1;
495*5e96a66cSDavid du Colombier 
496*5e96a66cSDavid du Colombier 	if(part == PartData && b->iostate == BioEmpty){
497*5e96a66cSDavid du Colombier 		if(!readLabel(c, &b->l, addr)){
498*5e96a66cSDavid du Colombier 			blockPut(b);
499*5e96a66cSDavid du Colombier 			return nil;
500*5e96a66cSDavid du Colombier 		}
501*5e96a66cSDavid du Colombier 		blockSetIOState(b, BioLabel);
502*5e96a66cSDavid du Colombier 	}
503*5e96a66cSDavid du Colombier 	if(epoch && b->l.epoch != epoch){
504*5e96a66cSDavid du Colombier 		blockPut(b);
505*5e96a66cSDavid du Colombier fprint(2, "_cacheLocal want epoch %ud got %ud\n", epoch, b->l.epoch);
506*5e96a66cSDavid du Colombier 		vtSetError(ELabelMismatch);
507*5e96a66cSDavid du Colombier 		return nil;
508*5e96a66cSDavid du Colombier 	}
509*5e96a66cSDavid du Colombier 
510*5e96a66cSDavid du Colombier 	b->pc = getcallerpc(&c);
511*5e96a66cSDavid du Colombier 	for(;;){
512*5e96a66cSDavid du Colombier 		switch(b->iostate){
513*5e96a66cSDavid du Colombier 		default:
514*5e96a66cSDavid du Colombier 			abort();
515*5e96a66cSDavid du Colombier 		case BioEmpty:
516*5e96a66cSDavid du Colombier 		case BioLabel:
517*5e96a66cSDavid du Colombier 			if(mode == OOverWrite){
518*5e96a66cSDavid du Colombier 				blockSetIOState(b, BioClean);
519*5e96a66cSDavid du Colombier 				return b;
520*5e96a66cSDavid du Colombier 			}
521*5e96a66cSDavid du Colombier 			diskRead(c->disk, b);
522*5e96a66cSDavid du Colombier 			vtSleep(b->ioready);
523*5e96a66cSDavid du Colombier 			break;
524*5e96a66cSDavid du Colombier 		case BioClean:
525*5e96a66cSDavid du Colombier 		case BioDirty:
526*5e96a66cSDavid du Colombier 			return b;
527*5e96a66cSDavid du Colombier 		case BioReading:
528*5e96a66cSDavid du Colombier 		case BioWriting:
529*5e96a66cSDavid du Colombier 			vtSleep(b->ioready);
530*5e96a66cSDavid du Colombier 			break;
531*5e96a66cSDavid du Colombier 		case BioReadError:
532*5e96a66cSDavid du Colombier 			blockSetIOState(b, BioEmpty);
533*5e96a66cSDavid du Colombier 			blockPut(b);
534*5e96a66cSDavid du Colombier 			vtSetError(EIO);
535*5e96a66cSDavid du Colombier 			return nil;
536*5e96a66cSDavid du Colombier 		}
537*5e96a66cSDavid du Colombier 	}
538*5e96a66cSDavid du Colombier 	/* NOT REACHED */
539*5e96a66cSDavid du Colombier }
540*5e96a66cSDavid du Colombier 
541*5e96a66cSDavid du Colombier Block *
542*5e96a66cSDavid du Colombier cacheLocal(Cache *c, int part, u32int addr, int mode)
543*5e96a66cSDavid du Colombier {
544*5e96a66cSDavid du Colombier 	return _cacheLocal(c, part, addr, mode, 0);
545*5e96a66cSDavid du Colombier }
546*5e96a66cSDavid du Colombier 
547*5e96a66cSDavid du Colombier /*
548*5e96a66cSDavid du Colombier  * fetch a local (on-disk) block from the memory cache.
549*5e96a66cSDavid du Colombier  * if it's not there, load it, bumping some other block.
550*5e96a66cSDavid du Colombier  * check tag and type.
551*5e96a66cSDavid du Colombier  */
552*5e96a66cSDavid du Colombier Block *
553*5e96a66cSDavid du Colombier cacheLocalData(Cache *c, u32int addr, int type, u32int tag, int mode, u32int epoch)
554*5e96a66cSDavid du Colombier {
555*5e96a66cSDavid du Colombier 	Block *b;
556*5e96a66cSDavid du Colombier 
557*5e96a66cSDavid du Colombier 	b = _cacheLocal(c, PartData, addr, mode, epoch);
558*5e96a66cSDavid du Colombier 	if(b == nil)
559*5e96a66cSDavid du Colombier 		return nil;
560*5e96a66cSDavid du Colombier 	if(b->l.type != type || b->l.tag != tag){
561*5e96a66cSDavid du Colombier 		fprint(2, "cacheLocalData: addr=%d type got %d exp %d: tag got %x exp %x\n",
562*5e96a66cSDavid du Colombier 			addr, b->l.type, type, b->l.tag, tag);
563*5e96a66cSDavid du Colombier abort();
564*5e96a66cSDavid du Colombier 		vtSetError(ELabelMismatch);
565*5e96a66cSDavid du Colombier 		blockPut(b);
566*5e96a66cSDavid du Colombier 		return nil;
567*5e96a66cSDavid du Colombier 	}
568*5e96a66cSDavid du Colombier 	b->pc = getcallerpc(&c);
569*5e96a66cSDavid du Colombier 	return b;
570*5e96a66cSDavid du Colombier }
571*5e96a66cSDavid du Colombier 
572*5e96a66cSDavid du Colombier /*
573*5e96a66cSDavid du Colombier  * fetch a global (Venti) block from the memory cache.
574*5e96a66cSDavid du Colombier  * if it's not there, load it, bumping some other block.
575*5e96a66cSDavid du Colombier  * check tag and type if it's really a local block in disguise.
576*5e96a66cSDavid du Colombier  */
577*5e96a66cSDavid du Colombier Block *
578*5e96a66cSDavid du Colombier cacheGlobal(Cache *c, uchar score[VtScoreSize], int type, u32int tag, int mode)
579*5e96a66cSDavid du Colombier {
580*5e96a66cSDavid du Colombier 	int n;
581*5e96a66cSDavid du Colombier 	Block *b;
582*5e96a66cSDavid du Colombier 	ulong h;
583*5e96a66cSDavid du Colombier 	u32int addr;
584*5e96a66cSDavid du Colombier 
585*5e96a66cSDavid du Colombier 	addr = globalToLocal(score);
586*5e96a66cSDavid du Colombier 	if(addr != NilBlock){
587*5e96a66cSDavid du Colombier 		b = cacheLocalData(c, addr, type, tag, mode, 0);
588*5e96a66cSDavid du Colombier 		if(b)
589*5e96a66cSDavid du Colombier 			b->pc = getcallerpc(&c);
590*5e96a66cSDavid du Colombier 		return b;
591*5e96a66cSDavid du Colombier 	}
592*5e96a66cSDavid du Colombier 
593*5e96a66cSDavid du Colombier 	h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->hashSize;
594*5e96a66cSDavid du Colombier 
595*5e96a66cSDavid du Colombier 	/*
596*5e96a66cSDavid du Colombier 	 * look for the block in the cache
597*5e96a66cSDavid du Colombier 	 */
598*5e96a66cSDavid du Colombier 	vtLock(c->lk);
599*5e96a66cSDavid du Colombier 	for(b = c->heads[h]; b != nil; b = b->next){
600*5e96a66cSDavid du Colombier 		if(b->part != PartVenti || memcmp(b->score, score, VtScoreSize) != 0 || b->l.type != type)
601*5e96a66cSDavid du Colombier 			continue;
602*5e96a66cSDavid du Colombier 		heapDel(b);
603*5e96a66cSDavid du Colombier 		b->ref++;
604*5e96a66cSDavid du Colombier 		break;
605*5e96a66cSDavid du Colombier 	}
606*5e96a66cSDavid du Colombier 
607*5e96a66cSDavid du Colombier 	if(b == nil){
608*5e96a66cSDavid du Colombier if(0)fprint(2, "cacheGlobal %V %d\n", score, type);
609*5e96a66cSDavid du Colombier 
610*5e96a66cSDavid du Colombier 		b = cacheBumpBlock(c);
611*5e96a66cSDavid du Colombier 
612*5e96a66cSDavid du Colombier 		b->part = PartVenti;
613*5e96a66cSDavid du Colombier 		b->addr = NilBlock;
614*5e96a66cSDavid du Colombier 		b->l.type = type;
615*5e96a66cSDavid du Colombier 		memmove(b->score, score, VtScoreSize);
616*5e96a66cSDavid du Colombier 
617*5e96a66cSDavid du Colombier 		/* chain onto correct hash */
618*5e96a66cSDavid du Colombier 		b->next = c->heads[h];
619*5e96a66cSDavid du Colombier 		c->heads[h] = b;
620*5e96a66cSDavid du Colombier 		if(b->next != nil)
621*5e96a66cSDavid du Colombier 			b->next->prev = &b->next;
622*5e96a66cSDavid du Colombier 		b->prev = &c->heads[h];
623*5e96a66cSDavid du Colombier 	}
624*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
625*5e96a66cSDavid du Colombier 
626*5e96a66cSDavid du Colombier 	bwatchLock(b);
627*5e96a66cSDavid du Colombier 	vtLock(b->lk);
628*5e96a66cSDavid du Colombier 	b->nlock = 1;
629*5e96a66cSDavid du Colombier 	b->pc = getcallerpc(&c);
630*5e96a66cSDavid du Colombier 
631*5e96a66cSDavid du Colombier 	switch(b->iostate){
632*5e96a66cSDavid du Colombier 	default:
633*5e96a66cSDavid du Colombier 		abort();
634*5e96a66cSDavid du Colombier 	case BioEmpty:
635*5e96a66cSDavid du Colombier 		n = vtRead(c->z, score, vtType[type], b->data, c->size);
636*5e96a66cSDavid du Colombier 		if(n < 0 || !vtSha1Check(score, b->data, n)){
637*5e96a66cSDavid du Colombier 			blockSetIOState(b, BioVentiError);
638*5e96a66cSDavid du Colombier 			blockPut(b);
639*5e96a66cSDavid du Colombier 			return nil;
640*5e96a66cSDavid du Colombier 		}
641*5e96a66cSDavid du Colombier 		vtZeroExtend(vtType[type], b->data, n, c->size);
642*5e96a66cSDavid du Colombier 		blockSetIOState(b, BioClean);
643*5e96a66cSDavid du Colombier 		return b;
644*5e96a66cSDavid du Colombier 	case BioClean:
645*5e96a66cSDavid du Colombier 		return b;
646*5e96a66cSDavid du Colombier 	case BioVentiError:
647*5e96a66cSDavid du Colombier 	case BioReadError:
648*5e96a66cSDavid du Colombier 		blockPut(b);
649*5e96a66cSDavid du Colombier 		vtSetError(EIO);
650*5e96a66cSDavid du Colombier 		blockSetIOState(b, BioEmpty);
651*5e96a66cSDavid du Colombier 		return nil;
652*5e96a66cSDavid du Colombier 	}
653*5e96a66cSDavid du Colombier 	/* NOT REACHED */
654*5e96a66cSDavid du Colombier }
655*5e96a66cSDavid du Colombier 
656*5e96a66cSDavid du Colombier /*
657*5e96a66cSDavid du Colombier  * allocate a new on-disk block and load it into the memory cache.
658*5e96a66cSDavid du Colombier  * BUG: if the disk is full, should we flush some of it to Venti?
659*5e96a66cSDavid du Colombier  */
660*5e96a66cSDavid du Colombier static u32int lastAlloc;
661*5e96a66cSDavid du Colombier 
662*5e96a66cSDavid du Colombier Block *
663*5e96a66cSDavid du Colombier cacheAllocBlock(Cache *c, int type, u32int tag, u32int epoch, u32int epochLow)
664*5e96a66cSDavid du Colombier {
665*5e96a66cSDavid du Colombier 	FreeList *fl;
666*5e96a66cSDavid du Colombier 	u32int addr;
667*5e96a66cSDavid du Colombier 	Block *b;
668*5e96a66cSDavid du Colombier 	int n, nwrap;
669*5e96a66cSDavid du Colombier 	Label lab;
670*5e96a66cSDavid du Colombier 
671*5e96a66cSDavid du Colombier 	n = c->size / LabelSize;
672*5e96a66cSDavid du Colombier 	fl = c->fl;
673*5e96a66cSDavid du Colombier 
674*5e96a66cSDavid du Colombier 	vtLock(fl->lk);
675*5e96a66cSDavid du Colombier fl->last = 0;
676*5e96a66cSDavid du Colombier 	addr = fl->last;
677*5e96a66cSDavid du Colombier 	b = cacheLocal(c, PartLabel, addr/n, OReadOnly);
678*5e96a66cSDavid du Colombier 	if(b == nil){
679*5e96a66cSDavid du Colombier 		fprint(2, "cacheAllocBlock: xxx %R\n");
680*5e96a66cSDavid du Colombier 		vtUnlock(fl->lk);
681*5e96a66cSDavid du Colombier 		return nil;
682*5e96a66cSDavid du Colombier 	}
683*5e96a66cSDavid du Colombier 	nwrap = 0;
684*5e96a66cSDavid du Colombier 	for(;;){
685*5e96a66cSDavid du Colombier 		if(++addr >= fl->end){
686*5e96a66cSDavid du Colombier 			addr = 0;
687*5e96a66cSDavid du Colombier 			fprint(2, "cacheAllocBlock wrap %d\n", fl->end);
688*5e96a66cSDavid du Colombier 			if(++nwrap >= 2){
689*5e96a66cSDavid du Colombier 				blockPut(b);
690*5e96a66cSDavid du Colombier 				fl->last = 0;
691*5e96a66cSDavid du Colombier 				vtSetError("disk is full");
692*5e96a66cSDavid du Colombier 				fprint(2, "cacheAllocBlock: xxx1 %R\n");
693*5e96a66cSDavid du Colombier 				vtUnlock(fl->lk);
694*5e96a66cSDavid du Colombier 				return nil;
695*5e96a66cSDavid du Colombier 			}
696*5e96a66cSDavid du Colombier 		}
697*5e96a66cSDavid du Colombier 		if(addr%n == 0){
698*5e96a66cSDavid du Colombier 			blockPut(b);
699*5e96a66cSDavid du Colombier 			b = cacheLocal(c, PartLabel, addr/n, OReadOnly);
700*5e96a66cSDavid du Colombier 			if(b == nil){
701*5e96a66cSDavid du Colombier 				fl->last = addr;
702*5e96a66cSDavid du Colombier 				fprint(2, "cacheAllocBlock: xxx2 %R\n");
703*5e96a66cSDavid du Colombier 				vtUnlock(fl->lk);
704*5e96a66cSDavid du Colombier 				return nil;
705*5e96a66cSDavid du Colombier 			}
706*5e96a66cSDavid du Colombier 		}
707*5e96a66cSDavid du Colombier 		if(!labelUnpack(&lab, b->data, addr%n))
708*5e96a66cSDavid du Colombier 			continue;
709*5e96a66cSDavid du Colombier 		if(lab.state == BsFree)
710*5e96a66cSDavid du Colombier 			goto Found;
711*5e96a66cSDavid du Colombier 		if((lab.state&BsClosed) && lab.epochClose <= epochLow)
712*5e96a66cSDavid du Colombier 			goto Found;
713*5e96a66cSDavid du Colombier 	}
714*5e96a66cSDavid du Colombier Found:
715*5e96a66cSDavid du Colombier 	blockPut(b);
716*5e96a66cSDavid du Colombier 	b = cacheLocal(c, PartData, addr, OOverWrite);
717*5e96a66cSDavid du Colombier 	if(b == nil){
718*5e96a66cSDavid du Colombier 		fprint(2, "cacheAllocBlock: xxx3 %R\n");
719*5e96a66cSDavid du Colombier 		return nil;
720*5e96a66cSDavid du Colombier 	}
721*5e96a66cSDavid du Colombier assert(b->iostate == BioLabel || b->iostate == BioClean);
722*5e96a66cSDavid du Colombier 	fl->last = addr;
723*5e96a66cSDavid du Colombier 	lab.type = type;
724*5e96a66cSDavid du Colombier 	lab.tag = tag;
725*5e96a66cSDavid du Colombier 	lab.state = BsAlloc;
726*5e96a66cSDavid du Colombier 	lab.epoch = epoch;
727*5e96a66cSDavid du Colombier 	lab.epochClose = ~(u32int)0;
728*5e96a66cSDavid du Colombier 	if(!blockSetLabel(b, &lab)){
729*5e96a66cSDavid du Colombier 		fprint(2, "cacheAllocBlock: xxx4 %R\n");
730*5e96a66cSDavid du Colombier 		blockPut(b);
731*5e96a66cSDavid du Colombier 		return nil;
732*5e96a66cSDavid du Colombier 	}
733*5e96a66cSDavid du Colombier 	vtZeroExtend(vtType[type], b->data, 0, c->size);
734*5e96a66cSDavid du Colombier if(0)diskWrite(c->disk, b);
735*5e96a66cSDavid du Colombier 
736*5e96a66cSDavid du Colombier if(0)fprint(2, "fsAlloc %ud type=%d tag = %ux\n", addr, type, tag);
737*5e96a66cSDavid du Colombier 	lastAlloc = addr;
738*5e96a66cSDavid du Colombier 	vtUnlock(fl->lk);
739*5e96a66cSDavid du Colombier 	b->pc = getcallerpc(&c);
740*5e96a66cSDavid du Colombier 	return b;
741*5e96a66cSDavid du Colombier }
742*5e96a66cSDavid du Colombier 
743*5e96a66cSDavid du Colombier static FreeList *
744*5e96a66cSDavid du Colombier flAlloc(u32int end)
745*5e96a66cSDavid du Colombier {
746*5e96a66cSDavid du Colombier 	FreeList *fl;
747*5e96a66cSDavid du Colombier 
748*5e96a66cSDavid du Colombier 	fl = vtMemAllocZ(sizeof(*fl));
749*5e96a66cSDavid du Colombier 	fl->lk = vtLockAlloc();
750*5e96a66cSDavid du Colombier 	fl->last = end;
751*5e96a66cSDavid du Colombier 	fl->end = end;
752*5e96a66cSDavid du Colombier 	return fl;
753*5e96a66cSDavid du Colombier }
754*5e96a66cSDavid du Colombier 
755*5e96a66cSDavid du Colombier static void
756*5e96a66cSDavid du Colombier flFree(FreeList *fl)
757*5e96a66cSDavid du Colombier {
758*5e96a66cSDavid du Colombier 	vtLockFree(fl->lk);
759*5e96a66cSDavid du Colombier 	vtMemFree(fl);
760*5e96a66cSDavid du Colombier }
761*5e96a66cSDavid du Colombier 
762*5e96a66cSDavid du Colombier u32int
763*5e96a66cSDavid du Colombier cacheLocalSize(Cache *c, int part)
764*5e96a66cSDavid du Colombier {
765*5e96a66cSDavid du Colombier 	return diskSize(c->disk, part);
766*5e96a66cSDavid du Colombier }
767*5e96a66cSDavid du Colombier 
768*5e96a66cSDavid du Colombier /*
769*5e96a66cSDavid du Colombier  * Copy on write.  Copied blocks have to be marked BaCopy.
770*5e96a66cSDavid du Colombier  * See the big comment near blockRemoveLink.
771*5e96a66cSDavid du Colombier  */
772*5e96a66cSDavid du Colombier Block*
773*5e96a66cSDavid du Colombier blockCopy(Block *b, u32int tag, u32int ehi, u32int elo)
774*5e96a66cSDavid du Colombier {
775*5e96a66cSDavid du Colombier 	Block *bb, *lb;
776*5e96a66cSDavid du Colombier 	Label l;
777*5e96a66cSDavid du Colombier 
778*5e96a66cSDavid du Colombier 	assert((b->l.state&BsClosed)==0 && b->l.epoch < ehi);
779*5e96a66cSDavid du Colombier 	bb = cacheAllocBlock(b->c, b->l.type, tag, ehi, elo);
780*5e96a66cSDavid du Colombier 	if(bb == nil){
781*5e96a66cSDavid du Colombier 		blockPut(b);
782*5e96a66cSDavid du Colombier 		return nil;
783*5e96a66cSDavid du Colombier 	}
784*5e96a66cSDavid du Colombier 
785*5e96a66cSDavid du Colombier 	/*
786*5e96a66cSDavid du Colombier 	 * Change label on b to mark that we've copied it.
787*5e96a66cSDavid du Colombier 	 * This has to come after cacheAllocBlock, since we
788*5e96a66cSDavid du Colombier 	 * can't hold any labels blocks (lb) while we try to
789*5e96a66cSDavid du Colombier 	 * fetch others (in cacheAllocBlock).
790*5e96a66cSDavid du Colombier 	 */
791*5e96a66cSDavid du Colombier 	if(!(b->l.state&BsCopied) && b->part==PartData){
792*5e96a66cSDavid du Colombier 		l = b->l;
793*5e96a66cSDavid du Colombier 		l.state |= BsCopied;
794*5e96a66cSDavid du Colombier 		lb = _blockSetLabel(b, &l);
795*5e96a66cSDavid du Colombier 		if(lb == nil){
796*5e96a66cSDavid du Colombier 			/* can't set label => can't copy block */
797*5e96a66cSDavid du Colombier 			blockPut(b);
798*5e96a66cSDavid du Colombier 			l.type = BtMax;
799*5e96a66cSDavid du Colombier 			l.state = BsFree;
800*5e96a66cSDavid du Colombier 			l.epoch = 0;
801*5e96a66cSDavid du Colombier 			l.epochClose = 0;
802*5e96a66cSDavid du Colombier 			l.tag = 0;
803*5e96a66cSDavid du Colombier 			/* ignore error: block gets lost on error */
804*5e96a66cSDavid du Colombier 			blockSetLabel(bb, &l);
805*5e96a66cSDavid du Colombier 			blockPut(bb);
806*5e96a66cSDavid du Colombier 			return nil;
807*5e96a66cSDavid du Colombier 		}
808*5e96a66cSDavid du Colombier 		blockDependency(bb, lb, -1, nil);
809*5e96a66cSDavid du Colombier 		blockPut(lb);
810*5e96a66cSDavid du Colombier 	}
811*5e96a66cSDavid du Colombier 
812*5e96a66cSDavid du Colombier 	if(0){
813*5e96a66cSDavid du Colombier 		if(b->addr != NilBlock)
814*5e96a66cSDavid du Colombier 			fprint(2, "blockCopy %#ux/%ud => %#ux/%ud\n",
815*5e96a66cSDavid du Colombier 				b->addr, b->l.epoch, bb->addr, bb->l.epoch);
816*5e96a66cSDavid du Colombier 		else if(memcmp(b->score, vtZeroScore, VtScoreSize) != 0)
817*5e96a66cSDavid du Colombier 			fprint(2, "blockCopy %V => %#ux/%ud\n",
818*5e96a66cSDavid du Colombier 				b->score, bb->addr, bb->l.epoch);
819*5e96a66cSDavid du Colombier 	}
820*5e96a66cSDavid du Colombier 
821*5e96a66cSDavid du Colombier 	memmove(bb->data, b->data, b->c->size);
822*5e96a66cSDavid du Colombier 	blockDirty(bb);
823*5e96a66cSDavid du Colombier 	blockPut(b);
824*5e96a66cSDavid du Colombier 	return bb;
825*5e96a66cSDavid du Colombier }
826*5e96a66cSDavid du Colombier 
827*5e96a66cSDavid du Colombier /*
828*5e96a66cSDavid du Colombier  * The thread that has locked b may refer to it by
829*5e96a66cSDavid du Colombier  * multiple names.  Nlock counts the number of
830*5e96a66cSDavid du Colombier  * references the locking thread holds.  It will call
831*5e96a66cSDavid du Colombier  * blockPut once per reference.
832*5e96a66cSDavid du Colombier  */
833*5e96a66cSDavid du Colombier void
834*5e96a66cSDavid du Colombier blockDupLock(Block *b)
835*5e96a66cSDavid du Colombier {
836*5e96a66cSDavid du Colombier 	assert(b->nlock > 0);
837*5e96a66cSDavid du Colombier 	b->nlock++;
838*5e96a66cSDavid du Colombier }
839*5e96a66cSDavid du Colombier 
840*5e96a66cSDavid du Colombier /*
841*5e96a66cSDavid du Colombier  * we're done with the block.
842*5e96a66cSDavid du Colombier  * unlock it.  can't use it after calling this.
843*5e96a66cSDavid du Colombier  */
844*5e96a66cSDavid du Colombier void
845*5e96a66cSDavid du Colombier blockPut(Block* b)
846*5e96a66cSDavid du Colombier {
847*5e96a66cSDavid du Colombier 	Cache *c;
848*5e96a66cSDavid du Colombier 
849*5e96a66cSDavid du Colombier 	if(b == nil)
850*5e96a66cSDavid du Colombier 		return;
851*5e96a66cSDavid du Colombier 
852*5e96a66cSDavid du Colombier if(0)fprint(2, "blockPut: %d: %d %x %d %s\n", getpid(), b->part, b->addr, c->nheap, bioStr(b->iostate));
853*5e96a66cSDavid du Colombier 
854*5e96a66cSDavid du Colombier 	if(b->iostate == BioDirty)
855*5e96a66cSDavid du Colombier 		bwatchDependency(b);
856*5e96a66cSDavid du Colombier 
857*5e96a66cSDavid du Colombier 	if(--b->nlock > 0)
858*5e96a66cSDavid du Colombier 		return;
859*5e96a66cSDavid du Colombier 
860*5e96a66cSDavid du Colombier 	/*
861*5e96a66cSDavid du Colombier 	 * b->nlock should probably stay at zero while
862*5e96a66cSDavid du Colombier 	 * the block is unlocked, but diskThread and vtSleep
863*5e96a66cSDavid du Colombier 	 * conspire to assume that they can just vtLock(b->lk); blockPut(b),
864*5e96a66cSDavid du Colombier 	 * so we have to keep b->nlock set to 1 even
865*5e96a66cSDavid du Colombier 	 * when the block is unlocked.
866*5e96a66cSDavid du Colombier 	 */
867*5e96a66cSDavid du Colombier 	assert(b->nlock == 0);
868*5e96a66cSDavid du Colombier 	b->nlock = 1;
869*5e96a66cSDavid du Colombier //	b->pc = 0;
870*5e96a66cSDavid du Colombier 
871*5e96a66cSDavid du Colombier 	bwatchUnlock(b);
872*5e96a66cSDavid du Colombier 	vtUnlock(b->lk);
873*5e96a66cSDavid du Colombier 	c = b->c;
874*5e96a66cSDavid du Colombier 	vtLock(c->lk);
875*5e96a66cSDavid du Colombier 
876*5e96a66cSDavid du Colombier 	if(--b->ref > 0){
877*5e96a66cSDavid du Colombier 		vtUnlock(c->lk);
878*5e96a66cSDavid du Colombier 		return;
879*5e96a66cSDavid du Colombier 	}
880*5e96a66cSDavid du Colombier 
881*5e96a66cSDavid du Colombier 	assert(b->ref == 0);
882*5e96a66cSDavid du Colombier 	switch(b->iostate){
883*5e96a66cSDavid du Colombier 	default:
884*5e96a66cSDavid du Colombier 		b->used = c->now++;
885*5e96a66cSDavid du Colombier 		heapIns(b);
886*5e96a66cSDavid du Colombier 		break;
887*5e96a66cSDavid du Colombier 	case BioEmpty:
888*5e96a66cSDavid du Colombier 	case BioLabel:
889*5e96a66cSDavid du Colombier 		if(c->nheap == 0)
890*5e96a66cSDavid du Colombier 			b->used = c->now++;
891*5e96a66cSDavid du Colombier 		else
892*5e96a66cSDavid du Colombier 			b->used = c->heap[0]->used;
893*5e96a66cSDavid du Colombier 		heapIns(b);
894*5e96a66cSDavid du Colombier 		break;
895*5e96a66cSDavid du Colombier 	case BioDirty:
896*5e96a66cSDavid du Colombier 		break;
897*5e96a66cSDavid du Colombier 	}
898*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
899*5e96a66cSDavid du Colombier }
900*5e96a66cSDavid du Colombier 
901*5e96a66cSDavid du Colombier /*
902*5e96a66cSDavid du Colombier  * we're deleting a block; delete all the blocks it points to
903*5e96a66cSDavid du Colombier  * that are still active (i.e., not needed by snapshots).
904*5e96a66cSDavid du Colombier  */
905*5e96a66cSDavid du Colombier static void
906*5e96a66cSDavid du Colombier blockCleanup(Block *b, u32int epoch)
907*5e96a66cSDavid du Colombier {
908*5e96a66cSDavid du Colombier 	Cache *c;
909*5e96a66cSDavid du Colombier 	Block *bb;
910*5e96a66cSDavid du Colombier 	int i, n;
911*5e96a66cSDavid du Colombier 	Label l;
912*5e96a66cSDavid du Colombier 	u32int a;
913*5e96a66cSDavid du Colombier 	int type;
914*5e96a66cSDavid du Colombier 	int mode;
915*5e96a66cSDavid du Colombier 
916*5e96a66cSDavid du Colombier 	type = b->l.type;
917*5e96a66cSDavid du Colombier 	c = b->c;
918*5e96a66cSDavid du Colombier 
919*5e96a66cSDavid du Colombier 	bwatchReset(b->score);
920*5e96a66cSDavid du Colombier 
921*5e96a66cSDavid du Colombier 	blockSetIOState(b, BioClean);
922*5e96a66cSDavid du Colombier 
923*5e96a66cSDavid du Colombier 	/* do not recursively free directories */
924*5e96a66cSDavid du Colombier 	if(type == BtData || type == BtDir)
925*5e96a66cSDavid du Colombier 		return;
926*5e96a66cSDavid du Colombier 
927*5e96a66cSDavid du Colombier 	n = c->size / VtScoreSize;
928*5e96a66cSDavid du Colombier 	mode = OReadWrite;
929*5e96a66cSDavid du Colombier 	if(type-1 == BtData || type-1 == BtDir)
930*5e96a66cSDavid du Colombier 		mode = OOverWrite;
931*5e96a66cSDavid du Colombier 	for(i=0; i<n; i++){
932*5e96a66cSDavid du Colombier 		a = globalToLocal(b->data + i*VtScoreSize);
933*5e96a66cSDavid du Colombier 		if(a == NilBlock || !readLabel(c, &l, a))
934*5e96a66cSDavid du Colombier 			continue;
935*5e96a66cSDavid du Colombier 		if((l.state&BsClosed) || l.epoch != epoch)
936*5e96a66cSDavid du Colombier 			continue;
937*5e96a66cSDavid du Colombier 		bb = cacheLocalData(c, a, type-1, b->l.tag, mode, 0);
938*5e96a66cSDavid du Colombier 		if(bb == nil)
939*5e96a66cSDavid du Colombier 			continue;
940*5e96a66cSDavid du Colombier 		if((bb->l.state&BsClosed) || bb->l.epoch != epoch){
941*5e96a66cSDavid du Colombier 			fprint(2, "cleanupBlock: block %ud changed underfoot! expected %L got %L\n",
942*5e96a66cSDavid du Colombier 				a, &l, &bb->l);
943*5e96a66cSDavid du Colombier 			blockPut(bb);
944*5e96a66cSDavid du Colombier 			continue;
945*5e96a66cSDavid du Colombier 		}
946*5e96a66cSDavid du Colombier 		blockCleanup(bb, epoch);
947*5e96a66cSDavid du Colombier 		l.type = BtMax;
948*5e96a66cSDavid du Colombier 		l.epoch = epoch;
949*5e96a66cSDavid du Colombier 		l.epochClose = 0;
950*5e96a66cSDavid du Colombier 		l.state = BsFree;
951*5e96a66cSDavid du Colombier 		l.tag = 0;
952*5e96a66cSDavid du Colombier 		blockSetLabel(bb, &l);
953*5e96a66cSDavid du Colombier 		blockPut(bb);
954*5e96a66cSDavid du Colombier 	}
955*5e96a66cSDavid du Colombier }
956*5e96a66cSDavid du Colombier 
957*5e96a66cSDavid du Colombier /*
958*5e96a66cSDavid du Colombier  * We don't need the block at addr anymore for the active file system.
959*5e96a66cSDavid du Colombier  * If we don't need it for the snapshots, remove it completely.
960*5e96a66cSDavid du Colombier  * Epoch is the epoch during which we got rid of the block.
961*5e96a66cSDavid du Colombier  * See blockRemoveLink for more.
962*5e96a66cSDavid du Colombier  */
963*5e96a66cSDavid du Colombier static int
964*5e96a66cSDavid du Colombier unlinkBlock(Cache *c, u32int addr, int type, u32int tag, u32int epoch)
965*5e96a66cSDavid du Colombier {
966*5e96a66cSDavid du Colombier 	Block *b;
967*5e96a66cSDavid du Colombier 	Label l;
968*5e96a66cSDavid du Colombier 
969*5e96a66cSDavid du Colombier 	if(addr == NilBlock)
970*5e96a66cSDavid du Colombier 		return 1;
971*5e96a66cSDavid du Colombier 
972*5e96a66cSDavid du Colombier //fprint(2, "unlinkBlock %#ux\n", addr);
973*5e96a66cSDavid du Colombier 	b = cacheLocalData(c, addr, type, tag, OReadOnly, 0);
974*5e96a66cSDavid du Colombier 	if(b == nil)
975*5e96a66cSDavid du Colombier 		return 0;
976*5e96a66cSDavid du Colombier 	if(b->l.epoch > epoch){
977*5e96a66cSDavid du Colombier fprint(2, "unlinkBlock: strange epoch :%ud %ud\n", b->l.epoch, epoch);
978*5e96a66cSDavid du Colombier 		blockPut(b);
979*5e96a66cSDavid du Colombier 		return 0;
980*5e96a66cSDavid du Colombier 	}
981*5e96a66cSDavid du Colombier 
982*5e96a66cSDavid du Colombier 	l = b->l;
983*5e96a66cSDavid du Colombier 	if((b->l.state&BsClosed)==0 && b->l.epoch==epoch){
984*5e96a66cSDavid du Colombier 		l.state = BsFree;
985*5e96a66cSDavid du Colombier 		l.type = BtMax;
986*5e96a66cSDavid du Colombier 		l.tag = 0;
987*5e96a66cSDavid du Colombier 		l.epoch = 0;
988*5e96a66cSDavid du Colombier 		l.epochClose = 0;
989*5e96a66cSDavid du Colombier 		blockCleanup(b, epoch);
990*5e96a66cSDavid du Colombier 	}else{
991*5e96a66cSDavid du Colombier 		l.state |= BsClosed;
992*5e96a66cSDavid du Colombier 		l.epochClose = epoch;
993*5e96a66cSDavid du Colombier 	}
994*5e96a66cSDavid du Colombier 	blockSetLabel(b, &l);
995*5e96a66cSDavid du Colombier 	blockPut(b);
996*5e96a66cSDavid du Colombier 	return 1;
997*5e96a66cSDavid du Colombier }
998*5e96a66cSDavid du Colombier 
999*5e96a66cSDavid du Colombier /*
1000*5e96a66cSDavid du Colombier  * try to allocate a BList so we can record that b must
1001*5e96a66cSDavid du Colombier  * be written out before some other block.
1002*5e96a66cSDavid du Colombier  * if can't find a BList, write b out instead and return nil.
1003*5e96a66cSDavid du Colombier  */
1004*5e96a66cSDavid du Colombier static BList *
1005*5e96a66cSDavid du Colombier blistAlloc(Block *b)
1006*5e96a66cSDavid du Colombier {
1007*5e96a66cSDavid du Colombier 	Cache *c;
1008*5e96a66cSDavid du Colombier 	BList *p;
1009*5e96a66cSDavid du Colombier 
1010*5e96a66cSDavid du Colombier 	/*
1011*5e96a66cSDavid du Colombier 	 * It's possible that when we marked b dirty, there were
1012*5e96a66cSDavid du Colombier 	 * too many dirty blocks so we just wrote b there and then.
1013*5e96a66cSDavid du Colombier 	 * So b might not be dirty.  If it's not, no need to worry
1014*5e96a66cSDavid du Colombier 	 * about recording the write constraint.
1015*5e96a66cSDavid du Colombier 	 *
1016*5e96a66cSDavid du Colombier 	 * BlockRemoveLink depends on the fact that if blistAlloc
1017*5e96a66cSDavid du Colombier 	 * returns non-nil, b really is dirty.
1018*5e96a66cSDavid du Colombier 	 */
1019*5e96a66cSDavid du Colombier 	if(b->iostate != BioDirty){
1020*5e96a66cSDavid du Colombier 		assert(b->iostate == BioClean);
1021*5e96a66cSDavid du Colombier 		return nil;
1022*5e96a66cSDavid du Colombier 	}
1023*5e96a66cSDavid du Colombier 
1024*5e96a66cSDavid du Colombier 	/*
1025*5e96a66cSDavid du Colombier 	 * Easy: maybe there's a free list left.
1026*5e96a66cSDavid du Colombier 	 */
1027*5e96a66cSDavid du Colombier 	c = b->c;
1028*5e96a66cSDavid du Colombier 	vtLock(c->lk);
1029*5e96a66cSDavid du Colombier 	if(c->blfree){
1030*5e96a66cSDavid du Colombier 	HaveBlFree:
1031*5e96a66cSDavid du Colombier 		p = c->blfree;
1032*5e96a66cSDavid du Colombier 		c->blfree = p->next;
1033*5e96a66cSDavid du Colombier 		vtUnlock(c->lk);
1034*5e96a66cSDavid du Colombier 		return p;
1035*5e96a66cSDavid du Colombier 	}
1036*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
1037*5e96a66cSDavid du Colombier 
1038*5e96a66cSDavid du Colombier 	/*
1039*5e96a66cSDavid du Colombier 	 * No free BLists.  What are our options?
1040*5e96a66cSDavid du Colombier 	 */
1041*5e96a66cSDavid du Colombier 
1042*5e96a66cSDavid du Colombier 	/* Block has no priors? Just write it. */
1043*5e96a66cSDavid du Colombier 	if(b->prior == nil){
1044*5e96a66cSDavid du Colombier 		diskWrite(c->disk, b);
1045*5e96a66cSDavid du Colombier 		while(b->iostate != BioClean)
1046*5e96a66cSDavid du Colombier 			vtSleep(b->ioready);
1047*5e96a66cSDavid du Colombier 		return nil;
1048*5e96a66cSDavid du Colombier 	}
1049*5e96a66cSDavid du Colombier 
1050*5e96a66cSDavid du Colombier 	/*
1051*5e96a66cSDavid du Colombier 	 * Wake the flush thread, which will hopefully free up
1052*5e96a66cSDavid du Colombier 	 * some BLists for us.  We used to flush a block from
1053*5e96a66cSDavid du Colombier 	 * our own prior list and reclaim that BList, but this is
1054*5e96a66cSDavid du Colombier 	 * a no-no: some of the blocks on our prior list may
1055*5e96a66cSDavid du Colombier 	 * be locked by our caller.  Or maybe their label blocks
1056*5e96a66cSDavid du Colombier 	 * are locked by our caller.  In any event, it's too hard
1057*5e96a66cSDavid du Colombier 	 * to make sure we can do I/O for ourselves.  Instead,
1058*5e96a66cSDavid du Colombier 	 * we assume the flush thread will find something.
1059*5e96a66cSDavid du Colombier 	 * (The flush thread never blocks waiting for a block,
1060*5e96a66cSDavid du Colombier 	 * so it won't deadlock like we will.)
1061*5e96a66cSDavid du Colombier 	 */
1062*5e96a66cSDavid du Colombier 	vtLock(c->lk);
1063*5e96a66cSDavid du Colombier 	while(c->blfree == nil){
1064*5e96a66cSDavid du Colombier 		vtWakeup(c->flush);
1065*5e96a66cSDavid du Colombier 		vtSleep(c->blrend);
1066*5e96a66cSDavid du Colombier 	}
1067*5e96a66cSDavid du Colombier 	goto HaveBlFree;
1068*5e96a66cSDavid du Colombier }
1069*5e96a66cSDavid du Colombier 
1070*5e96a66cSDavid du Colombier void
1071*5e96a66cSDavid du Colombier blistFree(Cache *c, BList *bl)
1072*5e96a66cSDavid du Colombier {
1073*5e96a66cSDavid du Colombier 	vtLock(c->lk);
1074*5e96a66cSDavid du Colombier 	bl->next = c->blfree;
1075*5e96a66cSDavid du Colombier 	c->blfree = bl;
1076*5e96a66cSDavid du Colombier 	vtWakeup(c->blrend);
1077*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
1078*5e96a66cSDavid du Colombier }
1079*5e96a66cSDavid du Colombier 
1080*5e96a66cSDavid du Colombier /*
1081*5e96a66cSDavid du Colombier  * Flush b or one of the blocks it depends on.
1082*5e96a66cSDavid du Colombier  */
1083*5e96a66cSDavid du Colombier void
1084*5e96a66cSDavid du Colombier blockFlush(Block *b)
1085*5e96a66cSDavid du Colombier {
1086*5e96a66cSDavid du Colombier 	int first, nlock;
1087*5e96a66cSDavid du Colombier 	BList *p, **pp;
1088*5e96a66cSDavid du Colombier 	Block *bb;
1089*5e96a66cSDavid du Colombier 	Cache *c;
1090*5e96a66cSDavid du Colombier 
1091*5e96a66cSDavid du Colombier //fprint(2, "blockFlush %p\n", b);
1092*5e96a66cSDavid du Colombier 
1093*5e96a66cSDavid du Colombier 	c = b->c;
1094*5e96a66cSDavid du Colombier 
1095*5e96a66cSDavid du Colombier 	first = 1;
1096*5e96a66cSDavid du Colombier 	pp = &b->prior;
1097*5e96a66cSDavid du Colombier 	for(p=*pp; p; p=*pp){
1098*5e96a66cSDavid du Colombier 		bb = cacheLocalLookup(c, p->part, p->addr, p->vers);
1099*5e96a66cSDavid du Colombier 		if(bb == nil){
1100*5e96a66cSDavid du Colombier 			*pp = p->next;
1101*5e96a66cSDavid du Colombier 			blistFree(c, p);
1102*5e96a66cSDavid du Colombier 			continue;
1103*5e96a66cSDavid du Colombier 		}
1104*5e96a66cSDavid du Colombier 		if(!first)
1105*5e96a66cSDavid du Colombier 			blockPut(b);
1106*5e96a66cSDavid du Colombier 		first = 0;
1107*5e96a66cSDavid du Colombier 		b = bb;
1108*5e96a66cSDavid du Colombier 		pp = &b->prior;
1109*5e96a66cSDavid du Colombier 	}
1110*5e96a66cSDavid du Colombier 
1111*5e96a66cSDavid du Colombier 	/*
1112*5e96a66cSDavid du Colombier 	 * If b->nlock > 1, the block is aliased within
1113*5e96a66cSDavid du Colombier 	 * a single thread.  That thread is us, and it's
1114*5e96a66cSDavid du Colombier 	 * the block that was passed in (rather than a prior).
1115*5e96a66cSDavid du Colombier 	 * DiskWrite does some funny stuff with VtLock
1116*5e96a66cSDavid du Colombier 	 * and blockPut that basically assumes b->nlock==1.
1117*5e96a66cSDavid du Colombier 	 * We humor diskWrite by temporarily setting
1118*5e96a66cSDavid du Colombier 	 * nlock to 1.  This needs to be revisited.  (TODO)
1119*5e96a66cSDavid du Colombier 	 */
1120*5e96a66cSDavid du Colombier 	nlock = b->nlock;
1121*5e96a66cSDavid du Colombier 	if(nlock > 1){
1122*5e96a66cSDavid du Colombier 		assert(first);
1123*5e96a66cSDavid du Colombier 		b->nlock = 1;
1124*5e96a66cSDavid du Colombier 	}
1125*5e96a66cSDavid du Colombier 	diskWrite(c->disk, b);
1126*5e96a66cSDavid du Colombier 	while(b->iostate != BioClean)
1127*5e96a66cSDavid du Colombier 		vtSleep(b->ioready);
1128*5e96a66cSDavid du Colombier 	b->nlock = nlock;
1129*5e96a66cSDavid du Colombier 	if(!first)
1130*5e96a66cSDavid du Colombier 		blockPut(b);
1131*5e96a66cSDavid du Colombier }
1132*5e96a66cSDavid du Colombier 
1133*5e96a66cSDavid du Colombier /*
1134*5e96a66cSDavid du Colombier  * record that bb must be written out before b.
1135*5e96a66cSDavid du Colombier  * if index is given, we're about to overwrite the score
1136*5e96a66cSDavid du Colombier  * at that index in the block.  save the old value so we
1137*5e96a66cSDavid du Colombier  * can write a safer ``old'' version of the block if pressed.
1138*5e96a66cSDavid du Colombier  */
1139*5e96a66cSDavid du Colombier void
1140*5e96a66cSDavid du Colombier blockDependency(Block *b, Block *bb, int index, uchar *score)
1141*5e96a66cSDavid du Colombier {
1142*5e96a66cSDavid du Colombier 	BList *p;
1143*5e96a66cSDavid du Colombier 
1144*5e96a66cSDavid du Colombier 	if(bb->iostate == BioClean)
1145*5e96a66cSDavid du Colombier 		return;
1146*5e96a66cSDavid du Colombier 
1147*5e96a66cSDavid du Colombier 	assert(bb->iostate == BioDirty);
1148*5e96a66cSDavid du Colombier 
1149*5e96a66cSDavid du Colombier 	p = blistAlloc(bb);
1150*5e96a66cSDavid du Colombier 	if(p == nil)
1151*5e96a66cSDavid du Colombier 		return;
1152*5e96a66cSDavid du Colombier 
1153*5e96a66cSDavid du Colombier if(0)fprint(2, "%d:%x:%d depends on %d:%x:%d\n", b->part, b->addr, b->l.type, bb->part, bb->addr, bb->l.type);
1154*5e96a66cSDavid du Colombier 
1155*5e96a66cSDavid du Colombier 	p->part = bb->part;
1156*5e96a66cSDavid du Colombier 	p->addr = bb->addr;
1157*5e96a66cSDavid du Colombier 	p->type = bb->l.type;
1158*5e96a66cSDavid du Colombier 	p->vers = bb->vers;
1159*5e96a66cSDavid du Colombier 	p->index = index;
1160*5e96a66cSDavid du Colombier 	if(p->index >= 0)
1161*5e96a66cSDavid du Colombier 		memmove(p->score, score, VtScoreSize);
1162*5e96a66cSDavid du Colombier 	p->next = b->prior;
1163*5e96a66cSDavid du Colombier 	b->prior = p;
1164*5e96a66cSDavid du Colombier }
1165*5e96a66cSDavid du Colombier 
1166*5e96a66cSDavid du Colombier /*
1167*5e96a66cSDavid du Colombier  * Mark an in-memory block as dirty.  If there are too many
1168*5e96a66cSDavid du Colombier  * dirty blocks, start writing some out to disk.  If there are way
1169*5e96a66cSDavid du Colombier  * too many dirty blocks, write this one out too.
1170*5e96a66cSDavid du Colombier  *
1171*5e96a66cSDavid du Colombier  * Note that since we might call blockFlush, which walks
1172*5e96a66cSDavid du Colombier  * the prior list, we can't call blockDirty while holding a lock
1173*5e96a66cSDavid du Colombier  * on any of our priors.  This can be tested by recompiling
1174*5e96a66cSDavid du Colombier  * with flush always set to 1 below.
1175*5e96a66cSDavid du Colombier  */
1176*5e96a66cSDavid du Colombier int
1177*5e96a66cSDavid du Colombier blockDirty(Block *b)
1178*5e96a66cSDavid du Colombier {
1179*5e96a66cSDavid du Colombier 	Cache *c;
1180*5e96a66cSDavid du Colombier 	int flush;
1181*5e96a66cSDavid du Colombier 
1182*5e96a66cSDavid du Colombier 	c = b->c;
1183*5e96a66cSDavid du Colombier 
1184*5e96a66cSDavid du Colombier 	assert(b->part != PartVenti);
1185*5e96a66cSDavid du Colombier 
1186*5e96a66cSDavid du Colombier 	if(b->iostate == BioDirty)
1187*5e96a66cSDavid du Colombier 		return 1;
1188*5e96a66cSDavid du Colombier 	assert(b->iostate == BioClean);
1189*5e96a66cSDavid du Colombier 	b->iostate = BioDirty;
1190*5e96a66cSDavid du Colombier 
1191*5e96a66cSDavid du Colombier 	vtLock(c->lk);
1192*5e96a66cSDavid du Colombier 	c->ndirty++;
1193*5e96a66cSDavid du Colombier 	if(c->ndirty > (c->maxdirty>>1))
1194*5e96a66cSDavid du Colombier 		vtWakeup(c->flush);
1195*5e96a66cSDavid du Colombier 	flush = c->ndirty > c->maxdirty;
1196*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
1197*5e96a66cSDavid du Colombier 
1198*5e96a66cSDavid du Colombier 	if(flush)
1199*5e96a66cSDavid du Colombier 		blockFlush(b);
1200*5e96a66cSDavid du Colombier 
1201*5e96a66cSDavid du Colombier 	return 1;
1202*5e96a66cSDavid du Colombier }
1203*5e96a66cSDavid du Colombier 
1204*5e96a66cSDavid du Colombier /*
1205*5e96a66cSDavid du Colombier  * Block b once pointed at the block bb at addr/type/tag, but no longer does.
1206*5e96a66cSDavid du Colombier  *
1207*5e96a66cSDavid du Colombier  * The file system maintains the following invariants (i-iv checked by flchk):
1208*5e96a66cSDavid du Colombier  *
1209*5e96a66cSDavid du Colombier  * (i) b.e in [bb.e, bb.eClose)
1210*5e96a66cSDavid du Colombier  * (ii) if b.e==bb.e,  then no other b' in e points at bb.
1211*5e96a66cSDavid du Colombier  * (iii) if !(b.state&Copied) and b.e==bb.e then no other b' points at bb.
1212*5e96a66cSDavid du Colombier  * (iv) if b is active then no other active b' points at bb.
1213*5e96a66cSDavid du Colombier  * (v) if b is a past life of b' then only one of b and b' is active (too hard to check)
1214*5e96a66cSDavid du Colombier  *
1215*5e96a66cSDavid du Colombier  * The file system initially satisfies these invariants, and we can verify that
1216*5e96a66cSDavid du Colombier  * the various file system operations maintain them.  See fossil.invariants.
1217*5e96a66cSDavid du Colombier  *
1218*5e96a66cSDavid du Colombier  * Condition (i) lets us reclaim blocks once the low epoch is greater
1219*5e96a66cSDavid du Colombier  * than epochClose.
1220*5e96a66cSDavid du Colombier  *
1221*5e96a66cSDavid du Colombier  * If the condition in (iii) is satisfied, then this is the only pointer to bb,
1222*5e96a66cSDavid du Colombier  * so bb can be reclaimed once b has been written to disk.  blockRemoveLink
1223*5e96a66cSDavid du Colombier  * checks !(b.state&Copied) as an optimization.  UnlinkBlock and blockCleanup
1224*5e96a66cSDavid du Colombier  * will check the conditions again for each block they consider.
1225*5e96a66cSDavid du Colombier  *
1226*5e96a66cSDavid du Colombier  * 12/28/2002 01:11 RSC BUG
1227*5e96a66cSDavid du Colombier  * When Entry structures are changed, most code does (for example):
1228*5e96a66cSDavid du Colombier  *
1229*5e96a66cSDavid du Colombier  *	oe = e;
1230*5e96a66cSDavid du Colombier  *	memset(&e, 0, sizeof e);
1231*5e96a66cSDavid du Colombier  *	entryPack(&e, b->data, index);
1232*5e96a66cSDavid du Colombier  *	blockDirty(b);
1233*5e96a66cSDavid du Colombier  *	blockDependency(b, block referenced by new e);
1234*5e96a66cSDavid du Colombier  *	addr = globalToLocal(oe.score);
1235*5e96a66cSDavid du Colombier  *	if(addr != NilBlock)
1236*5e96a66cSDavid du Colombier  *		blockRemoveLink(b, addr, entryType(&oe), oe.tag);
1237*5e96a66cSDavid du Colombier  *
1238*5e96a66cSDavid du Colombier  * This is wrong if there is already a different dependency for that entry
1239*5e96a66cSDavid du Colombier  * and the entries have different types (different heights of the hash tree).
1240*5e96a66cSDavid du Colombier  * Because the dependency only records the block address and not the
1241*5e96a66cSDavid du Colombier  * entry type, putting the old block address into the new entry results in
1242*5e96a66cSDavid du Colombier  * a bogus entry structure.  blockRollback catches this in an assert failure.
1243*5e96a66cSDavid du Colombier  * I think the solution is to record the entry type and tag in the BList structure,
1244*5e96a66cSDavid du Colombier  * but I want to mull it over a bit longer.
1245*5e96a66cSDavid du Colombier  *
1246*5e96a66cSDavid du Colombier  * In two and a half months running the system I have seen exactly one
1247*5e96a66cSDavid du Colombier  * crash due to this bug.
1248*5e96a66cSDavid du Colombier  */
1249*5e96a66cSDavid du Colombier int
1250*5e96a66cSDavid du Colombier blockRemoveLink(Block *b, u32int addr, int type, u32int tag)
1251*5e96a66cSDavid du Colombier {
1252*5e96a66cSDavid du Colombier 	BList *bl;
1253*5e96a66cSDavid du Colombier 	BList *p, **pp;
1254*5e96a66cSDavid du Colombier 	Cache *c;
1255*5e96a66cSDavid du Colombier 
1256*5e96a66cSDavid du Colombier 	c = b->c;
1257*5e96a66cSDavid du Colombier 
1258*5e96a66cSDavid du Colombier 	/* remove unlinked block from prior list */
1259*5e96a66cSDavid du Colombier 	pp = &b->prior;
1260*5e96a66cSDavid du Colombier 	for(p=*pp; p; p=*pp){
1261*5e96a66cSDavid du Colombier 		if(p->part != PartData || p->addr != addr){
1262*5e96a66cSDavid du Colombier 			pp = &p->next;
1263*5e96a66cSDavid du Colombier 			continue;
1264*5e96a66cSDavid du Colombier 		}
1265*5e96a66cSDavid du Colombier 		*pp = p->next;
1266*5e96a66cSDavid du Colombier 		blistFree(c, p);
1267*5e96a66cSDavid du Colombier 	}
1268*5e96a66cSDavid du Colombier 
1269*5e96a66cSDavid du Colombier 	/* if b has been copied, can't reclaim blocks it points at. */
1270*5e96a66cSDavid du Colombier 	if(b->l.state & BsCopied)
1271*5e96a66cSDavid du Colombier 		return 0;
1272*5e96a66cSDavid du Colombier 
1273*5e96a66cSDavid du Colombier 	bl = blistAlloc(b);
1274*5e96a66cSDavid du Colombier 	if(bl == nil)
1275*5e96a66cSDavid du Colombier 		return unlinkBlock(b->c, addr, type, tag, b->l.epoch);
1276*5e96a66cSDavid du Colombier 
1277*5e96a66cSDavid du Colombier 	/*
1278*5e96a66cSDavid du Colombier 	 * Because bl != nil, we know b is dirty.
1279*5e96a66cSDavid du Colombier 	 * (Linking b->uhead onto a clean block is
1280*5e96a66cSDavid du Colombier 	 * counterproductive, since we only look at
1281*5e96a66cSDavid du Colombier 	 * b->uhead when a block transitions from
1282*5e96a66cSDavid du Colombier 	 * dirty to clean.)
1283*5e96a66cSDavid du Colombier 	 */
1284*5e96a66cSDavid du Colombier 	assert(b->iostate == BioDirty);
1285*5e96a66cSDavid du Colombier 
1286*5e96a66cSDavid du Colombier 	bl->part = PartData;
1287*5e96a66cSDavid du Colombier 	bl->addr = addr;
1288*5e96a66cSDavid du Colombier 	bl->type = type;
1289*5e96a66cSDavid du Colombier 	bl->tag = tag;
1290*5e96a66cSDavid du Colombier 	bl->epoch = b->l.epoch;
1291*5e96a66cSDavid du Colombier 	if(b->uhead == nil)
1292*5e96a66cSDavid du Colombier 		b->uhead = bl;
1293*5e96a66cSDavid du Colombier 	else
1294*5e96a66cSDavid du Colombier 		b->utail->next = bl;
1295*5e96a66cSDavid du Colombier 	b->utail = bl;
1296*5e96a66cSDavid du Colombier 	bl->next = nil;
1297*5e96a66cSDavid du Colombier 	return 1;
1298*5e96a66cSDavid du Colombier }
1299*5e96a66cSDavid du Colombier 
1300*5e96a66cSDavid du Colombier /*
1301*5e96a66cSDavid du Colombier  * set the label associated with a block.
1302*5e96a66cSDavid du Colombier  */
1303*5e96a66cSDavid du Colombier Block*
1304*5e96a66cSDavid du Colombier _blockSetLabel(Block *b, Label *l)
1305*5e96a66cSDavid du Colombier {
1306*5e96a66cSDavid du Colombier 	int lpb;
1307*5e96a66cSDavid du Colombier 	Block *bb;
1308*5e96a66cSDavid du Colombier 	u32int a;
1309*5e96a66cSDavid du Colombier 	Cache *c;
1310*5e96a66cSDavid du Colombier 
1311*5e96a66cSDavid du Colombier 	c = b->c;
1312*5e96a66cSDavid du Colombier 
1313*5e96a66cSDavid du Colombier 	assert(b->part == PartData);
1314*5e96a66cSDavid du Colombier 	assert(b->iostate == BioLabel || b->iostate == BioClean || b->iostate == BioDirty);
1315*5e96a66cSDavid du Colombier 	lpb = c->size / LabelSize;
1316*5e96a66cSDavid du Colombier 	a = b->addr / lpb;
1317*5e96a66cSDavid du Colombier 	bb = cacheLocal(c, PartLabel, a, OReadWrite);
1318*5e96a66cSDavid du Colombier 	if(bb == nil){
1319*5e96a66cSDavid du Colombier 		blockPut(b);
1320*5e96a66cSDavid du Colombier 		return nil;
1321*5e96a66cSDavid du Colombier 	}
1322*5e96a66cSDavid du Colombier 	b->l = *l;
1323*5e96a66cSDavid du Colombier 	labelPack(l, bb->data, b->addr%lpb);
1324*5e96a66cSDavid du Colombier 	blockDirty(bb);
1325*5e96a66cSDavid du Colombier 	return bb;
1326*5e96a66cSDavid du Colombier }
1327*5e96a66cSDavid du Colombier 
1328*5e96a66cSDavid du Colombier int
1329*5e96a66cSDavid du Colombier blockSetLabel(Block *b, Label *l)
1330*5e96a66cSDavid du Colombier {
1331*5e96a66cSDavid du Colombier 	Block *lb;
1332*5e96a66cSDavid du Colombier 	Label oldl;
1333*5e96a66cSDavid du Colombier 
1334*5e96a66cSDavid du Colombier 	oldl = b->l;
1335*5e96a66cSDavid du Colombier 	lb = _blockSetLabel(b, l);
1336*5e96a66cSDavid du Colombier 	if(lb == nil)
1337*5e96a66cSDavid du Colombier 		return 0;
1338*5e96a66cSDavid du Colombier 
1339*5e96a66cSDavid du Colombier 	/*
1340*5e96a66cSDavid du Colombier 	 * If we're allocating the block, make sure the label (bl)
1341*5e96a66cSDavid du Colombier 	 * goes to disk before the data block (b) itself.  This is to help
1342*5e96a66cSDavid du Colombier 	 * the blocks that in turn depend on b.
1343*5e96a66cSDavid du Colombier 	 *
1344*5e96a66cSDavid du Colombier 	 * Suppose bx depends on (must be written out after) b.
1345*5e96a66cSDavid du Colombier 	 * Once we write b we'll think it's safe to write bx.
1346*5e96a66cSDavid du Colombier 	 * Bx can't get at b unless it has a valid label, though.
1347*5e96a66cSDavid du Colombier 	 *
1348*5e96a66cSDavid du Colombier 	 * Allocation is the only case in which having a current label
1349*5e96a66cSDavid du Colombier 	 * is vital because:
1350*5e96a66cSDavid du Colombier 	 *
1351*5e96a66cSDavid du Colombier 	 *	- l.type is set at allocation and never changes.
1352*5e96a66cSDavid du Colombier 	 *	- l.tag is set at allocation and never changes.
1353*5e96a66cSDavid du Colombier 	 *	- l.state is not checked when we load blocks.
1354*5e96a66cSDavid du Colombier 	 *	- the archiver cares deeply about l.state being
1355*5e96a66cSDavid du Colombier 	 *		BaActive vs. BaCopied, but that's handled
1356*5e96a66cSDavid du Colombier 	 *		by direct calls to _blockSetLabel.
1357*5e96a66cSDavid du Colombier 	 */
1358*5e96a66cSDavid du Colombier 
1359*5e96a66cSDavid du Colombier 	if(oldl.state == BsFree)
1360*5e96a66cSDavid du Colombier 		blockDependency(b, lb, -1, nil);
1361*5e96a66cSDavid du Colombier 	blockPut(lb);
1362*5e96a66cSDavid du Colombier 	return 1;
1363*5e96a66cSDavid du Colombier }
1364*5e96a66cSDavid du Colombier 
1365*5e96a66cSDavid du Colombier /*
1366*5e96a66cSDavid du Colombier  * We've decided to write out b.
1367*5e96a66cSDavid du Colombier  * Maybe b has some pointers to blocks
1368*5e96a66cSDavid du Colombier  * that haven't yet been written to disk.
1369*5e96a66cSDavid du Colombier  * If so, construct a slightly out-of-date
1370*5e96a66cSDavid du Colombier  * copy of b that is safe to write out.
1371*5e96a66cSDavid du Colombier  * (diskThread will make sure the block
1372*5e96a66cSDavid du Colombier  * remains marked as dirty.)
1373*5e96a66cSDavid du Colombier  */
1374*5e96a66cSDavid du Colombier uchar *
1375*5e96a66cSDavid du Colombier blockRollback(Block *b, uchar *buf)
1376*5e96a66cSDavid du Colombier {
1377*5e96a66cSDavid du Colombier 	u32int addr;
1378*5e96a66cSDavid du Colombier 	BList *p;
1379*5e96a66cSDavid du Colombier 	Entry e;
1380*5e96a66cSDavid du Colombier 	Super super;
1381*5e96a66cSDavid du Colombier 
1382*5e96a66cSDavid du Colombier 	/* easy case */
1383*5e96a66cSDavid du Colombier 	if(b->prior == nil)
1384*5e96a66cSDavid du Colombier 		return b->data;
1385*5e96a66cSDavid du Colombier 
1386*5e96a66cSDavid du Colombier 	memmove(buf, b->data, b->c->size);
1387*5e96a66cSDavid du Colombier 	for(p=b->prior; p; p=p->next){
1388*5e96a66cSDavid du Colombier 		/*
1389*5e96a66cSDavid du Colombier 		 * we know p->index >= 0 because blockWrite has vetted this block for us.
1390*5e96a66cSDavid du Colombier 		 */
1391*5e96a66cSDavid du Colombier 		assert(p->index >= 0);
1392*5e96a66cSDavid du Colombier 		assert(b->part == PartSuper || (b->part == PartData && b->l.type != BtData));
1393*5e96a66cSDavid du Colombier 		if(b->part == PartSuper){
1394*5e96a66cSDavid du Colombier 			assert(p->index == 0);
1395*5e96a66cSDavid du Colombier 			superUnpack(&super, buf);
1396*5e96a66cSDavid du Colombier 			addr = globalToLocal(p->score);
1397*5e96a66cSDavid du Colombier 			if(addr == NilBlock){
1398*5e96a66cSDavid du Colombier 				fprint(2, "rolling back super block: bad replacement addr %V\n", p->score);
1399*5e96a66cSDavid du Colombier 				abort();
1400*5e96a66cSDavid du Colombier 			}
1401*5e96a66cSDavid du Colombier 			super.active = addr;
1402*5e96a66cSDavid du Colombier 			superPack(&super, buf);
1403*5e96a66cSDavid du Colombier 			continue;
1404*5e96a66cSDavid du Colombier 		}
1405*5e96a66cSDavid du Colombier 		if(b->l.type != BtDir){
1406*5e96a66cSDavid du Colombier 			memmove(buf+p->index*VtScoreSize, p->score, VtScoreSize);
1407*5e96a66cSDavid du Colombier 			continue;
1408*5e96a66cSDavid du Colombier 		}
1409*5e96a66cSDavid du Colombier 		entryUnpack(&e, buf, p->index);
1410*5e96a66cSDavid du Colombier 		assert(entryType(&e) == p->type);
1411*5e96a66cSDavid du Colombier 		memmove(e.score, p->score, VtScoreSize);
1412*5e96a66cSDavid du Colombier 		if(globalToLocal(p->score) == NilBlock){
1413*5e96a66cSDavid du Colombier 			e.flags &= ~VtEntryLocal;
1414*5e96a66cSDavid du Colombier 			e.tag = 0;
1415*5e96a66cSDavid du Colombier 			e.snap = 0;
1416*5e96a66cSDavid du Colombier 		}
1417*5e96a66cSDavid du Colombier 		entryPack(&e, buf, p->index);
1418*5e96a66cSDavid du Colombier 	}
1419*5e96a66cSDavid du Colombier 	return buf;
1420*5e96a66cSDavid du Colombier }
1421*5e96a66cSDavid du Colombier 
1422*5e96a66cSDavid du Colombier /*
1423*5e96a66cSDavid du Colombier  * Try to write block b.
1424*5e96a66cSDavid du Colombier  * If b depends on other blocks:
1425*5e96a66cSDavid du Colombier  *
1426*5e96a66cSDavid du Colombier  *	If the block has been written out, remove the dependency.
1427*5e96a66cSDavid du Colombier  *	If we know how to write out an old version of b that doesn't
1428*5e96a66cSDavid du Colombier  *		depend on it, do that.
1429*5e96a66cSDavid du Colombier  *
1430*5e96a66cSDavid du Colombier  *	Otherwise, bail.
1431*5e96a66cSDavid du Colombier  */
1432*5e96a66cSDavid du Colombier int
1433*5e96a66cSDavid du Colombier blockWrite(Block *b)
1434*5e96a66cSDavid du Colombier {
1435*5e96a66cSDavid du Colombier 	Cache *c;
1436*5e96a66cSDavid du Colombier 	BList *p, **pp;
1437*5e96a66cSDavid du Colombier 	Block *bb;
1438*5e96a66cSDavid du Colombier 	int lockfail;
1439*5e96a66cSDavid du Colombier 
1440*5e96a66cSDavid du Colombier 	c = b->c;
1441*5e96a66cSDavid du Colombier 
1442*5e96a66cSDavid du Colombier 	if(b->iostate != BioDirty)
1443*5e96a66cSDavid du Colombier 		return 1;
1444*5e96a66cSDavid du Colombier 
1445*5e96a66cSDavid du Colombier 	pp = &b->prior;
1446*5e96a66cSDavid du Colombier 	for(p=*pp; p; p=*pp){
1447*5e96a66cSDavid du Colombier 		bb = _cacheLocalLookup(c, p->part, p->addr, p->vers, 0, &lockfail);
1448*5e96a66cSDavid du Colombier 		if(bb == nil){
1449*5e96a66cSDavid du Colombier 			if(lockfail)
1450*5e96a66cSDavid du Colombier 				return 0;
1451*5e96a66cSDavid du Colombier 			/* block must have been written already */
1452*5e96a66cSDavid du Colombier 			*pp = p->next;
1453*5e96a66cSDavid du Colombier 			blistFree(c, p);
1454*5e96a66cSDavid du Colombier 			continue;
1455*5e96a66cSDavid du Colombier 		}
1456*5e96a66cSDavid du Colombier 
1457*5e96a66cSDavid du Colombier 		/*
1458*5e96a66cSDavid du Colombier 		 * same version of block is still in cache.
1459*5e96a66cSDavid du Colombier 		 *
1460*5e96a66cSDavid du Colombier 		 * the assertion is true because the block still has version p->vers,
1461*5e96a66cSDavid du Colombier 		 * which means it hasn't been written out since we last saw it.
1462*5e96a66cSDavid du Colombier 		 */
1463*5e96a66cSDavid du Colombier 		assert(bb->iostate == BioDirty);
1464*5e96a66cSDavid du Colombier 		blockPut(bb);
1465*5e96a66cSDavid du Colombier 
1466*5e96a66cSDavid du Colombier 		if(p->index < 0){
1467*5e96a66cSDavid du Colombier 			/*
1468*5e96a66cSDavid du Colombier 			 * We don't know how to temporarily undo
1469*5e96a66cSDavid du Colombier 			 * b's dependency on bb, so just don't write b yet.
1470*5e96a66cSDavid du Colombier 			 */
1471*5e96a66cSDavid du Colombier 			if(0) fprint(2, "blockWrite skipping %d %x %d %d; need to write %d %x %d\n",
1472*5e96a66cSDavid du Colombier 				b->part, b->addr, b->vers, b->l.type, p->part, p->addr, bb->vers);
1473*5e96a66cSDavid du Colombier 			return 0;
1474*5e96a66cSDavid du Colombier 		}
1475*5e96a66cSDavid du Colombier 		/* keep walking down the list */
1476*5e96a66cSDavid du Colombier 		pp = &p->next;
1477*5e96a66cSDavid du Colombier 	}
1478*5e96a66cSDavid du Colombier 
1479*5e96a66cSDavid du Colombier 	diskWrite(c->disk, b);
1480*5e96a66cSDavid du Colombier 	return 1;
1481*5e96a66cSDavid du Colombier }
1482*5e96a66cSDavid du Colombier 
1483*5e96a66cSDavid du Colombier /*
1484*5e96a66cSDavid du Colombier  * Change the I/O state of block b.
1485*5e96a66cSDavid du Colombier  * Just an assignment except for magic in
1486*5e96a66cSDavid du Colombier  * switch statement (read comments there).
1487*5e96a66cSDavid du Colombier  */
1488*5e96a66cSDavid du Colombier void
1489*5e96a66cSDavid du Colombier blockSetIOState(Block *b, int iostate)
1490*5e96a66cSDavid du Colombier {
1491*5e96a66cSDavid du Colombier 	int dowakeup;
1492*5e96a66cSDavid du Colombier 	Cache *c;
1493*5e96a66cSDavid du Colombier 	BList *p, *q;
1494*5e96a66cSDavid du Colombier 
1495*5e96a66cSDavid du Colombier if(0) fprint(2, "iostate part=%d addr=%x %s->%s\n", b->part, b->addr, bioStr(b->iostate), bioStr(iostate));
1496*5e96a66cSDavid du Colombier 
1497*5e96a66cSDavid du Colombier 	c = b->c;
1498*5e96a66cSDavid du Colombier 
1499*5e96a66cSDavid du Colombier 	dowakeup = 0;
1500*5e96a66cSDavid du Colombier 	switch(iostate){
1501*5e96a66cSDavid du Colombier 	default:
1502*5e96a66cSDavid du Colombier 		abort();
1503*5e96a66cSDavid du Colombier 	case BioEmpty:
1504*5e96a66cSDavid du Colombier 		assert(!b->uhead);
1505*5e96a66cSDavid du Colombier 		break;
1506*5e96a66cSDavid du Colombier 	case BioLabel:
1507*5e96a66cSDavid du Colombier 		assert(!b->uhead);
1508*5e96a66cSDavid du Colombier 		break;
1509*5e96a66cSDavid du Colombier 	case BioClean:
1510*5e96a66cSDavid du Colombier 		bwatchDependency(b);
1511*5e96a66cSDavid du Colombier 		/*
1512*5e96a66cSDavid du Colombier 		 * If b->prior is set, it means a write just finished.
1513*5e96a66cSDavid du Colombier 		 * The prior list isn't needed anymore.
1514*5e96a66cSDavid du Colombier 		 */
1515*5e96a66cSDavid du Colombier 		for(p=b->prior; p; p=q){
1516*5e96a66cSDavid du Colombier 			q = p->next;
1517*5e96a66cSDavid du Colombier 			blistFree(c, p);
1518*5e96a66cSDavid du Colombier 		}
1519*5e96a66cSDavid du Colombier 		b->prior = nil;
1520*5e96a66cSDavid du Colombier 		/*
1521*5e96a66cSDavid du Colombier 		 * Freeing a block or just finished a write.
1522*5e96a66cSDavid du Colombier 		 * Move the blocks from the per-block unlink
1523*5e96a66cSDavid du Colombier 		 * queue to the cache unlink queue.
1524*5e96a66cSDavid du Colombier 		 */
1525*5e96a66cSDavid du Colombier 		if(b->iostate == BioDirty || b->iostate == BioWriting){
1526*5e96a66cSDavid du Colombier 			vtLock(c->lk);
1527*5e96a66cSDavid du Colombier 			c->ndirty--;
1528*5e96a66cSDavid du Colombier 			b->vers = c->vers++;
1529*5e96a66cSDavid du Colombier 			if(b->uhead){
1530*5e96a66cSDavid du Colombier 				/* add unlink blocks to unlink queue */
1531*5e96a66cSDavid du Colombier 				if(c->uhead == nil){
1532*5e96a66cSDavid du Colombier 					c->uhead = b->uhead;
1533*5e96a66cSDavid du Colombier 					vtWakeup(c->unlink);
1534*5e96a66cSDavid du Colombier 				}else
1535*5e96a66cSDavid du Colombier 					c->utail->next = b->uhead;
1536*5e96a66cSDavid du Colombier 				c->utail = b->utail;
1537*5e96a66cSDavid du Colombier 				b->uhead = nil;
1538*5e96a66cSDavid du Colombier 			}
1539*5e96a66cSDavid du Colombier 			vtUnlock(c->lk);
1540*5e96a66cSDavid du Colombier 		}
1541*5e96a66cSDavid du Colombier 		assert(!b->uhead);
1542*5e96a66cSDavid du Colombier 		dowakeup = 1;
1543*5e96a66cSDavid du Colombier 		break;
1544*5e96a66cSDavid du Colombier 	case BioDirty:
1545*5e96a66cSDavid du Colombier 		/*
1546*5e96a66cSDavid du Colombier 		 * Wrote out an old version of the block (see blockRollback).
1547*5e96a66cSDavid du Colombier 		 * Bump a version count, leave it dirty.
1548*5e96a66cSDavid du Colombier 		 */
1549*5e96a66cSDavid du Colombier 		if(b->iostate == BioWriting){
1550*5e96a66cSDavid du Colombier 			vtLock(c->lk);
1551*5e96a66cSDavid du Colombier 			b->vers = c->vers++;
1552*5e96a66cSDavid du Colombier 			vtUnlock(c->lk);
1553*5e96a66cSDavid du Colombier 			dowakeup = 1;
1554*5e96a66cSDavid du Colombier 		}
1555*5e96a66cSDavid du Colombier 		break;
1556*5e96a66cSDavid du Colombier 	case BioReading:
1557*5e96a66cSDavid du Colombier 	case BioWriting:
1558*5e96a66cSDavid du Colombier 		/*
1559*5e96a66cSDavid du Colombier 		 * Adding block to disk queue.  Bump reference count.
1560*5e96a66cSDavid du Colombier 		 * diskThread decs the count later by calling blockPut.
1561*5e96a66cSDavid du Colombier 		 * This is here because we need to lock c->lk to
1562*5e96a66cSDavid du Colombier 		 * manipulate the ref count.
1563*5e96a66cSDavid du Colombier 		 */
1564*5e96a66cSDavid du Colombier 		vtLock(c->lk);
1565*5e96a66cSDavid du Colombier 		b->ref++;
1566*5e96a66cSDavid du Colombier 		vtUnlock(c->lk);
1567*5e96a66cSDavid du Colombier 		break;
1568*5e96a66cSDavid du Colombier 	case BioReadError:
1569*5e96a66cSDavid du Colombier 	case BioVentiError:
1570*5e96a66cSDavid du Colombier 		/*
1571*5e96a66cSDavid du Colombier 		 * Oops.
1572*5e96a66cSDavid du Colombier 		 */
1573*5e96a66cSDavid du Colombier 		dowakeup = 1;
1574*5e96a66cSDavid du Colombier 		break;
1575*5e96a66cSDavid du Colombier 	}
1576*5e96a66cSDavid du Colombier 	b->iostate = iostate;
1577*5e96a66cSDavid du Colombier 	/*
1578*5e96a66cSDavid du Colombier 	 * Now that the state has changed, we can wake the waiters.
1579*5e96a66cSDavid du Colombier 	 */
1580*5e96a66cSDavid du Colombier 	if(dowakeup)
1581*5e96a66cSDavid du Colombier 		vtWakeupAll(b->ioready);
1582*5e96a66cSDavid du Colombier }
1583*5e96a66cSDavid du Colombier 
1584*5e96a66cSDavid du Colombier char*
1585*5e96a66cSDavid du Colombier bsStr(int state)
1586*5e96a66cSDavid du Colombier {
1587*5e96a66cSDavid du Colombier 	static char s[100];
1588*5e96a66cSDavid du Colombier 
1589*5e96a66cSDavid du Colombier 	if(state == BsFree)
1590*5e96a66cSDavid du Colombier 		return "Free";
1591*5e96a66cSDavid du Colombier 	if(state == BsBad)
1592*5e96a66cSDavid du Colombier 		return "Bad";
1593*5e96a66cSDavid du Colombier 
1594*5e96a66cSDavid du Colombier 	sprint(s, "%x", state);
1595*5e96a66cSDavid du Colombier 	if(!(state&BsAlloc))
1596*5e96a66cSDavid du Colombier 		strcat(s, ",Free");	/* should not happen */
1597*5e96a66cSDavid du Colombier 	if(state&BsCopied)
1598*5e96a66cSDavid du Colombier 		strcat(s, ",Copied");
1599*5e96a66cSDavid du Colombier 	if(state&BsVenti)
1600*5e96a66cSDavid du Colombier 		strcat(s, ",Venti");
1601*5e96a66cSDavid du Colombier 	if(state&BsClosed)
1602*5e96a66cSDavid du Colombier 		strcat(s, ",Closed");
1603*5e96a66cSDavid du Colombier 	return s;
1604*5e96a66cSDavid du Colombier }
1605*5e96a66cSDavid du Colombier 
1606*5e96a66cSDavid du Colombier char *
1607*5e96a66cSDavid du Colombier bioStr(int iostate)
1608*5e96a66cSDavid du Colombier {
1609*5e96a66cSDavid du Colombier 	switch(iostate){
1610*5e96a66cSDavid du Colombier 	default:
1611*5e96a66cSDavid du Colombier 		return "Unknown!!";
1612*5e96a66cSDavid du Colombier 	case BioEmpty:
1613*5e96a66cSDavid du Colombier 		return "Empty";
1614*5e96a66cSDavid du Colombier 	case BioLabel:
1615*5e96a66cSDavid du Colombier 		return "Label";
1616*5e96a66cSDavid du Colombier 	case BioClean:
1617*5e96a66cSDavid du Colombier 		return "Clean";
1618*5e96a66cSDavid du Colombier 	case BioDirty:
1619*5e96a66cSDavid du Colombier 		return "Dirty";
1620*5e96a66cSDavid du Colombier 	case BioReading:
1621*5e96a66cSDavid du Colombier 		return "Reading";
1622*5e96a66cSDavid du Colombier 	case BioWriting:
1623*5e96a66cSDavid du Colombier 		return "Writing";
1624*5e96a66cSDavid du Colombier 	case BioReadError:
1625*5e96a66cSDavid du Colombier 		return "ReadError";
1626*5e96a66cSDavid du Colombier 	case BioVentiError:
1627*5e96a66cSDavid du Colombier 		return "VentiError";
1628*5e96a66cSDavid du Colombier 	case BioMax:
1629*5e96a66cSDavid du Colombier 		return "Max";
1630*5e96a66cSDavid du Colombier 	}
1631*5e96a66cSDavid du Colombier }
1632*5e96a66cSDavid du Colombier 
1633*5e96a66cSDavid du Colombier static char *bttab[] = {
1634*5e96a66cSDavid du Colombier 	"BtData",
1635*5e96a66cSDavid du Colombier 	"BtData+1",
1636*5e96a66cSDavid du Colombier 	"BtData+2",
1637*5e96a66cSDavid du Colombier 	"BtData+3",
1638*5e96a66cSDavid du Colombier 	"BtData+4",
1639*5e96a66cSDavid du Colombier 	"BtData+5",
1640*5e96a66cSDavid du Colombier 	"BtData+6",
1641*5e96a66cSDavid du Colombier 	"BtData+7",
1642*5e96a66cSDavid du Colombier 	"BtDir",
1643*5e96a66cSDavid du Colombier 	"BtDir+1",
1644*5e96a66cSDavid du Colombier 	"BtDir+2",
1645*5e96a66cSDavid du Colombier 	"BtDir+3",
1646*5e96a66cSDavid du Colombier 	"BtDir+4",
1647*5e96a66cSDavid du Colombier 	"BtDir+5",
1648*5e96a66cSDavid du Colombier 	"BtDir+6",
1649*5e96a66cSDavid du Colombier 	"BtDir+7",
1650*5e96a66cSDavid du Colombier };
1651*5e96a66cSDavid du Colombier 
1652*5e96a66cSDavid du Colombier char*
1653*5e96a66cSDavid du Colombier btStr(int type)
1654*5e96a66cSDavid du Colombier {
1655*5e96a66cSDavid du Colombier 	if(type < nelem(bttab))
1656*5e96a66cSDavid du Colombier 		return bttab[type];
1657*5e96a66cSDavid du Colombier 	return "unknown";
1658*5e96a66cSDavid du Colombier }
1659*5e96a66cSDavid du Colombier 
1660*5e96a66cSDavid du Colombier int
1661*5e96a66cSDavid du Colombier labelFmt(Fmt *f)
1662*5e96a66cSDavid du Colombier {
1663*5e96a66cSDavid du Colombier 	Label *l;
1664*5e96a66cSDavid du Colombier 
1665*5e96a66cSDavid du Colombier 	l = va_arg(f->args, Label*);
1666*5e96a66cSDavid du Colombier 	return fmtprint(f, "%s,%s,e=%ud,%d,tag=%#ux",
1667*5e96a66cSDavid du Colombier 		btStr(l->type), bsStr(l->state), l->epoch, (int)l->epochClose, l->tag);
1668*5e96a66cSDavid du Colombier }
1669*5e96a66cSDavid du Colombier 
1670*5e96a66cSDavid du Colombier int
1671*5e96a66cSDavid du Colombier scoreFmt(Fmt *f)
1672*5e96a66cSDavid du Colombier {
1673*5e96a66cSDavid du Colombier 	uchar *v;
1674*5e96a66cSDavid du Colombier 	int i;
1675*5e96a66cSDavid du Colombier 	u32int addr;
1676*5e96a66cSDavid du Colombier 
1677*5e96a66cSDavid du Colombier 	v = va_arg(f->args, uchar*);
1678*5e96a66cSDavid du Colombier 	if(v == nil){
1679*5e96a66cSDavid du Colombier 		fmtprint(f, "*");
1680*5e96a66cSDavid du Colombier 	}else if((addr = globalToLocal(v)) != NilBlock)
1681*5e96a66cSDavid du Colombier 		fmtprint(f, "0x%.8ux", addr);
1682*5e96a66cSDavid du Colombier 	else{
1683*5e96a66cSDavid du Colombier 		for(i = 0; i < VtScoreSize; i++)
1684*5e96a66cSDavid du Colombier 			fmtprint(f, "%2.2ux", v[i]);
1685*5e96a66cSDavid du Colombier 	}
1686*5e96a66cSDavid du Colombier 
1687*5e96a66cSDavid du Colombier 	return 0;
1688*5e96a66cSDavid du Colombier }
1689*5e96a66cSDavid du Colombier 
1690*5e96a66cSDavid du Colombier static int
1691*5e96a66cSDavid du Colombier upHeap(int i, Block *b)
1692*5e96a66cSDavid du Colombier {
1693*5e96a66cSDavid du Colombier 	Block *bb;
1694*5e96a66cSDavid du Colombier 	u32int now;
1695*5e96a66cSDavid du Colombier 	int p;
1696*5e96a66cSDavid du Colombier 	Cache *c;
1697*5e96a66cSDavid du Colombier 
1698*5e96a66cSDavid du Colombier 	c = b->c;
1699*5e96a66cSDavid du Colombier 	now = c->now;
1700*5e96a66cSDavid du Colombier 	for(; i != 0; i = p){
1701*5e96a66cSDavid du Colombier 		p = (i - 1) >> 1;
1702*5e96a66cSDavid du Colombier 		bb = c->heap[p];
1703*5e96a66cSDavid du Colombier 		if(b->used - now >= bb->used - now)
1704*5e96a66cSDavid du Colombier 			break;
1705*5e96a66cSDavid du Colombier 		c->heap[i] = bb;
1706*5e96a66cSDavid du Colombier 		bb->heap = i;
1707*5e96a66cSDavid du Colombier 	}
1708*5e96a66cSDavid du Colombier 	c->heap[i] = b;
1709*5e96a66cSDavid du Colombier 	b->heap = i;
1710*5e96a66cSDavid du Colombier 
1711*5e96a66cSDavid du Colombier 	return i;
1712*5e96a66cSDavid du Colombier }
1713*5e96a66cSDavid du Colombier 
1714*5e96a66cSDavid du Colombier static int
1715*5e96a66cSDavid du Colombier downHeap(int i, Block *b)
1716*5e96a66cSDavid du Colombier {
1717*5e96a66cSDavid du Colombier 	Block *bb;
1718*5e96a66cSDavid du Colombier 	u32int now;
1719*5e96a66cSDavid du Colombier 	int k;
1720*5e96a66cSDavid du Colombier 	Cache *c;
1721*5e96a66cSDavid du Colombier 
1722*5e96a66cSDavid du Colombier 	c = b->c;
1723*5e96a66cSDavid du Colombier 	now = c->now;
1724*5e96a66cSDavid du Colombier 	for(; ; i = k){
1725*5e96a66cSDavid du Colombier 		k = (i << 1) + 1;
1726*5e96a66cSDavid du Colombier 		if(k >= c->nheap)
1727*5e96a66cSDavid du Colombier 			break;
1728*5e96a66cSDavid du Colombier 		if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now)
1729*5e96a66cSDavid du Colombier 			k++;
1730*5e96a66cSDavid du Colombier 		bb = c->heap[k];
1731*5e96a66cSDavid du Colombier 		if(b->used - now <= bb->used - now)
1732*5e96a66cSDavid du Colombier 			break;
1733*5e96a66cSDavid du Colombier 		c->heap[i] = bb;
1734*5e96a66cSDavid du Colombier 		bb->heap = i;
1735*5e96a66cSDavid du Colombier 	}
1736*5e96a66cSDavid du Colombier 	c->heap[i] = b;
1737*5e96a66cSDavid du Colombier 	b->heap = i;
1738*5e96a66cSDavid du Colombier 	return i;
1739*5e96a66cSDavid du Colombier }
1740*5e96a66cSDavid du Colombier 
1741*5e96a66cSDavid du Colombier /*
1742*5e96a66cSDavid du Colombier  * Delete a block from the heap.
1743*5e96a66cSDavid du Colombier  * Called with c->lk held.
1744*5e96a66cSDavid du Colombier  */
1745*5e96a66cSDavid du Colombier static void
1746*5e96a66cSDavid du Colombier heapDel(Block *b)
1747*5e96a66cSDavid du Colombier {
1748*5e96a66cSDavid du Colombier 	int i, si;
1749*5e96a66cSDavid du Colombier 	Cache *c;
1750*5e96a66cSDavid du Colombier 
1751*5e96a66cSDavid du Colombier 	c = b->c;
1752*5e96a66cSDavid du Colombier 
1753*5e96a66cSDavid du Colombier 	si = b->heap;
1754*5e96a66cSDavid du Colombier 	if(si == BadHeap)
1755*5e96a66cSDavid du Colombier 		return;
1756*5e96a66cSDavid du Colombier 	b->heap = BadHeap;
1757*5e96a66cSDavid du Colombier 	c->nheap--;
1758*5e96a66cSDavid du Colombier 	if(si == c->nheap)
1759*5e96a66cSDavid du Colombier 		return;
1760*5e96a66cSDavid du Colombier 	b = c->heap[c->nheap];
1761*5e96a66cSDavid du Colombier 	i = upHeap(si, b);
1762*5e96a66cSDavid du Colombier 	if(i == si)
1763*5e96a66cSDavid du Colombier 		downHeap(i, b);
1764*5e96a66cSDavid du Colombier }
1765*5e96a66cSDavid du Colombier 
1766*5e96a66cSDavid du Colombier /*
1767*5e96a66cSDavid du Colombier  * Insert a block into the heap.
1768*5e96a66cSDavid du Colombier  * Called with c->lk held.
1769*5e96a66cSDavid du Colombier  */
1770*5e96a66cSDavid du Colombier static void
1771*5e96a66cSDavid du Colombier heapIns(Block *b)
1772*5e96a66cSDavid du Colombier {
1773*5e96a66cSDavid du Colombier 	assert(b->heap == BadHeap);
1774*5e96a66cSDavid du Colombier 	upHeap(b->c->nheap++, b);
1775*5e96a66cSDavid du Colombier }
1776*5e96a66cSDavid du Colombier 
1777*5e96a66cSDavid du Colombier /*
1778*5e96a66cSDavid du Colombier  * Get just the label for a block.
1779*5e96a66cSDavid du Colombier  */
1780*5e96a66cSDavid du Colombier static int
1781*5e96a66cSDavid du Colombier readLabel(Cache *c, Label *l, u32int addr)
1782*5e96a66cSDavid du Colombier {
1783*5e96a66cSDavid du Colombier 	int lpb;
1784*5e96a66cSDavid du Colombier 	Block *b;
1785*5e96a66cSDavid du Colombier 	u32int a;
1786*5e96a66cSDavid du Colombier 
1787*5e96a66cSDavid du Colombier 	lpb = c->size / LabelSize;
1788*5e96a66cSDavid du Colombier 	a = addr / lpb;
1789*5e96a66cSDavid du Colombier 	b = cacheLocal(c, PartLabel, a, OReadOnly);
1790*5e96a66cSDavid du Colombier 	if(b == nil){
1791*5e96a66cSDavid du Colombier 		blockPut(b);
1792*5e96a66cSDavid du Colombier 		return 0;
1793*5e96a66cSDavid du Colombier 	}
1794*5e96a66cSDavid du Colombier 
1795*5e96a66cSDavid du Colombier 	if(!labelUnpack(l, b->data, addr%lpb)){
1796*5e96a66cSDavid du Colombier 		blockPut(b);
1797*5e96a66cSDavid du Colombier 		return 0;
1798*5e96a66cSDavid du Colombier 	}
1799*5e96a66cSDavid du Colombier 	blockPut(b);
1800*5e96a66cSDavid du Colombier 	return 1;
1801*5e96a66cSDavid du Colombier }
1802*5e96a66cSDavid du Colombier 
1803*5e96a66cSDavid du Colombier /*
1804*5e96a66cSDavid du Colombier  * Process unlink queue.
1805*5e96a66cSDavid du Colombier  * Called with c->lk held.
1806*5e96a66cSDavid du Colombier  */
1807*5e96a66cSDavid du Colombier static void
1808*5e96a66cSDavid du Colombier unlinkBody(Cache *c)
1809*5e96a66cSDavid du Colombier {
1810*5e96a66cSDavid du Colombier 	BList *p;
1811*5e96a66cSDavid du Colombier 
1812*5e96a66cSDavid du Colombier 	while(c->uhead != nil){
1813*5e96a66cSDavid du Colombier 		p = c->uhead;
1814*5e96a66cSDavid du Colombier 		c->uhead = p->next;
1815*5e96a66cSDavid du Colombier 		vtUnlock(c->lk);
1816*5e96a66cSDavid du Colombier 
1817*5e96a66cSDavid du Colombier 		if(!unlinkBlock(c, p->addr, p->type, p->tag, p->epoch))
1818*5e96a66cSDavid du Colombier 			fprint(2, "unlinkBlock failed: addr=%x type=%d tag = %ux: %r\n",
1819*5e96a66cSDavid du Colombier 				p->addr, p->type, p->tag);
1820*5e96a66cSDavid du Colombier 
1821*5e96a66cSDavid du Colombier 		vtLock(c->lk);
1822*5e96a66cSDavid du Colombier 		p->next = c->blfree;
1823*5e96a66cSDavid du Colombier 		c->blfree = p;
1824*5e96a66cSDavid du Colombier 	}
1825*5e96a66cSDavid du Colombier }
1826*5e96a66cSDavid du Colombier 
1827*5e96a66cSDavid du Colombier /*
1828*5e96a66cSDavid du Colombier  * Occasionally unlink the blocks on the cache unlink queue.
1829*5e96a66cSDavid du Colombier  */
1830*5e96a66cSDavid du Colombier static void
1831*5e96a66cSDavid du Colombier unlinkThread(void *a)
1832*5e96a66cSDavid du Colombier {
1833*5e96a66cSDavid du Colombier 	Cache *c = a;
1834*5e96a66cSDavid du Colombier 
1835*5e96a66cSDavid du Colombier 	vtThreadSetName("unlink");
1836*5e96a66cSDavid du Colombier 
1837*5e96a66cSDavid du Colombier 	vtLock(c->lk);
1838*5e96a66cSDavid du Colombier 	for(;;){
1839*5e96a66cSDavid du Colombier 		while(c->uhead == nil && c->die == nil)
1840*5e96a66cSDavid du Colombier 			vtSleep(c->unlink);
1841*5e96a66cSDavid du Colombier 		if(c->die != nil)
1842*5e96a66cSDavid du Colombier 			break;
1843*5e96a66cSDavid du Colombier 		unlinkBody(c);
1844*5e96a66cSDavid du Colombier 	}
1845*5e96a66cSDavid du Colombier 	c->ref--;
1846*5e96a66cSDavid du Colombier 	vtWakeup(c->die);
1847*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
1848*5e96a66cSDavid du Colombier }
1849*5e96a66cSDavid du Colombier 
1850*5e96a66cSDavid du Colombier static int
1851*5e96a66cSDavid du Colombier baddrCmp(void *a0, void *a1)
1852*5e96a66cSDavid du Colombier {
1853*5e96a66cSDavid du Colombier 	BAddr *b0, *b1;
1854*5e96a66cSDavid du Colombier 	b0 = a0;
1855*5e96a66cSDavid du Colombier 	b1 = a1;
1856*5e96a66cSDavid du Colombier 
1857*5e96a66cSDavid du Colombier 	if(b0->part < b1->part)
1858*5e96a66cSDavid du Colombier 		return -1;
1859*5e96a66cSDavid du Colombier 	if(b0->part > b1->part)
1860*5e96a66cSDavid du Colombier 		return 1;
1861*5e96a66cSDavid du Colombier 	if(b0->addr < b1->addr)
1862*5e96a66cSDavid du Colombier 		return -1;
1863*5e96a66cSDavid du Colombier 	if(b0->addr > b1->addr)
1864*5e96a66cSDavid du Colombier 		return 1;
1865*5e96a66cSDavid du Colombier 	return 0;
1866*5e96a66cSDavid du Colombier }
1867*5e96a66cSDavid du Colombier 
1868*5e96a66cSDavid du Colombier /*
1869*5e96a66cSDavid du Colombier  * Scan the block list for dirty blocks; add them to the list c->baddr.
1870*5e96a66cSDavid du Colombier  */
1871*5e96a66cSDavid du Colombier static void
1872*5e96a66cSDavid du Colombier flushFill(Cache *c)
1873*5e96a66cSDavid du Colombier {
1874*5e96a66cSDavid du Colombier 	int i;
1875*5e96a66cSDavid du Colombier 	BAddr *p;
1876*5e96a66cSDavid du Colombier 	Block *b;
1877*5e96a66cSDavid du Colombier 
1878*5e96a66cSDavid du Colombier 	vtLock(c->lk);
1879*5e96a66cSDavid du Colombier 	if(c->ndirty == 0){
1880*5e96a66cSDavid du Colombier 		vtUnlock(c->lk);
1881*5e96a66cSDavid du Colombier 		return;
1882*5e96a66cSDavid du Colombier 	}
1883*5e96a66cSDavid du Colombier 
1884*5e96a66cSDavid du Colombier 	p = c->baddr;
1885*5e96a66cSDavid du Colombier 	for(i=0; i<c->nblocks; i++){
1886*5e96a66cSDavid du Colombier 		b = c->blocks + i;
1887*5e96a66cSDavid du Colombier 		if(b->part == PartError || b->iostate != BioDirty)
1888*5e96a66cSDavid du Colombier 			continue;
1889*5e96a66cSDavid du Colombier 		p->part = b->part;
1890*5e96a66cSDavid du Colombier 		p->addr = b->addr;
1891*5e96a66cSDavid du Colombier 		p->vers = b->vers;
1892*5e96a66cSDavid du Colombier 		p++;
1893*5e96a66cSDavid du Colombier 	}
1894*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
1895*5e96a66cSDavid du Colombier 
1896*5e96a66cSDavid du Colombier 	c->bw = p - c->baddr;
1897*5e96a66cSDavid du Colombier 	qsort(c->baddr, c->bw, sizeof(BAddr), baddrCmp);
1898*5e96a66cSDavid du Colombier }
1899*5e96a66cSDavid du Colombier 
1900*5e96a66cSDavid du Colombier /*
1901*5e96a66cSDavid du Colombier  * This is not thread safe, i.e. it can't be called from multiple threads.
1902*5e96a66cSDavid du Colombier  *
1903*5e96a66cSDavid du Colombier  * It's okay how we use it, because it only gets called in
1904*5e96a66cSDavid du Colombier  * the flushThread.  And cacheFree, but only after
1905*5e96a66cSDavid du Colombier  * cacheFree has killed off the flushThread.
1906*5e96a66cSDavid du Colombier  */
1907*5e96a66cSDavid du Colombier static int
1908*5e96a66cSDavid du Colombier cacheFlushBlock(Cache *c)
1909*5e96a66cSDavid du Colombier {
1910*5e96a66cSDavid du Colombier 	Block *b;
1911*5e96a66cSDavid du Colombier 	BAddr *p;
1912*5e96a66cSDavid du Colombier 	int lockfail, nfail;
1913*5e96a66cSDavid du Colombier 
1914*5e96a66cSDavid du Colombier 	nfail = 0;
1915*5e96a66cSDavid du Colombier 	for(;;){
1916*5e96a66cSDavid du Colombier 		if(c->br == c->be){
1917*5e96a66cSDavid du Colombier 			if(c->bw == 0 || c->bw == c->be)
1918*5e96a66cSDavid du Colombier 				flushFill(c);
1919*5e96a66cSDavid du Colombier 			c->br = 0;
1920*5e96a66cSDavid du Colombier 			c->be = c->bw;
1921*5e96a66cSDavid du Colombier 			c->bw = 0;
1922*5e96a66cSDavid du Colombier 			c->nflush = 0;
1923*5e96a66cSDavid du Colombier 		}
1924*5e96a66cSDavid du Colombier 
1925*5e96a66cSDavid du Colombier 		if(c->br == c->be)
1926*5e96a66cSDavid du Colombier 			return 0;
1927*5e96a66cSDavid du Colombier 		p = c->baddr + c->br;
1928*5e96a66cSDavid du Colombier 		c->br++;
1929*5e96a66cSDavid du Colombier 		b = _cacheLocalLookup(c, p->part, p->addr, p->vers, 0, &lockfail);
1930*5e96a66cSDavid du Colombier 
1931*5e96a66cSDavid du Colombier 		if(b && blockWrite(b)){
1932*5e96a66cSDavid du Colombier 			c->nflush++;
1933*5e96a66cSDavid du Colombier 			blockPut(b);
1934*5e96a66cSDavid du Colombier 			return 1;
1935*5e96a66cSDavid du Colombier 		}
1936*5e96a66cSDavid du Colombier 		if(b)
1937*5e96a66cSDavid du Colombier 			blockPut(b);
1938*5e96a66cSDavid du Colombier 
1939*5e96a66cSDavid du Colombier 		/*
1940*5e96a66cSDavid du Colombier 		 * Why didn't we write the block?
1941*5e96a66cSDavid du Colombier 		 */
1942*5e96a66cSDavid du Colombier 
1943*5e96a66cSDavid du Colombier 		/* Block already written out */
1944*5e96a66cSDavid du Colombier 		if(b == nil && !lockfail)
1945*5e96a66cSDavid du Colombier 			continue;
1946*5e96a66cSDavid du Colombier 
1947*5e96a66cSDavid du Colombier 		/* Failed to acquire lock; sleep if happens a lot. */
1948*5e96a66cSDavid du Colombier 		if(lockfail && ++nfail > 100)
1949*5e96a66cSDavid du Colombier 			sleep(500);
1950*5e96a66cSDavid du Colombier 
1951*5e96a66cSDavid du Colombier 		/* Requeue block. */
1952*5e96a66cSDavid du Colombier 		if(c->bw < c->be)
1953*5e96a66cSDavid du Colombier 			c->baddr[c->bw++] = *p;
1954*5e96a66cSDavid du Colombier 	}
1955*5e96a66cSDavid du Colombier 	return 0;
1956*5e96a66cSDavid du Colombier }
1957*5e96a66cSDavid du Colombier 
1958*5e96a66cSDavid du Colombier /*
1959*5e96a66cSDavid du Colombier  * Occasionally flush dirty blocks from memory to the disk.
1960*5e96a66cSDavid du Colombier  */
1961*5e96a66cSDavid du Colombier static void
1962*5e96a66cSDavid du Colombier flushThread(void *a)
1963*5e96a66cSDavid du Colombier {
1964*5e96a66cSDavid du Colombier 	Cache *c = a;
1965*5e96a66cSDavid du Colombier 	int i;
1966*5e96a66cSDavid du Colombier 
1967*5e96a66cSDavid du Colombier 	vtThreadSetName("flush");
1968*5e96a66cSDavid du Colombier 	vtLock(c->lk);
1969*5e96a66cSDavid du Colombier 	while(c->die == nil){
1970*5e96a66cSDavid du Colombier 		vtSleep(c->flush);
1971*5e96a66cSDavid du Colombier 		vtUnlock(c->lk);
1972*5e96a66cSDavid du Colombier 		for(i=0; i<FlushSize; i++)
1973*5e96a66cSDavid du Colombier 			if(!cacheFlushBlock(c))
1974*5e96a66cSDavid du Colombier 				break;
1975*5e96a66cSDavid du Colombier 		vtLock(c->lk);
1976*5e96a66cSDavid du Colombier 		vtWakeupAll(c->flushwait);
1977*5e96a66cSDavid du Colombier 	}
1978*5e96a66cSDavid du Colombier 	c->ref--;
1979*5e96a66cSDavid du Colombier 	vtWakeup(c->die);
1980*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
1981*5e96a66cSDavid du Colombier }
1982*5e96a66cSDavid du Colombier 
1983*5e96a66cSDavid du Colombier /*
1984*5e96a66cSDavid du Colombier  * Keep flushing until everything is clean.
1985*5e96a66cSDavid du Colombier  */
1986*5e96a66cSDavid du Colombier void
1987*5e96a66cSDavid du Colombier cacheFlush(Cache *c, int wait)
1988*5e96a66cSDavid du Colombier {
1989*5e96a66cSDavid du Colombier 	vtLock(c->lk);
1990*5e96a66cSDavid du Colombier 	if(wait){
1991*5e96a66cSDavid du Colombier 		while(c->ndirty){
1992*5e96a66cSDavid du Colombier 			consPrint("cacheFlush: %d dirty blocks\n", c->ndirty);
1993*5e96a66cSDavid du Colombier 			vtWakeup(c->flush);
1994*5e96a66cSDavid du Colombier 			vtSleep(c->flushwait);
1995*5e96a66cSDavid du Colombier 		}
1996*5e96a66cSDavid du Colombier 		consPrint("cacheFlush: done\n", c->ndirty);
1997*5e96a66cSDavid du Colombier 	}else
1998*5e96a66cSDavid du Colombier 		vtWakeup(c->flush);
1999*5e96a66cSDavid du Colombier 	vtUnlock(c->lk);
2000*5e96a66cSDavid du Colombier }
2001