]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/venti/srv/index.c
venti: warn when opening /dev/swap fails
[plan9front.git] / sys / src / cmd / venti / srv / index.c
1 /*
2  * Index, mapping scores to log positions. 
3  *
4  * The index is made up of some number of index sections, each of
5  * which is typically stored on a different disk.  The blocks in all the 
6  * index sections are logically numbered, with each index section 
7  * responsible for a range of blocks.  Blocks are typically 8kB.
8  *
9  * The N index blocks are treated as a giant hash table.  The top 32 bits
10  * of score are used as the key for a lookup.  Each index block holds
11  * one hash bucket, which is responsible for ceil(2^32 / N) of the key space.
12  * 
13  * The index is sized so that a particular bucket is extraordinarily 
14  * unlikely to overflow: assuming compressed data blocks are 4kB 
15  * on disk, and assuming each block has a 40 byte index entry,
16  * the index data will be 1% of the total data.  Since scores are essentially
17  * random, all buckets should be about the same fullness.
18  * A factor of 5 gives us a wide comfort boundary to account for 
19  * random variation.  So the index disk space should be 5% of the arena disk space.
20  */
21
22 #include "stdinc.h"
23 #include "dat.h"
24 #include "fns.h"
25
26 static int      initindex1(Index*);
27 static ISect    *initisect1(ISect *is);
28
29 #define KEY(k,d)        ((d) ? (k)>>(32-(d)) : 0)
30
31 static char IndexMagic[] = "venti index configuration";
32
33 Index*
34 initindex(char *name, ISect **sects, int n)
35 {
36         IFile f;
37         Index *ix;
38         ISect *is;
39         u32int last, blocksize, tabsize;
40         int i;
41
42         if(n <= 0){
43 fprint(2, "bad n\n");
44                 seterr(EOk, "no index sections to initialize index");
45                 return nil;
46         }
47         ix = MKZ(Index);
48         if(ix == nil){
49 fprint(2, "no mem\n");
50                 seterr(EOk, "can't initialize index: out of memory");
51                 freeindex(ix);
52                 return nil;
53         }
54
55         tabsize = sects[0]->tabsize;
56         if(partifile(&f, sects[0]->part, sects[0]->tabbase, tabsize) < 0)
57                 return nil;
58         if(parseindex(&f, ix) < 0){
59                 freeifile(&f);
60                 freeindex(ix);
61                 return nil;
62         }
63         freeifile(&f);
64         if(namecmp(ix->name, name) != 0){
65                 seterr(ECorrupt, "mismatched index name: found %s expected %s", ix->name, name);
66                 return nil;
67         }
68         if(ix->nsects != n){
69                 seterr(ECorrupt, "mismatched number index sections: found %d expected %d", n, ix->nsects);
70                 freeindex(ix);
71                 return nil;
72         }
73         ix->sects = sects;
74         last = 0;
75         blocksize = ix->blocksize;
76         for(i = 0; i < ix->nsects; i++){
77                 is = sects[i];
78                 if(namecmp(ix->name, is->index) != 0
79                 || is->blocksize != blocksize
80                 || is->tabsize != tabsize
81                 || namecmp(is->name, ix->smap[i].name) != 0
82                 || is->start != ix->smap[i].start
83                 || is->stop != ix->smap[i].stop
84                 || last != is->start
85                 || is->start > is->stop){
86                         seterr(ECorrupt, "inconsistent index sections in %s", ix->name);
87                         freeindex(ix);
88                         return nil;
89                 }
90                 last = is->stop;
91         }
92         ix->tabsize = tabsize;
93         ix->buckets = last;
94
95         if(initindex1(ix) < 0){
96                 freeindex(ix);
97                 return nil;
98         }
99
100         ix->arenas = MKNZ(Arena*, ix->narenas);
101         if(maparenas(ix->amap, ix->arenas, ix->narenas, ix->name) < 0){
102                 freeindex(ix);
103                 return nil;
104         }
105
106         return ix;
107 }
108
109 static int
110 initindex1(Index *ix)
111 {
112         u32int buckets;
113
114         ix->div = (((u64int)1 << 32) + ix->buckets - 1) / ix->buckets;
115         buckets = (((u64int)1 << 32) - 1) / ix->div + 1;
116         if(buckets != ix->buckets){
117                 seterr(ECorrupt, "inconsistent math for divisor and buckets in %s", ix->name);
118                 return -1;
119         }
120
121         return 0;
122 }
123
124 int
125 wbindex(Index *ix)
126 {
127         Fmt f;
128         ZBlock *b;
129         int i;
130
131         if(ix->nsects == 0){
132                 seterr(EOk, "no sections in index %s", ix->name);
133                 return -1;
134         }
135         b = alloczblock(ix->tabsize, 1, ix->blocksize);
136         if(b == nil){
137                 seterr(EOk, "can't write index configuration: out of memory");
138                 return -1;
139         }
140         fmtzbinit(&f, b);
141         if(outputindex(&f, ix) < 0){
142                 seterr(EOk, "can't make index configuration: table storage too small %d", ix->tabsize);
143                 freezblock(b);
144                 return -1;
145         }
146         for(i = 0; i < ix->nsects; i++){
147                 if(writepart(ix->sects[i]->part, ix->sects[i]->tabbase, b->data, ix->tabsize) < 0
148                 || flushpart(ix->sects[i]->part) < 0){
149                         seterr(EOk, "can't write index: %r");
150                         freezblock(b);
151                         return -1;
152                 }
153         }
154         freezblock(b);
155
156         for(i = 0; i < ix->nsects; i++)
157                 if(wbisect(ix->sects[i]) < 0)
158                         return -1;
159
160         return 0;
161 }
162
163 /*
164  * index: IndexMagic '\n' version '\n' name '\n' blocksize '\n' [V2: bitblocks '\n'] sections arenas
165  * version, blocksize: u32int
166  * name: max. ANameSize string
167  * sections, arenas: AMap
168  */
169 int
170 outputindex(Fmt *f, Index *ix)
171 {
172         if(fmtprint(f, "%s\n%ud\n%s\n%ud\n", IndexMagic, ix->version, ix->name, ix->blocksize) < 0
173         || outputamap(f, ix->smap, ix->nsects) < 0
174         || outputamap(f, ix->amap, ix->narenas) < 0)
175                 return -1;
176         return 0;
177 }
178
179 int
180 parseindex(IFile *f, Index *ix)
181 {
182         AMapN amn;
183         u32int v;
184         char *s;
185
186         /*
187          * magic
188          */
189         s = ifileline(f);
190         if(s == nil || strcmp(s, IndexMagic) != 0){
191                 seterr(ECorrupt, "bad index magic for %s", f->name);
192                 return -1;
193         }
194
195         /*
196          * version
197          */
198         if(ifileu32int(f, &v) < 0){
199                 seterr(ECorrupt, "syntax error: bad version number in %s", f->name);
200                 return -1;
201         }
202         ix->version = v;
203         if(ix->version != IndexVersion){
204                 seterr(ECorrupt, "bad version number in %s", f->name);
205                 return -1;
206         }
207
208         /*
209          * name
210          */
211         if(ifilename(f, ix->name) < 0){
212                 seterr(ECorrupt, "syntax error: bad index name in %s", f->name);
213                 return -1;
214         }
215
216         /*
217          * block size
218          */
219         if(ifileu32int(f, &v) < 0){
220                 seterr(ECorrupt, "syntax error: bad block size number in %s", f->name);
221                 return -1;
222         }
223         ix->blocksize = v;
224
225         if(parseamap(f, &amn) < 0)
226                 return -1;
227         ix->nsects = amn.n;
228         ix->smap = amn.map;
229
230         if(parseamap(f, &amn) < 0)
231                 return -1;
232         ix->narenas = amn.n;
233         ix->amap = amn.map;
234
235         return 0;
236 }
237
238 /*
239  * initialize an entirely new index
240  */
241 Index *
242 newindex(char *name, ISect **sects, int n)
243 {
244         Index *ix;
245         AMap *smap;
246         u64int nb;
247         u32int div, ub, xb, start, stop, blocksize, tabsize;
248         int i, j;
249
250         if(n < 1){
251                 seterr(EOk, "creating index with no index sections");
252                 return nil;
253         }
254
255         /*
256          * compute the total buckets available in the index,
257          * and the total buckets which are used.
258          */
259         nb = 0;
260         blocksize = sects[0]->blocksize;
261         tabsize = sects[0]->tabsize;
262         for(i = 0; i < n; i++){
263                 /*
264                  * allow index, start, and stop to be set if index is correct
265                  * and start and stop are what we would have picked.
266                  * this allows calling fmtindex to reformat the index after
267                  * replacing a bad index section with a freshly formatted one.
268                  * start and stop are checked below.
269                  */
270                 if(sects[i]->index[0] != '\0' && strcmp(sects[i]->index, name) != 0){
271                         seterr(EOk, "creating new index using non-empty section %s", sects[i]->name);
272                         return nil;
273                 }
274                 if(blocksize != sects[i]->blocksize){
275                         seterr(EOk, "mismatched block sizes in index sections");
276                         return nil;
277                 }
278                 if(tabsize != sects[i]->tabsize){
279                         seterr(EOk, "mismatched config table sizes in index sections");
280                         return nil;
281                 }
282                 nb += sects[i]->blocks;
283         }
284
285         /*
286          * check for duplicate names
287          */
288         for(i = 0; i < n; i++){
289                 for(j = i + 1; j < n; j++){
290                         if(namecmp(sects[i]->name, sects[j]->name) == 0){
291                                 seterr(EOk, "duplicate section name %s for index %s", sects[i]->name, name);
292                                 return nil;
293                         }
294                 }
295         }
296
297         if(nb >= ((u64int)1 << 32)){
298                 fprint(2, "%s: index is 2^32 blocks or more; ignoring some of it\n",
299                         argv0);
300                 nb = ((u64int)1 << 32) - 1;
301         }
302
303         div = (((u64int)1 << 32) + nb - 1) / nb;
304         if(div < 100){
305                 fprint(2, "%s: index divisor %d too coarse; "
306                         "index larger than needed, ignoring some of it\n",
307                         argv0, div);
308                 div = 100;
309                 nb = (((u64int)1 << 32) - 1) / (100 - 1);
310         }
311         ub = (((u64int)1 << 32) - 1) / div + 1;
312         if(ub > nb){
313                 seterr(EBug, "index initialization math wrong");
314                 return nil;
315         }
316         xb = nb - ub;
317
318         /*
319          * initialize each of the index sections
320          * and the section map table
321          */
322         smap = MKNZ(AMap, n);
323         if(smap == nil){
324                 seterr(EOk, "can't create new index: out of memory");
325                 return nil;
326         }
327         start = 0;
328         for(i = 0; i < n; i++){
329                 stop = start + sects[i]->blocks - xb / n;
330                 if(i == n - 1)
331                         stop = ub;
332
333                 if(sects[i]->start != 0 || sects[i]->stop != 0)
334                 if(sects[i]->start != start || sects[i]->stop != stop){
335                         seterr(EOk, "creating new index using non-empty section %s", sects[i]->name);
336                         return nil;
337                 }
338
339                 sects[i]->start = start;
340                 sects[i]->stop = stop;
341                 namecp(sects[i]->index, name);
342
343                 smap[i].start = start;
344                 smap[i].stop = stop;
345                 namecp(smap[i].name, sects[i]->name);
346                 start = stop;
347         }
348
349         /*
350          * initialize the index itself
351          */
352         ix = MKZ(Index);
353         if(ix == nil){
354                 seterr(EOk, "can't create new index: out of memory");
355                 free(smap);
356                 return nil;
357         }
358         ix->version = IndexVersion;
359         namecp(ix->name, name);
360         ix->sects = sects;
361         ix->smap = smap;
362         ix->nsects = n;
363         ix->blocksize = blocksize;
364         ix->buckets = ub;
365         ix->tabsize = tabsize;
366         ix->div = div;
367
368         if(initindex1(ix) < 0){
369                 free(smap);
370                 return nil;
371         }
372
373         return ix;
374 }
375
376 ISect*
377 initisect(Part *part)
378 {
379         ISect *is;
380         ZBlock *b;
381         int ok;
382
383         b = alloczblock(HeadSize, 0, 0);
384         if(b == nil || readpart(part, PartBlank, b->data, HeadSize) < 0){
385                 seterr(EAdmin, "can't read index section header: %r");
386                 return nil;
387         }
388
389         is = MKZ(ISect);
390         if(is == nil){
391                 freezblock(b);
392                 return nil;
393         }
394         is->part = part;
395         ok = unpackisect(is, b->data);
396         freezblock(b);
397         if(ok < 0){
398                 seterr(ECorrupt, "corrupted index section header: %r");
399                 freeisect(is);
400                 return nil;
401         }
402
403         if(is->version != ISectVersion1 && is->version != ISectVersion2){
404                 seterr(EAdmin, "unknown index section version %d", is->version);
405                 freeisect(is);
406                 return nil;
407         }
408
409         return initisect1(is);
410 }
411
412 ISect*
413 newisect(Part *part, u32int vers, char *name, u32int blocksize, u32int tabsize)
414 {
415         ISect *is;
416         u32int tabbase;
417
418         is = MKZ(ISect);
419         if(is == nil)
420                 return nil;
421
422         namecp(is->name, name);
423         is->version = vers;
424         is->part = part;
425         is->blocksize = blocksize;
426         is->start = 0;
427         is->stop = 0;
428         tabbase = (PartBlank + HeadSize + blocksize - 1) & ~(blocksize - 1);
429         is->blockbase = (tabbase + tabsize + blocksize - 1) & ~(blocksize - 1);
430         is->blocks = is->part->size / blocksize - is->blockbase / blocksize;
431         is->bucketmagic = 0;
432         if(is->version == ISectVersion2){
433                 do{
434                         is->bucketmagic = fastrand();
435                 }while(is->bucketmagic==0);
436         }
437         is = initisect1(is);
438         if(is == nil)
439                 return nil;
440
441         return is;
442 }
443
444 /*
445  * initialize the computed parameters for an index
446  */
447 static ISect*
448 initisect1(ISect *is)
449 {
450         u64int v;
451
452         is->buckmax = (is->blocksize - IBucketSize) / IEntrySize;
453         is->blocklog = u64log2(is->blocksize);
454         if(is->blocksize != (1 << is->blocklog)){
455                 seterr(ECorrupt, "illegal non-power-of-2 bucket size %d\n", is->blocksize);
456                 freeisect(is);
457                 return nil;
458         }
459         partblocksize(is->part, is->blocksize);
460         is->tabbase = (PartBlank + HeadSize + is->blocksize - 1) & ~(is->blocksize - 1);
461         if(is->tabbase >= is->blockbase){
462                 seterr(ECorrupt, "index section config table overlaps bucket storage");
463                 freeisect(is);
464                 return nil;
465         }
466         is->tabsize = is->blockbase - is->tabbase;
467         v = is->part->size & ~(u64int)(is->blocksize - 1);
468         if(is->blockbase + (u64int)is->blocks * is->blocksize != v){
469                 seterr(ECorrupt, "invalid blocks in index section %s", is->name);
470                 /* ZZZ what to do? 
471                 freeisect(is);
472                 return nil;
473                 */
474         }
475
476         if(is->stop - is->start > is->blocks){
477                 seterr(ECorrupt, "index section overflows available space");
478                 freeisect(is);
479                 return nil;
480         }
481         if(is->start > is->stop){
482                 seterr(ECorrupt, "invalid index section range");
483                 freeisect(is);
484                 return nil;
485         }
486
487         return is;
488 }
489
490 int
491 wbisect(ISect *is)
492 {
493         ZBlock *b;
494
495         b = alloczblock(HeadSize, 1, 0);
496         if(b == nil){
497                 /* ZZZ set error? */
498                 return -1;
499         }
500
501         if(packisect(is, b->data) < 0){
502                 seterr(ECorrupt, "can't make index section header: %r");
503                 freezblock(b);
504                 return -1;
505         }
506         if(writepart(is->part, PartBlank, b->data, HeadSize) < 0 || flushpart(is->part) < 0){
507                 seterr(EAdmin, "can't write index section header: %r");
508                 freezblock(b);
509                 return -1;
510         }
511         freezblock(b);
512
513         return 0;
514 }
515
516 void
517 freeisect(ISect *is)
518 {
519         if(is == nil)
520                 return;
521         free(is);
522 }
523
524 void
525 freeindex(Index *ix)
526 {
527         int i;
528
529         if(ix == nil)
530                 return;
531         free(ix->amap);
532         free(ix->arenas);
533         if(ix->sects)
534                 for(i = 0; i < ix->nsects; i++)
535                         freeisect(ix->sects[i]);
536         free(ix->sects);
537         free(ix->smap);
538         free(ix);
539 }
540
541 /*
542  * write a clump to an available arena in the index
543  * and return the address of the clump within the index.
544 ZZZ question: should this distinguish between an arena
545 filling up and real errors writing the clump?
546  */
547 u64int
548 writeiclump(Index *ix, Clump *c, u8int *clbuf)
549 {
550         u64int a;
551         int i;
552         IAddr ia;
553         AState as;
554
555         trace(TraceLump, "writeiclump enter");
556         qlock(&ix->writing);
557         for(i = ix->mapalloc; i < ix->narenas; i++){
558                 a = writeaclump(ix->arenas[i], c, clbuf);
559                 if(a != TWID64){
560                         ix->mapalloc = i;
561                         ia.addr = ix->amap[i].start + a;
562                         ia.type = c->info.type;
563                         ia.size = c->info.uncsize;
564                         ia.blocks = (c->info.size + ClumpSize + (1<<ABlockLog) - 1) >> ABlockLog;
565                         as.arena = ix->arenas[i];
566                         as.aa = ia.addr;
567                         as.stats = as.arena->memstats;
568                         insertscore(c->info.score, &ia, IEDirty, &as);
569                         qunlock(&ix->writing);
570                         trace(TraceLump, "writeiclump exit");
571                         return ia.addr;
572                 }
573         }
574         qunlock(&ix->writing);
575
576         seterr(EAdmin, "no space left in arenas");
577         trace(TraceLump, "writeiclump failed");
578         return TWID64;
579 }
580
581 /*
582  * convert an arena index to an relative arena address
583  */
584 Arena*
585 amapitoa(Index *ix, u64int a, u64int *aa)
586 {
587         int i, r, l, m;
588
589         l = 1;
590         r = ix->narenas - 1;
591         while(l <= r){
592                 m = (r + l) / 2;
593                 if(ix->amap[m].start <= a)
594                         l = m + 1;
595                 else
596                         r = m - 1;
597         }
598         l--;
599
600         if(a > ix->amap[l].stop){
601 for(i=0; i<ix->narenas; i++)
602         print("arena %d: %llux - %llux\n", i, ix->amap[i].start, ix->amap[i].stop);
603 print("want arena %d for %llux\n", l, a);
604                 seterr(ECrash, "unmapped address passed to amapitoa");
605                 return nil;
606         }
607
608         if(ix->arenas[l] == nil){
609                 seterr(ECrash, "unmapped arena selected in amapitoa");
610                 return nil;
611         }
612         *aa = a - ix->amap[l].start;
613         return ix->arenas[l];
614 }
615
616 /*
617  * convert an arena index to the bounds of the containing arena group.
618  */
619 Arena*
620 amapitoag(Index *ix, u64int a, u64int *gstart, u64int *glimit, int *g)
621 {
622         u64int aa;
623         Arena *arena;
624         
625         arena = amapitoa(ix, a, &aa);
626         if(arena == nil)
627                 return nil;
628         if(arenatog(arena, aa, gstart, glimit, g) < 0)
629                 return nil;
630         *gstart += a - aa;
631         *glimit += a - aa;
632         return arena;
633 }
634
635 int
636 iaddrcmp(IAddr *ia1, IAddr *ia2)
637 {
638         return ia1->type != ia2->type
639                 || ia1->size != ia2->size
640                 || ia1->blocks != ia2->blocks
641                 || ia1->addr != ia2->addr;
642 }
643
644 /*
645  * lookup the score in the partition
646  *
647  * nothing needs to be explicitly locked:
648  * only static parts of ix are used, and
649  * the bucket is locked by the DBlock lock.
650  */
651 int
652 loadientry(Index *ix, u8int *score, int type, IEntry *ie)
653 {
654         ISect *is;
655         DBlock *b;
656         IBucket ib;
657         u32int buck;
658         int h, ok;
659
660         ok = -1;
661
662         trace(TraceLump, "loadientry enter");
663
664         /*
665         qlock(&stats.lock);
666         stats.indexreads++;
667         qunlock(&stats.lock);
668         */
669
670         if(!inbloomfilter(mainindex->bloom, score)){
671                 trace(TraceLump, "loadientry bloomhit");
672                 return -1;
673         }
674
675         trace(TraceLump, "loadientry loadibucket");
676         b = loadibucket(ix, score, &is, &buck, &ib);
677         trace(TraceLump, "loadientry loadedibucket");
678         if(b == nil)
679                 return -1;
680
681         if(okibucket(&ib, is) < 0){
682                 trace(TraceLump, "loadientry badbucket");
683                 goto out;
684         }
685
686         h = bucklook(score, type, ib.data, ib.n);
687         if(h & 1){
688                 h ^= 1;
689                 trace(TraceLump, "loadientry found");
690                 unpackientry(ie, &ib.data[h]);
691                 ok = 0;
692                 goto out;
693         }
694         trace(TraceLump, "loadientry notfound");
695         addstat(StatBloomFalseMiss, 1);
696 out:
697         putdblock(b);
698         trace(TraceLump, "loadientry exit");
699         return ok;
700 }
701
702 int
703 okibucket(IBucket *ib, ISect *is)
704 {
705         if(ib->n <= is->buckmax)
706                 return 0;
707
708         seterr(EICorrupt, "corrupted disk index bucket: n=%ud max=%ud, range=[%lud,%lud)",
709                 ib->n, is->buckmax, is->start, is->stop);
710         return -1;
711 }
712
713 /*
714  * look for score within data;
715  * return 1 | byte index of matching index,
716  * or 0 | index of least element > score
717  */
718 int
719 bucklook(u8int *score, int otype, u8int *data, int n)
720 {
721         int i, r, l, m, h, c, cc, type;
722
723         if(otype == -1)
724                 type = -1;
725         else
726                 type = vttodisktype(otype);
727         l = 0;
728         r = n - 1;
729         while(l <= r){
730                 m = (r + l) >> 1;
731                 h = m * IEntrySize;
732                 for(i = 0; i < VtScoreSize; i++){
733                         c = score[i];
734                         cc = data[h + i];
735                         if(c != cc){
736                                 if(c > cc)
737                                         l = m + 1;
738                                 else
739                                         r = m - 1;
740                                 goto cont;
741                         }
742                 }
743                 cc = data[h + IEntryTypeOff];
744                 if(type != cc && type != -1){
745                         if(type > cc)
746                                 l = m + 1;
747                         else
748                                 r = m - 1;
749                         goto cont;
750                 }
751                 return h | 1;
752         cont:;
753         }
754
755         return l * IEntrySize;
756 }
757
758 /*
759  * compare two IEntries; consistent with bucklook
760  */
761 int
762 ientrycmp(const void *vie1, const void *vie2)
763 {
764         u8int *ie1, *ie2;
765         int i, v1, v2;
766
767         ie1 = (u8int*)vie1;
768         ie2 = (u8int*)vie2;
769         for(i = 0; i < VtScoreSize; i++){
770                 v1 = ie1[i];
771                 v2 = ie2[i];
772                 if(v1 != v2){
773                         if(v1 < v2)
774                                 return -1;
775                         return 1;
776                 }
777         }
778         v1 = ie1[IEntryTypeOff];
779         v2 = ie2[IEntryTypeOff];
780         if(v1 != v2){
781                 if(v1 < v2)
782                         return -1;
783                 return 1;
784         }
785         return 0;
786 }
787
788 /*
789  * find the number of the index section holding bucket #buck
790  */
791 int
792 indexsect0(Index *ix, u32int buck)
793 {
794         int r, l, m;
795
796         l = 1;
797         r = ix->nsects - 1;
798         while(l <= r){
799                 m = (r + l) >> 1;
800                 if(ix->sects[m]->start <= buck)
801                         l = m + 1;
802                 else
803                         r = m - 1;
804         }
805         return l - 1;
806 }
807
808 /*
809  * load the index block at bucket #buck
810  */
811 static DBlock*
812 loadibucket0(Index *ix, u32int buck, ISect **pis, u32int *pbuck, IBucket *ib, int mode)
813 {
814         ISect *is;
815         DBlock *b;
816
817         is = ix->sects[indexsect0(ix, buck)];
818         if(buck < is->start || is->stop <= buck){
819                 seterr(EAdmin, "index lookup out of range: %ud not found in index\n", buck);
820                 return nil;
821         }
822
823         buck -= is->start;
824         if((b = getdblock(is->part, is->blockbase + ((u64int)buck << is->blocklog), mode)) == nil)
825                 return nil;
826
827         if(pis)
828                 *pis = is;
829         if(pbuck)
830                 *pbuck = buck;
831         if(ib)
832                 unpackibucket(ib, b->data, is->bucketmagic);
833         return b;
834 }
835
836 /*
837  * find the number of the index section holding score
838  */
839 int
840 indexsect1(Index *ix, u8int *score)
841 {
842         return indexsect0(ix, hashbits(score, 32) / ix->div);
843 }
844
845 /*
846  * load the index block responsible for score.
847  */
848 static DBlock*
849 loadibucket1(Index *ix, u8int *score, ISect **pis, u32int *pbuck, IBucket *ib)
850 {
851         return loadibucket0(ix, hashbits(score, 32)/ix->div, pis, pbuck, ib, OREAD);
852 }
853
854 int
855 indexsect(Index *ix, u8int *score)
856 {
857         return indexsect1(ix, score);
858 }
859
860 DBlock*
861 loadibucket(Index *ix, u8int *score, ISect **pis, u32int *pbuck, IBucket *ib)
862 {
863         return loadibucket1(ix, score, pis, pbuck, ib);
864 }
865
866