xref: /plan9/sys/src/cmd/venti/srv/dcache.c (revision 368c31ab13393dea083228fdd1c3445076f83a4b)
1*368c31abSDavid du Colombier /*
2*368c31abSDavid du Colombier  * Disk cache.
3*368c31abSDavid du Colombier  *
4*368c31abSDavid du Colombier  * Caches raw disk blocks.  Getdblock() gets a block, putdblock puts it back.
5*368c31abSDavid du Colombier  * Getdblock has a mode parameter that determines i/o and access to a block:
6*368c31abSDavid du Colombier  * if mode is OREAD or ORDWR, it is read from disk if not already in memory.
7*368c31abSDavid du Colombier  * If mode is ORDWR or OWRITE, it is locked for exclusive use before being returned.
8*368c31abSDavid du Colombier  * It is *not* marked dirty -- once changes have been made, they should be noted
9*368c31abSDavid du Colombier  * by using dirtydblock() before putdblock().
10*368c31abSDavid du Colombier  *
11*368c31abSDavid du Colombier  * There is a global cache lock as well as a lock on each block.
12*368c31abSDavid du Colombier  * Within a thread, the cache lock can be acquired while holding a block lock,
13*368c31abSDavid du Colombier  * but not vice versa; and a block cannot be locked if you already hold the lock
14*368c31abSDavid du Colombier  * on another block.
15*368c31abSDavid du Colombier  *
16*368c31abSDavid du Colombier  * The flush proc writes out dirty blocks in batches, one batch per dirty tag.
17*368c31abSDavid du Colombier  * For example, the DirtyArena blocks are all written to disk before any of the
18*368c31abSDavid du Colombier  * DirtyArenaCib blocks.
19*368c31abSDavid du Colombier  *
20*368c31abSDavid du Colombier  * This code used to be in charge of flushing the dirty index blocks out to
21*368c31abSDavid du Colombier  * disk, but updating the index turned out to benefit from extra care.
22*368c31abSDavid du Colombier  * Now cached index blocks are never marked dirty.  The index.c code takes
23*368c31abSDavid du Colombier  * care of updating them behind our back, and uses _getdblock to update any
24*368c31abSDavid du Colombier  * cached copies of the blocks as it changes them on disk.
25*368c31abSDavid du Colombier  */
26*368c31abSDavid du Colombier 
27*368c31abSDavid du Colombier #include "stdinc.h"
28*368c31abSDavid du Colombier #include "dat.h"
29*368c31abSDavid du Colombier #include "fns.h"
30*368c31abSDavid du Colombier 
31*368c31abSDavid du Colombier typedef struct DCache	DCache;
32*368c31abSDavid du Colombier 
33*368c31abSDavid du Colombier enum
34*368c31abSDavid du Colombier {
35*368c31abSDavid du Colombier 	HashLog		= 9,
36*368c31abSDavid du Colombier 	HashSize	= 1<<HashLog,
37*368c31abSDavid du Colombier 	HashMask	= HashSize - 1,
38*368c31abSDavid du Colombier };
39*368c31abSDavid du Colombier 
40*368c31abSDavid du Colombier struct DCache
41*368c31abSDavid du Colombier {
42*368c31abSDavid du Colombier 	QLock		lock;
43*368c31abSDavid du Colombier 	RWLock		dirtylock;		/* must be held to inspect or set b->dirty */
44*368c31abSDavid du Colombier 	Rendez		full;
45*368c31abSDavid du Colombier 	Round		round;
46*368c31abSDavid du Colombier 	DBlock		*free;			/* list of available lumps */
47*368c31abSDavid du Colombier 	u32int		now;			/* ticks for usage timestamps */
48*368c31abSDavid du Colombier 	int		size;			/* max. size of any block; allocated to each block */
49*368c31abSDavid du Colombier 	DBlock		**heads;		/* hash table for finding address */
50*368c31abSDavid du Colombier 	int		nheap;			/* number of available victims */
51*368c31abSDavid du Colombier 	DBlock		**heap;			/* heap for locating victims */
52*368c31abSDavid du Colombier 	int		nblocks;		/* number of blocks allocated */
53*368c31abSDavid du Colombier 	DBlock		*blocks;		/* array of block descriptors */
54*368c31abSDavid du Colombier 	DBlock		**write;		/* array of block pointers to be written */
55*368c31abSDavid du Colombier 	u8int		*mem;			/* memory for all block descriptors */
56*368c31abSDavid du Colombier 	int		ndirty;			/* number of dirty blocks */
57*368c31abSDavid du Colombier 	int		maxdirty;		/* max. number of dirty blocks */
58*368c31abSDavid du Colombier 	Channel	*ra;
59*368c31abSDavid du Colombier 	u8int		*rabuf;
60*368c31abSDavid du Colombier 	u32int		ramax;
61*368c31abSDavid du Colombier 	u32int		rasize;
62*368c31abSDavid du Colombier 	u64int		raaddr;
63*368c31abSDavid du Colombier 	Part		*rapart;
64*368c31abSDavid du Colombier 
65*368c31abSDavid du Colombier 	AState	diskstate;
66*368c31abSDavid du Colombier 	AState	state;
67*368c31abSDavid du Colombier };
68*368c31abSDavid du Colombier 
69*368c31abSDavid du Colombier typedef struct Ra Ra;
70*368c31abSDavid du Colombier struct Ra
71*368c31abSDavid du Colombier {
72*368c31abSDavid du Colombier 	Part *part;
73*368c31abSDavid du Colombier 	u64int addr;
74*368c31abSDavid du Colombier };
75*368c31abSDavid du Colombier 
76*368c31abSDavid du Colombier static DCache	dcache;
77*368c31abSDavid du Colombier 
78*368c31abSDavid du Colombier static int	downheap(int i, DBlock *b);
79*368c31abSDavid du Colombier static int	upheap(int i, DBlock *b);
80*368c31abSDavid du Colombier static DBlock	*bumpdblock(void);
81*368c31abSDavid du Colombier static void	delheap(DBlock *db);
82*368c31abSDavid du Colombier static void	fixheap(int i, DBlock *b);
83*368c31abSDavid du Colombier static void	flushproc(void*);
84*368c31abSDavid du Colombier static void	writeproc(void*);
85*368c31abSDavid du Colombier static void raproc(void*);
86*368c31abSDavid du Colombier 
87*368c31abSDavid du Colombier void
88*368c31abSDavid du Colombier initdcache(u32int mem)
89*368c31abSDavid du Colombier {
90*368c31abSDavid du Colombier 	DBlock *b, *last;
91*368c31abSDavid du Colombier 	u32int nblocks, blocksize;
92*368c31abSDavid du Colombier 	int i;
93*368c31abSDavid du Colombier 	u8int *p;
94*368c31abSDavid du Colombier 
95*368c31abSDavid du Colombier 	if(mem < maxblocksize * 2)
96*368c31abSDavid du Colombier 		sysfatal("need at least %d bytes for the disk cache", maxblocksize * 2);
97*368c31abSDavid du Colombier 	if(maxblocksize == 0)
98*368c31abSDavid du Colombier 		sysfatal("no max. block size given for disk cache");
99*368c31abSDavid du Colombier 	blocksize = maxblocksize;
100*368c31abSDavid du Colombier 	nblocks = mem / blocksize;
101*368c31abSDavid du Colombier 	dcache.full.l = &dcache.lock;
102*368c31abSDavid du Colombier 	dcache.nblocks = nblocks;
103*368c31abSDavid du Colombier 	dcache.maxdirty = (nblocks * 2) / 3;
104*368c31abSDavid du Colombier 	trace(TraceProc, "initialize disk cache with %d blocks of %d bytes, maximum %d dirty blocks\n",
105*368c31abSDavid du Colombier 			nblocks, blocksize, dcache.maxdirty);
106*368c31abSDavid du Colombier 	dcache.size = blocksize;
107*368c31abSDavid du Colombier 	dcache.heads = MKNZ(DBlock*, HashSize);
108*368c31abSDavid du Colombier 	dcache.heap = MKNZ(DBlock*, nblocks);
109*368c31abSDavid du Colombier 	dcache.blocks = MKNZ(DBlock, nblocks);
110*368c31abSDavid du Colombier 	dcache.write = MKNZ(DBlock*, nblocks);
111*368c31abSDavid du Colombier 	dcache.mem = MKNZ(u8int, (nblocks+1+128) * blocksize);
112*368c31abSDavid du Colombier 	dcache.ra = chancreate(sizeof(Ra), 0);
113*368c31abSDavid du Colombier 
114*368c31abSDavid du Colombier 	last = nil;
115*368c31abSDavid du Colombier 	p = (u8int*)(((ulong)dcache.mem+blocksize-1)&~(ulong)(blocksize-1));
116*368c31abSDavid du Colombier 	for(i = 0; i < nblocks; i++){
117*368c31abSDavid du Colombier 		b = &dcache.blocks[i];
118*368c31abSDavid du Colombier 		b->data = &p[i * blocksize];
119*368c31abSDavid du Colombier 		b->heap = TWID32;
120*368c31abSDavid du Colombier 		b->writedonechan = chancreate(sizeof(void*), 1);
121*368c31abSDavid du Colombier 		b->next = last;
122*368c31abSDavid du Colombier 		last = b;
123*368c31abSDavid du Colombier 	}
124*368c31abSDavid du Colombier 	dcache.rabuf = &p[i*blocksize];
125*368c31abSDavid du Colombier 	dcache.ramax = 128*blocksize;
126*368c31abSDavid du Colombier 	dcache.raaddr = 0;
127*368c31abSDavid du Colombier 	dcache.rapart = nil;
128*368c31abSDavid du Colombier 
129*368c31abSDavid du Colombier 	dcache.free = last;
130*368c31abSDavid du Colombier 	dcache.nheap = 0;
131*368c31abSDavid du Colombier 	setstat(StatDcacheSize, nblocks);
132*368c31abSDavid du Colombier 	initround(&dcache.round, "dcache", 120*1000);
133*368c31abSDavid du Colombier 
134*368c31abSDavid du Colombier 	vtproc(flushproc, nil);
135*368c31abSDavid du Colombier 	vtproc(delaykickroundproc, &dcache.round);
136*368c31abSDavid du Colombier 	vtproc(raproc, nil);
137*368c31abSDavid du Colombier }
138*368c31abSDavid du Colombier 
139*368c31abSDavid du Colombier void
140*368c31abSDavid du Colombier setdcachestate(AState *a)
141*368c31abSDavid du Colombier {
142*368c31abSDavid du Colombier 	trace(TraceBlock, "setdcachestate %s 0x%llux clumps %d", a->arena ? a->arena->name : nil, a->aa, a->stats.clumps);
143*368c31abSDavid du Colombier 	qlock(&dcache.lock);
144*368c31abSDavid du Colombier 	dcache.state = *a;
145*368c31abSDavid du Colombier 	qunlock(&dcache.lock);
146*368c31abSDavid du Colombier }
147*368c31abSDavid du Colombier 
148*368c31abSDavid du Colombier AState
149*368c31abSDavid du Colombier diskstate(void)
150*368c31abSDavid du Colombier {
151*368c31abSDavid du Colombier 	AState a;
152*368c31abSDavid du Colombier 
153*368c31abSDavid du Colombier 	qlock(&dcache.lock);
154*368c31abSDavid du Colombier 	a = dcache.diskstate;
155*368c31abSDavid du Colombier 	qunlock(&dcache.lock);
156*368c31abSDavid du Colombier 	return a;
157*368c31abSDavid du Colombier }
158*368c31abSDavid du Colombier 
159*368c31abSDavid du Colombier static void
160*368c31abSDavid du Colombier raproc(void *v)
161*368c31abSDavid du Colombier {
162*368c31abSDavid du Colombier 	Ra ra;
163*368c31abSDavid du Colombier 	DBlock *b;
164*368c31abSDavid du Colombier 
165*368c31abSDavid du Colombier 	USED(v);
166*368c31abSDavid du Colombier 	while(recv(dcache.ra, &ra) == 1){
167*368c31abSDavid du Colombier 		if(ra.part->size <= ra.addr)
168*368c31abSDavid du Colombier 			continue;
169*368c31abSDavid du Colombier 		b = _getdblock(ra.part, ra.addr, OREAD, 2);
170*368c31abSDavid du Colombier 		putdblock(b);
171*368c31abSDavid du Colombier 	}
172*368c31abSDavid du Colombier }
173*368c31abSDavid du Colombier 
174*368c31abSDavid du Colombier /*
175*368c31abSDavid du Colombier  * We do readahead a whole arena at a time now,
176*368c31abSDavid du Colombier  * so dreadahead is a no-op.  The original implementation
177*368c31abSDavid du Colombier  * is in unused_dreadahead below.
178*368c31abSDavid du Colombier  */
179*368c31abSDavid du Colombier void
180*368c31abSDavid du Colombier dreadahead(Part *part, u64int addr, int miss)
181*368c31abSDavid du Colombier {
182*368c31abSDavid du Colombier 	USED(part);
183*368c31abSDavid du Colombier 	USED(addr);
184*368c31abSDavid du Colombier 	USED(miss);
185*368c31abSDavid du Colombier }
186*368c31abSDavid du Colombier 
187*368c31abSDavid du Colombier void
188*368c31abSDavid du Colombier unused_dreadahead(Part *part, u64int addr, int miss)
189*368c31abSDavid du Colombier {
190*368c31abSDavid du Colombier 	Ra ra;
191*368c31abSDavid du Colombier 	static struct {
192*368c31abSDavid du Colombier 		Part *part;
193*368c31abSDavid du Colombier 		u64int addr;
194*368c31abSDavid du Colombier 	} lastmiss;
195*368c31abSDavid du Colombier 	static struct {
196*368c31abSDavid du Colombier 		Part *part;
197*368c31abSDavid du Colombier 		u64int addr;
198*368c31abSDavid du Colombier 		int dir;
199*368c31abSDavid du Colombier 	} lastra;
200*368c31abSDavid du Colombier 
201*368c31abSDavid du Colombier 	if(miss){
202*368c31abSDavid du Colombier 		if(lastmiss.part==part && lastmiss.addr==addr-dcache.size){
203*368c31abSDavid du Colombier 		XRa:
204*368c31abSDavid du Colombier 			lastra.part = part;
205*368c31abSDavid du Colombier 			lastra.dir = addr-lastmiss.addr;
206*368c31abSDavid du Colombier 			lastra.addr = addr+lastra.dir;
207*368c31abSDavid du Colombier 			ra.part = part;
208*368c31abSDavid du Colombier 			ra.addr = lastra.addr;
209*368c31abSDavid du Colombier 			nbsend(dcache.ra, &ra);
210*368c31abSDavid du Colombier 		}else if(lastmiss.part==part && lastmiss.addr==addr+dcache.size){
211*368c31abSDavid du Colombier 			addr -= dcache.size;
212*368c31abSDavid du Colombier 			goto XRa;
213*368c31abSDavid du Colombier 		}
214*368c31abSDavid du Colombier 	}else{
215*368c31abSDavid du Colombier 		if(lastra.part==part && lastra.addr==addr){
216*368c31abSDavid du Colombier 			lastra.addr += lastra.dir;
217*368c31abSDavid du Colombier 			ra.part = part;
218*368c31abSDavid du Colombier 			ra.addr = lastra.addr;
219*368c31abSDavid du Colombier 			nbsend(dcache.ra, &ra);
220*368c31abSDavid du Colombier 		}
221*368c31abSDavid du Colombier 	}
222*368c31abSDavid du Colombier 
223*368c31abSDavid du Colombier 	if(miss){
224*368c31abSDavid du Colombier 		lastmiss.part = part;
225*368c31abSDavid du Colombier 		lastmiss.addr = addr;
226*368c31abSDavid du Colombier 	}
227*368c31abSDavid du Colombier }
228*368c31abSDavid du Colombier 
229*368c31abSDavid du Colombier int
230*368c31abSDavid du Colombier rareadpart(Part *part, u64int addr, u8int *buf, uint n, int load)
231*368c31abSDavid du Colombier {
232*368c31abSDavid du Colombier 	uint nn;
233*368c31abSDavid du Colombier 	static RWLock ralock;
234*368c31abSDavid du Colombier 
235*368c31abSDavid du Colombier 	rlock(&ralock);
236*368c31abSDavid du Colombier 	if(dcache.rapart==part && dcache.raaddr <= addr && addr+n <= dcache.raaddr+dcache.rasize){
237*368c31abSDavid du Colombier 		memmove(buf, dcache.rabuf+(addr-dcache.raaddr), n);
238*368c31abSDavid du Colombier 		runlock(&ralock);
239*368c31abSDavid du Colombier 		return 0;
240*368c31abSDavid du Colombier 	}
241*368c31abSDavid du Colombier 	if(load != 2 || addr >= part->size){	/* addr >= part->size: let readpart do the error */
242*368c31abSDavid du Colombier 		runlock(&ralock);
243*368c31abSDavid du Colombier 		diskaccess(0);
244*368c31abSDavid du Colombier 		return readpart(part, addr, buf, n);
245*368c31abSDavid du Colombier 	}
246*368c31abSDavid du Colombier 
247*368c31abSDavid du Colombier 	runlock(&ralock);
248*368c31abSDavid du Colombier 	wlock(&ralock);
249*368c31abSDavid du Colombier fprint(2, "raread %s %llx\n", part->name, addr);
250*368c31abSDavid du Colombier 	nn = dcache.ramax;
251*368c31abSDavid du Colombier 	if(addr+nn > part->size)
252*368c31abSDavid du Colombier 		nn = part->size - addr;
253*368c31abSDavid du Colombier 	diskaccess(0);
254*368c31abSDavid du Colombier 	if(readpart(part, addr, dcache.rabuf, nn) < 0){
255*368c31abSDavid du Colombier 		wunlock(&ralock);
256*368c31abSDavid du Colombier 		return -1;
257*368c31abSDavid du Colombier 	}
258*368c31abSDavid du Colombier 	memmove(buf, dcache.rabuf, n);
259*368c31abSDavid du Colombier 	dcache.rapart = part;
260*368c31abSDavid du Colombier 	dcache.rasize = nn;
261*368c31abSDavid du Colombier 	dcache.raaddr = addr;
262*368c31abSDavid du Colombier 	wunlock(&ralock);
263*368c31abSDavid du Colombier 
264*368c31abSDavid du Colombier 	addstat(StatApartReadBytes, nn-n);
265*368c31abSDavid du Colombier 	return 0;
266*368c31abSDavid du Colombier }
267*368c31abSDavid du Colombier 
268*368c31abSDavid du Colombier static u32int
269*368c31abSDavid du Colombier pbhash(u64int addr)
270*368c31abSDavid du Colombier {
271*368c31abSDavid du Colombier 	u32int h;
272*368c31abSDavid du Colombier 
273*368c31abSDavid du Colombier #define hashit(c)	((((c) * 0x6b43a9b5) >> (32 - HashLog)) & HashMask)
274*368c31abSDavid du Colombier 	h = (addr >> 32) ^ addr;
275*368c31abSDavid du Colombier 	return hashit(h);
276*368c31abSDavid du Colombier }
277*368c31abSDavid du Colombier 
278*368c31abSDavid du Colombier DBlock*
279*368c31abSDavid du Colombier getdblock(Part *part, u64int addr, int mode)
280*368c31abSDavid du Colombier {
281*368c31abSDavid du Colombier 	DBlock *b;
282*368c31abSDavid du Colombier 	uint ms;
283*368c31abSDavid du Colombier 
284*368c31abSDavid du Colombier 	ms = msec();
285*368c31abSDavid du Colombier 	b = _getdblock(part, addr, mode, 1);
286*368c31abSDavid du Colombier 	if(mode == OREAD || mode == ORDWR)
287*368c31abSDavid du Colombier 		addstat(StatDcacheRead, 1);
288*368c31abSDavid du Colombier 	if(mode == OWRITE || mode == ORDWR)
289*368c31abSDavid du Colombier 		addstat(StatDcacheWrite, 1);
290*368c31abSDavid du Colombier 	ms = msec() - ms;
291*368c31abSDavid du Colombier 	addstat2(StatDcacheLookup, 1, StatDcacheLookupTime, ms);
292*368c31abSDavid du Colombier 	return b;
293*368c31abSDavid du Colombier }
294*368c31abSDavid du Colombier 
295*368c31abSDavid du Colombier DBlock*
296*368c31abSDavid du Colombier _getdblock(Part *part, u64int addr, int mode, int load)
297*368c31abSDavid du Colombier {
298*368c31abSDavid du Colombier 	DBlock *b;
299*368c31abSDavid du Colombier 	u32int h, size;
300*368c31abSDavid du Colombier 
301*368c31abSDavid du Colombier 	trace(TraceBlock, "getdblock enter %s 0x%llux", part->name, addr);
302*368c31abSDavid du Colombier 	size = part->blocksize;
303*368c31abSDavid du Colombier 	if(size > dcache.size){
304*368c31abSDavid du Colombier 		seterr(EAdmin, "block size %d too big for cache with size %d", size, dcache.size);
305*368c31abSDavid du Colombier 		return nil;
306*368c31abSDavid du Colombier 	}
307*368c31abSDavid du Colombier 	h = pbhash(addr);
308*368c31abSDavid du Colombier 
309*368c31abSDavid du Colombier 	/*
310*368c31abSDavid du Colombier 	 * look for the block in the cache
311*368c31abSDavid du Colombier 	 */
312*368c31abSDavid du Colombier 	qlock(&dcache.lock);
313*368c31abSDavid du Colombier again:
314*368c31abSDavid du Colombier 	for(b = dcache.heads[h]; b != nil; b = b->next){
315*368c31abSDavid du Colombier 		if(b->part == part && b->addr == addr){
316*368c31abSDavid du Colombier 			/*
317*368c31abSDavid du Colombier 			qlock(&stats.lock);
318*368c31abSDavid du Colombier 			stats.pchit++;
319*368c31abSDavid du Colombier 			qunlock(&stats.lock);
320*368c31abSDavid du Colombier 			*/
321*368c31abSDavid du Colombier 			if(load){
322*368c31abSDavid du Colombier 				addstat(StatDcacheHit, 1);
323*368c31abSDavid du Colombier 				if(load != 2 && mode != OWRITE)
324*368c31abSDavid du Colombier 					dreadahead(part, b->addr, 0);
325*368c31abSDavid du Colombier 			}
326*368c31abSDavid du Colombier 			goto found;
327*368c31abSDavid du Colombier 		}
328*368c31abSDavid du Colombier 	}
329*368c31abSDavid du Colombier 
330*368c31abSDavid du Colombier 	/*
331*368c31abSDavid du Colombier 	 * missed: locate the block with the oldest second to last use.
332*368c31abSDavid du Colombier 	 * remove it from the heap, and fix up the heap.
333*368c31abSDavid du Colombier 	 */
334*368c31abSDavid du Colombier 	if(!load){
335*368c31abSDavid du Colombier 		qunlock(&dcache.lock);
336*368c31abSDavid du Colombier 		return nil;
337*368c31abSDavid du Colombier 	}
338*368c31abSDavid du Colombier 
339*368c31abSDavid du Colombier 	addstat(StatDcacheMiss, 1);
340*368c31abSDavid du Colombier 
341*368c31abSDavid du Colombier 	b = bumpdblock();
342*368c31abSDavid du Colombier 	if(b == nil){
343*368c31abSDavid du Colombier 		trace(TraceBlock, "all disk cache blocks in use");
344*368c31abSDavid du Colombier 		addstat(StatDcacheStall, 1);
345*368c31abSDavid du Colombier 		rsleep(&dcache.full);
346*368c31abSDavid du Colombier 		addstat(StatDcacheStall, -1);
347*368c31abSDavid du Colombier 		goto again;
348*368c31abSDavid du Colombier 	}
349*368c31abSDavid du Colombier 
350*368c31abSDavid du Colombier 	assert(!b->dirty);
351*368c31abSDavid du Colombier 
352*368c31abSDavid du Colombier 	/*
353*368c31abSDavid du Colombier 	 * the new block has no last use, so assume it happens sometime in the middle
354*368c31abSDavid du Colombier ZZZ this is not reasonable
355*368c31abSDavid du Colombier 	 */
356*368c31abSDavid du Colombier 	b->used = (b->used2 + dcache.now) / 2;
357*368c31abSDavid du Colombier 
358*368c31abSDavid du Colombier 	/*
359*368c31abSDavid du Colombier 	 * rechain the block on the correct hash chain
360*368c31abSDavid du Colombier 	 */
361*368c31abSDavid du Colombier 	b->next = dcache.heads[h];
362*368c31abSDavid du Colombier 	dcache.heads[h] = b;
363*368c31abSDavid du Colombier 	if(b->next != nil)
364*368c31abSDavid du Colombier 		b->next->prev = b;
365*368c31abSDavid du Colombier 	b->prev = nil;
366*368c31abSDavid du Colombier 
367*368c31abSDavid du Colombier 	b->addr = addr;
368*368c31abSDavid du Colombier 	b->part = part;
369*368c31abSDavid du Colombier 	b->size = 0;
370*368c31abSDavid du Colombier 	if(load != 2 && mode != OWRITE)
371*368c31abSDavid du Colombier 		dreadahead(part, b->addr, 1);
372*368c31abSDavid du Colombier 
373*368c31abSDavid du Colombier found:
374*368c31abSDavid du Colombier 	b->ref++;
375*368c31abSDavid du Colombier 	b->used2 = b->used;
376*368c31abSDavid du Colombier 	b->used = dcache.now++;
377*368c31abSDavid du Colombier 	if(b->heap != TWID32)
378*368c31abSDavid du Colombier 		fixheap(b->heap, b);
379*368c31abSDavid du Colombier 
380*368c31abSDavid du Colombier 	qunlock(&dcache.lock);
381*368c31abSDavid du Colombier 
382*368c31abSDavid du Colombier 	trace(TraceBlock, "getdblock lock");
383*368c31abSDavid du Colombier 	addstat(StatDblockStall, 1);
384*368c31abSDavid du Colombier 	if(mode == OREAD)
385*368c31abSDavid du Colombier 		rlock(&b->lock);
386*368c31abSDavid du Colombier 	else
387*368c31abSDavid du Colombier 		wlock(&b->lock);
388*368c31abSDavid du Colombier 	addstat(StatDblockStall, -1);
389*368c31abSDavid du Colombier 	trace(TraceBlock, "getdblock locked");
390*368c31abSDavid du Colombier 
391*368c31abSDavid du Colombier 	if(b->size != size){
392*368c31abSDavid du Colombier 		if(mode == OREAD){
393*368c31abSDavid du Colombier 			addstat(StatDblockStall, 1);
394*368c31abSDavid du Colombier 			runlock(&b->lock);
395*368c31abSDavid du Colombier 			wlock(&b->lock);
396*368c31abSDavid du Colombier 			addstat(StatDblockStall, -1);
397*368c31abSDavid du Colombier 		}
398*368c31abSDavid du Colombier 		if(b->size < size){
399*368c31abSDavid du Colombier 			if(mode == OWRITE)
400*368c31abSDavid du Colombier 				memset(&b->data[b->size], 0, size - b->size);
401*368c31abSDavid du Colombier 			else{
402*368c31abSDavid du Colombier 				trace(TraceBlock, "getdblock readpart %s 0x%llux", part->name, addr);
403*368c31abSDavid du Colombier 				if(rareadpart(part, addr + b->size, &b->data[b->size], size - b->size, load) < 0){
404*368c31abSDavid du Colombier 					b->mode = ORDWR;	/* so putdblock wunlocks */
405*368c31abSDavid du Colombier 					putdblock(b);
406*368c31abSDavid du Colombier 					return nil;
407*368c31abSDavid du Colombier 				}
408*368c31abSDavid du Colombier 				trace(TraceBlock, "getdblock readpartdone");
409*368c31abSDavid du Colombier 				addstat(StatApartRead, 1);
410*368c31abSDavid du Colombier 				addstat(StatApartReadBytes, size-b->size);
411*368c31abSDavid du Colombier 			}
412*368c31abSDavid du Colombier 		}
413*368c31abSDavid du Colombier 		b->size = size;
414*368c31abSDavid du Colombier 		if(mode == OREAD){
415*368c31abSDavid du Colombier 			addstat(StatDblockStall, 1);
416*368c31abSDavid du Colombier 			wunlock(&b->lock);
417*368c31abSDavid du Colombier 			rlock(&b->lock);
418*368c31abSDavid du Colombier 			addstat(StatDblockStall, -1);
419*368c31abSDavid du Colombier 		}
420*368c31abSDavid du Colombier 	}
421*368c31abSDavid du Colombier 
422*368c31abSDavid du Colombier 	b->mode = mode;
423*368c31abSDavid du Colombier 	trace(TraceBlock, "getdblock exit");
424*368c31abSDavid du Colombier 	return b;
425*368c31abSDavid du Colombier }
426*368c31abSDavid du Colombier 
427*368c31abSDavid du Colombier void
428*368c31abSDavid du Colombier putdblock(DBlock *b)
429*368c31abSDavid du Colombier {
430*368c31abSDavid du Colombier 	if(b == nil)
431*368c31abSDavid du Colombier 		return;
432*368c31abSDavid du Colombier 
433*368c31abSDavid du Colombier 	trace(TraceBlock, "putdblock %s 0x%llux", b->part->name, b->addr);
434*368c31abSDavid du Colombier 
435*368c31abSDavid du Colombier 	if(b->mode == OREAD)
436*368c31abSDavid du Colombier 		runlock(&b->lock);
437*368c31abSDavid du Colombier 	else
438*368c31abSDavid du Colombier 		wunlock(&b->lock);
439*368c31abSDavid du Colombier 
440*368c31abSDavid du Colombier 	qlock(&dcache.lock);
441*368c31abSDavid du Colombier 	if(--b->ref == 0 && !b->dirty){
442*368c31abSDavid du Colombier 		if(b->heap == TWID32)
443*368c31abSDavid du Colombier 			upheap(dcache.nheap++, b);
444*368c31abSDavid du Colombier 		rwakeupall(&dcache.full);
445*368c31abSDavid du Colombier 	}
446*368c31abSDavid du Colombier 	qunlock(&dcache.lock);
447*368c31abSDavid du Colombier }
448*368c31abSDavid du Colombier 
449*368c31abSDavid du Colombier void
450*368c31abSDavid du Colombier dirtydblock(DBlock *b, int dirty)
451*368c31abSDavid du Colombier {
452*368c31abSDavid du Colombier 	int odirty;
453*368c31abSDavid du Colombier 	Part *p;
454*368c31abSDavid du Colombier 
455*368c31abSDavid du Colombier 
456*368c31abSDavid du Colombier 	trace(TraceBlock, "dirtydblock enter %s 0x%llux %d from 0x%lux", b->part->name, b->addr, dirty, getcallerpc(&b));
457*368c31abSDavid du Colombier 	assert(b->ref != 0);
458*368c31abSDavid du Colombier 	assert(b->mode==ORDWR || b->mode==OWRITE);
459*368c31abSDavid du Colombier 
460*368c31abSDavid du Colombier 	odirty = b->dirty;
461*368c31abSDavid du Colombier 	if(b->dirty)
462*368c31abSDavid du Colombier 		assert(b->dirty == dirty);
463*368c31abSDavid du Colombier 	else
464*368c31abSDavid du Colombier 		b->dirty = dirty;
465*368c31abSDavid du Colombier 
466*368c31abSDavid du Colombier 	p = b->part;
467*368c31abSDavid du Colombier 	if(p->writechan == nil){
468*368c31abSDavid du Colombier 		trace(TraceBlock, "dirtydblock allocwriteproc %s", p->name);
469*368c31abSDavid du Colombier 		/* XXX hope this doesn't fail! */
470*368c31abSDavid du Colombier 		p->writechan = chancreate(sizeof(DBlock*), dcache.nblocks);
471*368c31abSDavid du Colombier 		vtproc(writeproc, p);
472*368c31abSDavid du Colombier 	}
473*368c31abSDavid du Colombier 	qlock(&dcache.lock);
474*368c31abSDavid du Colombier 	if(!odirty){
475*368c31abSDavid du Colombier 		dcache.ndirty++;
476*368c31abSDavid du Colombier 		setstat(StatDcacheDirty, dcache.ndirty);
477*368c31abSDavid du Colombier 		if(dcache.ndirty >= dcache.maxdirty)
478*368c31abSDavid du Colombier 			kickround(&dcache.round, 0);
479*368c31abSDavid du Colombier 		else
480*368c31abSDavid du Colombier 			delaykickround(&dcache.round);
481*368c31abSDavid du Colombier 	}
482*368c31abSDavid du Colombier 	qunlock(&dcache.lock);
483*368c31abSDavid du Colombier }
484*368c31abSDavid du Colombier 
485*368c31abSDavid du Colombier static void
486*368c31abSDavid du Colombier unchain(DBlock *b)
487*368c31abSDavid du Colombier {
488*368c31abSDavid du Colombier 	ulong h;
489*368c31abSDavid du Colombier 
490*368c31abSDavid du Colombier 	/*
491*368c31abSDavid du Colombier 	 * unchain the block
492*368c31abSDavid du Colombier 	 */
493*368c31abSDavid du Colombier 	if(b->prev == nil){
494*368c31abSDavid du Colombier 		h = pbhash(b->addr);
495*368c31abSDavid du Colombier 		if(dcache.heads[h] != b)
496*368c31abSDavid du Colombier 			sysfatal("bad hash chains in disk cache");
497*368c31abSDavid du Colombier 		dcache.heads[h] = b->next;
498*368c31abSDavid du Colombier 	}else
499*368c31abSDavid du Colombier 		b->prev->next = b->next;
500*368c31abSDavid du Colombier 	if(b->next != nil)
501*368c31abSDavid du Colombier 		b->next->prev = b->prev;
502*368c31abSDavid du Colombier }
503*368c31abSDavid du Colombier 
504*368c31abSDavid du Colombier /*
505*368c31abSDavid du Colombier  * remove some block from use and update the free list and counters
506*368c31abSDavid du Colombier  */
507*368c31abSDavid du Colombier static DBlock*
508*368c31abSDavid du Colombier bumpdblock(void)
509*368c31abSDavid du Colombier {
510*368c31abSDavid du Colombier 	DBlock *b;
511*368c31abSDavid du Colombier 
512*368c31abSDavid du Colombier 	trace(TraceBlock, "bumpdblock enter");
513*368c31abSDavid du Colombier 	b = dcache.free;
514*368c31abSDavid du Colombier 	if(b != nil){
515*368c31abSDavid du Colombier 		dcache.free = b->next;
516*368c31abSDavid du Colombier 		return b;
517*368c31abSDavid du Colombier 	}
518*368c31abSDavid du Colombier 
519*368c31abSDavid du Colombier 	if(dcache.ndirty >= dcache.maxdirty)
520*368c31abSDavid du Colombier 		kickdcache();
521*368c31abSDavid du Colombier 
522*368c31abSDavid du Colombier 	/*
523*368c31abSDavid du Colombier 	 * remove blocks until we find one that is unused
524*368c31abSDavid du Colombier 	 * referenced blocks are left in the heap even though
525*368c31abSDavid du Colombier 	 * they can't be scavenged; this is simple a speed optimization
526*368c31abSDavid du Colombier 	 */
527*368c31abSDavid du Colombier 	for(;;){
528*368c31abSDavid du Colombier 		if(dcache.nheap == 0){
529*368c31abSDavid du Colombier 			kickdcache();
530*368c31abSDavid du Colombier 			trace(TraceBlock, "bumpdblock gotnothing");
531*368c31abSDavid du Colombier 			return nil;
532*368c31abSDavid du Colombier 		}
533*368c31abSDavid du Colombier 		b = dcache.heap[0];
534*368c31abSDavid du Colombier 		delheap(b);
535*368c31abSDavid du Colombier 		if(!b->ref && !b->dirty)
536*368c31abSDavid du Colombier 			break;
537*368c31abSDavid du Colombier 	}
538*368c31abSDavid du Colombier 
539*368c31abSDavid du Colombier 	trace(TraceBlock, "bumpdblock bumping %s 0x%llux", b->part->name, b->addr);
540*368c31abSDavid du Colombier 
541*368c31abSDavid du Colombier 	unchain(b);
542*368c31abSDavid du Colombier 	return b;
543*368c31abSDavid du Colombier }
544*368c31abSDavid du Colombier 
545*368c31abSDavid du Colombier void
546*368c31abSDavid du Colombier emptydcache(void)
547*368c31abSDavid du Colombier {
548*368c31abSDavid du Colombier 	DBlock *b;
549*368c31abSDavid du Colombier 
550*368c31abSDavid du Colombier 	qlock(&dcache.lock);
551*368c31abSDavid du Colombier 	while(dcache.nheap > 0){
552*368c31abSDavid du Colombier 		b = dcache.heap[0];
553*368c31abSDavid du Colombier 		delheap(b);
554*368c31abSDavid du Colombier 		if(!b->ref && !b->dirty){
555*368c31abSDavid du Colombier 			unchain(b);
556*368c31abSDavid du Colombier 			b->next = dcache.free;
557*368c31abSDavid du Colombier 			dcache.free = b;
558*368c31abSDavid du Colombier 		}
559*368c31abSDavid du Colombier 	}
560*368c31abSDavid du Colombier 	qunlock(&dcache.lock);
561*368c31abSDavid du Colombier }
562*368c31abSDavid du Colombier 
563*368c31abSDavid du Colombier /*
564*368c31abSDavid du Colombier  * delete an arbitrary block from the heap
565*368c31abSDavid du Colombier  */
566*368c31abSDavid du Colombier static void
567*368c31abSDavid du Colombier delheap(DBlock *db)
568*368c31abSDavid du Colombier {
569*368c31abSDavid du Colombier 	if(db->heap == TWID32)
570*368c31abSDavid du Colombier 		return;
571*368c31abSDavid du Colombier 	fixheap(db->heap, dcache.heap[--dcache.nheap]);
572*368c31abSDavid du Colombier 	db->heap = TWID32;
573*368c31abSDavid du Colombier }
574*368c31abSDavid du Colombier 
575*368c31abSDavid du Colombier /*
576*368c31abSDavid du Colombier  * push an element up or down to it's correct new location
577*368c31abSDavid du Colombier  */
578*368c31abSDavid du Colombier static void
579*368c31abSDavid du Colombier fixheap(int i, DBlock *b)
580*368c31abSDavid du Colombier {
581*368c31abSDavid du Colombier 	if(upheap(i, b) == i)
582*368c31abSDavid du Colombier 		downheap(i, b);
583*368c31abSDavid du Colombier }
584*368c31abSDavid du Colombier 
585*368c31abSDavid du Colombier static int
586*368c31abSDavid du Colombier upheap(int i, DBlock *b)
587*368c31abSDavid du Colombier {
588*368c31abSDavid du Colombier 	DBlock *bb;
589*368c31abSDavid du Colombier 	u32int now;
590*368c31abSDavid du Colombier 	int p;
591*368c31abSDavid du Colombier 
592*368c31abSDavid du Colombier 	now = dcache.now;
593*368c31abSDavid du Colombier 	for(; i != 0; i = p){
594*368c31abSDavid du Colombier 		p = (i - 1) >> 1;
595*368c31abSDavid du Colombier 		bb = dcache.heap[p];
596*368c31abSDavid du Colombier 		if(b->used2 - now >= bb->used2 - now)
597*368c31abSDavid du Colombier 			break;
598*368c31abSDavid du Colombier 		dcache.heap[i] = bb;
599*368c31abSDavid du Colombier 		bb->heap = i;
600*368c31abSDavid du Colombier 	}
601*368c31abSDavid du Colombier 
602*368c31abSDavid du Colombier 	dcache.heap[i] = b;
603*368c31abSDavid du Colombier 	b->heap = i;
604*368c31abSDavid du Colombier 	return i;
605*368c31abSDavid du Colombier }
606*368c31abSDavid du Colombier 
607*368c31abSDavid du Colombier static int
608*368c31abSDavid du Colombier downheap(int i, DBlock *b)
609*368c31abSDavid du Colombier {
610*368c31abSDavid du Colombier 	DBlock *bb;
611*368c31abSDavid du Colombier 	u32int now;
612*368c31abSDavid du Colombier 	int k;
613*368c31abSDavid du Colombier 
614*368c31abSDavid du Colombier 	now = dcache.now;
615*368c31abSDavid du Colombier 	for(; ; i = k){
616*368c31abSDavid du Colombier 		k = (i << 1) + 1;
617*368c31abSDavid du Colombier 		if(k >= dcache.nheap)
618*368c31abSDavid du Colombier 			break;
619*368c31abSDavid du Colombier 		if(k + 1 < dcache.nheap && dcache.heap[k]->used2 - now > dcache.heap[k + 1]->used2 - now)
620*368c31abSDavid du Colombier 			k++;
621*368c31abSDavid du Colombier 		bb = dcache.heap[k];
622*368c31abSDavid du Colombier 		if(b->used2 - now <= bb->used2 - now)
623*368c31abSDavid du Colombier 			break;
624*368c31abSDavid du Colombier 		dcache.heap[i] = bb;
625*368c31abSDavid du Colombier 		bb->heap = i;
626*368c31abSDavid du Colombier 	}
627*368c31abSDavid du Colombier 
628*368c31abSDavid du Colombier 	dcache.heap[i] = b;
629*368c31abSDavid du Colombier 	b->heap = i;
630*368c31abSDavid du Colombier 	return i;
631*368c31abSDavid du Colombier }
632*368c31abSDavid du Colombier 
633*368c31abSDavid du Colombier static void
634*368c31abSDavid du Colombier findblock(DBlock *bb)
635*368c31abSDavid du Colombier {
636*368c31abSDavid du Colombier 	DBlock *b, *last;
637*368c31abSDavid du Colombier 	int h;
638*368c31abSDavid du Colombier 
639*368c31abSDavid du Colombier 	last = nil;
640*368c31abSDavid du Colombier 	h = pbhash(bb->addr);
641*368c31abSDavid du Colombier 	for(b = dcache.heads[h]; b != nil; b = b->next){
642*368c31abSDavid du Colombier 		if(last != b->prev)
643*368c31abSDavid du Colombier 			sysfatal("bad prev link");
644*368c31abSDavid du Colombier 		if(b == bb)
645*368c31abSDavid du Colombier 			return;
646*368c31abSDavid du Colombier 		last = b;
647*368c31abSDavid du Colombier 	}
648*368c31abSDavid du Colombier 	sysfatal("block missing from hash table");
649*368c31abSDavid du Colombier }
650*368c31abSDavid du Colombier 
651*368c31abSDavid du Colombier void
652*368c31abSDavid du Colombier checkdcache(void)
653*368c31abSDavid du Colombier {
654*368c31abSDavid du Colombier 	DBlock *b;
655*368c31abSDavid du Colombier 	u32int size, now;
656*368c31abSDavid du Colombier 	int i, k, refed, nfree;
657*368c31abSDavid du Colombier 
658*368c31abSDavid du Colombier 	qlock(&dcache.lock);
659*368c31abSDavid du Colombier 	size = dcache.size;
660*368c31abSDavid du Colombier 	now = dcache.now;
661*368c31abSDavid du Colombier 	for(i = 0; i < dcache.nheap; i++){
662*368c31abSDavid du Colombier 		if(dcache.heap[i]->heap != i)
663*368c31abSDavid du Colombier 			sysfatal("dc: mis-heaped at %d: %d", i, dcache.heap[i]->heap);
664*368c31abSDavid du Colombier 		if(i > 0 && dcache.heap[(i - 1) >> 1]->used2 - now > dcache.heap[i]->used2 - now)
665*368c31abSDavid du Colombier 			sysfatal("dc: bad heap ordering");
666*368c31abSDavid du Colombier 		k = (i << 1) + 1;
667*368c31abSDavid du Colombier 		if(k < dcache.nheap && dcache.heap[i]->used2 - now > dcache.heap[k]->used2 - now)
668*368c31abSDavid du Colombier 			sysfatal("dc: bad heap ordering");
669*368c31abSDavid du Colombier 		k++;
670*368c31abSDavid du Colombier 		if(k < dcache.nheap && dcache.heap[i]->used2 - now > dcache.heap[k]->used2 - now)
671*368c31abSDavid du Colombier 			sysfatal("dc: bad heap ordering");
672*368c31abSDavid du Colombier 	}
673*368c31abSDavid du Colombier 
674*368c31abSDavid du Colombier 	refed = 0;
675*368c31abSDavid du Colombier 	for(i = 0; i < dcache.nblocks; i++){
676*368c31abSDavid du Colombier 		b = &dcache.blocks[i];
677*368c31abSDavid du Colombier 		if(b->data != &dcache.mem[i * size])
678*368c31abSDavid du Colombier 			sysfatal("dc: mis-blocked at %d", i);
679*368c31abSDavid du Colombier 		if(b->ref && b->heap == TWID32)
680*368c31abSDavid du Colombier 			refed++;
681*368c31abSDavid du Colombier 		if(b->addr)
682*368c31abSDavid du Colombier 			findblock(b);
683*368c31abSDavid du Colombier 		if(b->heap != TWID32
684*368c31abSDavid du Colombier 		&& dcache.heap[b->heap] != b)
685*368c31abSDavid du Colombier 			sysfatal("dc: spurious heap value");
686*368c31abSDavid du Colombier 	}
687*368c31abSDavid du Colombier 
688*368c31abSDavid du Colombier 	nfree = 0;
689*368c31abSDavid du Colombier 	for(b = dcache.free; b != nil; b = b->next){
690*368c31abSDavid du Colombier 		if(b->addr != 0 || b->heap != TWID32)
691*368c31abSDavid du Colombier 			sysfatal("dc: bad free list");
692*368c31abSDavid du Colombier 		nfree++;
693*368c31abSDavid du Colombier 	}
694*368c31abSDavid du Colombier 
695*368c31abSDavid du Colombier 	if(dcache.nheap + nfree + refed != dcache.nblocks)
696*368c31abSDavid du Colombier 		sysfatal("dc: missing blocks: %d %d %d", dcache.nheap, refed, dcache.nblocks);
697*368c31abSDavid du Colombier 	qunlock(&dcache.lock);
698*368c31abSDavid du Colombier }
699*368c31abSDavid du Colombier 
700*368c31abSDavid du Colombier void
701*368c31abSDavid du Colombier flushdcache(void)
702*368c31abSDavid du Colombier {
703*368c31abSDavid du Colombier 	trace(TraceProc, "flushdcache enter");
704*368c31abSDavid du Colombier 	kickround(&dcache.round, 1);
705*368c31abSDavid du Colombier 	trace(TraceProc, "flushdcache exit");
706*368c31abSDavid du Colombier }
707*368c31abSDavid du Colombier 
708*368c31abSDavid du Colombier void
709*368c31abSDavid du Colombier kickdcache(void)
710*368c31abSDavid du Colombier {
711*368c31abSDavid du Colombier 	kickround(&dcache.round, 0);
712*368c31abSDavid du Colombier }
713*368c31abSDavid du Colombier 
714*368c31abSDavid du Colombier static int
715*368c31abSDavid du Colombier parallelwrites(DBlock **b, DBlock **eb, int dirty)
716*368c31abSDavid du Colombier {
717*368c31abSDavid du Colombier 	DBlock **p, **q;
718*368c31abSDavid du Colombier 	Part *part;
719*368c31abSDavid du Colombier 
720*368c31abSDavid du Colombier 	for(p=b; p<eb && (*p)->dirty == dirty; p++){
721*368c31abSDavid du Colombier 		assert(b<=p && p<eb);
722*368c31abSDavid du Colombier 		sendp((*p)->part->writechan, *p);
723*368c31abSDavid du Colombier 	}
724*368c31abSDavid du Colombier 	q = p;
725*368c31abSDavid du Colombier 	for(p=b; p<q; p++){
726*368c31abSDavid du Colombier 		assert(b<=p && p<eb);
727*368c31abSDavid du Colombier 		recvp((*p)->writedonechan);
728*368c31abSDavid du Colombier 	}
729*368c31abSDavid du Colombier 
730*368c31abSDavid du Colombier 	/*
731*368c31abSDavid du Colombier 	 * Flush the partitions that have been written to.
732*368c31abSDavid du Colombier 	 */
733*368c31abSDavid du Colombier 	part = nil;
734*368c31abSDavid du Colombier 	for(p=b; p<q; p++){
735*368c31abSDavid du Colombier 		if(part != (*p)->part){
736*368c31abSDavid du Colombier 			part = (*p)->part;
737*368c31abSDavid du Colombier 			flushpart(part);	/* what if it fails? */
738*368c31abSDavid du Colombier 		}
739*368c31abSDavid du Colombier 	}
740*368c31abSDavid du Colombier 
741*368c31abSDavid du Colombier 	return p-b;
742*368c31abSDavid du Colombier }
743*368c31abSDavid du Colombier 
744*368c31abSDavid du Colombier /*
745*368c31abSDavid du Colombier  * Sort first by dirty flag, then by partition, then by address in partition.
746*368c31abSDavid du Colombier  */
747*368c31abSDavid du Colombier static int
748*368c31abSDavid du Colombier writeblockcmp(const void *va, const void *vb)
749*368c31abSDavid du Colombier {
750*368c31abSDavid du Colombier 	DBlock *a, *b;
751*368c31abSDavid du Colombier 
752*368c31abSDavid du Colombier 	a = *(DBlock**)va;
753*368c31abSDavid du Colombier 	b = *(DBlock**)vb;
754*368c31abSDavid du Colombier 
755*368c31abSDavid du Colombier 	if(a->dirty != b->dirty)
756*368c31abSDavid du Colombier 		return a->dirty - b->dirty;
757*368c31abSDavid du Colombier 	if(a->part != b->part){
758*368c31abSDavid du Colombier 		if(a->part < b->part)
759*368c31abSDavid du Colombier 			return -1;
760*368c31abSDavid du Colombier 		if(a->part > b->part)
761*368c31abSDavid du Colombier 			return 1;
762*368c31abSDavid du Colombier 	}
763*368c31abSDavid du Colombier 	if(a->addr < b->addr)
764*368c31abSDavid du Colombier 		return -1;
765*368c31abSDavid du Colombier 	return 1;
766*368c31abSDavid du Colombier }
767*368c31abSDavid du Colombier 
768*368c31abSDavid du Colombier static void
769*368c31abSDavid du Colombier flushproc(void *v)
770*368c31abSDavid du Colombier {
771*368c31abSDavid du Colombier 	int i, j, n;
772*368c31abSDavid du Colombier 	ulong t0;
773*368c31abSDavid du Colombier 	DBlock *b, **write;
774*368c31abSDavid du Colombier 	AState as;
775*368c31abSDavid du Colombier 
776*368c31abSDavid du Colombier 	USED(v);
777*368c31abSDavid du Colombier 	threadsetname("flushproc");
778*368c31abSDavid du Colombier 	for(;;){
779*368c31abSDavid du Colombier 		waitforkick(&dcache.round);
780*368c31abSDavid du Colombier 
781*368c31abSDavid du Colombier 		trace(TraceWork, "start");
782*368c31abSDavid du Colombier 		qlock(&dcache.lock);
783*368c31abSDavid du Colombier 		as = dcache.state;
784*368c31abSDavid du Colombier 		qunlock(&dcache.lock);
785*368c31abSDavid du Colombier 
786*368c31abSDavid du Colombier 		t0 = nsec()/1000;
787*368c31abSDavid du Colombier 
788*368c31abSDavid du Colombier 		trace(TraceProc, "build t=%lud", (ulong)(nsec()/1000)-t0);
789*368c31abSDavid du Colombier 		write = dcache.write;
790*368c31abSDavid du Colombier 		n = 0;
791*368c31abSDavid du Colombier 		for(i=0; i<dcache.nblocks; i++){
792*368c31abSDavid du Colombier 			b = &dcache.blocks[i];
793*368c31abSDavid du Colombier 			if(b->dirty)
794*368c31abSDavid du Colombier 				write[n++] = b;
795*368c31abSDavid du Colombier 		}
796*368c31abSDavid du Colombier 
797*368c31abSDavid du Colombier 		qsort(write, n, sizeof(write[0]), writeblockcmp);
798*368c31abSDavid du Colombier 
799*368c31abSDavid du Colombier 		/* Write each stage of blocks out. */
800*368c31abSDavid du Colombier 		trace(TraceProc, "writeblocks t=%lud", (ulong)(nsec()/1000)-t0);
801*368c31abSDavid du Colombier 		i = 0;
802*368c31abSDavid du Colombier 		for(j=1; j<DirtyMax; j++){
803*368c31abSDavid du Colombier 			trace(TraceProc, "writeblocks.%d t=%lud", j, (ulong)(nsec()/1000)-t0);
804*368c31abSDavid du Colombier 			i += parallelwrites(write+i, write+n, j);
805*368c31abSDavid du Colombier 		}
806*368c31abSDavid du Colombier 		if(i != n){
807*368c31abSDavid du Colombier 			fprint(2, "in flushproc i=%d n=%d\n", i, n);
808*368c31abSDavid du Colombier 			for(i=0; i<n; i++)
809*368c31abSDavid du Colombier 				fprint(2, "\tblock %d: dirty=%d\n", i, write[i]->dirty);
810*368c31abSDavid du Colombier 			abort();
811*368c31abSDavid du Colombier 		}
812*368c31abSDavid du Colombier 
813*368c31abSDavid du Colombier /* XXX
814*368c31abSDavid du Colombier * the locking here is suspect.  what if a block is redirtied
815*368c31abSDavid du Colombier * after the write happens?  we'll still decrement dcache.ndirty here.
816*368c31abSDavid du Colombier */
817*368c31abSDavid du Colombier 		trace(TraceProc, "undirty.%d t=%lud", j, (ulong)(nsec()/1000)-t0);
818*368c31abSDavid du Colombier 		qlock(&dcache.lock);
819*368c31abSDavid du Colombier 		dcache.diskstate = as;
820*368c31abSDavid du Colombier 		for(i=0; i<n; i++){
821*368c31abSDavid du Colombier 			b = write[i];
822*368c31abSDavid du Colombier 			--dcache.ndirty;
823*368c31abSDavid du Colombier 			if(b->ref == 0 && b->heap == TWID32){
824*368c31abSDavid du Colombier 				upheap(dcache.nheap++, b);
825*368c31abSDavid du Colombier 				rwakeupall(&dcache.full);
826*368c31abSDavid du Colombier 			}
827*368c31abSDavid du Colombier 		}
828*368c31abSDavid du Colombier 		setstat(StatDcacheDirty, dcache.ndirty);
829*368c31abSDavid du Colombier 		qunlock(&dcache.lock);
830*368c31abSDavid du Colombier 		addstat(StatDcacheFlush, 1);
831*368c31abSDavid du Colombier 		trace(TraceWork, "finish");
832*368c31abSDavid du Colombier 	}
833*368c31abSDavid du Colombier }
834*368c31abSDavid du Colombier 
835*368c31abSDavid du Colombier static void
836*368c31abSDavid du Colombier writeproc(void *v)
837*368c31abSDavid du Colombier {
838*368c31abSDavid du Colombier 	DBlock *b;
839*368c31abSDavid du Colombier 	Part *p;
840*368c31abSDavid du Colombier 
841*368c31abSDavid du Colombier 	p = v;
842*368c31abSDavid du Colombier 
843*368c31abSDavid du Colombier 	threadsetname("writeproc:%s", p->name);
844*368c31abSDavid du Colombier 	for(;;){
845*368c31abSDavid du Colombier 		b = recvp(p->writechan);
846*368c31abSDavid du Colombier 		trace(TraceWork, "start");
847*368c31abSDavid du Colombier 		assert(b->part == p);
848*368c31abSDavid du Colombier 		trace(TraceProc, "wlock %s 0x%llux", p->name, b->addr);
849*368c31abSDavid du Colombier 		wlock(&b->lock);
850*368c31abSDavid du Colombier 		trace(TraceProc, "writepart %s 0x%llux", p->name, b->addr);
851*368c31abSDavid du Colombier 		diskaccess(0);
852*368c31abSDavid du Colombier 		if(writepart(p, b->addr, b->data, b->size) < 0)
853*368c31abSDavid du Colombier 			fprint(2, "write error: %r\n"); /* XXX details! */
854*368c31abSDavid du Colombier 		addstat(StatApartWrite, 1);
855*368c31abSDavid du Colombier 		addstat(StatApartWriteBytes, b->size);
856*368c31abSDavid du Colombier 		b->dirty = 0;
857*368c31abSDavid du Colombier 		wunlock(&b->lock);
858*368c31abSDavid du Colombier 		trace(TraceProc, "finish %s 0x%llux", p->name, b->addr);
859*368c31abSDavid du Colombier 		trace(TraceWork, "finish");
860*368c31abSDavid du Colombier 		sendp(b->writedonechan, b);
861*368c31abSDavid du Colombier 	}
862*368c31abSDavid du Colombier }
863