summaryrefslogtreecommitdiff
path: root/sys/src/cmd/iostats
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/iostats
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/iostats')
-rwxr-xr-xsys/src/cmd/iostats/iostats.c598
-rwxr-xr-xsys/src/cmd/iostats/mkfile11
-rwxr-xr-xsys/src/cmd/iostats/statfs.h142
-rwxr-xr-xsys/src/cmd/iostats/statsrv.c672
4 files changed, 1423 insertions, 0 deletions
diff --git a/sys/src/cmd/iostats/iostats.c b/sys/src/cmd/iostats/iostats.c
new file mode 100755
index 000000000..8b1356d0a
--- /dev/null
+++ b/sys/src/cmd/iostats/iostats.c
@@ -0,0 +1,598 @@
+/*
+ * iostats - Gather file system information
+ */
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#define Extern
+#include "statfs.h"
+
+void runprog(char**);
+
+void (*fcalls[])(Fsrpc*) =
+{
+ [Tversion] Xversion,
+ [Tauth] Xauth,
+ [Tflush] Xflush,
+ [Tattach] Xattach,
+ [Twalk] Xwalk,
+ [Topen] slave,
+ [Tcreate] Xcreate,
+ [Tclunk] Xclunk,
+ [Tread] slave,
+ [Twrite] slave,
+ [Tremove] Xremove,
+ [Tstat] Xstat,
+ [Twstat] Xwstat,
+};
+
+int p[2];
+
+void
+usage(void)
+{
+ fprint(2, "usage: iostats [-d] [-f debugfile] cmds [args ...]\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ Fsrpc *r;
+ Rpc *rpc;
+ Proc *m;
+ Frec *fr;
+ Fid *fid;
+ ulong ttime;
+ char *dbfile, *s;
+ char buf[128];
+ float brpsec, bwpsec, bppsec;
+ int type, cpid, fspid, n;
+
+ dbfile = DEBUGFILE;
+
+ ARGBEGIN{
+ case 'd':
+ dbg++;
+ break;
+ case 'f':
+ dbfile = ARGF();
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc == 0)
+ usage();
+
+ if(dbg) {
+ close(2);
+ create(dbfile, OWRITE, 0666);
+ }
+
+ if(pipe(p) < 0)
+ fatal("pipe");
+
+ switch(cpid = fork()) {
+ case -1:
+ fatal("fork");
+ case 0:
+ close(p[1]);
+ if(getwd(buf, sizeof(buf)) == 0)
+ fatal("no working directory");
+
+ rfork(RFENVG|RFNAMEG|RFNOTEG);
+ if(mount(p[0], -1, "/", MREPL, "") < 0)
+ fatal("mount /");
+
+ bind("#c/pid", "/dev/pid", MREPL);
+ bind("#e", "/env", MREPL|MCREATE);
+ close(0);
+ close(1);
+ close(2);
+ open("/fd/0", OREAD);
+ open("/fd/1", OWRITE);
+ open("/fd/2", OWRITE);
+
+ if(chdir(buf) < 0)
+ fatal("chdir");
+
+ runprog(argv);
+ default:
+ close(p[0]);
+ }
+
+ switch(fspid = fork()) {
+ default:
+ while(cpid != waitpid())
+ ;
+ postnote(PNPROC, fspid, DONESTR);
+ while(fspid != waitpid())
+ ;
+ exits(0);
+ case -1:
+ fatal("fork");
+ case 0:
+ break;
+ }
+
+ /* Allocate work queues in shared memory */
+ malloc(Dsegpad);
+ Workq = malloc(sizeof(Fsrpc)*Nr_workbufs);
+ stats = malloc(sizeof(Stats));
+ fhash = mallocz(sizeof(Fid*)*FHASHSIZE, 1);
+
+ if(Workq == 0 || fhash == 0 || stats == 0)
+ fatal("no initial memory");
+
+ memset(Workq, 0, sizeof(Fsrpc)*Nr_workbufs);
+ memset(stats, 0, sizeof(Stats));
+
+ stats->rpc[Tversion].name = "version";
+ stats->rpc[Tauth].name = "auth";
+ stats->rpc[Tflush].name = "flush";
+ stats->rpc[Tattach].name = "attach";
+ stats->rpc[Twalk].name = "walk";
+ stats->rpc[Topen].name = "open";
+ stats->rpc[Tcreate].name = "create";
+ stats->rpc[Tclunk].name = "clunk";
+ stats->rpc[Tread].name = "read";
+ stats->rpc[Twrite].name = "write";
+ stats->rpc[Tremove].name = "remove";
+ stats->rpc[Tstat].name = "stat";
+ stats->rpc[Twstat].name = "wstat";
+
+ for(n = 0; n < Maxrpc; n++)
+ stats->rpc[n].lo = 10000000000LL;
+
+ fmtinstall('M', dirmodefmt);
+ fmtinstall('D', dirfmt);
+ fmtinstall('F', fcallfmt);
+
+ if(chdir("/") < 0)
+ fatal("chdir");
+
+ initroot();
+
+ DEBUG(2, "statfs: %s\n", buf);
+
+ notify(catcher);
+
+ for(;;) {
+ r = getsbuf();
+ if(r == 0)
+ fatal("Out of service buffers");
+
+ n = read9pmsg(p[1], r->buf, sizeof(r->buf));
+ if(done)
+ break;
+ if(n < 0)
+ fatal("read server");
+
+ if(convM2S(r->buf, n, &r->work) == 0)
+ fatal("format error");
+
+ stats->nrpc++;
+ stats->nproto += n;
+
+ DEBUG(2, "%F\n", &r->work);
+
+ type = r->work.type;
+ rpc = &stats->rpc[type];
+ rpc->count++;
+ rpc->bin += n;
+ (fcalls[type])(r);
+ }
+
+ /* Clear away the slave children */
+ for(m = Proclist; m; m = m->next)
+ postnote(PNPROC, m->pid, "kill");
+
+ rpc = &stats->rpc[Tread];
+ brpsec = (float)stats->totread / (((float)rpc->time/1e9)+.000001);
+
+ rpc = &stats->rpc[Twrite];
+ bwpsec = (float)stats->totwrite / (((float)rpc->time/1e9)+.000001);
+
+ ttime = 0;
+ for(n = 0; n < Maxrpc; n++) {
+ rpc = &stats->rpc[n];
+ if(rpc->count == 0)
+ continue;
+ ttime += rpc->time;
+ }
+
+ bppsec = (float)stats->nproto / ((ttime/1e9)+.000001);
+
+ fprint(2, "\nread %lud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0);
+ fprint(2, "write %lud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0);
+ fprint(2, "protocol %lud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0);
+ fprint(2, "rpc %lud count\n\n", stats->nrpc);
+
+ fprint(2, "%-10s %5s %5s %5s %5s %5s T R\n",
+ "Message", "Count", "Low", "High", "Time", "Averg");
+
+ for(n = 0; n < Maxrpc; n++) {
+ rpc = &stats->rpc[n];
+ if(rpc->count == 0)
+ continue;
+ fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8lud %8lud bytes\n",
+ rpc->name,
+ rpc->count,
+ rpc->lo/1000000,
+ rpc->hi/1000000,
+ rpc->time/1000000,
+ rpc->time/1000000/rpc->count,
+ rpc->bin,
+ rpc->bout);
+ }
+
+ for(n = 0; n < FHASHSIZE; n++)
+ for(fid = fhash[n]; fid; fid = fid->next)
+ if(fid->nread || fid->nwrite)
+ fidreport(fid);
+ if(frhead == 0)
+ exits(0);
+
+ fprint(2, "\nOpens Reads (bytes) Writes (bytes) File\n");
+ for(fr = frhead; fr; fr = fr->next) {
+ s = fr->op;
+ if(*s) {
+ if(strcmp(s, "/fd/0") == 0)
+ s = "(stdin)";
+ else
+ if(strcmp(s, "/fd/1") == 0)
+ s = "(stdout)";
+ else
+ if(strcmp(s, "/fd/2") == 0)
+ s = "(stderr)";
+ }
+ else
+ s = "/.";
+
+ fprint(2, "%5lud %8lud %8lud %8lud %8lud %s\n", fr->opens, fr->nread, fr->bread,
+ fr->nwrite, fr->bwrite, s);
+ }
+
+ exits(0);
+}
+
+void
+reply(Fcall *r, Fcall *t, char *err)
+{
+ uchar data[IOHDRSZ+Maxfdata];
+ int n;
+
+ t->tag = r->tag;
+ t->fid = r->fid;
+ if(err) {
+ t->type = Rerror;
+ t->ename = err;
+ }
+ else
+ t->type = r->type + 1;
+
+ DEBUG(2, "\t%F\n", t);
+
+ n = convS2M(t, data, sizeof data);
+ if(write(p[1], data, n)!=n)
+ fatal("mount write");
+ stats->nproto += n;
+ stats->rpc[t->type-1].bout += n;
+}
+
+Fid *
+getfid(int nr)
+{
+ Fid *f;
+
+ for(f = fidhash(nr); f; f = f->next)
+ if(f->nr == nr)
+ return f;
+
+ return 0;
+}
+
+int
+freefid(int nr)
+{
+ Fid *f, **l;
+
+ l = &fidhash(nr);
+ for(f = *l; f; f = f->next) {
+ if(f->nr == nr) {
+ *l = f->next;
+ f->next = fidfree;
+ fidfree = f;
+ return 1;
+ }
+ l = &f->next;
+ }
+
+ return 0;
+}
+
+Fid *
+newfid(int nr)
+{
+ Fid *new, **l;
+ int i;
+
+ l = &fidhash(nr);
+ for(new = *l; new; new = new->next)
+ if(new->nr == nr)
+ return 0;
+
+ if(fidfree == 0) {
+ fidfree = mallocz(sizeof(Fid) * Fidchunk, 1);
+ if(fidfree == 0)
+ fatal("out of memory");
+
+ for(i = 0; i < Fidchunk-1; i++)
+ fidfree[i].next = &fidfree[i+1];
+
+ fidfree[Fidchunk-1].next = 0;
+ }
+
+ new = fidfree;
+ fidfree = new->next;
+
+ memset(new, 0, sizeof(Fid));
+ new->next = *l;
+ *l = new;
+ new->nr = nr;
+ new->fid = -1;
+ new->nread = 0;
+ new->nwrite = 0;
+ new->bread = 0;
+ new->bwrite = 0;
+
+ return new;
+}
+
+Fsrpc *
+getsbuf(void)
+{
+ static int ap;
+ int look;
+ Fsrpc *wb;
+
+ for(look = 0; look < Nr_workbufs; look++) {
+ if(++ap == Nr_workbufs)
+ ap = 0;
+ if(Workq[ap].busy == 0)
+ break;
+ }
+
+ if(look == Nr_workbufs)
+ fatal("No more work buffers");
+
+ wb = &Workq[ap];
+ wb->pid = 0;
+ wb->canint = 0;
+ wb->flushtag = NOTAG;
+ wb->busy = 1;
+
+ return wb;
+}
+
+char *
+strcatalloc(char *p, char *n)
+{
+ char *v;
+
+ v = realloc(p, strlen(p)+strlen(n)+1);
+ if(v == 0)
+ fatal("no memory");
+ strcat(v, n);
+ return v;
+}
+
+File *
+file(File *parent, char *name)
+{
+ char buf[128];
+ File *f, *new;
+ Dir *dir;
+
+ DEBUG(2, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
+
+ for(f = parent->child; f; f = f->childlist)
+ if(strcmp(name, f->name) == 0)
+ break;
+
+ if(f != nil && !f->inval)
+ return f;
+ makepath(buf, parent, name);
+ dir = dirstat(buf);
+ if(dir == nil)
+ return 0;
+ if(f != nil){
+ free(dir);
+ f->inval = 0;
+ return f;
+ }
+
+ new = malloc(sizeof(File));
+ if(new == 0)
+ fatal("no memory");
+
+ memset(new, 0, sizeof(File));
+ new->name = strdup(name);
+ if(new->name == nil)
+ fatal("can't strdup");
+ new->qid.type = dir->qid.type;
+ new->qid.vers = dir->qid.vers;
+ new->qid.path = ++qid;
+
+ new->parent = parent;
+ new->childlist = parent->child;
+ parent->child = new;
+
+ free(dir);
+ return new;
+}
+
+void
+initroot(void)
+{
+ Dir *dir;
+
+ root = malloc(sizeof(File));
+ if(root == 0)
+ fatal("no memory");
+
+ memset(root, 0, sizeof(File));
+ root->name = strdup("/");
+ if(root->name == nil)
+ fatal("can't strdup");
+ dir = dirstat(root->name);
+ if(dir == nil)
+ fatal("root stat");
+
+ root->qid.type = dir->qid.type;
+ root->qid.vers = dir->qid.vers;
+ root->qid.path = ++qid;
+ free(dir);
+}
+
+void
+makepath(char *as, File *p, char *name)
+{
+ char *c, *seg[100];
+ int i;
+ char *s;
+
+ seg[0] = name;
+ for(i = 1; i < 100 && p; i++, p = p->parent){
+ seg[i] = p->name;
+ if(strcmp(p->name, "/") == 0)
+ seg[i] = ""; /* will insert slash later */
+ }
+
+ s = as;
+ while(i--) {
+ for(c = seg[i]; *c; c++)
+ *s++ = *c;
+ *s++ = '/';
+ }
+ while(s[-1] == '/')
+ s--;
+ *s = '\0';
+ if(as == s) /* empty string is root */
+ strcpy(as, "/");
+}
+
+void
+fatal(char *s)
+{
+ Proc *m;
+
+ fprint(2, "iostats: %s: %r\n", s);
+
+ /* Clear away the slave children */
+ for(m = Proclist; m; m = m->next)
+ postnote(PNPROC, m->pid, "exit");
+
+ exits("fatal");
+}
+
+char*
+rdenv(char *v, char **end)
+{
+ int fd, n;
+ char *buf;
+ Dir *d;
+ if((fd = open(v, OREAD)) == -1)
+ return nil;
+ d = dirfstat(fd);
+ if(d == nil || (buf = malloc(d->length + 1)) == nil)
+ return nil;
+ n = (int)d->length;
+ n = read(fd, buf, n);
+ close(fd);
+ if(n <= 0){
+ free(buf);
+ buf = nil;
+ }else{
+ if(buf[n-1] != '\0')
+ buf[n++] = '\0';
+ *end = &buf[n];
+ }
+ free(d);
+ return buf;
+}
+
+char Defaultpath[] = ".\0/bin";
+void
+runprog(char *argv[])
+{
+ char *path, *ep, *p;
+ char arg0[256];
+
+ path = rdenv("/env/path", &ep);
+ if(path == nil){
+ path = Defaultpath;
+ ep = path+sizeof(Defaultpath);
+ }
+ for(p = path; p < ep; p += strlen(p)+1){
+ snprint(arg0, sizeof arg0, "%s/%s", p, argv[0]);
+ exec(arg0, argv);
+ }
+ fatal("exec");
+}
+
+void
+catcher(void *a, char *msg)
+{
+ USED(a);
+ if(strcmp(msg, DONESTR) == 0) {
+ done = 1;
+ noted(NCONT);
+ }
+ if(strcmp(msg, "exit") == 0)
+ exits("exit");
+
+ noted(NDFLT);
+}
+
+void
+fidreport(Fid *f)
+{
+ char *p, path[128];
+ Frec *fr;
+
+ p = path;
+ makepath(p, f->f, "");
+
+ for(fr = frhead; fr; fr = fr->next) {
+ if(strcmp(fr->op, p) == 0) {
+ fr->nread += f->nread;
+ fr->nwrite += f->nwrite;
+ fr->bread += f->bread;
+ fr->bwrite += f->bwrite;
+ fr->opens++;
+ return;
+ }
+ }
+
+ fr = malloc(sizeof(Frec));
+ if(fr == 0 || (fr->op = strdup(p)) == 0)
+ fatal("no memory");
+
+ fr->nread = f->nread;
+ fr->nwrite = f->nwrite;
+ fr->bread = f->bread;
+ fr->bwrite = f->bwrite;
+ fr->opens = 1;
+ if(frhead == 0) {
+ frhead = fr;
+ frtail = fr;
+ }
+ else {
+ frtail->next = fr;
+ frtail = fr;
+ }
+ fr->next = 0;
+}
diff --git a/sys/src/cmd/iostats/mkfile b/sys/src/cmd/iostats/mkfile
new file mode 100755
index 000000000..1d4bee534
--- /dev/null
+++ b/sys/src/cmd/iostats/mkfile
@@ -0,0 +1,11 @@
+</$objtype/mkfile
+
+TARG=iostats
+OFILES=iostats.$O\
+ statsrv.$O\
+
+HFILES=statfs.h\
+
+BIN=/$objtype/bin
+</sys/src/cmd/mkone
+
diff --git a/sys/src/cmd/iostats/statfs.h b/sys/src/cmd/iostats/statfs.h
new file mode 100755
index 000000000..6d82f1938
--- /dev/null
+++ b/sys/src/cmd/iostats/statfs.h
@@ -0,0 +1,142 @@
+/*
+ * statfs.h - definitions for statistic gathering file server
+ */
+
+#define DEBUGFILE "iostats.out"
+#define DONESTR "done"
+#define DEBUG if(!dbg){}else fprint
+#define MAXPROC 16
+#define FHASHSIZE 64
+#define fidhash(s) fhash[s%FHASHSIZE]
+
+enum{
+ Maxfdata = 8192, /* max size of data in 9P message */
+ Maxrpc = 20000,/* number of RPCs we'll log */
+};
+
+typedef struct Fsrpc Fsrpc;
+typedef struct Fid Fid;
+typedef struct File File;
+typedef struct Proc Proc;
+typedef struct Stats Stats;
+typedef struct Rpc Rpc;
+typedef struct Frec Frec;
+
+struct Frec
+{
+ Frec *next;
+ char *op;
+ ulong nread;
+ ulong nwrite;
+ ulong bread;
+ ulong bwrite;
+ ulong opens;
+};
+
+struct Rpc
+{
+ char *name;
+ ulong count;
+ vlong time;
+ vlong lo;
+ vlong hi;
+ ulong bin;
+ ulong bout;
+};
+
+struct Stats
+{
+ ulong totread;
+ ulong totwrite;
+ ulong nrpc;
+ ulong nproto;
+ Rpc rpc[Maxrpc];
+};
+
+struct Fsrpc
+{
+ int busy; /* Work buffer has pending rpc to service */
+ uintptr pid; /* Pid of slave process executing the rpc */
+ int canint; /* Interrupt gate */
+ int flushtag; /* Tag on which to reply to flush */
+ Fcall work; /* Plan 9 incoming Fcall */
+ uchar buf[IOHDRSZ+Maxfdata]; /* Data buffer */
+};
+
+struct Fid
+{
+ int fid; /* system fd for i/o */
+ File *f; /* File attached to this fid */
+ int mode;
+ int nr; /* fid number */
+ Fid *next; /* hash link */
+ ulong nread;
+ ulong nwrite;
+ ulong bread;
+ ulong bwrite;
+ vlong offset; /* for directories */
+};
+
+struct File
+{
+ char *name;
+ Qid qid;
+ int inval;
+ File *parent;
+ File *child;
+ File *childlist;
+};
+
+struct Proc
+{
+ uintptr pid;
+ int busy;
+ Proc *next;
+};
+
+enum
+{
+ Nr_workbufs = 40,
+ Dsegpad = 8192,
+ Fidchunk = 1000,
+};
+
+Extern Fsrpc *Workq;
+Extern int dbg;
+Extern File *root;
+Extern Fid **fhash;
+Extern Fid *fidfree;
+Extern int qid;
+Extern Proc *Proclist;
+Extern int done;
+Extern Stats *stats;
+Extern Frec *frhead;
+Extern Frec *frtail;
+Extern int myiounit;
+
+/* File system protocol service procedures */
+void Xcreate(Fsrpc*), Xclunk(Fsrpc*);
+void Xversion(Fsrpc*), Xauth(Fsrpc*), Xflush(Fsrpc*);
+void Xattach(Fsrpc*), Xwalk(Fsrpc*), Xauth(Fsrpc*);
+void Xremove(Fsrpc*), Xstat(Fsrpc*), Xwstat(Fsrpc*);
+void slave(Fsrpc*);
+
+void reply(Fcall*, Fcall*, char*);
+Fid *getfid(int);
+int freefid(int);
+Fid *newfid(int);
+Fsrpc *getsbuf(void);
+void initroot(void);
+void fatal(char*);
+void makepath(char*, File*, char*);
+File *file(File*, char*);
+void slaveopen(Fsrpc*);
+void slaveread(Fsrpc*);
+void slavewrite(Fsrpc*);
+void blockingslave(void);
+void reopen(Fid *f);
+void noteproc(int, char*);
+void flushaction(void*, char*);
+void catcher(void*, char*);
+ulong msec(void);
+void fidreport(Fid*);
diff --git a/sys/src/cmd/iostats/statsrv.c b/sys/src/cmd/iostats/statsrv.c
new file mode 100755
index 000000000..7222bd825
--- /dev/null
+++ b/sys/src/cmd/iostats/statsrv.c
@@ -0,0 +1,672 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#define Extern extern
+#include "statfs.h"
+
+char Ebadfid[] = "Bad fid";
+char Enotdir[] ="Not a directory";
+char Edupfid[] = "Fid already in use";
+char Eopen[] = "Fid already opened";
+char Exmnt[] = "Cannot .. past mount point";
+char Enoauth[] = "iostats: Authentication failed";
+char Ebadver[] = "Unrecognized 9P version";
+
+int
+okfile(char *s, int mode)
+{
+ if(strncmp(s, "/fd/", 3) == 0){
+ /* 0, 1, and 2 we handle ourselves */
+ if(s[4]=='/' || atoi(s+4) > 2)
+ return 0;
+ return 1;
+ }
+ if(strncmp(s, "/net/ssl", 8) == 0)
+ return 0;
+ if(strncmp(s, "/net/tls", 8) == 0)
+ return 0;
+ if(strncmp(s, "/srv/", 5) == 0 && ((mode&3) == OWRITE || (mode&3) == ORDWR))
+ return 0;
+ return 1;
+}
+
+void
+update(Rpc *rpc, vlong t)
+{
+ vlong t2;
+
+ t2 = nsec();
+ t = t2 - t;
+ if(t < 0)
+ t = 0;
+
+ rpc->time += t;
+ if(t < rpc->lo)
+ rpc->lo = t;
+ if(t > rpc->hi)
+ rpc->hi = t;
+}
+
+void
+Xversion(Fsrpc *r)
+{
+ Fcall thdr;
+ vlong t;
+
+ t = nsec();
+
+ if(r->work.msize > IOHDRSZ+Maxfdata)
+ thdr.msize = IOHDRSZ+Maxfdata;
+ else
+ thdr.msize = r->work.msize;
+ myiounit = thdr.msize - IOHDRSZ;
+ if(strncmp(r->work.version, "9P2000", 6) != 0){
+ reply(&r->work, &thdr, Ebadver);
+ r->busy = 0;
+ return;
+ }
+ thdr.version = "9P2000";
+ /* BUG: should clunk all fids */
+ reply(&r->work, &thdr, 0);
+ r->busy = 0;
+
+ update(&stats->rpc[Tversion], t);
+}
+
+void
+Xauth(Fsrpc *r)
+{
+ Fcall thdr;
+ vlong t;
+
+ t = nsec();
+
+ reply(&r->work, &thdr, Enoauth);
+ r->busy = 0;
+
+ update(&stats->rpc[Tauth], t);
+}
+
+void
+Xflush(Fsrpc *r)
+{
+ Fsrpc *t, *e;
+ Fcall thdr;
+
+ e = &Workq[Nr_workbufs];
+
+ for(t = Workq; t < e; t++) {
+ if(t->work.tag == r->work.oldtag) {
+ DEBUG(2, "\tQ busy %d pid %p can %d\n", t->busy, t->pid, t->canint);
+ if(t->busy && t->pid) {
+ t->flushtag = r->work.tag;
+ DEBUG(2, "\tset flushtag %d\n", r->work.tag);
+ if(t->canint)
+ postnote(PNPROC, t->pid, "flush");
+ r->busy = 0;
+ return;
+ }
+ }
+ }
+
+ reply(&r->work, &thdr, 0);
+ DEBUG(2, "\tflush reply\n");
+ r->busy = 0;
+}
+
+void
+Xattach(Fsrpc *r)
+{
+ Fcall thdr;
+ Fid *f;
+ vlong t;
+
+ t = nsec();
+
+ f = newfid(r->work.fid);
+ if(f == 0) {
+ reply(&r->work, &thdr, Ebadfid);
+ r->busy = 0;
+ return;
+ }
+
+ f->f = root;
+ thdr.qid = f->f->qid;
+ reply(&r->work, &thdr, 0);
+ r->busy = 0;
+
+ update(&stats->rpc[Tattach], t);
+}
+
+void
+Xwalk(Fsrpc *r)
+{
+ char errbuf[ERRMAX], *err;
+ Fcall thdr;
+ Fid *f, *n;
+ File *nf;
+ vlong t;
+ int i;
+
+ t = nsec();
+
+ f = getfid(r->work.fid);
+ if(f == 0) {
+ reply(&r->work, &thdr, Ebadfid);
+ r->busy = 0;
+ return;
+ }
+ n = nil;
+ if(r->work.newfid != r->work.fid){
+ n = newfid(r->work.newfid);
+ if(n == 0) {
+ reply(&r->work, &thdr, Edupfid);
+ r->busy = 0;
+ return;
+ }
+ n->f = f->f;
+ f = n; /* walk new guy */
+ }
+
+ thdr.nwqid = 0;
+ err = nil;
+ for(i=0; i<r->work.nwname; i++){
+ if(i >= MAXWELEM)
+ break;
+ if(strcmp(r->work.wname[i], "..") == 0) {
+ if(f->f->parent == 0) {
+ err = Exmnt;
+ break;
+ }
+ f->f = f->f->parent;
+ thdr.wqid[thdr.nwqid++] = f->f->qid;
+ continue;
+ }
+
+ nf = file(f->f, r->work.wname[i]);
+ if(nf == 0) {
+ errstr(errbuf, sizeof errbuf);
+ err = errbuf;
+ break;
+ }
+
+ f->f = nf;
+ thdr.wqid[thdr.nwqid++] = nf->qid;
+ continue;
+ }
+
+ if(err == nil && thdr.nwqid == 0 && r->work.nwname > 0)
+ err = "file does not exist";
+
+ if(n != nil && (err != 0 || thdr.nwqid < r->work.nwname)){
+ /* clunk the new fid, which is the one we walked */
+ freefid(n->nr);
+ }
+
+ if(thdr.nwqid > 0)
+ err = nil;
+ reply(&r->work, &thdr, err);
+ r->busy = 0;
+
+ update(&stats->rpc[Twalk], t);
+}
+
+void
+Xclunk(Fsrpc *r)
+{
+ Fcall thdr;
+ Fid *f;
+ vlong t;
+ int fid;
+
+ t = nsec();
+
+ f = getfid(r->work.fid);
+ if(f == 0) {
+ reply(&r->work, &thdr, Ebadfid);
+ r->busy = 0;
+ return;
+ }
+
+ if(f->fid >= 0)
+ close(f->fid);
+
+ fid = r->work.fid;
+ reply(&r->work, &thdr, 0);
+ r->busy = 0;
+
+ update(&stats->rpc[Tclunk], t);
+
+ if(f->nread || f->nwrite)
+ fidreport(f);
+
+ freefid(fid);
+}
+
+void
+Xstat(Fsrpc *r)
+{
+ char err[ERRMAX], path[128];
+ uchar statbuf[STATMAX];
+ Fcall thdr;
+ Fid *f;
+ int s;
+ vlong t;
+
+ t = nsec();
+
+ f = getfid(r->work.fid);
+ if(f == 0) {
+ reply(&r->work, &thdr, Ebadfid);
+ r->busy = 0;
+ return;
+ }
+ makepath(path, f->f, "");
+ if(!okfile(path, -1)){
+ snprint(err, sizeof err, "iostats: can't simulate %s", path);
+ reply(&r->work, &thdr, err);
+ r->busy = 0;
+ return;
+ }
+
+ if(f->fid >= 0)
+ s = fstat(f->fid, statbuf, sizeof statbuf);
+ else
+ s = stat(path, statbuf, sizeof statbuf);
+
+ if(s < 0) {
+ errstr(err, sizeof err);
+ reply(&r->work, &thdr, err);
+ r->busy = 0;
+ return;
+ }
+ thdr.stat = statbuf;
+ thdr.nstat = s;
+ reply(&r->work, &thdr, 0);
+ r->busy = 0;
+
+ update(&stats->rpc[Tstat], t);
+}
+
+void
+Xcreate(Fsrpc *r)
+{
+ char err[ERRMAX], path[128];
+ Fcall thdr;
+ Fid *f;
+ File *nf;
+ vlong t;
+
+ t = nsec();
+
+ f = getfid(r->work.fid);
+ if(f == 0) {
+ reply(&r->work, &thdr, Ebadfid);
+ r->busy = 0;
+ return;
+ }
+
+
+ makepath(path, f->f, r->work.name);
+ f->fid = create(path, r->work.mode, r->work.perm);
+ if(f->fid < 0) {
+ errstr(err, sizeof err);
+ reply(&r->work, &thdr, err);
+ r->busy = 0;
+ return;
+ }
+
+ nf = file(f->f, r->work.name);
+ if(nf == 0) {
+ errstr(err, sizeof err);
+ reply(&r->work, &thdr, err);
+ r->busy = 0;
+ return;
+ }
+
+ f->mode = r->work.mode;
+ f->f = nf;
+ thdr.iounit = myiounit;
+ thdr.qid = f->f->qid;
+ reply(&r->work, &thdr, 0);
+ r->busy = 0;
+
+ update(&stats->rpc[Tcreate], t);
+}
+
+
+void
+Xremove(Fsrpc *r)
+{
+ char err[ERRMAX], path[128];
+ Fcall thdr;
+ Fid *f;
+ vlong t;
+
+ t = nsec();
+
+ f = getfid(r->work.fid);
+ if(f == 0) {
+ reply(&r->work, &thdr, Ebadfid);
+ r->busy = 0;
+ return;
+ }
+
+ makepath(path, f->f, "");
+ DEBUG(2, "\tremove: %s\n", path);
+ if(remove(path) < 0) {
+ errstr(err, sizeof err);
+ reply(&r->work, &thdr, err);
+ freefid(r->work.fid);
+ r->busy = 0;
+ return;
+ }
+
+ f->f->inval = 1;
+ if(f->fid >= 0)
+ close(f->fid);
+ freefid(r->work.fid);
+
+ reply(&r->work, &thdr, 0);
+ r->busy = 0;
+
+ update(&stats->rpc[Tremove], t);
+}
+
+void
+Xwstat(Fsrpc *r)
+{
+ char err[ERRMAX], path[128];
+ Fcall thdr;
+ Fid *f;
+ int s;
+ vlong t;
+
+ t = nsec();
+
+ f = getfid(r->work.fid);
+ if(f == 0) {
+ reply(&r->work, &thdr, Ebadfid);
+ r->busy = 0;
+ return;
+ }
+ if(f->fid >= 0)
+ s = fwstat(f->fid, r->work.stat, r->work.nstat);
+ else {
+ makepath(path, f->f, "");
+ s = wstat(path, r->work.stat, r->work.nstat);
+ }
+ if(s < 0) {
+ errstr(err, sizeof err);
+ reply(&r->work, &thdr, err);
+ }
+ else
+ reply(&r->work, &thdr, 0);
+
+ r->busy = 0;
+ update(&stats->rpc[Twstat], t);
+}
+
+void
+slave(Fsrpc *f)
+{
+ int r;
+ Proc *p;
+ uintptr pid;
+ static int nproc;
+
+ for(;;) {
+ for(p = Proclist; p; p = p->next) {
+ if(p->busy == 0) {
+ f->pid = p->pid;
+ p->busy = 1;
+ pid = (uintptr)rendezvous((void*)p->pid, f);
+ if(pid != p->pid)
+ fatal("rendezvous sync fail");
+ return;
+ }
+ }
+
+ if(++nproc > MAXPROC)
+ fatal("too many procs");
+
+ r = rfork(RFPROC|RFMEM);
+ if(r < 0)
+ fatal("rfork");
+
+ if(r == 0)
+ blockingslave();
+
+ p = malloc(sizeof(Proc));
+ if(p == 0)
+ fatal("out of memory");
+
+ p->busy = 0;
+ p->pid = r;
+ p->next = Proclist;
+ Proclist = p;
+
+ rendezvous((void*)p->pid, p);
+ }
+}
+
+void
+blockingslave(void)
+{
+ Proc *m;
+ uintptr pid;
+ Fsrpc *p;
+ Fcall thdr;
+
+ notify(flushaction);
+
+ pid = getpid();
+
+ m = rendezvous((void*)pid, 0);
+
+ for(;;) {
+ p = rendezvous((void*)pid, (void*)pid);
+ if(p == (void*)~0) /* Interrupted */
+ continue;
+
+ DEBUG(2, "\tslave: %p %F b %d p %p\n", pid, &p->work, p->busy, p->pid);
+ if(p->flushtag != NOTAG)
+ return;
+
+ switch(p->work.type) {
+ case Tread:
+ slaveread(p);
+ break;
+ case Twrite:
+ slavewrite(p);
+ break;
+ case Topen:
+ slaveopen(p);
+ break;
+ default:
+ reply(&p->work, &thdr, "exportfs: slave type error");
+ }
+ if(p->flushtag != NOTAG) {
+ p->work.type = Tflush;
+ p->work.tag = p->flushtag;
+ reply(&p->work, &thdr, 0);
+ }
+ p->busy = 0;
+ m->busy = 0;
+ }
+}
+
+void
+slaveopen(Fsrpc *p)
+{
+ char err[ERRMAX], path[128];
+ Fcall *work, thdr;
+ Fid *f;
+ vlong t;
+
+ work = &p->work;
+
+ t = nsec();
+
+ f = getfid(work->fid);
+ if(f == 0) {
+ reply(work, &thdr, Ebadfid);
+ return;
+ }
+ if(f->fid >= 0) {
+ close(f->fid);
+ f->fid = -1;
+ }
+
+ makepath(path, f->f, "");
+ DEBUG(2, "\topen: %s %d\n", path, work->mode);
+
+ p->canint = 1;
+ if(p->flushtag != NOTAG)
+ return;
+
+ if(!okfile(path, work->mode)){
+ snprint(err, sizeof err, "iostats can't simulate %s", path);
+ reply(work, &thdr, err);
+ return;
+ }
+
+ /* There is a race here I ignore because there are no locks */
+ f->fid = open(path, work->mode);
+ p->canint = 0;
+ if(f->fid < 0) {
+ errstr(err, sizeof err);
+ reply(work, &thdr, err);
+ return;
+ }
+
+ DEBUG(2, "\topen: fd %d\n", f->fid);
+ f->mode = work->mode;
+ thdr.iounit = myiounit;
+ thdr.qid = f->f->qid;
+ reply(work, &thdr, 0);
+
+ update(&stats->rpc[Topen], t);
+}
+
+void
+slaveread(Fsrpc *p)
+{
+ char data[Maxfdata], err[ERRMAX];
+ Fcall *work, thdr;
+ Fid *f;
+ int n, r;
+ vlong t;
+
+ work = &p->work;
+
+ t = nsec();
+
+ f = getfid(work->fid);
+ if(f == 0) {
+ reply(work, &thdr, Ebadfid);
+ return;
+ }
+
+ n = (work->count > Maxfdata) ? Maxfdata : work->count;
+ p->canint = 1;
+ if(p->flushtag != NOTAG)
+ return;
+ /* can't just call pread, since directories must update the offset */
+ if(f->f->qid.type&QTDIR){
+ if(work->offset != f->offset){
+ if(work->offset != 0){
+ snprint(err, sizeof err, "can't seek in directory from %lld to %lld", f->offset, work->offset);
+ reply(work, &thdr, err);
+ return;
+ }
+ if(seek(f->fid, 0, 0) != 0){
+ errstr(err, sizeof err);
+ reply(work, &thdr, err);
+ return;
+ }
+ f->offset = 0;
+ }
+ r = read(f->fid, data, n);
+ if(r > 0)
+ f->offset += r;
+ }else
+ r = pread(f->fid, data, n, work->offset);
+ p->canint = 0;
+ if(r < 0) {
+ errstr(err, sizeof err);
+ reply(work, &thdr, err);
+ return;
+ }
+
+ DEBUG(2, "\tread: fd=%d %d bytes\n", f->fid, r);
+
+ thdr.data = data;
+ thdr.count = r;
+ stats->totread += r;
+ f->nread++;
+ f->bread += r;
+ reply(work, &thdr, 0);
+
+ update(&stats->rpc[Tread], t);
+}
+
+void
+slavewrite(Fsrpc *p)
+{
+ char err[ERRMAX];
+ Fcall *work, thdr;
+ Fid *f;
+ int n;
+ vlong t;
+
+ work = &p->work;
+
+ t = nsec();
+
+ f = getfid(work->fid);
+ if(f == 0) {
+ reply(work, &thdr, Ebadfid);
+ return;
+ }
+
+ n = (work->count > Maxfdata) ? Maxfdata : work->count;
+ p->canint = 1;
+ if(p->flushtag != NOTAG)
+ return;
+ n = pwrite(f->fid, work->data, n, work->offset);
+ p->canint = 0;
+ if(n < 0) {
+ errstr(err, sizeof err);
+ reply(work, &thdr, err);
+ return;
+ }
+
+ DEBUG(2, "\twrite: %d bytes fd=%d\n", n, f->fid);
+
+ thdr.count = n;
+ f->nwrite++;
+ f->bwrite += n;
+ stats->totwrite += n;
+ reply(work, &thdr, 0);
+
+ update(&stats->rpc[Twrite], t);
+}
+
+void
+reopen(Fid *f)
+{
+ USED(f);
+ fatal("reopen");
+}
+
+void
+flushaction(void *a, char *cause)
+{
+ USED(a);
+ if(strncmp(cause, "kill", 4) == 0)
+ noted(NDFLT);
+
+ noted(NCONT);
+}