* Re: [9fans] jukebox configuration changes
@ 2002-11-02 9:37 Geoff Collyer
0 siblings, 0 replies; 2+ messages in thread
From: Geoff Collyer @ 2002-11-02 9:37 UTC (permalink / raw)
To: 9fans
[-- Attachment #1: Type: text/plain, Size: 612 bytes --]
As promised, here are changes to the file server kernel to permit the
use of jukeboxes with different-sized discs on a single file server.
I've tested it with an HP 80EX with 5.2GB discs and an HP 40FX with
2.6GB discs on one file server.
I've enclosed the diffs (a little over 200 lines) and a bundle of the
changed files. I've done a little other file server work, including
incorporating nigel's/forsyth's wormcopy, since I last synced kernels
with nemo, but I don't think these changes will be hard to integrate.
The files in the fs directory are the prototypes for cloning file
server kernels.
[-- Attachment #2.1: Type: text/plain, Size: 308 bytes --]
The following attachment had content that we can't
prove to be harmless. To avoid possible automatic
execution, we changed the content headers.
The original header was:
Content-Disposition: attachment; filename=fs.diffs
Content-Type: text/plain; charset="US-ASCII"
Content-Transfer-Encoding: 7bit
[-- Attachment #2.2: fs.diffs.suspect --]
[-- Type: application/octet-stream, Size: 5643 bytes --]
diff /n/dump/2002/1020/sys/src/fs/dev/cw.c ./dev/cw.c
125c125
< long hmsize, hmaddr;
---
> long hmsize, hmaddr, dsize, dsizepct;
188a189,196
> /* print stats in terms of (first-)disc sides */
> dsize = wormsizeside(dev, 0);
> if (dsize < 1) {
> print("wormsizeside returned a size of %ld for %Z side 0\n",
> dsize, dev);
> dsize = 100;
> }
> dsizepct = dsize/100;
190,191c198
< h->fsize/DSIZE,
< (h->fsize%DSIZE)/(DSIZE/100));
---
> h->fsize/dsize, (h->fsize%dsize)/dsizepct);
195,196c202
< h->wmax/DSIZE,
< (h->wmax%DSIZE)/(DSIZE/100));
---
> h->wmax/dsize, (h->wmax%dsize)/dsizepct);
198,199c204
< h->wsize/DSIZE,
< (h->wsize%DSIZE)/(DSIZE/100));
---
> h->wsize/dsize, (h->wsize%dsize)/dsizepct);
1640c1645
< mvstates(Device *dev, int s1, int s2, int drive)
---
> mvstates(Device *dev, int s1, int s2, int side)
1651,1654c1656,1663
< hi = lo + 500*DSIZE; // BOTCH arbitrary large number
< if(drive >= 0) {
< lo = drive * DSIZE;
< hi = lo + DSIZE;
---
> hi = lo + devsize(dev->cw.w); /* size of all sides totalled */
> if(side >= 0) {
> /* operate on only a single disc side */
> Sidestarts ss;
>
> wormsidestarts(dev, side, &ss);
> lo = ss.sstart;
> hi = ss.s1start;
1881a1891
> Sidestarts ss;
1891a1902,1903
> if (dskno >= 0)
> wormsidestarts(dev, dskno, &ss);
1904,1905c1916,1917
< if(dskno < 0 ||
< m >= dskno*DSIZE && m < (dskno+1)*DSIZE) {
---
> /* if given a diskno, restrict to just that disc side */
> if(dskno < 0 || m >= ss.sstart && m < ss.s1start) {
1926a1939
> Sidestarts ss;
1935c1948,1949
< ml = dskno*DSIZE;
---
> wormsidestarts(dev, dskno, &ss);
> ml = ss.sstart; /* start at beginning of disc side #dskno */
2206d2219
< s2 = DSIZE;
2208a2222,2223
> else
> s2 = wormsizeside(dev, s1); /* default to 1 disc side */
diff /n/dump/2002/1020/sys/src/fs/dev/juke.c ./dev/juke.c
6c6
< #define FIXEDSIZE 0
---
>
58a59,60
> extern int FIXEDSIZE;
>
282c284
< if(FIXEDSIZE)
---
> if(FIXEDSIZE) // TODO? push FIXEDSIZE into Device or Juke struct
289a292,347
> /*
> * d must be, or be on, the same jukebox as `side' resides on.
> * have to paw through Devmcat of sides.
> */
> long
> wormsizeside(Device *d, int side)
> {
> int s;
> Device *x;
>
> switch(d->type) {
> default:
> print("wormsizeside: dev not ro, cw, juke nor mcat: %Z\n", d);
> return 0;
>
> case Devmcat: /* of sides */
> break;
> case Devjuke:
> return wormsizeside(d->j.m, side); /* mcat of sides */
> case Devcw:
> return wormsizeside(d->cw.w, side); /* should be juke */
> case Devro:
> return wormsizeside(d->ro.parent, side); /* cw */
> }
>
> if (side < 0 || side >= d->cat.ndev) {
> print("wormsizeside: side %d not in range [0..%d] for %Z\n",
> side, d->cat.ndev, d);
> return 0;
> }
> x = d->cat.first;
> for (s = 0; s < side; s++) {
> x = x->link;
> if (x == nil)
> panic("wormsizeside nil link");
> }
> if (x->type != Devworm && x->type != Devlworm) {
> print("wormsizeside: %Z of %Z type not (l)worm\n", x, d);
> return 0;
> }
> return wormsize(x);
> }
>
> /* returns starts of side #side and #(side+1) in *stp */
> void
> wormsidestarts(Device *dev, int side, Sidestarts *stp)
> {
> int s;
> long dstart;
>
> for (dstart = s = 0; s < side; s++)
> dstart += wormsizeside(dev, s);
> stp->sstart = dstart;
> stp->s1start = dstart + wormsizeside(dev, side);
> }
>
583a642
> Device *dev = d;
587c646
< print("juke platter not devworm: %Z\n", d);
---
> print("juke platter not (devmcat of) dev(l)worm: %Z\n", d);
590a650,653
> /*
> * we don't call mcatinit(d) here, so we have to set d->cat.ndev
> * ourselves.
> */
592a656
> dev->cat.ndev = o;
diff /n/dump/2002/1020/sys/src/fs/fs/9fsfs.c ./fs/9fsfs.c
7a8,14
> /*
> * setting this to zero permits the use of discs of different sizes, but
> * can make jukeinit() quite slow while the robotics work through each disc
> * twice (once per side).
> */
> int FIXEDSIZE = 1;
>
diff /n/dump/2002/1020/sys/src/fs/fs/dat.h ./fs/dat.h
0a1,6
> /*
> * The most fundamental constant.
> * Will the code compile with RBUFSIZE a variable?
> * Nope, for one thing, RBUFSIZE determines FEPERBUF, which determines
> * the number of elements in a free-list-block array.
> */
2d7
< #define DSIZE (631736-1) /* 5.2GB on first disc */
diff /n/dump/2002/1020/sys/src/fs/port/chk.c ./port/chk.c
163,165c163,166
< oldblock = fsize/DSIZE;
< oldblock *= DSIZE;
< if(oldblock < 0)
---
> /* round fsize down to start of current side */
> int s;
> long dsize;
>
166a168,170
> for (s = 0; dsize = wormsizeside(dev, s),
> dsize > 0 && oldblock + dsize < fsize; s++)
> oldblock += dsize;
diff /n/dump/2002/1020/sys/src/fs/port/portdat.h ./port/portdat.h
153c153
< struct /* worm wren */
---
> struct /* worm wren, (l)worm in targ */
178,179c178,179
< Device* j;
< Device* m;
---
> Device* j; /* (robotics, worm drives) - wrens */
> Device* m; /* (sides) - r or l devices */
195c195
< struct /* part */
---
> struct /* byte-swapped */
200a201,205
>
> typedef struct Sidestarts {
> long sstart; /* blocks before start of side */
> long s1start; /* blocks before start of next side */
> } Sidestarts;
diff /n/dump/2002/1020/sys/src/fs/port/portfns.h ./port/portfns.h
265a266,267
> long wormsizeside(Device *, int side);
> void wormsidestarts(Device *dev, int side, Sidestarts *stp);
[-- Attachment #3.1: Type: text/plain, Size: 306 bytes --]
The following attachment had content that we can't
prove to be harmless. To avoid possible automatic
execution, we changed the content headers.
The original header was:
Content-Disposition: attachment; filename=fs.bun
Content-Type: text/plain; charset="US-ASCII"
Content-Transfer-Encoding: 7bit
[-- Attachment #3.2: fs.bun.suspect --]
[-- Type: application/octet-stream, Size: 111894 bytes --]
# To unbundle, run this file
echo dev/cw.c
sed 's/^X//' >dev/cw.c <<'!'
X#include "all.h"
X#define DEBUG 0
X#define FIRST SUPER_ADDR
X#define ADDFREE (100)
X#define CACHE_ADDR SUPER_ADDR
X#define MAXAGE 10000
X#define CDEV(d) (d->cw.c)
X#define WDEV(d) (d->cw.w)
X#define RDEV(d) (d->cw.ro)
X/* cache state */
enum
X{
X /* states -- beware these are recorded on the cache */
X /* cache worm */
X Cnone = 0, /* 0 ? */
X Cdirty, /* 1 0 */
X Cdump, /* 1 0->1 */
X Cread, /* 1 1 */
X Cwrite, /* 2 1 */
X Cdump1, /* inactive form of dump */
X Cerror,
X /* opcodes -- these are not recorded */
X Onone,
X Oread,
X Owrite,
X Ogrow,
X Odump,
X Orele,
X Ofree,
X};
typedef struct Cw Cw;
struct Cw
X{
X Device* dev;
X Device* cdev;
X Device* wdev;
X Device* rodev;
X Cw* link;
X Filter ncwio[3];
X int dbucket; /* last bucket dumped */
X long daddr; /* last block dumped */
X long ncopy;
X int nodump;
X/*
X * following are cached variables for dumps
X */
X long fsize;
X long ndump;
X int depth;
X int all; /* local flag to recur on modified directories */
X int allflag; /* global flag to recur on modified directories */
X long falsehits; /* times recur found modified blocks */
X struct
X {
X char name[500];
X char namepad[NAMELEN+10];
X };
X};
static
char* cwnames[] =
X{
X [Cnone] "none",
X [Cdirty] "dirty",
X [Cdump] "dump",
X [Cread] "read",
X [Cwrite] "write",
X [Cdump1] "dump1",
X [Cerror] "error",
X [Onone] "none",
X [Oread] "read",
X [Owrite] "write",
X [Ogrow] "grow",
X [Odump] "dump",
X [Orele] "rele",
X};
Centry* getcentry(Bucket*, long);
int cwio(Device*, long, void*, int);
void cmd_cwcmd(int, char*[]);
X/*
X * console command
X * initiate a dump
X */
void
cmd_dump(int argc, char *argv[])
X{
X Filsys *fs;
X fs = cons.curfs;
X if(argc > 1)
X fs = fsstr(argv[1]);
X if(fs == 0) {
X print("%s: unknown file system\n", argv[1]);
X return;
X }
X cfsdump(fs);
X}
X/*
X * console command
X * worm stats
X */
static
void
cmd_statw(int, char*[])
X{
X Filsys *fs;
X Iobuf *p;
X Superb *sb;
X Cache *h;
X Bucket *b;
X Centry *c, *ce;
X long m, nw, bw, state[Onone];
X long sbfsize, sbcwraddr, sbroraddr, sblast, sbnext;
X long hmsize, hmaddr, dsize, dsizepct;
X Device *dev;
X Cw *cw;
X int s;
X fs = cons.curfs;
X dev = fs->dev;
X if(dev->type != Devcw) {
X print("curfs not type cw\n");
X return;
X }
X cw = dev->private;
X if(cw == 0) {
X print("curfs not inited\n");
X return;
X }
X print("cwstats %s\n", fs->name);
X sbfsize = 0;
X sbcwraddr = 0;
X sbroraddr = 0;
X sblast = 0;
X sbnext = 0;
X print(" filesys %s\n", fs->name);
X print(" nio =%7W%7W%7W\n", cw->ncwio+0, cw->ncwio+1, cw->ncwio+2);
X p = getbuf(dev, cwsaddr(dev), Bread);
X if(!p || checktag(p, Tsuper, QPSUPER)) {
X print("cwstats: checktag super\n");
X if(p) {
X putbuf(p);
X p = 0;
X }
X }
X if(p) {
X sb = (Superb*)p->iobuf;
X sbfsize = sb->fsize;
X sbcwraddr = sb->cwraddr;
X sbroraddr = sb->roraddr;
X sblast = sb->last;
X sbnext = sb->next;
X putbuf(p);
X }
X p = getbuf(cw->cdev, CACHE_ADDR, Bread|Bres);
X if(!p || checktag(p, Tcache, QPSUPER)) {
X print("cwstats: checktag c bucket\n");
X if(p)
X putbuf(p);
X return;
X }
X h = (Cache*)p->iobuf;
X hmaddr = h->maddr;
X hmsize = h->msize;
X print(" maddr = %8ld\n", hmaddr);
X print(" msize = %8ld\n", hmsize);
X print(" caddr = %8ld\n", h->caddr);
X print(" csize = %8ld\n", h->csize);
X print(" sbaddr = %8ld\n", h->sbaddr);
X print(" craddr = %8ld %8ld\n", h->cwraddr, sbcwraddr);
X print(" roaddr = %8ld %8ld\n", h->roraddr, sbroraddr);
X /* print stats in terms of (first-)disc sides */
X dsize = wormsizeside(dev, 0);
X if (dsize < 1) {
X print("wormsizeside returned a size of %ld for %Z side 0\n",
X dsize, dev);
X dsize = 100;
X }
X dsizepct = dsize/100;
X print(" fsize = %8ld %8ld %2ld+%2ld%%\n", h->fsize, sbfsize,
X h->fsize/dsize, (h->fsize%dsize)/dsizepct);
X print(" slast = %8ld\n", sblast);
X print(" snext = %8ld\n", sbnext);
X print(" wmax = %8ld %2ld+%2ld%%\n", h->wmax,
X h->wmax/dsize, (h->wmax%dsize)/dsizepct);
X print(" wsize = %8ld %2ld+%2ld%%\n", h->wsize,
X h->wsize/dsize, (h->wsize%dsize)/dsizepct);
X putbuf(p);
X bw = 0; /* max filled bucket */
X memset(state, 0, sizeof(state));
X for(m=0; m<hmsize; m++) {
X p = getbuf(cw->cdev, hmaddr + m/BKPERBLK, Bread);
X if(!p || checktag(p, Tbuck, hmaddr + m/BKPERBLK)) {
X print("cwstats: checktag c bucket\n");
X if(p)
X putbuf(p);
X return;
X }
X b = (Bucket*)p->iobuf + m%BKPERBLK;
X ce = b->entry + CEPERBK;
X nw = 0;
X for(c=b->entry; c<ce; c++) {
X s = c->state;
X state[s]++;
X if(s != Cnone && s != Cread)
X nw++;
X }
X putbuf(p);
X if(nw > bw)
X bw = nw;
X }
X for(s=Cnone; s<Cerror; s++)
X print(" %6ld %s\n", state[s], cwnames[s]);
X print(" cache %2ld%% full\n", (bw*100)/CEPERBK);
X}
int
dumpblock(Device *dev)
X{
X Iobuf *p, *cb, *p1, *p2;
X Cache *h;
X Centry *c, *ce, *bc;
X Bucket *b;
X long m, a, msize, maddr, wmax, caddr;
X int s1, s2, count;
X Cw *cw;
X cw = dev->private;
X if(cw == 0 || cw->nodump)
X return 0;
X cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bres);
X h = (Cache*)cb->iobuf;
X msize = h->msize;
X maddr = h->maddr;
X wmax = h->wmax;
X caddr = h->caddr;
X putbuf(cb);
X for(m=msize; m>=0; m--) {
X a = cw->dbucket + 1;
X if(a < 0 || a >= msize)
X a = 0;
X cw->dbucket = a;
X p = getbuf(cw->cdev, maddr + a/BKPERBLK, Bread);
X b = (Bucket*)p->iobuf + a%BKPERBLK;
X ce = b->entry + CEPERBK;
X bc = 0;
X for(c=b->entry; c<ce; c++)
X if(c->state == Cdump) {
X if(bc == 0) {
X bc = c;
X continue;
X }
X if(c->waddr < cw->daddr) {
X if(bc->waddr < cw->daddr &&
X bc->waddr > c->waddr)
X bc = c;
X continue;
X }
X if(bc->waddr < cw->daddr ||
X bc->waddr > c->waddr)
X bc = c;
X }
X if(bc) {
X c = bc;
X goto found;
X }
X putbuf(p);
X }
X if(cw->ncopy) {
X print("%ld blocks copied to worm\n", cw->ncopy);
X cw->ncopy = 0;
X }
X cw->nodump = 1;
X return 0;
found:
X a = a*CEPERBK + (c - b->entry) + caddr;
X p1 = getbuf(devnone, Cwdump1, 0);
X count = 0;
retry:
X count++;
X if(count > 10)
X goto stop;
X if(devread(cw->cdev, a, p1->iobuf))
X goto stop;
X m = c->waddr;
X cw->daddr = m;
X s1 = devwrite(cw->wdev, m, p1->iobuf);
X if(s1) {
X p2 = getbuf(devnone, Cwdump2, 0);
X s2 = devread(cw->wdev, m, p2->iobuf);
X if(s2) {
X if(s1 == 0x61 && s2 == 0x60) {
X putbuf(p2);
X goto retry;
X }
X goto stop1;
X }
X if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE))
X goto stop1;
X putbuf(p2);
X }
X /*
X * reread and compare
X */
X if(conf.dumpreread) {
X p2 = getbuf(devnone, Cwdump2, 0);
X s1 = devread(cw->wdev, m, p2->iobuf);
X if(s1)
X goto stop1;
X if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
X print("reread C%ld W%ld didnt compare\n", a, m);
X goto stop1;
X }
X putbuf(p2);
X }
X putbuf(p1);
X c->state = Cread;
X p->flags |= Bmod;
X putbuf(p);
X if(m > wmax) {
X cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bres);
X h = (Cache*)cb->iobuf;
X if(m > h->wmax)
X h->wmax = m;
X putbuf(cb);
X }
X cw->ncopy++;
X return 1;
stop1:
X putbuf(p2);
X putbuf(p1);
X c->state = Cdump1;
X p->flags |= Bmod;
X putbuf(p);
X return 1;
stop:
X putbuf(p1);
X putbuf(p);
X print("stopping dump!!\n");
X cw->nodump = 1;
X return 0;
X}
void
cwinit1(Device *dev)
X{
X Cw *cw;
X static int first;
X cw = dev->private;
X if(cw)
X return;
X if(first == 0) {
X cmd_install("dump", "-- make dump backup to worm", cmd_dump);
X cmd_install("statw", "-- cache/worm stats", cmd_statw);
X cmd_install("cwcmd", "subcommand -- cache/worm errata", cmd_cwcmd);
X roflag = flag_install("ro", "-- ro reads and writes");
X first = 1;
X }
X cw = ialloc(sizeof(Cw), 0);
X dev->private = cw;
X cw->allflag = 0;
X dofilter(cw->ncwio+0, C0a, C0b, 1);
X dofilter(cw->ncwio+1, C1a, C1b, 1);
X dofilter(cw->ncwio+2, C2a, C2b, 1);
X cw->dev = dev;
X cw->cdev = CDEV(dev);
X cw->wdev = WDEV(dev);
X cw->rodev = RDEV(dev);
X devinit(cw->cdev);
X devinit(cw->wdev);
X}
void
cwinit(Device *dev)
X{
X Cw *cw;
X Cache *h;
X Iobuf *cb, *p;
X long l, m;
X cwinit1(dev);
X cw = dev->private;
X l = devsize(cw->wdev);
X cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bres);
X h = (Cache*)cb->iobuf;
X h->toytime = toytime() + SECOND(30);
X h->time = time();
X m = h->wsize;
X if(l != m) {
X print("wdev changed size %ld to %ld\n", m, l);
X h->wsize = l;
X cb->flags |= Bmod;
X }
X for(m=0; m<h->msize; m++) {
X p = getbuf(cw->cdev, h->maddr + m/BKPERBLK, Bread);
X if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
X panic("cwinit: checktag c bucket");
X putbuf(p);
X }
X putbuf(cb);
X}
long
cwsaddr(Device *dev)
X{
X Iobuf *cb;
X long sa;
X cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
X sa = ((Cache*)cb->iobuf)->sbaddr;
X putbuf(cb);
X return sa;
X}
long
cwraddr(Device *dev)
X{
X Iobuf *cb;
X long ra;
X switch(dev->type) {
X default:
X print("unknown dev in cwraddr %Z\n", dev);
X return 1;
X case Devcw:
X cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
X ra = ((Cache*)cb->iobuf)->cwraddr;
X break;
X case Devro:
X cb = getbuf(CDEV(dev->ro.parent), CACHE_ADDR, Bread|Bres);
X ra = ((Cache*)cb->iobuf)->roraddr;
X break;
X }
X putbuf(cb);
X return ra;
X}
long
cwsize(Device *dev)
X{
X Iobuf *cb;
X long fs;
X cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
X fs = ((Cache*)cb->iobuf)->fsize;
X putbuf(cb);
X return fs;
X}
int
cwread(Device *dev, long b, void *c)
X{
X return cwio(dev, b, c, Oread) == Cerror;
X}
int
cwwrite(Device *dev, long b, void *c)
X{
X return cwio(dev, b, c, Owrite) == Cerror;
X}
int
roread(Device *dev, long b, void *c)
X{
X Device *d;
X int s;
X /*
X * maybe better is to try buffer pool first
X */
X d = dev->ro.parent;
X if(d == 0 || d->type != Devcw ||
X d->private == 0 || RDEV(d) != dev) {
X print("bad rodev %Z\n", dev);
X return 1;
X }
X s = cwio(d, b, 0, Onone);
X if(s == Cdump || s == Cdump1 || s == Cread) {
X s = cwio(d, b, c, Oread);
X if(s == Cdump || s == Cdump1 || s == Cread) {
X if(cons.flags & roflag)
X print("roread: %Z %ld -> %Z(hit)\n", dev, b, d);
X return 0;
X }
X }
X if(cons.flags & roflag)
X print("roread: %Z %ld -> %Z(miss)\n", dev, b, WDEV(d));
X return devread(WDEV(d), b, c);
X}
int
cwio(Device *dev, long addr, void *buf, int opcode)
X{
X Iobuf *p, *p1, *p2, *cb;
X Cache *h;
X Bucket *b;
X Centry *c;
X long bn, a1, a2, max, newmax;
X int state;
X Cw *cw;
X cw = dev->private;
X cw->ncwio[0].count++;
X cw->ncwio[1].count++;
X cw->ncwio[2].count++;
X cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bres);
X h = (Cache*)cb->iobuf;
X if(toytime() >= h->toytime) {
X cb->flags |= Bmod;
X h->toytime = toytime() + SECOND(30);
X h->time = time();
X }
X if(addr < 0) {
X putbuf(cb);
X return Cerror;
X }
X bn = addr % h->msize;
X a1 = h->maddr + bn/BKPERBLK;
X a2 = bn*CEPERBK + h->caddr;
X max = h->wmax;
X putbuf(cb);
X newmax = 0;
X p = getbuf(cw->cdev, a1, Bread|Bmod);
X if(!p || checktag(p, Tbuck, a1))
X panic("cwio: checktag c bucket");
X b = (Bucket*)p->iobuf + bn%BKPERBLK;
X c = getcentry(b, addr);
X if(c == 0) {
X putbuf(p);
X print("disk cache bucket %ld is full\n", a1);
X return Cerror;
X }
X a2 += c - b->entry;
X state = c->state;
X switch(opcode)
X {
X default:
X goto bad;
X case Onone:
X break;
X case Oread:
X switch(state) {
X default:
X goto bad;
X case Cread:
X if(!devread(cw->cdev, a2, buf))
X break;
X c->state = Cnone;
X case Cnone:
X if(devread(cw->wdev, addr, buf)) {
X state = Cerror;
X break;
X }
X if(addr > max)
X newmax = addr;
X if(!devwrite(cw->cdev, a2, buf))
X c->state = Cread;
X break;
X case Cdirty:
X case Cdump:
X case Cdump1:
X case Cwrite:
X if(devread(cw->cdev, a2, buf))
X state = Cerror;
X break;
X }
X break;
X case Owrite:
X switch(state) {
X default:
X goto bad;
X case Cdump:
X case Cdump1:
X /*
X * this is hard part -- a dump block must be
X * sent to the worm if it is rewritten.
X * if this causes an error, there is no
X * place to save the dump1 data. the block
X * is just reclassified as 'dump1' (botch)
X */
X p1 = getbuf(devnone, Cwio1, 0);
X if(devread(cw->cdev, a2, p1->iobuf)) {
X putbuf(p1);
X print("cwio: write induced dump error - r cache\n");
X casenone:
X if(devwrite(cw->cdev, a2, buf)) {
X state = Cerror;
X break;
X }
X c->state = Cdump1;
X break;
X }
X if(devwrite(cw->wdev, addr, p1->iobuf)) {
X p2 = getbuf(devnone, Cwio2, 0);
X if(devread(cw->wdev, addr, p2->iobuf)) {
X putbuf(p1);
X putbuf(p2);
X print("cwio: write induced dump error - r+w worm\n");
X goto casenone;
X }
X if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
X putbuf(p1);
X putbuf(p2);
X print("cwio: write induced dump error - w worm\n");
X goto casenone;
X }
X putbuf(p2);
X }
X putbuf(p1);
X c->state = Cread;
X if(addr > max)
X newmax = addr;
X cw->ncopy++;
X case Cnone:
X case Cread:
X if(devwrite(cw->cdev, a2, buf)) {
X state = Cerror;
X break;
X }
X c->state = Cwrite;
X break;
X case Cdirty:
X case Cwrite:
X if(devwrite(cw->cdev, a2, buf))
X state = Cerror;
X break;
X }
X break;
X case Ogrow:
X if(state != Cnone) {
X print("cwgrow with state = %s\n",
X cwnames[state]);
X break;
X }
X c->state = Cdirty;
X break;
X case Odump:
X if(state != Cdirty) { /* BOTCH */
X print("cwdump with state = %s\n",
X cwnames[state]);
X break;
X }
X c->state = Cdump;
X cw->ndump++; /* only called from dump command */
X break;
X case Orele:
X if(state != Cwrite) {
X if(state != Cdump1)
X print("cwrele with state = %s\n",
X cwnames[state]);
X break;
X }
X c->state = Cnone;
X break;
X case Ofree:
X if(state == Cwrite || state == Cread)
X c->state = Cnone;
X break;
X }
X if(DEBUG)
X print("cwio: %ld s=%s o=%s ns=%s\n",
X addr, cwnames[state],
X cwnames[opcode],
X cwnames[c->state]);
X putbuf(p);
X if(newmax) {
X cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bres);
X h = (Cache*)cb->iobuf;
X if(newmax > h->wmax)
X h->wmax = newmax;
X putbuf(cb);
X }
X return state;
bad:
X print("cw state = %s; cw opcode = %s",
X cwnames[state], cwnames[opcode]);
X return Cerror;
X}
extern Filsys* dev2fs(Device *dev);
int
cwgrow(Device *dev, Superb *sb, int uid)
X{
X char str[NAMELEN];
X Iobuf *cb;
X Cache *h;
X Filsys *filsys;
X long fs, nfs, ws;
X cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bmod|Bres);
X h = (Cache*)cb->iobuf;
X ws = h->wsize;
X fs = h->fsize;
X if(fs >= ws)
X return 0;
X nfs = fs + ADDFREE;
X if(nfs >= ws)
X nfs = ws;
X h->fsize = nfs;
X putbuf(cb);
X sb->fsize = nfs;
X filsys = dev2fs(dev);
X if (filsys == nil)
X print("%Z", dev);
X else
X print("%s", filsys->name);
X uidtostr(str, uid, 1);
X print(" grow from %ld to %ld limit %ld by %s uid=%d\n",
X fs, nfs, ws, str, uid);
X for(nfs--; nfs>=fs; nfs--) {
X switch(cwio(dev, nfs, 0, Ogrow)) {
X case Cerror:
X return 0;
X case Cnone:
X addfree(dev, nfs, sb);
X }
X }
X return 1;
X}
int
cwfree(Device *dev, long addr)
X{
X int state;
X if(dev->type == Devcw) {
X state = cwio(dev, addr, 0, Ofree);
X if(state != Cdirty)
X return 1; /* do not put in freelist */
X }
X return 0; /* put in freelist */
X}
int
bktcheck(Bucket *b)
X{
X Centry *c, *c1, *c2, *ce;
X int err;
X err = 0;
X if(b->agegen < CEPERBK || b->agegen > MAXAGE) {
X print("agegen %ld\n", b->agegen);
X err = 1;
X }
X ce = b->entry + CEPERBK;
X c1 = 0; /* lowest age last pass */
X for(;;) {
X c2 = 0; /* lowest age this pass */
X for(c = b->entry; c < ce; c++) {
X if(c1 != 0 && c != c1) {
X if(c->age == c1->age) {
X print("same age %d\n", c->age);
X err = 1;
X }
X if(c1->waddr == c->waddr)
X if(c1->state != Cnone)
X if(c->state != Cnone) {
X print("same waddr %ld\n", c->waddr);
X err = 1;
X }
X }
X if(c1 != 0 && c->age <= c1->age)
X continue;
X if(c2 == 0 || c->age < c2->age)
X c2 = c;
X }
X if(c2 == 0)
X break;
X c1 = c2;
X if(c1->age >= b->agegen) {
X print("age >= generator %d %ld\n", c1->age, b->agegen);
X err = 1;
X }
X }
X return err;
X}
void
resequence(Bucket *b)
X{
X Centry *c, *ce, *cr;
X int age, i;
X ce = b->entry + CEPERBK;
X for(c = b->entry; c < ce; c++) {
X c->age += CEPERBK;
X if(c->age < CEPERBK)
X c->age = MAXAGE;
X }
X b->agegen += CEPERBK;
X age = 0;
X for(i=0;; i++) {
X cr = 0;
X for(c = b->entry; c < ce; c++) {
X if(c->age < i)
X continue;
X if(cr == 0 || c->age < age) {
X cr = c;
X age = c->age;
X }
X }
X if(cr == 0)
X break;
X cr->age = i;
X }
X b->agegen = i;
X cons.nreseq++;
X}
Centry*
getcentry(Bucket *b, long addr)
X{
X Centry *c, *ce, *cr;
X int s, age;
X /*
X * search for cache hit
X * find oldest block as byproduct
X */
X ce = b->entry + CEPERBK;
X age = 0;
X cr = 0;
X for(c = b->entry; c < ce; c++) {
X s = c->state;
X if(s == Cnone) {
X cr = c;
X age = 0;
X continue;
X }
X if(c->waddr == addr)
X goto found;
X if(s == Cread) {
X if(cr == 0 || c->age < age) {
X cr = c;
X age = c->age;
X }
X }
X }
X /*
X * remap entry
X */
X c = cr;
X if(c == 0)
X return 0; /* bucket is full */
X c->state = Cnone;
X c->waddr = addr;
found:
X /*
X * update the age to get filo cache.
X * small number in age means old
X */
X if(!cons.noage || c->state == Cnone) {
X age = b->agegen;
X c->age = age;
X age++;
X b->agegen = age;
X if(age < 0 || age >= MAXAGE)
X resequence(b);
X }
X return c;
X}
X/*
X * ream the cache
X * calculate new buckets
X */
Iobuf*
cacheinit(Device *dev)
X{
X Iobuf *cb, *p;
X Cache *h;
X Device *cdev;
X long m;
X print("cache init %Z\n", dev);
X cdev = CDEV(dev);
X devinit(cdev);
X cb = getbuf(cdev, CACHE_ADDR, Bmod|Bres);
X memset(cb->iobuf, 0, RBUFSIZE);
X settag(cb, Tcache, QPSUPER);
X h = (Cache*)cb->iobuf;
X /*
X * calculate csize such that
X * tsize = msize/BKPERBLK + csize and
X * msize = csize/CEPERBK
X */
X h->maddr = CACHE_ADDR + 1;
X m = devsize(cdev) - h->maddr;
X h->csize = ((long long)(m-1) * CEPERBK*BKPERBLK) / (CEPERBK*BKPERBLK+1);
X h->msize = h->csize/CEPERBK - 5;
X while(!prime(h->msize))
X h->msize--;
X h->csize = h->msize*CEPERBK;
X h->caddr = h->maddr + (h->msize+BKPERBLK-1)/BKPERBLK;
X h->wsize = devsize(WDEV(dev));
X if(h->msize <= 0)
X panic("cache too small");
X if(h->caddr + h->csize > m)
X panic("cache size error");
X /*
X * setup cache map
X */
X for(m=h->maddr; m<h->caddr; m++) {
X p = getbuf(cdev, m, Bmod);
X memset(p->iobuf, 0, RBUFSIZE);
X settag(p, Tbuck, m);
X putbuf(p);
X }
X print("done cacheinit\n");
X return cb;
X}
long
getstartsb(Device *dev)
X{
X Filsys *f;
X Startsb *s;
X for(f=filsys; f->name; f++)
X if(devcmpr(f->dev, dev) == 0)
X goto found;
print("getstartsb: not found 1 %Z\n", dev);
X return FIRST;
found:
X for(s=startsb; s->name; s++)
X if(strcmp(f->name, s->name) == 0)
X return s->startsb;
print("getstartsb: not found 2 %Z %s\n", dev, f->name);
X return FIRST;
X}
X/*
X * ream the cache
X * calculate new buckets
X * get superblock from
X * last worm dump block.
X */
void
cwrecover(Device *dev)
X{
X Iobuf *p, *cb;
X Cache *h;
X Superb *s;
X long m, baddr;
X Device *wdev;
X cwinit1(dev);
X wdev = WDEV(dev);
X p = getbuf(devnone, Cwxx1, 0);
X s = (Superb*)p->iobuf;
X baddr = 0;
X m = getstartsb(dev);
X localconfinit();
X if(conf.firstsb)
X m = conf.firstsb;
X for(;;) {
X memset(p->iobuf, 0, RBUFSIZE);
X if(devread(wdev, m, p->iobuf) ||
X checktag(p, Tsuper, QPSUPER))
X break;
X baddr = m;
X m = s->next;
X print("dump %ld is good; %ld next\n", baddr, m);
X if(baddr == conf.recovsb)
X break;
X }
X putbuf(p);
X if(!baddr)
X panic("recover: no superblock\n");
X p = getbuf(wdev, baddr, Bread);
X s = (Superb*)p->iobuf;
X cb = cacheinit(dev);
X h = (Cache*)cb->iobuf;
X h->sbaddr = baddr;
X h->cwraddr = s->cwraddr;
X h->roraddr = s->roraddr;
X h->fsize = s->fsize + 100; /* this must be conservative */
X if(conf.recovcw)
X h->cwraddr = conf.recovcw;
X if(conf.recovro)
X h->roraddr = conf.recovro;
X putbuf(cb);
X putbuf(p);
X p = getbuf(dev, baddr, Bread|Bmod);
X s = (Superb*)p->iobuf;
X memset(&s->fbuf, 0, sizeof(s->fbuf));
X s->fbuf.free[0] = 0;
X s->fbuf.nfree = 1;
X s->tfree = 0;
X if(conf.recovcw)
X s->cwraddr = conf.recovcw;
X if(conf.recovro)
X s->roraddr = conf.recovro;
X putbuf(p);
X print("done recover\n");
X}
X/*
X * ream the cache
X * calculate new buckets
X * initialize superblock.
X */
void
cwream(Device *dev)
X{
X Iobuf *p, *cb;
X Cache *h;
X Superb *s;
X long m, baddr;
X Device *cdev;
X print("cwream %Z\n", dev);
X cwinit1(dev);
X cdev = CDEV(dev);
X devinit(cdev);
X baddr = FIRST; /* baddr = super addr
X baddr+1 = cw root
X baddr+2 = ro root
X baddr+3 = reserved next superblock */
X cb = cacheinit(dev);
X h = (Cache*)cb->iobuf;
X h->sbaddr = baddr;
X h->cwraddr = baddr+1;
X h->roraddr = baddr+2;
X h->fsize = 0; /* prevents superream from freeing */
X putbuf(cb);
X for(m=0; m<3; m++)
X cwio(dev, baddr+m, 0, Ogrow);
X superream(dev, baddr);
X rootream(dev, baddr+1); /* cw root */
X rootream(dev, baddr+2); /* ro root */
X cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres);
X h = (Cache*)cb->iobuf;
X h->fsize = baddr+4;
X putbuf(cb);
X p = getbuf(dev, baddr, Bread|Bmod|Bimm);
X s = (Superb*)p->iobuf;
X s->last = baddr;
X s->cwraddr = baddr+1;
X s->roraddr = baddr+2;
X s->next = baddr+3;
X s->fsize = baddr+4;
X putbuf(p);
X for(m=0; m<3; m++)
X cwio(dev, baddr+m, 0, Odump);
X}
long
rewalk1(Cw *cw, long addr, int slot, Wpath *up)
X{
X Iobuf *p, *p1;
X Dentry *d;
X if(up == 0)
X return cwraddr(cw->dev);
X up->addr = rewalk1(cw, up->addr, up->slot, up->up);
X p = getbuf(cw->dev, up->addr, Bread|Bmod);
X d = getdir(p, up->slot);
X if(!d || !(d->mode & DALLOC)) {
X print("rewalk1 1\n");
X if(p)
X putbuf(p);
X return addr;
X }
X p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0);
X if(!p1) {
X print("rewalk1 2\n");
X if(p)
X putbuf(p);
X return addr;
X }
X if(DEBUG)
X print("rewalk1 %ld to %ld \"%s\"\n",
X addr, p1->addr, d->name);
X addr = p1->addr;
X p1->flags |= Bmod;
X putbuf(p1);
X putbuf(p);
X return addr;
X}
long
rewalk2(Cw *cw, long addr, int slot, Wpath *up)
X{
X Iobuf *p, *p1;
X Dentry *d;
X if(up == 0)
X return cwraddr(cw->rodev);
X up->addr = rewalk2(cw, up->addr, up->slot, up->up);
X p = getbuf(cw->rodev, up->addr, Bread);
X d = getdir(p, up->slot);
X if(!d || !(d->mode & DALLOC)) {
X print("rewalk2 1\n");
X if(p)
X putbuf(p);
X return addr;
X }
X p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0);
X if(!p1) {
X print("rewalk2 2\n");
X if(p)
X putbuf(p);
X return addr;
X }
X if(DEBUG)
X print("rewalk2 %ld to %ld \"%s\"\n",
X addr, p1->addr, d->name);
X addr = p1->addr;
X putbuf(p1);
X putbuf(p);
X return addr;
X}
void
rewalk(Cw *cw)
X{
X int h;
X File *f;
X for(h=0; h<nelem(flist); h++) {
X for(f=flist[h]; f; f=f->next) {
X if(!f->fs)
X continue;
X if(cw->dev == f->fs->dev)
X f->addr = rewalk1(cw, f->addr, f->slot, f->wpath);
X else
X if(cw->rodev == f->fs->dev)
X f->addr = rewalk2(cw, f->addr, f->slot, f->wpath);
X }
X }
X}
long
split(Cw *cw, Iobuf *p, long addr)
X{
X long na;
X int state;
X na = 0;
X if(p && (p->flags & Bmod)) {
X p->flags |= Bimm;
X putbuf(p);
X p = 0;
X }
X state = cwio(cw->dev, addr, 0, Onone); /* read the state (twice?) */
X switch(state)
X {
X default:
X panic("split: unknown state %s", cwnames[state]);
X case Cerror:
X case Cnone:
X case Cdump:
X case Cread:
X break;
X case Cdump1:
X case Cwrite:
X /*
X * botch.. could be done by relabeling
X */
X if(!p) {
X p = getbuf(cw->dev, addr, Bread);
X if(!p) {
X print("split: null getbuf\n");
X break;
X }
X }
X na = cw->fsize;
X cw->fsize = na+1;
X cwio(cw->dev, na, 0, Ogrow);
X cwio(cw->dev, na, p->iobuf, Owrite);
X cwio(cw->dev, na, 0, Odump);
X cwio(cw->dev, addr, 0, Orele);
X break;
X case Cdirty:
X cwio(cw->dev, addr, 0, Odump);
X break;
X }
X if(p)
X putbuf(p);
X return na;
X}
int
isdirty(Cw *cw, Iobuf *p, long addr, int tag)
X{
X int s;
X if(p && (p->flags & Bmod))
X return 1;
X s = cwio(cw->dev, addr, 0, Onone);
X if(s == Cdirty || s == Cwrite)
X return 1;
X if(tag == Tind1 || tag == Tind2) /* botch, get these modified */
X if(s != Cnone)
X return 1;
X return 0;
X}
long
cwrecur(Cw *cw, long addr, int tag, int tag1, long qp)
X{
X Iobuf *p;
X Dentry *d;
X int i, j, shouldstop;
X long na;
X char *np;
X shouldstop = 0;
X p = getbuf(cw->dev, addr, Bprobe);
X if(!isdirty(cw, p, addr, tag)) {
X if(!cw->all) {
X if(DEBUG)
X print("cwrecur: %ld t=%s not dirty %s\n",
X addr, tagnames[tag], cw->name);
X if(p)
X putbuf(p);
X return 0;
X }
X shouldstop = 1;
X }
X if(DEBUG)
X print("cwrecur: %ld t=%s %s\n",
X addr, tagnames[tag], cw->name);
X if(cw->depth >= 100) {
X print("dump depth too great %s\n", cw->name);
X if(p)
X putbuf(p);
X return 0;
X }
X cw->depth++;
X switch(tag)
X {
X default:
X print("cwrecur: unknown tag %d %s\n", tag, cw->name);
X case Tfile:
X break;
X case Tsuper:
X case Tdir:
X if(!p) {
X p = getbuf(cw->dev, addr, Bread);
X if(!p) {
X print("cwrecur: Tdir p null %s\n",
X cw->name);
X break;
X }
X }
X if(tag == Tdir) {
X cw->namepad[0] = 0; /* force room */
X np = strchr(cw->name, 0);
X *np++ = '/';
X } else {
X np = 0; /* set */
X cw->name[0] = 0;
X }
X for(i=0; i<DIRPERBUF; i++) {
X d = getdir(p, i);
X if(!(d->mode & DALLOC))
X continue;
X qp = d->qid.path & ~QPDIR;
X if(tag == Tdir)
X strncpy(np, d->name, NAMELEN);
X else
X if(i > 0)
X print("cwrecur: root with >1 directory\n");
X tag1 = Tfile;
X if(d->mode & DDIR)
X tag1 = Tdir;
X for(j=0; j<NDBLOCK; j++) {
X if(na = d->dblock[j]) {
X na = cwrecur(cw, na, tag1, 0, qp);
X if(na) {
X d->dblock[j] = na;
X p->flags |= Bmod;
X }
X }
X }
X if(na = d->iblock) {
X na = cwrecur(cw, na, Tind1, tag1, qp);
X if(na) {
X d->iblock = na;
X p->flags |= Bmod;
X }
X }
X if(na = d->diblock) {
X na = cwrecur(cw, na, Tind2, tag1, qp);
X if(na) {
X d->diblock = na;
X p->flags |= Bmod;
X }
X }
X }
X break;
X case Tind1:
X j = tag1;
X tag1 = 0;
X goto tind;
X case Tind2:
X j = Tind1;
X tind:
X if(!p) {
X p = getbuf(cw->dev, addr, Bread);
X if(!p) {
X print("cwrecur: Tind p null %s\n",
X cw->name);
X break;
X }
X }
X for(i=0; i<INDPERBUF; i++) {
X if(na = ((long*)p->iobuf)[i]) {
X na = cwrecur(cw, na, j, tag1, qp);
X if(na) {
X ((long*)p->iobuf)[i] = na;
X p->flags |= Bmod;
X }
X }
X }
X break;
X }
X na = split(cw, p, addr);
X cw->depth--;
X if(na && shouldstop) {
X if(cw->falsehits < 10)
X print("shouldstop %ld %ld t=%s %s\n",
X addr, na, tagnames[tag], cw->name);
X cw->falsehits++;
X }
X return na;
X}
void
cfsdump(Filsys *fs)
X{
X Iobuf *pr, *p1, *p;
X Dentry *dr, *d1, *d;
X Cache *h;
X Superb *s;
X long orba, rba, oroa, roa, sba, a, m, n, i, tim;
X char tstr[20];
X Cw *cw;
X if(fs->dev->type != Devcw) {
X print("cant dump; not cw device: %Z\n", fs->dev);
X return;
X }
X cw = fs->dev->private;
X if(cw == 0) {
X print("cant dump: has not been inited: %Z\n", fs->dev);
X return;
X }
X tim = toytime();
X wlock(&mainlock); /* dump */
X /*
X * set up static structure
X * with frequent variables
X */
X cw->ndump = 0;
X cw->name[0] = 0;
X cw->depth = 0;
X /*
X * cw root
X */
X sync("before dump");
X cw->fsize = cwsize(cw->dev);
X orba = cwraddr(cw->dev);
X print("cwroot %ld", orba);
X cons.noage = 1;
X cw->all = cw->allflag;
X rba = cwrecur(cw, orba, Tsuper, 0, QPROOT);
X if(rba == 0)
X rba = orba;
X print("->%ld\n", rba);
X sync("after cw");
X /*
X * partial super block
X */
X p = getbuf(cw->dev, cwsaddr(cw->dev), Bread|Bmod|Bimm);
X s = (Superb*)p->iobuf;
X s->fsize = cw->fsize;
X s->cwraddr = rba;
X putbuf(p);
X /*
X * partial cache block
X */
X p = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bimm|Bres);
X h = (Cache*)p->iobuf;
X h->fsize = cw->fsize;
X h->cwraddr = rba;
X putbuf(p);
X /*
X * ro root
X */
X oroa = cwraddr(cw->rodev);
X pr = getbuf(cw->dev, oroa, Bread|Bmod);
X dr = getdir(pr, 0);
X datestr(tstr, time()); /* tstr = "yyyymmdd" */
X n = 0;
X for(a=0;; a++) {
X p1 = dnodebuf(pr, dr, a, Tdir, 0);
X if(!p1)
X goto bad;
X n++;
X for(i=0; i<DIRPERBUF; i++) {
X d1 = getdir(p1, i);
X if(!d1)
X goto bad;
X if(!(d1->mode & DALLOC))
X goto found1;
X if(!memcmp(d1->name, tstr, 4))
X goto found2; /* found entry */
X }
X putbuf(p1);
X }
X /*
X * no year directory, create one
X */
found1:
X p = getbuf(cw->dev, rba, Bread);
X d = getdir(p, 0);
X d1->qid = d->qid;
X d1->qid.version += n;
X memmove(d1->name, tstr, 4);
X d1->mode = d->mode;
X d1->uid = d->uid;
X d1->gid = d->gid;
X putbuf(p);
X accessdir(p1, d1, FWRITE, 0);
X /*
X * put mmdd[count] in year directory
X */
found2:
X accessdir(p1, d1, FREAD, 0);
X putbuf(pr);
X pr = p1;
X dr = d1;
X n = 0;
X m = 0;
X for(a=0;; a++) {
X p1 = dnodebuf(pr, dr, a, Tdir, 0);
X if(!p1)
X goto bad;
X n++;
X for(i=0; i<DIRPERBUF; i++) {
X d1 = getdir(p1, i);
X if(!d1)
X goto bad;
X if(!(d1->mode & DALLOC))
X goto found;
X if(!memcmp(d1->name, tstr+4, 4))
X m++;
X }
X putbuf(p1);
X }
X /*
X * empty slot put in root
X */
found:
X if(m) /* how many dumps this date */
X sprint(tstr+8, "%ld", m);
X p = getbuf(cw->dev, rba, Bread);
X d = getdir(p, 0);
X *d1 = *d; /* qid is QPROOT */
X putbuf(p);
X strcpy(d1->name, tstr+4);
X d1->qid.version += n;
X accessdir(p1, d1, FWRITE, 0);
X putbuf(p1);
X putbuf(pr);
X cw->fsize = cwsize(cw->dev);
X oroa = cwraddr(cw->rodev); /* probably redundant */
X print("roroot %ld", oroa);
X cons.noage = 0;
X cw->all = 0;
X roa = cwrecur(cw, oroa, Tsuper, 0, QPROOT);
X if(roa == 0) {
X print("[same]");
X roa = oroa;
X }
X print("->%ld /%.4s/%s\n", roa, tstr, tstr+4);
X sync("after ro");
X /*
X * final super block
X */
X a = cwsaddr(cw->dev);
X print("sblock %ld", a);
X p = getbuf(cw->dev, a, Bread|Bmod|Bimm);
X s = (Superb*)p->iobuf;
X s->last = a;
X sba = s->next;
X s->next = cw->fsize;
X cw->fsize++;
X s->fsize = cw->fsize;
X s->roraddr = roa;
X cwio(cw->dev, sba, 0, Ogrow);
X cwio(cw->dev, sba, p->iobuf, Owrite);
X cwio(cw->dev, sba, 0, Odump);
X print("->%ld (->%ld)\n", sba, s->next);
X putbuf(p);
X /*
X * final cache block
X */
X p = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bimm|Bres);
X h = (Cache*)p->iobuf;
X h->fsize = cw->fsize;
X h->roraddr = roa;
X h->sbaddr = sba;
X putbuf(p);
X rewalk(cw);
X sync("all done");
X print("%ld blocks queued for worm\n", cw->ndump);
X print("%ld falsehits\n", cw->falsehits);
X cw->nodump = 0;
X /*
X * extend all of the locks
X */
X tim = toytime() - tim;
X for(i=0; i<NTLOCK; i++)
X if(tlocks[i].time > 0)
X tlocks[i].time += tim;
X wunlock(&mainlock);
X return;
bad:
X panic("dump: bad");
X}
void
mvstates(Device *dev, int s1, int s2, int side)
X{
X Iobuf *p, *cb;
X Cache *h;
X Bucket *b;
X Centry *c, *ce;
X long m, lo, hi, msize, maddr;
X Cw *cw;
X cw = dev->private;
X lo = 0;
X hi = lo + devsize(dev->cw.w); /* size of all sides totalled */
X if(side >= 0) {
X /* operate on only a single disc side */
X Sidestarts ss;
X wormsidestarts(dev, side, &ss);
X lo = ss.sstart;
X hi = ss.s1start;
X }
X cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bres);
X if(!cb || checktag(cb, Tcache, QPSUPER))
X panic("cwstats: checktag c bucket");
X h = (Cache*)cb->iobuf;
X msize = h->msize;
X maddr = h->maddr;
X putbuf(cb);
X for(m=0; m<msize; m++) {
X p = getbuf(cw->cdev, maddr + m/BKPERBLK, Bread|Bmod);
X if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
X panic("cwtest: checktag c bucket");
X b = (Bucket*)p->iobuf + m%BKPERBLK;
X ce = b->entry + CEPERBK;
X for(c=b->entry; c<ce; c++)
X if(c->state == s1 && c->waddr >= lo && c->waddr < hi)
X c->state = s2;
X putbuf(p);
X }
X}
void
prchain(Device *dev, long m, int flg)
X{
X Iobuf *p;
X Superb *s;
X if(m == 0) {
X if(flg)
X m = cwsaddr(dev);
X else
X m = getstartsb(dev);
X }
X p = getbuf(devnone, Cwxx2, 0);
X s = (Superb*)p->iobuf;
X for(;;) {
X memset(p->iobuf, 0, RBUFSIZE);
X if(devread(WDEV(dev), m, p->iobuf) ||
X checktag(p, Tsuper, QPSUPER))
X break;
X if(flg) {
X print("dump %ld is good; %ld prev\n", m, s->last);
X print("\t%ld cwroot; %ld roroot\n", s->cwraddr, s->roraddr);
X if(m <= s->last)
X break;
X m = s->last;
X } else {
X print("dump %ld is good; %ld next\n", m, s->next);
X print("\t%ld cwroot; %ld roroot\n", s->cwraddr, s->roraddr);
X if(m >= s->next)
X break;
X m = s->next;
X }
X }
X putbuf(p);
X}
void
touchsb(Device *dev)
X{
X Iobuf *p;
X long m;
X m = cwsaddr(dev);
X p = getbuf(devnone, Cwxx2, 0);
X memset(p->iobuf, 0, RBUFSIZE);
X if(devread(WDEV(dev), m, p->iobuf) ||
X checktag(p, Tsuper, QPSUPER))
X print("WORM SUPER BLOCK READ FAILED\n");
X else
X print("touch superblock %ld\n", m);
X putbuf(p);
X}
void
storesb(Device *dev, long last, int doit)
X{
X Iobuf *ph, *ps;
X Cache *h;
X Superb *s;
X long sbaddr, qidgen;
X sbaddr = cwsaddr(dev);
X ps = getbuf(devnone, Cwxx2, 0);
X if(!ps) {
X print("sbstore: getbuf\n");
X return;
X }
X /*
X * try to read last sb
X */
X memset(ps->iobuf, 0, RBUFSIZE);
X if(devread(WDEV(dev), last, ps->iobuf) ||
X checktag(ps, Tsuper, QPSUPER))
X print("read last failed\n");
X else
X print("read last succeeded\n");
X s = (Superb*)ps->iobuf;
X qidgen = s->qidgen;
X if(qidgen == 0)
X qidgen = 0x31415;
X qidgen += 1000;
X if(s->next != sbaddr)
X print("next(last) is not sbaddr %ld %ld\n",
X s->next, sbaddr);
X else
X print("next(last) is sbaddr\n");
X /*
X * read cached superblock
X */
X ph = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
X if(!ph || checktag(ph, Tcache, QPSUPER)) {
X print("cwstats: checktag c bucket\n");
X if(ph)
X putbuf(ph);
X putbuf(ps);
X return;
X } else
X print("read cached sb succeeded\n");
X
X h = (Cache*)ph->iobuf;
X memset(ps->iobuf, 0, RBUFSIZE);
X settag(ps, Tsuper, QPSUPER);
X ps->flags = 0;
X s = (Superb*)ps->iobuf;
X s->cwraddr = h->cwraddr;
X s->roraddr = h->roraddr;
X s->fsize = h->fsize;
X s->fstart = 2;
X s->last = last;
X s->next = h->roraddr+1;
X s->qidgen = qidgen;
X putbuf(ph);
X if(s->fsize-1 != s->next ||
X s->fsize-2 != s->roraddr ||
X s->fsize-5 != s->cwraddr) {
X print("addrs not in relationship %ld %ld %ld %ld\n",
X s->cwraddr, s->roraddr, s->next, s->fsize);
X putbuf(ps);
X return;
X } else
X print("addresses in relation\n");
X if(doit)
X if(devwrite(WDEV(dev), sbaddr, ps->iobuf))
X print("WORM SUPER BLOCK WRITE FAILED\n");
X ps->flags = 0;
X putbuf(ps);
X}
void
savecache(Device *dev)
X{
X Iobuf *p, *cb;
X Cache *h;
X Bucket *b;
X Centry *c, *ce;
X long m, n, maddr, msize, left, *longp, nbyte;
X Device *cdev;
X if(walkto("/adm/cache"))
X goto bad;
X if(con_open(FID2, OWRITE|OTRUNC))
X goto bad;
X cdev = CDEV(dev);
X cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
X if(!cb || checktag(cb, Tcache, QPSUPER))
X panic("savecache: checktag c bucket");
X h = (Cache*)cb->iobuf;
X msize = h->msize;
X maddr = h->maddr;
X putbuf(cb);
X n = BUFSIZE; /* calculate write size */
X if(n > MAXDAT)
X n = MAXDAT;
X cb = getbuf(devnone, Cwxx4, 0);
X longp = (long*)cb->iobuf;
X left = n/sizeof(long);
X cons.offset = 0;
X for(m=0; m<msize; m++) {
X if(left < BKPERBLK) {
X nbyte = (n/sizeof(long) - left) * sizeof(long);
X con_write(FID2, cb->iobuf, cons.offset, nbyte);
X cons.offset += nbyte;
X longp = (long*)cb->iobuf;
X left = n/sizeof(long);
X }
X p = getbuf(cdev, maddr + m/BKPERBLK, Bread);
X if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
X panic("cwtest: checktag c bucket");
X b = (Bucket*)p->iobuf + m%BKPERBLK;
X ce = b->entry + CEPERBK;
X for(c=b->entry; c<ce; c++)
X if(c->state == Cread) {
X *longp++ = c->waddr;
X left--;
X }
X putbuf(p);
X }
X nbyte = (n/sizeof(long) - left) * sizeof(long);
X con_write(FID2, cb->iobuf, cons.offset, nbyte);
X putbuf(cb);
X return;
bad:
X print("cant open /adm/cache\n");
X}
void
loadcache(Device *dev, int dskno)
X{
X Iobuf *p, *cb;
X long m, nbyte, *longp, count;
X Sidestarts ss;
X if(walkto("/adm/cache"))
X goto bad;
X if(con_open(FID2, OREAD))
X goto bad;
X cb = getbuf(devnone, Cwxx4, 0);
X cons.offset = 0;
X count = 0;
X if (dskno >= 0)
X wormsidestarts(dev, dskno, &ss);
X for(;;) {
X memset(cb->iobuf, 0, BUFSIZE);
X nbyte = con_read(FID2, cb->iobuf, cons.offset, 100) / sizeof(long);
X if(nbyte <= 0)
X break;
X cons.offset += nbyte * sizeof(long);
X longp = (long*)cb->iobuf;
X while(nbyte > 0) {
X m = *longp++;
X nbyte--;
X if(m == 0)
X continue;
X /* if given a diskno, restrict to just that disc side */
X if(dskno < 0 || m >= ss.sstart && m < ss.s1start) {
X p = getbuf(dev, m, Bread);
X if(p)
X putbuf(p);
X count++;
X }
X }
X }
X putbuf(cb);
X print("%ld blocks loaded from worm %d\n", count, dskno);
X return;
bad:
X print("cant open /adm/cache\n");
X}
void
morecache(Device *dev, int dskno, long size)
X{
X Iobuf *p;
X long m, ml, mh, mm, count;
X Cache *h;
X Sidestarts ss;
X p = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
X if(!p || checktag(p, Tcache, QPSUPER))
X panic("savecache: checktag c bucket");
X h = (Cache*)p->iobuf;
X mm = h->wmax;
X putbuf(p);
X wormsidestarts(dev, dskno, &ss);
X ml = ss.sstart; /* start at beginning of disc side #dskno */
X mh = ml + size;
X if(mh > mm) {
X mh = mm;
X print("limited to %ld\n", mh-ml);
X }
X count = 0;
X for(m=ml; m < mh; m++) {
X p = getbuf(dev, m, Bread);
X if(p)
X putbuf(p);
X count++;
X }
X print("%ld blocks loaded from worm %d\n", count, dskno);
X}
void
blockcmp(Device *dev, long wa, long ca)
X{
X Iobuf *p1, *p2;
X int i, c;
X p1 = getbuf(WDEV(dev), wa, Bread);
X if(!p1) {
X print("dowcmp: wdev error\n");
X return;
X }
X p2 = getbuf(CDEV(dev), ca, Bread);
X if(!p2) {
X print("dowcmp: cdev error\n");
X putbuf(p1);
X return;
X }
X c = 0;
X for(i=0; i<RBUFSIZE; i++)
X if(p1->iobuf[i] != p2->iobuf[i]) {
X print("%4d: %.2x %.2x\n",
X i,
X p1->iobuf[i]&0xff,
X p2->iobuf[i]&0xff);
X c++;
X if(c >= 10)
X break;
X }
X if(c == 0)
X print("no error\n");
X putbuf(p1);
X putbuf(p2);
X}
void
wblock(Device *dev, long addr)
X{
X Iobuf *p1;
X int i;
X p1 = getbuf(dev, addr, Bread);
X if(p1) {
X i = devwrite(WDEV(dev), addr, p1->iobuf);
X print("i = %d\n", i);
X putbuf(p1);
X }
X}
void
cwtest(Device*)
X{
X}
X#ifdef XXX
X/* garbage to change sb size
X * probably will need it someday
X */
X fsz = number(0, 0, 10);
X count = 0;
X if(fsz == number(0, -1, 10))
X count = -1; /* really do it */
X print("fsize = %ld\n", fsz);
X cdev = CDEV(dev);
X cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
X if(!cb || checktag(cb, Tcache, QPSUPER))
X panic("cwstats: checktag c bucket");
X h = (Cache*)cb->iobuf;
X for(m=0; m<h->msize; m++) {
X p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bmod);
X if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
X panic("cwtest: checktag c bucket");
X b = (Bucket*)p->iobuf + m%BKPERBLK;
X ce = b->entry + CEPERBK;
X for(c=b->entry; c<ce; c++) {
X if(c->waddr < fsz)
X continue;
X if(count < 0) {
X c->state = Cnone;
X continue;
X }
X if(c->state != Cdirty)
X count++;
X }
X putbuf(p);
X }
X if(count < 0) {
X print("old cache hsize = %ld\n", h->fsize);
X h->fsize = fsz;
X cb->flags |= Bmod;
X p = getbuf(dev, h->sbaddr, Bread|Bmod);
X s = (Superb*)p->iobuf;
X print("old super hsize = %ld\n", s->fsize);
X s->fsize = fsz;
X putbuf(p);
X }
X putbuf(cb);
X print("count = %ld\n", count);
X#endif
int
convstate(char *name)
X{
X int i;
X for(i=0; i<nelem(cwnames); i++)
X if(cwnames[i])
X if(strcmp(cwnames[i], name) == 0)
X return i;
X return -1;
X}
void
searchtag(Device *d, long a, int tag, int n)
X{
X Iobuf *p;
X Tag *t;
X int i;
X if(a == 0)
X a = getstartsb(d);
X p = getbuf(devnone, Cwxx2, 0);
X t = (Tag*)(p->iobuf+BUFSIZE);
X for(i=0; i<n; i++) {
X memset(p->iobuf, 0, RBUFSIZE);
X if(devread(WDEV(d), a+i, p->iobuf)) {
X if(n == 1000)
X break;
X continue;
X }
X if(t->tag == tag) {
X print("tag %d found at %Z %ld\n", tag, d, a+i);
X break;
X }
X }
X putbuf(p);
X}
void
cmd_cwcmd(int argc, char *argv[])
X{
X Device *dev;
X char *arg;
X long s1, s2, a, b, n;
X Cw *cw;
X char str[28];
X if(argc <= 1) {
X print(" cwcmd mvstate state1 state2 [platter]\n");
X print(" cwcmd prchain [start] [bakflg]\n");
X print(" cwcmd searchtag [start] [tag]\n");
X print(" cwcmd touchsb\n");
X print(" cwcmd savecache\n");
X print(" cwcmd loadcache [dskno]\n");
X print(" cwcmd morecache dskno [count]\n");
X print(" cwcmd blockcmp wbno cbno\n");
X print(" cwcmd startdump [01]\n");
X print(" cwcmd acct\n");
X print(" cwcmd clearacct\n");
X goto out;
X }
X arg = argv[1];
X /*
X * items not depend on a cw filesystem
X */
X if(strcmp(arg, "acct") == 0) {
X for(a=0; a<nelem(growacct); a++) {
X b = growacct[a];
X if(b) {
X uidtostr(str, a, 1);
X print("%10ld %s\n",
X (b*ADDFREE*RBUFSIZE+500000)/1000000,
X str);
X }
X }
X goto out;
X }
X if(strcmp(arg, "clearacct") == 0) {
X memset(growacct, 0, sizeof(growacct));
X goto out;
X }
X /*
X * items depend on cw filesystem
X */
X dev = cons.curfs->dev;
X if(dev == 0 || dev->type != Devcw || dev->private == 0) {
X print("cfs not a cw filesystem: %Z\n", dev);
X goto out;
X }
X cw = dev->private;
X if(strcmp(arg, "searchtag") == 0) {
X a = 0;
X if(argc > 2)
X a = number(argv[2], 0, 10);
X b = Tsuper;
X if(argc > 3)
X b = number(argv[3], 0, 10);
X n = 1000;
X if(argc > 4)
X n = number(argv[4], 0, 10);
X searchtag(dev, a, b, n);
X goto out;
X }
X if(strcmp(arg, "mvstate") == 0) {
X if(argc < 4)
X goto bad;
X s1 = convstate(argv[2]);
X s2 = convstate(argv[3]);
X if(s1 < 0 || s2 < 0)
X goto bad;
X a = -1;
X if(argc > 4)
X a = number(argv[4], 0, 10);
X mvstates(dev, s1, s2, a);
X goto out;
X bad:
X print("cwcmd mvstate: bad args\n");
X goto out;
X }
X if(strcmp(arg, "prchain") == 0) {
X a = 0;
X if(argc > 2)
X a = number(argv[2], 0, 10);
X s1 = 0;
X if(argc > 3)
X s1 = number(argv[3], 0, 10);
X prchain(dev, a, s1);
X goto out;
X }
X if(strcmp(arg, "touchsb") == 0) {
X touchsb(dev);
X goto out;
X }
X if(strcmp(arg, "savecache") == 0) {
X savecache(dev);
X goto out;
X }
X if(strcmp(arg, "loadcache") == 0) {
X s1 = -1;
X if(argc > 2)
X s1 = number(argv[2], 0, 10);
X loadcache(dev, s1);
X goto out;
X }
X if(strcmp(arg, "morecache") == 0) {
X if(argc <= 2) {
X print("arg count\n");
X goto out;
X }
X s1 = number(argv[2], 0, 10);
X if(argc > 3)
X s2 = number(argv[3], 0, 10);
X else
X s2 = wormsizeside(dev, s1); /* default to 1 disc side */
X morecache(dev, s1, s2);
X goto out;
X }
X if(strcmp(arg, "blockcmp") == 0) {
X if(argc < 4) {
X print("cannot arg count\n");
X goto out;
X }
X s1 = number(argv[2], 0, 10);
X s2 = number(argv[3], 0, 10);
X blockcmp(dev, s1, s2);
X goto out;
X }
X if(strcmp(arg, "startdump") == 0) {
X if(argc > 2)
X cw->nodump = number(argv[2], 0, 10);
X cw->nodump = !cw->nodump;
X if(cw->nodump)
X print("dump stopped\n");
X else
X print("dump allowed\n");
X goto out;
X }
X if(strcmp(arg, "allflag") == 0) {
X if(argc > 2)
X cw->allflag = number(argv[2], 0, 10);
X else
X cw->allflag = !cw->allflag;
X print("allflag = %d; falsehits = %ld\n",
X cw->allflag, cw->falsehits);
X goto out;
X }
X if(strcmp(arg, "storesb") == 0) {
X a = 4168344;
X b = 0;
X if(argc > 2)
X a = number(argv[2], 4168344, 10);
X if(argc > 3)
X b = number(argv[3], 0, 10);
X storesb(dev, a, b);
X goto out;
X }
X if(strcmp(arg, "test") == 0) {
X cwtest(dev);
X goto out;
X }
X print("unknown cwcmd %s\n", arg);
out:;
X}
!
echo dev/juke.c
sed 's/^X//' >dev/juke.c <<'!'
X#include "all.h"
X#define SCSInone SCSIread
X#define MAXDRIVE 10
X#define MAXSIDE 500
X#define TWORM MINUTE(10)
X#define THYSTER SECOND(10)
typedef struct Side Side;
struct Side
X{
X QLock; /* protects loading/unloading */
X int elem; /* element number */
X int drive; /* if loaded, where */
X uchar status; /* Sunload, etc */
X uchar rot; /* if backside */
X int ord; /* ordinal number for labeling */
X long time; /* time since last access, to unspin */
X long stime; /* time since last spinup, for hysteresis */
X long nblock; /* number of native blocks */
X long block; /* bytes per native block */
X long mult; /* multiplier to get plan9 blocks */
X long max; /* max size in plan9 blocks */
X};
typedef struct Juke Juke;
struct Juke
X{
X QLock; /* protects drive mechanism */
X Side side[MAXSIDE];
X int nside; /* how many storage elements (*2 if rev) */
X int ndrive; /* number of transfer elements */
X Device* juke; /* devworm of changer */
X Device* drive[MAXDRIVE]; /* devworm for i/o */
X uchar offline[MAXDRIVE]; /* drives removed from service */
X long fixedsize; /* one size fits all */
X int probeok; /* wait for init to probe */
X /* geometry returned by mode sense */
X int mt0, nmt;
X int se0, nse;
X int ie0, nie;
X int dt0, ndt;
X int rot;
X Juke* link;
X};
static Juke* jukelist;
enum
X{
X Sempty = 0, /* does not exist */
X Sunload, /* on the shelf */
X Sstart, /* loaded and spinning */
X};
extern int FIXEDSIZE;
static int wormsense(Device*);
static Side* wormunit(Device*);
static void shelves(void);
static int mmove(Juke*, int, int, int, int);
static int bestdrive(Juke*, int);
static void waitready(Device*);
static void element(Juke*, int);
X/*
X * mounts and spins up the device
X * locks the structure
X */
static
Side*
wormunit(Device *d)
X{
X int p, s, drive;
X Side *v;
X Juke *w;
X uchar cmd[10], buf[8];
X w = d->private;
X p = d->wren.targ;
X if(p < 0 || p >= w->nside) {
X// panic("wormunit partition %Z\n", d);
X return 0;
X }
X /*
X * if disk is unloaded, must load it
X * into next (circular) logical unit
X */
X v = &w->side[p];
X qlock(v);
X if(v->status == Sunload) {
X for(;;) {
X qlock(w);
X drive = bestdrive(w, p);
X if(drive >= 0)
X break;
X qunlock(w);
X waitsec(100);
X }
X print(" load r%ld drive %Z\n", v-w->side, w->drive[drive]);
X if(mmove(w, w->mt0, v->elem, w->dt0+drive, v->rot)) {
X qunlock(w);
X goto sbad;
X }
X v->drive = drive;
X v->status = Sstart;
X v->stime = toytime();
X qunlock(w);
X waitready(w->drive[drive]);
X v->stime = toytime();
X }
X if(v->status != Sstart) {
X if(v->status == Sempty)
X print("worm: unit empty %Z\n", d);
X else
X print("worm: not started %Z\n", d);
X goto sbad;
X }
X v->time = toytime();
X if(v->block)
X return v;
X /*
X * capacity command
X */
X memset(cmd, 0, sizeof(cmd));
X memset(buf, 0, sizeof(buf));
X cmd[0] = 0x25; /* read capacity */
X s = scsiio(w->drive[v->drive], SCSIread,
X cmd, sizeof(cmd), buf, sizeof(buf));
X if(s)
X goto sbad;
X v->nblock =
X (buf[0]<<24) |
X (buf[1]<<16) |
X (buf[2]<<8) |
X (buf[3]<<0);
X v->block =
X (buf[4]<<24) |
X (buf[5]<<16) |
X (buf[6]<<8) |
X (buf[7]<<0);
X v->mult =
X (RBUFSIZE + v->block - 1) /
X v->block;
X v->max =
X (v->nblock + 1) / v->mult;
X print(" worm %Z: drive %Z\n", d, w->drive[v->drive]);
X print(" %ld blocks at %ld bytes each\n",
X v->nblock, v->block);
X print(" %ld logical blocks at %d bytes each\n",
X v->max, RBUFSIZE);
X print(" %ld multiplier\n",
X v->mult);
X if(d->type != Devlworm)
X return v;
X /* check for label */
X print("label %Z ordinal %d\n", d, v->ord);
X qunlock(v);
X return wormunit(d);
sbad:
X qunlock(v);
X// panic("wormunit sbad");
X return 0;
X}
static
void
waitready(Device *d)
X{
X uchar cmd[6];
X int s, e;
X for(e=0;e<100;e++) {
X memset(cmd, 0, sizeof(cmd));
X s = scsiio(d, SCSInone, cmd, sizeof(cmd), cmd, 0);
X if(s == 0)
X break;
X waitsec(100);
X }
X}
static
int
bestdrive(Juke *w, int side)
X{
X Side *v, *bv[MAXDRIVE];
X int i, s, e, drive;
X long t, t0;
loop:
X /* build table of what platters on what drives */
X for(i=0; i<w->ndt; i++)
X bv[i] = 0;
X v = &w->side[0];
X for(i=0; i<w->nside; i++, v++) {
X s = v->status;
X if(s == Sstart) {
X drive = v->drive;
X if(drive >= 0 && drive < w->ndt)
X bv[drive] = v;
X }
X }
X /*
X * find oldest drive, but must be
X * at least THYSTER old.
X */
X e = w->side[side].elem;
X t0 = toytime() - THYSTER;
X t = t0;
X drive = -1;
X for(i=0; i<w->ndt; i++) {
X v = bv[i];
X if(v == 0) { /* 2nd priority: empty drive */
X if(w->offline[i])
X continue;
X if(w->drive[i] != devnone) {
X drive = i;
X t = 0;
X }
X continue;
X }
X if(v->elem == e) { /* 1st priority: other side */
X drive = -1;
X if(v->stime < t0)
X drive = i;
X break;
X }
X if(v->stime < t) { /* 3rd priority: by time */
X drive = i;
X t = v->stime;
X }
X }
X if(drive >= 0) {
X v = bv[drive];
X if(v) {
X qlock(v);
X if(v->status != Sstart) {
X qunlock(v);
X goto loop;
X }
X print(" unload r%ld drive %Z\n",
X v-w->side, w->drive[drive]);
X if(mmove(w, w->mt0, w->dt0+drive, v->elem, v->rot)) {
X qunlock(v);
X goto loop;
X }
X v->status = Sunload;
X qunlock(v);
X }
X }
X return drive;
X}
long
wormsize(Device *d)
X{
X Side *v;
X Juke *w;
X long size;
X w = d->private;
X if(w->fixedsize) {
X size = w->fixedsize;
X goto out;
X }
X v = wormunit(d);
X if(v == 0)
X return 0;
X size = v->max;
X qunlock(v);
X if(FIXEDSIZE) // TODO? push FIXEDSIZE into Device or Juke struct
X w->fixedsize = size;
out:
X if(d->type == Devlworm)
X return size-1;
X return size;
X}
X/*
X * d must be, or be on, the same jukebox as `side' resides on.
X * have to paw through Devmcat of sides.
X */
long
wormsizeside(Device *d, int side)
X{
X int s;
X Device *x;
X switch(d->type) {
X default:
X print("wormsizeside: dev not ro, cw, juke nor mcat: %Z\n", d);
X return 0;
X case Devmcat: /* of sides */
X break;
X case Devjuke:
X return wormsizeside(d->j.m, side); /* mcat of sides */
X case Devcw:
X return wormsizeside(d->cw.w, side); /* should be juke */
X case Devro:
X return wormsizeside(d->ro.parent, side); /* cw */
X }
X if (side < 0 || side >= d->cat.ndev) {
X print("wormsizeside: side %d not in range [0..%d] for %Z\n",
X side, d->cat.ndev, d);
X return 0;
X }
X x = d->cat.first;
X for (s = 0; s < side; s++) {
X x = x->link;
X if (x == nil)
X panic("wormsizeside nil link");
X }
X if (x->type != Devworm && x->type != Devlworm) {
X print("wormsizeside: %Z of %Z type not (l)worm\n", x, d);
X return 0;
X }
X return wormsize(x);
X}
X/* returns starts of side #side and #(side+1) in *stp */
void
wormsidestarts(Device *dev, int side, Sidestarts *stp)
X{
X int s;
X long dstart;
X for (dstart = s = 0; s < side; s++)
X dstart += wormsizeside(dev, s);
X stp->sstart = dstart;
X stp->s1start = dstart + wormsizeside(dev, side);
X}
static
int
wormiocmd(Device *d, int io, long b, void *c)
X{
X Side *v;
X Juke *w;
X long l, m;
X int s;
X uchar cmd[10];
X w = d->private;
X v = wormunit(d);
X if(v == 0)
X return 0x71;
X if(b >= v->max) {
X qunlock(v);
X print("worm: wormiocmd out of range %Z(%ld)\n", d, b);
X return 0x071;
X }
X memset(cmd, 0, sizeof(cmd));
X cmd[0] = 0x28; /* extended read */
X if(io != SCSIread)
X cmd[0] = 0x2a; /* extended write */
X m = v->mult;
X l = b * m;
X cmd[2] = l>>24;
X cmd[3] = l>>16;
X cmd[4] = l>>8;
X cmd[5] = l;
X cmd[7] = m>>8;
X cmd[8] = m;
X s = scsiio(w->drive[v->drive], io, cmd, sizeof(cmd), c, RBUFSIZE);
X qunlock(v);
X return s;
X}
int
wormread(Device *d, long b, void *c)
X{
X int s;
X s = wormiocmd(d, SCSIread, b, c);
X if(s) {
X print("wormread: %Z(%ld) bad status #%x\n", d, b, s);
X cons.nwormre++;
X return s;
X }
X return 0;
X}
int
wormwrite(Device *d, long b, void *c)
X{
X int s;
X s = wormiocmd(d, SCSIwrite, b, c);
X if(s) {
X print("wormwrite: %Z(%ld) bad status #%x\n", d, b, s);
X cons.nwormwe++;
X return s;
X }
X return 0;
X}
static
int
mmove(Juke *w, int trans, int from, int to, int rot)
X{
X uchar cmd[12], buf[4];
X int s;
X static recur = 0;
X memset(cmd, 0, sizeof(cmd));
X cmd[0] = 0xa5; /* move medium */
X cmd[2] = trans>>8;
X cmd[3] = trans;
X cmd[4] = from>>8;
X cmd[5] = from;
X cmd[6] = to>>8;
X cmd[7] = to;
X if(rot)
X cmd[10] = 1;
X s = scsiio(w->juke, SCSInone, cmd, sizeof(cmd), buf, 0);
X if(s) {
X print("scsio status #%x\n", s);
X print("move medium t=%d fr=%d to=%d rot=%d", trans, from, to, rot);
X// panic("mmove");
X if(recur == 0) {
X recur = 1;
X print("element from=%d\n", from);
X element(w, from);
X print("element to=%d\n", to);
X element(w, to);
X print("element trans=%d\n", trans);
X element(w, trans);
X recur = 0;
X }
X return 1;
X }
X return 0;
X}
static
void
geometry(Juke *w)
X{
X int s;
X uchar cmd[6], buf[4+20];
X memset(cmd, 0, sizeof(cmd));
X memset(buf, 0, sizeof(buf));
X cmd[0] = 0x1a; /* mode sense */
X cmd[2] = 0x1d; /* element address assignment */
X cmd[4] = sizeof(buf); /* allocation length */
X s = scsiio(w->juke, SCSIread, cmd, sizeof(cmd), buf, sizeof(buf));
X if(s)
X panic("geometry #%x\n", s);
X w->mt0 = (buf[4+2]<<8) | buf[4+3];
X w->nmt = (buf[4+4]<<8) | buf[4+5];
X w->se0 = (buf[4+6]<<8) | buf[4+7];
X w->nse = (buf[4+8]<<8) | buf[4+9];
X w->ie0 = (buf[4+10]<<8) | buf[4+11];
X w->nie = (buf[4+12]<<8) | buf[4+13];
X w->dt0 = (buf[4+14]<<8) | buf[4+15];
X w->ndt = (buf[4+16]<<8) | buf[4+17];
X memset(cmd, 0, 6);
X memset(buf, 0, sizeof(buf));
X cmd[0] = 0x1a; /* mode sense */
X cmd[2] = 0x1e; /* transport geometry */
X cmd[4] = sizeof(buf); /* allocation length */
X s = scsiio(w->juke, SCSIread, cmd, sizeof(cmd), buf, sizeof(buf));
X if(s)
X panic("geometry #%x\n", s);
X w->rot = buf[4+2] & 1;
X print(" mt %d %d\n", w->mt0, w->nmt);
X print(" se %d %d\n", w->se0, w->nse);
X print(" ie %d %d\n", w->ie0, w->nie);
X print(" dt %d %d\n", w->dt0, w->ndt);
X print(" rot %d\n", w->rot);
X prflush();
X}
static
void
element(Juke *w, int e)
X{
X uchar cmd[12], buf[8+8+88];
X int s, t;
X//loop:
X memset(cmd, 0, sizeof(cmd));
X memset(buf, 0, sizeof(buf));
X cmd[0] = 0xb8; /* read element status */
X cmd[2] = e>>8; /* starting element */
X cmd[3] = e;
X cmd[5] = 1; /* number of elements */
X cmd[9] = sizeof(buf); /* allocation length */
X s = scsiio(w->juke, SCSIread, cmd, sizeof(cmd), buf, sizeof(buf));
X if(s) {
X print("scsiio #%x\n", s);
X goto bad;
X }
X s = (buf[0]<<8) | buf[1];
X if(s != e) {
X print("element = %d\n", s);
X goto bad;
X }
X if(buf[3] != 1) {
X print("number reported = %d\n", buf[3]);
X goto bad;
X }
X s = (buf[8+8+0]<<8) | buf[8+8+1];
X if(s != e) {
X print("element1 = %d\n", s);
X goto bad;
X }
X switch(buf[8+0]) { /* element type */
X default:
X print("unknown element %d: %d\n", e, buf[8+0]);
X goto bad;
X case 1: /* transport */
X s = e - w->mt0;
X if(s < 0 || s >= w->nmt)
X goto bad;
X if(buf[8+8+2] & 1)
X print("transport %d full %d.%d\n", s,
X (buf[8+8+10]<<8) | buf[8+8+11],
X (buf[8+8+9]>>6) & 1);
X break;
X case 2: /* storage */
X s = e - w->se0;
X if(s < 0 || s >= w->nse)
X goto bad;
X w->side[s].status = Sempty;
X if(buf[8+8+2] & 1)
X w->side[s].status = Sunload;
X if(w->rot)
X w->side[w->nse+s].status = w->side[s].status;
X break;
X case 3: /* import/export */
X s = e - w->ie0;
X if(s < 0 || s >= w->nie)
X goto bad;
X print("import/export %d #%.2x %d.%d\n", s,
X buf[8+8+2],
X (buf[8+8+10]<<8) | buf[8+8+11],
X (buf[8+8+9]>>6) & 1);
X break;
X case 4: /* data transfer */
X s = e - w->dt0;
X if(s < 0 || s >= w->ndt)
X goto bad;
X print("data transfer %d #%.2x %d.%d\n", s,
X buf[8+8+2],
X (buf[8+8+10]<<8) | buf[8+8+11],
X (buf[8+8+9]>>6) & 1);
X if(buf[8+8+2] & 1) {
X t = ((buf[8+8+10]<<8) | buf[8+8+11]) - w->se0;
X print("r%d in drive %d\n", t, s);
X if(mmove(w, w->mt0, w->dt0+s, w->se0+t, (buf[8+8+9]>>6) & 1)) {
X print("mmove initial unload\n");
X goto bad;
X }
X w->side[t].status = Sunload;
X if(w->rot)
X w->side[w->nse+t].status = Sunload;
X }
X if(buf[8+8+2] & 4) {
X print("drive w%d has exception #%.2x #%.2x\n", s,
X buf[8+8+4], buf[8+8+5]);
X goto bad;
X }
X break;
X }
X return;
bad:
X// panic("element");
X return;
X}
static
void
positions(Juke *w)
X{
X int i, f;
X /* mark empty shelves */
X for(i=0; i<w->nse; i++)
X element(w, w->se0+i);
X for(i=0; i<w->nmt; i++)
X element(w, w->mt0+i);
X for(i=0; i<w->nie; i++)
X element(w, w->ie0+i);
X for(i=0; i<w->ndt; i++)
X element(w, w->dt0+i);
X f = 0;
X for(i=0; i<w->nse; i++) {
X if(w->side[i].status == Sempty) {
X if(f) {
X print("r%d\n", i-1);
X f = 0;
X }
X } else {
X if(!f) {
X print(" shelves r%d-", i);
X f = 1;
X }
X }
X }
X if(f)
X print("r%d\n", i-1);
X}
static
void
jinit(Juke *w, Device *d, int o)
X{
X int p;
X Device *dev = d;
X switch(d->type) {
X default:
X print("juke platter not (devmcat of) dev(l)worm: %Z\n", d);
X goto bad;
X case Devmcat:
X /*
X * we don't call mcatinit(d) here, so we have to set d->cat.ndev
X * ourselves.
X */
X for(d=d->cat.first; d; d=d->link)
X jinit(w, d, o++);
X dev->cat.ndev = o;
X break;
X case Devlworm:
X p = d->wren.targ;
X if(p < 0 || p >= w->nside)
X panic("jinit partition %Z\n", d);
X w->side[p].ord = o;
X case Devworm:
X if(d->private) {
X print("juke platter private pointer set %p\n",
X d->private);
X goto bad;
X }
X d->private = w;
X break;
X }
X return;
bad:
X panic("jinit");
X}
Side*
wormi(char *arg)
X{
X int i, j;
X Juke *w;
X Side *v;
X w = jukelist;
X i = number(arg, -1, 10) - 1;
X if(i < 0 || i >= w->nside) {
X print("bad unit number %s (%d)\n", arg, i+1);
X return 0;
X }
X j = i;
X if(j >= w->nse)
X j -= w->nse;
X if(j < w->nside) {
X v = &w->side[j];
X qlock(v);
X if(v->status == Sstart) {
X if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) {
X qunlock(v);
X return 0;
X }
X v->status = Sunload;
X }
X qunlock(v);
X }
X j += w->nse;
X if(j < w->nside) {
X v = &w->side[j];
X qlock(v);
X if(v->status == Sstart) {
X if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) {
X qunlock(v);
X return 0;
X }
X v->status = Sunload;
X }
X qunlock(v);
X }
X v = &w->side[i];
X qlock(v);
X return v;
X}
static
void
cmd_wormoffline(int argc, char *argv[])
X{
X int u, i;
X Juke *w;
X w = jukelist;
X if(argc <= 1) {
X print("usage: wormoffline drive\n");
X return;
X }
X u = number(argv[1], -1, 10);
X if(u < 0 || u >= w->ndrive) {
X print("bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive);
X return;
X }
X if(w->offline[u])
X print("drive %d already offline\n", u);
X w->offline[u] = 1;
X for(i=0; i<w->ndrive; i++)
X if(w->offline[i] == 0)
X return;
X print("that would take all drives offline\n");
X w->offline[u] = 0;
X}
static
void
cmd_wormonline(int argc, char *argv[])
X{
X int u;
X Juke *w;
X w = jukelist;
X if(argc <= 1) {
X print("usage: wormonline drive\n");
X return;
X }
X u = number(argv[1], -1, 10);
X if(u < 0 || u >= w->ndrive) {
X print("bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive);
X return;
X }
X if(w->offline[u] == 0)
X print("drive %d already online\n", u);
X w->offline[u] = 0;
X}
static
void
cmd_wormreset(int, char *[])
X{
X Juke *w;
X for(w=jukelist; w; w=w->link) {
X qlock(w);
X positions(w);
X qunlock(w);
X }
X}
static
void
cmd_wormeject(int argc, char *argv[])
X{
X Juke *w;
X Side *v;
X if(argc <= 1) {
X print("usage: wormeject unit\n");
X return;
X }
X w = jukelist;
X v = wormi(argv[1]);
X if(v == 0)
X return;
X mmove(w, w->mt0, v->elem, w->ie0, 0);
X qunlock(v);
X}
static
void
cmd_wormingest(int argc, char *argv[])
X{
X Juke *w;
X Side *v;
X w = jukelist;
X if(argc <= 1) {
X print("usage: wormingest unit\n");
X return;
X }
X v = wormi(argv[1]);
X if(v == 0)
X return;
X mmove(w, w->mt0, w->ie0, v->elem, 0);
X qunlock(v);
X}
void
jukeinit(Device *d)
X{
X Juke *w;
X Device *xdev;
X Side *v;
X int i;
X /* j(w<changer>w<station0>...)(r<platters>) */
X xdev = d->j.j;
X if(xdev->type != Devmcat) {
X print("juke union not mcat\n");
X goto bad;
X }
X /*
X * pick up the changer device
X */
X xdev = xdev->cat.first;
X if(xdev->type != Devwren) {
X print("juke changer not wren %Z\n", xdev);
X goto bad;
X }
X for(w=jukelist; w; w=w->link)
X if(xdev == w->juke)
X goto found;
X /*
X * allocate a juke structure
X * no locking problems.
X */
X w = ialloc(sizeof(Juke), 0);
X w->link = jukelist;
X jukelist = w;
X print("alloc juke %Z\n", xdev);
X qlock(w);
X qunlock(w);
X w->name = "juke";
X w->juke = xdev;
X geometry(w);
X /*
X * pick up each side
X */
X w->nside = w->nse;
X if(w->rot)
X w->nside += w->nside;
X if(w->nside > MAXSIDE) {
X print("too many sides: %d max %d\n", w->nside, MAXSIDE);
X goto bad;
X }
X for(i=0; i<w->nse; i++) {
X v = &w->side[i];
X qlock(v);
X qunlock(v);
X v->name = "shelf";
X v->elem = w->se0 + i;
X v->rot = 0;
X v->status = Sempty;
X v->time = toytime();
X if(w->rot) {
X v += w->nse;
X qlock(v);
X qunlock(v);
X v->name = "shelf";
X v->elem = w->se0 + i;
X v->rot = 1;
X v->status = Sempty;
X v->time = toytime();
X }
X }
X positions(w);
X w->ndrive = w->ndt;
X if(w->ndrive > MAXDRIVE) {
X print("ndrives truncated to %d\n", MAXDRIVE);
X w->ndrive = MAXDRIVE;
X }
X /*
X * pick up each drive
X */
X for(i=0; i<w->ndrive; i++)
X w->drive[i] = devnone;
X cmd_install("wormreset", "-- put drives back where jukebox thinks they belong", cmd_wormreset);
X cmd_install("wormeject", "unit -- shelf to outside", cmd_wormeject);
X cmd_install("wormingest", "unit -- outside to shelf", cmd_wormingest);
X cmd_install("wormoffline", "unit -- disable drive", cmd_wormoffline);
X cmd_install("wormonline", "unit -- enable drive", cmd_wormonline);
found:
X i = 0;
X while(xdev = xdev->link) {
X if(xdev->type != Devwren) {
X print("drive not devwren: %Z\n", xdev);
X goto bad;
X }
X if(w->drive[i]->type != Devnone &&
X xdev != w->drive[i]) {
X print("double init drive %d %Z %Z\n", i, w->drive[i], xdev);
X goto bad;
X }
X if(i >= w->ndrive) {
X print("too many drives %Z\n", xdev);
X goto bad;
X }
X w->drive[i++] = xdev;
X }
X if(i <= 0) {
X print("no drives\n");
X goto bad;
X }
X /*
X * put w pointer in each platter
X */
X d->private = w;
X jinit(w, d->j.m, 0);
X w->probeok = 1;
X return;
bad:
X panic("juke init");
X}
int
dowcp(void)
X{
X return 0;
X}
X/*
X * called periodically
X */
void
wormprobe(void)
X{
X int i, drive;
X long t;
X Side *v;
X Juke *w;
X t = toytime() - TWORM;
X for(w=jukelist; w; w=w->link) {
X if(w->probeok == 0 || !canqlock(w))
X continue;
X for(i=0; i<w->nside; i++) {
X v = &w->side[i];
X if(!canqlock(v))
X continue;
X if(v->status == Sstart && t > v->time) {
X drive = v->drive;
X print(" time r%ld drive %Z\n",
X v-w->side, w->drive[drive]);
X mmove(w, w->mt0, w->dt0+drive, v->elem, v->rot);
X v->status = Sunload;
X }
X qunlock(v);
X }
X qunlock(w);
X }
X}
!
echo fs/9fsfs.c
sed 's/^X//' >fs/9fsfs.c <<'!'
X#include "all.h"
X#include "mem.h"
X#include "io.h"
X#include "ureg.h"
X#include "../pc/dosfs.h"
X/*
X * setting this to zero permits the use of discs of different sizes, but
X * can make jukeinit() quite slow while the robotics work through each disc
X * twice (once per side).
X */
int FIXEDSIZE = 1;
X#ifndef DATE
X#define DATE 568011600L+4*3600
X#endif
ulong mktime = DATE; /* set by mkfile */
Startsb startsb[] =
X{
X "main", 2,
X 0
X};
Dos dos;
static
struct
X{
X char *name;
X long (*read)(int, void*, long);
X vlong (*seek)(int, vlong);
X long (*write)(int, void*, long);
X int (*part)(int, char*);
X} nvrdevs[] =
X{
X { "fd", floppyread, floppyseek, floppywrite, 0, },
X { "hd", ataread, ataseek, atawrite, setatapart, },
X /*
X { "sd", scsiread, scsiseek, scsiwrite, setscsipart, },
X */
X { 0, },
X};
void
otherinit(void)
X{
X int dev, i, nfd, nhd, s;
X char *p, *q, buf[sizeof(nvrfile)+8];
X kbdinit();
X printcpufreq();
X etherinit();
X scsiinit();
X s = spllo();
X nhd = atainit();
X nfd = floppyinit();
X dev = 0;
X if(p = getconf("nvr")){
X strncpy(buf, p, sizeof(buf)-2);
X buf[sizeof(buf)-1] = 0;
X p = strchr(buf, '!');
X q = strrchr(buf, '!');
X if(p == 0 || q == 0 || strchr(p+1, '!') != q)
X panic("malformed nvrfile: %s\n", buf);
X *p++ = 0;
X *q++ = 0;
X dev = strtoul(p, 0, 0);
X strcpy(nvrfile, q);
X p = buf;
X } else
X if(p = getconf("bootfile")){
X strncpy(buf, p, sizeof(buf)-2);
X buf[sizeof(buf)-1] = 0;
X p = strchr(buf, '!');
X q = strrchr(buf, '!');
X if(p == 0 || q == 0 || strchr(p+1, '!') != q)
X panic("malformed bootfile: %s\n", buf);
X *p++ = 0;
X *q = 0;
X dev = strtoul(p, 0, 0);
X p = buf;
X } else
X if(nfd)
X p = "fd";
X else
X if(nhd)
X p = "hd";
X else
X p = "sd";
X for(i = 0; nvrdevs[i].name; i++){
X if(strcmp(p, nvrdevs[i].name) == 0){
X dos.dev = dev;
X if(nvrdevs[i].part && (*nvrdevs[i].part)(dos.dev, "disk") == 0)
X break;
X dos.read = nvrdevs[i].read;
X dos.seek = nvrdevs[i].seek;
X dos.write = nvrdevs[i].write;
X break;
X }
X }
X if(dos.read == 0)
X panic("no device for nvram\n");
X if(dosinit(&dos) < 0)
X panic("can't init dos dosfs on %s\n", p);
X splx(s);
X}
void
touser(void)
X{
X int i;
X settime(rtctime());
X boottime = time();
X print("sysinit\n");
X sysinit();
X userinit(floppyproc, 0, "floppyproc");
X /*
X * Ethernet i/o processes
X */
X etherstart();
X /*
X * read ahead processes
X */
X userinit(rahead, 0, "rah");
X /*
X * server processes
X */
X for(i=0; i<conf.nserve; i++)
X userinit(serve, 0, "srv");
X /*
X * worm "dump" copy process
X */
X userinit(wormcopy, 0, "wcp");
X /*
X * processes to read the console
X */
X consserve();
X /*
X * "sync" copy process
X * this doesn't return.
X */
X u->text = "scp";
X synccopy();
X}
void
localconfinit(void)
X{
X /* conf.nfile = 60000; */ /* from emelie */
X conf.nodump = 0;
X conf.dumpreread = 1;
X conf.firstsb = 0; /* time- & jukebox-dependent optimisation */
X conf.recovsb = 0;
X conf.ripoff = 1;
X conf.nlgmsg = 100;
X conf.nsmmsg = 500;
X}
int (*fsprotocol[])(Msgbuf*) = {
X serve9p1,
X serve9p2,
X nil,
X};
!
echo fs/dat.h
sed 's/^X//' >fs/dat.h <<'!'
X/*
X * The most fundamental constant.
X * Will the code compile with RBUFSIZE a variable?
X * Nope, for one thing, RBUFSIZE determines FEPERBUF, which determines
X * the number of elements in a free-list-block array.
X */
X#define RBUFSIZE (4*1024) /* raw buffer size */
X#include "../port/portdat.h"
extern Mach mach0;
typedef struct Segdesc Segdesc;
struct Segdesc
X{
X ulong d0;
X ulong d1;
X};
typedef struct Mbank {
X ulong base;
X ulong limit;
X} Mbank;
X#define MAXBANK 8
typedef struct Mconf {
X Lock;
X Mbank bank[MAXBANK];
X int nbank;
X ulong topofmem;
X} Mconf;
extern Mconf mconf;
extern char nvrfile[128];
!
echo port/chk.c
sed 's/^X//' >port/chk.c <<'!'
X#include "all.h"
static char* abits;
static long sizabits;
static char* qbits;
static long sizqbits;
static char* name;
static long sizname;
static long fstart;
static long fsize;
static long nfiles;
static long maxq;
static char* calloc;
static Device* dev;
static long ndup;
static long nused;
static long nfdup;
static long nqbad;
static long nfree;
static long nbad;
static int mod;
static int flags;
static int ronly;
static int cwflag;
static long sbaddr;
static long oldblock;
static int depth;
static int maxdepth;
X/* local prototypes */
static int fsck(Dentry*);
static void ckfreelist(Superb*);
static void mkfreelist(Superb*);
static void trfreelist(Superb*);
static void xaddfree(Device*, long, Superb*, Iobuf*);
static void xflush(Device*, Superb*, Iobuf*);
static Dentry* maked(long, int, long);
static void modd(long, int, Dentry*);
static void xread(long, long);
static int amark(long);
static int fmark(long);
static int ftest(long);
static void missing(void);
static void qmark(long);
static void* malloc(ulong);
static Iobuf* xtag(long, int, long);
static
void*
malloc(ulong n)
X{
X char *p, *q;
X p = calloc;
X while((ulong)p & 3)
X p++;
X q = p+n;
X if(((ulong)q&0x0fffffffL) >= conf.mem)
X panic("check: mem size");
X calloc = q;
X memset(p, 0, n);
X return p;
X}
X/*
X * check flags
X */
enum
X{
X Crdall = (1<<0), /* read all files */
X Ctag = (1<<1), /* rebuild tags */
X Cpfile = (1<<2), /* print files */
X Cpdir = (1<<3), /* print directories */
X Cfree = (1<<4), /* rebuild free list */
X Csetqid = (1<<5), /* resequence qids */
X Cream = (1<<6), /* clear all bad tags */
X Cbad = (1<<7), /* clear all bad blocks */
X Ctouch = (1<<8), /* touch old dir and indir */
X};
static
struct
X{
X char* option;
X long flag;
X} ckoption[] =
X{
X "rdall", Crdall,
X "tag", Ctag,
X "pfile", Cpfile,
X "pdir", Cpdir,
X "free", Cfree,
X "setqid", Csetqid,
X "ream", Cream,
X "bad", Cbad,
X "touch", Ctouch,
X 0,
X};
void
cmd_check(int argc, char *argv[])
X{
X long f, i;
X Filsys *fs;
X Iobuf *p;
X Superb *sb;
X Dentry *d;
X long raddr, flag;
X flag = 0;
X for(i=1; i<argc; i++) {
X for(f=0; ckoption[f].option; f++)
X if(strcmp(argv[i], ckoption[f].option) == 0)
X goto found;
X print("unknown check option %s\n", argv[i]);
X for(f=0; ckoption[f].option; f++)
X print(" %s\n", ckoption[f].option);
X return;
X found:
X flag |= ckoption[f].flag;
X }
X fs = cons.curfs;
X dev = fs->dev;
X ronly = (dev->type == Devro);
X cwflag = (dev->type == Devcw) | (dev->type == Devro);
X if(!ronly)
X wlock(&mainlock); /* check */
X calloc = (char*)ialloc(0, 1) + 100000;
X flags = flag;
X sizqbits = ((1<<22) + 7) / 8; /* botch */
X qbits = malloc(sizqbits);
X sbaddr = superaddr(dev);
X raddr = getraddr(dev);
X p = xtag(sbaddr, Tsuper, QPSUPER);
X if(!p)
X goto out;
X sb = (Superb*)p->iobuf;
X fstart = 2;
X cons.noage = 1;
X fsize = sb->fsize;
X sizabits = (fsize-fstart + 7)/8;
X abits = malloc(sizabits);
X sizname = 4000;
X name = malloc(sizname);
X sizname -= NAMELEN+10; /* for safety */
X mod = 0;
X nfree = 0;
X nfdup = 0;
X nused = 0;
X nbad = 0;
X ndup = 0;
X nqbad = 0;
X depth = 0;
X maxdepth = 0;
X if(flags & Ctouch) {
X /* round fsize down to start of current side */
X int s;
X long dsize;
X oldblock = 0;
X for (s = 0; dsize = wormsizeside(dev, s),
X dsize > 0 && oldblock + dsize < fsize; s++)
X oldblock += dsize;
X print("oldblock = %ld\n", oldblock);
X }
X amark(sbaddr);
X if(cwflag) {
X amark(sb->roraddr);
X amark(sb->next);
X }
X print("checking filsys: %s\n", fs->name);
X nfiles = 0;
X maxq = 0;
X d = maked(raddr, 0, QPROOT);
X if(d) {
X amark(raddr);
X if(fsck(d))
X modd(raddr, 0, d);
X depth--;
X calloc -= sizeof(Dentry);
X if(depth)
X print("depth not zero on return\n");
X }
X if(flags & Cfree) {
X if(cwflag)
X trfreelist(sb);
X else
X mkfreelist(sb);
X }
X if(sb->qidgen < maxq)
X print("qid generator low path=%ld maxq=%ld\n",
X sb->qidgen, maxq);
X if(!(flags & Cfree))
X ckfreelist(sb);
X if(mod) {
X sb->qidgen = maxq;
X print("file system was modified\n");
X settag(p, Tsuper, QPNONE);
X }
X print("nfiles = %ld\n", nfiles);
X print("fsize = %ld\n", fsize);
X print("nused = %ld\n", nused);
X print("ndup = %ld\n", ndup);
X print("nfree = %ld\n", nfree);
X print("tfree = %ld\n", sb->tfree);
X print("nfdup = %ld\n", nfdup);
X print("nmiss = %ld\n", fsize-fstart-nused-nfree);
X print("nbad = %ld\n", nbad);
X print("nqbad = %ld\n", nqbad);
X print("maxq = %ld\n", maxq);
X if(!cwflag)
X missing();
out:
X cons.noage = 0;
X putbuf(p);
X if(!ronly)
X wunlock(&mainlock);
X}
static
int
fsck(Dentry *d)
X{
X Dentry *nd;
X Iobuf *p1, *p2, *pd;
X int i, j, k, ns, dmod;
X long a, qpath;
X depth++;
X if(depth >= maxdepth) {
X maxdepth = depth;
X /*
X * On a 386 each recursion costs 72 bytes or thereabouts,
X * for some slop bump it up to 100.
X * Alternatives here might be to give the check process
X * a much bigger stack or rewrite it without recursion.
X */
X if(maxdepth >= MAXSTACK/100) {
X print("max depth exceeded: %s\n", name);
X return 0;
X }
X }
X dmod = 0;
X if(!(d->mode & DALLOC))
X goto out;
X nfiles++;
X ns = strlen(name);
X i = strlen(d->name);
X if(i >= NAMELEN) {
X d->name[NAMELEN-1] = 0;
X print("%s->name (%s) not terminated\n", name, d->name);
X return 0;
X }
X ns += i;
X if(ns >= sizname) {
X print("%s->name (%s) name too large\n", name, d->name);
X return 0;
X }
X strcat(name, d->name);
X if(d->mode & DDIR) {
X if(ns > 1) {
X strcat(name, "/");
X ns++;
X }
X if(flags & Cpdir) {
X print("%s\n", name);
X prflush();
X }
X } else
X if(flags & Cpfile) {
X print("%s\n", name);
X prflush();
X }
X qpath = d->qid.path & ~QPDIR;
X qmark(qpath);
X if(qpath > maxq)
X maxq = qpath;
X for(i=0; i<NDBLOCK; i++) {
X a = d->dblock[i];
X if(amark(a)) {
X if(flags & Cbad) {
X d->dblock[i] = 0;
X dmod++;
X }
X a = 0;
X }
X if(!a)
X continue;
X if(d->mode & DDIR) {
X if((flags&Ctouch) && a < oldblock) {
X pd = getbuf(dev, a, Bread|Bmod);
X if(pd)
X putbuf(pd);
X dmod++;
X }
X for(k=0; k<DIRPERBUF; k++) {
X nd = maked(a, k, qpath);
X if(!nd)
X break;
X if(fsck(nd)) {
X modd(a, k, nd);
X dmod++;
X }
X depth--;
X calloc -= sizeof(Dentry);
X name[ns] = 0;
X }
X continue;
X }
X if(flags & Crdall)
X xread(a, qpath);
X }
X a = d->iblock;
X if(amark(a)) {
X if(flags & Cbad) {
X d->iblock = 0;
X dmod++;
X }
X a = 0;
X }
X if(a) {
X if((flags&Ctouch) && a < oldblock) {
X pd = getbuf(dev, a, Bread|Bmod);
X if(pd)
X putbuf(pd);
X dmod++;
X }
X if(p1 = xtag(a, Tind1, qpath))
X for(i=0; i<INDPERBUF; i++) {
X a = ((long*)p1->iobuf)[i];
X if(amark(a)) {
X if(flags & Cbad) {
X ((long*)p1->iobuf)[i] = 0;
X p1->flags |= Bmod;
X }
X a = 0;
X }
X if(!a)
X continue;
X if(d->mode & DDIR) {
X if((flags&Ctouch) && a < oldblock) {
X pd = getbuf(dev, a, Bread|Bmod);
X if(pd)
X putbuf(pd);
X dmod++;
X }
X for(k=0; k<DIRPERBUF; k++) {
X nd = maked(a, k, qpath);
X if(!nd)
X break;
X if(fsck(nd)) {
X modd(a, k, nd);
X dmod++;
X }
X depth--;
X calloc -= sizeof(Dentry);
X name[ns] = 0;
X }
X continue;
X }
X if(flags & Crdall)
X xread(a, qpath);
X }
X if(p1)
X putbuf(p1);
X }
X a = d->diblock;
X if(amark(a)) {
X if(flags & Cbad) {
X d->diblock = 0;
X dmod++;
X }
X a = 0;
X }
X if((flags&Ctouch) && a && a < oldblock) {
X pd = getbuf(dev, a, Bread|Bmod);
X if(pd)
X putbuf(pd);
X dmod++;
X }
X if(p2 = xtag(a, Tind2, qpath))
X for(i=0; i<INDPERBUF; i++) {
X a = ((long*)p2->iobuf)[i];
X if(amark(a)) {
X if(flags & Cbad) {
X ((long*)p2->iobuf)[i] = 0;
X p2->flags |= Bmod;
X }
X continue;
X }
X if((flags&Ctouch) && a && a < oldblock) {
X pd = getbuf(dev, a, Bread|Bmod);
X if(pd)
X putbuf(pd);
X dmod++;
X }
X if(p1 = xtag(a, Tind1, qpath))
X for(j=0; j<INDPERBUF; j++) {
X a = ((long*)p1->iobuf)[j];
X if(amark(a)) {
X if(flags & Cbad) {
X ((long*)p1->iobuf)[j] = 0;
X p1->flags |= Bmod;
X }
X continue;
X }
X if(!a)
X continue;
X if(d->mode & DDIR) {
X if((flags&Ctouch) && a < oldblock) {
X pd = getbuf(dev, a, Bread|Bmod);
X if(pd)
X putbuf(pd);
X dmod++;
X }
X for(k=0; k<DIRPERBUF; k++) {
X nd = maked(a, k, qpath);
X if(!nd)
X break;
X if(fsck(nd)) {
X modd(a, k, nd);
X dmod++;
X }
X depth--;
X calloc -= sizeof(Dentry);
X name[ns] = 0;
X }
X continue;
X }
X if(flags & Crdall)
X xread(a, qpath);
X }
X if(p1)
X putbuf(p1);
X }
X if(p2)
X putbuf(p2);
out:
X return dmod;
X}
X#define XFEN (FEPERBUF+6)
typedef
struct
X{
X int flag;
X int count;
X int next;
X long addr[XFEN];
X} Xfree;
static
void
xaddfree(Device *dev, long a, Superb *sb, Iobuf *p)
X{
X Xfree *x;
X x = (Xfree*)p->iobuf;
X if(x->count < XFEN) {
X x->addr[x->count] = a;
X x->count++;
X return;
X }
X if(!x->flag) {
X memset(&sb->fbuf, 0, sizeof(sb->fbuf));
X sb->fbuf.free[0] = 0L;
X sb->fbuf.nfree = 1;
X sb->tfree = 0;
X x->flag = 1;
X }
X addfree(dev, a, sb);
X}
static
void
xflush(Device *dev, Superb *sb, Iobuf *p)
X{
X int i;
X Xfree *x;
X x = (Xfree*)p->iobuf;
X if(!x->flag) {
X memset(&sb->fbuf, 0, sizeof(sb->fbuf));
X sb->fbuf.free[0] = 0L;
X sb->fbuf.nfree = 1;
X sb->tfree = 0;
X }
X for(i=0; i<x->count; i++)
X addfree(dev, x->addr[i], sb);
X}
X/*
X * make freelist
X * from existing freelist
X * (cw devices)
X */
static
void
trfreelist(Superb *sb)
X{
X long a;
X int n, i;
X Iobuf *p, *xp;
X Fbuf *fb;
X xp = getbuf(devnone, Cckbuf, 0);
X memset(xp->iobuf, 0, BUFSIZE);
X fb = &sb->fbuf;
X p = 0;
X for(;;) {
X n = fb->nfree;
X if(n < 0 || n > FEPERBUF)
X break;
X for(i=1; i<n; i++) {
X a = fb->free[i];
X if(a && !ftest(a))
X xaddfree(dev, a, sb, xp);
X }
X a = fb->free[0];
X if(!a)
X break;
X if(ftest(a))
X break;
X xaddfree(dev, a, sb, xp);
X if(p)
X putbuf(p);
X p = xtag(a, Tfree, QPNONE);
X if(!p)
X break;
X fb = (Fbuf*)p->iobuf;
X }
X if(p)
X putbuf(p);
X xflush(dev, sb, xp);
X putbuf(xp);
X mod++;
X print("%ld blocks free\n", sb->tfree);
X}
static
void
ckfreelist(Superb *sb)
X{
X long a, lo, hi;
X int n, i;
X Iobuf *p;
X Fbuf *fb;
X strcpy(name, "free list");
X print("check %s\n", name);
X fb = &sb->fbuf;
X a = sbaddr;
X p = 0;
X lo = 0;
X hi = 0;
X for(;;) {
X n = fb->nfree;
X if(n < 0 || n > FEPERBUF) {
X print("check: nfree bad %ld\n", a);
X break;
X }
X for(i=1; i<n; i++) {
X a = fb->free[i];
X if(a && !fmark(a)) {
X if(!lo || lo > a)
X lo = a;
X if(!hi || hi < a)
X hi = a;
X }
X }
X a = fb->free[0];
X if(!a)
X break;
X if(fmark(a))
X break;
X if(!lo || lo > a)
X lo = a;
X if(!hi || hi < a)
X hi = a;
X if(p)
X putbuf(p);
X p = xtag(a, Tfree, QPNONE);
X if(!p)
X break;
X fb = (Fbuf*)p->iobuf;
X }
X if(p)
X putbuf(p);
X print("lo = %ld; hi = %ld\n", lo, hi);
X}
X/*
X * make freelist from scratch
X */
static
void
mkfreelist(Superb *sb)
X{
X long a;
X int i, b;
X if(ronly) {
X print("cant make freelist on ronly device\n");
X return;
X }
X strcpy(name, "free list");
X memset(&sb->fbuf, 0, sizeof(sb->fbuf));
X sb->fbuf.free[0] = 0L;
X sb->fbuf.nfree = 1;
X sb->tfree = 0;
X for(a=fsize-fstart-1; a >= 0; a--) {
X i = a/8;
X if(i < 0 || i >= sizabits)
X continue;
X b = 1 << (a&7);
X if(abits[i] & b)
X continue;
X addfree(dev, fstart+a, sb);
X }
X print("%ld blocks free\n", sb->tfree);
X mod++;
X}
static
Dentry*
maked(long a, int s, long qpath)
X{
X Iobuf *p;
X Dentry *d, *d1;
X p = xtag(a, Tdir, qpath);
X if(!p)
X return 0;
X d = getdir(p, s);
X d1 = malloc(sizeof(Dentry));
X memmove(d1, d, sizeof(Dentry));
X putbuf(p);
X return d1;
X}
static
void
modd(long a, int s, Dentry *d1)
X{
X Iobuf *p;
X Dentry *d;
X if(!(flags & Cbad))
X return;
X p = getbuf(dev, a, Bread);
X d = getdir(p, s);
X if(!d) {
X if(p)
X putbuf(p);
X return;
X }
X memmove(d, d1, sizeof(Dentry));
X p->flags |= Bmod;
X putbuf(p);
X}
static
void
xread(long a, long qpath)
X{
X Iobuf *p;
X p = xtag(a, Tfile, qpath);
X if(p)
X putbuf(p);
X}
static
Iobuf*
xtag(long a, int tag, long qpath)
X{
X Iobuf *p;
X if(a == 0)
X return 0;
X p = getbuf(dev, a, Bread);
X if(!p) {
X print("check: \"%s\": xtag: p null\n", name);
X if(flags & (Cream|Ctag)) {
X p = getbuf(dev, a, Bmod);
X if(p) {
X memset(p->iobuf, 0, RBUFSIZE);
X settag(p, tag, qpath);
X mod++;
X return p;
X }
X }
X return 0;
X }
X if(checktag(p, tag, qpath)) {
X print("check: \"%s\": xtag: checktag\n", name);
X if(flags & (Cream|Ctag)) {
X if(flags & Cream)
X memset(p->iobuf, 0, RBUFSIZE);
X settag(p, tag, qpath);
X mod++;
X return p;
X }
X return p;
X }
X return p;
X}
static
int
amark(long a)
X{
X long i;
X int b;
X if(a < fstart || a >= fsize) {
X if(a == 0)
X return 0;
X print("check: \"%s\": range %ld\n",
X name, a);
X nbad++;
X return 1;
X }
X a -= fstart;
X i = a/8;
X b = 1 << (a&7);
X if(abits[i] & b) {
X if(!ronly) {
X if(ndup < 10)
X print("check: \"%s\": address dup %ld\n",
X name, fstart+a);
X else
X if(ndup == 10)
X print("...");
X }
X ndup++;
X return 1;
X }
X abits[i] |= b;
X nused++;
X return 0;
X}
static
int
fmark(long a)
X{
X long i;
X int b;
X if(a < fstart || a >= fsize) {
X print("check: \"%s\": range %ld\n",
X name, a);
X nbad++;
X return 1;
X }
X a -= fstart;
X i = a/8;
X b = 1 << (a&7);
X if(abits[i] & b) {
X print("check: \"%s\": address dup %ld\n",
X name, fstart+a);
X nfdup++;
X return 1;
X }
X abits[i] |= b;
X nfree++;
X return 0;
X}
static
int
ftest(long a)
X{
X long i;
X int b;
X if(a < fstart || a >= fsize)
X return 1;
X a -= fstart;
X i = a/8;
X b = 1 << (a&7);
X if(abits[i] & b)
X return 1;
X abits[i] |= b;
X return 0;
X}
static
void
missing(void)
X{
X long a, i;
X int b, n;
X n = 0;
X for(a=fsize-fstart-1; a>=0; a--) {
X i = a/8;
X b = 1 << (a&7);
X if(!(abits[i] & b)) {
X print("missing: %ld\n", fstart+a);
X n++;
X }
X if(n > 10) {
X print(" ...\n");
X break;
X }
X }
X}
static
void
qmark(long qpath)
X{
X int i, b;
X i = qpath/8;
X b = 1 << (qpath&7);
X if(i < 0 || i >= sizqbits) {
X nqbad++;
X if(nqbad < 20)
X print("check: \"%s\": qid out of range %lux\n",
X name, qpath);
X return;
X }
X if((qbits[i] & b) && !ronly) {
X nqbad++;
X if(nqbad < 20)
X print("check: \"%s\": qid dup %lux\n",
X name, qpath);
X }
X qbits[i] |= b;
X}
!
echo port/portdat.h
sed 's/^X//' >port/portdat.h <<'!'
X/*
X * fundamental constants
X */
X#define SUPER_ADDR 2 /* address of superblock */
X#define ROOT_ADDR 3 /* address of root directory */
X#define NAMELEN 28 /* size of names */
X#define NDBLOCK 6 /* number of direct blocks in Dentry */
X#define MAXDAT 8192 /* max allowable data message */
X#define MAXMSG 128 /* max size protocol message sans data */
X#define OFFMSG 60 /* offset of msg in buffer */
X#define NDRIVE 16 /* size of drive structure */
X#define NTLOCK 200 /* number of active file Tlocks */
X#define LRES 3 /* profiling resolution */
X#define NATTID 10 /* the last 10 ID's in attaches */
X#define C0a 59 /* time constants for filters */
X#define C0b 60
X#define C1a 599
X#define C1b 600
X#define C2a 5999
X#define C2b 6000
X/*
X * derived constants
X */
X#define BUFSIZE (RBUFSIZE-sizeof(Tag))
X#define DIRPERBUF (BUFSIZE/sizeof(Dentry))
X#define INDPERBUF (BUFSIZE/sizeof(long))
X#define INDPERBUF2 (INDPERBUF*INDPERBUF)
X#define FEPERBUF ((BUFSIZE-sizeof(Super1)-sizeof(long))/sizeof(long))
X#define SMALLBUF (MAXMSG)
X#define LARGEBUF (MAXMSG+MAXDAT+256)
X#define RAGAP (300*1024)/BUFSIZE /* readahead parameter */
X#define CEPERBK ((BUFSIZE-BKPERBLK*sizeof(long))/\
X (sizeof(Centry)*BKPERBLK))
X#define BKPERBLK 10
typedef struct Alarm Alarm;
typedef struct Auth Auth;
typedef struct Conf Conf;
typedef struct Label Label;
typedef struct Lock Lock;
typedef struct Mach Mach;
typedef struct QLock QLock;
typedef struct Ureg Ureg;
typedef struct User User;
typedef struct Fbuf Fbuf;
typedef struct Super1 Super1;
typedef struct Superb Superb;
typedef struct Filsys Filsys;
typedef struct Startsb Startsb;
typedef struct Dentry Dentry;
typedef struct Tag Tag;
typedef struct Talarm Talarm;
typedef struct Uid Uid;
typedef struct Device Device;
typedef struct Qid9p1 Qid9p1;
typedef struct Iobuf Iobuf;
typedef struct Wpath Wpath;
typedef struct File File;
typedef struct Chan Chan;
typedef struct Cons Cons;
typedef struct Time Time;
typedef struct Tm Tm;
typedef struct Rtc Rtc;
typedef struct Hiob Hiob;
typedef struct RWlock RWlock;
typedef struct Msgbuf Msgbuf;
typedef struct Queue Queue;
typedef struct Command Command;
typedef struct Flag Flag;
typedef struct Bp Bp;
typedef struct Rabuf Rabuf;
typedef struct Rendez Rendez;
typedef struct Filter Filter;
typedef ulong Float;
typedef struct Tlock Tlock;
typedef struct Cache Cache;
typedef struct Centry Centry;
typedef struct Bucket Bucket;
struct Lock
X{
X ulong* sbsem; /* addr of sync bus semaphore */
X ulong pc;
X ulong sr;
X};
struct Rendez
X{
X Lock;
X User* p;
X};
struct Filter
X{
X ulong count; /* count and old count kept separate */
X ulong oldcount; /* so interrput can read them */
X int c1; /* time const multiplier */
X int c2; /* time const divider */
X int c3; /* scale for printing */
X Float filter; /* filter */
X};
struct QLock
X{
X Lock; /* to use object */
X User* head; /* next process waiting for object */
X User* tail; /* last process waiting for object */
X char* name; /* for diagnostics */
X int locked; /* flag, is locked */
X};
struct RWlock
X{
X int nread;
X QLock wr;
X QLock rd;
X};
X/*
X * send/recv queue structure
X */
struct Queue
X{
X Lock; /* to manipulate values */
X int size; /* size of queue */
X int loc; /* circular pointer */
X int count; /* how many in queue */
X User* rhead; /* process's waiting for send */
X User* rtail;
X User* whead; /* process's waiting for recv */
X User* wtail;
X void* args[1]; /* list of saved pointers, [->size] */
X};
struct Tag
X{
X short pad;
X short tag;
X long path;
X};
struct Device
X{
X uchar type;
X uchar init;
X Device* link; /* link for mcat/mlev/mirror */
X Device* dlink; /* link all devices */
X void* private;
X long size;
X union
X {
X struct /* worm wren, (l)worm in targ */
X {
X int ctrl;
X int targ;
X int lun;
X } wren;
X struct /* ata/ide */
X {
X int ctrl;
X int unit;
X } ata;
X struct /* mcat mlev mirror */
X {
X Device* first;
X Device* last;
X int ndev;
X } cat;
X struct /* cw */
X {
X Device* c;
X Device* w;
X Device* ro;
X } cw;
X struct /* juke */
X {
X Device* j; /* (robotics, worm drives) - wrens */
X Device* m; /* (sides) - r or l devices */
X } j;
X struct /* ro */
X {
X Device* parent;
X } ro;
X struct /* fworm */
X {
X Device* fw;
X } fw;
X struct /* part */
X {
X Device* d;
X long base;
X long size;
X } part;
X struct /* byte-swapped */
X {
X Device* d;
X } swab;
X };
X};
typedef struct Sidestarts {
X long sstart; /* blocks before start of side */
X long s1start; /* blocks before start of next side */
X} Sidestarts;
struct Rabuf
X{
X union
X {
X struct
X {
X Device* dev;
X long addr;
X };
X Rabuf* link;
X };
X};
typedef
struct Qid
X{
X uvlong path;
X ulong vers;
X uchar type;
X} Qid;
X/* bits in Qid.type */
X#define QTDIR 0x80 /* type bit for directories */
X#define QTAPPEND 0x40 /* type bit for append only files */
X#define QTEXCL 0x20 /* type bit for exclusive use files */
X#define QTMOUNT 0x10 /* type bit for mounted channel */
X#define QTAUTH 0x08 /* type bit for authentication file */
X#define QTFILE 0x00 /* plain file */
X/* DONT TOUCH, this is the disk structure */
struct Qid9p1
X{
X long path;
X long version;
X};
struct Hiob
X{
X Iobuf* link;
X Lock;
X};
struct Chan
X{
X char type; /* major driver type i.e. Dev* */
X int (*protocol)(Msgbuf*); /* version */
X int msize; /* version */
X char whochan[50];
X char whoname[NAMELEN];
X void (*whoprint)(Chan*);
X ulong flags;
X int chan; /* overall channel number, mostly for printing */
X int nmsgs; /* outstanding messages, set under flock -- for flush */
X ulong whotime;
X Filter work;
X Filter rate;
X int nfile; /* used by cmd_files */
X RWlock reflock;
X Chan* next; /* link list of chans */
X Queue* send;
X Queue* reply;
X uchar authinfo[64];
X void* ifc;
X void* pdata;
X};
struct Filsys
X{
X char* name; /* name of filsys */
X char* conf; /* symbolic configuration */
X Device* dev; /* device that filsys is on */
X int flags;
X #define FREAM (1<<0) /* mkfs */
X #define FRECOVER (1<<1) /* install last dump */
X #define FEDIT (1<<2) /* modified */
X};
struct Startsb
X{
X char* name;
X long startsb;
X};
struct Time
X{
X ulong lasttoy;
X long bias;
X long offset;
X};
X/*
X * array of qids that are locked
X */
struct Tlock
X{
X Device* dev;
X ulong time;
X long qpath;
X File* file;
X};
struct Cons
X{
X ulong flags; /* overall flags for all channels */
X QLock; /* generic qlock for mutex */
X int uid; /* botch -- used to get uid on cons_create */
X int gid; /* botch -- used to get gid on cons_create */
X int nuid; /* number of uids */
X int ngid; /* number of gids */
X long offset; /* used to read files, c.f. fchar */
X int chano; /* generator for channel numbers */
X Chan* chan; /* console channel */
X Filsys* curfs; /* current filesystem */
X int profile; /* are we profiling? */
X long* profbuf;
X ulong minpc;
X ulong maxpc;
X ulong nprofbuf;
X long nlarge; /* number of large message buffers */
X long nsmall; /* ... small ... */
X long nwormre; /* worm read errors */
X long nwormwe; /* worm write errors */
X long nwormhit; /* worm read cache hits */
X long nwormmiss; /* worm read cache non-hits */
X int noage; /* dont update cache age, dump and check */
X long nwrenre; /* disk read errors */
X long nwrenwe; /* disk write errors */
X long nreseq; /* cache bucket resequence */
X Filter work[3]; /* thruput in messages */
X Filter rate[3]; /* thruput in bytes */
X Filter bhit[3]; /* getbufs that hit */
X Filter bread[3]; /* getbufs that miss and read */
X Filter brahead[3]; /* messages to readahead */
X Filter binit[3]; /* getbufs that miss and dont read */
X};
struct File
X{
X QLock;
X Qid qid;
X Wpath* wpath;
X Chan* cp; /* null means a free slot */
X Tlock* tlock; /* if file is locked */
X File* next; /* in cp->flist */
X Filsys* fs;
X long addr;
X long slot;
X long lastra; /* read ahead address */
X ulong fid;
X short uid;
X Auth *auth;
X char open;
X #define FREAD 1
X #define FWRITE 2
X #define FREMOV 4
X long doffset; /* directory reading */
X ulong dvers;
X long dslot;
X};
struct Wpath
X{
X Wpath* up; /* pointer upwards in path */
X long addr; /* directory entry addr */
X long slot; /* directory entry slot */
X short refs; /* number of files using this structure */
X};
struct Iobuf
X{
X QLock;
X Device* dev;
X Iobuf* fore; /* for lru */
X Iobuf* back; /* for lru */
X char* iobuf; /* only active while locked */
X char* xiobuf; /* "real" buffer pointer */
X long addr;
X int flags;
X};
struct Uid
X{
X short uid; /* user id */
X short lead; /* leader of group */
X short *gtab; /* group table */
X int ngrp; /* number of group entries */
X char name[NAMELEN]; /* user name */
X};
X/* DONT TOUCH, this is the disk structure */
struct Dentry
X{
X char name[NAMELEN];
X short uid;
X short gid;
X ushort mode;
X #define DALLOC 0x8000
X #define DDIR 0x4000
X #define DAPND 0x2000
X #define DLOCK 0x1000
X #define DREAD 0x4
X #define DWRITE 0x2
X #define DEXEC 0x1
X short muid;
X Qid9p1 qid;
X long size;
X long dblock[NDBLOCK];
X long iblock;
X long diblock;
X long atime;
X long mtime;
X};
X/* DONT TOUCH, this is the disk structure */
struct Super1
X{
X long fstart;
X long fsize;
X long tfree;
X long qidgen; /* generator for unique ids */
X /*
X * Stuff for WWC device
X */
X long cwraddr; /* cfs root addr */
X long roraddr; /* dump root addr */
X long last; /* last super block addr */
X long next; /* next super block addr */
X};
X/* DONT TOUCH, this is the disk structure */
struct Fbuf
X{
X long nfree;
X long free[FEPERBUF];
X};
X/* DONT TOUCH, this is the disk structure */
struct Superb
X{
X Fbuf fbuf;
X Super1;
X};
struct Label
X{
X ulong pc;
X ulong sp;
X};
struct Alarm
X{
X Lock;
X Alarm* next;
X int busy;
X int dt; /* in ticks */
X void (*f)(Alarm*, void*);
X void* arg;
X};
struct Talarm
X{
X Lock;
X User *list;
X};
struct Conf
X{
X ulong nmach; /* processors */
X ulong nproc; /* processes */
X ulong mem; /* total physical bytes of memory */
X ulong sparemem; /* memory left for check/dump and chans */
X ulong nalarm; /* alarms */
X ulong nuid; /* distinct uids */
X ulong nserve; /* server processes */
X ulong nfile; /* number of fid -- system wide */
X ulong nwpath; /* number of active paths, derrived from nfile */
X ulong gidspace; /* space for gid names -- derrived from nuid */
X ulong nlgmsg; /* number of large message buffers */
X ulong nsmmsg; /* number of small message buffers */
X ulong wcpsize; /* memory for worm copies; unused in 4e */
X ulong recovcw; /* recover addresses */
X ulong recovro;
X ulong firstsb;
X ulong recovsb;
X ulong nauth; /* number of Auth structs */
X uchar nodump; /* no periodic dumps */
X uchar ripoff;
X uchar dumpreread; /* read and compare in dump copy */
X ulong npage0; /* total physical pages of memory */
X ulong npage1; /* total physical pages of memory */
X ulong base0; /* base of bank 0 */
X ulong base1; /* base of bank 1 */
X};
X/*
X * message buffers
X * 2 types, large and small
X */
struct Msgbuf
X{
X short count;
X short flags;
X #define LARGE (1<<0)
X #define FREE (1<<1)
X #define BFREE (1<<2)
X #define BTRACE (1<<7)
X #define Mbrcvbuf (1<<15) /* to free, call (*free)(this) */
X Chan* chan;
X Msgbuf* next;
X ulong param;
X int category;
X uchar* data; /* rp or wp: current processing point */
X uchar* xdata; /* base of allocation */
X /* added for cpu kernel compatibility - geoff */
X void (*free)(Msgbuf *);
X};
X/*
X * message buffer categories
X */
enum
X{
X Mxxx = 0,
X Mbreply1,
X Mbreply2,
X Mbreply3,
X Mbreply4,
X Mbarp1,
X Mbarp2,
X Mbip1,
X Mbip2,
X Mbip3,
X Mbil1,
X Mbil2,
X Mbil3,
X Mbil4,
X Mbilauth,
X Maeth1,
X Maeth2,
X Maeth3,
X Mbeth1,
X Mbeth2,
X Mbeth3,
X Mbeth4,
X Mbsntp,
X MAXCAT,
X};
struct Mach
X{
X int machno; /* physical id of processor */
X int mmask; /* 1<<m->machno */
X ulong ticks; /* of the clock since boot time */
X int lights; /* light lights, this processor */
X User* proc; /* current process on this processor */
X Label sched; /* scheduler wakeup */
X Lock alarmlock; /* access to alarm list */
X void* alarm; /* alarms bound to this clock */
X void (*intr)(Ureg*, ulong); /* pending interrupt */
X User* intrp; /* process that was interrupted */
X ulong cause; /* arg to intr */
X Ureg* ureg; /* arg to intr */
X uchar stack[1];
X};
X#define MAXSTACK 4000
X#define NHAS 100
struct User
X{
X Label sched;
X Mach* mach; /* machine running this proc */
X User* rnext; /* next process in run queue */
X User* qnext; /* next process on queue for a QLock */
X void (*start)(void); /* startup function */
X char* text; /* name of this process */
X void* arg;
X Filter time[3]; /* cpu time used */
X int exiting;
X int pid;
X int state;
X Rendez tsleep;
X ulong twhen;
X Rendez *trend;
X User *tlink;
X int (*tfn)(void*);
X struct
X {
X QLock* q[NHAS];/* list of locks this process has */
X QLock* want; /* lock waiting */
X } has;
X uchar stack[MAXSTACK];
X};
X#define PRINTSIZE 256
struct
X{
X Lock;
X int machs;
X int exiting;
X} active;
struct Command
X{
X char* arg0;
X char* help;
X void (*func)(int, char*[]);
X};
struct Flag
X{
X char* arg0;
X char* help;
X ulong flag;
X};
struct Tm
X{
X /* see ctime(3) */
X int sec;
X int min;
X int hour;
X int mday;
X int mon;
X int year;
X int wday;
X int yday;
X int isdst;
X};
struct Rtc
X{
X int sec;
X int min;
X int hour;
X int mday;
X int mon;
X int year;
X};
X/*
X * cw device
X */
X/* DONT TOUCH, this is the disk structure */
struct Cache
X{
X long maddr; /* cache map addr */
X long msize; /* cache map size in buckets */
X long caddr; /* cache addr */
X long csize; /* cache size */
X long fsize; /* current size of worm */
X long wsize; /* max size of the worm */
X long wmax; /* highwater write */
X long sbaddr; /* super block addr */
X long cwraddr; /* cw root addr */
X long roraddr; /* dump root addr */
X long toytime; /* somewhere convienent */
X long time;
X};
X/* DONT TOUCH, this is the disk structure */
struct Centry
X{
X ushort age;
X short state;
X long waddr; /* worm addr */
X};
X/* DONT TOUCH, this is the disk structure */
struct Bucket
X{
X long agegen; /* generator for ages in this bkt */
X Centry entry[CEPERBK];
X};
X/*
X * scsi i/o
X */
enum
X{
X SCSIread = 0,
X SCSIwrite = 1,
X};
X/*
X * Process states
X */
enum
X{
X Dead = 0,
X Moribund,
X Zombie,
X Ready,
X Scheding,
X Running,
X Queueing,
X Sending,
X Recving,
X MMUing,
X Exiting,
X Inwait,
X Wakeme,
X Broken,
X};
X/*
X * Lights
X */
enum
X{
X Lreal = 0, /* blink in clock interrupt */
X Lintr, /* on while in interrupt */
X Lpanic, /* in panic */
X Lcwmap, /* in cw lookup */
X};
X/*
X * devnone block numbers
X */
enum
X{
X Cwio1 = 1,
X Cwio2,
X Cwxx1,
X Cwxx2,
X Cwxx3,
X Cwxx4,
X Cwdump1,
X Cwdump2,
X Cuidbuf,
X Cckbuf,
X};
X/*
X * error codes generated from the file server
X */
enum
X{
X Ebadspc = 1,
X Efid,
X Echar,
X Eopen,
X Ecount,
X Ealloc,
X Eqid,
X Eaccess,
X Eentry,
X Emode,
X Edir1,
X Edir2,
X Ephase,
X Eexist,
X Edot,
X Eempty,
X Ebadu,
X Enoattach,
X Ewstatb,
X Ewstatd,
X Ewstatg,
X Ewstatl,
X Ewstatm,
X Ewstato,
X Ewstatp,
X Ewstatq,
X Ewstatu,
X Ewstatv,
X Ename,
X Ewalk,
X Eronly,
X Efull,
X Eoffset,
X Elocked,
X Ebroken,
X Eauth,
X Eauth2,
X Efidinuse,
X Etoolong,
X Econvert,
X Eversion,
X Eauthdisabled,
X Eauthnone,
X Eedge,
X MAXERR
X};
X/*
X * device types
X */
enum
X{
X Devnone = 0,
X Devcon, /* console */
X Devwren, /* scsi disk drive */
X Devworm, /* scsi video drive */
X Devlworm, /* scsi video drive (labeled) */
X Devfworm, /* fake read-only device */
X Devjuke, /* jukebox */
X Devcw, /* cache with worm */
X Devro, /* readonly worm */
X Devmcat, /* multiple cat devices */
X Devmlev, /* multiple interleave devices */
X Devil, /* internet link */
X Devpart, /* partition */
X Devfloppy, /* floppy drive */
X Devide, /* IDE drive */
X Devswab, /* swab data between mem and device */
X Devdup, /* Dup drive */
X Devmirr, /* mirror devices */
X MAXDEV
X};
X/*
X * tags on block
X */
X/* DONT TOUCH, this is in disk structures */
enum
X{
X Tnone = 0,
X Tsuper, /* the super block */
X Tdir, /* directory contents */
X Tind1, /* points to blocks */
X Tind2, /* points to Tind1 */
X Tfile, /* file contents */
X Tfree, /* in free list */
X Tbuck, /* cache fs bucket */
X Tvirgo, /* fake worm virgin bits */
X Tcache, /* cw cache things */
X Tconfig, /* configuration block */
X MAXTAG
X};
X/*
X * flags to getbuf
X */
enum
X{
X Bread = (1<<0), /* read the block if miss */
X Bprobe = (1<<1), /* return null if miss */
X Bmod = (1<<2), /* buffer is dirty, needs writing */
X Bimm = (1<<3), /* write immediately on putbuf */
X Bres = (1<<4), /* reserved, never renammed */
X};
extern register Mach* m;
extern register User* u;
extern Talarm talarm;
Conf conf;
Cons cons;
X#define MACHP(n) ((Mach*)(MACHADDR+n*BY2PG))
X#pragma varargck type "Z" Device*
X#pragma varargck type "T" ulong
X#pragma varargck type "I" uchar*
X#pragma varargck type "E" uchar*
X#pragma varargck type "W" Filter*
X#pragma varargck type "G" int
extern int (*fsprotocol[])(Msgbuf*);
!
echo port/portfns.h
sed 's/^X//' >port/portfns.h <<'!'
void accessdir(Iobuf*, Dentry*, int, int);
void addfree(Device*, long, Superb*);
Alarm* alarm(int, void(*)(Alarm*, void*), void*);
void alarminit(void);
void arpstart(void);
void arginit(void);
char* authaname(Auth*);
void authinit(void);
void authfree(Auth*);
Auth* authnew(char*, char*);
int authread(File*, uchar*, int);
int authuid(Auth*);
char* authuname(Auth*);
int authwrite(File*, uchar*, int);
void cdiag(char*, int);
int cnumb(void);
Device* config(void);
int rawchar(int);
long bufalloc(Device*, int, long, int);
void buffree(Device*, long, int);
int byuid(void*, void*);
void cancel(Alarm*);
int canlock(Lock*);
int canqlock(QLock*);
void cfsdump(Filsys*);
Chan* chaninit(int, int, int);
void cmd_check(int, char*[]);
void cmd_users(int, char*[]);
void cmd_newuser(int, char*[]);
void cmd_netdb(int, char*[]);
void cmd_passwd(int, char*[]);
void cmd_printconf(int, char*[]);
int checkname(char*);
int checktag(Iobuf*, int, long);
int cksum(void*, int, int);
int cksum0(int, int);
void clock(ulong, ulong);
void clockinit(void);
void clockreload(ulong);
void cyclstart(void);
void dotrace(int);
int conschar(void);
void consinit(void (*)(char*, int));
void consreset(void);
void consstart(int);
int (*consgetc)(void);
void (*consputc)(int);
void (*consputs)(char*, int);
void consserve(void);
int conslock(void);
int con_attach(int, char*, char*);
int con_clone(int, int);
int con_create(int, char*, int, int, long, int);
int con_clri(int);
int con_fstat(int);
int con_open(int, int);
int con_read(int, char*, long, int);
int con_remove(int);
void con_rintr(int);
int con_session(void);
int con_walk(int, char*);
int con_write(int, char*, long, int);
int cwgrow(Device*, Superb*, int);
int cwfree(Device*, long);
void cwinit(Device*);
long cwraddr(Device*);
int cwread(Device*, long, void*);
void cwream(Device*);
void cwrecover(Device*);
long cwsaddr(Device*);
long cwsize(Device*);
long dbufread(Iobuf*, Dentry*, long, long, int);
int cwwrite(Device*, long, void*);
void datestr(char*, ulong);
void delay(int);
int devcmpr(Device*, Device*);
void devream(Device*, int);
void devrecover(Device*);
void devinit(Device*);
int devread(Device*, long, void*);
long devsize(Device*);
int devwrite(Device*, long, void*);
Iobuf* dnodebuf(Iobuf*, Dentry*, long, int, int);
Iobuf* dnodebuf1(Iobuf*, Dentry*, long, int, int);
void dofilter(Filter*, int, int, int);
int doremove(File*, int);
void dtrunc(Iobuf*, Dentry*, int);
int dumpblock(Device*);
void dumpregs(Ureg*);
void dumpstack(User*);
void exit(void);
XFloat famd(Float, int, int, int);
ulong fdf(Float, int);
void fileinit(Chan*);
XFile* filep(Chan*, ulong, int);
void firmware(void);
int fname(char*);
int fpair(char*, char*);
void formatinit(void);
int fread(void*, int);
void freealarm(Alarm*);
void freefp(File*);
void freewp(Wpath*);
XFilsys* fsstr(char*);
long fwormsize(Device*);
void fwormream(Device*);
void fworminit(Device*);
int fwormread(Device*, long, void*);
int fwormwrite(Device*, long, void*);
char* getauthlist(void);
Iobuf* getbuf(Device*, long, int);
void* getarg(void);
char* getwd(char*, char*);
int getc(void);
ulong getcallerpc(void*);
Dentry* getdir(Iobuf*, int);
Chan* getlcp(uchar*, long);
long getraddr(Device*);
void getstring(char*, int, int);
void gotolabel(Label*);
void hexdump(void*, int);
int iaccess(File*, Dentry*, int);
void* ialloc(ulong, int);
void ilock(Lock*);
void iunlock(Lock*);
long indfetch(Device*, long, long, long , int, int, int);
int ingroup(int, int);
int inh(int, uchar*);
void init0(void);
void iobufinit(void);
void* iobufmap(Iobuf*);
void iobufunmap(Iobuf*);
int iobufql(QLock*);
int jukeread(Device*, long, void*);
int jukewrite(Device*, long, void*);
void jukeinit(Device*);
void jukeream(Device*);
void jukerecover(Device*);
long jukesaddr(Device*);
long jukesize(Device*);
void kbdchar(int);
void lights(int, int);
void launchinit(void);
void localconfinit(void);
int leadgroup(int, int);
void lock(Lock*);
void lockinit(void);
void machinit(void);
Msgbuf* mballoc(int, Chan*, int);
void mbinit(void);
void mbfree(Msgbuf*);
ulong meminit(void);
Iobuf* movebuf(Iobuf*);
void mcatinit(Device*);
int mcatread(Device*, long, void*);
long mcatsize(Device*);
int mcatwrite(Device*, long, void*);
void mirrinit(Device*);
int mirrread(Device*, long, void*);
long mirrsize(Device*);
int mirrwrite(Device*, long, void*);
void mkqid(Qid*, Dentry*, int);
int mkqidcmp(Qid*, Dentry*);
void mkqid9p1(Qid9p1*, Qid*);
void mkqid9p2(Qid*, Qid9p1*, int);
void mlevinit(Device*);
int mlevread(Device*, long, void*);
long mlevsize(Device*);
int mlevwrite(Device*, long, void*);
int nametokey(char*, char*);
Alarm* newalarm(void);
XFile* newfp(void);
User* newproc(void);
Queue* newqueue(int);
void newstart(void);
Wpath* newwp(void);
Auth* newauth(void);
int nvrcheck(void);
int nvread(int, void*, int);
char* nvrgetconfig(void);
int nvrsetconfig(char*);
int nvwrite(int, void*, int);
int walkto(char*);
int no(void*);
long number(char*, int, int);
void online(void);
void otherinit(void);
void panic(char*, ...);
void partinit(Device*);
int partread(Device*, long, void*);
long partsize(Device*);
int partwrite(Device*, long, void*);
void prdate(void);
void preread(Device*, long);
void prflush(void);
int prime(long);
void printinit(void);
void procinit(void);
void putbuf(Iobuf*);
long qidpathgen(Device*);
void qlock(QLock*);
void qunlock(QLock*);
void rahead(void);
void ready(User*);
void ream(Filsys*);
void* recv(Queue*, int);
void restartprint(Alarm*);
void rlock(RWlock*);
void rootream(Device*, long);
int roread(Device*, long, void*);
void rstate(Chan*, int);
ulong rtc2sec(Rtc *);
void runlock(RWlock*);
User* runproc(void);
void sched(void);
void schedinit(void);
int scsiio(Device*, int, uchar*, int, void*, int);
void sec2rtc(ulong, Rtc *);
void send(Queue*, void*);
void serve(void);
int serve9p1(Msgbuf*);
int serve9p2(Msgbuf*);
int setlabel(Label*);
void settag(Iobuf*, int, long);
void settime(ulong);
void sleep(Rendez*, int(*)(void*), void*);
void sntpinit(void);
int splhi(void);
int spllo(void);
void splx(int);
void startprint(void);
int strtouid(char*);
long superaddr(Device*);
void superream(Device*, long);
void swab(void*, int);
void sync(char*);
int syncblock(void);
long syscall(Ureg*);
void sysinit(void);
int Tfmt(Fmt*);
ulong time(void);
ulong nextime(ulong, int, int);
Tlock* tlocked(Iobuf*, Dentry*);
void tsleep(Rendez*, int(*)(void*), void*, int);
void touser(void);
ulong toytime(void);
ulong rtctime(void);
void setrtc(ulong);
void uidtostr(char*, int, int);
Uid* uidpstr(char*);
void unlock(Lock*);
void userinit(void(*)(void), void*, char*);
void vecinit(void);
void wakeup(Rendez*);
void wbflush(void);
void wlock(RWlock*);
void wormcopy(void);
void wormprobe(void);
int dowcp(void);
int dowcmp(void);
void synccopy(void);
void waitsec(int);
long wormsearch(Device*, int, long, long);
int wormread(Device*, long, void*);
long wormsize(Device*);
long wormsizeside(Device *, int side);
void wormsidestarts(Device *dev, int side, Sidestarts *stp);
int wormwrite(Device*, long, void*);
void wreninit(Device*);
int wrenread(Device*, long, void*);
long wrensize(Device*);
int wrenwrite(Device*, long, void*);
void wunlock(RWlock*);
void cmd_exec(char*);
void cmd_install(char*, char*, void (*)(int, char*[]));
ulong flag_install(char*, char*);
int chartoip(uchar *, char *);
int isvalidip(uchar*);
int nhgets(uchar*);
long nhgetl(uchar*);
void hnputs(uchar*, int);
void hnputl(uchar*, long);
void pokewcp(void);
!
^ permalink raw reply [flat|nested] 2+ messages in thread
* [9fans] jukebox configuration changes
@ 2002-11-01 9:43 Geoff Collyer
0 siblings, 0 replies; 2+ messages in thread
From: Geoff Collyer @ 2002-11-01 9:43 UTC (permalink / raw)
To: 9fans
I've just modified the file server kernel to cope with different
DSIZEs (optical disc sizes) per jukebox and even per disc. It's now
also possible to change FIXEDSIZES in /sys/src/fs/*/9*.c rather than
having to edit dev/juke.c before compiling each distinct kernel.
The way it used to work was that DSIZE was the size in RBUFSIZE-byte
blocks of each optical disc *side*. However, it is possible to use
discs of different sizes within a single jukebox: you set FIXEDSIZES
to 0 and pay with a delay at each boot while the file server pulls
each disc side in and asks the drive how big it is. It's probably
wiser to use discs of the same size and set FIXEDSIZES to 1, which
makes the file server just look at one disc and assume that all the
sides are the size of its. So DSIZE was only used for computing the
statistics that "statw" prints on the console, for working out sizes
in the cache save and restore commands, and in the implementation of
"check touch". Those operations could be led astray if DSIZE wasn't
the size in Plan 9 blocks of all the optical discs in all jukeboxes on
a given file server. If one wanted to put a jukebox with 5.2GB discs
and one with 2.6GB discs on the same file server, it wouldn't work
perfectly.
DSIZE is gone. It's now possible to mix jukeboxes with discs of
different sizes, and even, if you're willing to put up with the
delays, to use discs of different sizes within a single jukebox, and
everything should now work right.
The whole thing compiles but I want to test it tomorrow (after the
third jukebox arrives) before I offer the code to anyone else.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2002-11-02 9:37 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-11-02 9:37 [9fans] jukebox configuration changes Geoff Collyer
-- strict thread matches above, loose matches on Subject: below --
2002-11-01 9:43 Geoff Collyer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).