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.
This commit is contained in:
parent
d14b6a0bf9
commit
23aaa0c59c
6 changed files with 538 additions and 1429 deletions
|
@ -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.
|
||||
|
|
534
sys/src/cmd/iostats.c
Normal file
534
sys/src/cmd/iostats.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
TARG=iostats
|
||||
OFILES=iostats.$O\
|
||||
statsrv.$O\
|
||||
|
||||
HFILES=statfs.h\
|
||||
|
||||
BIN=/$objtype/bin
|
||||
</sys/src/cmd/mkone
|
||||
|
|
@ -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*);
|
|
@ -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);
|
||||
}
|
Loading…
Reference in a new issue