Blame


1 a0d146ed 2005-07-12 devnull /*
2 a0d146ed 2005-07-12 devnull * Write the dirty icache entries to disk. Random seeks are
3 a0d146ed 2005-07-12 devnull * so expensive that it makes sense to wait until we have
4 a0d146ed 2005-07-12 devnull * a lot and then just make a sequential pass over the disk.
5 a0d146ed 2005-07-12 devnull */
6 a0d146ed 2005-07-12 devnull #include "stdinc.h"
7 a0d146ed 2005-07-12 devnull #include "dat.h"
8 a0d146ed 2005-07-12 devnull #include "fns.h"
9 a0d146ed 2005-07-12 devnull
10 a0d146ed 2005-07-12 devnull static void icachewriteproc(void*);
11 a0d146ed 2005-07-12 devnull static void icachewritecoord(void*);
12 a0d146ed 2005-07-12 devnull static IEntry *iesort(IEntry*);
13 a0d146ed 2005-07-12 devnull
14 a0d146ed 2005-07-12 devnull int icachesleeptime = 1000; /* milliseconds */
15 45ac814c 2007-10-29 rsc int minicachesleeptime = 0;
16 a0d146ed 2005-07-12 devnull
17 a0d146ed 2005-07-12 devnull enum
18 a0d146ed 2005-07-12 devnull {
19 a0d146ed 2005-07-12 devnull Bufsize = 8*1024*1024
20 a0d146ed 2005-07-12 devnull };
21 a0d146ed 2005-07-12 devnull
22 a0d146ed 2005-07-12 devnull typedef struct IWrite IWrite;
23 a0d146ed 2005-07-12 devnull struct IWrite
24 a0d146ed 2005-07-12 devnull {
25 a0d146ed 2005-07-12 devnull Round round;
26 a0d146ed 2005-07-12 devnull AState as;
27 a0d146ed 2005-07-12 devnull };
28 a0d146ed 2005-07-12 devnull
29 a0d146ed 2005-07-12 devnull static IWrite iwrite;
30 a0d146ed 2005-07-12 devnull
31 a0d146ed 2005-07-12 devnull void
32 a0d146ed 2005-07-12 devnull initicachewrite(void)
33 a0d146ed 2005-07-12 devnull {
34 a0d146ed 2005-07-12 devnull int i;
35 a0d146ed 2005-07-12 devnull Index *ix;
36 a0d146ed 2005-07-12 devnull
37 a0d146ed 2005-07-12 devnull initround(&iwrite.round, "icache", 120*60*1000);
38 a0d146ed 2005-07-12 devnull ix = mainindex;
39 a0d146ed 2005-07-12 devnull for(i=0; i<ix->nsects; i++){
40 a0d146ed 2005-07-12 devnull ix->sects[i]->writechan = chancreate(sizeof(ulong), 1);
41 a0d146ed 2005-07-12 devnull ix->sects[i]->writedonechan = chancreate(sizeof(ulong), 1);
42 a0d146ed 2005-07-12 devnull vtproc(icachewriteproc, ix->sects[i]);
43 a0d146ed 2005-07-12 devnull }
44 a0d146ed 2005-07-12 devnull vtproc(icachewritecoord, nil);
45 a0d146ed 2005-07-12 devnull vtproc(delaykickroundproc, &iwrite.round);
46 a0d146ed 2005-07-12 devnull }
47 a0d146ed 2005-07-12 devnull
48 7a400ee9 2007-09-25 rsc static u64int
49 7a400ee9 2007-09-25 rsc ie2diskaddr(Index *ix, ISect *is, IEntry *ie)
50 7a400ee9 2007-09-25 rsc {
51 7a400ee9 2007-09-25 rsc u64int bucket, addr;
52 7a400ee9 2007-09-25 rsc
53 7a400ee9 2007-09-25 rsc bucket = hashbits(ie->score, 32)/ix->div;
54 7a400ee9 2007-09-25 rsc addr = is->blockbase + ((bucket - is->start) << is->blocklog);
55 7a400ee9 2007-09-25 rsc return addr;
56 7a400ee9 2007-09-25 rsc }
57 7a400ee9 2007-09-25 rsc
58 a0d146ed 2005-07-12 devnull static IEntry*
59 a0d146ed 2005-07-12 devnull nextchunk(Index *ix, ISect *is, IEntry **pie, u64int *paddr, uint *pnbuf)
60 a0d146ed 2005-07-12 devnull {
61 a0d146ed 2005-07-12 devnull u64int addr, naddr;
62 a0d146ed 2005-07-12 devnull uint nbuf;
63 a0d146ed 2005-07-12 devnull int bsize;
64 a0d146ed 2005-07-12 devnull IEntry *iefirst, *ie, **l;
65 a0d146ed 2005-07-12 devnull
66 a0d146ed 2005-07-12 devnull bsize = 1<<is->blocklog;
67 a0d146ed 2005-07-12 devnull iefirst = *pie;
68 7a400ee9 2007-09-25 rsc addr = ie2diskaddr(ix, is, iefirst);
69 a0d146ed 2005-07-12 devnull nbuf = 0;
70 7a400ee9 2007-09-25 rsc for(l = &iefirst->nextdirty; (ie = *l) != nil; l = &(*l)->nextdirty){
71 7a400ee9 2007-09-25 rsc naddr = ie2diskaddr(ix, is, ie);
72 a0d146ed 2005-07-12 devnull if(naddr - addr >= Bufsize)
73 a0d146ed 2005-07-12 devnull break;
74 7a400ee9 2007-09-25 rsc nbuf = naddr - addr;
75 a0d146ed 2005-07-12 devnull }
76 a0d146ed 2005-07-12 devnull nbuf += bsize;
77 a0d146ed 2005-07-12 devnull
78 a0d146ed 2005-07-12 devnull *l = nil;
79 a0d146ed 2005-07-12 devnull *pie = ie;
80 a0d146ed 2005-07-12 devnull *paddr = addr;
81 a0d146ed 2005-07-12 devnull *pnbuf = nbuf;
82 a0d146ed 2005-07-12 devnull return iefirst;
83 a0d146ed 2005-07-12 devnull }
84 fa325e9b 2020-01-10 cross
85 a0d146ed 2005-07-12 devnull static int
86 a0d146ed 2005-07-12 devnull icachewritesect(Index *ix, ISect *is, u8int *buf)
87 a0d146ed 2005-07-12 devnull {
88 7a400ee9 2007-09-25 rsc int err, i, werr, h, bsize, t;
89 a0d146ed 2005-07-12 devnull u32int lo, hi;
90 a0d146ed 2005-07-12 devnull u64int addr, naddr;
91 a0d146ed 2005-07-12 devnull uint nbuf, off;
92 a0d146ed 2005-07-12 devnull DBlock *b;
93 a0d146ed 2005-07-12 devnull IBucket ib;
94 a0d146ed 2005-07-12 devnull IEntry *ie, *iedirty, **l, *chunk;
95 a0d146ed 2005-07-12 devnull
96 a0d146ed 2005-07-12 devnull lo = is->start * ix->div;
97 a0d146ed 2005-07-12 devnull if(TWID32/ix->div < is->stop)
98 a0d146ed 2005-07-12 devnull hi = TWID32;
99 a0d146ed 2005-07-12 devnull else
100 a0d146ed 2005-07-12 devnull hi = is->stop * ix->div - 1;
101 a0d146ed 2005-07-12 devnull
102 7a400ee9 2007-09-25 rsc trace(TraceProc, "icachewritesect enter %ud %ud %llud",
103 7a400ee9 2007-09-25 rsc lo, hi, iwrite.as.aa);
104 a0d146ed 2005-07-12 devnull
105 a0d146ed 2005-07-12 devnull iedirty = icachedirty(lo, hi, iwrite.as.aa);
106 a0d146ed 2005-07-12 devnull iedirty = iesort(iedirty);
107 7a400ee9 2007-09-25 rsc bsize = 1 << is->blocklog;
108 a0d146ed 2005-07-12 devnull err = 0;
109 a0d146ed 2005-07-12 devnull
110 a0d146ed 2005-07-12 devnull while(iedirty){
111 28b49df3 2006-07-18 devnull disksched();
112 7a400ee9 2007-09-25 rsc while((t = icachesleeptime) == SleepForever){
113 28b49df3 2006-07-18 devnull sleep(1000);
114 28b49df3 2006-07-18 devnull disksched();
115 28b49df3 2006-07-18 devnull }
116 28b49df3 2006-07-18 devnull if(t < minicachesleeptime)
117 28b49df3 2006-07-18 devnull t = minicachesleeptime;
118 7a400ee9 2007-09-25 rsc if(t > 0)
119 7a400ee9 2007-09-25 rsc sleep(t);
120 a0d146ed 2005-07-12 devnull trace(TraceProc, "icachewritesect nextchunk");
121 a0d146ed 2005-07-12 devnull chunk = nextchunk(ix, is, &iedirty, &addr, &nbuf);
122 a0d146ed 2005-07-12 devnull
123 7a400ee9 2007-09-25 rsc trace(TraceProc, "icachewritesect readpart 0x%llux+0x%ux",
124 7a400ee9 2007-09-25 rsc addr, nbuf);
125 a0d146ed 2005-07-12 devnull if(readpart(is->part, addr, buf, nbuf) < 0){
126 7a400ee9 2007-09-25 rsc fprint(2, "%s: part %s addr 0x%llux: icachewritesect "
127 7a400ee9 2007-09-25 rsc "readpart: %r\n", argv0, is->part->name, addr);
128 a0d146ed 2005-07-12 devnull err = -1;
129 a0d146ed 2005-07-12 devnull continue;
130 a0d146ed 2005-07-12 devnull }
131 a0d146ed 2005-07-12 devnull trace(TraceProc, "icachewritesect updatebuf");
132 a0d146ed 2005-07-12 devnull addstat(StatIsectReadBytes, nbuf);
133 a0d146ed 2005-07-12 devnull addstat(StatIsectRead, 1);
134 a0d146ed 2005-07-12 devnull
135 a0d146ed 2005-07-12 devnull for(l=&chunk; (ie=*l)!=nil; l=&ie->nextdirty){
136 7a400ee9 2007-09-25 rsc again:
137 7a400ee9 2007-09-25 rsc naddr = ie2diskaddr(ix, is, ie);
138 a0d146ed 2005-07-12 devnull off = naddr - addr;
139 a0d146ed 2005-07-12 devnull if(off+bsize > nbuf){
140 7a400ee9 2007-09-25 rsc fprint(2, "%s: whoops! addr=0x%llux nbuf=%ud "
141 7a400ee9 2007-09-25 rsc "addr+nbuf=0x%llux naddr=0x%llux\n",
142 7a400ee9 2007-09-25 rsc argv0, addr, nbuf, addr+nbuf, naddr);
143 a0d146ed 2005-07-12 devnull assert(off+bsize <= nbuf);
144 a0d146ed 2005-07-12 devnull }
145 a0d146ed 2005-07-12 devnull unpackibucket(&ib, buf+off, is->bucketmagic);
146 a0d146ed 2005-07-12 devnull if(okibucket(&ib, is) < 0){
147 7a400ee9 2007-09-25 rsc fprint(2, "%s: bad bucket XXX\n", argv0);
148 a0d146ed 2005-07-12 devnull goto skipit;
149 a0d146ed 2005-07-12 devnull }
150 7a400ee9 2007-09-25 rsc trace(TraceProc, "icachewritesect add %V at 0x%llux",
151 7a400ee9 2007-09-25 rsc ie->score, naddr);
152 a0d146ed 2005-07-12 devnull h = bucklook(ie->score, ie->ia.type, ib.data, ib.n);
153 a0d146ed 2005-07-12 devnull if(h & 1){
154 a0d146ed 2005-07-12 devnull h ^= 1;
155 a0d146ed 2005-07-12 devnull packientry(ie, &ib.data[h]);
156 a0d146ed 2005-07-12 devnull }else if(ib.n < is->buckmax){
157 7a400ee9 2007-09-25 rsc memmove(&ib.data[h + IEntrySize], &ib.data[h],
158 7a400ee9 2007-09-25 rsc ib.n*IEntrySize - h);
159 a0d146ed 2005-07-12 devnull ib.n++;
160 a0d146ed 2005-07-12 devnull packientry(ie, &ib.data[h]);
161 a0d146ed 2005-07-12 devnull }else{
162 7a400ee9 2007-09-25 rsc fprint(2, "%s: bucket overflow XXX\n", argv0);
163 7a400ee9 2007-09-25 rsc skipit:
164 a0d146ed 2005-07-12 devnull err = -1;
165 a0d146ed 2005-07-12 devnull *l = ie->nextdirty;
166 a0d146ed 2005-07-12 devnull ie = *l;
167 a0d146ed 2005-07-12 devnull if(ie)
168 a0d146ed 2005-07-12 devnull goto again;
169 a0d146ed 2005-07-12 devnull else
170 a0d146ed 2005-07-12 devnull break;
171 a0d146ed 2005-07-12 devnull }
172 a0d146ed 2005-07-12 devnull packibucket(&ib, buf+off, is->bucketmagic);
173 a0d146ed 2005-07-12 devnull }
174 a0d146ed 2005-07-12 devnull
175 28b49df3 2006-07-18 devnull diskaccess(1);
176 28b49df3 2006-07-18 devnull
177 a0d146ed 2005-07-12 devnull trace(TraceProc, "icachewritesect writepart", addr, nbuf);
178 7a400ee9 2007-09-25 rsc werr = 0;
179 7a400ee9 2007-09-25 rsc if(writepart(is->part, addr, buf, nbuf) < 0 || flushpart(is->part) < 0)
180 7a400ee9 2007-09-25 rsc werr = -1;
181 7a400ee9 2007-09-25 rsc
182 7a400ee9 2007-09-25 rsc for(i=0; i<nbuf; i+=bsize){
183 7a400ee9 2007-09-25 rsc if((b = _getdblock(is->part, addr+i, ORDWR, 0)) != nil){
184 7a400ee9 2007-09-25 rsc memmove(b->data, buf+i, bsize);
185 7a400ee9 2007-09-25 rsc putdblock(b);
186 7a400ee9 2007-09-25 rsc }
187 7a400ee9 2007-09-25 rsc }
188 7a400ee9 2007-09-25 rsc
189 7a400ee9 2007-09-25 rsc if(werr < 0){
190 7a400ee9 2007-09-25 rsc fprint(2, "%s: part %s addr 0x%llux: icachewritesect "
191 7a400ee9 2007-09-25 rsc "writepart: %r\n", argv0, is->part->name, addr);
192 a0d146ed 2005-07-12 devnull err = -1;
193 a0d146ed 2005-07-12 devnull continue;
194 a0d146ed 2005-07-12 devnull }
195 fa325e9b 2020-01-10 cross
196 a0d146ed 2005-07-12 devnull addstat(StatIsectWriteBytes, nbuf);
197 a0d146ed 2005-07-12 devnull addstat(StatIsectWrite, 1);
198 a0d146ed 2005-07-12 devnull icacheclean(chunk);
199 a0d146ed 2005-07-12 devnull }
200 a0d146ed 2005-07-12 devnull
201 a0d146ed 2005-07-12 devnull trace(TraceProc, "icachewritesect done");
202 a0d146ed 2005-07-12 devnull return err;
203 a0d146ed 2005-07-12 devnull }
204 a0d146ed 2005-07-12 devnull
205 a0d146ed 2005-07-12 devnull static void
206 a0d146ed 2005-07-12 devnull icachewriteproc(void *v)
207 a0d146ed 2005-07-12 devnull {
208 28b49df3 2006-07-18 devnull int ret;
209 a0d146ed 2005-07-12 devnull uint bsize;
210 a0d146ed 2005-07-12 devnull ISect *is;
211 a0d146ed 2005-07-12 devnull Index *ix;
212 a0d146ed 2005-07-12 devnull u8int *buf;
213 a0d146ed 2005-07-12 devnull
214 a0d146ed 2005-07-12 devnull ix = mainindex;
215 a0d146ed 2005-07-12 devnull is = v;
216 a0d146ed 2005-07-12 devnull threadsetname("icachewriteproc:%s", is->part->name);
217 a0d146ed 2005-07-12 devnull
218 a0d146ed 2005-07-12 devnull bsize = 1<<is->blocklog;
219 a0d146ed 2005-07-12 devnull buf = emalloc(Bufsize+bsize);
220 54dd92be 2008-01-30 rsc buf = (u8int*)(((uintptr)buf+bsize-1)&~(uintptr)(bsize-1));
221 a0d146ed 2005-07-12 devnull
222 a0d146ed 2005-07-12 devnull for(;;){
223 a0d146ed 2005-07-12 devnull trace(TraceProc, "icachewriteproc recv");
224 a0d146ed 2005-07-12 devnull recv(is->writechan, 0);
225 a0d146ed 2005-07-12 devnull trace(TraceWork, "start");
226 28b49df3 2006-07-18 devnull ret = icachewritesect(ix, is, buf);
227 a0d146ed 2005-07-12 devnull trace(TraceProc, "icachewriteproc send");
228 a0d146ed 2005-07-12 devnull trace(TraceWork, "finish");
229 28b49df3 2006-07-18 devnull sendul(is->writedonechan, ret);
230 a0d146ed 2005-07-12 devnull }
231 a0d146ed 2005-07-12 devnull }
232 a0d146ed 2005-07-12 devnull
233 a0d146ed 2005-07-12 devnull static void
234 a0d146ed 2005-07-12 devnull icachewritecoord(void *v)
235 a0d146ed 2005-07-12 devnull {
236 28b49df3 2006-07-18 devnull int i, err;
237 a0d146ed 2005-07-12 devnull Index *ix;
238 a0d146ed 2005-07-12 devnull AState as;
239 a0d146ed 2005-07-12 devnull
240 a0d146ed 2005-07-12 devnull USED(v);
241 a0d146ed 2005-07-12 devnull
242 a0d146ed 2005-07-12 devnull threadsetname("icachewritecoord");
243 a0d146ed 2005-07-12 devnull
244 a0d146ed 2005-07-12 devnull ix = mainindex;
245 45ac814c 2007-10-29 rsc iwrite.as = icachestate();
246 a0d146ed 2005-07-12 devnull
247 a0d146ed 2005-07-12 devnull for(;;){
248 a0d146ed 2005-07-12 devnull trace(TraceProc, "icachewritecoord sleep");
249 a0d146ed 2005-07-12 devnull waitforkick(&iwrite.round);
250 a0d146ed 2005-07-12 devnull trace(TraceWork, "start");
251 45ac814c 2007-10-29 rsc as = icachestate();
252 a0d146ed 2005-07-12 devnull if(as.arena==iwrite.as.arena && as.aa==iwrite.as.aa){
253 a0d146ed 2005-07-12 devnull /* will not be able to do anything more than last flush - kick disk */
254 28b49df3 2006-07-18 devnull trace(TraceProc, "icachewritecoord kick dcache");
255 a0d146ed 2005-07-12 devnull kickdcache();
256 28b49df3 2006-07-18 devnull trace(TraceProc, "icachewritecoord kicked dcache");
257 45ac814c 2007-10-29 rsc goto SkipWork; /* won't do anything; don't bother rewriting bloom filter */
258 a0d146ed 2005-07-12 devnull }
259 a0d146ed 2005-07-12 devnull iwrite.as = as;
260 a0d146ed 2005-07-12 devnull
261 a0d146ed 2005-07-12 devnull trace(TraceProc, "icachewritecoord start flush");
262 a0d146ed 2005-07-12 devnull if(iwrite.as.arena){
263 a0d146ed 2005-07-12 devnull for(i=0; i<ix->nsects; i++)
264 a0d146ed 2005-07-12 devnull send(ix->sects[i]->writechan, 0);
265 a0d146ed 2005-07-12 devnull if(ix->bloom)
266 a0d146ed 2005-07-12 devnull send(ix->bloom->writechan, 0);
267 fa325e9b 2020-01-10 cross
268 28b49df3 2006-07-18 devnull err = 0;
269 a0d146ed 2005-07-12 devnull for(i=0; i<ix->nsects; i++)
270 28b49df3 2006-07-18 devnull err |= recvul(ix->sects[i]->writedonechan);
271 a0d146ed 2005-07-12 devnull if(ix->bloom)
272 28b49df3 2006-07-18 devnull err |= recvul(ix->bloom->writedonechan);
273 a0d146ed 2005-07-12 devnull
274 28b49df3 2006-07-18 devnull trace(TraceProc, "icachewritecoord donewrite err=%d", err);
275 45ac814c 2007-10-29 rsc if(err == 0){
276 28b49df3 2006-07-18 devnull setatailstate(&iwrite.as);
277 45ac814c 2007-10-29 rsc }
278 a0d146ed 2005-07-12 devnull }
279 45ac814c 2007-10-29 rsc SkipWork:
280 a0d146ed 2005-07-12 devnull icacheclean(nil); /* wake up anyone waiting */
281 a0d146ed 2005-07-12 devnull trace(TraceWork, "finish");
282 a0d146ed 2005-07-12 devnull addstat(StatIcacheFlush, 1);
283 a0d146ed 2005-07-12 devnull }
284 a0d146ed 2005-07-12 devnull }
285 a0d146ed 2005-07-12 devnull
286 a0d146ed 2005-07-12 devnull void
287 a0d146ed 2005-07-12 devnull flushicache(void)
288 a0d146ed 2005-07-12 devnull {
289 a0d146ed 2005-07-12 devnull trace(TraceProc, "flushicache enter");
290 a0d146ed 2005-07-12 devnull kickround(&iwrite.round, 1);
291 a0d146ed 2005-07-12 devnull trace(TraceProc, "flushicache exit");
292 a0d146ed 2005-07-12 devnull }
293 a0d146ed 2005-07-12 devnull
294 a0d146ed 2005-07-12 devnull void
295 a0d146ed 2005-07-12 devnull kickicache(void)
296 a0d146ed 2005-07-12 devnull {
297 a0d146ed 2005-07-12 devnull kickround(&iwrite.round, 0);
298 a0d146ed 2005-07-12 devnull }
299 a0d146ed 2005-07-12 devnull
300 a0d146ed 2005-07-12 devnull void
301 a0d146ed 2005-07-12 devnull delaykickicache(void)
302 a0d146ed 2005-07-12 devnull {
303 a0d146ed 2005-07-12 devnull delaykickround(&iwrite.round);
304 a0d146ed 2005-07-12 devnull }
305 a0d146ed 2005-07-12 devnull
306 a0d146ed 2005-07-12 devnull static IEntry*
307 a0d146ed 2005-07-12 devnull iesort(IEntry *ie)
308 a0d146ed 2005-07-12 devnull {
309 a0d146ed 2005-07-12 devnull int cmp;
310 a0d146ed 2005-07-12 devnull IEntry **l;
311 a0d146ed 2005-07-12 devnull IEntry *ie1, *ie2, *sorted;
312 a0d146ed 2005-07-12 devnull
313 a0d146ed 2005-07-12 devnull if(ie == nil || ie->nextdirty == nil)
314 a0d146ed 2005-07-12 devnull return ie;
315 a0d146ed 2005-07-12 devnull
316 a0d146ed 2005-07-12 devnull /* split the lists */
317 a0d146ed 2005-07-12 devnull ie1 = ie;
318 a0d146ed 2005-07-12 devnull ie2 = ie;
319 a0d146ed 2005-07-12 devnull if(ie2)
320 a0d146ed 2005-07-12 devnull ie2 = ie2->nextdirty;
321 a0d146ed 2005-07-12 devnull if(ie2)
322 a0d146ed 2005-07-12 devnull ie2 = ie2->nextdirty;
323 a0d146ed 2005-07-12 devnull while(ie1 && ie2){
324 a0d146ed 2005-07-12 devnull ie1 = ie1->nextdirty;
325 a0d146ed 2005-07-12 devnull ie2 = ie2->nextdirty;
326 a0d146ed 2005-07-12 devnull if(ie2)
327 a0d146ed 2005-07-12 devnull ie2 = ie2->nextdirty;
328 a0d146ed 2005-07-12 devnull }
329 a0d146ed 2005-07-12 devnull if(ie1){
330 a0d146ed 2005-07-12 devnull ie2 = ie1->nextdirty;
331 a0d146ed 2005-07-12 devnull ie1->nextdirty = nil;
332 a0d146ed 2005-07-12 devnull }
333 a0d146ed 2005-07-12 devnull
334 a0d146ed 2005-07-12 devnull /* sort the lists */
335 a0d146ed 2005-07-12 devnull ie1 = iesort(ie);
336 a0d146ed 2005-07-12 devnull ie2 = iesort(ie2);
337 a0d146ed 2005-07-12 devnull
338 a0d146ed 2005-07-12 devnull /* merge the lists */
339 a0d146ed 2005-07-12 devnull sorted = nil;
340 a0d146ed 2005-07-12 devnull l = &sorted;
341 a0d146ed 2005-07-12 devnull cmp = 0;
342 a0d146ed 2005-07-12 devnull while(ie1 || ie2){
343 a0d146ed 2005-07-12 devnull if(ie1 && ie2)
344 a0d146ed 2005-07-12 devnull cmp = scorecmp(ie1->score, ie2->score);
345 a0d146ed 2005-07-12 devnull if(ie1==nil || (ie2 && cmp > 0)){
346 a0d146ed 2005-07-12 devnull *l = ie2;
347 a0d146ed 2005-07-12 devnull l = &ie2->nextdirty;
348 a0d146ed 2005-07-12 devnull ie2 = ie2->nextdirty;
349 a0d146ed 2005-07-12 devnull }else{
350 a0d146ed 2005-07-12 devnull *l = ie1;
351 a0d146ed 2005-07-12 devnull l = &ie1->nextdirty;
352 a0d146ed 2005-07-12 devnull ie1 = ie1->nextdirty;
353 a0d146ed 2005-07-12 devnull }
354 a0d146ed 2005-07-12 devnull }
355 a0d146ed 2005-07-12 devnull *l = nil;
356 a0d146ed 2005-07-12 devnull return sorted;
357 a0d146ed 2005-07-12 devnull }