]> git.lizzy.rs Git - plan9front.git/commitdiff
iostats: reimplement iostats as a 9p filter instead of duplicating exportfs
authorcinap_lenrek <cinap_lenrek@felloff.net>
Sat, 2 Aug 2014 00:30:19 +0000 (02:30 +0200)
committercinap_lenrek <cinap_lenrek@felloff.net>
Sat, 2 Aug 2014 00:30:19 +0000 (02:30 +0200)
old iostats failed to work when builidng the kernel due to old bugs
that where already fixed in exportfs. instead of backporting the fixes,
reimplement iostats as a filter that sits between exportfs and the
process mount. from users perspective, theres no difference.

the result is much smaller and can handle everything that exportfs
can like /srv.

sys/man/4/iostats
sys/src/cmd/iostats.c [new file with mode: 0644]
sys/src/cmd/iostats/iostats.c [deleted file]
sys/src/cmd/iostats/mkfile [deleted file]
sys/src/cmd/iostats/statfs.h [deleted file]
sys/src/cmd/iostats/statsrv.c [deleted file]

index cfc77c9458b52f4b605604e9c9c5b5f48504e635..58387a0f5bc1c443de60aff7e7f6d5db65cc9aee 100644 (file)
@@ -17,7 +17,7 @@ iostats \- file system to measure I/O
 ]
 .SH DESCRIPTION
 .I Iostats
-is a user-level file server that interposes itself between a program
+is a user-level 9p filter that interposes itself between a program
 and the regular file server, which
 allows it to gather statistics of file system
 use at the level of the Plan 9 file system protocol, 9P.
@@ -76,9 +76,10 @@ Start a new shell, displaying all 9P traffic caused by the shell or its children
 iostats -df /fd/1 rc
 .EE
 .SH SOURCE
-.B /sys/src/cmd/iostats
+.B /sys/src/cmd/iostats.c
 .SH SEE ALSO
-.IR dup (3)
+.IR dup (3),
+.IR exportfs (4)
 .SH BUGS
 Poor clock resolution means that large amounts of I/O must be done to
 get accurate rate figures.
diff --git a/sys/src/cmd/iostats.c b/sys/src/cmd/iostats.c
new file mode 100644 (file)
index 0000000..0807f5b
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * iostats - Gather file system information
+ */
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+
+#define DEBUGFILE      "iostats.out"
+#define DONESTR                "done"
+
+enum{
+       Maxfile         = 1000, /* number of Files we'll log */
+       Maxrpc          = 20000,/* number of RPCs we'll log */
+};
+
+typedef struct File File;
+typedef struct Fid Fid;
+typedef struct Req Req;
+typedef struct Rpc Rpc;
+typedef struct Stats Stats;
+
+/* per file statistics */
+struct File
+{
+       Qid     qid;
+       char    *path;
+
+       ulong   nopen;
+
+       ulong   nread;
+       vlong   bread;
+
+       ulong   nwrite;
+       vlong   bwrite;
+};
+
+/* fid context */
+struct Fid
+{
+       int     fid;
+       Qid     qid;
+       char    *path;
+       File    *file;  /* set on open/create */
+       Fid     *next;
+};
+
+/* a request */
+struct Req
+{
+       Req     *next;
+       vlong   t;
+
+       Fcall   f;
+       uchar   buf[];
+};
+
+/* per rpc statistics */
+struct Rpc
+{
+       char    *name;
+       ulong   count;
+       vlong   time;
+       vlong   lo;
+       vlong   hi;
+       vlong   bin;
+       vlong   bout;
+};
+
+/* all the statistics */
+struct Stats
+{
+       vlong   totread;
+       vlong   totwrite;
+       ulong   nrpc;
+       vlong   nproto;
+       Rpc     rpc[Maxrpc];
+       File    file[Maxfile];
+};
+
+Stats  stats[1];
+
+int pfd[2];
+int efd[2];
+int done;
+
+Lock rqlock;
+Req *rqhead;
+
+Fid *fidtab[1024];
+
+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
+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;
+}
+
+Fid**
+fidhash(int fid)
+{
+       return &fidtab[fid % nelem(fidtab)];
+}
+
+Fid*
+getfid(int fid, int new)
+{
+       Fid *f, **ff;
+
+       ff = fidhash(fid);
+       for(f = *ff; f != nil; f = f->next){
+               if(f->fid == fid)
+                       return f;
+       }
+       if(new){
+               f = mallocz(sizeof(*f), 1);
+               f->fid = fid;
+               f->next = *ff;
+               *ff = f;
+       }
+       return f;
+}
+
+void
+setfid(Fid *f, char *path, Qid qid)
+{
+       if(path != f->path){
+               free(f->path);
+               f->path = path;
+       }
+       f->qid = qid;
+       f->file = nil;
+}
+
+void
+rattach(Fcall *fin, Fcall *fout)
+{
+       setfid(getfid(fin->fid, 1), strdup("/"), fout->qid);
+}
+
+void
+rwalk(Fcall *fin, Fcall *fout)
+{
+       Fid *of, *f;
+       int i;
+
+       if((of = getfid(fin->fid, 0)) == nil)
+               return;
+       f = getfid(fin->newfid, 1);
+       if(f != of)
+               setfid(f, strdup(of->path), of->qid);
+       for(i=0; i<fout->nwqid; i++)
+               setfid(f, cleanname(smprint("%s/%s", f->path, fin->wname[i])), fout->wqid[i]);
+}
+
+void
+ropen(Fcall *fin, Fcall *fout)
+{
+       File *fs;
+       Fid *f;
+
+       if((f = getfid(fin->fid, 0)) == nil)
+               return;
+       if(fin->type == Tcreate)
+               setfid(f, cleanname(smprint("%s/%s", f->path, fin->name)), fout->qid);
+       else
+               setfid(f, f->path, fout->qid);
+       for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){
+               if(fs->nopen == 0){
+                       fs->path = strdup(f->path);
+                       fs->qid = f->qid;
+                       f->file = fs;
+                       break;
+               }
+               if(fs->qid.path == f->qid.path && strcmp(fs->path, f->path) == 0){
+                       f->file = fs;
+                       break;
+               }
+       }
+       if(f->file != nil)
+               f->file->nopen++;
+}
+
+void
+rclunk(Fcall *fin)
+{
+       Fid **ff, *f;
+
+       for(ff = fidhash(fin->fid); (f = *ff) != nil; ff = &f->next){
+               if(f->fid == fin->fid){
+                       *ff = f->next;
+                       free(f->path);
+                       free(f);
+                       return;
+               }
+       }
+}
+
+void
+rio(Fcall *fin, Fcall *fout)
+{
+       Fid *f;
+       int count;
+
+       count = fout->count;
+       if((f = getfid(fin->fid, 0)) == nil)
+               return;
+       switch(fout->type){
+       case Rread:
+               if(f->file != nil){
+                       f->file->nread++;
+                       f->file->bread += count;
+               }
+               stats->totread += count;
+               break;
+       case Rwrite:
+               if(f->file != nil){
+                       f->file->nwrite++;
+                       f->file->bwrite += count;
+               }
+               stats->totwrite += count;
+               break;
+       }
+}
+
+void
+usage(void)
+{
+       fprint(2, "usage: iostats [-d] [-f debugfile] cmds [args ...]\n");
+       exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+       Rpc *rpc;
+       ulong ttime;
+       char *dbfile;
+       char buf[64*1024];
+       float brpsec, bwpsec, bppsec;
+       int cpid, fspid, rspid, dbg, n, mflag;
+       File *fs;
+       Req *r, **rr;
+
+       dbg = 0;
+       mflag = MREPL;
+       dbfile = DEBUGFILE;
+
+       ARGBEGIN{
+       case 'd':
+               dbg++;
+               break;
+       case 'f':
+               dbfile = ARGF();
+               break;
+       case 'C':
+               mflag |= MCACHE;
+               break;
+       default:
+               usage();
+       }ARGEND
+
+       USED(dbfile);
+
+       if(argc == 0)
+               usage();
+
+       if(pipe(pfd) < 0)
+               sysfatal("pipe");
+
+       switch(cpid = fork()) {
+       case -1:
+               sysfatal("fork");
+       case 0:
+               close(pfd[1]);
+               if(getwd(buf, sizeof(buf)) == 0)
+                       sysfatal("no working directory");
+
+               rfork(RFENVG|RFNAMEG|RFNOTEG);
+
+               if(mount(pfd[0], -1, "/", mflag, "") < 0)
+                       sysfatal("mount /");
+
+               bind("#c/pid", "/dev/pid", MREPL);
+               bind("#c/ppid", "/dev/ppid", 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)
+                       sysfatal("chdir");
+               exec(argv[0], argv);
+               exec(smprint("/bin/%s", argv[0]), argv);
+               sysfatal("exec: %r");
+       default:
+               close(pfd[0]);
+       }
+
+       switch(fspid = fork()) {
+       default:
+               while(cpid != waitpid())
+                       ;
+               postnote(PNPROC, fspid, DONESTR);
+               while(fspid != waitpid())
+                       ;
+               exits(0);
+       case -1:
+               sysfatal("fork");
+       case 0:
+               notify(catcher);
+               break;
+       }
+
+       if(pipe(efd) < 0)
+               sysfatal("pipe");
+
+       /* spawn exportfs */
+       switch(fork()) {
+       default:
+               close(efd[0]);
+               break;
+       case -1:
+               sysfatal("fork");
+       case 0:
+               dup(efd[0], 0);
+               close(efd[0]);
+               close(efd[1]);
+               if(dbg){
+                       execl("/bin/exportfs", "exportfs", "-df", dbfile, "-r", "/", nil);
+               } else {
+                       execl("/bin/exportfs", "exportfs", "-r", "/", nil);
+               }
+               exits(0);
+       }
+
+       fmtinstall('F', fcallfmt);
+
+       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;
+
+       switch(rspid = rfork(RFPROC|RFMEM)) {
+       case 0:
+               /* read response from exportfs and pass to mount */
+               while(!done){
+                       uchar tmp[sizeof(buf)];
+                       Fcall f;
+
+                       n = read(efd[1], buf, sizeof(buf));
+                       if(n < 0)
+                               break;
+                       if(n == 0)
+                               continue;
+
+                       /* convert response */
+                       memset(&f, 0, sizeof(f));
+                       memmove(tmp, buf, n);
+                       if(convM2S(tmp, n, &f) != n)
+                               sysfatal("convM2S: %r");
+
+                       /* find request to this response */
+                       lock(&rqlock);
+                       for(rr = &rqhead; (r = *rr) != nil; rr = &r->next){
+                               if(r->f.tag == f.tag){
+                                       *rr = r->next;
+                                       r->next = nil;
+                                       break;
+                               }
+                       }
+                       stats->nproto += n;
+                       unlock(&rqlock);
+
+                       switch(f.type){
+                       case Ropen:
+                       case Rcreate:
+                               ropen(&r->f, &f);
+                               break;
+                       case Rclunk:
+                       case Rremove:
+                               rclunk(&r->f);
+                               break;
+                       case Rattach:
+                               rattach(&r->f, &f);
+                               break;
+                       case Rwalk:
+                               rwalk(&r->f, &f);
+                               break;
+                       case Rread:
+                       case Rwrite:
+                               rio(&r->f, &f);
+                               break;
+                       }
+
+                       rpc = &stats->rpc[r->f.type];
+                       update(rpc, r->t);
+                       rpc->bout += n;
+                       free(r);
+
+                       if(write(pfd[1], buf, n) != n)
+                               break;
+               }
+               exits(0);
+       default:
+               /* read request from mount and pass to exportfs */
+               while(!done){
+                       n = read(pfd[1], buf, sizeof(buf));
+                       if(n < 0)
+                               break;
+                       if(n == 0)
+                               continue;
+
+                       r = mallocz(sizeof(*r) + n, 1);
+                       memmove(r->buf, buf, n);
+                       if(convM2S(r->buf, n, &r->f) != n)
+                               sysfatal("convM2S: %r");
+
+                       rpc = &stats->rpc[r->f.type];
+                       rpc->count++;
+                       rpc->bin += n;
+
+                       lock(&rqlock);
+                       stats->nrpc++;
+                       stats->nproto += n;
+                       r->next = rqhead;
+                       rqhead = r;
+                       unlock(&rqlock);
+
+                       r->t = nsec();
+
+                       if(write(efd[1], buf, n) != n)
+                               break;
+               }
+       }
+
+       /* shutdown */
+       done = 1;
+       postnote(PNPROC, rspid, DONESTR);
+       close(pfd[1]);
+       close(efd[1]);
+
+       /* dump statistics */
+       rpc = &stats->rpc[Tread];
+       brpsec = (double)stats->totread / (((float)rpc->time/1e9)+.000001);
+
+       rpc = &stats->rpc[Twrite];
+       bwpsec = (double)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 = (double)stats->nproto / ((ttime/1e9)+.000001);
+
+       fprint(2, "\nread      %llud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0);
+       fprint(2, "write     %llud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0);
+       fprint(2, "protocol  %llud 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 %8llud %8llud bytes\n", 
+                       rpc->name, 
+                       rpc->count,
+                       rpc->lo/1000000,
+                       rpc->hi/1000000,
+                       rpc->time/1000000,
+                       rpc->time/1000000/rpc->count,
+                       rpc->bin,
+                       rpc->bout);
+       }
+
+       fprint(2, "\nOpens    Reads  (bytes)   Writes  (bytes) File\n");
+       for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){
+               if(fs->nopen == 0)
+                       break;
+               fprint(2, "%5lud %8lud %8llud %8lud %8llud %s\n",
+                       fs->nopen,
+                       fs->nread, fs->bread,
+                       fs->nwrite, fs->bwrite,
+                       fs->path);
+       }
+
+       exits(0);
+}
diff --git a/sys/src/cmd/iostats/iostats.c b/sys/src/cmd/iostats/iostats.c
deleted file mode 100644 (file)
index b85e1d7..0000000
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * 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, mflag;
-
-       mflag = MREPL;
-       dbfile = DEBUGFILE;
-
-       ARGBEGIN{
-       case 'd':
-               dbg++;
-               break;
-       case 'f':
-               dbfile = ARGF();
-               break;
-       case 'C':
-               mflag |= MCACHE;
-               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, "/", mflag, "") < 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");
-
-               while((n = read9pmsg(p[1], r->buf, sizeof(r->buf))) == 0 && !done)
-                       ;
-               if(done || n < 0)
-                       break;
-
-               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
deleted file mode 100644 (file)
index 1d4bee5..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-</$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
deleted file mode 100644 (file)
index 68fccdc..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * statfs.h - definitions for statistic gathering file server
- */
-
-#define DEBUGFILE      "iostats.out"
-#define DONESTR                "done"
-#define DEBUG          if(!dbg){}else fprint
-#define MAXPROC                64
-#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
deleted file mode 100644 (file)
index 7222bd8..0000000
+++ /dev/null
@@ -1,672 +0,0 @@
-#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);
-}