summaryrefslogtreecommitdiff
path: root/sys/src/cmd/cwfs/cw.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/cwfs/cw.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/cwfs/cw.c')
-rwxr-xr-xsys/src/cmd/cwfs/cw.c2268
1 files changed, 2268 insertions, 0 deletions
diff --git a/sys/src/cmd/cwfs/cw.c b/sys/src/cmd/cwfs/cw.c
new file mode 100755
index 000000000..a626658bb
--- /dev/null
+++ b/sys/src/cmd/cwfs/cw.c
@@ -0,0 +1,2268 @@
+/*
+ * cached-worm device
+ */
+#include "all.h"
+
+#define CDEV(d) ((d)->cw.c)
+#define WDEV(d) ((d)->cw.w)
+#define RDEV(d) ((d)->cw.ro)
+
+enum {
+ DEBUG = 0,
+ FIRST = SUPER_ADDR,
+
+ ADDFREE = 100,
+ CACHE_ADDR = SUPER_ADDR,
+ MAXAGE = 10000,
+};
+
+/* cache state */
+enum
+{
+ /* states -- beware these are recorded on the cache */
+ /* cache worm */
+ Cnone = 0, /* 0 ? */
+ Cdirty, /* 1 0 */
+ Cdump, /* 1 0->1 */
+ Cread, /* 1 1 */
+ Cwrite, /* 2 1 */
+ Cdump1, /* inactive form of dump */
+ Cerror,
+
+ /* opcodes -- these are not recorded */
+ Onone,
+ Oread,
+ Owrite,
+ Ogrow,
+ Odump,
+ Orele,
+ Ofree,
+};
+
+typedef struct Cw Cw;
+struct Cw
+{
+ Device* dev;
+ Device* cdev;
+ Device* wdev;
+ Device* rodev;
+ Cw* link;
+
+ int dbucket; /* last bucket dumped */
+ Off daddr; /* last block dumped */
+ Off ncopy;
+ int nodump;
+/*
+ * following are cached variables for dumps
+ */
+ Off fsize;
+ Off ndump;
+ int depth;
+ int all; /* local flag to recur on modified dirs */
+ int allflag; /* global flag to recur on modified dirs */
+ Off falsehits; /* times recur found modified blocks */
+ struct {
+ char name[500];
+ char namepad[NAMELEN+10];
+ };
+};
+
+static char* cwnames[] =
+{
+ [Cnone] "none",
+ [Cdirty] "dirty",
+ [Cdump] "dump",
+ [Cread] "read",
+ [Cwrite] "write",
+ [Cdump1] "dump1",
+ [Cerror] "error",
+
+ [Onone] "none",
+ [Oread] "read",
+ [Owrite] "write",
+ [Ogrow] "grow",
+ [Odump] "dump",
+ [Orele] "rele",
+};
+
+int oldcachefmt = 1;
+
+Centry* getcentry(Bucket*, Off);
+int cwio(Device*, Off, void*, int);
+void cmd_cwcmd(int, char*[]);
+
+/*
+ * console command
+ * initiate a dump
+ */
+void
+cmd_dump(int argc, char *argv[])
+{
+ Filsys *fs;
+
+ fs = cons.curfs;
+ if(argc > 1)
+ fs = fsstr(argv[1]);
+ if(fs == 0) {
+ print("%s: unknown file system\n", argv[1]);
+ return;
+ }
+ cfsdump(fs);
+}
+
+/*
+ * console command
+ * worm stats
+ */
+static void
+cmd_statw(int, char*[])
+{
+ Filsys *fs;
+ Iobuf *p;
+ Superb *sb;
+ Cache *h;
+ Bucket *b;
+ Centry *c, *ce;
+ Off m, nw, bw, state[Onone];
+ Off sbfsize, sbcwraddr, sbroraddr, sblast, sbnext;
+ Off hmsize, hmaddr, dsize, dsizepct;
+ Device *dev;
+ Cw *cw;
+ int s;
+
+ fs = cons.curfs;
+ dev = fs->dev;
+ if(dev->type != Devcw) {
+ print("curfs not type cw\n");
+ return;
+ }
+
+ cw = dev->private;
+ if(cw == 0) {
+ print("curfs not inited\n");
+ return;
+ }
+
+ print("cwstats %s\n", fs->name);
+
+ sbfsize = 0;
+ sbcwraddr = 0;
+ sbroraddr = 0;
+ sblast = 0;
+ sbnext = 0;
+
+ print("\tfilesys %s\n", fs->name);
+// print("\tnio =%7W%7W%7W\n", cw->ncwio+0, cw->ncwio+1, cw->ncwio+2);
+ p = getbuf(dev, cwsaddr(dev), Brd);
+ if(!p || checktag(p, Tsuper, QPSUPER)) {
+ print("cwstats: checktag super\n");
+ if(p) {
+ putbuf(p);
+ p = 0;
+ }
+ }
+ if(p) {
+ sb = (Superb*)p->iobuf;
+ sbfsize = sb->fsize;
+ sbcwraddr = sb->cwraddr;
+ sbroraddr = sb->roraddr;
+ sblast = sb->last;
+ sbnext = sb->next;
+ putbuf(p);
+ }
+
+ p = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
+ if(!p || checktag(p, Tcache, QPSUPER)) {
+ print("cwstats: checktag c bucket\n");
+ if(p)
+ putbuf(p);
+ return;
+ }
+ h = (Cache*)p->iobuf;
+ hmaddr = h->maddr;
+ hmsize = h->msize;
+
+ print("\t\tmaddr = %8lld\n", (Wideoff)hmaddr);
+ print("\t\tmsize = %8lld\n", (Wideoff)hmsize);
+ print("\t\tcaddr = %8lld\n", (Wideoff)h->caddr);
+ print("\t\tcsize = %8lld\n", (Wideoff)h->csize);
+ print("\t\tsbaddr = %8lld\n", (Wideoff)h->sbaddr);
+ print("\t\tcraddr = %8lld %8lld\n",
+ (Wideoff)h->cwraddr, (Wideoff)sbcwraddr);
+ print("\t\troaddr = %8lld %8lld\n",
+ (Wideoff)h->roraddr, (Wideoff)sbroraddr);
+ /* print stats in terms of (first-)disc sides */
+ dsize = wormsizeside(dev, 0);
+ if (dsize < 1) {
+ if (DEBUG)
+ print("wormsizeside returned size %lld for %Z side 0\n",
+ (Wideoff)dsize, dev);
+ dsize = h->wsize; /* it's probably a fake worm */
+ if (dsize < 1)
+ dsize = 1000; /* don't divide by zero */
+ }
+ dsizepct = dsize/100;
+ print("\t\tfsize = %8lld %8lld %2lld+%2lld%%\n", (Wideoff)h->fsize,
+ (Wideoff)sbfsize, (Wideoff)h->fsize/dsize,
+ (Wideoff)(h->fsize%dsize)/dsizepct);
+ print("\t\tslast = %8lld\n", (Wideoff)sblast);
+ print("\t\tsnext = %8lld\n", (Wideoff)sbnext);
+ print("\t\twmax = %8lld %2lld+%2lld%%\n",
+ (Wideoff)h->wmax, (Wideoff)h->wmax/dsize,
+ (Wideoff)(h->wmax%dsize)/dsizepct);
+ print("\t\twsize = %8lld %2lld+%2lld%%\n",
+ (Wideoff)h->wsize, (Wideoff)h->wsize/dsize,
+ (Wideoff)(h->wsize%dsize)/dsizepct);
+ putbuf(p);
+
+ bw = 0; /* max filled bucket */
+ memset(state, 0, sizeof(state));
+ for(m = 0; m < hmsize; m++) {
+ p = getbuf(cw->cdev, hmaddr + m/BKPERBLK, Brd);
+ if(!p || checktag(p, Tbuck, hmaddr + m/BKPERBLK)) {
+ print("cwstats: checktag c bucket\n");
+ if(p)
+ putbuf(p);
+ return;
+ }
+ b = (Bucket*)p->iobuf + m%BKPERBLK;
+ ce = b->entry + CEPERBK;
+ nw = 0;
+ for(c = b->entry; c < ce; c++) {
+ s = c->state;
+ state[s]++;
+ if(s != Cnone && s != Cread)
+ nw++;
+ }
+ putbuf(p);
+ if(nw > bw)
+ bw = nw;
+ }
+ for(s = Cnone; s < Cerror; s++)
+ print("\t\t%6lld %s\n", (Wideoff)state[s], cwnames[s]);
+ print("\t\tcache %2lld%% full\n", ((Wideoff)bw*100)/CEPERBK);
+}
+
+int
+dumpblock(Device *dev)
+{
+ Iobuf *p, *cb, *p1, *p2;
+ Cache *h;
+ Centry *c, *ce, *bc;
+ Bucket *b;
+ Off m, a, msize, maddr, wmax, caddr;
+ int s1, s2, count;
+ Cw *cw;
+
+ cw = dev->private;
+ if(cw == 0 || cw->nodump)
+ return 0;
+
+ cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
+ h = (Cache*)cb->iobuf;
+ msize = h->msize;
+ maddr = h->maddr;
+ wmax = h->wmax;
+ caddr = h->caddr;
+ putbuf(cb);
+
+ for(m=msize; m>=0; m--) {
+ a = cw->dbucket + 1;
+ if(a < 0 || a >= msize)
+ a = 0;
+ cw->dbucket = a;
+ p = getbuf(cw->cdev, maddr + a/BKPERBLK, Brd);
+ b = (Bucket*)p->iobuf + a%BKPERBLK;
+ ce = b->entry + CEPERBK;
+ bc = 0;
+ for(c = b->entry; c < ce; c++)
+ if(c->state == Cdump) {
+ if(bc == 0) {
+ bc = c;
+ continue;
+ }
+ if(c->waddr < cw->daddr) {
+ if(bc->waddr < cw->daddr &&
+ bc->waddr > c->waddr)
+ bc = c;
+ continue;
+ }
+ if(bc->waddr < cw->daddr ||
+ bc->waddr > c->waddr)
+ bc = c;
+ }
+ if(bc) {
+ c = bc;
+ goto found;
+ }
+ putbuf(p);
+ }
+ if(cw->ncopy) {
+ print("%lld blocks copied to worm\n", (Wideoff)cw->ncopy);
+ cw->ncopy = 0;
+ }
+ cw->nodump = 1;
+ return 0;
+
+found:
+ if (oldcachefmt)
+ a = a*CEPERBK + (c - b->entry) + caddr;
+ else
+ a += (c - b->entry)*msize + caddr;
+ p1 = getbuf(devnone, Cwdump1, 0);
+ count = 0;
+
+retry:
+ count++;
+ if(count > 10 || devread(cw->cdev, a, p1->iobuf))
+ goto stop;
+ m = c->waddr;
+ cw->daddr = m;
+ s1 = devwrite(cw->wdev, m, p1->iobuf);
+ if(s1) {
+ p2 = getbuf(devnone, Cwdump2, 0);
+ s2 = devread(cw->wdev, m, p2->iobuf);
+ if(s2) {
+ if(s1 == 0x61 && s2 == 0x60) {
+ putbuf(p2);
+ goto retry;
+ }
+ goto stop1;
+ }
+ if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE))
+ goto stop1;
+ putbuf(p2);
+ }
+ /*
+ * reread and compare
+ */
+ if(conf.dumpreread) {
+ p2 = getbuf(devnone, Cwdump2, 0);
+ s1 = devread(cw->wdev, m, p2->iobuf);
+ if(s1)
+ goto stop1;
+ if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
+ print("reread C%lld W%lld didnt compare\n",
+ (Wideoff)a, (Wideoff)m);
+ goto stop1;
+ }
+ putbuf(p2);
+ }
+
+ putbuf(p1);
+ c->state = Cread;
+ p->flags |= Bmod;
+ putbuf(p);
+
+ if(m > wmax) {
+ cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bres);
+ h = (Cache*)cb->iobuf;
+ if(m > h->wmax)
+ h->wmax = m;
+ putbuf(cb);
+ }
+ cw->ncopy++;
+ return 1;
+
+stop1:
+ putbuf(p2);
+ putbuf(p1);
+ c->state = Cdump1;
+ p->flags |= Bmod;
+ putbuf(p);
+ return 1;
+
+stop:
+ putbuf(p1);
+ putbuf(p);
+ print("stopping dump!!\n");
+ cw->nodump = 1;
+ return 0;
+}
+
+void
+cwinit1(Device *dev)
+{
+ Cw *cw;
+ static int first;
+
+ cw = dev->private;
+ if(cw)
+ return;
+
+ if(first == 0) {
+ cmd_install("dump", "-- make dump backup to worm", cmd_dump);
+ cmd_install("statw", "-- cache/worm stats", cmd_statw);
+ cmd_install("cwcmd", "subcommand -- cache/worm errata", cmd_cwcmd);
+ roflag = flag_install("ro", "-- ro reads and writes");
+ first = 1;
+ }
+ cw = malloc(sizeof(Cw));
+ dev->private = cw;
+
+ cw->allflag = 0;
+
+ cw->dev = dev;
+ cw->cdev = CDEV(dev);
+ cw->wdev = WDEV(dev);
+ cw->rodev = RDEV(dev);
+
+ devinit(cw->cdev);
+ devinit(cw->wdev);
+}
+
+void
+cwinit(Device *dev)
+{
+ Cw *cw;
+ Cache *h;
+ Iobuf *cb, *p;
+ Off l, m;
+
+ cwinit1(dev);
+
+ cw = dev->private;
+ l = devsize(cw->wdev);
+ cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bres);
+ h = (Cache*)cb->iobuf;
+ h->toytime = toytime() + SECOND(30);
+ h->time = time(nil);
+ m = h->wsize;
+ if(l != m) {
+ print("wdev changed size %lld to %lld\n",
+ (Wideoff)m, (Wideoff)l);
+ h->wsize = l;
+ cb->flags |= Bmod;
+ }
+
+ for(m=0; m<h->msize; m++) {
+ p = getbuf(cw->cdev, h->maddr + m/BKPERBLK, Brd);
+ if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
+ panic("cwinit: checktag c bucket");
+ putbuf(p);
+ }
+ putbuf(cb);
+}
+
+Off
+cwsaddr(Device *dev)
+{
+ Iobuf *cb;
+ Off sa;
+
+ cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
+ sa = ((Cache*)cb->iobuf)->sbaddr;
+ putbuf(cb);
+ return sa;
+}
+
+Off
+cwraddr(Device *dev)
+{
+ Iobuf *cb;
+ Off ra;
+
+ switch(dev->type) {
+ default:
+ print("unknown dev in cwraddr %Z\n", dev);
+ return 1;
+
+ case Devcw:
+ cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
+ ra = ((Cache*)cb->iobuf)->cwraddr;
+ break;
+
+ case Devro:
+ cb = getbuf(CDEV(dev->ro.parent), CACHE_ADDR, Brd|Bres);
+ ra = ((Cache*)cb->iobuf)->roraddr;
+ break;
+ }
+ putbuf(cb);
+ return ra;
+}
+
+Devsize
+cwsize(Device *dev)
+{
+ Iobuf *cb;
+ Devsize fs;
+
+ cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
+ fs = ((Cache*)cb->iobuf)->fsize;
+ putbuf(cb);
+ return fs;
+}
+
+int
+cwread(Device *dev, Off b, void *c)
+{
+ return cwio(dev, b, c, Oread) == Cerror;
+}
+
+int
+cwwrite(Device *dev, Off b, void *c)
+{
+ return cwio(dev, b, c, Owrite) == Cerror;
+}
+
+int
+roread(Device *dev, Off b, void *c)
+{
+ Device *d;
+ int s;
+
+ /*
+ * maybe better is to try buffer pool first
+ */
+ d = dev->ro.parent;
+ if(d == 0 || d->type != Devcw ||
+ d->private == 0 || RDEV(d) != dev) {
+ print("bad rodev %Z\n", dev);
+ return 1;
+ }
+ s = cwio(d, b, 0, Onone);
+ if(s == Cdump || s == Cdump1 || s == Cread) {
+ s = cwio(d, b, c, Oread);
+ if(s == Cdump || s == Cdump1 || s == Cread) {
+ if(cons.flags & roflag)
+ print("roread: %Z %lld -> %Z(hit)\n",
+ dev, (Wideoff)b, d);
+ return 0;
+ }
+ }
+ if(cons.flags & roflag)
+ print("roread: %Z %lld -> %Z(miss)\n",
+ dev, (Wideoff)b, WDEV(d));
+ return devread(WDEV(d), b, c);
+}
+
+int
+cwio(Device *dev, Off addr, void *buf, int opcode)
+{
+ Iobuf *p, *p1, *p2, *cb;
+ Cache *h;
+ Bucket *b;
+ Centry *c;
+ Off bn, a1, a2, max, newmax;
+ int state;
+ Cw *cw;
+
+ cw = dev->private;
+
+ cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
+ h = (Cache*)cb->iobuf;
+ if(toytime() >= h->toytime) {
+ cb->flags |= Bmod;
+ h->toytime = toytime() + SECOND(30);
+ h->time = time(nil);
+ }
+
+ if(addr < 0) {
+ putbuf(cb);
+ return Cerror;
+ }
+
+ bn = addr % h->msize;
+ a1 = h->maddr + bn/BKPERBLK;
+ if (oldcachefmt)
+ a2 = bn*CEPERBK + h->caddr;
+ else
+ a2 = bn + h->caddr;
+ max = h->wmax;
+
+ putbuf(cb);
+ newmax = 0;
+
+ p = getbuf(cw->cdev, a1, Brd|Bmod);
+ if(!p || checktag(p, Tbuck, a1))
+ panic("cwio: checktag c bucket");
+ b = (Bucket*)p->iobuf + bn%BKPERBLK;
+
+ c = getcentry(b, addr);
+ if(c == 0) {
+ putbuf(p);
+ print("%Z disk cache bucket %lld is full\n",
+ cw->cdev, (Wideoff)a1);
+ return Cerror;
+ }
+ if (oldcachefmt)
+ a2 += c - b->entry;
+ else
+ a2 += (c - b->entry) * h->msize;
+
+ state = c->state;
+ switch(opcode) {
+ default:
+ goto bad;
+
+ case Onone:
+ break;
+
+ case Oread:
+ switch(state) {
+ default:
+ goto bad;
+
+ case Cread:
+ if(!devread(cw->cdev, a2, buf))
+ break;
+ c->state = Cnone;
+
+ case Cnone:
+ if(devread(cw->wdev, addr, buf)) {
+ state = Cerror;
+ break;
+ }
+ if(addr > max)
+ newmax = addr;
+ if(!devwrite(cw->cdev, a2, buf))
+ c->state = Cread;
+ break;
+
+ case Cdirty:
+ case Cdump:
+ case Cdump1:
+ case Cwrite:
+ if(devread(cw->cdev, a2, buf))
+ state = Cerror;
+ break;
+ }
+ break;
+
+ case Owrite:
+ switch(state) {
+ default:
+ goto bad;
+
+ case Cdump:
+ case Cdump1:
+ /*
+ * this is hard part -- a dump block must be
+ * sent to the worm if it is rewritten.
+ * if this causes an error, there is no
+ * place to save the dump1 data. the block
+ * is just reclassified as 'dump1' (botch)
+ */
+ p1 = getbuf(devnone, Cwio1, 0);
+ if(devread(cw->cdev, a2, p1->iobuf)) {
+ putbuf(p1);
+ print("cwio: write induced dump error - r cache\n");
+
+ casenone:
+ if(devwrite(cw->cdev, a2, buf)) {
+ state = Cerror;
+ break;
+ }
+ c->state = Cdump1;
+ break;
+ }
+ if(devwrite(cw->wdev, addr, p1->iobuf)) {
+ p2 = getbuf(devnone, Cwio2, 0);
+ if(devread(cw->wdev, addr, p2->iobuf)) {
+ putbuf(p1);
+ putbuf(p2);
+ print("cwio: write induced dump error - r+w worm\n");
+ goto casenone;
+ }
+ if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
+ putbuf(p1);
+ putbuf(p2);
+ print("cwio: write induced dump error - w worm\n");
+ goto casenone;
+ }
+ putbuf(p2);
+ }
+ putbuf(p1);
+ c->state = Cread;
+ if(addr > max)
+ newmax = addr;
+ cw->ncopy++;
+
+ case Cnone:
+ case Cread:
+ if(devwrite(cw->cdev, a2, buf)) {
+ state = Cerror;
+ break;
+ }
+ c->state = Cwrite;
+ break;
+
+ case Cdirty:
+ case Cwrite:
+ if(devwrite(cw->cdev, a2, buf))
+ state = Cerror;
+ break;
+ }
+ break;
+
+ case Ogrow:
+ if(state != Cnone) {
+ print("%Z for block %lld cwgrow with state = %s\n",
+ cw->cdev, (Wideoff)addr, cwnames[state]);
+ break;
+ }
+ c->state = Cdirty;
+ break;
+
+ case Odump:
+ if(state != Cdirty) { /* BOTCH */
+ print("%Z for block %lld cwdump with state = %s\n",
+ cw->cdev, (Wideoff)addr, cwnames[state]);
+ break;
+ }
+ c->state = Cdump;
+ cw->ndump++; /* only called from dump command */
+ break;
+
+ case Orele:
+ if(state != Cwrite) {
+ if(state != Cdump1)
+ print("%Z for block %lld cwrele with state = %s\n",
+ cw->cdev, (Wideoff)addr, cwnames[state]);
+ break;
+ }
+ c->state = Cnone;
+ break;
+
+ case Ofree:
+ if(state == Cwrite || state == Cread)
+ c->state = Cnone;
+ break;
+ }
+ if(DEBUG)
+ print("cwio: %Z %lld s=%s o=%s ns=%s\n",
+ dev, (Wideoff)addr, cwnames[state],
+ cwnames[opcode],
+ cwnames[c->state]);
+ putbuf(p);
+ if(newmax) {
+ cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bres);
+ h = (Cache*)cb->iobuf;
+ if(newmax > h->wmax)
+ h->wmax = newmax;
+ putbuf(cb);
+ }
+ return state;
+
+bad:
+ print("%Z block %lld cw state = %s; cw opcode = %s",
+ dev, (Wideoff)addr, cwnames[state], cwnames[opcode]);
+ return Cerror;
+}
+
+
+int
+cwgrow(Device *dev, Superb *sb, int uid)
+{
+ char str[NAMELEN];
+ Iobuf *cb;
+ Cache *h;
+ Filsys *filsys;
+ Off fs, nfs, ws;
+
+ cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bmod|Bres);
+ h = (Cache*)cb->iobuf;
+ ws = h->wsize;
+ fs = h->fsize;
+ if(fs >= ws)
+ return 0;
+ nfs = fs + ADDFREE;
+ if(nfs >= ws)
+ nfs = ws;
+ h->fsize = nfs;
+ putbuf(cb);
+
+ sb->fsize = nfs;
+ filsys = dev2fs(dev);
+ if (filsys == nil)
+ print("%Z", dev);
+ else
+ print("%s", filsys->name);
+ uidtostr(str, uid, 1);
+ print(" grow from %lld to %lld limit %lld by %s uid=%d\n",
+ (Wideoff)fs, (Wideoff)nfs, (Wideoff)ws, str, uid);
+ for(nfs--; nfs>=fs; nfs--)
+ switch(cwio(dev, nfs, 0, Ogrow)) {
+ case Cerror:
+ return 0;
+ case Cnone:
+ addfree(dev, nfs, sb);
+ }
+ return 1;
+}
+
+int
+cwfree(Device *dev, Off addr)
+{
+ int state;
+
+ if(dev->type == Devcw) {
+ state = cwio(dev, addr, 0, Ofree);
+ if(state != Cdirty)
+ return 1; /* do not put in freelist */
+ }
+ return 0; /* put in freelist */
+}
+
+#ifdef unused
+int
+bktcheck(Bucket *b)
+{
+ Centry *c, *c1, *c2, *ce;
+ int err;
+
+ err = 0;
+ if(b->agegen < CEPERBK || b->agegen > MAXAGE) {
+ print("agegen %ld\n", b->agegen);
+ err = 1;
+ }
+
+ ce = b->entry + CEPERBK;
+ c1 = 0; /* lowest age last pass */
+ for(;;) {
+ c2 = 0; /* lowest age this pass */
+ for(c = b->entry; c < ce; c++) {
+ if(c1 != 0 && c != c1) {
+ if(c->age == c1->age) {
+ print("same age %d\n", c->age);
+ err = 1;
+ }
+ if(c1->waddr == c->waddr)
+ if(c1->state != Cnone)
+ if(c->state != Cnone) {
+ print("same waddr %lld\n",
+ (Wideoff)c->waddr);
+ err = 1;
+ }
+ }
+ if(c1 != 0 && c->age <= c1->age)
+ continue;
+ if(c2 == 0 || c->age < c2->age)
+ c2 = c;
+ }
+ if(c2 == 0)
+ break;
+ c1 = c2;
+ if(c1->age >= b->agegen) {
+ print("age >= generator %d %ld\n", c1->age, b->agegen);
+ err = 1;
+ }
+ }
+ return err;
+}
+#endif
+
+void
+resequence(Bucket *b)
+{
+ Centry *c, *ce, *cr;
+ int age, i;
+
+ ce = b->entry + CEPERBK;
+ for(c = b->entry; c < ce; c++) {
+ c->age += CEPERBK;
+ if(c->age < CEPERBK)
+ c->age = MAXAGE;
+ }
+ b->agegen += CEPERBK;
+
+ age = 0;
+ for(i=0;; i++) {
+ cr = 0;
+ for(c = b->entry; c < ce; c++) {
+ if(c->age < i)
+ continue;
+ if(cr == 0 || c->age < age) {
+ cr = c;
+ age = c->age;
+ }
+ }
+ if(cr == 0)
+ break;
+ cr->age = i;
+ }
+ b->agegen = i;
+ cons.nreseq++;
+}
+
+Centry*
+getcentry(Bucket *b, Off addr)
+{
+ Centry *c, *ce, *cr;
+ int s, age;
+
+ /*
+ * search for cache hit
+ * find oldest block as byproduct
+ */
+ ce = b->entry + CEPERBK;
+ age = 0;
+ cr = 0;
+ for(c = b->entry; c < ce; c++) {
+ s = c->state;
+ if(s == Cnone) {
+ cr = c;
+ age = 0;
+ continue;
+ }
+ if(c->waddr == addr)
+ goto found;
+ if(s == Cread)
+ if(cr == 0 || c->age < age) {
+ cr = c;
+ age = c->age;
+ }
+ }
+
+ /*
+ * remap entry
+ */
+ c = cr;
+ if(c == 0)
+ return 0; /* bucket is full */
+
+ c->state = Cnone;
+ c->waddr = addr;
+
+found:
+ /*
+ * update the age to get filo cache.
+ * small number in age means old
+ */
+ if(!cons.noage || c->state == Cnone) {
+ age = b->agegen;
+ c->age = age;
+ age++;
+ b->agegen = age;
+ if(age < 0 || age >= MAXAGE)
+ resequence(b);
+ }
+ return c;
+}
+
+/*
+ * ream the cache
+ * calculate new buckets
+ */
+Iobuf*
+cacheinit(Device *dev)
+{
+ Iobuf *cb, *p;
+ Cache *h;
+ Device *cdev;
+ Off m;
+
+ print("cache init %Z\n", dev);
+ cdev = CDEV(dev);
+ devinit(cdev);
+
+ cb = getbuf(cdev, CACHE_ADDR, Bmod|Bres);
+ memset(cb->iobuf, 0, RBUFSIZE);
+ settag(cb, Tcache, QPSUPER);
+ h = (Cache*)cb->iobuf;
+
+ /*
+ * calculate csize such that
+ * tsize = msize/BKPERBLK + csize and
+ * msize = csize/CEPERBK
+ */
+ h->maddr = CACHE_ADDR + 1;
+ m = devsize(cdev) - h->maddr;
+ h->csize = ((Devsize)(m-1) * CEPERBK*BKPERBLK) / (CEPERBK*BKPERBLK+1);
+ h->msize = h->csize/CEPERBK - 5;
+ while(!prime(h->msize))
+ h->msize--;
+ h->csize = h->msize*CEPERBK;
+ h->caddr = h->maddr + (h->msize+BKPERBLK-1)/BKPERBLK;
+ h->wsize = devsize(WDEV(dev));
+
+ if(h->msize <= 0)
+ panic("cache too small");
+ if(h->caddr + h->csize > m)
+ panic("cache size error");
+
+ /*
+ * setup cache map
+ */
+ for(m=h->maddr; m<h->caddr; m++) {
+ p = getbuf(cdev, m, Bmod);
+ memset(p->iobuf, 0, RBUFSIZE);
+ settag(p, Tbuck, m);
+ putbuf(p);
+ }
+ print("done cacheinit\n");
+ return cb;
+}
+
+Off
+getstartsb(Device *dev)
+{
+ Filsys *f;
+ Startsb *s;
+
+ for(f=filsys; f->name; f++)
+ if(devcmpr(f->dev, dev) == 0) {
+ for(s=startsb; s->name; s++)
+ if(strcmp(f->name, s->name) == 0)
+ return s->startsb;
+ print(
+ "getstartsb: no special starting superblock for %Z %s\n",
+ dev, f->name);
+ return FIRST;
+ }
+ print("getstartsb: no filsys for device %Z\n", dev);
+ return FIRST;
+}
+
+/*
+ * ream the cache
+ * calculate new buckets
+ * get superblock from
+ * last worm dump block.
+ */
+void
+cwrecover(Device *dev)
+{
+ Iobuf *p, *cb;
+ Cache *h;
+ Superb *s;
+ Off m, baddr;
+ Device *wdev;
+
+// print("cwrecover %Z\n", dev); // DEBUG
+ cwinit1(dev);
+ wdev = WDEV(dev);
+
+ p = getbuf(devnone, Cwxx1, 0);
+ s = (Superb*)p->iobuf;
+ baddr = 0;
+ m = getstartsb(dev);
+ localconfinit();
+ if(conf.firstsb)
+ m = conf.firstsb;
+ for(;;) {
+ memset(p->iobuf, 0, RBUFSIZE);
+ if(devread(wdev, m, p->iobuf) ||
+ checktag(p, Tsuper, QPSUPER))
+ break;
+ baddr = m;
+ m = s->next;
+ print("dump %lld is good; %lld next\n", (Wideoff)baddr, (Wideoff)m);
+ if(baddr == conf.recovsb)
+ break;
+ }
+ putbuf(p);
+ if(!baddr)
+ panic("recover: no superblock");
+
+ p = getbuf(wdev, baddr, Brd);
+ s = (Superb*)p->iobuf;
+
+ cb = cacheinit(dev);
+ h = (Cache*)cb->iobuf;
+ h->sbaddr = baddr;
+ h->cwraddr = s->cwraddr;
+ h->roraddr = s->roraddr;
+ h->fsize = s->fsize + 100; /* this must be conservative */
+ if(conf.recovcw)
+ h->cwraddr = conf.recovcw;
+ if(conf.recovro)
+ h->roraddr = conf.recovro;
+
+ putbuf(cb);
+ putbuf(p);
+
+ p = getbuf(dev, baddr, Brd|Bmod);
+ s = (Superb*)p->iobuf;
+
+ memset(&s->fbuf, 0, sizeof(s->fbuf));
+ s->fbuf.free[0] = 0;
+ s->fbuf.nfree = 1;
+ s->tfree = 0;
+ if(conf.recovcw)
+ s->cwraddr = conf.recovcw;
+ if(conf.recovro)
+ s->roraddr = conf.recovro;
+
+ putbuf(p);
+ print("done recover\n");
+}
+
+/*
+ * ream the cache
+ * calculate new buckets
+ * initialize superblock.
+ */
+void
+cwream(Device *dev)
+{
+ Iobuf *p, *cb;
+ Cache *h;
+ Superb *s;
+ Off m, baddr;
+ Device *cdev;
+
+ print("cwream %Z\n", dev);
+ cwinit1(dev);
+ cdev = CDEV(dev);
+ devinit(cdev);
+
+ baddr = FIRST; /* baddr = super addr
+ baddr+1 = cw root
+ baddr+2 = ro root
+ baddr+3 = reserved next superblock */
+
+ cb = cacheinit(dev);
+ h = (Cache*)cb->iobuf;
+
+ h->sbaddr = baddr;
+ h->cwraddr = baddr+1;
+ h->roraddr = baddr+2;
+ h->fsize = 0; /* prevents superream from freeing */
+
+ putbuf(cb);
+
+ for(m=0; m<3; m++)
+ cwio(dev, baddr+m, 0, Ogrow);
+ superream(dev, baddr);
+ rootream(dev, baddr+1); /* cw root */
+ rootream(dev, baddr+2); /* ro root */
+
+ cb = getbuf(cdev, CACHE_ADDR, Brd|Bmod|Bres);
+ h = (Cache*)cb->iobuf;
+ h->fsize = baddr+4;
+ putbuf(cb);
+
+ p = getbuf(dev, baddr, Brd|Bmod|Bimm);
+ s = (Superb*)p->iobuf;
+ s->last = baddr;
+ s->cwraddr = baddr+1;
+ s->roraddr = baddr+2;
+ s->next = baddr+3;
+ s->fsize = baddr+4;
+ putbuf(p);
+
+ for(m=0; m<3; m++)
+ cwio(dev, baddr+m, 0, Odump);
+}
+
+Off
+rewalk1(Cw *cw, Off addr, int slot, Wpath *up)
+{
+ Iobuf *p, *p1;
+ Dentry *d;
+
+ if(up == 0)
+ return cwraddr(cw->dev);
+ up->addr = rewalk1(cw, up->addr, up->slot, up->up);
+ p = getbuf(cw->dev, up->addr, Brd|Bmod);
+ d = getdir(p, up->slot);
+ if(!d || !(d->mode & DALLOC)) {
+ print("rewalk1 1\n");
+ if(p)
+ putbuf(p);
+ return addr;
+ }
+ p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0);
+ if(!p1) {
+ print("rewalk1 2\n");
+ if(p)
+ putbuf(p);
+ return addr;
+ }
+ if(DEBUG)
+ print("rewalk1 %lld to %lld \"%s\"\n",
+ (Wideoff)addr, (Wideoff)p1->addr, d->name);
+ addr = p1->addr;
+ p1->flags |= Bmod;
+ putbuf(p1);
+ putbuf(p);
+ return addr;
+}
+
+Off
+rewalk2(Cw *cw, Off addr, int slot, Wpath *up)
+{
+ Iobuf *p, *p1;
+ Dentry *d;
+
+ if(up == 0)
+ return cwraddr(cw->rodev);
+ up->addr = rewalk2(cw, up->addr, up->slot, up->up);
+ p = getbuf(cw->rodev, up->addr, Brd);
+ d = getdir(p, up->slot);
+ if(!d || !(d->mode & DALLOC)) {
+ print("rewalk2 1\n");
+ if(p)
+ putbuf(p);
+ return addr;
+ }
+ p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0);
+ if(!p1) {
+ print("rewalk2 2\n");
+ if(p)
+ putbuf(p);
+ return addr;
+ }
+ if(DEBUG)
+ print("rewalk2 %lld to %lld \"%s\"\n",
+ (Wideoff)addr, (Wideoff)p1->addr, d->name);
+ addr = p1->addr;
+ putbuf(p1);
+ putbuf(p);
+ return addr;
+}
+
+void
+rewalk(Cw *cw)
+{
+ int h;
+ File *f;
+
+ for(h=0; h<nelem(flist); h++)
+ for(f=flist[h]; f; f=f->next) {
+ if(!f->fs)
+ continue;
+ if(cw->dev == f->fs->dev)
+ f->addr = rewalk1(cw, f->addr, f->slot, f->wpath);
+ else
+ if(cw->rodev == f->fs->dev)
+ f->addr = rewalk2(cw, f->addr, f->slot, f->wpath);
+ }
+}
+
+Off
+split(Cw *cw, Iobuf *p, Off addr)
+{
+ Off na;
+ int state;
+
+ na = 0;
+ if(p && (p->flags & Bmod)) {
+ p->flags |= Bimm;
+ putbuf(p);
+ p = 0;
+ }
+ state = cwio(cw->dev, addr, 0, Onone); /* read the state (twice?) */
+ switch(state) {
+ default:
+ panic("split: unknown state %s", cwnames[state]);
+
+ case Cerror:
+ case Cnone:
+ case Cdump:
+ case Cread:
+ break;
+
+ case Cdump1:
+ case Cwrite:
+ /*
+ * botch.. could be done by relabeling
+ */
+ if(!p) {
+ p = getbuf(cw->dev, addr, Brd);
+ if(!p) {
+ print("split: null getbuf\n");
+ break;
+ }
+ }
+ na = cw->fsize;
+ cw->fsize = na+1;
+ cwio(cw->dev, na, 0, Ogrow);
+ cwio(cw->dev, na, p->iobuf, Owrite);
+ cwio(cw->dev, na, 0, Odump);
+ cwio(cw->dev, addr, 0, Orele);
+ break;
+
+ case Cdirty:
+ cwio(cw->dev, addr, 0, Odump);
+ break;
+ }
+ if(p)
+ putbuf(p);
+ return na;
+}
+
+int
+isdirty(Cw *cw, Iobuf *p, Off addr, int tag)
+{
+ int s;
+
+ if(p && (p->flags & Bmod))
+ return 1;
+ s = cwio(cw->dev, addr, 0, Onone);
+ if(s == Cdirty || s == Cwrite)
+ return 1;
+ if(tag >= Tind1 && tag <= Tmaxind)
+ /* botch, get these modified */
+ if(s != Cnone)
+ return 1;
+ return 0;
+}
+
+Off
+cwrecur(Cw *cw, Off addr, int tag, int tag1, long qp)
+{
+ Iobuf *p;
+ Dentry *d;
+ int i, j, shouldstop;
+ Off na;
+ char *np;
+
+ shouldstop = 0;
+ p = getbuf(cw->dev, addr, Bprobe);
+ if(!isdirty(cw, p, addr, tag)) {
+ if(!cw->all) {
+ if(DEBUG)
+ print("cwrecur: %lld t=%s not dirty %s\n",
+ (Wideoff)addr, tagnames[tag], cw->name);
+ if(p)
+ putbuf(p);
+ return 0;
+ }
+ shouldstop = 1;
+ }
+ if(DEBUG)
+ print("cwrecur: %lld t=%s %s\n",
+ (Wideoff)addr, tagnames[tag], cw->name);
+ if(cw->depth >= 100) {
+ print("dump depth too great %s\n", cw->name);
+ if(p)
+ putbuf(p);
+ return 0;
+ }
+ cw->depth++;
+
+ switch(tag) {
+ default:
+ print("cwrecur: unknown tag %d %s\n", tag, cw->name);
+
+ case Tfile:
+ break;
+
+ case Tsuper:
+ case Tdir:
+ if(!p) {
+ p = getbuf(cw->dev, addr, Brd);
+ if(!p) {
+ print("cwrecur: Tdir p null %s\n",
+ cw->name);
+ break;
+ }
+ }
+ if(tag == Tdir) {
+ cw->namepad[0] = 0; /* force room */
+ np = strchr(cw->name, 0);
+ *np++ = '/';
+ } else {
+ np = 0; /* set */
+ cw->name[0] = 0;
+ }
+
+ for(i=0; i<DIRPERBUF; i++) {
+ d = getdir(p, i);
+ if(!(d->mode & DALLOC))
+ continue;
+ qp = d->qid.path & ~QPDIR;
+ if(tag == Tdir)
+ strncpy(np, d->name, NAMELEN);
+ else
+ if(i > 0)
+ print("cwrecur: root with >1 directory\n");
+ tag1 = Tfile;
+ if(d->mode & DDIR)
+ tag1 = Tdir;
+ for(j=0; j<NDBLOCK; j++) {
+ na = d->dblock[j];
+ if(na) {
+ na = cwrecur(cw, na, tag1, 0, qp);
+ if(na) {
+ d->dblock[j] = na;
+ p->flags |= Bmod;
+ }
+ }
+ }
+ for (j = 0; j < NIBLOCK; j++) {
+ na = d->iblocks[j];
+ if(na) {
+ na = cwrecur(cw, na, Tind1+j, tag1, qp);
+ if(na) {
+ d->iblocks[j] = na;
+ p->flags |= Bmod;
+ }
+ }
+ }
+ }
+ break;
+
+ case Tind1:
+ j = tag1;
+ tag1 = 0;
+ goto tind;
+
+ case Tind2:
+#ifndef COMPAT32
+ case Tind3:
+ case Tind4:
+ /* add more Tind tags here ... */
+#endif
+ j = tag-1;
+ tind:
+ if(!p) {
+ p = getbuf(cw->dev, addr, Brd);
+ if(!p) {
+ print("cwrecur: Tind p null %s\n", cw->name);
+ break;
+ }
+ }
+ for(i=0; i<INDPERBUF; i++) {
+ na = ((Off *)p->iobuf)[i];
+ if(na) {
+ na = cwrecur(cw, na, j, tag1, qp);
+ if(na) {
+ ((Off *)p->iobuf)[i] = na;
+ p->flags |= Bmod;
+ }
+ }
+ }
+ break;
+ }
+ na = split(cw, p, addr);
+ cw->depth--;
+ if(na && shouldstop) {
+ if(cw->falsehits < 10)
+ print("shouldstop %lld %lld t=%s %s\n",
+ (Wideoff)addr, (Wideoff)na,
+ tagnames[tag], cw->name);
+ cw->falsehits++;
+ }
+ return na;
+}
+
+Timet nextdump(Timet t);
+
+void
+cfsdump(Filsys *fs)
+{
+ long m, n, i;
+ Off orba, rba, oroa, roa, sba, a;
+ Timet tim;
+ char tstr[20];
+ Iobuf *pr, *p1, *p;
+ Dentry *dr, *d1, *d;
+ Cache *h;
+ Superb *s;
+ Cw *cw;
+
+ if(fs->dev->type != Devcw) {
+ print("cant dump; not cw device: %Z\n", fs->dev);
+ return;
+ }
+ cw = fs->dev->private;
+ if(cw == 0) {
+ print("cant dump: has not been inited: %Z\n", fs->dev);
+ return;
+ }
+
+ tim = toytime();
+ wlock(&mainlock); /* dump */
+
+ /*
+ * set up static structure
+ * with frequent variables
+ */
+ cw->ndump = 0;
+ cw->name[0] = 0;
+ cw->depth = 0;
+
+ /*
+ * cw root
+ */
+ sync("before dump");
+ cw->fsize = cwsize(cw->dev);
+ orba = cwraddr(cw->dev);
+ print("cwroot %lld", (Wideoff)orba);
+ cons.noage = 1;
+ cw->all = cw->allflag;
+ rba = cwrecur(cw, orba, Tsuper, 0, QPROOT);
+ if(rba == 0)
+ rba = orba;
+ print("->%lld\n", (Wideoff)rba);
+ sync("after cw");
+
+ /*
+ * partial super block
+ */
+ p = getbuf(cw->dev, cwsaddr(cw->dev), Brd|Bmod|Bimm);
+ s = (Superb*)p->iobuf;
+ s->fsize = cw->fsize;
+ s->cwraddr = rba;
+ putbuf(p);
+
+ /*
+ * partial cache block
+ */
+ p = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bimm|Bres);
+ h = (Cache*)p->iobuf;
+ h->fsize = cw->fsize;
+ h->cwraddr = rba;
+ putbuf(p);
+
+ /*
+ * ro root
+ */
+ oroa = cwraddr(cw->rodev);
+ pr = getbuf(cw->dev, oroa, Brd|Bmod);
+ dr = getdir(pr, 0);
+
+ datestr(tstr, time(nil)); /* tstr = "yyyymmdd" */
+ n = 0;
+ for(a=0;; a++) {
+ p1 = dnodebuf(pr, dr, a, Tdir, 0);
+ if(!p1)
+ goto bad;
+ n++;
+ for(i=0; i<DIRPERBUF; i++) {
+ d1 = getdir(p1, i);
+ if(!d1)
+ goto bad;
+ if(!(d1->mode & DALLOC))
+ goto found1;
+ if(!memcmp(d1->name, tstr, 4))
+ goto found2; /* found entry */
+ }
+ putbuf(p1);
+ }
+
+ /*
+ * no year directory, create one
+ */
+found1:
+ p = getbuf(cw->dev, rba, Brd);
+ d = getdir(p, 0);
+ d1->qid = d->qid;
+ d1->qid.version += n;
+ memmove(d1->name, tstr, 4);
+ d1->mode = d->mode;
+ d1->uid = d->uid;
+ d1->gid = d->gid;
+ putbuf(p);
+ accessdir(p1, d1, FWRITE, 0);
+
+ /*
+ * put mmdd[count] in year directory
+ */
+found2:
+ accessdir(p1, d1, FREAD, 0);
+ putbuf(pr);
+ pr = p1;
+ dr = d1;
+
+ n = 0;
+ m = 0;
+ for(a=0;; a++) {
+ p1 = dnodebuf(pr, dr, a, Tdir, 0);
+ if(!p1)
+ goto bad;
+ n++;
+ for(i=0; i<DIRPERBUF; i++) {
+ d1 = getdir(p1, i);
+ if(!d1)
+ goto bad;
+ if(!(d1->mode & DALLOC))
+ goto found;
+ if(!memcmp(d1->name, tstr+4, 4))
+ m++;
+ }
+ putbuf(p1);
+ }
+
+ /*
+ * empty slot put in root
+ */
+found:
+ if(m) /* how many dumps this date */
+ sprint(tstr+8, "%ld", m);
+
+ p = getbuf(cw->dev, rba, Brd);
+ d = getdir(p, 0);
+ *d1 = *d; /* qid is QPROOT */
+ putbuf(p);
+ strcpy(d1->name, tstr+4);
+ d1->qid.version += n;
+ accessdir(p1, d1, FWRITE, 0);
+ putbuf(p1);
+ putbuf(pr);
+
+ cw->fsize = cwsize(cw->dev);
+ oroa = cwraddr(cw->rodev); /* probably redundant */
+ print("roroot %lld", (Wideoff)oroa);
+
+ cons.noage = 0;
+ cw->all = 0;
+ roa = cwrecur(cw, oroa, Tsuper, 0, QPROOT);
+ if(roa == 0) {
+ print("[same]");
+ roa = oroa;
+ }
+ print("->%lld /%.4s/%s\n", (Wideoff)roa, tstr, tstr+4);
+ sync("after ro");
+
+ /*
+ * final super block
+ */
+ a = cwsaddr(cw->dev);
+ print("sblock %lld", (Wideoff)a);
+ p = getbuf(cw->dev, a, Brd|Bmod|Bimm);
+ s = (Superb*)p->iobuf;
+ s->last = a;
+ sba = s->next;
+ s->next = cw->fsize;
+ cw->fsize++;
+ s->fsize = cw->fsize;
+ s->roraddr = roa;
+
+ cwio(cw->dev, sba, 0, Ogrow);
+ cwio(cw->dev, sba, p->iobuf, Owrite);
+ cwio(cw->dev, sba, 0, Odump);
+ print("->%lld (->%lld)\n", (Wideoff)sba, (Wideoff)s->next);
+
+ putbuf(p);
+
+ /*
+ * final cache block
+ */
+ p = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bimm|Bres);
+ h = (Cache*)p->iobuf;
+ h->fsize = cw->fsize;
+ h->roraddr = roa;
+ h->sbaddr = sba;
+ putbuf(p);
+
+ rewalk(cw);
+ sync("all done");
+
+ print("%lld blocks queued for worm\n", (Wideoff)cw->ndump);
+ print("%lld falsehits\n", (Wideoff)cw->falsehits);
+ cw->nodump = 0;
+
+ /*
+ * extend all of the locks
+ */
+ tim = toytime() - tim;
+ for(i=0; i<NTLOCK; i++)
+ if(tlocks[i].time > 0)
+ tlocks[i].time += tim;
+
+ wunlock(&mainlock);
+ nextdump(time(nil));
+ return;
+
+bad:
+ panic("dump: bad");
+}
+
+void
+mvstates(Device *dev, int s1, int s2, int side)
+{
+ Iobuf *p, *cb;
+ Cache *h;
+ Bucket *b;
+ Centry *c, *ce;
+ Off m, lo, hi, msize, maddr;
+ Cw *cw;
+
+ cw = dev->private;
+ lo = 0;
+ 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;
+ }
+ cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
+ if(!cb || checktag(cb, Tcache, QPSUPER))
+ panic("cwstats: checktag c bucket");
+ h = (Cache*)cb->iobuf;
+ msize = h->msize;
+ maddr = h->maddr;
+ putbuf(cb);
+
+ for(m=0; m<msize; m++) {
+ p = getbuf(cw->cdev, maddr + m/BKPERBLK, Brd|Bmod);
+ if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
+ panic("cwtest: checktag c bucket");
+ b = (Bucket*)p->iobuf + m%BKPERBLK;
+ ce = b->entry + CEPERBK;
+ for(c=b->entry; c<ce; c++)
+ if(c->state == s1 && c->waddr >= lo && c->waddr < hi)
+ c->state = s2;
+ putbuf(p);
+ }
+}
+
+void
+prchain(Device *dev, Off m, int flg)
+{
+ Iobuf *p;
+ Superb *s;
+
+ if(m == 0) {
+ if(flg)
+ m = cwsaddr(dev);
+ else
+ m = getstartsb(dev);
+ }
+ p = getbuf(devnone, Cwxx2, 0);
+ s = (Superb*)p->iobuf;
+ for(;;) {
+ memset(p->iobuf, 0, RBUFSIZE);
+ if(devread(WDEV(dev), m, p->iobuf) ||
+ checktag(p, Tsuper, QPSUPER))
+ break;
+ if(flg) {
+ print("dump %lld is good; %lld prev\n", (Wideoff)m,
+ (Wideoff)s->last);
+ print("\t%lld cwroot; %lld roroot\n",
+ (Wideoff)s->cwraddr, (Wideoff)s->roraddr);
+ if(m <= s->last)
+ break;
+ m = s->last;
+ } else {
+ print("dump %lld is good; %lld next\n", (Wideoff)m,
+ (Wideoff)s->next);
+ print("\t%lld cwroot; %lld roroot\n",
+ (Wideoff)s->cwraddr, (Wideoff)s->roraddr);
+ if(m >= s->next)
+ break;
+ m = s->next;
+ }
+ }
+ putbuf(p);
+}
+
+void
+touchsb(Device *dev)
+{
+ Iobuf *p;
+ Off m;
+
+ m = cwsaddr(dev);
+ p = getbuf(devnone, Cwxx2, 0);
+
+ memset(p->iobuf, 0, RBUFSIZE);
+ if(devread(WDEV(dev), m, p->iobuf) ||
+ checktag(p, Tsuper, QPSUPER))
+ print("%Z block %lld WORM SUPER BLOCK READ FAILED\n",
+ WDEV(dev), (Wideoff)m);
+ else
+ print("%Z touch superblock %lld\n", WDEV(dev), (Wideoff)m);
+ putbuf(p);
+}
+
+void
+storesb(Device *dev, Off last, int doit)
+{
+ Iobuf *ph, *ps;
+ Cache *h;
+ Superb *s;
+ Off sbaddr, qidgen;
+
+ sbaddr = cwsaddr(dev);
+
+ ps = getbuf(devnone, Cwxx2, 0);
+ if(!ps) {
+ print("sbstore: getbuf\n");
+ return;
+ }
+
+ /*
+ * try to read last sb
+ */
+ memset(ps->iobuf, 0, RBUFSIZE);
+ if(devread(WDEV(dev), last, ps->iobuf) ||
+ checktag(ps, Tsuper, QPSUPER))
+ print("read last failed\n");
+ else
+ print("read last succeeded\n");
+
+ s = (Superb*)ps->iobuf;
+ qidgen = s->qidgen;
+ if(qidgen == 0)
+ qidgen = 0x31415;
+ qidgen += 1000;
+ if(s->next != sbaddr)
+ print("next(last) is not sbaddr %lld %lld\n",
+ (Wideoff)s->next, (Wideoff)sbaddr);
+ else
+ print("next(last) is sbaddr\n");
+
+ /*
+ * read cached superblock
+ */
+ ph = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
+ if(!ph || checktag(ph, Tcache, QPSUPER)) {
+ print("cwstats: checktag c bucket\n");
+ if(ph)
+ putbuf(ph);
+ putbuf(ps);
+ return;
+ } else
+ print("read cached sb succeeded\n");
+
+ h = (Cache*)ph->iobuf;
+
+ memset(ps->iobuf, 0, RBUFSIZE);
+ settag(ps, Tsuper, QPSUPER);
+ ps->flags = 0;
+ s = (Superb*)ps->iobuf;
+
+ s->cwraddr = h->cwraddr;
+ s->roraddr = h->roraddr;
+ s->fsize = h->fsize;
+ s->fstart = 2;
+ s->last = last;
+ s->next = h->roraddr+1;
+
+ s->qidgen = qidgen;
+ putbuf(ph);
+
+ if(s->fsize-1 != s->next ||
+ s->fsize-2 != s->roraddr ||
+ s->fsize-5 != s->cwraddr) {
+ print("addrs not in relationship %lld %lld %lld %lld\n",
+ (Wideoff)s->cwraddr, (Wideoff)s->roraddr,
+ (Wideoff)s->next, (Wideoff)s->fsize);
+ putbuf(ps);
+ return;
+ } else
+ print("addresses in relation\n");
+
+ if(doit)
+ if(devwrite(WDEV(dev), sbaddr, ps->iobuf))
+ print("%Z block %lld WORM SUPER BLOCK WRITE FAILED\n",
+ WDEV(dev), (Wideoff)sbaddr);
+ ps->flags = 0;
+ putbuf(ps);
+}
+
+void
+savecache(Device *dev)
+{
+ Iobuf *p, *cb;
+ Cache *h;
+ Bucket *b;
+ Centry *c, *ce;
+ long n, left;
+ Off m, maddr, msize, *longp, nbyte;
+ Device *cdev;
+
+ if(walkto("/adm/cache") || con_open(FID2, OWRITE|OTRUNC)) {
+ print("cant open /adm/cache\n");
+ return;
+ }
+ cdev = CDEV(dev);
+ cb = getbuf(cdev, CACHE_ADDR, Brd|Bres);
+ if(!cb || checktag(cb, Tcache, QPSUPER))
+ panic("savecache: checktag c bucket");
+ h = (Cache*)cb->iobuf;
+ msize = h->msize;
+ maddr = h->maddr;
+ putbuf(cb);
+
+ n = BUFSIZE; /* calculate write size */
+ if(n > MAXDAT)
+ n = MAXDAT;
+
+ cb = getbuf(devnone, Cwxx4, 0);
+ longp = (Off *)cb->iobuf;
+ left = n/sizeof(Off);
+ cons.offset = 0;
+
+ for(m=0; m<msize; m++) {
+ if(left < BKPERBLK) {
+ nbyte = (n/sizeof(Off) - left) * sizeof(Off);
+ con_write(FID2, cb->iobuf, cons.offset, nbyte);
+ cons.offset += nbyte;
+ longp = (Off *)cb->iobuf;
+ left = n/sizeof(Off);
+ }
+ p = getbuf(cdev, maddr + m/BKPERBLK, Brd);
+ if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
+ panic("cwtest: checktag c bucket");
+ b = (Bucket*)p->iobuf + m%BKPERBLK;
+ ce = b->entry + CEPERBK;
+ for(c = b->entry; c < ce; c++)
+ if(c->state == Cread) {
+ *longp++ = c->waddr;
+ left--;
+ }
+ putbuf(p);
+ }
+ nbyte = (n/sizeof(Off) - left) * sizeof(Off);
+ con_write(FID2, cb->iobuf, cons.offset, nbyte);
+ putbuf(cb);
+}
+
+void
+loadcache(Device *dev, int dskno)
+{
+ Iobuf *p, *cb;
+ Off m, nbyte, *longp, count;
+ Sidestarts ss;
+
+ if(walkto("/adm/cache") || con_open(FID2, OREAD)) {
+ print("cant open /adm/cache\n");
+ return;
+ }
+
+ cb = getbuf(devnone, Cwxx4, 0);
+ cons.offset = 0;
+ count = 0;
+
+ if (dskno >= 0)
+ wormsidestarts(dev, dskno, &ss);
+ for(;;) {
+ memset(cb->iobuf, 0, BUFSIZE);
+ nbyte = con_read(FID2, cb->iobuf, cons.offset, 100) / sizeof(Off);
+ if(nbyte <= 0)
+ break;
+ cons.offset += nbyte * sizeof(Off);
+ longp = (Off *)cb->iobuf;
+ while(nbyte > 0) {
+ m = *longp++;
+ nbyte--;
+ if(m == 0)
+ continue;
+ /* if given a diskno, restrict to just that disc side */
+ if(dskno < 0 || m >= ss.sstart && m < ss.s1start) {
+ p = getbuf(dev, m, Brd);
+ if(p)
+ putbuf(p);
+ count++;
+ }
+ }
+ }
+ putbuf(cb);
+ print("%lld blocks loaded from worm %d\n", (Wideoff)count, dskno);
+}
+
+void
+morecache(Device *dev, int dskno, Off size)
+{
+ Iobuf *p;
+ Off m, ml, mh, mm, count;
+ Cache *h;
+ Sidestarts ss;
+
+ p = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
+ if(!p || checktag(p, Tcache, QPSUPER))
+ panic("savecache: checktag c bucket");
+ h = (Cache*)p->iobuf;
+ mm = h->wmax;
+ putbuf(p);
+
+ wormsidestarts(dev, dskno, &ss);
+ ml = ss.sstart; /* start at beginning of disc side #dskno */
+ mh = ml + size;
+ if(mh > mm) {
+ mh = mm;
+ print("limited to %lld\n", (Wideoff)mh-ml);
+ }
+
+ count = 0;
+ for(m=ml; m < mh; m++) {
+ p = getbuf(dev, m, Brd);
+ if(p)
+ putbuf(p);
+ count++;
+ }
+ print("%lld blocks loaded from worm %d\n", (Wideoff)count, dskno);
+}
+
+void
+blockcmp(Device *dev, Off wa, Off ca)
+{
+ Iobuf *p1, *p2;
+ int i, c;
+
+ p1 = getbuf(WDEV(dev), wa, Brd);
+ if(!p1) {
+ print("blockcmp: wdev error\n");
+ return;
+ }
+
+ p2 = getbuf(CDEV(dev), ca, Brd);
+ if(!p2) {
+ print("blockcmp: cdev error\n");
+ putbuf(p1);
+ return;
+ }
+
+ c = 0;
+ for(i=0; i<RBUFSIZE; i++)
+ if(p1->iobuf[i] != p2->iobuf[i]) {
+ print("%4d: %.2x %.2x\n",
+ i,
+ p1->iobuf[i]&0xff,
+ p2->iobuf[i]&0xff);
+ c++;
+ if(c >= 10)
+ break;
+ }
+
+ if(c == 0)
+ print("no error\n");
+ putbuf(p1);
+ putbuf(p2);
+}
+
+void
+wblock(Device *dev, Off addr)
+{
+ Iobuf *p1;
+ int i;
+
+ p1 = getbuf(dev, addr, Brd);
+ if(p1) {
+ i = devwrite(WDEV(dev), addr, p1->iobuf);
+ print("i = %d\n", i);
+ putbuf(p1);
+ }
+}
+
+void
+cwtest(Device*)
+{
+}
+
+#ifdef XXX
+/* garbage to change sb size
+ * probably will need it someday
+ */
+ fsz = number(0, 0, 10);
+ count = 0;
+ if(fsz == number(0, -1, 10))
+ count = -1; /* really do it */
+ print("fsize = %ld\n", fsz);
+ cdev = CDEV(dev);
+ cb = getbuf(cdev, CACHE_ADDR, Brd|Bres);
+ if(!cb || checktag(cb, Tcache, QPSUPER))
+ panic("cwstats: checktag c bucket");
+ h = (Cache*)cb->iobuf;
+ for(m=0; m<h->msize; m++) {
+ p = getbuf(cdev, h->maddr + m/BKPERBLK, Brd|Bmod);
+ if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
+ panic("cwtest: checktag c bucket");
+ b = (Bucket*)p->iobuf + m%BKPERBLK;
+ ce = b->entry + CEPERBK;
+ for(c=b->entry; c<ce; c++) {
+ if(c->waddr < fsz)
+ continue;
+ if(count < 0) {
+ c->state = Cnone;
+ continue;
+ }
+ if(c->state != Cdirty)
+ count++;
+ }
+ putbuf(p);
+ }
+ if(count < 0) {
+ print("old cache hsize = %ld\n", h->fsize);
+ h->fsize = fsz;
+ cb->flags |= Bmod;
+ p = getbuf(dev, h->sbaddr, Brd|Bmod);
+ s = (Superb*)p->iobuf;
+ print("old super hsize = %ld\n", s->fsize);
+ s->fsize = fsz;
+ putbuf(p);
+ }
+ putbuf(cb);
+ print("count = %lld\n", (Wideoff)count);
+#endif
+
+int
+convstate(char *name)
+{
+ int i;
+
+ for(i=0; i<nelem(cwnames); i++)
+ if(cwnames[i])
+ if(strcmp(cwnames[i], name) == 0)
+ return i;
+ return -1;
+}
+
+void
+searchtag(Device *d, Off a, int tag, int n)
+{
+ Iobuf *p;
+ Tag *t;
+ int i;
+
+ if(a == 0)
+ a = getstartsb(d);
+ p = getbuf(devnone, Cwxx2, 0);
+ t = (Tag*)(p->iobuf+BUFSIZE);
+ for(i=0; i<n; i++) {
+ memset(p->iobuf, 0, RBUFSIZE);
+ if(devread(WDEV(d), a+i, p->iobuf)) {
+ if(n == 1000)
+ break;
+ continue;
+ }
+ if(t->tag == tag) {
+ print("tag %d found at %Z %lld\n", tag, d, (Wideoff)a+i);
+ break;
+ }
+ }
+ putbuf(p);
+}
+
+void
+cmd_cwcmd(int argc, char *argv[])
+{
+ Device *dev;
+ char *arg;
+ char str[28];
+ Off s1, s2, a, b, n;
+ Cw *cw;
+
+ if(argc <= 1) {
+ print("\tcwcmd mvstate state1 state2 [platter]\n");
+ print("\tcwcmd prchain [start] [bakflg]\n");
+ print("\tcwcmd searchtag [start] [tag] [blocks]\n");
+ print("\tcwcmd touchsb\n");
+ print("\tcwcmd savecache\n");
+ print("\tcwcmd loadcache [dskno]\n");
+ print("\tcwcmd morecache dskno [count]\n");
+ print("\tcwcmd blockcmp wbno cbno\n");
+ print("\tcwcmd startdump [01]\n");
+ print("\tcwcmd acct\n");
+ print("\tcwcmd clearacct\n");
+ return;
+ }
+ arg = argv[1];
+
+ /*
+ * items not depend on a cw filesystem
+ */
+ if(strcmp(arg, "acct") == 0) {
+ for(a=0; a<nelem(growacct); a++) {
+ b = growacct[a];
+ if(b) {
+ uidtostr(str, a, 1);
+ print("%10lld %s\n",
+ ((Wideoff)b*ADDFREE*RBUFSIZE+500000)/1000000,
+ str);
+ }
+ }
+ return;
+ }
+ if(strcmp(arg, "clearacct") == 0) {
+ memset(growacct, 0, sizeof(growacct));
+ return;
+ }
+
+ /*
+ * items depend on cw filesystem
+ */
+ dev = cons.curfs->dev;
+ if(dev == 0 || dev->type != Devcw || dev->private == 0) {
+ print("cfs not a cw filesystem: %Z\n", dev);
+ return;
+ }
+ cw = dev->private;
+ if(strcmp(arg, "searchtag") == 0) {
+ a = 0;
+ if(argc > 2)
+ a = number(argv[2], 0, 10);
+ b = Tsuper;
+ if(argc > 3)
+ b = number(argv[3], 0, 10);
+ n = 1000;
+ if(argc > 4)
+ n = number(argv[4], 0, 10);
+ searchtag(dev, a, b, n);
+ } else if(strcmp(arg, "mvstate") == 0) {
+ if(argc < 4)
+ goto bad;
+ s1 = convstate(argv[2]);
+ s2 = convstate(argv[3]);
+ if(s1 < 0 || s2 < 0)
+ goto bad;
+ a = -1;
+ if(argc > 4)
+ a = number(argv[4], 0, 10);
+ mvstates(dev, s1, s2, a);
+ return;
+ bad:
+ print("cwcmd mvstate: bad args\n");
+ } else if(strcmp(arg, "prchain") == 0) {
+ a = 0;
+ if(argc > 2)
+ a = number(argv[2], 0, 10);
+ s1 = 0;
+ if(argc > 3)
+ s1 = number(argv[3], 0, 10);
+ prchain(dev, a, s1);
+ } else if(strcmp(arg, "touchsb") == 0)
+ touchsb(dev);
+ else if(strcmp(arg, "savecache") == 0)
+ savecache(dev);
+ else if(strcmp(arg, "loadcache") == 0) {
+ s1 = -1;
+ if(argc > 2)
+ s1 = number(argv[2], 0, 10);
+ loadcache(dev, s1);
+ } else if(strcmp(arg, "morecache") == 0) {
+ if(argc <= 2) {
+ print("arg count\n");
+ return;
+ }
+ s1 = number(argv[2], 0, 10);
+ if(argc > 3)
+ s2 = number(argv[3], 0, 10);
+ else
+ s2 = wormsizeside(dev, s1); /* default to 1 disc side */
+ morecache(dev, s1, s2);
+ } else if(strcmp(arg, "blockcmp") == 0) {
+ if(argc < 4) {
+ print("cannot arg count\n");
+ return;
+ }
+ s1 = number(argv[2], 0, 10);
+ s2 = number(argv[3], 0, 10);
+ blockcmp(dev, s1, s2);
+ } else if(strcmp(arg, "startdump") == 0) {
+ if(argc > 2)
+ cw->nodump = number(argv[2], 0, 10);
+ cw->nodump = !cw->nodump;
+ if(cw->nodump)
+ print("dump stopped\n");
+ else
+ print("dump allowed\n");
+ } else if(strcmp(arg, "allflag") == 0) {
+ if(argc > 2)
+ cw->allflag = number(argv[2], 0, 10);
+ else
+ cw->allflag = !cw->allflag;
+ print("allflag = %d; falsehits = %lld\n",
+ cw->allflag, (Wideoff)cw->falsehits);
+ } else if(strcmp(arg, "storesb") == 0) {
+ a = 4168344;
+ b = 0;
+ if(argc > 2)
+ a = number(argv[2], 4168344, 10);
+ if(argc > 3)
+ b = number(argv[3], 0, 10);
+ storesb(dev, a, b);
+ } else if(strcmp(arg, "test") == 0)
+ cwtest(dev);
+ else
+ print("unknown cwcmd %s\n", arg);
+}