Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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,
        Emergency       = 0,            /* flag: performing emergency surgery */
};

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, 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){
                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){
                if(!Emergency){
                        freearenapart(ap, 0);
                        return nil;
                }
                /* else keep on, for emergency use */
        }

        ap->arenas = MKNZ(Arena*, ap->narenas);
        for(i = 0; i < ap->narenas; i++){
                debugarena = 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){
                        seterr(ECorrupt, "%s: %r", ap->map[i].name);
                        if(!Emergency){
                                freearenapart(ap, 1);
                                return nil;
                        }else{
                                /* keep on, for emergency use */
                                ap->narenas = i;
                                break;
                        }
                }
                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++) {
                debugarena = i;
                addarena(ap->arenas[i]);
        }
        debugarena = -1;

        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, 0);
        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 ||
           flushpart(ap->part) < 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, part->blocksize);
        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 || flushpart(part) < 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' astop '\n'
 * astart, astop: u64int
 */
int
parseamap(IFile *f, AMapN *amn)
{
        AMap *am;
        u64int v64;
        u32int v;
        char *s, *t, *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 %d in %s",
                        n, f->name);
                return -1;
        }
        am = MKNZ(AMap, n);
        if(am == nil){
                fprint(2, "out of memory\n");
                return -1;
        }
        for(i = 0; i < n; i++){
                s = ifileline(f);
                if(s)
                        t = estrdup(s);
                else
                        t = nil;
                if(s == nil || getfields(s, flds, 4, 0, "\t") != 3){
                        fprint(2, "early eof after %d of %d, %s:#%d: %s\n", i, n, f->name, f->pos, t);
                        free(t);
                        return -1;
                }
                free(t);
                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;
}