diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/iostats |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/iostats')
-rwxr-xr-x | sys/src/cmd/iostats/iostats.c | 598 | ||||
-rwxr-xr-x | sys/src/cmd/iostats/mkfile | 11 | ||||
-rwxr-xr-x | sys/src/cmd/iostats/statfs.h | 142 | ||||
-rwxr-xr-x | sys/src/cmd/iostats/statsrv.c | 672 |
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); +} |