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/cfs/cfs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/cfs/cfs.c')
-rwxr-xr-x | sys/src/cmd/cfs/cfs.c | 921 |
1 files changed, 921 insertions, 0 deletions
diff --git a/sys/src/cmd/cfs/cfs.c b/sys/src/cmd/cfs/cfs.c new file mode 100755 index 000000000..8bedeeb88 --- /dev/null +++ b/sys/src/cmd/cfs/cfs.c @@ -0,0 +1,921 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> + +#include "cformat.h" +#include "lru.h" +#include "bcache.h" +#include "disk.h" +#include "inode.h" +#include "file.h" +#include "stats.h" + +enum +{ + Nfid= 10240, +}; + +/* maximum length of a file */ +enum { MAXLEN = ~0ULL >> 1 }; + +typedef struct Mfile Mfile; +typedef struct Ram Ram; +typedef struct P9fs P9fs; + +struct Mfile +{ + Qid qid; + char busy; +}; + +Mfile mfile[Nfid]; +Icache ic; +int debug, statson, noauth, openserver; + +struct P9fs +{ + int fd[2]; + Fcall rhdr; + Fcall thdr; + long len; + char *name; +}; + +P9fs c; /* client conversation */ +P9fs s; /* server conversation */ + +struct Cfsstat cfsstat, cfsprev; +char statbuf[2048]; +int statlen; + +#define MAXFDATA 8192 /* i/o size for read/write */ + +int messagesize = MAXFDATA+IOHDRSZ; + +uchar datasnd[MAXFDATA + IOHDRSZ]; +uchar datarcv[MAXFDATA + IOHDRSZ]; + +Qid rootqid; +Qid ctlqid = {0x5555555555555555LL, 0, 0}; + +void rversion(void); +void rauth(Mfile*); +void rflush(void); +void rattach(Mfile*); +void rwalk(Mfile*); +void ropen(Mfile*); +void rcreate(Mfile*); +void rread(Mfile*); +void rwrite(Mfile*); +void rclunk(Mfile*); +void rremove(Mfile*); +void rstat(Mfile*); +void rwstat(Mfile*); +void error(char*, ...); +void warning(char*); +void mountinit(char*, char*); +void io(void); +void sendreply(char*); +void sendmsg(P9fs*, Fcall*); +void rcvmsg(P9fs*, Fcall*); +int delegate(void); +int askserver(void); +void cachesetup(int, char*, char*); +int ctltest(Mfile*); +void genstats(void); + +char *mname[]={ + [Tversion] "Tversion", + [Tauth] "Tauth", + [Tflush] "Tflush", + [Tattach] "Tattach", + [Twalk] "Twalk", + [Topen] "Topen", + [Tcreate] "Tcreate", + [Tclunk] "Tclunk", + [Tread] "Tread", + [Twrite] "Twrite", + [Tremove] "Tremove", + [Tstat] "Tstat", + [Twstat] "Twstat", + [Rversion] "Rversion", + [Rauth] "Rauth", + [Rerror] "Rerror", + [Rflush] "Rflush", + [Rattach] "Rattach", + [Rwalk] "Rwalk", + [Ropen] "Ropen", + [Rcreate] "Rcreate", + [Rclunk] "Rclunk", + [Rread] "Rread", + [Rwrite] "Rwrite", + [Rremove] "Rremove", + [Rstat] "Rstat", + [Rwstat] "Rwstat", + 0, +}; + +void +usage(void) +{ + fprint(2, "usage:\tcfs -s [-dknrS] [-f partition]\n"); + fprint(2, "\tcfs [-a netaddr | -F srv] [-dknrS] [-f partition] [mntpt]\n"); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + int std, format, chkid; + char *part, *server, *mtpt; + NetConnInfo *snci; + + std = 0; + format = 0; + chkid = 1; + part = "/dev/sdC0/cache"; + server = "tcp!fs"; + mtpt = "/tmp"; + + ARGBEGIN{ + case 'a': + server = EARGF(usage()); + break; + case 'd': + debug = 1; + break; + case 'f': + part = EARGF(usage()); + break; + case 'F': + server = EARGF(usage()); + openserver = 1; + break; + case 'k': + chkid = 0; + break; + case 'n': + noauth = 1; + break; + case 'r': + format = 1; + break; + case 'S': + statson = 1; + break; + case 's': + std = 1; + break; + default: + usage(); + }ARGEND + if(argc && *argv) + mtpt = *argv; + + if(debug) + fmtinstall('F', fcallfmt); + + c.name = "client"; + s.name = "server"; + if(std){ + c.fd[0] = c.fd[1] = 1; + s.fd[0] = s.fd[1] = 0; + }else + mountinit(server, mtpt); + + if(chkid){ + if((snci = getnetconninfo(nil, s.fd[0])) == nil) + /* Failed to lookup information; format */ + cachesetup(1, nil, part); + else + /* Do partition check */ + cachesetup(0, snci->raddr, part); + }else + /* Obey -f w/o regard to cache vs. remote server */ + cachesetup(format, nil, part); + + switch(fork()){ + case 0: + io(); + exits(""); + case -1: + error("fork"); + default: + exits(""); + } +} + +void +cachesetup(int format, char *name, char *partition) +{ + int f; + int secsize; + int inodes; + int blocksize; + + secsize = 512; + inodes = 1024; + blocksize = 4*1024; + + f = open(partition, ORDWR); + if(f < 0) + error("opening partition"); + + if(format || iinit(&ic, f, secsize, name) < 0){ + /* + * If we need to format and don't have a name, fall + * back to our old behavior of using "bootes" + */ + name = (name == nil? "bootes": name); + if(iformat(&ic, f, inodes, name, blocksize, secsize) < 0) + error("formatting failed"); + } +} + +void +mountinit(char *server, char *mountpoint) +{ + int err; + int p[2]; + + /* + * grab a channel and call up the file server + */ + if (openserver) + s.fd[0] = open(server, ORDWR); + else + s.fd[0] = dial(netmkaddr(server, 0, "9fs"), 0, 0, 0); + if(s.fd[0] < 0) + error("opening data: %r"); + s.fd[1] = s.fd[0]; + + /* + * mount onto name space + */ + if(pipe(p) < 0) + error("pipe failed"); + switch(fork()){ + case 0: + break; + default: + if (noauth) + err = mount(p[1], -1, mountpoint, MREPL|MCREATE, ""); + else + err = amount(p[1], mountpoint, MREPL|MCREATE, ""); + if (err < 0) + error("mount failed: %r"); + exits(0); + case -1: + error("fork failed\n"); +/*BUG: no wait!*/ + } + c.fd[0] = c.fd[1] = p[0]; +} + +void +io(void) +{ + int type; + Mfile *mf; + loop: + rcvmsg(&c, &c.thdr); + + type = c.thdr.type; + + if(statson){ + cfsstat.cm[type].n++; + cfsstat.cm[type].s = nsec(); + } + mf = &mfile[c.thdr.fid]; + switch(type){ + default: + error("type"); + break; + case Tversion: + rversion(); + break; + case Tauth: + mf = &mfile[c.thdr.afid]; + rauth(mf); + break; + case Tflush: + rflush(); + break; + case Tattach: + rattach(mf); + break; + case Twalk: + rwalk(mf); + break; + case Topen: + ropen(mf); + break; + case Tcreate: + rcreate(mf); + break; + case Tread: + rread(mf); + break; + case Twrite: + rwrite(mf); + break; + case Tclunk: + rclunk(mf); + break; + case Tremove: + rremove(mf); + break; + case Tstat: + rstat(mf); + break; + case Twstat: + rwstat(mf); + break; + } + if(statson){ + cfsstat.cm[type].t += nsec() -cfsstat.cm[type].s; + } + goto loop; +} + +void +rversion(void) +{ + if(messagesize > c.thdr.msize) + messagesize = c.thdr.msize; + c.thdr.msize = messagesize; /* set downstream size */ + delegate(); +} + +void +rauth(Mfile *mf) +{ + if(mf->busy) + error("auth to used channel"); + + if(delegate() == 0){ + mf->qid = s.rhdr.aqid; + mf->busy = 1; + } +} + +void +rflush(void) /* synchronous so easy */ +{ + sendreply(0); +} + +void +rattach(Mfile *mf) +{ + if(delegate() == 0){ + mf->qid = s.rhdr.qid; + mf->busy = 1; + if (statson == 1){ + statson++; + rootqid = mf->qid; + } + } +} + +void +rwalk(Mfile *mf) +{ + Mfile *nmf; + + nmf = nil; + if(statson + && mf->qid.type == rootqid.type && mf->qid.path == rootqid.path + && c.thdr.nwname == 1 && strcmp(c.thdr.wname[0], "cfsctl") == 0){ + /* This is the ctl file */ + nmf = &mfile[c.thdr.newfid]; + if(c.thdr.newfid != c.thdr.fid && nmf->busy) + error("clone to used channel"); + nmf = &mfile[c.thdr.newfid]; + nmf->qid = ctlqid; + nmf->busy = 1; + c.rhdr.nwqid = 1; + c.rhdr.wqid[0] = ctlqid; + sendreply(0); + return; + } + if(c.thdr.newfid != c.thdr.fid){ + if(c.thdr.newfid >= Nfid) + error("clone nfid out of range"); + nmf = &mfile[c.thdr.newfid]; + if(nmf->busy) + error("clone to used channel"); + nmf = &mfile[c.thdr.newfid]; + nmf->qid = mf->qid; + nmf->busy = 1; + mf = nmf; /* Walk mf */ + } + + if(delegate() < 0){ /* complete failure */ + if(nmf) + nmf->busy = 0; + return; + } + + if(s.rhdr.nwqid == c.thdr.nwname){ /* complete success */ + if(s.rhdr.nwqid > 0) + mf->qid = s.rhdr.wqid[s.rhdr.nwqid-1]; + return; + } + + /* partial success; release fid */ + if(nmf) + nmf->busy = 0; +} + +void +ropen(Mfile *mf) +{ + if(statson && ctltest(mf)){ + /* Opening ctl file */ + if(c.thdr.mode != OREAD){ + sendreply("does not exist"); + return; + } + c.rhdr.qid = ctlqid; + c.rhdr.iounit = 0; + sendreply(0); + genstats(); + return; + } + if(delegate() == 0){ + mf->qid = s.rhdr.qid; + if(c.thdr.mode & OTRUNC) + iget(&ic, mf->qid); + } +} + +void +rcreate(Mfile *mf) +{ + if(statson && ctltest(mf)){ + sendreply("exists"); + return; + } + if(delegate() == 0){ + mf->qid = s.rhdr.qid; + mf->qid.vers++; + } +} + +void +rclunk(Mfile *mf) +{ + if(!mf->busy){ + sendreply(0); + return; + } + mf->busy = 0; + delegate(); +} + +void +rremove(Mfile *mf) +{ + if(statson && ctltest(mf)){ + sendreply("not removed"); + return; + } + mf->busy = 0; + delegate(); +} + +void +rread(Mfile *mf) +{ + int cnt, done; + long n; + vlong off, first; + char *cp; + char data[MAXFDATA]; + Ibuf *b; + + off = c.thdr.offset; + first = off; + cnt = c.thdr.count; + + if(statson && ctltest(mf)){ + if(cnt > statlen-off) + c.rhdr.count = statlen-off; + else + c.rhdr.count = cnt; + if((int)c.rhdr.count < 0){ + sendreply("eof"); + return; + } + c.rhdr.data = statbuf + off; + sendreply(0); + return; + } + if(mf->qid.type & (QTDIR|QTAUTH)){ + delegate(); + if (statson) { + cfsstat.ndirread++; + if(c.rhdr.count > 0){ + cfsstat.bytesread += c.rhdr.count; + cfsstat.bytesfromdirs += c.rhdr.count; + } + } + return; + } + + b = iget(&ic, mf->qid); + if(b == 0){ + DPRINT(2, "delegating read\n"); + delegate(); + if (statson){ + cfsstat.ndelegateread++; + if(c.rhdr.count > 0){ + cfsstat.bytesread += c.rhdr.count; + cfsstat.bytesfromserver += c.rhdr.count; + } + } + return; + } + + cp = data; + done = 0; + while(cnt>0 && !done){ + if(off >= b->inode.length){ + DPRINT(2, "offset %lld greater than length %lld\n", + off, b->inode.length); + break; + } + n = fread(&ic, b, cp, off, cnt); + if(n <= 0){ + n = -n; + if(n==0 || n>cnt) + n = cnt; + DPRINT(2, + "fetch %ld bytes of data from server at offset %lld\n", + n, off); + s.thdr.type = c.thdr.type; + s.thdr.fid = c.thdr.fid; + s.thdr.tag = c.thdr.tag; + s.thdr.offset = off; + s.thdr.count = n; + if(statson) + cfsstat.ndelegateread++; + if(askserver() < 0){ + sendreply(s.rhdr.ename); + return; + } + if(s.rhdr.count != n) + done = 1; + n = s.rhdr.count; + if(n == 0){ + /* end of file */ + if(b->inode.length > off){ + DPRINT(2, "file %llud.%ld, length %lld\n", + b->inode.qid.path, + b->inode.qid.vers, off); + b->inode.length = off; + } + break; + } + memmove(cp, s.rhdr.data, n); + fwrite(&ic, b, cp, off, n); + if (statson){ + cfsstat.bytestocache += n; + cfsstat.bytesfromserver += n; + } + }else{ + DPRINT(2, "fetched %ld bytes from cache\n", n); + if(statson) + cfsstat.bytesfromcache += n; + } + cnt -= n; + off += n; + cp += n; + } + c.rhdr.data = data; + c.rhdr.count = off - first; + if(statson) + cfsstat.bytesread += c.rhdr.count; + sendreply(0); +} + +void +rwrite(Mfile *mf) +{ + Ibuf *b; + char buf[MAXFDATA]; + + if(statson && ctltest(mf)){ + sendreply("read only"); + return; + } + if(mf->qid.type & (QTDIR|QTAUTH)){ + delegate(); + if(statson && c.rhdr.count > 0) + cfsstat.byteswritten += c.rhdr.count; + return; + } + + memmove(buf, c.thdr.data, c.thdr.count); + if(delegate() < 0) + return; + + if(s.rhdr.count > 0) + cfsstat.byteswritten += s.rhdr.count; + /* don't modify our cache for append-only data; always read from server*/ + if(mf->qid.type & QTAPPEND) + return; + b = iget(&ic, mf->qid); + if(b == 0) + return; + if (b->inode.length < c.thdr.offset + s.rhdr.count) + b->inode.length = c.thdr.offset + s.rhdr.count; + mf->qid.vers++; + if (s.rhdr.count != c.thdr.count) + syslog(0, "cfslog", "rhdr.count %ud, thdr.count %ud\n", + s.rhdr.count, c.thdr.count); + if(fwrite(&ic, b, buf, c.thdr.offset, s.rhdr.count) == s.rhdr.count){ + iinc(&ic, b); + if(statson) + cfsstat.bytestocache += s.rhdr.count; + } +} + +void +rstat(Mfile *mf) +{ + Dir d; + + if(statson && ctltest(mf)){ + genstats(); + d.qid = ctlqid; + d.mode = 0444; + d.length = statlen; /* would be nice to do better */ + d.name = "cfsctl"; + d.uid = "none"; + d.gid = "none"; + d.muid = "none"; + d.atime = time(nil); + d.mtime = d.atime; + c.rhdr.nstat = convD2M(&d, c.rhdr.stat, + sizeof c.rhdr - (c.rhdr.stat - (uchar*)&c.rhdr)); + sendreply(0); + return; + } + if(delegate() == 0){ + Ibuf *b; + + convM2D(s.rhdr.stat, s.rhdr.nstat , &d, nil); + mf->qid = d.qid; + b = iget(&ic, mf->qid); + if(b) + b->inode.length = d.length; + } +} + +void +rwstat(Mfile *mf) +{ + Ibuf *b; + + if(statson && ctltest(mf)){ + sendreply("read only"); + return; + } + delegate(); + if(b = iget(&ic, mf->qid)) + b->inode.length = MAXLEN; +} + +void +error(char *fmt, ...) +{ + va_list arg; + static char buf[2048]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%s: %s\n", argv0, buf); + exits("error"); +} + +void +warning(char *s) +{ + fprint(2, "cfs: %s: %r\n", s); +} + +/* + * send a reply to the client + */ +void +sendreply(char *err) +{ + + if(err){ + c.rhdr.type = Rerror; + c.rhdr.ename = err; + }else{ + c.rhdr.type = c.thdr.type+1; + c.rhdr.fid = c.thdr.fid; + } + c.rhdr.tag = c.thdr.tag; + sendmsg(&c, &c.rhdr); +} + +/* + * send a request to the server, get the reply, and send that to + * the client + */ +int +delegate(void) +{ + int type; + + type = c.thdr.type; + if(statson){ + cfsstat.sm[type].n++; + cfsstat.sm[type].s = nsec(); + } + + sendmsg(&s, &c.thdr); + rcvmsg(&s, &s.rhdr); + + if(statson) + cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s; + + sendmsg(&c, &s.rhdr); + return c.thdr.type+1 == s.rhdr.type ? 0 : -1; +} + +/* + * send a request to the server and get a reply + */ +int +askserver(void) +{ + int type; + + s.thdr.tag = c.thdr.tag; + + type = s.thdr.type; + if(statson){ + cfsstat.sm[type].n++; + cfsstat.sm[type].s = nsec(); + } + + sendmsg(&s, &s.thdr); + rcvmsg(&s, &s.rhdr); + + if(statson) + cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s; + + return s.thdr.type+1 == s.rhdr.type ? 0 : -1; +} + +/* + * send/receive messages with logging + */ +void +sendmsg(P9fs *p, Fcall *f) +{ + DPRINT(2, "->%s: %F\n", p->name, f); + + p->len = convS2M(f, datasnd, messagesize); + if(p->len <= 0) + error("convS2M"); + if(write(p->fd[1], datasnd, p->len)!=p->len) + error("sendmsg"); +} + +void +dump(uchar *p, int len) +{ + fprint(2, "%d bytes", len); + while(len-- > 0) + fprint(2, " %.2ux", *p++); + fprint(2, "\n"); +} + +void +rcvmsg(P9fs *p, Fcall *f) +{ + int olen, rlen; + char buf[128]; + + olen = p->len; + p->len = read9pmsg(p->fd[0], datarcv, sizeof(datarcv)); + if(p->len <= 0){ + snprint(buf, sizeof buf, "read9pmsg(%d)->%ld: %r", + p->fd[0], p->len); + error(buf); + } + + if((rlen = convM2S(datarcv, p->len, f)) != p->len) + error("rcvmsg format error, expected length %d, got %d", + rlen, p->len); + if(f->fid >= Nfid){ + fprint(2, "<-%s: %d %s on %d\n", p->name, f->type, + mname[f->type]? mname[f->type]: "mystery", f->fid); + dump((uchar*)datasnd, olen); + dump((uchar*)datarcv, p->len); + error("rcvmsg fid out of range"); + } + DPRINT(2, "<-%s: %F\n", p->name, f); +} + +int +ctltest(Mfile *mf) +{ + return mf->busy && mf->qid.type == ctlqid.type && + mf->qid.path == ctlqid.path; +} + +void +genstats(void) +{ + int i; + char *p; + + p = statbuf; + + p += snprint(p, sizeof statbuf+statbuf-p, + " Client Server\n"); + p += snprint(p, sizeof statbuf+statbuf-p, + " #calls Δ ms/call Δ #calls Δ ms/call Δ\n"); + for (i = 0; i < nelem(cfsstat.cm); i++) + if(cfsstat.cm[i].n || cfsstat.sm[i].n) { + p += snprint(p, sizeof statbuf+statbuf-p, + "%7lud %7lud ", cfsstat.cm[i].n, + cfsstat.cm[i].n - cfsprev.cm[i].n); + if (cfsstat.cm[i].n) + p += snprint(p, sizeof statbuf+statbuf-p, + "%7.3f ", 0.000001*cfsstat.cm[i].t/ + cfsstat.cm[i].n); + else + p += snprint(p, sizeof statbuf+statbuf-p, + " "); + if(cfsstat.cm[i].n - cfsprev.cm[i].n) + p += snprint(p, sizeof statbuf+statbuf-p, + "%7.3f ", 0.000001* + (cfsstat.cm[i].t - cfsprev.cm[i].t)/ + (cfsstat.cm[i].n - cfsprev.cm[i].n)); + else + p += snprint(p, sizeof statbuf+statbuf-p, + " "); + p += snprint(p, sizeof statbuf+statbuf-p, + "%7lud %7lud ", cfsstat.sm[i].n, + cfsstat.sm[i].n - cfsprev.sm[i].n); + if (cfsstat.sm[i].n) + p += snprint(p, sizeof statbuf+statbuf-p, + "%7.3f ", 0.000001*cfsstat.sm[i].t/ + cfsstat.sm[i].n); + else + p += snprint(p, sizeof statbuf+statbuf-p, + " "); + if(cfsstat.sm[i].n - cfsprev.sm[i].n) + p += snprint(p, sizeof statbuf+statbuf-p, + "%7.3f ", 0.000001* + (cfsstat.sm[i].t - cfsprev.sm[i].t)/ + (cfsstat.sm[i].n - cfsprev.sm[i].n)); + else + p += snprint(p, sizeof statbuf+statbuf-p, + " "); + p += snprint(p, sizeof statbuf+statbuf-p, "%s\n", + mname[i]); + } + p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ndirread\n", + cfsstat.ndirread, cfsstat.ndirread - cfsprev.ndirread); + p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ndelegateread\n", + cfsstat.ndelegateread, cfsstat.ndelegateread - + cfsprev.ndelegateread); + p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ninsert\n", + cfsstat.ninsert, cfsstat.ninsert - cfsprev.ninsert); + p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ndelete\n", + cfsstat.ndelete, cfsstat.ndelete - cfsprev.ndelete); + p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud nupdate\n", + cfsstat.nupdate, cfsstat.nupdate - cfsprev.nupdate); + + p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesread\n", + cfsstat.bytesread, cfsstat.bytesread - cfsprev.bytesread); + p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud byteswritten\n", + cfsstat.byteswritten, cfsstat.byteswritten - + cfsprev.byteswritten); + p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesfromserver\n", + cfsstat.bytesfromserver, cfsstat.bytesfromserver - + cfsprev.bytesfromserver); + p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesfromdirs\n", + cfsstat.bytesfromdirs, cfsstat.bytesfromdirs - + cfsprev.bytesfromdirs); + p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesfromcache\n", + cfsstat.bytesfromcache, cfsstat.bytesfromcache - + cfsprev.bytesfromcache); + p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytestocache\n", + cfsstat.bytestocache, cfsstat.bytestocache - + cfsprev.bytestocache); + statlen = p - statbuf; + cfsprev = cfsstat; +} |