Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/* cached-worm file server */
#include "all.h"
#include "io.h"
#include "9p1.h"

extern int oldcachefmt;

Map *devmap;

Biobuf bin;

void
machinit(void)
{
        active.exiting = 0;
}

/*
 * Put a string on the console.
 */
void
puts(char *s, int n)
{
        print("%.*s", n, s);
}

void
prflush(void)
{
}

/*
 * Print a string on the console.
 */
void
putstrn(char *str, int n)
{
        puts(str, n);
}

/*
 * get a character from the console
 */
int
getc(void)
{
        return Bgetrune(&bin);
}

void
panic(char *fmt, ...)
{
        int n;
        va_list arg;
        char buf[PRINTSIZE];

        va_start(arg, fmt);
        n = vseprint(buf, buf + sizeof buf, fmt, arg) - buf;
        va_end(arg);
        buf[n] = '\0';
        print("panic: %s\n", buf);
        exit();
}

int
okay(char *quest)
{
        char *ln;

        print("okay to %s? ", quest);
        if ((ln = Brdline(&bin, '\n')) == nil)
                return 0;
        ln[Blinelen(&bin)-1] = '\0';
        if (isascii(*ln) && isupper(*ln))
                *ln = tolower(*ln);
        return *ln == 'y';
}

static void
mapinit(char *mapfile)
{
        int nf;
        char *ln;
        char *fields[2];
        Biobuf *bp;
        Map *map;

        if (mapfile == nil)
                return;
        bp = Bopen(mapfile, OREAD);
        if (bp == nil)
                sysfatal("can't read %s", mapfile);
        devmap = nil;
        while ((ln = Brdline(bp, '\n')) != nil) {
                ln[Blinelen(bp)-1] = '\0';
                if (*ln == '\0' || *ln == '#')
                        continue;
                nf = tokenize(ln, fields, nelem(fields));
                if (nf != 2)
                        continue;
                if(testconfig(fields[0]) != 0) {
                        print("bad `from' device %s in %s\n",
                                fields[0], mapfile);
                        continue;
                }
                map = malloc(sizeof *map);
                map->from = strdup(fields[0]);
                map->to =   strdup(fields[1]);
                map->fdev = iconfig(fields[0]);
                map->tdev = nil;
                if (access(map->to, AEXIST) < 0) {
                        /*
                         * map->to isn't an existing file, so it had better be
                         * a config string for a device.
                         */
                        if(testconfig(fields[1]) == 0)
                                map->tdev = iconfig(fields[1]);
                }
                /* else map->to is the replacement file name */
                map->next = devmap;
                devmap = map;
        }
        Bterm(bp);
}

static void
confinit(void)
{
        conf.nmach = 1;

        conf.mem = meminit();

        conf.nuid = 1000;
        conf.nserve = 15;               /* tunable */
        conf.nfile = 30000;
        conf.nlgmsg = 100;
        conf.nsmmsg = 500;

        localconfinit();

        conf.nwpath = conf.nfile*8;
        conf.nauth =  conf.nfile/10;
        conf.gidspace = conf.nuid*3;

        cons.flags = 0;

        if (conf.devmap)
                mapinit(conf.devmap);
}

/*
 * compute BUFSIZE*(NDBLOCK+INDPERBUF+INDPERBUF⁲+INDPERBUF⁳+INDPERBUF⁴)
 * while watching for overflow; in that case, return 0.
 */

static uvlong
adduvlongov(uvlong a, uvlong b)
{
        uvlong r = a + b;

        if (r < a || r < b)
                return 0;
        return r;
}

static uvlong
muluvlongov(uvlong a, uvlong b)
{
        uvlong r = a * b;

        if (a != 0 && r/a != b || r < a || r < b)
                return 0;
        return r;
}

static uvlong
maxsize(void)
{
        int i;
        uvlong max = NDBLOCK, ind = 1;

        for (i = 0; i < NIBLOCK; i++) {
                ind = muluvlongov(ind, INDPERBUF);      /* power of INDPERBUF */
                if (ind == 0)
                        return 0;
                max = adduvlongov(max, ind);
                if (max == 0)
                        return 0;
        }
        return muluvlongov(max, BUFSIZE);
}

enum {
        INDPERBUF⁲ = ((uvlong)INDPERBUF *INDPERBUF),
        INDPERBUF⁴ = ((uvlong)INDPERBUF⁲*INDPERBUF⁲),
};

static void
printsizes(void)
{
        uvlong max = maxsize();

        print("\tblock size = %d; ", RBUFSIZE);
        if (max == 0)
                print("max file size exceeds 2⁶⁴ bytes\n");
        else {
                uvlong offlim = 1ULL << (sizeof(Off)*8 - 1);

                if (max >= offlim)
                        max = offlim - 1;
                print("max file size = %,llud\n", (Wideoff)max);
        }
        if (INDPERBUF⁲/INDPERBUF != INDPERBUF)
                print("overflow computing INDPERBUF⁲\n");
        if (INDPERBUF⁴/INDPERBUF⁲ != INDPERBUF⁲)
                print("overflow computing INDPERBUF⁴\n");
        print("\tINDPERBUF = %d, INDPERBUF^4 = %,lld, ", INDPERBUF,
                (Wideoff)INDPERBUF⁴);
        print("CEPERBK = %d\n", CEPERBK);
        print("\tsizeofs: Dentry = %d, Cache = %d\n",
                sizeof(Dentry), sizeof(Cache));
}

void
usage(void)
{
        fprint(2, "usage: %s [-cf][-a ann-str][-m dev-map] config-dev\n",
                argv0);
        exits("usage");
}

void
main(int argc, char **argv)
{
        int i, nets = 0;
        char *ann;

        rfork(RFNOTEG);
        formatinit();
        machinit();
        conf.confdev = "n";             /* Devnone */

        ARGBEGIN{
        case 'a':                       /* announce on this net */
                ann = EARGF(usage());
                if (nets >= Maxnets) {
                        fprint(2, "%s: too many networks to announce: %s\n",
                                argv0, ann);
                        exits("too many nets");
                }
                annstrs[nets++] = ann;
                break;
        case 'c':                       /* use new, faster cache layout */
                oldcachefmt = 0;
                break;
        case 'f':                       /* enter configuration mode first */
                conf.configfirst++;
                break;
        case 'm':                       /* name device-map file */
                conf.devmap = EARGF(usage());
                break;
        default:
                usage();
                break;
        }ARGEND

        if (argc != 1)
                usage();
        conf.confdev = argv[0]; /* config string for dev holding full config */

        Binit(&bin, 0, OREAD);
        confinit();

        print("\nPlan 9 %d-bit cached-worm file server with %d-deep indir blks\n",
                sizeof(Off)*8 - 1, NIBLOCK);
        printsizes();

        qlock(&reflock);
        qunlock(&reflock);
        serveq = newqueue(1000, "9P service");  /* tunable */
        raheadq = newqueue(1000, "readahead");  /* tunable */

        mbinit();
        netinit();
        scsiinit();

        files = malloc(conf.nfile * sizeof *files);
        for(i=0; i < conf.nfile; i++) {
                qlock(&files[i]);
                qunlock(&files[i]);
        }

        wpaths = malloc(conf.nwpath * sizeof(*wpaths));
        uid = malloc(conf.nuid * sizeof(*uid));
        gidspace = malloc(conf.gidspace * sizeof(*gidspace));
        authinit();

        print("iobufinit\n");
        iobufinit();

        arginit();
        boottime = time(nil);

        print("sysinit\n");
        sysinit();

        /*
         * Ethernet i/o processes
         */
        netstart();

        /*
         * read ahead processes
         */
        newproc(rahead, 0, "rah");

        /*
         * server processes
         */
        for(i=0; i < conf.nserve; i++)
                newproc(serve, 0, "srv");

        /*
         * worm "dump" copy process
         */
        newproc(wormcopy, 0, "wcp");

        /*
         * processes to read the console
         */
        consserve();

        /*
         * "sync" copy process
         * this doesn't return.
         */
        procsetname("scp");
        synccopy();
}

/*
 * read ahead processes.
 * read message from q and then
 * read the device.
 */
int
rbcmp(void *va, void *vb)
{
        Rabuf *ra, *rb;

        ra = *(Rabuf**)va;
        rb = *(Rabuf**)vb;
        if(rb == 0)
                return 1;
        if(ra == 0)
                return -1;
        if(ra->dev > rb->dev)
                return 1;
        if(ra->dev < rb->dev)
                return -1;
        if(ra->addr > rb->addr)
                return 1;
        if(ra->addr < rb->addr)
                return -1;
        return 0;
}

void
rahead(void *)
{
        Rabuf *rb[50];
        Iobuf *p;
        int i, n;

        for (;;) {
                rb[0] = fs_recv(raheadq, 0);
                for(n = 1; n < nelem(rb); n++) {
                        if(raheadq->count <= 0)
                                break;
                        rb[n] = fs_recv(raheadq, 0);
                }
                qsort(rb, n, sizeof rb[0], rbcmp);
                for(i = 0; i < n; i++) {
                        if(rb[i] == 0)
                                continue;
                        p = getbuf(rb[i]->dev, rb[i]->addr, Brd);
                        if(p)
                                putbuf(p);
                        lock(&rabuflock);
                        rb[i]->link = rabuffree;
                        rabuffree = rb[i];
                        unlock(&rabuflock);
                }
        }
}

/*
 * main filesystem server loop.
 * entered by many processes.
 * they wait for message buffers and
 * then process them.
 */
void
serve(void *)
{
        int i;
        Chan *cp;
        Msgbuf *mb;

        for (;;) {
                qlock(&reflock);
                /* read 9P request from a network input process */
                mb = fs_recv(serveq, 0);
                assert(mb->magic == Mbmagic);
                /* fs kernel sets chan in /sys/src/fs/ip/il.c:/^getchan */
                cp = mb->chan;
                if (cp == nil)
                        panic("serve: nil mb->chan");
                rlock(&cp->reflock);
                qunlock(&reflock);

                rlock(&mainlock);

                if (mb->data == nil)
                        panic("serve: nil mb->data");
                /* better sniffing code in /sys/src/cmd/disk/kfs/9p12.c */
                if(cp->protocol == nil){
                        /* do we recognise the protocol in this packet? */
                        /* better sniffing code: /sys/src/cmd/disk/kfs/9p12.c */
                        for(i = 0; fsprotocol[i] != nil; i++)
                                if(fsprotocol[i](mb) != 0) {
                                        cp->protocol = fsprotocol[i];
                                        break;
                                }
                        if(cp->protocol == nil){
                                print("no protocol for message\n");
                                for(i = 0; i < 12; i++)
                                        print(" %2.2uX", mb->data[i]);
                                print("\n");
                        }
                } else
                        /* process the request, generate an answer and reply */
                        cp->protocol(mb);

                mbfree(mb);
                runlock(&mainlock);
                runlock(&cp->reflock);
        }
}

void
exit(void)
{
        lock(&active);
        active.exiting = 1;
        unlock(&active);

        print("halted at %T.\n", time(nil));
        postnote(PNGROUP, getpid(), "die");
        exits(nil);
}

enum {
        DUMPTIME = 5,   /* 5 am */
        WEEKMASK = 0,   /* every day (1=sun, 2=mon, 4=tue, etc.) */
};

/*
 * calculate the next dump time.
 * minimum delay is 100 minutes.
 */
Timet
nextdump(Timet t)
{
        Timet nddate = nextime(t+MINUTE(100), DUMPTIME, WEEKMASK);

        if(!conf.nodump)
                print("next dump at %T\n", nddate);
        return nddate;
}

/*
 * process to copy dump blocks from
 * cache to worm. it runs flat out when
 * it gets work, but only looks for
 * work every 10 seconds.
 */
void
wormcopy(void *)
{
        int f, dorecalc = 1;
        Timet dt, t = 0, nddate = 0, ntoytime = 0;
        Filsys *fs;

        for (;;) {
                if (dorecalc) {
                        dorecalc = 0;
                        t = time(nil);
                        nddate = nextdump(t);           /* chatters */
                        ntoytime = time(nil);
                }
                dt = time(nil) - t;
                if(dt < 0 || dt > MINUTE(100)) {
                        if(dt < 0)
                                print("time went back\n");
                        else
                                print("time jumped ahead\n");
                        dorecalc = 1;
                        continue;
                }
                t += dt;
                f = 0;
                if(t > ntoytime)
                        ntoytime = time(nil) + HOUR(1);
                else if(t > nddate) {
                        if(!conf.nodump) {
                                print("automatic dump %T\n", t);
                                for(fs=filsys; fs->name; fs++)
                                        if(fs->dev->type == Devcw)
                                                cfsdump(fs);
                        }
                        dorecalc = 1;
                } else {
                        rlock(&mainlock);
                        for(fs=filsys; fs->name; fs++)
                                if(fs->dev->type == Devcw)
                                        f |= dumpblock(fs->dev);
                        runlock(&mainlock);
                        if(!f)
                                delay(10000);
                        wormprobe();
                }
        }
}

/*
 * process to synch blocks
 * it puts out a block/cache-line every second
 * it waits 10 seconds if caught up.
 * in both cases, it takes about 10 seconds
 * to get up-to-date.
 */
void
synccopy(void)
{
        int f;

        for (;;) {
                rlock(&mainlock);
                f = syncblock();
                runlock(&mainlock);
                if(!f)
                        delay(10000);
                else
                        delay(1000);
        }
}

Devsize
inqsize(char *file)
{
        int nf;
        char *ln, *end, *data = malloc(strlen(file) + 5 + 1);
        char *fields[4];
        Devsize rv = -1;
        Biobuf *bp;

        strcpy(data, file);
        end = strstr(data, "/data");
        if (end == nil)
                strcat(data, "/ctl");
        else
                strcpy(end, "/ctl");
        bp = Bopen(data, OREAD);
        if (bp) {
                while (rv < 0 && (ln = Brdline(bp, '\n')) != nil) {
                        ln[Blinelen(bp)-1] = '\0';
                        nf = tokenize(ln, fields, nelem(fields));
                        if (nf == 3 && strcmp(fields[0], "geometry") == 0)
                                rv = atoi(fields[2]);
                }
                Bterm(bp);
        }
        free(data);
        return rv;
}