ref: 4308f6e7e6586e3a27aa46c4d54c434b8533b16d
dir: /sys/src/cmd/aux/disksim.c/
#include <u.h> #include <libc.h> #include <auth.h> #include <fcall.h> #include <thread.h> #include <9p.h> typedef struct Part Part; typedef struct Trip Trip; typedef struct Dbl Dbl; typedef struct Ind Ind; /* * with 8192-byte blocks and 4-byte pointers, * double-indirect gets us 34 GB. * triple-indirect gets us 70,368 GB. */ enum { LOGBLKSZ = 13, BLKSZ = 1<<LOGBLKSZ, /* 8192 */ NPTR = BLKSZ/sizeof(void*), }; static uchar zero[BLKSZ]; struct Trip { Dbl *dbl[NPTR]; }; struct Dbl { Ind *ind[NPTR]; }; struct Ind { uchar *blk[NPTR]; }; Trip trip; struct Part { int inuse; int vers; ulong mode; char *name; vlong offset; /* in sectors */ vlong length; /* in sectors */ }; enum { Qroot = 0, Qdir, Qctl, Qpart, }; Part tab[64]; int fd = -1; char *sdname = "sdXX"; ulong ctlmode = 0666; char *inquiry = "aux/disksim hard drive"; vlong nsect, sectsize, c, h, s; ulong time0; int rdonly; char* ctlstring(void) { int i; Fmt fmt; fmtstrinit(&fmt); fmtprint(&fmt, "inquiry %s\n", inquiry); fmtprint(&fmt, "geometry %lld %lld %lld %lld %lld\n", nsect, sectsize, c, h, s); for(i=0; i<nelem(tab); i++) if(tab[i].inuse) fmtprint(&fmt, "part %s %lld %lld\n", tab[i].name, tab[i].offset, tab[i].length); return fmtstrflush(&fmt); } int addpart(char *name, vlong start, vlong end) { int i; if(start < 0 || start > end || end > nsect){ werrstr("bad partition boundaries"); return -1; } for(i=0; i<nelem(tab); i++) if(tab[i].inuse == 0) break; if(i == nelem(tab)){ werrstr("no free partition slots"); return -1; } free(tab[i].name); tab[i].inuse = 1; tab[i].name = estrdup9p(name); tab[i].offset = start; tab[i].length = end - start; tab[i].mode = ctlmode; tab[i].vers++; return 0; } int delpart(char *s) { int i; for(i=0; i<nelem(tab); i++) if(tab[i].inuse && strcmp(tab[i].name, s) == 0) break; if(i==nelem(tab)){ werrstr("partition not found"); return -1; } tab[i].inuse = 0; free(tab[i].name); tab[i].name = 0; return 0; } void ctlwrite(Req *r) { int i; Cmdbuf *cb; vlong start, end; r->ofcall.count = r->ifcall.count; cb = parsecmd(r->ifcall.data, r->ifcall.count); if(cb->nf < 1){ respond(r, "empty control message"); free(cb); return; } if(strcmp(cb->f[0], "part") == 0){ if(cb->nf != 4){ respondcmderror(r, cb, "part takes 3 args"); free(cb); return; } start = strtoll(cb->f[2], 0, 0); end = strtoll(cb->f[3], 0, 0); if(addpart(cb->f[1], start, end) < 0){ respondcmderror(r, cb, "%r"); free(cb); return; } } else if(strcmp(cb->f[0], "delpart") == 0){ if(cb->nf != 2){ respondcmderror(r, cb, "delpart takes 1 arg"); free(cb); return; } if(delpart(cb->f[1]) < 0){ respondcmderror(r, cb, "%r"); free(cb); return; } } else if(strcmp(cb->f[0], "inquiry") == 0){ if(cb->nf != 2){ respondcmderror(r, cb, "inquiry takes 1 arg"); free(cb); return; } free(inquiry); inquiry = estrdup9p(cb->f[1]); } else if(strcmp(cb->f[0], "geometry") == 0){ if(cb->nf != 6){ respondcmderror(r, cb, "geometry takes 5 args"); free(cb); return; } nsect = strtoll(cb->f[1], 0, 0); sectsize = strtoll(cb->f[2], 0, 0); c = strtoll(cb->f[3], 0, 0); h = strtoll(cb->f[4], 0, 0); s = strtoll(cb->f[5], 0, 0); if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 && tab[0].vers == 0){ tab[0].offset = 0; tab[0].length = nsect; } for(i=0; i<nelem(tab); i++){ if(tab[i].inuse && tab[i].offset+tab[i].length > nsect){ tab[i].inuse = 0; free(tab[i].name); tab[i].name = 0; } } } else{ respondcmderror(r, cb, "unknown control message"); free(cb); return; } free(cb); respond(r, nil); } void* allocblk(vlong addr) { uchar *op; static uchar *p; static ulong n; if(n == 0){ p = malloc(4*1024*1024); if(p == 0) sysfatal("out of memory"); n = 4*1024*1024; } op = p; p += BLKSZ; n -= BLKSZ; memset(op, 0, BLKSZ); if(fd != -1 && addr != -1) pread(fd, op, BLKSZ, addr); return op; } uchar* getblock(vlong addr, int alloc) { Dbl *p2; Ind *p1; uchar *p0; uint i0, i1, i2; vlong oaddr; if(fd >= 0) alloc = 1; addr >>= LOGBLKSZ; oaddr = addr<<LOGBLKSZ; i0 = addr % NPTR; addr /= NPTR; i1 = addr % NPTR; addr /= NPTR; i2 = addr % NPTR; addr /= NPTR; assert(addr == 0); if((p2 = trip.dbl[i2]) == 0){ if(!alloc) return zero; trip.dbl[i2] = p2 = allocblk(-1); } if((p1 = p2->ind[i1]) == 0){ if(!alloc) return zero; p2->ind[i1] = p1 = allocblk(-1); } if((p0 = p1->blk[i0]) == 0){ if(!alloc) return zero; p1->blk[i0] = p0 = allocblk(oaddr); } return p0; } void dirty(vlong addr, uchar *buf) { vlong oaddr; if(fd == -1 || rdonly) return; oaddr = addr&~((vlong)BLKSZ-1); if(pwrite(fd, buf, BLKSZ, oaddr) != BLKSZ) sysfatal("write: %r"); } int rootgen(int off, Dir *d, void*) { memset(d, 0, sizeof *d); d->atime = time0; d->mtime = time0; if(off == 0){ d->name = estrdup9p(sdname); d->mode = DMDIR|0777; d->qid.path = Qdir; d->qid.type = QTDIR; d->uid = estrdup9p("disksim"); d->gid = estrdup9p("disksim"); d->muid = estrdup9p(""); return 0; } return -1; } int dirgen(int off, Dir *d, void*) { int n, j; memset(d, 0, sizeof *d); d->atime = time0; d->mtime = time0; if(off == 0){ d->name = estrdup9p("ctl"); d->mode = ctlmode; d->qid.path = Qctl; goto Have; } off--; n = 0; for(j=0; j<nelem(tab); j++){ if(tab[j].inuse==0) continue; if(n == off){ d->name = estrdup9p(tab[j].name); d->length = tab[j].length*sectsize; d->mode = tab[j].mode; d->qid.path = Qpart+j; d->qid.vers = tab[j].vers; goto Have; } n++; } return -1; Have: d->uid = estrdup9p("disksim"); d->gid = estrdup9p("disksim"); d->muid = estrdup9p(""); return 0; } void* evommem(void *a, void *b, ulong n) { return memmove(b, a, n); } int isnonzero(void *v, ulong n) { uchar *a, *ea; a = v; ea = a+n; for(; a<ea; a++) if(*a) return 1; return 0; } int rdwrpart(Req *r) { int q, nonzero; Part *p; vlong offset; long count, tot, n, o; uchar *blk, *dat; void *(*move)(void*, void*, ulong); q = r->fid->qid.path-Qpart; if(q < 0 || q > nelem(tab) || !tab[q].inuse || tab[q].vers != r->fid->qid.vers){ respond(r, "unknown partition"); return -1; } p = &tab[q]; offset = r->ifcall.offset; count = r->ifcall.count; if(offset < 0){ respond(r, "negative offset"); return -1; } if(count < 0){ respond(r, "negative count"); return -1; } if(offset > p->length*sectsize){ respond(r, "offset past end of partition"); return -1; } if(offset+count > p->length*sectsize) count = p->length*sectsize - offset; offset += p->offset*sectsize; if(r->ifcall.type == Tread) move = memmove; else move = evommem; tot = 0; nonzero = 1; if(r->ifcall.type == Tread) dat = (uchar*)r->ofcall.data; else{ dat = (uchar*)r->ifcall.data; nonzero = isnonzero(dat, r->ifcall.count); } o = offset & (BLKSZ-1); /* left fringe block */ if(o && count){ blk = getblock(offset, r->ifcall.type==Twrite && nonzero); n = BLKSZ - o; if(n > count) n = count; if(r->ifcall.type != Twrite || blk != zero) (*move)(dat, blk+o, n); if(r->ifcall.type == Twrite) dirty(offset, blk); tot += n; } /* full and right fringe blocks */ while(tot < count){ blk = getblock(offset+tot, r->ifcall.type==Twrite && nonzero); n = BLKSZ; if(n > count-tot) n = count-tot; if(r->ifcall.type != Twrite || blk != zero) (*move)(dat+tot, blk, n); if(r->ifcall.type == Twrite) dirty(offset+tot, blk); tot += n; } r->ofcall.count = tot; respond(r, nil); return 0; } void fsread(Req *r) { char *s; switch((int)r->fid->qid.path){ case Qroot: dirread9p(r, rootgen, nil); respond(r, nil); break; case Qdir: dirread9p(r, dirgen, nil); respond(r, nil); break; case Qctl: s = ctlstring(); readstr(r, s); free(s); respond(r, nil); break; default: rdwrpart(r); break; } } void fswrite(Req *r) { switch((int)r->fid->qid.path){ case Qroot: case Qdir: respond(r, "write to a directory?"); break; case Qctl: ctlwrite(r); break; default: rdwrpart(r); break; } } void fsopen(Req *r) { if(r->ifcall.mode&ORCLOSE) respond(r, "cannot open ORCLOSE"); switch((int)r->fid->qid.path){ case Qroot: case Qdir: if(r->ifcall.mode != OREAD){ respond(r, "bad mode for directory open"); return; } } respond(r, nil); } void fsstat(Req *r) { int q; Dir *d; Part *p; d = &r->d; memset(d, 0, sizeof *d); d->qid = r->fid->qid; d->atime = d->mtime = time0; q = r->fid->qid.path; switch(q){ case Qroot: d->name = estrdup9p("/"); d->mode = DMDIR|0777; break; case Qdir: d->name = estrdup9p(sdname); d->mode = DMDIR|0777; break; default: q -= Qpart; if(q < 0 || q > nelem(tab) || tab[q].inuse==0 || r->fid->qid.vers != tab[q].vers){ respond(r, "partition no longer exists"); return; } p = &tab[q]; d->name = estrdup9p(p->name); d->length = p->length * sectsize; d->mode = p->mode; break; } d->uid = estrdup9p("disksim"); d->gid = estrdup9p("disksim"); d->muid = estrdup9p(""); respond(r, nil); } void fsattach(Req *r) { char *spec; spec = r->ifcall.aname; if(spec && spec[0]){ respond(r, "invalid attach specifier"); return; } r->ofcall.qid = (Qid){Qroot, 0, QTDIR}; r->fid->qid = r->ofcall.qid; respond(r, nil); } char* fswalk1(Fid *fid, char *name, Qid *qid) { int i; switch((int)fid->qid.path){ case Qroot: if(strcmp(name, sdname) == 0){ fid->qid.path = Qdir; fid->qid.type = QTDIR; *qid = fid->qid; return nil; } break; case Qdir: if(strcmp(name, "ctl") == 0){ fid->qid.path = Qctl; fid->qid.vers = 0; fid->qid.type = 0; *qid = fid->qid; return nil; } for(i=0; i<nelem(tab); i++){ if(tab[i].inuse && strcmp(tab[i].name, name) == 0){ fid->qid.path = i+Qpart; fid->qid.vers = tab[i].vers; fid->qid.type = 0; *qid = fid->qid; return nil; } } break; } return "file not found"; } Srv fs = { .attach= fsattach, .open= fsopen, .read= fsread, .write= fswrite, .stat= fsstat, .walk1= fswalk1, }; char *mtpt = "/dev"; char *srvname; void usage(void) { fprint(2, "usage: aux/disksim [-D] [-f file] [-s srvname] [-m mtpt] [sdXX]\n"); fprint(2, "\tdefault mtpt is /dev\n"); exits("usage"); } void main(int argc, char **argv) { char *file; file = nil; quotefmtinstall(); time0 = time(0); ARGBEGIN{ case 'D': chatty9p++; break; case 'f': file = EARGF(usage()); break; case 'r': rdonly = 1; break; case 's': srvname = EARGF(usage()); break; case 'm': mtpt = EARGF(usage()); break; default: usage(); }ARGEND if(argc > 1) usage(); if(argc == 1) sdname = argv[0]; if(file){ if((fd = open(file, rdonly ? OREAD : ORDWR)) < 0) sysfatal("open %s: %r", file); } inquiry = estrdup9p(inquiry); tab[0].name = estrdup9p("data"); tab[0].inuse = 1; tab[0].mode = 0666; postmountsrv(&fs, srvname, mtpt, MBEFORE); exits(nil); }