From 23aaa0c59cb0bf3e82e587571ee70d2c5c9e410b Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sat, 2 Aug 2014 02:30:19 +0200 Subject: iostats: reimplement iostats as a 9p filter instead of duplicating exportfs 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 | 7 +- sys/src/cmd/iostats.c | 534 +++++++++++++++++++++++++++++++++ sys/src/cmd/iostats/iostats.c | 601 ------------------------------------- sys/src/cmd/iostats/mkfile | 11 - sys/src/cmd/iostats/statfs.h | 142 --------- sys/src/cmd/iostats/statsrv.c | 672 ------------------------------------------ 6 files changed, 538 insertions(+), 1429 deletions(-) create mode 100644 sys/src/cmd/iostats.c delete mode 100644 sys/src/cmd/iostats/iostats.c delete mode 100644 sys/src/cmd/iostats/mkfile delete mode 100644 sys/src/cmd/iostats/statfs.h delete mode 100644 sys/src/cmd/iostats/statsrv.c diff --git a/sys/man/4/iostats b/sys/man/4/iostats index cfc77c945..58387a0f5 100644 --- a/sys/man/4/iostats +++ b/sys/man/4/iostats @@ -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 index 000000000..0807f5bb1 --- /dev/null +++ b/sys/src/cmd/iostats.c @@ -0,0 +1,534 @@ +/* + * iostats - Gather file system information + */ +#include +#include +#include +#include + +#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; inwqid; 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 index b85e1d75e..000000000 --- a/sys/src/cmd/iostats/iostats.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * iostats - Gather file system information - */ -#include -#include -#include -#include -#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 index 1d4bee534..000000000 --- a/sys/src/cmd/iostats/mkfile +++ /dev/null @@ -1,11 +0,0 @@ - -#include -#include -#include -#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; iwork.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); -} -- cgit v1.2.3