xref: /plan9/sys/src/cmd/venti/srv/icachewrite.c (revision 23566e0c4b9b5ce80121325d240a2f6e83a70a8f)
1368c31abSDavid du Colombier /*
2368c31abSDavid du Colombier  * Write the dirty icache entries to disk.  Random seeks are
3368c31abSDavid du Colombier  * so expensive that it makes sense to wait until we have
4368c31abSDavid du Colombier  * a lot and then just make a sequential pass over the disk.
5368c31abSDavid du Colombier  */
6368c31abSDavid du Colombier #include "stdinc.h"
7368c31abSDavid du Colombier #include "dat.h"
8368c31abSDavid du Colombier #include "fns.h"
9368c31abSDavid du Colombier 
10368c31abSDavid du Colombier static void icachewriteproc(void*);
11368c31abSDavid du Colombier static void icachewritecoord(void*);
12368c31abSDavid du Colombier static IEntry *iesort(IEntry*);
13368c31abSDavid du Colombier 
14368c31abSDavid du Colombier int icachesleeptime = 1000;	/* milliseconds */
15f9e1cf08SDavid du Colombier int minicachesleeptime = 0;
16368c31abSDavid du Colombier 
17368c31abSDavid du Colombier enum
18368c31abSDavid du Colombier {
19368c31abSDavid du Colombier 	Bufsize = 8*1024*1024
20368c31abSDavid du Colombier };
21368c31abSDavid du Colombier 
22368c31abSDavid du Colombier typedef struct IWrite IWrite;
23368c31abSDavid du Colombier struct IWrite
24368c31abSDavid du Colombier {
25368c31abSDavid du Colombier 	Round round;
26368c31abSDavid du Colombier 	AState as;
27368c31abSDavid du Colombier };
28368c31abSDavid du Colombier 
29368c31abSDavid du Colombier static IWrite iwrite;
30368c31abSDavid du Colombier 
31368c31abSDavid du Colombier void
initicachewrite(void)32368c31abSDavid du Colombier initicachewrite(void)
33368c31abSDavid du Colombier {
34368c31abSDavid du Colombier 	int i;
35368c31abSDavid du Colombier 	Index *ix;
36368c31abSDavid du Colombier 
37368c31abSDavid du Colombier 	initround(&iwrite.round, "icache", 120*60*1000);
38368c31abSDavid du Colombier 	ix = mainindex;
39368c31abSDavid du Colombier 	for(i=0; i<ix->nsects; i++){
40368c31abSDavid du Colombier 		ix->sects[i]->writechan = chancreate(sizeof(ulong), 1);
41368c31abSDavid du Colombier 		ix->sects[i]->writedonechan = chancreate(sizeof(ulong), 1);
42368c31abSDavid du Colombier 		vtproc(icachewriteproc, ix->sects[i]);
43368c31abSDavid du Colombier 	}
44368c31abSDavid du Colombier 	vtproc(icachewritecoord, nil);
45368c31abSDavid du Colombier 	vtproc(delaykickroundproc, &iwrite.round);
46368c31abSDavid du Colombier }
47368c31abSDavid du Colombier 
48a6b1725cSDavid du Colombier static u64int
ie2diskaddr(Index * ix,ISect * is,IEntry * ie)49a6b1725cSDavid du Colombier ie2diskaddr(Index *ix, ISect *is, IEntry *ie)
50a6b1725cSDavid du Colombier {
51a6b1725cSDavid du Colombier 	u64int bucket, addr;
52a6b1725cSDavid du Colombier 
53a6b1725cSDavid du Colombier 	bucket = hashbits(ie->score, 32)/ix->div;
54a6b1725cSDavid du Colombier 	addr = is->blockbase + ((bucket - is->start) << is->blocklog);
55a6b1725cSDavid du Colombier 	return addr;
56a6b1725cSDavid du Colombier }
57a6b1725cSDavid du Colombier 
58368c31abSDavid du Colombier static IEntry*
nextchunk(Index * ix,ISect * is,IEntry ** pie,u64int * paddr,uint * pnbuf)59368c31abSDavid du Colombier nextchunk(Index *ix, ISect *is, IEntry **pie, u64int *paddr, uint *pnbuf)
60368c31abSDavid du Colombier {
61368c31abSDavid du Colombier 	u64int addr, naddr;
62368c31abSDavid du Colombier 	uint nbuf;
63368c31abSDavid du Colombier 	int bsize;
64368c31abSDavid du Colombier 	IEntry *iefirst, *ie, **l;
65368c31abSDavid du Colombier 
66368c31abSDavid du Colombier 	bsize = 1<<is->blocklog;
67368c31abSDavid du Colombier 	iefirst = *pie;
68a6b1725cSDavid du Colombier 	addr = ie2diskaddr(ix, is, iefirst);
69368c31abSDavid du Colombier 	nbuf = 0;
70368c31abSDavid du Colombier 	for(l = &iefirst->nextdirty; (ie = *l) != nil; l = &(*l)->nextdirty){
71a6b1725cSDavid du Colombier 		naddr = ie2diskaddr(ix, is, ie);
72368c31abSDavid du Colombier 		if(naddr - addr >= Bufsize)
73368c31abSDavid du Colombier 			break;
74368c31abSDavid du Colombier 		nbuf = naddr - addr;
75368c31abSDavid du Colombier 	}
76368c31abSDavid du Colombier 	nbuf += bsize;
77368c31abSDavid du Colombier 
78368c31abSDavid du Colombier 	*l = nil;
79368c31abSDavid du Colombier 	*pie = ie;
80368c31abSDavid du Colombier 	*paddr = addr;
81368c31abSDavid du Colombier 	*pnbuf = nbuf;
82368c31abSDavid du Colombier 	return iefirst;
83368c31abSDavid du Colombier }
84368c31abSDavid du Colombier 
85368c31abSDavid du Colombier static int
icachewritesect(Index * ix,ISect * is,u8int * buf)86368c31abSDavid du Colombier icachewritesect(Index *ix, ISect *is, u8int *buf)
87368c31abSDavid du Colombier {
88f9e1cf08SDavid du Colombier 	int err, i, werr, h, bsize, t;
89368c31abSDavid du Colombier 	u32int lo, hi;
90368c31abSDavid du Colombier 	u64int addr, naddr;
91368c31abSDavid du Colombier 	uint nbuf, off;
92368c31abSDavid du Colombier 	DBlock *b;
93368c31abSDavid du Colombier 	IBucket ib;
94368c31abSDavid du Colombier 	IEntry *ie, *iedirty, **l, *chunk;
95368c31abSDavid du Colombier 
96368c31abSDavid du Colombier 	lo = is->start * ix->div;
97368c31abSDavid du Colombier 	if(TWID32/ix->div < is->stop)
98368c31abSDavid du Colombier 		hi = TWID32;
99368c31abSDavid du Colombier 	else
100368c31abSDavid du Colombier 		hi = is->stop * ix->div - 1;
101368c31abSDavid du Colombier 
10229e26a97SDavid du Colombier 	trace(TraceProc, "icachewritesect enter %ud %ud %llud",
10329e26a97SDavid du Colombier 		lo, hi, iwrite.as.aa);
104368c31abSDavid du Colombier 
105368c31abSDavid du Colombier 	iedirty = icachedirty(lo, hi, iwrite.as.aa);
106368c31abSDavid du Colombier 	iedirty = iesort(iedirty);
107368c31abSDavid du Colombier 	bsize = 1 << is->blocklog;
108368c31abSDavid du Colombier 	err = 0;
109368c31abSDavid du Colombier 
110368c31abSDavid du Colombier 	while(iedirty){
111368c31abSDavid du Colombier 		disksched();
112368c31abSDavid du Colombier 		while((t = icachesleeptime) == SleepForever){
113368c31abSDavid du Colombier 			sleep(1000);
114368c31abSDavid du Colombier 			disksched();
115368c31abSDavid du Colombier 		}
116368c31abSDavid du Colombier 		if(t < minicachesleeptime)
117368c31abSDavid du Colombier 			t = minicachesleeptime;
118f9e1cf08SDavid du Colombier 		if(t > 0)
119368c31abSDavid du Colombier 			sleep(t);
120368c31abSDavid du Colombier 		trace(TraceProc, "icachewritesect nextchunk");
121368c31abSDavid du Colombier 		chunk = nextchunk(ix, is, &iedirty, &addr, &nbuf);
122368c31abSDavid du Colombier 
12329e26a97SDavid du Colombier 		trace(TraceProc, "icachewritesect readpart 0x%llux+0x%ux",
12429e26a97SDavid du Colombier 			addr, nbuf);
125368c31abSDavid du Colombier 		if(readpart(is->part, addr, buf, nbuf) < 0){
12629e26a97SDavid du Colombier 			fprint(2, "%s: part %s addr 0x%llux: icachewritesect "
12729e26a97SDavid du Colombier 				"readpart: %r\n", argv0, is->part->name, addr);
128368c31abSDavid du Colombier 			err  = -1;
129368c31abSDavid du Colombier 			continue;
130368c31abSDavid du Colombier 		}
131368c31abSDavid du Colombier 		trace(TraceProc, "icachewritesect updatebuf");
132368c31abSDavid du Colombier 		addstat(StatIsectReadBytes, nbuf);
133368c31abSDavid du Colombier 		addstat(StatIsectRead, 1);
134368c31abSDavid du Colombier 
135368c31abSDavid du Colombier 		for(l=&chunk; (ie=*l)!=nil; l=&ie->nextdirty){
136368c31abSDavid du Colombier again:
137a6b1725cSDavid du Colombier 			naddr = ie2diskaddr(ix, is, ie);
138368c31abSDavid du Colombier 			off = naddr - addr;
139368c31abSDavid du Colombier 			if(off+bsize > nbuf){
14029e26a97SDavid du Colombier 				fprint(2, "%s: whoops! addr=0x%llux nbuf=%ud "
14129e26a97SDavid du Colombier 					"addr+nbuf=0x%llux naddr=0x%llux\n",
14229e26a97SDavid du Colombier 					argv0, addr, nbuf, addr+nbuf, naddr);
143368c31abSDavid du Colombier 				assert(off+bsize <= nbuf);
144368c31abSDavid du Colombier 			}
145368c31abSDavid du Colombier 			unpackibucket(&ib, buf+off, is->bucketmagic);
146368c31abSDavid du Colombier 			if(okibucket(&ib, is) < 0){
14729e26a97SDavid du Colombier 				fprint(2, "%s: bad bucket XXX\n", argv0);
148368c31abSDavid du Colombier 				goto skipit;
149368c31abSDavid du Colombier 			}
15029e26a97SDavid du Colombier 			trace(TraceProc, "icachewritesect add %V at 0x%llux",
15129e26a97SDavid du Colombier 				ie->score, naddr);
152368c31abSDavid du Colombier 			h = bucklook(ie->score, ie->ia.type, ib.data, ib.n);
153368c31abSDavid du Colombier 			if(h & 1){
154368c31abSDavid du Colombier 				h ^= 1;
155368c31abSDavid du Colombier 				packientry(ie, &ib.data[h]);
156368c31abSDavid du Colombier 			}else if(ib.n < is->buckmax){
15729e26a97SDavid du Colombier 				memmove(&ib.data[h + IEntrySize], &ib.data[h],
15829e26a97SDavid du Colombier 					ib.n*IEntrySize - h);
159368c31abSDavid du Colombier 				ib.n++;
160368c31abSDavid du Colombier 				packientry(ie, &ib.data[h]);
161368c31abSDavid du Colombier 			}else{
16229e26a97SDavid du Colombier 				fprint(2, "%s: bucket overflow XXX\n", argv0);
163368c31abSDavid du Colombier skipit:
164368c31abSDavid du Colombier 				err = -1;
165368c31abSDavid du Colombier 				*l = ie->nextdirty;
166368c31abSDavid du Colombier 				ie = *l;
167368c31abSDavid du Colombier 				if(ie)
168368c31abSDavid du Colombier 					goto again;
169368c31abSDavid du Colombier 				else
170368c31abSDavid du Colombier 					break;
171368c31abSDavid du Colombier 			}
172368c31abSDavid du Colombier 			packibucket(&ib, buf+off, is->bucketmagic);
173368c31abSDavid du Colombier 		}
174368c31abSDavid du Colombier 
175368c31abSDavid du Colombier 		diskaccess(1);
176368c31abSDavid du Colombier 
177368c31abSDavid du Colombier 		trace(TraceProc, "icachewritesect writepart", addr, nbuf);
178f9e1cf08SDavid du Colombier 		werr = 0;
179f9e1cf08SDavid du Colombier 		if(writepart(is->part, addr, buf, nbuf) < 0 || flushpart(is->part) < 0)
180f9e1cf08SDavid du Colombier 			werr = -1;
181f9e1cf08SDavid du Colombier 
182f9e1cf08SDavid du Colombier 		for(i=0; i<nbuf; i+=bsize){
183f9e1cf08SDavid du Colombier 			if((b = _getdblock(is->part, addr+i, ORDWR, 0)) != nil){
184f9e1cf08SDavid du Colombier 				memmove(b->data, buf+i, bsize);
185f9e1cf08SDavid du Colombier 				putdblock(b);
186f9e1cf08SDavid du Colombier 			}
187f9e1cf08SDavid du Colombier 		}
188f9e1cf08SDavid du Colombier 
189f9e1cf08SDavid du Colombier 		if(werr < 0){
19029e26a97SDavid du Colombier 			fprint(2, "%s: part %s addr 0x%llux: icachewritesect "
19129e26a97SDavid du Colombier 				"writepart: %r\n", argv0, is->part->name, addr);
192f9e1cf08SDavid du Colombier 			err = -1;
193368c31abSDavid du Colombier 			continue;
194368c31abSDavid du Colombier 		}
195f9e1cf08SDavid du Colombier 
196368c31abSDavid du Colombier 		addstat(StatIsectWriteBytes, nbuf);
197368c31abSDavid du Colombier 		addstat(StatIsectWrite, 1);
198368c31abSDavid du Colombier 		icacheclean(chunk);
199368c31abSDavid du Colombier 	}
200368c31abSDavid du Colombier 
201368c31abSDavid du Colombier 	trace(TraceProc, "icachewritesect done");
202368c31abSDavid du Colombier 	return err;
203368c31abSDavid du Colombier }
204368c31abSDavid du Colombier 
205368c31abSDavid du Colombier static void
icachewriteproc(void * v)206368c31abSDavid du Colombier icachewriteproc(void *v)
207368c31abSDavid du Colombier {
208368c31abSDavid du Colombier 	int ret;
209368c31abSDavid du Colombier 	uint bsize;
210368c31abSDavid du Colombier 	ISect *is;
211368c31abSDavid du Colombier 	Index *ix;
212368c31abSDavid du Colombier 	u8int *buf;
213368c31abSDavid du Colombier 
214368c31abSDavid du Colombier 	ix = mainindex;
215368c31abSDavid du Colombier 	is = v;
216368c31abSDavid du Colombier 	threadsetname("icachewriteproc:%s", is->part->name);
217368c31abSDavid du Colombier 
218368c31abSDavid du Colombier 	bsize = 1<<is->blocklog;
219368c31abSDavid du Colombier 	buf = emalloc(Bufsize+bsize);
220*1f9fb570SDavid du Colombier 	buf = (u8int*)(((uintptr)buf+bsize-1)&~(uintptr)(bsize-1));
221368c31abSDavid du Colombier 
222368c31abSDavid du Colombier 	for(;;){
223368c31abSDavid du Colombier 		trace(TraceProc, "icachewriteproc recv");
224368c31abSDavid du Colombier 		recv(is->writechan, 0);
225368c31abSDavid du Colombier 		trace(TraceWork, "start");
226368c31abSDavid du Colombier 		ret = icachewritesect(ix, is, buf);
227368c31abSDavid du Colombier 		trace(TraceProc, "icachewriteproc send");
228368c31abSDavid du Colombier 		trace(TraceWork, "finish");
229368c31abSDavid du Colombier 		sendul(is->writedonechan, ret);
230368c31abSDavid du Colombier 	}
231368c31abSDavid du Colombier }
232368c31abSDavid du Colombier 
233368c31abSDavid du Colombier static void
icachewritecoord(void * v)234368c31abSDavid du Colombier icachewritecoord(void *v)
235368c31abSDavid du Colombier {
236368c31abSDavid du Colombier 	int i, err;
237368c31abSDavid du Colombier 	Index *ix;
238368c31abSDavid du Colombier 	AState as;
239368c31abSDavid du Colombier 
240368c31abSDavid du Colombier 	USED(v);
241368c31abSDavid du Colombier 
242368c31abSDavid du Colombier 	threadsetname("icachewritecoord");
243368c31abSDavid du Colombier 
244368c31abSDavid du Colombier 	ix = mainindex;
245f9e1cf08SDavid du Colombier 	iwrite.as = icachestate();
246368c31abSDavid du Colombier 
247368c31abSDavid du Colombier 	for(;;){
248368c31abSDavid du Colombier 		trace(TraceProc, "icachewritecoord sleep");
249368c31abSDavid du Colombier 		waitforkick(&iwrite.round);
250368c31abSDavid du Colombier 		trace(TraceWork, "start");
251f9e1cf08SDavid du Colombier 		as = icachestate();
252368c31abSDavid du Colombier 		if(as.arena==iwrite.as.arena && as.aa==iwrite.as.aa){
253368c31abSDavid du Colombier 			/* will not be able to do anything more than last flush - kick disk */
254368c31abSDavid du Colombier 			trace(TraceProc, "icachewritecoord kick dcache");
255368c31abSDavid du Colombier 			kickdcache();
256368c31abSDavid du Colombier 			trace(TraceProc, "icachewritecoord kicked dcache");
257f9e1cf08SDavid du Colombier 			goto SkipWork;	/* won't do anything; don't bother rewriting bloom filter */
258368c31abSDavid du Colombier 		}
259368c31abSDavid du Colombier 		iwrite.as = as;
260368c31abSDavid du Colombier 
261368c31abSDavid du Colombier 		trace(TraceProc, "icachewritecoord start flush");
262368c31abSDavid du Colombier 		if(iwrite.as.arena){
263368c31abSDavid du Colombier 			for(i=0; i<ix->nsects; i++)
264368c31abSDavid du Colombier 				send(ix->sects[i]->writechan, 0);
265368c31abSDavid du Colombier 			if(ix->bloom)
266368c31abSDavid du Colombier 				send(ix->bloom->writechan, 0);
267368c31abSDavid du Colombier 
268368c31abSDavid du Colombier 			err = 0;
269368c31abSDavid du Colombier 			for(i=0; i<ix->nsects; i++)
270368c31abSDavid du Colombier 				err |= recvul(ix->sects[i]->writedonechan);
271368c31abSDavid du Colombier 			if(ix->bloom)
272368c31abSDavid du Colombier 				err |= recvul(ix->bloom->writedonechan);
273368c31abSDavid du Colombier 
274368c31abSDavid du Colombier 			trace(TraceProc, "icachewritecoord donewrite err=%d", err);
275f9e1cf08SDavid du Colombier 			if(err == 0){
276368c31abSDavid du Colombier 				setatailstate(&iwrite.as);
277368c31abSDavid du Colombier 			}
278f9e1cf08SDavid du Colombier 		}
279f9e1cf08SDavid du Colombier 	SkipWork:
280368c31abSDavid du Colombier 		icacheclean(nil);	/* wake up anyone waiting */
281368c31abSDavid du Colombier 		trace(TraceWork, "finish");
282368c31abSDavid du Colombier 		addstat(StatIcacheFlush, 1);
283368c31abSDavid du Colombier 	}
284368c31abSDavid du Colombier }
285368c31abSDavid du Colombier 
286368c31abSDavid du Colombier void
flushicache(void)287368c31abSDavid du Colombier flushicache(void)
288368c31abSDavid du Colombier {
289368c31abSDavid du Colombier 	trace(TraceProc, "flushicache enter");
290368c31abSDavid du Colombier 	kickround(&iwrite.round, 1);
291368c31abSDavid du Colombier 	trace(TraceProc, "flushicache exit");
292368c31abSDavid du Colombier }
293368c31abSDavid du Colombier 
294368c31abSDavid du Colombier void
kickicache(void)295368c31abSDavid du Colombier kickicache(void)
296368c31abSDavid du Colombier {
297368c31abSDavid du Colombier 	kickround(&iwrite.round, 0);
298368c31abSDavid du Colombier }
299368c31abSDavid du Colombier 
300368c31abSDavid du Colombier void
delaykickicache(void)301368c31abSDavid du Colombier delaykickicache(void)
302368c31abSDavid du Colombier {
303368c31abSDavid du Colombier 	delaykickround(&iwrite.round);
304368c31abSDavid du Colombier }
305368c31abSDavid du Colombier 
306368c31abSDavid du Colombier static IEntry*
iesort(IEntry * ie)307368c31abSDavid du Colombier iesort(IEntry *ie)
308368c31abSDavid du Colombier {
309368c31abSDavid du Colombier 	int cmp;
310368c31abSDavid du Colombier 	IEntry **l;
311368c31abSDavid du Colombier 	IEntry *ie1, *ie2, *sorted;
312368c31abSDavid du Colombier 
313368c31abSDavid du Colombier 	if(ie == nil || ie->nextdirty == nil)
314368c31abSDavid du Colombier 		return ie;
315368c31abSDavid du Colombier 
316368c31abSDavid du Colombier 	/* split the lists */
317368c31abSDavid du Colombier 	ie1 = ie;
318368c31abSDavid du Colombier 	ie2 = ie;
319368c31abSDavid du Colombier 	if(ie2)
320368c31abSDavid du Colombier 		ie2 = ie2->nextdirty;
321368c31abSDavid du Colombier 	if(ie2)
322368c31abSDavid du Colombier 		ie2 = ie2->nextdirty;
323368c31abSDavid du Colombier 	while(ie1 && ie2){
324368c31abSDavid du Colombier 		ie1 = ie1->nextdirty;
325368c31abSDavid du Colombier 		ie2 = ie2->nextdirty;
326368c31abSDavid du Colombier 		if(ie2)
327368c31abSDavid du Colombier 			ie2 = ie2->nextdirty;
328368c31abSDavid du Colombier 	}
329368c31abSDavid du Colombier 	if(ie1){
330368c31abSDavid du Colombier 		ie2 = ie1->nextdirty;
331368c31abSDavid du Colombier 		ie1->nextdirty = nil;
332368c31abSDavid du Colombier 	}
333368c31abSDavid du Colombier 
334368c31abSDavid du Colombier 	/* sort the lists */
335368c31abSDavid du Colombier 	ie1 = iesort(ie);
336368c31abSDavid du Colombier 	ie2 = iesort(ie2);
337368c31abSDavid du Colombier 
338368c31abSDavid du Colombier 	/* merge the lists */
339368c31abSDavid du Colombier 	sorted = nil;
340368c31abSDavid du Colombier 	l = &sorted;
341368c31abSDavid du Colombier 	cmp = 0;
342368c31abSDavid du Colombier 	while(ie1 || ie2){
343368c31abSDavid du Colombier 		if(ie1 && ie2)
344368c31abSDavid du Colombier 			cmp = scorecmp(ie1->score, ie2->score);
345368c31abSDavid du Colombier 		if(ie1==nil || (ie2 && cmp > 0)){
346368c31abSDavid du Colombier 			*l = ie2;
347368c31abSDavid du Colombier 			l = &ie2->nextdirty;
348368c31abSDavid du Colombier 			ie2 = ie2->nextdirty;
349368c31abSDavid du Colombier 		}else{
350368c31abSDavid du Colombier 			*l = ie1;
351368c31abSDavid du Colombier 			l = &ie1->nextdirty;
352368c31abSDavid du Colombier 			ie1 = ie1->nextdirty;
353368c31abSDavid du Colombier 		}
354368c31abSDavid du Colombier 	}
355368c31abSDavid du Colombier 	*l = nil;
356368c31abSDavid du Colombier 	return sorted;
357368c31abSDavid du Colombier }
358368c31abSDavid du Colombier 
359