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