xref: /plan9/sys/src/cmd/fossil/cache.c (revision e12a987081f10894b49298514b3a97b41db862b0)
15e96a66cSDavid du Colombier #include "stdinc.h"
25e96a66cSDavid du Colombier #include "dat.h"
35e96a66cSDavid du Colombier #include "fns.h"
45e96a66cSDavid du Colombier #include "error.h"
55e96a66cSDavid du Colombier 
65e96a66cSDavid du Colombier #include "9.h"	/* for cacheFlush */
75e96a66cSDavid du Colombier 
85e96a66cSDavid du Colombier typedef struct FreeList FreeList;
95e96a66cSDavid du Colombier typedef struct BAddr BAddr;
105e96a66cSDavid du Colombier 
115e96a66cSDavid du Colombier enum {
125e96a66cSDavid du Colombier 	BadHeap = ~0,
135e96a66cSDavid du Colombier };
145e96a66cSDavid du Colombier 
155e96a66cSDavid du Colombier /*
165e96a66cSDavid du Colombier  * Store data to the memory cache in c->size blocks
175e96a66cSDavid du Colombier  * with the block zero extended to fill it out.  When writing to
185e96a66cSDavid du Colombier  * Venti, the block will be zero truncated.  The walker will also check
195e96a66cSDavid du Colombier  * that the block fits within psize or dsize as the case may be.
205e96a66cSDavid du Colombier  */
215e96a66cSDavid du Colombier 
225e96a66cSDavid du Colombier struct Cache
235e96a66cSDavid du Colombier {
245e96a66cSDavid du Colombier 	VtLock	*lk;
255e96a66cSDavid du Colombier 	int 	ref;
265e96a66cSDavid du Colombier 	int	mode;
275e96a66cSDavid du Colombier 
285e96a66cSDavid du Colombier 	Disk 	*disk;
295e96a66cSDavid du Colombier 	int	size;			/* block size */
3061201b97SDavid du Colombier 	int	ndmap;		/* size of per-block dirty pointer map used in blockWrite */
315e96a66cSDavid du Colombier 	VtSession *z;
325e96a66cSDavid du Colombier 	u32int	now;			/* ticks for usage timestamps */
335e96a66cSDavid du Colombier 	Block	**heads;		/* hash table for finding address */
345e96a66cSDavid du Colombier 	int	nheap;			/* number of available victims */
355e96a66cSDavid du Colombier 	Block	**heap;			/* heap for locating victims */
365e96a66cSDavid du Colombier 	long	nblocks;		/* number of blocks allocated */
375e96a66cSDavid du Colombier 	Block	*blocks;		/* array of block descriptors */
385e96a66cSDavid du Colombier 	u8int	*mem;			/* memory for all block data & blists */
395e96a66cSDavid du Colombier 
405e96a66cSDavid du Colombier 	BList	*blfree;
415e96a66cSDavid du Colombier 	VtRendez *blrend;
425e96a66cSDavid du Colombier 
435e96a66cSDavid du Colombier 	int 	ndirty;			/* number of dirty blocks in the cache */
445e96a66cSDavid du Colombier 	int 	maxdirty;		/* max number of dirty blocks */
455e96a66cSDavid du Colombier 	u32int	vers;
465e96a66cSDavid du Colombier 
475e96a66cSDavid du Colombier 	long hashSize;
485e96a66cSDavid du Colombier 
495e96a66cSDavid du Colombier 	FreeList *fl;
505e96a66cSDavid du Colombier 
515e96a66cSDavid du Colombier 	VtRendez *die;			/* daemon threads should die when != nil */
525e96a66cSDavid du Colombier 
535e96a66cSDavid du Colombier 	VtRendez *flush;
545e96a66cSDavid du Colombier 	VtRendez *flushwait;
55fe853e23SDavid du Colombier 	VtRendez *heapwait;
565e96a66cSDavid du Colombier 	BAddr *baddr;
575e96a66cSDavid du Colombier 	int bw, br, be;
585e96a66cSDavid du Colombier 	int nflush;
595e96a66cSDavid du Colombier 
6061201b97SDavid du Colombier 	Periodic *sync;
6161201b97SDavid du Colombier 
625e96a66cSDavid du Colombier 	/* unlink daemon */
635e96a66cSDavid du Colombier 	BList *uhead;
645e96a66cSDavid du Colombier 	BList *utail;
655e96a66cSDavid du Colombier 	VtRendez *unlink;
667abd426fSDavid du Colombier 
677abd426fSDavid du Colombier 	/* block counts */
687abd426fSDavid du Colombier 	int nused;
697abd426fSDavid du Colombier 	int ndisk;
705e96a66cSDavid du Colombier };
715e96a66cSDavid du Colombier 
725e96a66cSDavid du Colombier struct BList {
735e96a66cSDavid du Colombier 	int part;
745e96a66cSDavid du Colombier 	u32int addr;
755e96a66cSDavid du Colombier 	uchar type;
765e96a66cSDavid du Colombier 	u32int tag;
775e96a66cSDavid du Colombier 	u32int epoch;
785e96a66cSDavid du Colombier 	u32int vers;
795e96a66cSDavid du Colombier 
80e569ccb5SDavid du Colombier 	int recurse;	/* for block unlink */
81e569ccb5SDavid du Colombier 
825e96a66cSDavid du Colombier 	/* for roll back */
835e96a66cSDavid du Colombier 	int index;			/* -1 indicates not valid */
8461201b97SDavid du Colombier 	union {
855e96a66cSDavid du Colombier 		uchar score[VtScoreSize];
8661201b97SDavid du Colombier 		uchar entry[VtEntrySize];
8761201b97SDavid du Colombier 	} old;
885e96a66cSDavid du Colombier 	BList *next;
895e96a66cSDavid du Colombier };
905e96a66cSDavid du Colombier 
915e96a66cSDavid du Colombier struct BAddr {
925e96a66cSDavid du Colombier 	int part;
935e96a66cSDavid du Colombier 	u32int addr;
945e96a66cSDavid du Colombier 	u32int vers;
955e96a66cSDavid du Colombier };
965e96a66cSDavid du Colombier 
975e96a66cSDavid du Colombier struct FreeList {
985e96a66cSDavid du Colombier 	VtLock *lk;
995e96a66cSDavid du Colombier 	u32int last;		/* last block allocated */
1005e96a66cSDavid du Colombier 	u32int end;		/* end of data partition */
1017abd426fSDavid du Colombier 	u32int nused;		/* number of used blocks */
102225077b0SDavid du Colombier 	u32int epochLow;	/* low epoch when last updated nused */
1035e96a66cSDavid du Colombier };
1045e96a66cSDavid du Colombier 
1055e96a66cSDavid du Colombier static FreeList *flAlloc(u32int end);
1065e96a66cSDavid du Colombier static void flFree(FreeList *fl);
1075e96a66cSDavid du Colombier 
1085e96a66cSDavid du Colombier static Block *cacheBumpBlock(Cache *c);
1095e96a66cSDavid du Colombier static void heapDel(Block*);
1105e96a66cSDavid du Colombier static void heapIns(Block*);
1115e96a66cSDavid du Colombier static void cacheCheck(Cache*);
1125e96a66cSDavid du Colombier static void unlinkThread(void *a);
1135e96a66cSDavid du Colombier static void flushThread(void *a);
1145e96a66cSDavid du Colombier static void unlinkBody(Cache *c);
1155e96a66cSDavid du Colombier static int cacheFlushBlock(Cache *c);
11661201b97SDavid du Colombier static void cacheSync(void*);
117e569ccb5SDavid du Colombier static BList *blistAlloc(Block*);
118e569ccb5SDavid du Colombier static void blistFree(Cache*, BList*);
119e569ccb5SDavid du Colombier static void doRemoveLink(Cache*, BList*);
120e569ccb5SDavid du Colombier 
1215e96a66cSDavid du Colombier /*
1225e96a66cSDavid du Colombier  * Mapping from local block type to Venti type
1235e96a66cSDavid du Colombier  */
1245e96a66cSDavid du Colombier int vtType[BtMax] = {
1255e96a66cSDavid du Colombier 	VtDataType,		/* BtData | 0  */
1265e96a66cSDavid du Colombier 	VtPointerType0,		/* BtData | 1  */
1275e96a66cSDavid du Colombier 	VtPointerType1,		/* BtData | 2  */
1285e96a66cSDavid du Colombier 	VtPointerType2,		/* BtData | 3  */
1295e96a66cSDavid du Colombier 	VtPointerType3,		/* BtData | 4  */
1305e96a66cSDavid du Colombier 	VtPointerType4,		/* BtData | 5  */
1315e96a66cSDavid du Colombier 	VtPointerType5,		/* BtData | 6  */
1325e96a66cSDavid du Colombier 	VtPointerType6,		/* BtData | 7  */
1335e96a66cSDavid du Colombier 	VtDirType,		/* BtDir | 0  */
1345e96a66cSDavid du Colombier 	VtPointerType0,		/* BtDir | 1  */
1355e96a66cSDavid du Colombier 	VtPointerType1,		/* BtDir | 2  */
1365e96a66cSDavid du Colombier 	VtPointerType2,		/* BtDir | 3  */
1375e96a66cSDavid du Colombier 	VtPointerType3,		/* BtDir | 4  */
1385e96a66cSDavid du Colombier 	VtPointerType4,		/* BtDir | 5  */
1395e96a66cSDavid du Colombier 	VtPointerType5,		/* BtDir | 6  */
1405e96a66cSDavid du Colombier 	VtPointerType6,		/* BtDir | 7  */
1415e96a66cSDavid du Colombier };
1425e96a66cSDavid du Colombier 
1435e96a66cSDavid du Colombier /*
1445e96a66cSDavid du Colombier  * Allocate the memory cache.
1455e96a66cSDavid du Colombier  */
1465e96a66cSDavid du Colombier Cache *
cacheAlloc(Disk * disk,VtSession * z,ulong nblocks,int mode)1475e96a66cSDavid du Colombier cacheAlloc(Disk *disk, VtSession *z, ulong nblocks, int mode)
1485e96a66cSDavid du Colombier {
1495e96a66cSDavid du Colombier 	int i;
1505e96a66cSDavid du Colombier 	Cache *c;
1515e96a66cSDavid du Colombier 	Block *b;
1525e96a66cSDavid du Colombier 	BList *bl;
1535e96a66cSDavid du Colombier 	u8int *p;
1545e96a66cSDavid du Colombier 	int nbl;
1555e96a66cSDavid du Colombier 
1565e96a66cSDavid du Colombier 	c = vtMemAllocZ(sizeof(Cache));
1575e96a66cSDavid du Colombier 
1585e96a66cSDavid du Colombier 	/* reasonable number of BList elements */
1595e96a66cSDavid du Colombier 	nbl = nblocks * 4;
1605e96a66cSDavid du Colombier 
1615e96a66cSDavid du Colombier 	c->lk = vtLockAlloc();
1625e96a66cSDavid du Colombier 	c->ref = 1;
1635e96a66cSDavid du Colombier 	c->disk = disk;
1645e96a66cSDavid du Colombier 	c->z = z;
1655e96a66cSDavid du Colombier 	c->size = diskBlockSize(disk);
1665e96a66cSDavid du Colombier bwatchSetBlockSize(c->size);
1675e96a66cSDavid du Colombier 	/* round c->size up to be a nice multiple */
1685e96a66cSDavid du Colombier 	c->size = (c->size + 127) & ~127;
16961201b97SDavid du Colombier 	c->ndmap = (c->size/20 + 7) / 8;
1705e96a66cSDavid du Colombier 	c->nblocks = nblocks;
1715e96a66cSDavid du Colombier 	c->hashSize = nblocks;
1725e96a66cSDavid du Colombier 	c->heads = vtMemAllocZ(c->hashSize*sizeof(Block*));
1735e96a66cSDavid du Colombier 	c->heap = vtMemAllocZ(nblocks*sizeof(Block*));
1745e96a66cSDavid du Colombier 	c->blocks = vtMemAllocZ(nblocks*sizeof(Block));
17561201b97SDavid du Colombier 	c->mem = vtMemAllocZ(nblocks * (c->size + c->ndmap) + nbl * sizeof(BList));
1765e96a66cSDavid du Colombier 	c->baddr = vtMemAllocZ(nblocks * sizeof(BAddr));
1775e96a66cSDavid du Colombier 	c->mode = mode;
1785e96a66cSDavid du Colombier 	c->vers++;
1795e96a66cSDavid du Colombier 	p = c->mem;
1805e96a66cSDavid du Colombier 	for(i = 0; i < nblocks; i++){
1815e96a66cSDavid du Colombier 		b = &c->blocks[i];
1825e96a66cSDavid du Colombier 		b->lk = vtLockAlloc();
1835e96a66cSDavid du Colombier 		b->c = c;
1845e96a66cSDavid du Colombier 		b->data = p;
1855e96a66cSDavid du Colombier 		b->heap = i;
1865e96a66cSDavid du Colombier 		b->ioready = vtRendezAlloc(b->lk);
1875e96a66cSDavid du Colombier 		c->heap[i] = b;
1885e96a66cSDavid du Colombier 		p += c->size;
1895e96a66cSDavid du Colombier 	}
1905e96a66cSDavid du Colombier 	c->nheap = nblocks;
1915e96a66cSDavid du Colombier 	for(i = 0; i < nbl; i++){
1925e96a66cSDavid du Colombier 		bl = (BList*)p;
1935e96a66cSDavid du Colombier 		bl->next = c->blfree;
1945e96a66cSDavid du Colombier 		c->blfree = bl;
1955e96a66cSDavid du Colombier 		p += sizeof(BList);
1965e96a66cSDavid du Colombier 	}
19761201b97SDavid du Colombier 	/* separate loop to keep blocks and blists reasonably aligned */
19861201b97SDavid du Colombier 	for(i = 0; i < nblocks; i++){
19961201b97SDavid du Colombier 		b = &c->blocks[i];
20061201b97SDavid du Colombier 		b->dmap = p;
20161201b97SDavid du Colombier 		p += c->ndmap;
20261201b97SDavid du Colombier 	}
20361201b97SDavid du Colombier 
2045e96a66cSDavid du Colombier 	c->blrend = vtRendezAlloc(c->lk);
2055e96a66cSDavid du Colombier 
2065e96a66cSDavid du Colombier 	c->maxdirty = nblocks*(DirtyPercentage*0.01);
2075e96a66cSDavid du Colombier 
2085e96a66cSDavid du Colombier 	c->fl = flAlloc(diskSize(disk, PartData));
2095e96a66cSDavid du Colombier 
2105e96a66cSDavid du Colombier 	c->unlink = vtRendezAlloc(c->lk);
2115e96a66cSDavid du Colombier 	c->flush = vtRendezAlloc(c->lk);
2125e96a66cSDavid du Colombier 	c->flushwait = vtRendezAlloc(c->lk);
213fe853e23SDavid du Colombier 	c->heapwait = vtRendezAlloc(c->lk);
21461201b97SDavid du Colombier 	c->sync = periodicAlloc(cacheSync, c, 30*1000);
2155e96a66cSDavid du Colombier 
2165e96a66cSDavid du Colombier 	if(mode == OReadWrite){
2175e96a66cSDavid du Colombier 		c->ref += 2;
2185e96a66cSDavid du Colombier 		vtThread(unlinkThread, c);
2195e96a66cSDavid du Colombier 		vtThread(flushThread, c);
2205e96a66cSDavid du Colombier 	}
2215e96a66cSDavid du Colombier 	cacheCheck(c);
2225e96a66cSDavid du Colombier 
2235e96a66cSDavid du Colombier 	return c;
2245e96a66cSDavid du Colombier }
2255e96a66cSDavid du Colombier 
2265e96a66cSDavid du Colombier /*
2275e96a66cSDavid du Colombier  * Free the whole memory cache, flushing all dirty blocks to the disk.
2285e96a66cSDavid du Colombier  */
2295e96a66cSDavid du Colombier void
cacheFree(Cache * c)2305e96a66cSDavid du Colombier cacheFree(Cache *c)
2315e96a66cSDavid du Colombier {
2325e96a66cSDavid du Colombier 	int i;
2335e96a66cSDavid du Colombier 
2345e96a66cSDavid du Colombier 	/* kill off daemon threads */
2355e96a66cSDavid du Colombier 	vtLock(c->lk);
2365e96a66cSDavid du Colombier 	c->die = vtRendezAlloc(c->lk);
23761201b97SDavid du Colombier 	periodicKill(c->sync);
2385e96a66cSDavid du Colombier 	vtWakeup(c->flush);
2395e96a66cSDavid du Colombier 	vtWakeup(c->unlink);
2405e96a66cSDavid du Colombier 	while(c->ref > 1)
2415e96a66cSDavid du Colombier 		vtSleep(c->die);
2425e96a66cSDavid du Colombier 
2435e96a66cSDavid du Colombier 	/* flush everything out */
2445e96a66cSDavid du Colombier 	do {
2455e96a66cSDavid du Colombier 		unlinkBody(c);
2465e96a66cSDavid du Colombier 		vtUnlock(c->lk);
2475e96a66cSDavid du Colombier 		while(cacheFlushBlock(c))
2485e96a66cSDavid du Colombier 			;
2495e96a66cSDavid du Colombier 		diskFlush(c->disk);
2505e96a66cSDavid du Colombier 		vtLock(c->lk);
2515e96a66cSDavid du Colombier 	} while(c->uhead || c->ndirty);
2525e96a66cSDavid du Colombier 	vtUnlock(c->lk);
2535e96a66cSDavid du Colombier 
2545e96a66cSDavid du Colombier 	cacheCheck(c);
2555e96a66cSDavid du Colombier 
2565e96a66cSDavid du Colombier 	for(i = 0; i < c->nblocks; i++){
2575e96a66cSDavid du Colombier 		assert(c->blocks[i].ref == 0);
2585e96a66cSDavid du Colombier 		vtRendezFree(c->blocks[i].ioready);
2595e96a66cSDavid du Colombier 		vtLockFree(c->blocks[i].lk);
2605e96a66cSDavid du Colombier 	}
2615e96a66cSDavid du Colombier 	flFree(c->fl);
2625e96a66cSDavid du Colombier 	vtMemFree(c->baddr);
2635e96a66cSDavid du Colombier 	vtMemFree(c->heads);
2645e96a66cSDavid du Colombier 	vtMemFree(c->blocks);
2655e96a66cSDavid du Colombier 	vtMemFree(c->mem);
2665e96a66cSDavid du Colombier 	vtLockFree(c->lk);
2675e96a66cSDavid du Colombier 	diskFree(c->disk);
2685e96a66cSDavid du Colombier 	vtRendezFree(c->blrend);
2695e96a66cSDavid du Colombier 	/* don't close vtSession */
2705e96a66cSDavid du Colombier 	vtMemFree(c);
2715e96a66cSDavid du Colombier }
2725e96a66cSDavid du Colombier 
2735e96a66cSDavid du Colombier static void
cacheDump(Cache * c)2745e96a66cSDavid du Colombier cacheDump(Cache *c)
2755e96a66cSDavid du Colombier {
2765e96a66cSDavid du Colombier 	int i;
2775e96a66cSDavid du Colombier 	Block *b;
2785e96a66cSDavid du Colombier 
2795e96a66cSDavid du Colombier 	for(i = 0; i < c->nblocks; i++){
2805e96a66cSDavid du Colombier 		b = &c->blocks[i];
28174f16c81SDavid du Colombier 		fprint(2, "%d. p=%d a=%ud %V t=%d ref=%d state=%s io=%s pc=%#p\n",
282fe853e23SDavid du Colombier 			i, b->part, b->addr, b->score, b->l.type, b->ref,
283fe853e23SDavid du Colombier 			bsStr(b->l.state), bioStr(b->iostate), b->pc);
2845e96a66cSDavid du Colombier 	}
2855e96a66cSDavid du Colombier }
2865e96a66cSDavid du Colombier 
2875e96a66cSDavid du Colombier static void
cacheCheck(Cache * c)2885e96a66cSDavid du Colombier cacheCheck(Cache *c)
2895e96a66cSDavid du Colombier {
2905e96a66cSDavid du Colombier 	u32int size, now;
2915e96a66cSDavid du Colombier 	int i, k, refed;
2925e96a66cSDavid du Colombier 	static uchar zero[VtScoreSize];
2935e96a66cSDavid du Colombier 	Block *b;
2945e96a66cSDavid du Colombier 
2955e96a66cSDavid du Colombier 	size = c->size;
2965e96a66cSDavid du Colombier 	now = c->now;
2975e96a66cSDavid du Colombier 
2985e96a66cSDavid du Colombier 	for(i = 0; i < c->nheap; i++){
2995e96a66cSDavid du Colombier 		if(c->heap[i]->heap != i)
3005e96a66cSDavid du Colombier 			vtFatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
3015e96a66cSDavid du Colombier 		if(i > 0 && c->heap[(i - 1) >> 1]->used - now > c->heap[i]->used - now)
3025e96a66cSDavid du Colombier 			vtFatal("bad heap ordering");
3035e96a66cSDavid du Colombier 		k = (i << 1) + 1;
3045e96a66cSDavid du Colombier 		if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
3055e96a66cSDavid du Colombier 			vtFatal("bad heap ordering");
3065e96a66cSDavid du Colombier 		k++;
3075e96a66cSDavid du Colombier 		if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
3085e96a66cSDavid du Colombier 			vtFatal("bad heap ordering");
3095e96a66cSDavid du Colombier 	}
3105e96a66cSDavid du Colombier 
3115e96a66cSDavid du Colombier 	refed = 0;
3125e96a66cSDavid du Colombier 	for(i = 0; i < c->nblocks; i++){
3135e96a66cSDavid du Colombier 		b = &c->blocks[i];
3145e96a66cSDavid du Colombier 		if(b->data != &c->mem[i * size])
3155e96a66cSDavid du Colombier 			vtFatal("mis-blocked at %d", i);
3165e96a66cSDavid du Colombier 		if(b->ref && b->heap == BadHeap){
3175e96a66cSDavid du Colombier 			refed++;
3185e96a66cSDavid du Colombier 		}
3195e96a66cSDavid du Colombier 	}
3205e96a66cSDavid du Colombier if(c->nheap + refed != c->nblocks){
3213b86f2f8SDavid du Colombier fprint(2, "%s: cacheCheck: nheap %d refed %d nblocks %ld\n", argv0, c->nheap, refed, c->nblocks);
3225e96a66cSDavid du Colombier cacheDump(c);
3235e96a66cSDavid du Colombier }
3245e96a66cSDavid du Colombier 	assert(c->nheap + refed == c->nblocks);
3255e96a66cSDavid du Colombier 	refed = 0;
3265e96a66cSDavid du Colombier 	for(i = 0; i < c->nblocks; i++){
3275e96a66cSDavid du Colombier 		b = &c->blocks[i];
3285e96a66cSDavid du Colombier 		if(b->ref){
3293b86f2f8SDavid du Colombier if(1)fprint(2, "%s: p=%d a=%ud %V ref=%d %L\n", argv0, b->part, b->addr, b->score, b->ref, &b->l);
3305e96a66cSDavid du Colombier 			refed++;
3315e96a66cSDavid du Colombier 		}
3325e96a66cSDavid du Colombier 	}
3333b86f2f8SDavid du Colombier if(refed > 0)fprint(2, "%s: cacheCheck: in used %d\n", argv0, refed);
3345e96a66cSDavid du Colombier }
3355e96a66cSDavid du Colombier 
3365e96a66cSDavid du Colombier 
3375e96a66cSDavid du Colombier /*
3385e96a66cSDavid du Colombier  * locate the block with the oldest second to last use.
3395e96a66cSDavid du Colombier  * remove it from the heap, and fix up the heap.
3405e96a66cSDavid du Colombier  */
3415e96a66cSDavid du Colombier /* called with c->lk held */
3425e96a66cSDavid du Colombier static Block *
cacheBumpBlock(Cache * c)3435e96a66cSDavid du Colombier cacheBumpBlock(Cache *c)
3445e96a66cSDavid du Colombier {
3450b9a5132SDavid du Colombier 	int printed;
3465e96a66cSDavid du Colombier 	Block *b;
3475e96a66cSDavid du Colombier 
3485e96a66cSDavid du Colombier 	/*
3495e96a66cSDavid du Colombier 	 * locate the block with the oldest second to last use.
3505e96a66cSDavid du Colombier 	 * remove it from the heap, and fix up the heap.
3515e96a66cSDavid du Colombier 	 */
3520b9a5132SDavid du Colombier 	printed = 0;
353fe853e23SDavid du Colombier 	if(c->nheap == 0){
354fe853e23SDavid du Colombier 		while(c->nheap == 0){
355fe853e23SDavid du Colombier 			vtWakeup(c->flush);
356fe853e23SDavid du Colombier 			vtSleep(c->heapwait);
3570b9a5132SDavid du Colombier 			if(c->nheap == 0){
3580b9a5132SDavid du Colombier 				printed = 1;
3593b86f2f8SDavid du Colombier 				fprint(2, "%s: entire cache is busy, %d dirty "
3603b86f2f8SDavid du Colombier 					"-- waking flush thread\n",
3613b86f2f8SDavid du Colombier 					argv0, c->ndirty);
362fe853e23SDavid du Colombier 			}
3630b9a5132SDavid du Colombier 		}
3640b9a5132SDavid du Colombier 		if(printed)
3653b86f2f8SDavid du Colombier 			fprint(2, "%s: cache is okay again, %d dirty\n",
3663b86f2f8SDavid du Colombier 				argv0, c->ndirty);
367fe853e23SDavid du Colombier 	}
368fe853e23SDavid du Colombier 
3695e96a66cSDavid du Colombier 	b = c->heap[0];
3705e96a66cSDavid du Colombier 	heapDel(b);
3715e96a66cSDavid du Colombier 
3725e96a66cSDavid du Colombier 	assert(b->heap == BadHeap);
3735e96a66cSDavid du Colombier 	assert(b->ref == 0);
37461a5f0d0SDavid du Colombier 	assert(b->iostate != BioDirty && b->iostate != BioReading && b->iostate != BioWriting);
3755e96a66cSDavid du Colombier 	assert(b->prior == nil);
3765e96a66cSDavid du Colombier 	assert(b->uhead == nil);
3775e96a66cSDavid du Colombier 
3785e96a66cSDavid du Colombier 	/*
3795e96a66cSDavid du Colombier 	 * unchain the block from hash chain
3805e96a66cSDavid du Colombier 	 */
3815e96a66cSDavid du Colombier 	if(b->prev){
3825e96a66cSDavid du Colombier 		*(b->prev) = b->next;
3835e96a66cSDavid du Colombier 		if(b->next)
3845e96a66cSDavid du Colombier 			b->next->prev = b->prev;
3855e96a66cSDavid du Colombier 		b->prev = nil;
3865e96a66cSDavid du Colombier 	}
3875e96a66cSDavid du Colombier 
3885e96a66cSDavid du Colombier 
3893b86f2f8SDavid du Colombier if(0)fprint(2, "%s: dropping %d:%x:%V\n", argv0, b->part, b->addr, b->score);
3905e96a66cSDavid du Colombier 	/* set block to a reasonable state */
3915e96a66cSDavid du Colombier 	b->ref = 1;
3925e96a66cSDavid du Colombier 	b->part = PartError;
3935e96a66cSDavid du Colombier 	memset(&b->l, 0, sizeof(b->l));
3945e96a66cSDavid du Colombier 	b->iostate = BioEmpty;
3955e96a66cSDavid du Colombier 
3965e96a66cSDavid du Colombier 	return b;
3975e96a66cSDavid du Colombier }
3985e96a66cSDavid du Colombier 
3995e96a66cSDavid du Colombier /*
4005e96a66cSDavid du Colombier  * look for a particular version of the block in the memory cache.
4015e96a66cSDavid du Colombier  */
4025e96a66cSDavid du Colombier static Block *
_cacheLocalLookup(Cache * c,int part,u32int addr,u32int vers,int waitlock,int * lockfailure)4035e96a66cSDavid du Colombier _cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers,
4045e96a66cSDavid du Colombier 	int waitlock, int *lockfailure)
4055e96a66cSDavid du Colombier {
4065e96a66cSDavid du Colombier 	Block *b;
4075e96a66cSDavid du Colombier 	ulong h;
4085e96a66cSDavid du Colombier 
4095e96a66cSDavid du Colombier 	h = addr % c->hashSize;
4105e96a66cSDavid du Colombier 
4115e96a66cSDavid du Colombier 	if(lockfailure)
4125e96a66cSDavid du Colombier 		*lockfailure = 0;
4135e96a66cSDavid du Colombier 
4145e96a66cSDavid du Colombier 	/*
4155e96a66cSDavid du Colombier 	 * look for the block in the cache
4165e96a66cSDavid du Colombier 	 */
4175e96a66cSDavid du Colombier 	vtLock(c->lk);
4185e96a66cSDavid du Colombier 	for(b = c->heads[h]; b != nil; b = b->next){
4195e96a66cSDavid du Colombier 		if(b->part == part && b->addr == addr)
4205e96a66cSDavid du Colombier 			break;
4215e96a66cSDavid du Colombier 	}
4225e96a66cSDavid du Colombier 	if(b == nil || b->vers != vers){
4235e96a66cSDavid du Colombier 		vtUnlock(c->lk);
4245e96a66cSDavid du Colombier 		return nil;
4255e96a66cSDavid du Colombier 	}
4265e96a66cSDavid du Colombier 	if(!waitlock && !vtCanLock(b->lk)){
4275e96a66cSDavid du Colombier 		*lockfailure = 1;
4285e96a66cSDavid du Colombier 		vtUnlock(c->lk);
4295e96a66cSDavid du Colombier 		return nil;
4305e96a66cSDavid du Colombier 	}
4315e96a66cSDavid du Colombier 	heapDel(b);
4325e96a66cSDavid du Colombier 	b->ref++;
4335e96a66cSDavid du Colombier 	vtUnlock(c->lk);
4345e96a66cSDavid du Colombier 
4355e96a66cSDavid du Colombier 	bwatchLock(b);
4365e96a66cSDavid du Colombier 	if(waitlock)
4375e96a66cSDavid du Colombier 		vtLock(b->lk);
4385e96a66cSDavid du Colombier 	b->nlock = 1;
4395e96a66cSDavid du Colombier 
4405e96a66cSDavid du Colombier 	for(;;){
4415e96a66cSDavid du Colombier 		switch(b->iostate){
4425e96a66cSDavid du Colombier 		default:
4435e96a66cSDavid du Colombier 			abort();
4445e96a66cSDavid du Colombier 		case BioEmpty:
4455e96a66cSDavid du Colombier 		case BioLabel:
4465e96a66cSDavid du Colombier 		case BioClean:
4475e96a66cSDavid du Colombier 		case BioDirty:
4485e96a66cSDavid du Colombier 			if(b->vers != vers){
4495e96a66cSDavid du Colombier 				blockPut(b);
4505e96a66cSDavid du Colombier 				return nil;
4515e96a66cSDavid du Colombier 			}
4525e96a66cSDavid du Colombier 			return b;
4535e96a66cSDavid du Colombier 		case BioReading:
4545e96a66cSDavid du Colombier 		case BioWriting:
4555e96a66cSDavid du Colombier 			vtSleep(b->ioready);
4565e96a66cSDavid du Colombier 			break;
4575e96a66cSDavid du Colombier 		case BioVentiError:
45811e1fb05SDavid du Colombier 			blockPut(b);
459e569ccb5SDavid du Colombier 			vtSetError("venti i/o error block 0x%.8ux", addr);
46011e1fb05SDavid du Colombier 			return nil;
4615e96a66cSDavid du Colombier 		case BioReadError:
4625e96a66cSDavid du Colombier 			blockPut(b);
463223a0358SDavid du Colombier 			vtSetError("error reading block 0x%.8ux", addr);
4645e96a66cSDavid du Colombier 			return nil;
4655e96a66cSDavid du Colombier 		}
4665e96a66cSDavid du Colombier 	}
4675e96a66cSDavid du Colombier 	/* NOT REACHED */
4685e96a66cSDavid du Colombier }
4695e96a66cSDavid du Colombier static Block*
cacheLocalLookup(Cache * c,int part,u32int addr,u32int vers)4705e96a66cSDavid du Colombier cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers)
4715e96a66cSDavid du Colombier {
4722651f6bbSDavid du Colombier 	return _cacheLocalLookup(c, part, addr, vers, Waitlock, 0);
4735e96a66cSDavid du Colombier }
4745e96a66cSDavid du Colombier 
4755e96a66cSDavid du Colombier 
4765e96a66cSDavid du Colombier /*
4775e96a66cSDavid du Colombier  * fetch a local (on-disk) block from the memory cache.
4785e96a66cSDavid du Colombier  * if it's not there, load it, bumping some other block.
4795e96a66cSDavid du Colombier  */
4805e96a66cSDavid du Colombier Block *
_cacheLocal(Cache * c,int part,u32int addr,int mode,u32int epoch)4815e96a66cSDavid du Colombier _cacheLocal(Cache *c, int part, u32int addr, int mode, u32int epoch)
4825e96a66cSDavid du Colombier {
4835e96a66cSDavid du Colombier 	Block *b;
4845e96a66cSDavid du Colombier 	ulong h;
4855e96a66cSDavid du Colombier 
4865e96a66cSDavid du Colombier 	assert(part != PartVenti);
4875e96a66cSDavid du Colombier 
4885e96a66cSDavid du Colombier 	h = addr % c->hashSize;
4895e96a66cSDavid du Colombier 
4905e96a66cSDavid du Colombier 	/*
4915e96a66cSDavid du Colombier 	 * look for the block in the cache
4925e96a66cSDavid du Colombier 	 */
4935e96a66cSDavid du Colombier 	vtLock(c->lk);
4945e96a66cSDavid du Colombier 	for(b = c->heads[h]; b != nil; b = b->next){
4955e96a66cSDavid du Colombier 		if(b->part != part || b->addr != addr)
4965e96a66cSDavid du Colombier 			continue;
4975e96a66cSDavid du Colombier 		if(epoch && b->l.epoch != epoch){
4983b86f2f8SDavid du Colombier fprint(2, "%s: _cacheLocal want epoch %ud got %ud\n", argv0, epoch, b->l.epoch);
4995e96a66cSDavid du Colombier 			vtUnlock(c->lk);
5005e96a66cSDavid du Colombier 			vtSetError(ELabelMismatch);
5015e96a66cSDavid du Colombier 			return nil;
5025e96a66cSDavid du Colombier 		}
5035e96a66cSDavid du Colombier 		heapDel(b);
5045e96a66cSDavid du Colombier 		b->ref++;
5055e96a66cSDavid du Colombier 		break;
5065e96a66cSDavid du Colombier 	}
5075e96a66cSDavid du Colombier 
5085e96a66cSDavid du Colombier 	if(b == nil){
5095e96a66cSDavid du Colombier 		b = cacheBumpBlock(c);
5105e96a66cSDavid du Colombier 
5115e96a66cSDavid du Colombier 		b->part = part;
5125e96a66cSDavid du Colombier 		b->addr = addr;
5135e96a66cSDavid du Colombier 		localToGlobal(addr, b->score);
5145e96a66cSDavid du Colombier 
5155e96a66cSDavid du Colombier 		/* chain onto correct hash */
5165e96a66cSDavid du Colombier 		b->next = c->heads[h];
5175e96a66cSDavid du Colombier 		c->heads[h] = b;
5185e96a66cSDavid du Colombier 		if(b->next != nil)
5195e96a66cSDavid du Colombier 			b->next->prev = &b->next;
5205e96a66cSDavid du Colombier 		b->prev = &c->heads[h];
5215e96a66cSDavid du Colombier 	}
5225e96a66cSDavid du Colombier 
5235e96a66cSDavid du Colombier 	vtUnlock(c->lk);
5245e96a66cSDavid du Colombier 
5255e96a66cSDavid du Colombier 	/*
5265e96a66cSDavid du Colombier 	 * BUG: what if the epoch changes right here?
5275e96a66cSDavid du Colombier 	 * In the worst case, we could end up in some weird
5285e96a66cSDavid du Colombier 	 * lock loop, because the block we want no longer exists,
5295e96a66cSDavid du Colombier 	 * and instead we're trying to lock a block we have no
5305e96a66cSDavid du Colombier 	 * business grabbing.
5315e96a66cSDavid du Colombier 	 *
5325e96a66cSDavid du Colombier 	 * For now, I'm not going to worry about it.
5335e96a66cSDavid du Colombier 	 */
5345e96a66cSDavid du Colombier 
5353b86f2f8SDavid du Colombier if(0)fprint(2, "%s: cacheLocal: %d: %d %x\n", argv0, getpid(), b->part, b->addr);
5365e96a66cSDavid du Colombier 	bwatchLock(b);
5375e96a66cSDavid du Colombier 	vtLock(b->lk);
5385e96a66cSDavid du Colombier 	b->nlock = 1;
5395e96a66cSDavid du Colombier 
5405e96a66cSDavid du Colombier 	if(part == PartData && b->iostate == BioEmpty){
5415e96a66cSDavid du Colombier 		if(!readLabel(c, &b->l, addr)){
5425e96a66cSDavid du Colombier 			blockPut(b);
5435e96a66cSDavid du Colombier 			return nil;
5445e96a66cSDavid du Colombier 		}
5455e96a66cSDavid du Colombier 		blockSetIOState(b, BioLabel);
5465e96a66cSDavid du Colombier 	}
5475e96a66cSDavid du Colombier 	if(epoch && b->l.epoch != epoch){
5485e96a66cSDavid du Colombier 		blockPut(b);
5493b86f2f8SDavid du Colombier fprint(2, "%s: _cacheLocal want epoch %ud got %ud\n", argv0, epoch, b->l.epoch);
5505e96a66cSDavid du Colombier 		vtSetError(ELabelMismatch);
5515e96a66cSDavid du Colombier 		return nil;
5525e96a66cSDavid du Colombier 	}
5535e96a66cSDavid du Colombier 
5545e96a66cSDavid du Colombier 	b->pc = getcallerpc(&c);
5555e96a66cSDavid du Colombier 	for(;;){
5565e96a66cSDavid du Colombier 		switch(b->iostate){
5575e96a66cSDavid du Colombier 		default:
5585e96a66cSDavid du Colombier 			abort();
5595e96a66cSDavid du Colombier 		case BioLabel:
560*e12a9870SDavid du Colombier 			if(mode == OOverWrite)
561*e12a9870SDavid du Colombier 				/*
562*e12a9870SDavid du Colombier 				 * leave iostate as BioLabel because data
563*e12a9870SDavid du Colombier 				 * hasn't been read.
564*e12a9870SDavid du Colombier 				 */
5655e96a66cSDavid du Colombier 				return b;
566*e12a9870SDavid du Colombier 			/* fall through */
567*e12a9870SDavid du Colombier 		case BioEmpty:
5685e96a66cSDavid du Colombier 			diskRead(c->disk, b);
5695e96a66cSDavid du Colombier 			vtSleep(b->ioready);
5705e96a66cSDavid du Colombier 			break;
5715e96a66cSDavid du Colombier 		case BioClean:
5725e96a66cSDavid du Colombier 		case BioDirty:
5735e96a66cSDavid du Colombier 			return b;
5745e96a66cSDavid du Colombier 		case BioReading:
5755e96a66cSDavid du Colombier 		case BioWriting:
5765e96a66cSDavid du Colombier 			vtSleep(b->ioready);
5775e96a66cSDavid du Colombier 			break;
5785e96a66cSDavid du Colombier 		case BioReadError:
5795e96a66cSDavid du Colombier 			blockSetIOState(b, BioEmpty);
5805e96a66cSDavid du Colombier 			blockPut(b);
581223a0358SDavid du Colombier 			vtSetError("error reading block 0x%.8ux", addr);
5825e96a66cSDavid du Colombier 			return nil;
5835e96a66cSDavid du Colombier 		}
5845e96a66cSDavid du Colombier 	}
5855e96a66cSDavid du Colombier 	/* NOT REACHED */
5865e96a66cSDavid du Colombier }
5875e96a66cSDavid du Colombier 
5885e96a66cSDavid du Colombier Block *
cacheLocal(Cache * c,int part,u32int addr,int mode)5895e96a66cSDavid du Colombier cacheLocal(Cache *c, int part, u32int addr, int mode)
5905e96a66cSDavid du Colombier {
5915e96a66cSDavid du Colombier 	return _cacheLocal(c, part, addr, mode, 0);
5925e96a66cSDavid du Colombier }
5935e96a66cSDavid du Colombier 
5945e96a66cSDavid du Colombier /*
5955e96a66cSDavid du Colombier  * fetch a local (on-disk) block from the memory cache.
5965e96a66cSDavid du Colombier  * if it's not there, load it, bumping some other block.
5975e96a66cSDavid du Colombier  * check tag and type.
5985e96a66cSDavid du Colombier  */
5995e96a66cSDavid du Colombier Block *
cacheLocalData(Cache * c,u32int addr,int type,u32int tag,int mode,u32int epoch)6005e96a66cSDavid du Colombier cacheLocalData(Cache *c, u32int addr, int type, u32int tag, int mode, u32int epoch)
6015e96a66cSDavid du Colombier {
6025e96a66cSDavid du Colombier 	Block *b;
6035e96a66cSDavid du Colombier 
6045e96a66cSDavid du Colombier 	b = _cacheLocal(c, PartData, addr, mode, epoch);
6055e96a66cSDavid du Colombier 	if(b == nil)
6065e96a66cSDavid du Colombier 		return nil;
6075e96a66cSDavid du Colombier 	if(b->l.type != type || b->l.tag != tag){
6083b86f2f8SDavid du Colombier 		fprint(2, "%s: cacheLocalData: addr=%d type got %d exp %d: tag got %ux exp %ux\n",
6093b86f2f8SDavid du Colombier 			argv0, addr, b->l.type, type, b->l.tag, tag);
6105e96a66cSDavid du Colombier 		vtSetError(ELabelMismatch);
6115e96a66cSDavid du Colombier 		blockPut(b);
6125e96a66cSDavid du Colombier 		return nil;
6135e96a66cSDavid du Colombier 	}
6145e96a66cSDavid du Colombier 	b->pc = getcallerpc(&c);
6155e96a66cSDavid du Colombier 	return b;
6165e96a66cSDavid du Colombier }
6175e96a66cSDavid du Colombier 
6185e96a66cSDavid du Colombier /*
6195e96a66cSDavid du Colombier  * fetch a global (Venti) block from the memory cache.
6205e96a66cSDavid du Colombier  * if it's not there, load it, bumping some other block.
6215e96a66cSDavid du Colombier  * check tag and type if it's really a local block in disguise.
6225e96a66cSDavid du Colombier  */
6235e96a66cSDavid du Colombier Block *
cacheGlobal(Cache * c,uchar score[VtScoreSize],int type,u32int tag,int mode)6245e96a66cSDavid du Colombier cacheGlobal(Cache *c, uchar score[VtScoreSize], int type, u32int tag, int mode)
6255e96a66cSDavid du Colombier {
6265e96a66cSDavid du Colombier 	int n;
6275e96a66cSDavid du Colombier 	Block *b;
6285e96a66cSDavid du Colombier 	ulong h;
6295e96a66cSDavid du Colombier 	u32int addr;
6305e96a66cSDavid du Colombier 
6315e96a66cSDavid du Colombier 	addr = globalToLocal(score);
6325e96a66cSDavid du Colombier 	if(addr != NilBlock){
6335e96a66cSDavid du Colombier 		b = cacheLocalData(c, addr, type, tag, mode, 0);
6345e96a66cSDavid du Colombier 		if(b)
6355e96a66cSDavid du Colombier 			b->pc = getcallerpc(&c);
6365e96a66cSDavid du Colombier 		return b;
6375e96a66cSDavid du Colombier 	}
6385e96a66cSDavid du Colombier 
6395e96a66cSDavid du Colombier 	h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->hashSize;
6405e96a66cSDavid du Colombier 
6415e96a66cSDavid du Colombier 	/*
6425e96a66cSDavid du Colombier 	 * look for the block in the cache
6435e96a66cSDavid du Colombier 	 */
6445e96a66cSDavid du Colombier 	vtLock(c->lk);
6455e96a66cSDavid du Colombier 	for(b = c->heads[h]; b != nil; b = b->next){
6465e96a66cSDavid du Colombier 		if(b->part != PartVenti || memcmp(b->score, score, VtScoreSize) != 0 || b->l.type != type)
6475e96a66cSDavid du Colombier 			continue;
6485e96a66cSDavid du Colombier 		heapDel(b);
6495e96a66cSDavid du Colombier 		b->ref++;
6505e96a66cSDavid du Colombier 		break;
6515e96a66cSDavid du Colombier 	}
6525e96a66cSDavid du Colombier 
6535e96a66cSDavid du Colombier 	if(b == nil){
6543b86f2f8SDavid du Colombier if(0)fprint(2, "%s: cacheGlobal %V %d\n", argv0, score, type);
6555e96a66cSDavid du Colombier 
6565e96a66cSDavid du Colombier 		b = cacheBumpBlock(c);
6575e96a66cSDavid du Colombier 
6585e96a66cSDavid du Colombier 		b->part = PartVenti;
6595e96a66cSDavid du Colombier 		b->addr = NilBlock;
6605e96a66cSDavid du Colombier 		b->l.type = type;
6615e96a66cSDavid du Colombier 		memmove(b->score, score, VtScoreSize);
6625e96a66cSDavid du Colombier 
6635e96a66cSDavid du Colombier 		/* chain onto correct hash */
6645e96a66cSDavid du Colombier 		b->next = c->heads[h];
6655e96a66cSDavid du Colombier 		c->heads[h] = b;
6665e96a66cSDavid du Colombier 		if(b->next != nil)
6675e96a66cSDavid du Colombier 			b->next->prev = &b->next;
6685e96a66cSDavid du Colombier 		b->prev = &c->heads[h];
6695e96a66cSDavid du Colombier 	}
6705e96a66cSDavid du Colombier 	vtUnlock(c->lk);
6715e96a66cSDavid du Colombier 
6725e96a66cSDavid du Colombier 	bwatchLock(b);
6735e96a66cSDavid du Colombier 	vtLock(b->lk);
6745e96a66cSDavid du Colombier 	b->nlock = 1;
6755e96a66cSDavid du Colombier 	b->pc = getcallerpc(&c);
6765e96a66cSDavid du Colombier 
6775e96a66cSDavid du Colombier 	switch(b->iostate){
6785e96a66cSDavid du Colombier 	default:
6795e96a66cSDavid du Colombier 		abort();
6805e96a66cSDavid du Colombier 	case BioEmpty:
6815e96a66cSDavid du Colombier 		n = vtRead(c->z, score, vtType[type], b->data, c->size);
6825e96a66cSDavid du Colombier 		if(n < 0 || !vtSha1Check(score, b->data, n)){
6835e96a66cSDavid du Colombier 			blockSetIOState(b, BioVentiError);
6845e96a66cSDavid du Colombier 			blockPut(b);
685223a0358SDavid du Colombier 			vtSetError(
686223a0358SDavid du Colombier 			"venti error reading block %V or wrong score: %r",
687223a0358SDavid du Colombier 				score);
6885e96a66cSDavid du Colombier 			return nil;
6895e96a66cSDavid du Colombier 		}
6905e96a66cSDavid du Colombier 		vtZeroExtend(vtType[type], b->data, n, c->size);
6915e96a66cSDavid du Colombier 		blockSetIOState(b, BioClean);
6925e96a66cSDavid du Colombier 		return b;
6935e96a66cSDavid du Colombier 	case BioClean:
6945e96a66cSDavid du Colombier 		return b;
6955e96a66cSDavid du Colombier 	case BioVentiError:
69611e1fb05SDavid du Colombier 		blockPut(b);
697223a0358SDavid du Colombier 		vtSetError("venti i/o error or wrong score, block %V", score);
69811e1fb05SDavid du Colombier 		return nil;
6995e96a66cSDavid du Colombier 	case BioReadError:
7005e96a66cSDavid du Colombier 		blockPut(b);
701223a0358SDavid du Colombier 		vtSetError("error reading block %V", b->score);
7025e96a66cSDavid du Colombier 		return nil;
7035e96a66cSDavid du Colombier 	}
7045e96a66cSDavid du Colombier 	/* NOT REACHED */
7055e96a66cSDavid du Colombier }
7065e96a66cSDavid du Colombier 
7075e96a66cSDavid du Colombier /*
7085e96a66cSDavid du Colombier  * allocate a new on-disk block and load it into the memory cache.
7095e96a66cSDavid du Colombier  * BUG: if the disk is full, should we flush some of it to Venti?
7105e96a66cSDavid du Colombier  */
7115e96a66cSDavid du Colombier static u32int lastAlloc;
7125e96a66cSDavid du Colombier 
7135e96a66cSDavid du Colombier Block *
cacheAllocBlock(Cache * c,int type,u32int tag,u32int epoch,u32int epochLow)7145e96a66cSDavid du Colombier cacheAllocBlock(Cache *c, int type, u32int tag, u32int epoch, u32int epochLow)
7155e96a66cSDavid du Colombier {
7165e96a66cSDavid du Colombier 	FreeList *fl;
7175e96a66cSDavid du Colombier 	u32int addr;
7185e96a66cSDavid du Colombier 	Block *b;
7195e96a66cSDavid du Colombier 	int n, nwrap;
7205e96a66cSDavid du Colombier 	Label lab;
7215e96a66cSDavid du Colombier 
7225e96a66cSDavid du Colombier 	n = c->size / LabelSize;
7235e96a66cSDavid du Colombier 	fl = c->fl;
7245e96a66cSDavid du Colombier 
7255e96a66cSDavid du Colombier 	vtLock(fl->lk);
7265e96a66cSDavid du Colombier 	addr = fl->last;
7275e96a66cSDavid du Colombier 	b = cacheLocal(c, PartLabel, addr/n, OReadOnly);
7285e96a66cSDavid du Colombier 	if(b == nil){
7293b86f2f8SDavid du Colombier 		fprint(2, "%s: cacheAllocBlock: xxx %R\n", argv0);
7305e96a66cSDavid du Colombier 		vtUnlock(fl->lk);
7315e96a66cSDavid du Colombier 		return nil;
7325e96a66cSDavid du Colombier 	}
7335e96a66cSDavid du Colombier 	nwrap = 0;
7345e96a66cSDavid du Colombier 	for(;;){
7355e96a66cSDavid du Colombier 		if(++addr >= fl->end){
7365e96a66cSDavid du Colombier 			addr = 0;
7375e96a66cSDavid du Colombier 			if(++nwrap >= 2){
7385e96a66cSDavid du Colombier 				blockPut(b);
7395e96a66cSDavid du Colombier 				vtSetError("disk is full");
740b3b810bfSDavid du Colombier 				/*
741b3b810bfSDavid du Colombier 				 * try to avoid a continuous spew of console
742b3b810bfSDavid du Colombier 				 * messages.
743b3b810bfSDavid du Colombier 				 */
744b3b810bfSDavid du Colombier 				if (fl->last != 0)
745b3b810bfSDavid du Colombier 					fprint(2, "%s: cacheAllocBlock: xxx1 %R\n",
746b3b810bfSDavid du Colombier 						argv0);
747b3b810bfSDavid du Colombier 				fl->last = 0;
7485e96a66cSDavid du Colombier 				vtUnlock(fl->lk);
7495e96a66cSDavid du Colombier 				return nil;
7505e96a66cSDavid du Colombier 			}
7515e96a66cSDavid du Colombier 		}
7525e96a66cSDavid du Colombier 		if(addr%n == 0){
7535e96a66cSDavid du Colombier 			blockPut(b);
7545e96a66cSDavid du Colombier 			b = cacheLocal(c, PartLabel, addr/n, OReadOnly);
7555e96a66cSDavid du Colombier 			if(b == nil){
7565e96a66cSDavid du Colombier 				fl->last = addr;
7573b86f2f8SDavid du Colombier 				fprint(2, "%s: cacheAllocBlock: xxx2 %R\n", argv0);
7585e96a66cSDavid du Colombier 				vtUnlock(fl->lk);
7595e96a66cSDavid du Colombier 				return nil;
7605e96a66cSDavid du Colombier 			}
7615e96a66cSDavid du Colombier 		}
7625e96a66cSDavid du Colombier 		if(!labelUnpack(&lab, b->data, addr%n))
7635e96a66cSDavid du Colombier 			continue;
7645e96a66cSDavid du Colombier 		if(lab.state == BsFree)
7655e96a66cSDavid du Colombier 			goto Found;
766e569ccb5SDavid du Colombier 		if(lab.state&BsClosed)
767e569ccb5SDavid du Colombier 		if(lab.epochClose <= epochLow || lab.epoch==lab.epochClose)
7685e96a66cSDavid du Colombier 			goto Found;
7695e96a66cSDavid du Colombier 	}
7705e96a66cSDavid du Colombier Found:
7715e96a66cSDavid du Colombier 	blockPut(b);
7725e96a66cSDavid du Colombier 	b = cacheLocal(c, PartData, addr, OOverWrite);
7735e96a66cSDavid du Colombier 	if(b == nil){
7743b86f2f8SDavid du Colombier 		fprint(2, "%s: cacheAllocBlock: xxx3 %R\n", argv0);
7755e96a66cSDavid du Colombier 		return nil;
7765e96a66cSDavid du Colombier 	}
7775e96a66cSDavid du Colombier assert(b->iostate == BioLabel || b->iostate == BioClean);
7785e96a66cSDavid du Colombier 	fl->last = addr;
7795e96a66cSDavid du Colombier 	lab.type = type;
7805e96a66cSDavid du Colombier 	lab.tag = tag;
7815e96a66cSDavid du Colombier 	lab.state = BsAlloc;
7825e96a66cSDavid du Colombier 	lab.epoch = epoch;
7835e96a66cSDavid du Colombier 	lab.epochClose = ~(u32int)0;
784e569ccb5SDavid du Colombier 	if(!blockSetLabel(b, &lab, 1)){
7853b86f2f8SDavid du Colombier 		fprint(2, "%s: cacheAllocBlock: xxx4 %R\n", argv0);
7865e96a66cSDavid du Colombier 		blockPut(b);
7875e96a66cSDavid du Colombier 		return nil;
7885e96a66cSDavid du Colombier 	}
7895e96a66cSDavid du Colombier 	vtZeroExtend(vtType[type], b->data, 0, c->size);
7905e96a66cSDavid du Colombier if(0)diskWrite(c->disk, b);
7915e96a66cSDavid du Colombier 
7923b86f2f8SDavid du Colombier if(0)fprint(2, "%s: fsAlloc %ud type=%d tag = %ux\n", argv0, addr, type, tag);
7935e96a66cSDavid du Colombier 	lastAlloc = addr;
7947abd426fSDavid du Colombier 	fl->nused++;
7955e96a66cSDavid du Colombier 	vtUnlock(fl->lk);
7965e96a66cSDavid du Colombier 	b->pc = getcallerpc(&c);
7975e96a66cSDavid du Colombier 	return b;
7985e96a66cSDavid du Colombier }
7995e96a66cSDavid du Colombier 
8000b9a5132SDavid du Colombier int
cacheDirty(Cache * c)8010b9a5132SDavid du Colombier cacheDirty(Cache *c)
8020b9a5132SDavid du Colombier {
8030b9a5132SDavid du Colombier 	return c->ndirty;
8040b9a5132SDavid du Colombier }
8050b9a5132SDavid du Colombier 
8067abd426fSDavid du Colombier void
cacheCountUsed(Cache * c,u32int epochLow,u32int * used,u32int * total,u32int * bsize)8077abd426fSDavid du Colombier cacheCountUsed(Cache *c, u32int epochLow, u32int *used, u32int *total, u32int *bsize)
8087abd426fSDavid du Colombier {
8097abd426fSDavid du Colombier 	int n;
8107abd426fSDavid du Colombier 	u32int addr, nused;
8117abd426fSDavid du Colombier 	Block *b;
8127abd426fSDavid du Colombier 	Label lab;
8137abd426fSDavid du Colombier 	FreeList *fl;
8147abd426fSDavid du Colombier 
8157abd426fSDavid du Colombier 	fl = c->fl;
8167abd426fSDavid du Colombier 	n = c->size / LabelSize;
8177abd426fSDavid du Colombier 	*bsize = c->size;
8187abd426fSDavid du Colombier 	vtLock(fl->lk);
8197abd426fSDavid du Colombier 	if(fl->epochLow == epochLow){
8207abd426fSDavid du Colombier 		*used = fl->nused;
8217abd426fSDavid du Colombier 		*total = fl->end;
8227abd426fSDavid du Colombier 		vtUnlock(fl->lk);
8237abd426fSDavid du Colombier 		return;
8247abd426fSDavid du Colombier 	}
8257abd426fSDavid du Colombier 	b = nil;
8267abd426fSDavid du Colombier 	nused = 0;
8277abd426fSDavid du Colombier 	for(addr=0; addr<fl->end; addr++){
8287abd426fSDavid du Colombier 		if(addr%n == 0){
8297abd426fSDavid du Colombier 			blockPut(b);
8307abd426fSDavid du Colombier 			b = cacheLocal(c, PartLabel, addr/n, OReadOnly);
8317abd426fSDavid du Colombier 			if(b == nil){
8323b86f2f8SDavid du Colombier 				fprint(2, "%s: flCountUsed: loading %ux: %R\n",
8333b86f2f8SDavid du Colombier 					argv0, addr/n);
8347abd426fSDavid du Colombier 				break;
8357abd426fSDavid du Colombier 			}
8367abd426fSDavid du Colombier 		}
8377abd426fSDavid du Colombier 		if(!labelUnpack(&lab, b->data, addr%n))
8387abd426fSDavid du Colombier 			continue;
8397abd426fSDavid du Colombier 		if(lab.state == BsFree)
8407abd426fSDavid du Colombier 			continue;
841e569ccb5SDavid du Colombier 		if(lab.state&BsClosed)
842e569ccb5SDavid du Colombier 		if(lab.epochClose <= epochLow || lab.epoch==lab.epochClose)
8437abd426fSDavid du Colombier 			continue;
8447abd426fSDavid du Colombier 		nused++;
8457abd426fSDavid du Colombier 	}
8467abd426fSDavid du Colombier 	blockPut(b);
8477abd426fSDavid du Colombier 	if(addr == fl->end){
8487abd426fSDavid du Colombier 		fl->nused = nused;
8497abd426fSDavid du Colombier 		fl->epochLow = epochLow;
8507abd426fSDavid du Colombier 	}
8517abd426fSDavid du Colombier 	*used = nused;
8527abd426fSDavid du Colombier 	*total = fl->end;
8537abd426fSDavid du Colombier 	vtUnlock(fl->lk);
8547abd426fSDavid du Colombier 	return;
8557abd426fSDavid du Colombier }
8567abd426fSDavid du Colombier 
8575e96a66cSDavid du Colombier static FreeList *
flAlloc(u32int end)8585e96a66cSDavid du Colombier flAlloc(u32int end)
8595e96a66cSDavid du Colombier {
8605e96a66cSDavid du Colombier 	FreeList *fl;
8615e96a66cSDavid du Colombier 
8625e96a66cSDavid du Colombier 	fl = vtMemAllocZ(sizeof(*fl));
8635e96a66cSDavid du Colombier 	fl->lk = vtLockAlloc();
864b5e190f4SDavid du Colombier 	fl->last = 0;
8655e96a66cSDavid du Colombier 	fl->end = end;
8665e96a66cSDavid du Colombier 	return fl;
8675e96a66cSDavid du Colombier }
8685e96a66cSDavid du Colombier 
8695e96a66cSDavid du Colombier static void
flFree(FreeList * fl)8705e96a66cSDavid du Colombier flFree(FreeList *fl)
8715e96a66cSDavid du Colombier {
8725e96a66cSDavid du Colombier 	vtLockFree(fl->lk);
8735e96a66cSDavid du Colombier 	vtMemFree(fl);
8745e96a66cSDavid du Colombier }
8755e96a66cSDavid du Colombier 
8765e96a66cSDavid du Colombier u32int
cacheLocalSize(Cache * c,int part)8775e96a66cSDavid du Colombier cacheLocalSize(Cache *c, int part)
8785e96a66cSDavid du Colombier {
8795e96a66cSDavid du Colombier 	return diskSize(c->disk, part);
8805e96a66cSDavid du Colombier }
8815e96a66cSDavid du Colombier 
8825e96a66cSDavid du Colombier /*
8835e96a66cSDavid du Colombier  * The thread that has locked b may refer to it by
8845e96a66cSDavid du Colombier  * multiple names.  Nlock counts the number of
8855e96a66cSDavid du Colombier  * references the locking thread holds.  It will call
8865e96a66cSDavid du Colombier  * blockPut once per reference.
8875e96a66cSDavid du Colombier  */
8885e96a66cSDavid du Colombier void
blockDupLock(Block * b)8895e96a66cSDavid du Colombier blockDupLock(Block *b)
8905e96a66cSDavid du Colombier {
8915e96a66cSDavid du Colombier 	assert(b->nlock > 0);
8925e96a66cSDavid du Colombier 	b->nlock++;
8935e96a66cSDavid du Colombier }
8945e96a66cSDavid du Colombier 
8955e96a66cSDavid du Colombier /*
8965e96a66cSDavid du Colombier  * we're done with the block.
8975e96a66cSDavid du Colombier  * unlock it.  can't use it after calling this.
8985e96a66cSDavid du Colombier  */
8995e96a66cSDavid du Colombier void
blockPut(Block * b)9005e96a66cSDavid du Colombier blockPut(Block* b)
9015e96a66cSDavid du Colombier {
9025e96a66cSDavid du Colombier 	Cache *c;
9035e96a66cSDavid du Colombier 
9045e96a66cSDavid du Colombier 	if(b == nil)
9055e96a66cSDavid du Colombier 		return;
9065e96a66cSDavid du Colombier 
9073b86f2f8SDavid du Colombier if(0)fprint(2, "%s: blockPut: %d: %d %x %d %s\n", argv0, getpid(), b->part, b->addr, c->nheap, bioStr(b->iostate));
9085e96a66cSDavid du Colombier 
9095e96a66cSDavid du Colombier 	if(b->iostate == BioDirty)
9105e96a66cSDavid du Colombier 		bwatchDependency(b);
9115e96a66cSDavid du Colombier 
9125e96a66cSDavid du Colombier 	if(--b->nlock > 0)
9135e96a66cSDavid du Colombier 		return;
9145e96a66cSDavid du Colombier 
9155e96a66cSDavid du Colombier 	/*
9165e96a66cSDavid du Colombier 	 * b->nlock should probably stay at zero while
9175e96a66cSDavid du Colombier 	 * the block is unlocked, but diskThread and vtSleep
9185e96a66cSDavid du Colombier 	 * conspire to assume that they can just vtLock(b->lk); blockPut(b),
9195e96a66cSDavid du Colombier 	 * so we have to keep b->nlock set to 1 even
9205e96a66cSDavid du Colombier 	 * when the block is unlocked.
9215e96a66cSDavid du Colombier 	 */
9225e96a66cSDavid du Colombier 	assert(b->nlock == 0);
9235e96a66cSDavid du Colombier 	b->nlock = 1;
9245e96a66cSDavid du Colombier //	b->pc = 0;
9255e96a66cSDavid du Colombier 
9265e96a66cSDavid du Colombier 	bwatchUnlock(b);
9275e96a66cSDavid du Colombier 	vtUnlock(b->lk);
9285e96a66cSDavid du Colombier 	c = b->c;
9295e96a66cSDavid du Colombier 	vtLock(c->lk);
9305e96a66cSDavid du Colombier 
9315e96a66cSDavid du Colombier 	if(--b->ref > 0){
9325e96a66cSDavid du Colombier 		vtUnlock(c->lk);
9335e96a66cSDavid du Colombier 		return;
9345e96a66cSDavid du Colombier 	}
9355e96a66cSDavid du Colombier 
9365e96a66cSDavid du Colombier 	assert(b->ref == 0);
9375e96a66cSDavid du Colombier 	switch(b->iostate){
9385e96a66cSDavid du Colombier 	default:
9395e96a66cSDavid du Colombier 		b->used = c->now++;
9405e96a66cSDavid du Colombier 		heapIns(b);
9415e96a66cSDavid du Colombier 		break;
9425e96a66cSDavid du Colombier 	case BioEmpty:
9435e96a66cSDavid du Colombier 	case BioLabel:
9445e96a66cSDavid du Colombier 		if(c->nheap == 0)
9455e96a66cSDavid du Colombier 			b->used = c->now++;
9465e96a66cSDavid du Colombier 		else
9475e96a66cSDavid du Colombier 			b->used = c->heap[0]->used;
9485e96a66cSDavid du Colombier 		heapIns(b);
9495e96a66cSDavid du Colombier 		break;
9505e96a66cSDavid du Colombier 	case BioDirty:
9515e96a66cSDavid du Colombier 		break;
9525e96a66cSDavid du Colombier 	}
9535e96a66cSDavid du Colombier 	vtUnlock(c->lk);
9545e96a66cSDavid du Colombier }
9555e96a66cSDavid du Colombier 
9565e96a66cSDavid du Colombier /*
957e569ccb5SDavid du Colombier  * set the label associated with a block.
9585e96a66cSDavid du Colombier  */
959e569ccb5SDavid du Colombier Block*
_blockSetLabel(Block * b,Label * l)960e569ccb5SDavid du Colombier _blockSetLabel(Block *b, Label *l)
9615e96a66cSDavid du Colombier {
962e569ccb5SDavid du Colombier 	int lpb;
9635e96a66cSDavid du Colombier 	Block *bb;
9645e96a66cSDavid du Colombier 	u32int a;
9655e96a66cSDavid du Colombier 	Cache *c;
9665e96a66cSDavid du Colombier 
9675e96a66cSDavid du Colombier 	c = b->c;
9685e96a66cSDavid du Colombier 
969e569ccb5SDavid du Colombier 	assert(b->part == PartData);
970e569ccb5SDavid du Colombier 	assert(b->iostate == BioLabel || b->iostate == BioClean || b->iostate == BioDirty);
971e569ccb5SDavid du Colombier 	lpb = c->size / LabelSize;
972e569ccb5SDavid du Colombier 	a = b->addr / lpb;
973e569ccb5SDavid du Colombier 	bb = cacheLocal(c, PartLabel, a, OReadWrite);
974e569ccb5SDavid du Colombier 	if(bb == nil){
975e569ccb5SDavid du Colombier 		blockPut(b);
9765e96a66cSDavid du Colombier 		return nil;
9775e96a66cSDavid du Colombier 	}
978e569ccb5SDavid du Colombier 	b->l = *l;
979e569ccb5SDavid du Colombier 	labelPack(l, bb->data, b->addr%lpb);
980e569ccb5SDavid du Colombier 	blockDirty(bb);
981e569ccb5SDavid du Colombier 	return bb;
982e569ccb5SDavid du Colombier }
983e569ccb5SDavid du Colombier 
984e569ccb5SDavid du Colombier int
blockSetLabel(Block * b,Label * l,int allocating)985e569ccb5SDavid du Colombier blockSetLabel(Block *b, Label *l, int allocating)
986e569ccb5SDavid du Colombier {
987e569ccb5SDavid du Colombier 	Block *lb;
988e569ccb5SDavid du Colombier 	Label oldl;
989e569ccb5SDavid du Colombier 
990e569ccb5SDavid du Colombier 	oldl = b->l;
991e569ccb5SDavid du Colombier 	lb = _blockSetLabel(b, l);
992e569ccb5SDavid du Colombier 	if(lb == nil)
993e569ccb5SDavid du Colombier 		return 0;
9945e96a66cSDavid du Colombier 
9955e96a66cSDavid du Colombier 	/*
996e569ccb5SDavid du Colombier 	 * If we're allocating the block, make sure the label (bl)
997e569ccb5SDavid du Colombier 	 * goes to disk before the data block (b) itself.  This is to help
998e569ccb5SDavid du Colombier 	 * the blocks that in turn depend on b.
999e569ccb5SDavid du Colombier 	 *
1000e569ccb5SDavid du Colombier 	 * Suppose bx depends on (must be written out after) b.
1001e569ccb5SDavid du Colombier 	 * Once we write b we'll think it's safe to write bx.
1002e569ccb5SDavid du Colombier 	 * Bx can't get at b unless it has a valid label, though.
1003e569ccb5SDavid du Colombier 	 *
1004e569ccb5SDavid du Colombier 	 * Allocation is the only case in which having a current label
1005e569ccb5SDavid du Colombier 	 * is vital because:
1006e569ccb5SDavid du Colombier 	 *
1007e569ccb5SDavid du Colombier 	 *	- l.type is set at allocation and never changes.
1008e569ccb5SDavid du Colombier 	 *	- l.tag is set at allocation and never changes.
1009e569ccb5SDavid du Colombier 	 *	- l.state is not checked when we load blocks.
1010e569ccb5SDavid du Colombier 	 *	- the archiver cares deeply about l.state being
1011e569ccb5SDavid du Colombier 	 *		BaActive vs. BaCopied, but that's handled
1012e569ccb5SDavid du Colombier 	 *		by direct calls to _blockSetLabel.
10135e96a66cSDavid du Colombier 	 */
10145e96a66cSDavid du Colombier 
1015e569ccb5SDavid du Colombier 	if(allocating)
1016e569ccb5SDavid du Colombier 		blockDependency(b, lb, -1, nil, nil);
1017e569ccb5SDavid du Colombier 	blockPut(lb);
1018e569ccb5SDavid du Colombier 	return 1;
10195e96a66cSDavid du Colombier }
10205e96a66cSDavid du Colombier 
10215e96a66cSDavid du Colombier /*
102261201b97SDavid du Colombier  * Record that bb must be written out before b.
102361201b97SDavid du Colombier  * If index is given, we're about to overwrite the score/e
102461201b97SDavid du Colombier  * at that index in the block.  Save the old value so we
10255e96a66cSDavid du Colombier  * can write a safer ``old'' version of the block if pressed.
10265e96a66cSDavid du Colombier  */
10275e96a66cSDavid du Colombier void
blockDependency(Block * b,Block * bb,int index,uchar * score,Entry * e)102861201b97SDavid du Colombier blockDependency(Block *b, Block *bb, int index, uchar *score, Entry *e)
10295e96a66cSDavid du Colombier {
10305e96a66cSDavid du Colombier 	BList *p;
10315e96a66cSDavid du Colombier 
10325e96a66cSDavid du Colombier 	if(bb->iostate == BioClean)
10335e96a66cSDavid du Colombier 		return;
10345e96a66cSDavid du Colombier 
103561201b97SDavid du Colombier 	/*
103661201b97SDavid du Colombier 	 * Dependencies for blocks containing Entry structures
103761201b97SDavid du Colombier 	 * or scores must always be explained.  The problem with
103861201b97SDavid du Colombier 	 * only explaining some of them is this.  Suppose we have two
103961201b97SDavid du Colombier 	 * dependencies for the same field, the first explained
104061201b97SDavid du Colombier 	 * and the second not.  We try to write the block when the first
104161201b97SDavid du Colombier 	 * dependency is not written but the second is.  We will roll back
104261201b97SDavid du Colombier 	 * the first change even though the second trumps it.
104361201b97SDavid du Colombier 	 */
104461201b97SDavid du Colombier 	if(index == -1 && bb->part == PartData)
104561201b97SDavid du Colombier 		assert(b->l.type == BtData);
104661201b97SDavid du Colombier 
10477abd426fSDavid du Colombier 	if(bb->iostate != BioDirty){
10483b86f2f8SDavid du Colombier 		fprint(2, "%s: %d:%x:%d iostate is %d in blockDependency\n",
10493b86f2f8SDavid du Colombier 			argv0, bb->part, bb->addr, bb->l.type, bb->iostate);
10507abd426fSDavid du Colombier 		abort();
10517abd426fSDavid du Colombier 	}
10525e96a66cSDavid du Colombier 
10535e96a66cSDavid du Colombier 	p = blistAlloc(bb);
10545e96a66cSDavid du Colombier 	if(p == nil)
10555e96a66cSDavid du Colombier 		return;
10565e96a66cSDavid du Colombier 
10577f1bc48aSDavid du Colombier 	assert(bb->iostate == BioDirty);
10583b86f2f8SDavid du Colombier if(0)fprint(2, "%s: %d:%x:%d depends on %d:%x:%d\n", argv0, b->part, b->addr, b->l.type, bb->part, bb->addr, bb->l.type);
10595e96a66cSDavid du Colombier 
10605e96a66cSDavid du Colombier 	p->part = bb->part;
10615e96a66cSDavid du Colombier 	p->addr = bb->addr;
10625e96a66cSDavid du Colombier 	p->type = bb->l.type;
10635e96a66cSDavid du Colombier 	p->vers = bb->vers;
10645e96a66cSDavid du Colombier 	p->index = index;
106561201b97SDavid du Colombier 	if(p->index >= 0){
106661201b97SDavid du Colombier 		/*
106761201b97SDavid du Colombier 		 * This test would just be b->l.type==BtDir except
106861201b97SDavid du Colombier 		 * we need to exclude the super block.
106961201b97SDavid du Colombier 		 */
107061201b97SDavid du Colombier 		if(b->l.type == BtDir && b->part == PartData)
107161201b97SDavid du Colombier 			entryPack(e, p->old.entry, 0);
107261201b97SDavid du Colombier 		else
107361201b97SDavid du Colombier 			memmove(p->old.score, score, VtScoreSize);
107461201b97SDavid du Colombier 	}
10755e96a66cSDavid du Colombier 	p->next = b->prior;
10765e96a66cSDavid du Colombier 	b->prior = p;
10775e96a66cSDavid du Colombier }
10785e96a66cSDavid du Colombier 
10795e96a66cSDavid du Colombier /*
10805e96a66cSDavid du Colombier  * Mark an in-memory block as dirty.  If there are too many
1081fe853e23SDavid du Colombier  * dirty blocks, start writing some out to disk.
10825e96a66cSDavid du Colombier  *
1083fe853e23SDavid du Colombier  * If there were way too many dirty blocks, we used to
1084fe853e23SDavid du Colombier  * try to do some flushing ourselves, but it's just too dangerous --
1085fe853e23SDavid du Colombier  * it implies that the callers cannot have any of our priors locked,
1086fe853e23SDavid du Colombier  * but this is hard to avoid in some cases.
10875e96a66cSDavid du Colombier  */
10885e96a66cSDavid du Colombier int
blockDirty(Block * b)10895e96a66cSDavid du Colombier blockDirty(Block *b)
10905e96a66cSDavid du Colombier {
10915e96a66cSDavid du Colombier 	Cache *c;
10925e96a66cSDavid du Colombier 
10935e96a66cSDavid du Colombier 	c = b->c;
10945e96a66cSDavid du Colombier 
10955e96a66cSDavid du Colombier 	assert(b->part != PartVenti);
10965e96a66cSDavid du Colombier 
10975e96a66cSDavid du Colombier 	if(b->iostate == BioDirty)
10985e96a66cSDavid du Colombier 		return 1;
1099*e12a9870SDavid du Colombier 	assert(b->iostate == BioClean || b->iostate == BioLabel);
11005e96a66cSDavid du Colombier 
11015e96a66cSDavid du Colombier 	vtLock(c->lk);
110261201b97SDavid du Colombier 	b->iostate = BioDirty;
11035e96a66cSDavid du Colombier 	c->ndirty++;
11045e96a66cSDavid du Colombier 	if(c->ndirty > (c->maxdirty>>1))
11055e96a66cSDavid du Colombier 		vtWakeup(c->flush);
11065e96a66cSDavid du Colombier 	vtUnlock(c->lk);
11075e96a66cSDavid du Colombier 
11085e96a66cSDavid du Colombier 	return 1;
11095e96a66cSDavid du Colombier }
11105e96a66cSDavid du Colombier 
11115e96a66cSDavid du Colombier /*
111261201b97SDavid du Colombier  * We've decided to write out b.  Maybe b has some pointers to blocks
111361201b97SDavid du Colombier  * that haven't yet been written to disk.  If so, construct a slightly out-of-date
111461201b97SDavid du Colombier  * copy of b that is safe to write out.  (diskThread will make sure the block
11155e96a66cSDavid du Colombier  * remains marked as dirty.)
11165e96a66cSDavid du Colombier  */
11175e96a66cSDavid du Colombier uchar *
blockRollback(Block * b,uchar * buf)11185e96a66cSDavid du Colombier blockRollback(Block *b, uchar *buf)
11195e96a66cSDavid du Colombier {
11205e96a66cSDavid du Colombier 	u32int addr;
11215e96a66cSDavid du Colombier 	BList *p;
11225e96a66cSDavid du Colombier 	Super super;
11235e96a66cSDavid du Colombier 
11245e96a66cSDavid du Colombier 	/* easy case */
11255e96a66cSDavid du Colombier 	if(b->prior == nil)
11265e96a66cSDavid du Colombier 		return b->data;
11275e96a66cSDavid du Colombier 
11285e96a66cSDavid du Colombier 	memmove(buf, b->data, b->c->size);
11295e96a66cSDavid du Colombier 	for(p=b->prior; p; p=p->next){
11305e96a66cSDavid du Colombier 		/*
11315e96a66cSDavid du Colombier 		 * we know p->index >= 0 because blockWrite has vetted this block for us.
11325e96a66cSDavid du Colombier 		 */
11335e96a66cSDavid du Colombier 		assert(p->index >= 0);
11345e96a66cSDavid du Colombier 		assert(b->part == PartSuper || (b->part == PartData && b->l.type != BtData));
11355e96a66cSDavid du Colombier 		if(b->part == PartSuper){
11365e96a66cSDavid du Colombier 			assert(p->index == 0);
11375e96a66cSDavid du Colombier 			superUnpack(&super, buf);
113861201b97SDavid du Colombier 			addr = globalToLocal(p->old.score);
11395e96a66cSDavid du Colombier 			if(addr == NilBlock){
11403b86f2f8SDavid du Colombier 				fprint(2, "%s: rolling back super block: "
11413b86f2f8SDavid du Colombier 					"bad replacement addr %V\n",
11423b86f2f8SDavid du Colombier 					argv0, p->old.score);
11435e96a66cSDavid du Colombier 				abort();
11445e96a66cSDavid du Colombier 			}
11455e96a66cSDavid du Colombier 			super.active = addr;
11465e96a66cSDavid du Colombier 			superPack(&super, buf);
11475e96a66cSDavid du Colombier 			continue;
11485e96a66cSDavid du Colombier 		}
114961201b97SDavid du Colombier 		if(b->l.type == BtDir)
115061201b97SDavid du Colombier 			memmove(buf+p->index*VtEntrySize, p->old.entry, VtEntrySize);
115161201b97SDavid du Colombier 		else
115261201b97SDavid du Colombier 			memmove(buf+p->index*VtScoreSize, p->old.score, VtScoreSize);
11535e96a66cSDavid du Colombier 	}
11545e96a66cSDavid du Colombier 	return buf;
11555e96a66cSDavid du Colombier }
11565e96a66cSDavid du Colombier 
11575e96a66cSDavid du Colombier /*
11585e96a66cSDavid du Colombier  * Try to write block b.
11595e96a66cSDavid du Colombier  * If b depends on other blocks:
11605e96a66cSDavid du Colombier  *
11615e96a66cSDavid du Colombier  *	If the block has been written out, remove the dependency.
11627abd426fSDavid du Colombier  *	If the dependency is replaced by a more recent dependency,
11637abd426fSDavid du Colombier  *		throw it out.
11645e96a66cSDavid du Colombier  *	If we know how to write out an old version of b that doesn't
11655e96a66cSDavid du Colombier  *		depend on it, do that.
11665e96a66cSDavid du Colombier  *
11675e96a66cSDavid du Colombier  *	Otherwise, bail.
11685e96a66cSDavid du Colombier  */
11695e96a66cSDavid du Colombier int
blockWrite(Block * b,int waitlock)1170*e12a9870SDavid du Colombier blockWrite(Block *b, int waitlock)
11715e96a66cSDavid du Colombier {
117261201b97SDavid du Colombier 	uchar *dmap;
11735e96a66cSDavid du Colombier 	Cache *c;
11745e96a66cSDavid du Colombier 	BList *p, **pp;
11755e96a66cSDavid du Colombier 	Block *bb;
11765e96a66cSDavid du Colombier 	int lockfail;
11775e96a66cSDavid du Colombier 
11785e96a66cSDavid du Colombier 	c = b->c;
11795e96a66cSDavid du Colombier 
11805e96a66cSDavid du Colombier 	if(b->iostate != BioDirty)
11815e96a66cSDavid du Colombier 		return 1;
11825e96a66cSDavid du Colombier 
118361201b97SDavid du Colombier 	dmap = b->dmap;
118461201b97SDavid du Colombier 	memset(dmap, 0, c->ndmap);
11855e96a66cSDavid du Colombier 	pp = &b->prior;
11865e96a66cSDavid du Colombier 	for(p=*pp; p; p=*pp){
118761201b97SDavid du Colombier 		if(p->index >= 0){
118861201b97SDavid du Colombier 			/* more recent dependency has succeeded; this one can go */
118961201b97SDavid du Colombier 			if(dmap[p->index/8] & (1<<(p->index%8)))
119061201b97SDavid du Colombier 				goto ignblock;
119161201b97SDavid du Colombier 		}
119261201b97SDavid du Colombier 
119361201b97SDavid du Colombier 		lockfail = 0;
1194*e12a9870SDavid du Colombier 		bb = _cacheLocalLookup(c, p->part, p->addr, p->vers, waitlock,
11952651f6bbSDavid du Colombier 			&lockfail);
11965e96a66cSDavid du Colombier 		if(bb == nil){
11975e96a66cSDavid du Colombier 			if(lockfail)
11985e96a66cSDavid du Colombier 				return 0;
119961201b97SDavid du Colombier 			/* block not in cache => was written already */
120061201b97SDavid du Colombier 			dmap[p->index/8] |= 1<<(p->index%8);
120161201b97SDavid du Colombier 			goto ignblock;
12025e96a66cSDavid du Colombier 		}
12035e96a66cSDavid du Colombier 
12045e96a66cSDavid du Colombier 		/*
12055e96a66cSDavid du Colombier 		 * same version of block is still in cache.
12065e96a66cSDavid du Colombier 		 *
12075e96a66cSDavid du Colombier 		 * the assertion is true because the block still has version p->vers,
12085e96a66cSDavid du Colombier 		 * which means it hasn't been written out since we last saw it.
12095e96a66cSDavid du Colombier 		 */
12107abd426fSDavid du Colombier 		if(bb->iostate != BioDirty){
12113b86f2f8SDavid du Colombier 			fprint(2, "%s: %d:%x:%d iostate is %d in blockWrite\n",
12123b86f2f8SDavid du Colombier 				argv0, bb->part, bb->addr, bb->l.type, bb->iostate);
12137abd426fSDavid du Colombier 			/* probably BioWriting if it happens? */
12147f1bc48aSDavid du Colombier 			if(bb->iostate == BioClean)
12157f1bc48aSDavid du Colombier 				goto ignblock;
12167abd426fSDavid du Colombier 		}
12177abd426fSDavid du Colombier 
12185e96a66cSDavid du Colombier 		blockPut(bb);
12195e96a66cSDavid du Colombier 
12205e96a66cSDavid du Colombier 		if(p->index < 0){
12215e96a66cSDavid du Colombier 			/*
12225e96a66cSDavid du Colombier 			 * We don't know how to temporarily undo
12235e96a66cSDavid du Colombier 			 * b's dependency on bb, so just don't write b yet.
12245e96a66cSDavid du Colombier 			 */
12253b86f2f8SDavid du Colombier 			if(0) fprint(2, "%s: blockWrite skipping %d %x %d %d; need to write %d %x %d\n",
12263b86f2f8SDavid du Colombier 				argv0, b->part, b->addr, b->vers, b->l.type, p->part, p->addr, bb->vers);
12275e96a66cSDavid du Colombier 			return 0;
12285e96a66cSDavid du Colombier 		}
12295e96a66cSDavid du Colombier 		/* keep walking down the list */
12305e96a66cSDavid du Colombier 		pp = &p->next;
123161201b97SDavid du Colombier 		continue;
123261201b97SDavid du Colombier 
123361201b97SDavid du Colombier ignblock:
123461201b97SDavid du Colombier 		*pp = p->next;
123561201b97SDavid du Colombier 		blistFree(c, p);
123661201b97SDavid du Colombier 		continue;
12375e96a66cSDavid du Colombier 	}
12385e96a66cSDavid du Colombier 
1239867bfcc6SDavid du Colombier 	/*
1240867bfcc6SDavid du Colombier 	 * DiskWrite must never be called with a double-locked block.
1241867bfcc6SDavid du Colombier 	 * This call to diskWrite is okay because blockWrite is only called
1242867bfcc6SDavid du Colombier 	 * from the cache flush thread, which never double-locks a block.
1243867bfcc6SDavid du Colombier 	 */
12445e96a66cSDavid du Colombier 	diskWrite(c->disk, b);
12455e96a66cSDavid du Colombier 	return 1;
12465e96a66cSDavid du Colombier }
12475e96a66cSDavid du Colombier 
12485e96a66cSDavid du Colombier /*
12495e96a66cSDavid du Colombier  * Change the I/O state of block b.
12505e96a66cSDavid du Colombier  * Just an assignment except for magic in
12515e96a66cSDavid du Colombier  * switch statement (read comments there).
12525e96a66cSDavid du Colombier  */
12535e96a66cSDavid du Colombier void
blockSetIOState(Block * b,int iostate)12545e96a66cSDavid du Colombier blockSetIOState(Block *b, int iostate)
12555e96a66cSDavid du Colombier {
12565e96a66cSDavid du Colombier 	int dowakeup;
12575e96a66cSDavid du Colombier 	Cache *c;
12585e96a66cSDavid du Colombier 	BList *p, *q;
12595e96a66cSDavid du Colombier 
12603b86f2f8SDavid du Colombier if(0) fprint(2, "%s: iostate part=%d addr=%x %s->%s\n", argv0, b->part, b->addr, bioStr(b->iostate), bioStr(iostate));
12615e96a66cSDavid du Colombier 
12625e96a66cSDavid du Colombier 	c = b->c;
12635e96a66cSDavid du Colombier 
12645e96a66cSDavid du Colombier 	dowakeup = 0;
12655e96a66cSDavid du Colombier 	switch(iostate){
12665e96a66cSDavid du Colombier 	default:
12675e96a66cSDavid du Colombier 		abort();
12685e96a66cSDavid du Colombier 	case BioEmpty:
12695e96a66cSDavid du Colombier 		assert(!b->uhead);
12705e96a66cSDavid du Colombier 		break;
12715e96a66cSDavid du Colombier 	case BioLabel:
12725e96a66cSDavid du Colombier 		assert(!b->uhead);
12735e96a66cSDavid du Colombier 		break;
12745e96a66cSDavid du Colombier 	case BioClean:
12755e96a66cSDavid du Colombier 		bwatchDependency(b);
12765e96a66cSDavid du Colombier 		/*
12775e96a66cSDavid du Colombier 		 * If b->prior is set, it means a write just finished.
12785e96a66cSDavid du Colombier 		 * The prior list isn't needed anymore.
12795e96a66cSDavid du Colombier 		 */
12805e96a66cSDavid du Colombier 		for(p=b->prior; p; p=q){
12815e96a66cSDavid du Colombier 			q = p->next;
12825e96a66cSDavid du Colombier 			blistFree(c, p);
12835e96a66cSDavid du Colombier 		}
12845e96a66cSDavid du Colombier 		b->prior = nil;
12855e96a66cSDavid du Colombier 		/*
12865e96a66cSDavid du Colombier 		 * Freeing a block or just finished a write.
12875e96a66cSDavid du Colombier 		 * Move the blocks from the per-block unlink
12885e96a66cSDavid du Colombier 		 * queue to the cache unlink queue.
12895e96a66cSDavid du Colombier 		 */
12905e96a66cSDavid du Colombier 		if(b->iostate == BioDirty || b->iostate == BioWriting){
12915e96a66cSDavid du Colombier 			vtLock(c->lk);
12925e96a66cSDavid du Colombier 			c->ndirty--;
129361201b97SDavid du Colombier 			b->iostate = iostate;	/* change here to keep in sync with ndirty */
12945e96a66cSDavid du Colombier 			b->vers = c->vers++;
12955e96a66cSDavid du Colombier 			if(b->uhead){
12965e96a66cSDavid du Colombier 				/* add unlink blocks to unlink queue */
12975e96a66cSDavid du Colombier 				if(c->uhead == nil){
12985e96a66cSDavid du Colombier 					c->uhead = b->uhead;
12995e96a66cSDavid du Colombier 					vtWakeup(c->unlink);
13005e96a66cSDavid du Colombier 				}else
13015e96a66cSDavid du Colombier 					c->utail->next = b->uhead;
13025e96a66cSDavid du Colombier 				c->utail = b->utail;
13035e96a66cSDavid du Colombier 				b->uhead = nil;
13045e96a66cSDavid du Colombier 			}
13055e96a66cSDavid du Colombier 			vtUnlock(c->lk);
13065e96a66cSDavid du Colombier 		}
13075e96a66cSDavid du Colombier 		assert(!b->uhead);
13085e96a66cSDavid du Colombier 		dowakeup = 1;
13095e96a66cSDavid du Colombier 		break;
13105e96a66cSDavid du Colombier 	case BioDirty:
13115e96a66cSDavid du Colombier 		/*
13125e96a66cSDavid du Colombier 		 * Wrote out an old version of the block (see blockRollback).
13135e96a66cSDavid du Colombier 		 * Bump a version count, leave it dirty.
13145e96a66cSDavid du Colombier 		 */
13155e96a66cSDavid du Colombier 		if(b->iostate == BioWriting){
13165e96a66cSDavid du Colombier 			vtLock(c->lk);
13175e96a66cSDavid du Colombier 			b->vers = c->vers++;
13185e96a66cSDavid du Colombier 			vtUnlock(c->lk);
13195e96a66cSDavid du Colombier 			dowakeup = 1;
13205e96a66cSDavid du Colombier 		}
13215e96a66cSDavid du Colombier 		break;
13225e96a66cSDavid du Colombier 	case BioReading:
13235e96a66cSDavid du Colombier 	case BioWriting:
13245e96a66cSDavid du Colombier 		/*
13255e96a66cSDavid du Colombier 		 * Adding block to disk queue.  Bump reference count.
13265e96a66cSDavid du Colombier 		 * diskThread decs the count later by calling blockPut.
13275e96a66cSDavid du Colombier 		 * This is here because we need to lock c->lk to
13285e96a66cSDavid du Colombier 		 * manipulate the ref count.
13295e96a66cSDavid du Colombier 		 */
13305e96a66cSDavid du Colombier 		vtLock(c->lk);
13315e96a66cSDavid du Colombier 		b->ref++;
13325e96a66cSDavid du Colombier 		vtUnlock(c->lk);
13335e96a66cSDavid du Colombier 		break;
13345e96a66cSDavid du Colombier 	case BioReadError:
13355e96a66cSDavid du Colombier 	case BioVentiError:
13365e96a66cSDavid du Colombier 		/*
13375e96a66cSDavid du Colombier 		 * Oops.
13385e96a66cSDavid du Colombier 		 */
13395e96a66cSDavid du Colombier 		dowakeup = 1;
13405e96a66cSDavid du Colombier 		break;
13415e96a66cSDavid du Colombier 	}
13425e96a66cSDavid du Colombier 	b->iostate = iostate;
13435e96a66cSDavid du Colombier 	/*
13445e96a66cSDavid du Colombier 	 * Now that the state has changed, we can wake the waiters.
13455e96a66cSDavid du Colombier 	 */
13465e96a66cSDavid du Colombier 	if(dowakeup)
13475e96a66cSDavid du Colombier 		vtWakeupAll(b->ioready);
13485e96a66cSDavid du Colombier }
13495e96a66cSDavid du Colombier 
1350e569ccb5SDavid du Colombier /*
1351e569ccb5SDavid du Colombier  * The active file system is a tree of blocks.
1352e569ccb5SDavid du Colombier  * When we add snapshots to the mix, the entire file system
1353e569ccb5SDavid du Colombier  * becomes a dag and thus requires a bit more care.
1354e569ccb5SDavid du Colombier  *
1355e569ccb5SDavid du Colombier  * The life of the file system is divided into epochs.  A snapshot
1356e569ccb5SDavid du Colombier  * ends one epoch and begins the next.  Each file system block
1357e569ccb5SDavid du Colombier  * is marked with the epoch in which it was created (b.epoch).
1358e569ccb5SDavid du Colombier  * When the block is unlinked from the file system (closed), it is marked
1359e569ccb5SDavid du Colombier  * with the epoch in which it was removed (b.epochClose).
1360e569ccb5SDavid du Colombier  * Once we have discarded or archived all snapshots up to
1361e569ccb5SDavid du Colombier  * b.epochClose, we can reclaim the block.
1362e569ccb5SDavid du Colombier  *
1363e569ccb5SDavid du Colombier  * If a block was created in a past epoch but is not yet closed,
1364e569ccb5SDavid du Colombier  * it is treated as copy-on-write.  Of course, in order to insert the
1365e569ccb5SDavid du Colombier  * new pointer into the tree, the parent must be made writable,
1366e569ccb5SDavid du Colombier  * and so on up the tree.  The recursion stops because the root
1367e569ccb5SDavid du Colombier  * block is always writable.
1368e569ccb5SDavid du Colombier  *
1369e569ccb5SDavid du Colombier  * If blocks are never closed, they will never be reused, and
1370e569ccb5SDavid du Colombier  * we will run out of disk space.  But marking a block as closed
1371e569ccb5SDavid du Colombier  * requires some care about dependencies and write orderings.
1372e569ccb5SDavid du Colombier  *
1373e569ccb5SDavid du Colombier  * (1) If a block p points at a copy-on-write block b and we
1374e569ccb5SDavid du Colombier  * copy b to create bb, then p must be written out after bb and
1375e569ccb5SDavid du Colombier  * lbb (bb's label block).
1376e569ccb5SDavid du Colombier  *
1377e569ccb5SDavid du Colombier  * (2) We have to mark b as closed, but only after we switch
1378e569ccb5SDavid du Colombier  * the pointer, so lb must be written out after p.  In fact, we
1379e569ccb5SDavid du Colombier  * can't even update the in-memory copy, or the cache might
1380e569ccb5SDavid du Colombier  * mistakenly give out b for reuse before p gets written.
1381e569ccb5SDavid du Colombier  *
1382e569ccb5SDavid du Colombier  * CacheAllocBlock's call to blockSetLabel records a "bb after lbb" dependency.
1383e569ccb5SDavid du Colombier  * The caller is expected to record a "p after bb" dependency
1384e569ccb5SDavid du Colombier  * to finish (1), and also expected to call blockRemoveLink
1385e569ccb5SDavid du Colombier  * to arrange for (2) to happen once p is written.
1386e569ccb5SDavid du Colombier  *
1387e569ccb5SDavid du Colombier  * Until (2) happens, some pieces of the code (e.g., the archiver)
1388e569ccb5SDavid du Colombier  * still need to know whether a block has been copied, so we
1389e569ccb5SDavid du Colombier  * set the BsCopied bit in the label and force that to disk *before*
1390e569ccb5SDavid du Colombier  * the copy gets written out.
1391e569ccb5SDavid du Colombier  */
1392e569ccb5SDavid du Colombier Block*
blockCopy(Block * b,u32int tag,u32int ehi,u32int elo)1393e569ccb5SDavid du Colombier blockCopy(Block *b, u32int tag, u32int ehi, u32int elo)
1394e569ccb5SDavid du Colombier {
1395e569ccb5SDavid du Colombier 	Block *bb, *lb;
1396e569ccb5SDavid du Colombier 	Label l;
1397e569ccb5SDavid du Colombier 
1398e569ccb5SDavid du Colombier 	if((b->l.state&BsClosed) || b->l.epoch >= ehi)
13993b86f2f8SDavid du Colombier 		fprint(2, "%s: blockCopy %#ux %L but fs is [%ud,%ud]\n",
14003b86f2f8SDavid du Colombier 			argv0, b->addr, &b->l, elo, ehi);
1401e569ccb5SDavid du Colombier 
1402e569ccb5SDavid du Colombier 	bb = cacheAllocBlock(b->c, b->l.type, tag, ehi, elo);
1403e569ccb5SDavid du Colombier 	if(bb == nil){
1404e569ccb5SDavid du Colombier 		blockPut(b);
1405e569ccb5SDavid du Colombier 		return nil;
1406e569ccb5SDavid du Colombier 	}
1407e569ccb5SDavid du Colombier 
1408e569ccb5SDavid du Colombier 	/*
1409e569ccb5SDavid du Colombier 	 * Update label so we know the block has been copied.
1410e569ccb5SDavid du Colombier 	 * (It will be marked closed once it has been unlinked from
1411e569ccb5SDavid du Colombier 	 * the tree.)  This must follow cacheAllocBlock since we
1412e569ccb5SDavid du Colombier 	 * can't be holding onto lb when we call cacheAllocBlock.
1413e569ccb5SDavid du Colombier 	 */
1414e569ccb5SDavid du Colombier 	if((b->l.state&BsCopied)==0)
1415e569ccb5SDavid du Colombier 	if(b->part == PartData){	/* not the superblock */
1416e569ccb5SDavid du Colombier 		l = b->l;
1417e569ccb5SDavid du Colombier 		l.state |= BsCopied;
1418e569ccb5SDavid du Colombier 		lb = _blockSetLabel(b, &l);
1419e569ccb5SDavid du Colombier 		if(lb == nil){
1420e569ccb5SDavid du Colombier 			/* can't set label => can't copy block */
1421e569ccb5SDavid du Colombier 			blockPut(b);
1422e569ccb5SDavid du Colombier 			l.type = BtMax;
1423e569ccb5SDavid du Colombier 			l.state = BsFree;
1424e569ccb5SDavid du Colombier 			l.epoch = 0;
1425e569ccb5SDavid du Colombier 			l.epochClose = 0;
1426e569ccb5SDavid du Colombier 			l.tag = 0;
1427e569ccb5SDavid du Colombier 			blockSetLabel(bb, &l, 0);
1428e569ccb5SDavid du Colombier 			blockPut(bb);
1429e569ccb5SDavid du Colombier 			return nil;
1430e569ccb5SDavid du Colombier 		}
1431e569ccb5SDavid du Colombier 		blockDependency(bb, lb, -1, nil, nil);
1432e569ccb5SDavid du Colombier 		blockPut(lb);
1433e569ccb5SDavid du Colombier 	}
1434e569ccb5SDavid du Colombier 
1435e569ccb5SDavid du Colombier 	memmove(bb->data, b->data, b->c->size);
1436e569ccb5SDavid du Colombier 	blockDirty(bb);
1437e569ccb5SDavid du Colombier 	blockPut(b);
1438e569ccb5SDavid du Colombier 	return bb;
1439e569ccb5SDavid du Colombier }
1440e569ccb5SDavid du Colombier 
1441e569ccb5SDavid du Colombier /*
1442e569ccb5SDavid du Colombier  * Block b once pointed at the block bb at addr/type/tag, but no longer does.
1443e569ccb5SDavid du Colombier  * If recurse is set, we are unlinking all of bb's children as well.
1444e569ccb5SDavid du Colombier  *
1445e569ccb5SDavid du Colombier  * We can't reclaim bb (or its kids) until the block b gets written to disk.  We add
1446e569ccb5SDavid du Colombier  * the relevant information to b's list of unlinked blocks.  Once b is written,
1447e569ccb5SDavid du Colombier  * the list will be queued for processing.
1448e569ccb5SDavid du Colombier  *
1449e569ccb5SDavid du Colombier  * If b depends on bb, it doesn't anymore, so we remove bb from the prior list.
1450e569ccb5SDavid du Colombier  */
1451e569ccb5SDavid du Colombier void
blockRemoveLink(Block * b,u32int addr,int type,u32int tag,int recurse)1452e569ccb5SDavid du Colombier blockRemoveLink(Block *b, u32int addr, int type, u32int tag, int recurse)
1453e569ccb5SDavid du Colombier {
1454e569ccb5SDavid du Colombier 	BList *p, **pp, bl;
1455e569ccb5SDavid du Colombier 
1456e569ccb5SDavid du Colombier 	/* remove bb from prior list */
1457e569ccb5SDavid du Colombier 	for(pp=&b->prior; (p=*pp)!=nil; ){
1458e569ccb5SDavid du Colombier 		if(p->part == PartData && p->addr == addr){
1459e569ccb5SDavid du Colombier 			*pp = p->next;
1460e569ccb5SDavid du Colombier 			blistFree(b->c, p);
1461e569ccb5SDavid du Colombier 		}else
1462e569ccb5SDavid du Colombier 			pp = &p->next;
1463e569ccb5SDavid du Colombier 	}
1464e569ccb5SDavid du Colombier 
1465e569ccb5SDavid du Colombier 	bl.part = PartData;
1466e569ccb5SDavid du Colombier 	bl.addr = addr;
1467e569ccb5SDavid du Colombier 	bl.type = type;
1468e569ccb5SDavid du Colombier 	bl.tag = tag;
1469e569ccb5SDavid du Colombier 	if(b->l.epoch == 0)
1470e569ccb5SDavid du Colombier 		assert(b->part == PartSuper);
1471e569ccb5SDavid du Colombier 	bl.epoch = b->l.epoch;
1472e569ccb5SDavid du Colombier 	bl.next = nil;
1473e569ccb5SDavid du Colombier 	bl.recurse = recurse;
1474e569ccb5SDavid du Colombier 
1475*e12a9870SDavid du Colombier 	if(b->part == PartSuper && b->iostate == BioClean)
1476*e12a9870SDavid du Colombier 		p = nil;
1477*e12a9870SDavid du Colombier 	else
1478e569ccb5SDavid du Colombier 		p = blistAlloc(b);
1479e569ccb5SDavid du Colombier 	if(p == nil){
1480e569ccb5SDavid du Colombier 		/*
1481*e12a9870SDavid du Colombier 		 * b has already been written to disk.
1482e569ccb5SDavid du Colombier 		 */
1483e569ccb5SDavid du Colombier 		doRemoveLink(b->c, &bl);
1484e569ccb5SDavid du Colombier 		return;
1485e569ccb5SDavid du Colombier 	}
1486e569ccb5SDavid du Colombier 
1487e569ccb5SDavid du Colombier 	/* Uhead is only processed when the block goes from Dirty -> Clean */
1488e569ccb5SDavid du Colombier 	assert(b->iostate == BioDirty);
1489e569ccb5SDavid du Colombier 
1490e569ccb5SDavid du Colombier 	*p = bl;
1491e569ccb5SDavid du Colombier 	if(b->uhead == nil)
1492e569ccb5SDavid du Colombier 		b->uhead = p;
1493e569ccb5SDavid du Colombier 	else
1494e569ccb5SDavid du Colombier 		b->utail->next = p;
1495e569ccb5SDavid du Colombier 	b->utail = p;
1496e569ccb5SDavid du Colombier }
1497e569ccb5SDavid du Colombier 
1498e569ccb5SDavid du Colombier /*
1499e569ccb5SDavid du Colombier  * Process removal of a single block and perhaps its children.
1500e569ccb5SDavid du Colombier  */
1501e569ccb5SDavid du Colombier static void
doRemoveLink(Cache * c,BList * p)1502e569ccb5SDavid du Colombier doRemoveLink(Cache *c, BList *p)
1503e569ccb5SDavid du Colombier {
15040b9a5132SDavid du Colombier 	int i, n, recurse;
1505e569ccb5SDavid du Colombier 	u32int a;
1506e569ccb5SDavid du Colombier 	Block *b;
1507e569ccb5SDavid du Colombier 	Label l;
1508e569ccb5SDavid du Colombier 	BList bl;
1509e569ccb5SDavid du Colombier 
15100b9a5132SDavid du Colombier 	recurse = (p->recurse && p->type != BtData && p->type != BtDir);
15110b9a5132SDavid du Colombier 
15120b9a5132SDavid du Colombier 	/*
15130b9a5132SDavid du Colombier 	 * We're not really going to overwrite b, but if we're not
15140b9a5132SDavid du Colombier 	 * going to look at its contents, there is no point in reading
15150b9a5132SDavid du Colombier 	 * them from the disk.
15160b9a5132SDavid du Colombier 	 */
15170b9a5132SDavid du Colombier 	b = cacheLocalData(c, p->addr, p->type, p->tag, recurse ? OReadOnly : OOverWrite, 0);
1518e569ccb5SDavid du Colombier 	if(b == nil)
1519e569ccb5SDavid du Colombier 		return;
1520e569ccb5SDavid du Colombier 
1521e569ccb5SDavid du Colombier 	/*
1522e569ccb5SDavid du Colombier 	 * When we're unlinking from the superblock, close with the next epoch.
1523e569ccb5SDavid du Colombier 	 */
1524e569ccb5SDavid du Colombier 	if(p->epoch == 0)
1525e569ccb5SDavid du Colombier 		p->epoch = b->l.epoch+1;
1526e569ccb5SDavid du Colombier 
1527e569ccb5SDavid du Colombier 	/* sanity check */
1528e569ccb5SDavid du Colombier 	if(b->l.epoch > p->epoch){
15293b86f2f8SDavid du Colombier 		fprint(2, "%s: doRemoveLink: strange epoch %ud > %ud\n",
15303b86f2f8SDavid du Colombier 			argv0, b->l.epoch, p->epoch);
1531e569ccb5SDavid du Colombier 		blockPut(b);
1532e569ccb5SDavid du Colombier 		return;
1533e569ccb5SDavid du Colombier 	}
1534e569ccb5SDavid du Colombier 
15350b9a5132SDavid du Colombier 	if(recurse){
1536e569ccb5SDavid du Colombier 		n = c->size / VtScoreSize;
1537e569ccb5SDavid du Colombier 		for(i=0; i<n; i++){
1538e569ccb5SDavid du Colombier 			a = globalToLocal(b->data + i*VtScoreSize);
1539e569ccb5SDavid du Colombier 			if(a == NilBlock || !readLabel(c, &l, a))
1540e569ccb5SDavid du Colombier 				continue;
1541e569ccb5SDavid du Colombier 			if(l.state&BsClosed)
1542e569ccb5SDavid du Colombier 				continue;
1543e569ccb5SDavid du Colombier 			/*
1544e569ccb5SDavid du Colombier 			 * If stack space becomes an issue...
1545e569ccb5SDavid du Colombier 			p->addr = a;
1546e569ccb5SDavid du Colombier 			p->type = l.type;
1547e569ccb5SDavid du Colombier 			p->tag = l.tag;
1548e569ccb5SDavid du Colombier 			doRemoveLink(c, p);
1549e569ccb5SDavid du Colombier 			 */
1550e569ccb5SDavid du Colombier 
1551e569ccb5SDavid du Colombier 			bl.part = PartData;
1552e569ccb5SDavid du Colombier 			bl.addr = a;
1553e569ccb5SDavid du Colombier 			bl.type = l.type;
1554e569ccb5SDavid du Colombier 			bl.tag = l.tag;
1555e569ccb5SDavid du Colombier 			bl.epoch = p->epoch;
1556e569ccb5SDavid du Colombier 			bl.next = nil;
1557e569ccb5SDavid du Colombier 			bl.recurse = 1;
15580b9a5132SDavid du Colombier 			/* give up the block lock - share with others */
15590b9a5132SDavid du Colombier 			blockPut(b);
1560e569ccb5SDavid du Colombier 			doRemoveLink(c, &bl);
15610b9a5132SDavid du Colombier 			b = cacheLocalData(c, p->addr, p->type, p->tag, OReadOnly, 0);
15620b9a5132SDavid du Colombier 			if(b == nil){
15633b86f2f8SDavid du Colombier 				fprint(2, "%s: warning: lost block in doRemoveLink\n",
15643b86f2f8SDavid du Colombier 					argv0);
15650b9a5132SDavid du Colombier 				return;
15660b9a5132SDavid du Colombier 			}
1567e569ccb5SDavid du Colombier 		}
1568e569ccb5SDavid du Colombier 	}
1569e569ccb5SDavid du Colombier 
1570e569ccb5SDavid du Colombier 	l = b->l;
1571e569ccb5SDavid du Colombier 	l.state |= BsClosed;
1572e569ccb5SDavid du Colombier 	l.epochClose = p->epoch;
1573225077b0SDavid du Colombier 	if(l.epochClose == l.epoch){
1574225077b0SDavid du Colombier 		vtLock(c->fl->lk);
1575225077b0SDavid du Colombier 		if(l.epoch == c->fl->epochLow)
1576225077b0SDavid du Colombier 			c->fl->nused--;
1577225077b0SDavid du Colombier 		blockSetLabel(b, &l, 0);
1578225077b0SDavid du Colombier 		vtUnlock(c->fl->lk);
1579225077b0SDavid du Colombier 	}else
1580e569ccb5SDavid du Colombier 		blockSetLabel(b, &l, 0);
1581e569ccb5SDavid du Colombier 	blockPut(b);
1582e569ccb5SDavid du Colombier }
1583e569ccb5SDavid du Colombier 
1584e569ccb5SDavid du Colombier /*
1585e569ccb5SDavid du Colombier  * Allocate a BList so that we can record a dependency
1586e569ccb5SDavid du Colombier  * or queue a removal related to block b.
1587e569ccb5SDavid du Colombier  * If we can't find a BList, we write out b and return nil.
1588e569ccb5SDavid du Colombier  */
1589e569ccb5SDavid du Colombier static BList *
blistAlloc(Block * b)1590e569ccb5SDavid du Colombier blistAlloc(Block *b)
1591e569ccb5SDavid du Colombier {
1592e569ccb5SDavid du Colombier 	Cache *c;
1593e569ccb5SDavid du Colombier 	BList *p;
1594e569ccb5SDavid du Colombier 
1595e569ccb5SDavid du Colombier 	if(b->iostate != BioDirty){
1596e569ccb5SDavid du Colombier 		/*
1597e569ccb5SDavid du Colombier 		 * should not happen anymore -
1598e569ccb5SDavid du Colombier 	 	 * blockDirty used to flush but no longer does.
1599e569ccb5SDavid du Colombier 		 */
1600e569ccb5SDavid du Colombier 		assert(b->iostate == BioClean);
16013b86f2f8SDavid du Colombier 		fprint(2, "%s: blistAlloc: called on clean block\n", argv0);
1602e569ccb5SDavid du Colombier 		return nil;
1603e569ccb5SDavid du Colombier 	}
1604e569ccb5SDavid du Colombier 
1605e569ccb5SDavid du Colombier 	c = b->c;
1606e569ccb5SDavid du Colombier 	vtLock(c->lk);
1607e569ccb5SDavid du Colombier 	if(c->blfree == nil){
1608e569ccb5SDavid du Colombier 		/*
1609e569ccb5SDavid du Colombier 		 * No free BLists.  What are our options?
1610e569ccb5SDavid du Colombier 		 */
1611e569ccb5SDavid du Colombier 
1612e569ccb5SDavid du Colombier 		/* Block has no priors? Just write it. */
1613e569ccb5SDavid du Colombier 		if(b->prior == nil){
1614e569ccb5SDavid du Colombier 			vtUnlock(c->lk);
1615e569ccb5SDavid du Colombier 			diskWriteAndWait(c->disk, b);
1616e569ccb5SDavid du Colombier 			return nil;
1617e569ccb5SDavid du Colombier 		}
1618e569ccb5SDavid du Colombier 
1619e569ccb5SDavid du Colombier 		/*
1620e569ccb5SDavid du Colombier 		 * Wake the flush thread, which will hopefully free up
1621e569ccb5SDavid du Colombier 		 * some BLists for us.  We used to flush a block from
1622e569ccb5SDavid du Colombier 		 * our own prior list and reclaim that BList, but this is
1623e569ccb5SDavid du Colombier 		 * a no-no: some of the blocks on our prior list may
1624e569ccb5SDavid du Colombier 		 * be locked by our caller.  Or maybe their label blocks
1625e569ccb5SDavid du Colombier 		 * are locked by our caller.  In any event, it's too hard
1626e569ccb5SDavid du Colombier 		 * to make sure we can do I/O for ourselves.  Instead,
1627e569ccb5SDavid du Colombier 		 * we assume the flush thread will find something.
1628e569ccb5SDavid du Colombier 		 * (The flush thread never blocks waiting for a block,
1629e569ccb5SDavid du Colombier 		 * so it can't deadlock like we can.)
1630e569ccb5SDavid du Colombier 		 */
1631e569ccb5SDavid du Colombier 		while(c->blfree == nil){
1632e569ccb5SDavid du Colombier 			vtWakeup(c->flush);
1633e569ccb5SDavid du Colombier 			vtSleep(c->blrend);
16340b9a5132SDavid du Colombier 			if(c->blfree == nil)
16353b86f2f8SDavid du Colombier 				fprint(2, "%s: flushing for blists\n", argv0);
1636e569ccb5SDavid du Colombier 		}
1637e569ccb5SDavid du Colombier 	}
1638e569ccb5SDavid du Colombier 
1639e569ccb5SDavid du Colombier 	p = c->blfree;
1640e569ccb5SDavid du Colombier 	c->blfree = p->next;
1641e569ccb5SDavid du Colombier 	vtUnlock(c->lk);
1642e569ccb5SDavid du Colombier 	return p;
1643e569ccb5SDavid du Colombier }
1644e569ccb5SDavid du Colombier 
1645e569ccb5SDavid du Colombier static void
blistFree(Cache * c,BList * bl)1646e569ccb5SDavid du Colombier blistFree(Cache *c, BList *bl)
1647e569ccb5SDavid du Colombier {
1648e569ccb5SDavid du Colombier 	vtLock(c->lk);
1649e569ccb5SDavid du Colombier 	bl->next = c->blfree;
1650e569ccb5SDavid du Colombier 	c->blfree = bl;
1651e569ccb5SDavid du Colombier 	vtWakeup(c->blrend);
1652e569ccb5SDavid du Colombier 	vtUnlock(c->lk);
1653e569ccb5SDavid du Colombier }
1654e569ccb5SDavid du Colombier 
16555e96a66cSDavid du Colombier char*
bsStr(int state)16565e96a66cSDavid du Colombier bsStr(int state)
16575e96a66cSDavid du Colombier {
16585e96a66cSDavid du Colombier 	static char s[100];
16595e96a66cSDavid du Colombier 
16605e96a66cSDavid du Colombier 	if(state == BsFree)
16615e96a66cSDavid du Colombier 		return "Free";
16625e96a66cSDavid du Colombier 	if(state == BsBad)
16635e96a66cSDavid du Colombier 		return "Bad";
16645e96a66cSDavid du Colombier 
16655e96a66cSDavid du Colombier 	sprint(s, "%x", state);
16665e96a66cSDavid du Colombier 	if(!(state&BsAlloc))
16675e96a66cSDavid du Colombier 		strcat(s, ",Free");	/* should not happen */
16685e96a66cSDavid du Colombier 	if(state&BsCopied)
16695e96a66cSDavid du Colombier 		strcat(s, ",Copied");
16705e96a66cSDavid du Colombier 	if(state&BsVenti)
16715e96a66cSDavid du Colombier 		strcat(s, ",Venti");
16725e96a66cSDavid du Colombier 	if(state&BsClosed)
16735e96a66cSDavid du Colombier 		strcat(s, ",Closed");
16745e96a66cSDavid du Colombier 	return s;
16755e96a66cSDavid du Colombier }
16765e96a66cSDavid du Colombier 
16775e96a66cSDavid du Colombier char *
bioStr(int iostate)16785e96a66cSDavid du Colombier bioStr(int iostate)
16795e96a66cSDavid du Colombier {
16805e96a66cSDavid du Colombier 	switch(iostate){
16815e96a66cSDavid du Colombier 	default:
16825e96a66cSDavid du Colombier 		return "Unknown!!";
16835e96a66cSDavid du Colombier 	case BioEmpty:
16845e96a66cSDavid du Colombier 		return "Empty";
16855e96a66cSDavid du Colombier 	case BioLabel:
16865e96a66cSDavid du Colombier 		return "Label";
16875e96a66cSDavid du Colombier 	case BioClean:
16885e96a66cSDavid du Colombier 		return "Clean";
16895e96a66cSDavid du Colombier 	case BioDirty:
16905e96a66cSDavid du Colombier 		return "Dirty";
16915e96a66cSDavid du Colombier 	case BioReading:
16925e96a66cSDavid du Colombier 		return "Reading";
16935e96a66cSDavid du Colombier 	case BioWriting:
16945e96a66cSDavid du Colombier 		return "Writing";
16955e96a66cSDavid du Colombier 	case BioReadError:
16965e96a66cSDavid du Colombier 		return "ReadError";
16975e96a66cSDavid du Colombier 	case BioVentiError:
16985e96a66cSDavid du Colombier 		return "VentiError";
16995e96a66cSDavid du Colombier 	case BioMax:
17005e96a66cSDavid du Colombier 		return "Max";
17015e96a66cSDavid du Colombier 	}
17025e96a66cSDavid du Colombier }
17035e96a66cSDavid du Colombier 
17045e96a66cSDavid du Colombier static char *bttab[] = {
17055e96a66cSDavid du Colombier 	"BtData",
17065e96a66cSDavid du Colombier 	"BtData+1",
17075e96a66cSDavid du Colombier 	"BtData+2",
17085e96a66cSDavid du Colombier 	"BtData+3",
17095e96a66cSDavid du Colombier 	"BtData+4",
17105e96a66cSDavid du Colombier 	"BtData+5",
17115e96a66cSDavid du Colombier 	"BtData+6",
17125e96a66cSDavid du Colombier 	"BtData+7",
17135e96a66cSDavid du Colombier 	"BtDir",
17145e96a66cSDavid du Colombier 	"BtDir+1",
17155e96a66cSDavid du Colombier 	"BtDir+2",
17165e96a66cSDavid du Colombier 	"BtDir+3",
17175e96a66cSDavid du Colombier 	"BtDir+4",
17185e96a66cSDavid du Colombier 	"BtDir+5",
17195e96a66cSDavid du Colombier 	"BtDir+6",
17205e96a66cSDavid du Colombier 	"BtDir+7",
17215e96a66cSDavid du Colombier };
17225e96a66cSDavid du Colombier 
17235e96a66cSDavid du Colombier char*
btStr(int type)17245e96a66cSDavid du Colombier btStr(int type)
17255e96a66cSDavid du Colombier {
17265e96a66cSDavid du Colombier 	if(type < nelem(bttab))
17275e96a66cSDavid du Colombier 		return bttab[type];
17285e96a66cSDavid du Colombier 	return "unknown";
17295e96a66cSDavid du Colombier }
17305e96a66cSDavid du Colombier 
17315e96a66cSDavid du Colombier int
labelFmt(Fmt * f)17325e96a66cSDavid du Colombier labelFmt(Fmt *f)
17335e96a66cSDavid du Colombier {
17345e96a66cSDavid du Colombier 	Label *l;
17355e96a66cSDavid du Colombier 
17365e96a66cSDavid du Colombier 	l = va_arg(f->args, Label*);
17375e96a66cSDavid du Colombier 	return fmtprint(f, "%s,%s,e=%ud,%d,tag=%#ux",
17385e96a66cSDavid du Colombier 		btStr(l->type), bsStr(l->state), l->epoch, (int)l->epochClose, l->tag);
17395e96a66cSDavid du Colombier }
17405e96a66cSDavid du Colombier 
17415e96a66cSDavid du Colombier int
scoreFmt(Fmt * f)17425e96a66cSDavid du Colombier scoreFmt(Fmt *f)
17435e96a66cSDavid du Colombier {
17445e96a66cSDavid du Colombier 	uchar *v;
17455e96a66cSDavid du Colombier 	int i;
17465e96a66cSDavid du Colombier 	u32int addr;
17475e96a66cSDavid du Colombier 
17485e96a66cSDavid du Colombier 	v = va_arg(f->args, uchar*);
17495e96a66cSDavid du Colombier 	if(v == nil){
17505e96a66cSDavid du Colombier 		fmtprint(f, "*");
17515e96a66cSDavid du Colombier 	}else if((addr = globalToLocal(v)) != NilBlock)
17525e96a66cSDavid du Colombier 		fmtprint(f, "0x%.8ux", addr);
17535e96a66cSDavid du Colombier 	else{
17545e96a66cSDavid du Colombier 		for(i = 0; i < VtScoreSize; i++)
17555e96a66cSDavid du Colombier 			fmtprint(f, "%2.2ux", v[i]);
17565e96a66cSDavid du Colombier 	}
17575e96a66cSDavid du Colombier 
17585e96a66cSDavid du Colombier 	return 0;
17595e96a66cSDavid du Colombier }
17605e96a66cSDavid du Colombier 
17615e96a66cSDavid du Colombier static int
upHeap(int i,Block * b)17625e96a66cSDavid du Colombier upHeap(int i, Block *b)
17635e96a66cSDavid du Colombier {
17645e96a66cSDavid du Colombier 	Block *bb;
17655e96a66cSDavid du Colombier 	u32int now;
17665e96a66cSDavid du Colombier 	int p;
17675e96a66cSDavid du Colombier 	Cache *c;
17685e96a66cSDavid du Colombier 
17695e96a66cSDavid du Colombier 	c = b->c;
17705e96a66cSDavid du Colombier 	now = c->now;
17715e96a66cSDavid du Colombier 	for(; i != 0; i = p){
17725e96a66cSDavid du Colombier 		p = (i - 1) >> 1;
17735e96a66cSDavid du Colombier 		bb = c->heap[p];
17745e96a66cSDavid du Colombier 		if(b->used - now >= bb->used - now)
17755e96a66cSDavid du Colombier 			break;
17765e96a66cSDavid du Colombier 		c->heap[i] = bb;
17775e96a66cSDavid du Colombier 		bb->heap = i;
17785e96a66cSDavid du Colombier 	}
17795e96a66cSDavid du Colombier 	c->heap[i] = b;
17805e96a66cSDavid du Colombier 	b->heap = i;
17815e96a66cSDavid du Colombier 
17825e96a66cSDavid du Colombier 	return i;
17835e96a66cSDavid du Colombier }
17845e96a66cSDavid du Colombier 
17855e96a66cSDavid du Colombier static int
downHeap(int i,Block * b)17865e96a66cSDavid du Colombier downHeap(int i, Block *b)
17875e96a66cSDavid du Colombier {
17885e96a66cSDavid du Colombier 	Block *bb;
17895e96a66cSDavid du Colombier 	u32int now;
17905e96a66cSDavid du Colombier 	int k;
17915e96a66cSDavid du Colombier 	Cache *c;
17925e96a66cSDavid du Colombier 
17935e96a66cSDavid du Colombier 	c = b->c;
17945e96a66cSDavid du Colombier 	now = c->now;
17955e96a66cSDavid du Colombier 	for(; ; i = k){
17965e96a66cSDavid du Colombier 		k = (i << 1) + 1;
17975e96a66cSDavid du Colombier 		if(k >= c->nheap)
17985e96a66cSDavid du Colombier 			break;
17995e96a66cSDavid du Colombier 		if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now)
18005e96a66cSDavid du Colombier 			k++;
18015e96a66cSDavid du Colombier 		bb = c->heap[k];
18025e96a66cSDavid du Colombier 		if(b->used - now <= bb->used - now)
18035e96a66cSDavid du Colombier 			break;
18045e96a66cSDavid du Colombier 		c->heap[i] = bb;
18055e96a66cSDavid du Colombier 		bb->heap = i;
18065e96a66cSDavid du Colombier 	}
18075e96a66cSDavid du Colombier 	c->heap[i] = b;
18085e96a66cSDavid du Colombier 	b->heap = i;
18095e96a66cSDavid du Colombier 	return i;
18105e96a66cSDavid du Colombier }
18115e96a66cSDavid du Colombier 
18125e96a66cSDavid du Colombier /*
18135e96a66cSDavid du Colombier  * Delete a block from the heap.
18145e96a66cSDavid du Colombier  * Called with c->lk held.
18155e96a66cSDavid du Colombier  */
18165e96a66cSDavid du Colombier static void
heapDel(Block * b)18175e96a66cSDavid du Colombier heapDel(Block *b)
18185e96a66cSDavid du Colombier {
18195e96a66cSDavid du Colombier 	int i, si;
18205e96a66cSDavid du Colombier 	Cache *c;
18215e96a66cSDavid du Colombier 
18225e96a66cSDavid du Colombier 	c = b->c;
18235e96a66cSDavid du Colombier 
18245e96a66cSDavid du Colombier 	si = b->heap;
18255e96a66cSDavid du Colombier 	if(si == BadHeap)
18265e96a66cSDavid du Colombier 		return;
18275e96a66cSDavid du Colombier 	b->heap = BadHeap;
18285e96a66cSDavid du Colombier 	c->nheap--;
18295e96a66cSDavid du Colombier 	if(si == c->nheap)
18305e96a66cSDavid du Colombier 		return;
18315e96a66cSDavid du Colombier 	b = c->heap[c->nheap];
18325e96a66cSDavid du Colombier 	i = upHeap(si, b);
18335e96a66cSDavid du Colombier 	if(i == si)
18345e96a66cSDavid du Colombier 		downHeap(i, b);
18355e96a66cSDavid du Colombier }
18365e96a66cSDavid du Colombier 
18375e96a66cSDavid du Colombier /*
18385e96a66cSDavid du Colombier  * Insert a block into the heap.
18395e96a66cSDavid du Colombier  * Called with c->lk held.
18405e96a66cSDavid du Colombier  */
18415e96a66cSDavid du Colombier static void
heapIns(Block * b)18425e96a66cSDavid du Colombier heapIns(Block *b)
18435e96a66cSDavid du Colombier {
18445e96a66cSDavid du Colombier 	assert(b->heap == BadHeap);
18455e96a66cSDavid du Colombier 	upHeap(b->c->nheap++, b);
1846fe853e23SDavid du Colombier 	vtWakeup(b->c->heapwait);
18475e96a66cSDavid du Colombier }
18485e96a66cSDavid du Colombier 
18495e96a66cSDavid du Colombier /*
18505e96a66cSDavid du Colombier  * Get just the label for a block.
18515e96a66cSDavid du Colombier  */
1852e569ccb5SDavid du Colombier int
readLabel(Cache * c,Label * l,u32int addr)18535e96a66cSDavid du Colombier readLabel(Cache *c, Label *l, u32int addr)
18545e96a66cSDavid du Colombier {
18555e96a66cSDavid du Colombier 	int lpb;
18565e96a66cSDavid du Colombier 	Block *b;
18575e96a66cSDavid du Colombier 	u32int a;
18585e96a66cSDavid du Colombier 
18595e96a66cSDavid du Colombier 	lpb = c->size / LabelSize;
18605e96a66cSDavid du Colombier 	a = addr / lpb;
18615e96a66cSDavid du Colombier 	b = cacheLocal(c, PartLabel, a, OReadOnly);
18625e96a66cSDavid du Colombier 	if(b == nil){
18635e96a66cSDavid du Colombier 		blockPut(b);
18645e96a66cSDavid du Colombier 		return 0;
18655e96a66cSDavid du Colombier 	}
18665e96a66cSDavid du Colombier 
18675e96a66cSDavid du Colombier 	if(!labelUnpack(l, b->data, addr%lpb)){
18685e96a66cSDavid du Colombier 		blockPut(b);
18695e96a66cSDavid du Colombier 		return 0;
18705e96a66cSDavid du Colombier 	}
18715e96a66cSDavid du Colombier 	blockPut(b);
18725e96a66cSDavid du Colombier 	return 1;
18735e96a66cSDavid du Colombier }
18745e96a66cSDavid du Colombier 
18755e96a66cSDavid du Colombier /*
18765e96a66cSDavid du Colombier  * Process unlink queue.
18775e96a66cSDavid du Colombier  * Called with c->lk held.
18785e96a66cSDavid du Colombier  */
18795e96a66cSDavid du Colombier static void
unlinkBody(Cache * c)18805e96a66cSDavid du Colombier unlinkBody(Cache *c)
18815e96a66cSDavid du Colombier {
18825e96a66cSDavid du Colombier 	BList *p;
18835e96a66cSDavid du Colombier 
18845e96a66cSDavid du Colombier 	while(c->uhead != nil){
18855e96a66cSDavid du Colombier 		p = c->uhead;
18865e96a66cSDavid du Colombier 		c->uhead = p->next;
18875e96a66cSDavid du Colombier 		vtUnlock(c->lk);
1888e569ccb5SDavid du Colombier 		doRemoveLink(c, p);
18895e96a66cSDavid du Colombier 		vtLock(c->lk);
18905e96a66cSDavid du Colombier 		p->next = c->blfree;
18915e96a66cSDavid du Colombier 		c->blfree = p;
18925e96a66cSDavid du Colombier 	}
18935e96a66cSDavid du Colombier }
18945e96a66cSDavid du Colombier 
18955e96a66cSDavid du Colombier /*
18965e96a66cSDavid du Colombier  * Occasionally unlink the blocks on the cache unlink queue.
18975e96a66cSDavid du Colombier  */
18985e96a66cSDavid du Colombier static void
unlinkThread(void * a)18995e96a66cSDavid du Colombier unlinkThread(void *a)
19005e96a66cSDavid du Colombier {
19015e96a66cSDavid du Colombier 	Cache *c = a;
19025e96a66cSDavid du Colombier 
19035e96a66cSDavid du Colombier 	vtThreadSetName("unlink");
19045e96a66cSDavid du Colombier 
19055e96a66cSDavid du Colombier 	vtLock(c->lk);
19065e96a66cSDavid du Colombier 	for(;;){
19075e96a66cSDavid du Colombier 		while(c->uhead == nil && c->die == nil)
19085e96a66cSDavid du Colombier 			vtSleep(c->unlink);
19095e96a66cSDavid du Colombier 		if(c->die != nil)
19105e96a66cSDavid du Colombier 			break;
19115e96a66cSDavid du Colombier 		unlinkBody(c);
19125e96a66cSDavid du Colombier 	}
19135e96a66cSDavid du Colombier 	c->ref--;
19145e96a66cSDavid du Colombier 	vtWakeup(c->die);
19155e96a66cSDavid du Colombier 	vtUnlock(c->lk);
19165e96a66cSDavid du Colombier }
19175e96a66cSDavid du Colombier 
19185e96a66cSDavid du Colombier static int
baddrCmp(void * a0,void * a1)19195e96a66cSDavid du Colombier baddrCmp(void *a0, void *a1)
19205e96a66cSDavid du Colombier {
19215e96a66cSDavid du Colombier 	BAddr *b0, *b1;
19225e96a66cSDavid du Colombier 	b0 = a0;
19235e96a66cSDavid du Colombier 	b1 = a1;
19245e96a66cSDavid du Colombier 
19255e96a66cSDavid du Colombier 	if(b0->part < b1->part)
19265e96a66cSDavid du Colombier 		return -1;
19275e96a66cSDavid du Colombier 	if(b0->part > b1->part)
19285e96a66cSDavid du Colombier 		return 1;
19295e96a66cSDavid du Colombier 	if(b0->addr < b1->addr)
19305e96a66cSDavid du Colombier 		return -1;
19315e96a66cSDavid du Colombier 	if(b0->addr > b1->addr)
19325e96a66cSDavid du Colombier 		return 1;
19335e96a66cSDavid du Colombier 	return 0;
19345e96a66cSDavid du Colombier }
19355e96a66cSDavid du Colombier 
19365e96a66cSDavid du Colombier /*
19375e96a66cSDavid du Colombier  * Scan the block list for dirty blocks; add them to the list c->baddr.
19385e96a66cSDavid du Colombier  */
19395e96a66cSDavid du Colombier static void
flushFill(Cache * c)19405e96a66cSDavid du Colombier flushFill(Cache *c)
19415e96a66cSDavid du Colombier {
194261201b97SDavid du Colombier 	int i, ndirty;
19435e96a66cSDavid du Colombier 	BAddr *p;
19445e96a66cSDavid du Colombier 	Block *b;
19455e96a66cSDavid du Colombier 
19465e96a66cSDavid du Colombier 	vtLock(c->lk);
194739734e7eSDavid du Colombier 	if(c->ndirty == 0){
194839734e7eSDavid du Colombier 		vtUnlock(c->lk);
194939734e7eSDavid du Colombier 		return;
195039734e7eSDavid du Colombier 	}
19515e96a66cSDavid du Colombier 
19525e96a66cSDavid du Colombier 	p = c->baddr;
195361201b97SDavid du Colombier 	ndirty = 0;
19545e96a66cSDavid du Colombier 	for(i=0; i<c->nblocks; i++){
19555e96a66cSDavid du Colombier 		b = c->blocks + i;
195661201b97SDavid du Colombier 		if(b->part == PartError)
195761201b97SDavid du Colombier 			continue;
195861201b97SDavid du Colombier 		if(b->iostate == BioDirty || b->iostate == BioWriting)
195961201b97SDavid du Colombier 			ndirty++;
196061201b97SDavid du Colombier 		if(b->iostate != BioDirty)
19615e96a66cSDavid du Colombier 			continue;
19625e96a66cSDavid du Colombier 		p->part = b->part;
19635e96a66cSDavid du Colombier 		p->addr = b->addr;
19645e96a66cSDavid du Colombier 		p->vers = b->vers;
19655e96a66cSDavid du Colombier 		p++;
19665e96a66cSDavid du Colombier 	}
196761201b97SDavid du Colombier 	if(ndirty != c->ndirty){
19683b86f2f8SDavid du Colombier 		fprint(2, "%s: ndirty mismatch expected %d found %d\n",
19693b86f2f8SDavid du Colombier 			argv0, c->ndirty, ndirty);
197061201b97SDavid du Colombier 		c->ndirty = ndirty;
197161201b97SDavid du Colombier 	}
19725e96a66cSDavid du Colombier 	vtUnlock(c->lk);
19735e96a66cSDavid du Colombier 
19745e96a66cSDavid du Colombier 	c->bw = p - c->baddr;
19755e96a66cSDavid du Colombier 	qsort(c->baddr, c->bw, sizeof(BAddr), baddrCmp);
19765e96a66cSDavid du Colombier }
19775e96a66cSDavid du Colombier 
19785e96a66cSDavid du Colombier /*
19795e96a66cSDavid du Colombier  * This is not thread safe, i.e. it can't be called from multiple threads.
19805e96a66cSDavid du Colombier  *
19815e96a66cSDavid du Colombier  * It's okay how we use it, because it only gets called in
19825e96a66cSDavid du Colombier  * the flushThread.  And cacheFree, but only after
19835e96a66cSDavid du Colombier  * cacheFree has killed off the flushThread.
19845e96a66cSDavid du Colombier  */
19855e96a66cSDavid du Colombier static int
cacheFlushBlock(Cache * c)19865e96a66cSDavid du Colombier cacheFlushBlock(Cache *c)
19875e96a66cSDavid du Colombier {
19885e96a66cSDavid du Colombier 	Block *b;
19895e96a66cSDavid du Colombier 	BAddr *p;
19905e96a66cSDavid du Colombier 	int lockfail, nfail;
19915e96a66cSDavid du Colombier 
19925e96a66cSDavid du Colombier 	nfail = 0;
19935e96a66cSDavid du Colombier 	for(;;){
19945e96a66cSDavid du Colombier 		if(c->br == c->be){
19955e96a66cSDavid du Colombier 			if(c->bw == 0 || c->bw == c->be)
19965e96a66cSDavid du Colombier 				flushFill(c);
19975e96a66cSDavid du Colombier 			c->br = 0;
19985e96a66cSDavid du Colombier 			c->be = c->bw;
19995e96a66cSDavid du Colombier 			c->bw = 0;
20005e96a66cSDavid du Colombier 			c->nflush = 0;
20015e96a66cSDavid du Colombier 		}
20025e96a66cSDavid du Colombier 
20035e96a66cSDavid du Colombier 		if(c->br == c->be)
20045e96a66cSDavid du Colombier 			return 0;
20055e96a66cSDavid du Colombier 		p = c->baddr + c->br;
20065e96a66cSDavid du Colombier 		c->br++;
20072651f6bbSDavid du Colombier 		b = _cacheLocalLookup(c, p->part, p->addr, p->vers, Nowaitlock,
20082651f6bbSDavid du Colombier 			&lockfail);
20095e96a66cSDavid du Colombier 
2010*e12a9870SDavid du Colombier 		if(b && blockWrite(b, Nowaitlock)){
20115e96a66cSDavid du Colombier 			c->nflush++;
20125e96a66cSDavid du Colombier 			blockPut(b);
20135e96a66cSDavid du Colombier 			return 1;
20145e96a66cSDavid du Colombier 		}
20155e96a66cSDavid du Colombier 		if(b)
20165e96a66cSDavid du Colombier 			blockPut(b);
20175e96a66cSDavid du Colombier 
20185e96a66cSDavid du Colombier 		/*
20195e96a66cSDavid du Colombier 		 * Why didn't we write the block?
20205e96a66cSDavid du Colombier 		 */
20215e96a66cSDavid du Colombier 
20225e96a66cSDavid du Colombier 		/* Block already written out */
20235e96a66cSDavid du Colombier 		if(b == nil && !lockfail)
20245e96a66cSDavid du Colombier 			continue;
20255e96a66cSDavid du Colombier 
20265e96a66cSDavid du Colombier 		/* Failed to acquire lock; sleep if happens a lot. */
2027fe853e23SDavid du Colombier 		if(lockfail && ++nfail > 100){
20285e96a66cSDavid du Colombier 			sleep(500);
2029fe853e23SDavid du Colombier 			nfail = 0;
2030fe853e23SDavid du Colombier 		}
20315e96a66cSDavid du Colombier 		/* Requeue block. */
20325e96a66cSDavid du Colombier 		if(c->bw < c->be)
20335e96a66cSDavid du Colombier 			c->baddr[c->bw++] = *p;
20345e96a66cSDavid du Colombier 	}
20355e96a66cSDavid du Colombier }
20365e96a66cSDavid du Colombier 
20375e96a66cSDavid du Colombier /*
20385e96a66cSDavid du Colombier  * Occasionally flush dirty blocks from memory to the disk.
20395e96a66cSDavid du Colombier  */
20405e96a66cSDavid du Colombier static void
flushThread(void * a)20415e96a66cSDavid du Colombier flushThread(void *a)
20425e96a66cSDavid du Colombier {
20435e96a66cSDavid du Colombier 	Cache *c = a;
20445e96a66cSDavid du Colombier 	int i;
20455e96a66cSDavid du Colombier 
20465e96a66cSDavid du Colombier 	vtThreadSetName("flush");
20475e96a66cSDavid du Colombier 	vtLock(c->lk);
20485e96a66cSDavid du Colombier 	while(c->die == nil){
20495e96a66cSDavid du Colombier 		vtSleep(c->flush);
20505e96a66cSDavid du Colombier 		vtUnlock(c->lk);
20515e96a66cSDavid du Colombier 		for(i=0; i<FlushSize; i++)
205267031067SDavid du Colombier 			if(!cacheFlushBlock(c)){
205367031067SDavid du Colombier 				/*
205467031067SDavid du Colombier 				 * If i==0, could be someone is waking us repeatedly
205567031067SDavid du Colombier 				 * to flush the cache but there's no work to do.
205667031067SDavid du Colombier 				 * Pause a little.
205767031067SDavid du Colombier 				 */
20580b9a5132SDavid du Colombier 				if(i==0){
20593b86f2f8SDavid du Colombier 					// fprint(2, "%s: flushthread found "
20603b86f2f8SDavid du Colombier 					//	"nothing to flush - %d dirty\n",
20613b86f2f8SDavid du Colombier 					//	argv0, c->ndirty);
206267031067SDavid du Colombier 					sleep(250);
20630b9a5132SDavid du Colombier 				}
20645e96a66cSDavid du Colombier 				break;
206567031067SDavid du Colombier 			}
206639734e7eSDavid du Colombier 		if(i==0 && c->ndirty){
206739734e7eSDavid du Colombier 			/*
206839734e7eSDavid du Colombier 			 * All the blocks are being written right now -- there's nothing to do.
206939734e7eSDavid du Colombier 			 * We might be spinning with cacheFlush though -- he'll just keep
207039734e7eSDavid du Colombier 			 * kicking us until c->ndirty goes down.  Probably we should sleep
207139734e7eSDavid du Colombier 			 * on something that the diskThread can kick, but for now we'll
207239734e7eSDavid du Colombier 			 * just pause for a little while waiting for disks to finish.
207339734e7eSDavid du Colombier 			 */
207439734e7eSDavid du Colombier 			sleep(100);
207539734e7eSDavid du Colombier 		}
20765e96a66cSDavid du Colombier 		vtLock(c->lk);
20775e96a66cSDavid du Colombier 		vtWakeupAll(c->flushwait);
20785e96a66cSDavid du Colombier 	}
20795e96a66cSDavid du Colombier 	c->ref--;
20805e96a66cSDavid du Colombier 	vtWakeup(c->die);
20815e96a66cSDavid du Colombier 	vtUnlock(c->lk);
20825e96a66cSDavid du Colombier }
20835e96a66cSDavid du Colombier 
20845e96a66cSDavid du Colombier /*
20850b9a5132SDavid du Colombier  * Flush the cache.
20865e96a66cSDavid du Colombier  */
20875e96a66cSDavid du Colombier void
cacheFlush(Cache * c,int wait)20885e96a66cSDavid du Colombier cacheFlush(Cache *c, int wait)
20895e96a66cSDavid du Colombier {
20905e96a66cSDavid du Colombier 	vtLock(c->lk);
20915e96a66cSDavid du Colombier 	if(wait){
20925e96a66cSDavid du Colombier 		while(c->ndirty){
2093dc5a79c1SDavid du Colombier 		//	consPrint("cacheFlush: %d dirty blocks, uhead %p\n",
2094dc5a79c1SDavid du Colombier 		//		c->ndirty, c->uhead);
20955e96a66cSDavid du Colombier 			vtWakeup(c->flush);
20965e96a66cSDavid du Colombier 			vtSleep(c->flushwait);
20975e96a66cSDavid du Colombier 		}
2098dc5a79c1SDavid du Colombier 	//	consPrint("cacheFlush: done (uhead %p)\n", c->ndirty, c->uhead);
20990b9a5132SDavid du Colombier 	}else if(c->ndirty)
21005e96a66cSDavid du Colombier 		vtWakeup(c->flush);
21015e96a66cSDavid du Colombier 	vtUnlock(c->lk);
21025e96a66cSDavid du Colombier }
210361201b97SDavid du Colombier 
210461201b97SDavid du Colombier /*
210561201b97SDavid du Colombier  * Kick the flushThread every 30 seconds.
210661201b97SDavid du Colombier  */
210761201b97SDavid du Colombier static void
cacheSync(void * v)210861201b97SDavid du Colombier cacheSync(void *v)
210961201b97SDavid du Colombier {
211061201b97SDavid du Colombier 	Cache *c;
211161201b97SDavid du Colombier 
211261201b97SDavid du Colombier 	c = v;
211361201b97SDavid du Colombier 	cacheFlush(c, 0);
211461201b97SDavid du Colombier }
2115