diff options
Diffstat (limited to 'src/cmd/venti/arenas.c')
-rw-r--r-- | src/cmd/venti/arenas.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/src/cmd/venti/arenas.c b/src/cmd/venti/arenas.c new file mode 100644 index 00000000..5275b938 --- /dev/null +++ b/src/cmd/venti/arenas.c @@ -0,0 +1,404 @@ +#include "stdinc.h" +#include "dat.h" +#include "fns.h" + +typedef struct AHash AHash; + +/* + * hash table for finding arena's based on their names. + */ +struct AHash +{ + AHash *next; + Arena *arena; +}; + +enum +{ + AHashSize = 512 +}; + +static AHash *ahash[AHashSize]; + +static u32int +hashstr(char *s) +{ + u32int h; + int c; + + h = 0; + for(; c = *s; s++){ + c ^= c << 6; + h += (c << 11) ^ (c >> 1); + c = *s; + h ^= (c << 14) + (c << 7) + (c << 4) + c; + } + return h; +} + +int +addarena(Arena *arena) +{ + AHash *a; + u32int h; + + h = hashstr(arena->name) & (AHashSize - 1); + a = MK(AHash); + if(a == nil) + return -1; + a->arena = arena; + a->next = ahash[h]; + ahash[h] = a; + return 0; +} + +Arena* +findarena(char *name) +{ + AHash *a; + u32int h; + + h = hashstr(name) & (AHashSize - 1); + for(a = ahash[h]; a != nil; a = a->next) + if(strcmp(a->arena->name, name) == 0) + return a->arena; + return nil; +} + +int +delarena(Arena *arena) +{ + AHash *a, *last; + u32int h; + + h = hashstr(arena->name) & (AHashSize - 1); + last = nil; + for(a = ahash[h]; a != nil; a = a->next){ + if(a->arena == arena){ + if(last != nil) + last->next = a->next; + else + ahash[h] = a->next; + free(a); + return 0; + } + last = a; + } + return -1; +} + +ArenaPart* +initarenapart(Part *part) +{ + AMapN amn; + ArenaPart *ap; + ZBlock *b; + u32int i; + int ok; + + b = alloczblock(HeadSize, 0); + if(b == nil || readpart(part, PartBlank, b->data, HeadSize) < 0){ + seterr(EAdmin, "can't read arena partition header: %r"); + return nil; + } + + ap = MKZ(ArenaPart); + if(ap == nil){ + freezblock(b); + return nil; + } + ap->part = part; + ok = unpackarenapart(ap, b->data); + freezblock(b); + if(ok < 0){ + seterr(ECorrupt, "corrupted arena partition header: %r"); + freearenapart(ap, 0); + return nil; + } + + ap->tabbase = (PartBlank + HeadSize + ap->blocksize - 1) & ~(ap->blocksize - 1); + if(ap->version != ArenaPartVersion){ + seterr(ECorrupt, "unknown arena partition version %d", ap->version); + freearenapart(ap, 0); + return nil; + } + if(ap->blocksize & (ap->blocksize - 1)){ + seterr(ECorrupt, "illegal non-power-of-2 block size %d\n", ap->blocksize); + freearenapart(ap, 0); + return nil; + } + if(ap->tabbase >= ap->arenabase){ + seterr(ECorrupt, "arena partition table overlaps with arena storage"); + freearenapart(ap, 0); + return nil; + } + ap->tabsize = ap->arenabase - ap->tabbase; + partblocksize(part, ap->blocksize); + ap->size = ap->part->size & ~(u64int)(ap->blocksize - 1); + + if(readarenamap(&amn, part, ap->tabbase, ap->tabsize) < 0){ + freearenapart(ap, 0); + return nil; + } + ap->narenas = amn.n; + ap->map = amn.map; + if(okamap(ap->map, ap->narenas, ap->arenabase, ap->size, "arena table") < 0){ + freearenapart(ap, 0); + return nil; + } + + ap->arenas = MKNZ(Arena*, ap->narenas); + for(i = 0; i < ap->narenas; i++){ + ap->arenas[i] = initarena(part, ap->map[i].start, ap->map[i].stop - ap->map[i].start, ap->blocksize); + if(ap->arenas[i] == nil){ + freearenapart(ap, 1); + return nil; + } + if(namecmp(ap->map[i].name, ap->arenas[i]->name) != 0){ + seterr(ECorrupt, "arena name mismatches with expected name: %s vs. %s", + ap->map[i].name, ap->arenas[i]->name); + freearenapart(ap, 1); + return nil; + } + if(findarena(ap->arenas[i]->name)){ + seterr(ECorrupt, "duplicate arena name %s in %s", + ap->map[i].name, ap->part->name); + freearenapart(ap, 1); + return nil; + } + } + + for(i = 0; i < ap->narenas; i++) + addarena(ap->arenas[i]); + + return ap; +} + +ArenaPart* +newarenapart(Part *part, u32int blocksize, u32int tabsize) +{ + ArenaPart *ap; + + if(blocksize & (blocksize - 1)){ + seterr(ECorrupt, "illegal non-power-of-2 block size %d\n", blocksize); + return nil; + } + ap = MKZ(ArenaPart); + if(ap == nil) + return nil; + + ap->version = ArenaPartVersion; + ap->part = part; + ap->blocksize = blocksize; + partblocksize(part, blocksize); + ap->size = part->size & ~(u64int)(blocksize - 1); + ap->tabbase = (PartBlank + HeadSize + blocksize - 1) & ~(blocksize - 1); + ap->arenabase = (ap->tabbase + tabsize + blocksize - 1) & ~(blocksize - 1); + ap->tabsize = ap->arenabase - ap->tabbase; + ap->narenas = 0; + + if(wbarenapart(ap) < 0){ + freearenapart(ap, 0); + return nil; + } + + return ap; +} + +int +wbarenapart(ArenaPart *ap) +{ + ZBlock *b; + + if(okamap(ap->map, ap->narenas, ap->arenabase, ap->size, "arena table") < 0) + return -1; + b = alloczblock(HeadSize, 1); + if(b == nil) +//ZZZ set error message? + return -1; + + if(packarenapart(ap, b->data) < 0){ + seterr(ECorrupt, "can't make arena partition header: %r"); + freezblock(b); + return -1; + } + if(writepart(ap->part, PartBlank, b->data, HeadSize) < 0){ + seterr(EAdmin, "can't write arena partition header: %r"); + freezblock(b); + return -1; + } + freezblock(b); + + return wbarenamap(ap->map, ap->narenas, ap->part, ap->tabbase, ap->tabsize); +} + +void +freearenapart(ArenaPart *ap, int freearenas) +{ + int i; + + if(ap == nil) + return; + if(freearenas){ + for(i = 0; i < ap->narenas; i++){ + if(ap->arenas[i] == nil) + continue; + delarena(ap->arenas[i]); + freearena(ap->arenas[i]); + } + } + free(ap->map); + free(ap->arenas); + free(ap); +} + +int +okamap(AMap *am, int n, u64int start, u64int stop, char *what) +{ + u64int last; + u32int i; + + last = start; + for(i = 0; i < n; i++){ + if(am[i].start < last){ + if(i == 0) + seterr(ECorrupt, "invalid start address in %s", what); + else + seterr(ECorrupt, "overlapping ranges in %s", what); + return -1; + } + if(am[i].stop < am[i].start){ + seterr(ECorrupt, "invalid range in %s", what); + return -1; + } + last = am[i].stop; + } + if(last > stop){ + seterr(ECorrupt, "invalid ending address in %s", what); + return -1; + } + return 0; +} + +int +maparenas(AMap *am, Arena **arenas, int n, char *what) +{ + u32int i; + + for(i = 0; i < n; i++){ + arenas[i] = findarena(am[i].name); + if(arenas[i] == nil){ + seterr(EAdmin, "can't find arena '%s' for '%s'\n", am[i].name, what); + return -1; + } + } + return 0; +} + +int +readarenamap(AMapN *amn, Part *part, u64int base, u32int size) +{ + IFile f; + u32int ok; + + if(partifile(&f, part, base, size) < 0) + return -1; + ok = parseamap(&f, amn); + freeifile(&f); + return ok; +} + +int +wbarenamap(AMap *am, int n, Part *part, u64int base, u64int size) +{ + Fmt f; + ZBlock *b; + + b = alloczblock(size, 1); + if(b == nil) + return -1; + + fmtzbinit(&f, b); + + if(outputamap(&f, am, n) < 0){ + seterr(ECorrupt, "arena set size too small"); + freezblock(b); + return -1; + } + if(writepart(part, base, b->data, size) < 0){ + seterr(EAdmin, "can't write arena set: %r"); + freezblock(b); + return -1; + } + freezblock(b); + return 0; +} + +/* + * amap: n '\n' amapelem * n + * n: u32int + * amapelem: name '\t' astart '\t' asize '\n' + * astart, asize: u64int + */ +int +parseamap(IFile *f, AMapN *amn) +{ + AMap *am; + u64int v64; + u32int v; + char *s, *flds[4]; + int i, n; + + /* + * arenas + */ + if(ifileu32int(f, &v) < 0){ + seterr(ECorrupt, "syntax error: bad number of elements in %s", f->name); + return -1; + } + n = v; + if(n > MaxAMap){ + seterr(ECorrupt, "illegal number of elements in %s", f->name); + return -1; + } + am = MKNZ(AMap, n); + if(am == nil) + return -1; + for(i = 0; i < n; i++){ + s = ifileline(f); + if(s == nil || getfields(s, flds, 4, 0, "\t") != 3) + return -1; + if(nameok(flds[0]) < 0) + return -1; + namecp(am[i].name, flds[0]); + if(stru64int(flds[1], &v64) < 0){ + seterr(ECorrupt, "syntax error: bad arena base address in %s", f->name); + free(am); + return -1; + } + am[i].start = v64; + if(stru64int(flds[2], &v64) < 0){ + seterr(ECorrupt, "syntax error: bad arena size in %s", f->name); + free(am); + return -1; + } + am[i].stop = v64; + } + + amn->map = am; + amn->n = n; + return 0; +} + +int +outputamap(Fmt *f, AMap *am, int n) +{ + int i; + + if(fmtprint(f, "%ud\n", n) < 0) + return -1; + for(i = 0; i < n; i++) + if(fmtprint(f, "%s\t%llud\t%llud\n", am[i].name, am[i].start, am[i].stop) < 0) + return -1; + return 0; +} |