aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/venti/arenas.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/venti/arenas.c')
-rw-r--r--src/cmd/venti/arenas.c404
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;
+}