tarena.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tarena.c (20303B) | |
--- | |
1 #include "stdinc.h" | |
2 #include "dat.h" | |
3 #include "fns.h" | |
4 | |
5 typedef struct ASum ASum; | |
6 | |
7 struct ASum | |
8 { | |
9 Arena *arena; | |
10 ASum *next; | |
11 }; | |
12 | |
13 static void sealarena(Arena *arena); | |
14 static int okarena(Arena *arena); | |
15 static int loadarena(Arena *arena); | |
16 static CIBlock *getcib(Arena *arena, int clump, int writing, CIBl… | |
17 static void putcib(Arena *arena, CIBlock *cib); | |
18 static void sumproc(void *); | |
19 static void loadcig(Arena *arena); | |
20 | |
21 static QLock sumlock; | |
22 static Rendez sumwait; | |
23 static ASum *sumq; | |
24 static ASum *sumqtail; | |
25 static uchar zero[8192]; | |
26 | |
27 int arenasumsleeptime; | |
28 | |
29 int | |
30 initarenasum(void) | |
31 { | |
32 needzeroscore(); /* OS X */ | |
33 | |
34 qlock(&sumlock); | |
35 sumwait.l = &sumlock; | |
36 qunlock(&sumlock); | |
37 | |
38 if(vtproc(sumproc, nil) < 0){ | |
39 seterr(EOk, "can't start arena checksum slave: %r"); | |
40 return -1; | |
41 } | |
42 return 0; | |
43 } | |
44 | |
45 /* | |
46 * make an Arena, and initialize it based upon the disk header and trail… | |
47 */ | |
48 Arena* | |
49 initarena(Part *part, u64int base, u64int size, u32int blocksize) | |
50 { | |
51 Arena *arena; | |
52 | |
53 arena = MKZ(Arena); | |
54 arena->part = part; | |
55 arena->blocksize = blocksize; | |
56 arena->clumpmax = arena->blocksize / ClumpInfoSize; | |
57 arena->base = base + blocksize; | |
58 arena->size = size - 2 * blocksize; | |
59 | |
60 if(loadarena(arena) < 0){ | |
61 seterr(ECorrupt, "arena header or trailer corrupted"); | |
62 freearena(arena); | |
63 return nil; | |
64 } | |
65 if(okarena(arena) < 0){ | |
66 freearena(arena); | |
67 return nil; | |
68 } | |
69 | |
70 if(arena->diskstats.sealed && scorecmp(zeroscore, arena->score)=… | |
71 sealarena(arena); | |
72 | |
73 return arena; | |
74 } | |
75 | |
76 void | |
77 freearena(Arena *arena) | |
78 { | |
79 if(arena == nil) | |
80 return; | |
81 free(arena); | |
82 } | |
83 | |
84 Arena* | |
85 newarena(Part *part, u32int vers, char *name, u64int base, u64int size, … | |
86 { | |
87 int bsize; | |
88 Arena *arena; | |
89 | |
90 if(nameok(name) < 0){ | |
91 seterr(EOk, "illegal arena name", name); | |
92 return nil; | |
93 } | |
94 arena = MKZ(Arena); | |
95 arena->part = part; | |
96 arena->version = vers; | |
97 if(vers == ArenaVersion4) | |
98 arena->clumpmagic = _ClumpMagic; | |
99 else{ | |
100 do | |
101 arena->clumpmagic = fastrand(); | |
102 while(arena->clumpmagic==_ClumpMagic || arena->clumpmagi… | |
103 } | |
104 arena->blocksize = blocksize; | |
105 arena->clumpmax = arena->blocksize / ClumpInfoSize; | |
106 arena->base = base + blocksize; | |
107 arena->size = size - 2 * blocksize; | |
108 | |
109 namecp(arena->name, name); | |
110 | |
111 bsize = sizeof zero; | |
112 if(bsize > arena->blocksize) | |
113 bsize = arena->blocksize; | |
114 | |
115 if(wbarena(arena)<0 || wbarenahead(arena)<0 | |
116 || writepart(arena->part, arena->base, zero, bsize)<0){ | |
117 freearena(arena); | |
118 return nil; | |
119 } | |
120 | |
121 return arena; | |
122 } | |
123 | |
124 int | |
125 readclumpinfo(Arena *arena, int clump, ClumpInfo *ci) | |
126 { | |
127 CIBlock *cib, r; | |
128 | |
129 cib = getcib(arena, clump, 0, &r); | |
130 if(cib == nil) | |
131 return -1; | |
132 unpackclumpinfo(ci, &cib->data->data[cib->offset]); | |
133 putcib(arena, cib); | |
134 return 0; | |
135 } | |
136 | |
137 int | |
138 readclumpinfos(Arena *arena, int clump, ClumpInfo *cis, int n) | |
139 { | |
140 CIBlock *cib, r; | |
141 int i; | |
142 | |
143 /* | |
144 * because the clump blocks are laid out | |
145 * in reverse order at the end of the arena, | |
146 * it can be a few percent faster to read | |
147 * the clumps backwards, which reads the | |
148 * disk blocks forwards. | |
149 */ | |
150 for(i = n-1; i >= 0; i--){ | |
151 cib = getcib(arena, clump + i, 0, &r); | |
152 if(cib == nil){ | |
153 n = i; | |
154 continue; | |
155 } | |
156 unpackclumpinfo(&cis[i], &cib->data->data[cib->offset]); | |
157 putcib(arena, cib); | |
158 } | |
159 return n; | |
160 } | |
161 | |
162 /* | |
163 * write directory information for one clump | |
164 * must be called the arena locked | |
165 */ | |
166 int | |
167 writeclumpinfo(Arena *arena, int clump, ClumpInfo *ci) | |
168 { | |
169 CIBlock *cib, r; | |
170 | |
171 cib = getcib(arena, clump, 1, &r); | |
172 if(cib == nil) | |
173 return -1; | |
174 dirtydblock(cib->data, DirtyArenaCib); | |
175 packclumpinfo(ci, &cib->data->data[cib->offset]); | |
176 putcib(arena, cib); | |
177 return 0; | |
178 } | |
179 | |
180 u64int | |
181 arenadirsize(Arena *arena, u32int clumps) | |
182 { | |
183 return ((clumps / arena->clumpmax) + 1) * arena->blocksize; | |
184 } | |
185 | |
186 /* | |
187 * read a clump of data | |
188 * n is a hint of the size of the data, not including the header | |
189 * make sure it won't run off the end, then return the number of bytes a… | |
190 */ | |
191 u32int | |
192 readarena(Arena *arena, u64int aa, u8int *buf, long n) | |
193 { | |
194 DBlock *b; | |
195 u64int a; | |
196 u32int blocksize, off, m; | |
197 long nn; | |
198 | |
199 if(n == 0) | |
200 return -1; | |
201 | |
202 qlock(&arena->lock); | |
203 a = arena->size - arenadirsize(arena, arena->memstats.clumps); | |
204 qunlock(&arena->lock); | |
205 if(aa >= a){ | |
206 seterr(EOk, "reading beyond arena clump storage: clumps=… | |
207 return -1; | |
208 } | |
209 if(aa + n > a) | |
210 n = a - aa; | |
211 | |
212 blocksize = arena->blocksize; | |
213 a = arena->base + aa; | |
214 off = a & (blocksize - 1); | |
215 a -= off; | |
216 nn = 0; | |
217 for(;;){ | |
218 b = getdblock(arena->part, a, OREAD); | |
219 if(b == nil) | |
220 return -1; | |
221 m = blocksize - off; | |
222 if(m > n - nn) | |
223 m = n - nn; | |
224 memmove(&buf[nn], &b->data[off], m); | |
225 putdblock(b); | |
226 nn += m; | |
227 if(nn == n) | |
228 break; | |
229 off = 0; | |
230 a += blocksize; | |
231 } | |
232 return n; | |
233 } | |
234 | |
235 /* | |
236 * write some data to the clump section at a given offset | |
237 * used to fix up corrupted arenas. | |
238 */ | |
239 u32int | |
240 writearena(Arena *arena, u64int aa, u8int *clbuf, u32int n) | |
241 { | |
242 DBlock *b; | |
243 u64int a; | |
244 u32int blocksize, off, m; | |
245 long nn; | |
246 int ok; | |
247 | |
248 if(n == 0) | |
249 return -1; | |
250 | |
251 qlock(&arena->lock); | |
252 a = arena->size - arenadirsize(arena, arena->memstats.clumps); | |
253 if(aa >= a || aa + n > a){ | |
254 qunlock(&arena->lock); | |
255 seterr(EOk, "writing beyond arena clump storage"); | |
256 return -1; | |
257 } | |
258 | |
259 blocksize = arena->blocksize; | |
260 a = arena->base + aa; | |
261 off = a & (blocksize - 1); | |
262 a -= off; | |
263 nn = 0; | |
264 for(;;){ | |
265 b = getdblock(arena->part, a, off != 0 || off + n < bloc… | |
266 if(b == nil){ | |
267 qunlock(&arena->lock); | |
268 return -1; | |
269 } | |
270 dirtydblock(b, DirtyArena); | |
271 m = blocksize - off; | |
272 if(m > n - nn) | |
273 m = n - nn; | |
274 memmove(&b->data[off], &clbuf[nn], m); | |
275 ok = 0; | |
276 putdblock(b); | |
277 if(ok < 0){ | |
278 qunlock(&arena->lock); | |
279 return -1; | |
280 } | |
281 nn += m; | |
282 if(nn == n) | |
283 break; | |
284 off = 0; | |
285 a += blocksize; | |
286 } | |
287 qunlock(&arena->lock); | |
288 return n; | |
289 } | |
290 | |
291 /* | |
292 * allocate space for the clump and write it, | |
293 * updating the arena directory | |
294 ZZZ question: should this distinguish between an arena | |
295 filling up and real errors writing the clump? | |
296 */ | |
297 u64int | |
298 writeaclump(Arena *arena, Clump *c, u8int *clbuf) | |
299 { | |
300 DBlock *b; | |
301 u64int a, aa; | |
302 u32int clump, n, nn, m, off, blocksize; | |
303 int ok; | |
304 | |
305 n = c->info.size + ClumpSize + U32Size; | |
306 qlock(&arena->lock); | |
307 aa = arena->memstats.used; | |
308 if(arena->memstats.sealed | |
309 || aa + n + U32Size + arenadirsize(arena, arena->memstats.clumps… | |
310 if(!arena->memstats.sealed){ | |
311 logerr(EOk, "seal memstats %s", arena->name); | |
312 arena->memstats.sealed = 1; | |
313 wbarena(arena); | |
314 } | |
315 qunlock(&arena->lock); | |
316 return TWID64; | |
317 } | |
318 if(packclump(c, &clbuf[0], arena->clumpmagic) < 0){ | |
319 qunlock(&arena->lock); | |
320 return TWID64; | |
321 } | |
322 | |
323 /* | |
324 * write the data out one block at a time | |
325 */ | |
326 blocksize = arena->blocksize; | |
327 a = arena->base + aa; | |
328 off = a & (blocksize - 1); | |
329 a -= off; | |
330 nn = 0; | |
331 for(;;){ | |
332 b = getdblock(arena->part, a, off != 0 ? ORDWR : OWRITE); | |
333 if(b == nil){ | |
334 qunlock(&arena->lock); | |
335 return TWID64; | |
336 } | |
337 dirtydblock(b, DirtyArena); | |
338 m = blocksize - off; | |
339 if(m > n - nn) | |
340 m = n - nn; | |
341 memmove(&b->data[off], &clbuf[nn], m); | |
342 ok = 0; | |
343 putdblock(b); | |
344 if(ok < 0){ | |
345 qunlock(&arena->lock); | |
346 return TWID64; | |
347 } | |
348 nn += m; | |
349 if(nn == n) | |
350 break; | |
351 off = 0; | |
352 a += blocksize; | |
353 } | |
354 | |
355 arena->memstats.used += c->info.size + ClumpSize; | |
356 arena->memstats.uncsize += c->info.uncsize; | |
357 if(c->info.size < c->info.uncsize) | |
358 arena->memstats.cclumps++; | |
359 | |
360 clump = arena->memstats.clumps; | |
361 if(clump % ArenaCIGSize == 0){ | |
362 if(arena->cig == nil){ | |
363 loadcig(arena); | |
364 if(arena->cig == nil) | |
365 goto NoCIG; | |
366 } | |
367 /* add aa as start of next cig */ | |
368 if(clump/ArenaCIGSize != arena->ncig){ | |
369 fprint(2, "bad arena cig computation %s: writing… | |
370 arena->name, clump, arena->ncig); | |
371 arena->ncig = -1; | |
372 vtfree(arena->cig); | |
373 arena->cig = nil; | |
374 goto NoCIG; | |
375 } | |
376 arena->cig = vtrealloc(arena->cig, (arena->ncig+1)*sizeo… | |
377 arena->cig[arena->ncig++].offset = aa; | |
378 } | |
379 NoCIG: | |
380 arena->memstats.clumps++; | |
381 | |
382 if(arena->memstats.clumps == 0) | |
383 sysfatal("clumps wrapped"); | |
384 arena->wtime = now(); | |
385 if(arena->ctime == 0) | |
386 arena->ctime = arena->wtime; | |
387 | |
388 writeclumpinfo(arena, clump, &c->info); | |
389 wbarena(arena); | |
390 | |
391 qunlock(&arena->lock); | |
392 | |
393 return aa; | |
394 } | |
395 | |
396 int | |
397 atailcmp(ATailStats *a, ATailStats *b) | |
398 { | |
399 /* good test */ | |
400 if(a->used < b->used) | |
401 return -1; | |
402 if(a->used > b->used) | |
403 return 1; | |
404 | |
405 /* suspect tests - why order this way? (no one cares) */ | |
406 if(a->clumps < b->clumps) | |
407 return -1; | |
408 if(a->clumps > b->clumps) | |
409 return 1; | |
410 if(a->cclumps < b->cclumps) | |
411 return -1; | |
412 if(a->cclumps > b->cclumps) | |
413 return 1; | |
414 if(a->uncsize < b->uncsize) | |
415 return -1; | |
416 if(a->uncsize > b->uncsize) | |
417 return 1; | |
418 if(a->sealed < b->sealed) | |
419 return -1; | |
420 if(a->sealed > b->sealed) | |
421 return 1; | |
422 | |
423 /* everything matches */ | |
424 return 0; | |
425 } | |
426 | |
427 void | |
428 setatailstate(AState *as) | |
429 { | |
430 int i, j, osealed; | |
431 Arena *a; | |
432 Index *ix; | |
433 | |
434 trace(0, "setatailstate %s 0x%llux clumps %d", as->arena->name, … | |
435 | |
436 /* | |
437 * Look up as->arena to find index. | |
438 */ | |
439 needmainindex(); /* OS X linker */ | |
440 ix = mainindex; | |
441 for(i=0; i<ix->narenas; i++) | |
442 if(ix->arenas[i] == as->arena) | |
443 break; | |
444 if(i==ix->narenas || as->aa < ix->amap[i].start || as->aa >= ix-… | |
445 fprint(2, "funny settailstate 0x%llux\n", as->aa); | |
446 return; | |
447 } | |
448 | |
449 for(j=0; j<=i; j++){ | |
450 a = ix->arenas[j]; | |
451 if(atailcmp(&a->diskstats, &a->memstats) == 0) | |
452 continue; | |
453 qlock(&a->lock); | |
454 osealed = a->diskstats.sealed; | |
455 if(j == i) | |
456 a->diskstats = as->stats; | |
457 else | |
458 a->diskstats = a->memstats; | |
459 wbarena(a); | |
460 if(a->diskstats.sealed != osealed && !a->inqueue) | |
461 sealarena(a); | |
462 qunlock(&a->lock); | |
463 } | |
464 } | |
465 | |
466 /* | |
467 * once sealed, an arena never has any data added to it. | |
468 * it should only be changed to fix errors. | |
469 * this also syncs the clump directory. | |
470 */ | |
471 static void | |
472 sealarena(Arena *arena) | |
473 { | |
474 arena->inqueue = 1; | |
475 backsumarena(arena); | |
476 } | |
477 | |
478 void | |
479 backsumarena(Arena *arena) | |
480 { | |
481 ASum *as; | |
482 | |
483 as = MK(ASum); | |
484 if(as == nil) | |
485 return; | |
486 qlock(&sumlock); | |
487 as->arena = arena; | |
488 as->next = nil; | |
489 if(sumq) | |
490 sumqtail->next = as; | |
491 else | |
492 sumq = as; | |
493 sumqtail = as; | |
494 /* | |
495 * Might get here while initializing arenas, | |
496 * before initarenasum has been called. | |
497 */ | |
498 if(sumwait.l) | |
499 rwakeup(&sumwait); | |
500 qunlock(&sumlock); | |
501 } | |
502 | |
503 static void | |
504 sumproc(void *unused) | |
505 { | |
506 ASum *as; | |
507 Arena *arena; | |
508 | |
509 USED(unused); | |
510 | |
511 for(;;){ | |
512 qlock(&sumlock); | |
513 while(sumq == nil) | |
514 rsleep(&sumwait); | |
515 as = sumq; | |
516 sumq = as->next; | |
517 qunlock(&sumlock); | |
518 arena = as->arena; | |
519 free(as); | |
520 sumarena(arena); | |
521 } | |
522 } | |
523 | |
524 void | |
525 sumarena(Arena *arena) | |
526 { | |
527 ZBlock *b; | |
528 DigestState s; | |
529 u64int a, e; | |
530 u32int bs; | |
531 int t; | |
532 u8int score[VtScoreSize]; | |
533 | |
534 bs = MaxIoSize; | |
535 if(bs < arena->blocksize) | |
536 bs = arena->blocksize; | |
537 | |
538 /* | |
539 * read & sum all blocks except the last one | |
540 */ | |
541 flushdcache(); | |
542 memset(&s, 0, sizeof s); | |
543 b = alloczblock(bs, 0, arena->part->blocksize); | |
544 e = arena->base + arena->size; | |
545 for(a = arena->base - arena->blocksize; a + arena->blocksize <= … | |
546 disksched(); | |
547 while((t=arenasumsleeptime) == SleepForever){ | |
548 sleep(1000); | |
549 disksched(); | |
550 } | |
551 sleep(t); | |
552 if(a + bs > e) | |
553 bs = arena->blocksize; | |
554 if(readpart(arena->part, a, b->data, bs) < 0) | |
555 goto ReadErr; | |
556 addstat(StatSumRead, 1); | |
557 addstat(StatSumReadBytes, bs); | |
558 sha1(b->data, bs, nil, &s); | |
559 } | |
560 | |
561 /* | |
562 * the last one is special, since it may already have the checks… | |
563 */ | |
564 bs = arena->blocksize; | |
565 if(readpart(arena->part, e, b->data, bs) < 0){ | |
566 ReadErr: | |
567 logerr(EOk, "sumarena can't sum %s, read at %lld failed:… | |
568 freezblock(b); | |
569 return; | |
570 } | |
571 addstat(StatSumRead, 1); | |
572 addstat(StatSumReadBytes, bs); | |
573 | |
574 sha1(b->data, bs-VtScoreSize, nil, &s); | |
575 sha1(zeroscore, VtScoreSize, nil, &s); | |
576 sha1(nil, 0, score, &s); | |
577 | |
578 /* | |
579 * check for no checksum or the same | |
580 */ | |
581 if(scorecmp(score, &b->data[bs - VtScoreSize]) != 0 | |
582 && scorecmp(zeroscore, &b->data[bs - VtScoreSize]) != 0) | |
583 logerr(EOk, "overwriting mismatched checksums for arena=… | |
584 arena->name, &b->data[bs - VtScoreSize], score); | |
585 freezblock(b); | |
586 | |
587 qlock(&arena->lock); | |
588 scorecp(arena->score, score); | |
589 wbarena(arena); | |
590 qunlock(&arena->lock); | |
591 } | |
592 | |
593 /* | |
594 * write the arena trailer block to the partition | |
595 */ | |
596 int | |
597 wbarena(Arena *arena) | |
598 { | |
599 DBlock *b; | |
600 int bad; | |
601 | |
602 if((b = getdblock(arena->part, arena->base + arena->size, OWRITE… | |
603 logerr(EAdmin, "can't write arena trailer: %r"); | |
604 return -1; | |
605 } | |
606 dirtydblock(b, DirtyArenaTrailer); | |
607 bad = okarena(arena)<0 || packarena(arena, b->data)<0; | |
608 scorecp(b->data + arena->blocksize - VtScoreSize, arena->score); | |
609 putdblock(b); | |
610 if(bad) | |
611 return -1; | |
612 return 0; | |
613 } | |
614 | |
615 int | |
616 wbarenahead(Arena *arena) | |
617 { | |
618 ZBlock *b; | |
619 ArenaHead head; | |
620 int bad; | |
621 | |
622 namecp(head.name, arena->name); | |
623 head.version = arena->version; | |
624 head.size = arena->size + 2 * arena->blocksize; | |
625 head.blocksize = arena->blocksize; | |
626 head.clumpmagic = arena->clumpmagic; | |
627 b = alloczblock(arena->blocksize, 1, arena->part->blocksize); | |
628 if(b == nil){ | |
629 logerr(EAdmin, "can't write arena header: %r"); | |
630 /* ZZZ add error message? */ | |
631 return -1; | |
632 } | |
633 /* | |
634 * this writepart is okay because it only happens | |
635 * during initialization. | |
636 */ | |
637 bad = packarenahead(&head, b->data)<0 || | |
638 writepart(arena->part, arena->base - arena->blocksize, b->… | |
639 flushpart(arena->part)<0; | |
640 freezblock(b); | |
641 if(bad) | |
642 return -1; | |
643 return 0; | |
644 } | |
645 | |
646 /* | |
647 * read the arena header and trailer blocks from disk | |
648 */ | |
649 static int | |
650 loadarena(Arena *arena) | |
651 { | |
652 ArenaHead head; | |
653 ZBlock *b; | |
654 | |
655 b = alloczblock(arena->blocksize, 0, arena->part->blocksize); | |
656 if(b == nil) | |
657 return -1; | |
658 if(readpart(arena->part, arena->base + arena->size, b->data, are… | |
659 freezblock(b); | |
660 return -1; | |
661 } | |
662 if(unpackarena(arena, b->data) < 0){ | |
663 freezblock(b); | |
664 return -1; | |
665 } | |
666 if(arena->version != ArenaVersion4 && arena->version != ArenaVer… | |
667 seterr(EAdmin, "unknown arena version %d", arena->versio… | |
668 freezblock(b); | |
669 return -1; | |
670 } | |
671 scorecp(arena->score, &b->data[arena->blocksize - VtScoreSize]); | |
672 | |
673 if(readpart(arena->part, arena->base - arena->blocksize, b->data… | |
674 logerr(EAdmin, "can't read arena header: %r"); | |
675 freezblock(b); | |
676 return 0; | |
677 } | |
678 if(unpackarenahead(&head, b->data) < 0) | |
679 logerr(ECorrupt, "corrupted arena header: %r"); | |
680 else if(namecmp(arena->name, head.name)!=0 | |
681 || arena->clumpmagic != head.clumpmagic | |
682 || arena->version != head.version | |
683 || arena->blocksize != head.blocksize | |
684 || arena->size + 2 * arena->blocksize != head.size){ | |
685 if(namecmp(arena->name, head.name)!=0) | |
686 logerr(ECorrupt, "arena tail name %s head %s", | |
687 arena->name, head.name); | |
688 else if(arena->clumpmagic != head.clumpmagic) | |
689 logerr(ECorrupt, "arena tail clumpmagic 0x%lux h… | |
690 (ulong)arena->clumpmagic, (ulong)head.cl… | |
691 else if(arena->version != head.version) | |
692 logerr(ECorrupt, "arena tail version %d head ver… | |
693 arena->version, head.version); | |
694 else if(arena->blocksize != head.blocksize) | |
695 logerr(ECorrupt, "arena tail block size %d head … | |
696 arena->blocksize, head.blocksize); | |
697 else if(arena->size+2*arena->blocksize != head.size) | |
698 logerr(ECorrupt, "arena tail size %lud head %lud… | |
699 (ulong)arena->size+2*arena->blocksize, h… | |
700 else | |
701 logerr(ECorrupt, "arena header inconsistent with… | |
702 } | |
703 freezblock(b); | |
704 | |
705 return 0; | |
706 } | |
707 | |
708 static int | |
709 okarena(Arena *arena) | |
710 { | |
711 u64int dsize; | |
712 int ok; | |
713 | |
714 ok = 0; | |
715 dsize = arenadirsize(arena, arena->diskstats.clumps); | |
716 if(arena->diskstats.used + dsize > arena->size){ | |
717 seterr(ECorrupt, "arena %s used > size", arena->name); | |
718 ok = -1; | |
719 } | |
720 | |
721 if(arena->diskstats.cclumps > arena->diskstats.clumps) | |
722 logerr(ECorrupt, "arena %s has more compressed clumps th… | |
723 | |
724 /* | |
725 * This need not be true if some of the disk is corrupted. | |
726 * | |
727 if(arena->diskstats.uncsize + arena->diskstats.clumps * ClumpSiz… | |
728 logerr(ECorrupt, "arena %s uncompressed size inconsisten… | |
729 */ | |
730 | |
731 /* | |
732 * this happens; it's harmless. | |
733 * | |
734 if(arena->ctime > arena->wtime) | |
735 logerr(ECorrupt, "arena %s creation time after last writ… | |
736 */ | |
737 return ok; | |
738 } | |
739 | |
740 static CIBlock* | |
741 getcib(Arena *arena, int clump, int writing, CIBlock *rock) | |
742 { | |
743 int mode; | |
744 CIBlock *cib; | |
745 u32int block, off; | |
746 | |
747 if(clump >= arena->memstats.clumps){ | |
748 seterr(EOk, "clump directory access out of range"); | |
749 return nil; | |
750 } | |
751 block = clump / arena->clumpmax; | |
752 off = (clump - block * arena->clumpmax) * ClumpInfoSize; | |
753 cib = rock; | |
754 cib->block = block; | |
755 cib->offset = off; | |
756 | |
757 if(writing){ | |
758 if(off == 0 && clump == arena->memstats.clumps-1) | |
759 mode = OWRITE; | |
760 else | |
761 mode = ORDWR; | |
762 }else | |
763 mode = OREAD; | |
764 | |
765 cib->data = getdblock(arena->part, | |
766 arena->base + arena->size - (block + 1) * arena->blocksi… | |
767 if(cib->data == nil) | |
768 return nil; | |
769 return cib; | |
770 } | |
771 | |
772 static void | |
773 putcib(Arena *arena, CIBlock *cib) | |
774 { | |
775 USED(arena); | |
776 | |
777 putdblock(cib->data); | |
778 cib->data = nil; | |
779 } | |
780 | |
781 | |
782 /* | |
783 * For index entry readahead purposes, the arenas are | |
784 * broken into smaller subpieces, called clump info groups | |
785 * or cigs. Each cig has ArenaCIGSize clumps (ArenaCIGSize | |
786 * is chosen to make the index entries take up about half | |
787 * a megabyte). The index entries do not contain enough | |
788 * information to determine what the clump index is for | |
789 * a given address in an arena. That info is needed both for | |
790 * figuring out which clump group an address belongs to | |
791 * and for prefetching a clump group's index entries from | |
792 * the arena table of contents. The first time clump groups | |
793 * are accessed, we scan the entire arena table of contents | |
794 * (which might be 10s of megabytes), recording the data | |
795 * offset of each clump group. | |
796 */ | |
797 | |
798 /* | |
799 * load clump info group information by scanning entire toc. | |
800 */ | |
801 static void | |
802 loadcig(Arena *arena) | |
803 { | |
804 u32int i, j, ncig, nci; | |
805 ArenaCIG *cig; | |
806 ClumpInfo *ci; | |
807 u64int offset; | |
808 int ms; | |
809 | |
810 if(arena->cig || arena->ncig < 0) | |
811 return; | |
812 | |
813 // fprint(2, "loadcig %s\n", arena->name); | |
814 | |
815 ncig = (arena->memstats.clumps+ArenaCIGSize-1) / ArenaCIGSize; | |
816 if(ncig == 0){ | |
817 arena->cig = vtmalloc(1); | |
818 arena->ncig = 0; | |
819 return; | |
820 } | |
821 | |
822 ms = msec(); | |
823 cig = vtmalloc(ncig*sizeof cig[0]); | |
824 ci = vtmalloc(ArenaCIGSize*sizeof ci[0]); | |
825 offset = 0; | |
826 for(i=0; i<ncig; i++){ | |
827 nci = readclumpinfos(arena, i*ArenaCIGSize, ci, ArenaCIG… | |
828 cig[i].offset = offset; | |
829 for(j=0; j<nci; j++) | |
830 offset += ClumpSize + ci[j].size; | |
831 if(nci < ArenaCIGSize){ | |
832 if(i != ncig-1){ | |
833 vtfree(ci); | |
834 vtfree(cig); | |
835 arena->ncig = -1; | |
836 fprint(2, "loadcig %s: got %ud cigs, exp… | |
837 goto out; | |
838 } | |
839 } | |
840 } | |
841 vtfree(ci); | |
842 | |
843 arena->ncig = ncig; | |
844 arena->cig = cig; | |
845 | |
846 out: | |
847 ms = msec() - ms; | |
848 addstat2(StatCigLoad, 1, StatCigLoadTime, ms); | |
849 } | |
850 | |
851 /* | |
852 * convert arena address into arena group + data boundaries. | |
853 */ | |
854 int | |
855 arenatog(Arena *arena, u64int addr, u64int *gstart, u64int *glimit, int … | |
856 { | |
857 int r, l, m; | |
858 | |
859 qlock(&arena->lock); | |
860 if(arena->cig == nil) | |
861 loadcig(arena); | |
862 if(arena->cig == nil || arena->ncig == 0){ | |
863 qunlock(&arena->lock); | |
864 return -1; | |
865 } | |
866 | |
867 l = 1; | |
868 r = arena->ncig - 1; | |
869 while(l <= r){ | |
870 m = (r + l) / 2; | |
871 if(arena->cig[m].offset <= addr) | |
872 l = m + 1; | |
873 else | |
874 r = m - 1; | |
875 } | |
876 l--; | |
877 | |
878 *g = l; | |
879 *gstart = arena->cig[l].offset; | |
880 if(l+1 < arena->ncig) | |
881 *glimit = arena->cig[l+1].offset; | |
882 else | |
883 *glimit = arena->memstats.used; | |
884 qunlock(&arena->lock); | |
885 return 0; | |
886 } | |
887 | |
888 /* | |
889 * load the clump info for group g into the index entries. | |
890 */ | |
891 int | |
892 asumload(Arena *arena, int g, IEntry *entries, int nentries) | |
893 { | |
894 int i, base, limit; | |
895 u64int addr; | |
896 ClumpInfo ci; | |
897 IEntry *ie; | |
898 | |
899 if(nentries < ArenaCIGSize){ | |
900 fprint(2, "asking for too few entries\n"); | |
901 return -1; | |
902 } | |
903 | |
904 qlock(&arena->lock); | |
905 if(arena->cig == nil) | |
906 loadcig(arena); | |
907 if(arena->cig == nil || arena->ncig == 0 || g >= arena->ncig){ | |
908 qunlock(&arena->lock); | |
909 return -1; | |
910 } | |
911 | |
912 addr = 0; | |
913 base = g*ArenaCIGSize; | |
914 limit = base + ArenaCIGSize; | |
915 if(base > arena->memstats.clumps) | |
916 base = arena->memstats.clumps; | |
917 ie = entries; | |
918 for(i=base; i<limit; i++){ | |
919 if(readclumpinfo(arena, i, &ci) < 0) | |
920 break; | |
921 if(ci.type != VtCorruptType){ | |
922 scorecp(ie->score, ci.score); | |
923 ie->ia.type = ci.type; | |
924 ie->ia.size = ci.uncsize; | |
925 ie->ia.blocks = (ci.size + ClumpSize + (1<<ABloc… | |
926 ie->ia.addr = addr; | |
927 ie++; | |
928 } | |
929 addr += ClumpSize + ci.size; | |
930 } | |
931 qunlock(&arena->lock); | |
932 return ie - entries; | |
933 } |