ref: a5db044fd3ccb8f021d4deb07b3dc732ed41d699
dir: /sys/src/9/port/devaoe.c/
/* * © 2005-13 coraid * aoe storage initiator */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "../port/error.h" #include "../port/netif.h" #include "../port/etherif.h" #include "../ip/ip.h" #include "../port/aoe.h" #include <fis.h> #pragma varargck argpos eventlog 1 #define dprint(...) if(debug) eventlog(__VA_ARGS__); else USED(debug); #define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); enum { Typebits = 4, Unitbits = 12, L3bits = 4, Maxtype = (1<<Typebits)-1, Maxunits = (1<<Unitbits)-1, Maxl3 = (1<<L3bits)-1, Maxframes = 128, Maxmtu = 100000, Ndevlink = 6, Nea = 6, Nnetlink = 6, }; #define TYPE(q) ((ulong)(q).path & Maxtype) #define UNIT(q) (((ulong)(q).path>>Typebits) & Maxunits) #define L(q) (((ulong)(q).path>>Typebits+Unitbits) & Maxl3) #define QID(u, t) ((u)<<Typebits | (t)) #define Q3(l, u, t) ((l)<<Typebits+Unitbits | QID(u, t)) #define UP(d) ((d)->flag & Dup) #define Ticks MACHP(0)->ticks #define Ms2tk(t) (((t)*HZ)/1000) #define Tk2ms(t) (((t)*1000)/HZ) enum { Qzero, Qtopdir = 1, Qtopbase, Qtopctl = Qtopbase, Qtoplog, Qtopend, Qunitdir, Qunitbase, Qctl = Qunitbase, Qdata, Qconfig, Qident, Qdevlinkdir, Qdevlinkbase, Qdevlink = Qdevlinkbase, Qdevlinkend, Qtopfiles = Qtopend-Qtopbase, Qdevlinkfiles = Qdevlinkend-Qdevlinkbase, Eventlen = 256, Nevents = 64, Fread = 0, Fwrite, Tfree = -1, Tmgmt, /* round trip bounds, timeouts, in ticks */ Rtmax = Ms2tk(320), Rtmin = Ms2tk(20), Srbtimeout = 45*HZ, Dbcnt = 1024, Crd = 0x20, Crdext = 0x24, Cwr = 0x30, Cwrext = 0x34, Cid = 0xec, Alloc = 0x01234567, Free = 0x89abcdef, }; enum { Read, Write, }; /* * unified set of flags * a Netlink + Aoedev most both be jumbo capable * to send jumbograms to that interface. */ enum { Dup = 1<<0, Djumbo = 1<<1, Dnofail = 1<<2, }; static char *flagname[] = { "up", "jumbo", "nofail", }; typedef struct { uchar flag; uint lostjumbo; Chan *cc; Chan *dc; Chan *mtu; /* open early to prevent bind issues. */ char path[Maxpath]; uchar ea[Eaddrlen]; } Netlink; typedef struct { Netlink *nl; int nea; ulong eaidx; uchar eatab[Nea][Eaddrlen]; int datamtu; ulong npkt; ulong resent; uchar flag; ulong rttavg; ulong mintimer; } Devlink; typedef struct Srb Srb; struct Srb { Rendez; uint state; Srb *next; ulong ticksent; ulong len; vlong sector; short write; short nout; char *error; void *dp; void *data; }; typedef struct { int tag; ulong bcnt; ulong dlen; vlong lba; ulong ticksent; int nhdr; uchar hdr[ETHERMINTU]; void *dp; Devlink *dl; Netlink *nl; int eaidx; Srb *srb; } Frame; typedef struct Aoedev Aoedev; struct Aoedev { QLock; Aoedev *next; ulong vers; int ndl; ulong dlidx; Devlink *dl; Devlink dltab[Ndevlink]; uchar flag; ushort fwver; int nopen; uint major; uint minor; int unit; int lasttag; int nframes; Frame *frames; vlong bsize; vlong realbsize; uint maxbcnt; uint maxmtu; ulong lostjumbo; ushort nout; ushort maxout; ulong lastwadj; Srb *head; Srb *tail; Srb *inprocess; Sfis; char serial[20+1]; char firmware[8+1]; char model[40+1]; int nconfig; uchar config[1024]; uchar ident[512]; }; #pragma varargck type "æ" Aoedev* static struct { Lock; QLock; Rendez; char buf[Eventlen*Nevents]; char *rp; char *wp; } events; static struct { RWlock; int nd; Aoedev *d; } devs; static struct { Lock; int reader[Nnetlink]; /* reader is running. */ Rendez rendez[Nnetlink]; /* confirm exit. */ Netlink nl[Nnetlink]; } netlinks; extern Dev aoedevtab; static Ref units; static Ref drivevers; static int debug; static int autodiscover = 1; static int rediscover; extern char Enotup[] = "aoe device is down"; static Srb* srballoc(ulong sz) { Srb *srb; srb = smalloc(sizeof *srb+sz); srb->state = Alloc; srb->dp = srb->data = srb+1; srb->ticksent = Ticks; return srb; } static Srb* srbkalloc(void *db, ulong) { Srb *srb; srb = smalloc(sizeof *srb); srb->state = Alloc; srb->dp = srb->data = db; srb->ticksent = Ticks; return srb; } static int srbready(void *v) { Srb *s; s = v; return s->nout == 0 && (s->len == 0 || s->error != nil); } static void srbfree(Srb *srb) { int n; for(n = 0; srb->state != Free; n++) sched(); free(srb); } /* under Aoedev qlock() so setting of srb->state is safe */ static void srbwakeup(Srb *srb) { if(srbready(srb)){ assert(srb->state == Alloc); wakeup(srb); srb->state = Free; } } static void srbcleanout(Aoedev *d, Srb *srb) { Srb *x, **ll; if(srb == d->inprocess) d->inprocess = nil; else for(ll = &d->head; (x = *ll) != nil; ll = &x->next){ d->tail = x; if(x == srb) *ll = x->next; } } static void srberror(Aoedev *d, Srb *srb, char *s) { srbcleanout(d, srb); srb->error = s; srbwakeup(srb); } static void frameerror(Aoedev *d, Frame *f, char *s) { Srb *srb; if(f->tag == Tfree) return; srb = f->srb; f->srb = nil; f->tag = Tfree; /* don't get fooled by way-slow responses */ if(srb == nil) return; srb->nout--; srberror(d, srb, s); d->nout--; } static char* unitname(Aoedev *d) { uprint("%ud.%ud", d->major, d->minor); return up->genbuf; } static long eventlogread(void *a, long n) { int len; char *p, *buf; buf = smalloc(Eventlen); qlock(&events); lock(&events); p = events.rp; len = *p; if(len == 0){ n = 0; unlock(&events); } else { if(n > len) n = len; /* can't move directly into pageable space with events lock held */ memmove(buf, p+1, n); *p = 0; events.rp = p += Eventlen; if(p >= events.buf + sizeof events.buf) events.rp = events.buf; unlock(&events); /* the concern here is page faults in memmove below */ if(waserror()){ qunlock(&events); free(buf); nexterror(); } memmove(a, buf, n); poperror(); } qunlock(&events); free(buf); return n; } static int eventlog(char *fmt, ...) { int dragrp, n; char *p; va_list arg; lock(&events); p = events.wp; dragrp = *p++; va_start(arg, fmt); n = vsnprint(p, Eventlen-1, fmt, arg); *--p = n; p = events.wp += Eventlen; if(p >= events.buf + sizeof events.buf) p = events.wp = events.buf; if(dragrp) events.rp = p; unlock(&events); wakeup(&events); return n; } static int eventcount(void) { uint n; lock(&events); if(*events.rp == 0) n = 0; else n = events.wp - events.rp & Nevents - 1; unlock(&events); return n/Eventlen; } static int tsince(int tag) { int n; n = Ticks & 0xffff; n -= tag & 0xffff; if(n < 0) n += 1<<16; return n; } static int newtag(Aoedev *d) { int t; do { t = ++d->lasttag << 16; t |= Ticks & 0xffff; } while (t == Tfree || t == Tmgmt); return t; } static void downdev(Aoedev *d, char *err) { Frame *f, *e; d->flag &= ~Dup; f = d->frames; e = f + d->nframes; for(; f < e; f++) frameerror(d, f, Enotup); d->inprocess = nil; eventlog("%æ: removed; %s\n", d, err); } static Block* allocfb(Frame *f) { int len; Block *b; len = f->nhdr + f->dlen; if(len < ETHERMINTU) len = ETHERMINTU; b = allocb(len); memmove(b->wp, f->hdr, f->nhdr); if(f->dlen) memmove(b->wp + f->nhdr, f->dp, f->dlen); b->wp += len; return b; } static void putlba(Aoeata *a, vlong lba) { uchar *c; c = a->lba; c[0] = lba; c[1] = lba >> 8; c[2] = lba >> 16; c[3] = lba >> 24; c[4] = lba >> 32; c[5] = lba >> 40; } static Devlink* pickdevlink(Aoedev *d) { ulong i, n; Devlink *l; for(i = 0; i < d->ndl; i++){ n = d->dlidx++ % d->ndl; l = d->dl + n; if(l->flag & Dup) return l; } return nil; } static int pickea(Devlink *l) { if(l == nil) return -1; if(l->nea == 0) return -1; return l->eaidx++ % l->nea; } /* * would like this to depend on the chan (srb). * not possible in the current structure. */ #define Nofail(d, s) (((d)->flag&Dnofail) == Dnofail) static int hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd, int new) { int i; Devlink *l; if(f->srb != nil) if((long)(Ticks-f->srb->ticksent) > Srbtimeout){ eventlog("%æ: srb timeout\n", d); if(cmd == ACata && Nofail(d, s)) f->srb->ticksent = Ticks; else frameerror(d, f, Etimedout); return -1; } l = pickdevlink(d); i = pickea(l); if(i == -1){ if(!(cmd == ACata && f->srb != nil && Nofail(d, s))) downdev(d, "resend fails; no netlink/ea"); return -1; } memmove(h->dst, l->eatab[i], Eaddrlen); memmove(h->src, l->nl->ea, sizeof h->src); hnputs(h->type, Aoetype); h->verflag = Aoever << 4; h->error = 0; hnputs(h->major, d->major); h->minor = d->minor; h->cmd = cmd; if(new) f->tag = newtag(d); hnputl(h->tag, f->tag); f->dl = l; f->nl = l->nl; f->eaidx = i; f->ticksent = Ticks; return f->tag; } static int resend(Aoedev *d, Frame *f) { ulong n; Aoeata *a; Aoehdr *h; h = (Aoehdr*)f->hdr; if(hset(d, f, h, h->cmd, 0) == -1) return -1; a = (Aoeata*)(f->hdr + Aoehsz); n = f->bcnt; if(n > d->maxbcnt){ n = d->maxbcnt; /* mtu mismatch (jumbo fail?) */ if(f->dlen > n) f->dlen = n; } a->scnt = n / Aoesectsz; f->dl->resent++; f->dl->npkt++; if(waserror()) /* should remove the netlink */ return -1; devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); poperror(); return 0; } static void discover(uint major, uint minor) { Aoehdr *h; Block *b; Netlink *nl, *e; nl = netlinks.nl; e = nl + nelem(netlinks.nl); for(; nl < e; nl++){ if(nl->cc == nil) continue; b = allocb(ETHERMINTU); if(waserror()){ freeb(b); nexterror(); } b->wp = b->rp + ETHERMINTU; memset(b->rp, 0, ETHERMINTU); h = (Aoehdr*)b->rp; memset(h->dst, 0xff, sizeof h->dst); memmove(h->src, nl->ea, sizeof h->src); hnputs(h->type, Aoetype); h->verflag = Aoever << 4; hnputs(h->major, major); h->minor = minor; h->cmd = ACconfig; poperror(); devtab[nl->dc->type]->bwrite(nl->dc, b, 0); } } /* * Check all frames on device and resend any frames that have been * outstanding for 200% of the device round trip time average. */ static void aoesweepproc(void*) { ulong i, tx, timeout, nbc; vlong starttick; enum { Nms = 100, Nbcms = 30*1000, }; uchar *ea; Aoeata *a; Aoedev *d; Devlink *l; Frame *f, *e; nbc = Nbcms/Nms; loop: if(nbc-- == 0){ if(rediscover && !waserror()){ discover(0xffff, 0xff); poperror(); } nbc = Nbcms/Nms; } starttick = Ticks; rlock(&devs); for(d = devs.d; d != nil; d = d->next){ if(!canqlock(d)) continue; if(!UP(d)){ qunlock(d); continue; } tx = 0; f = d->frames; e = f + d->nframes; for (; f < e; f++){ if(f->tag == Tfree) continue; l = f->dl; timeout = l->rttavg << 1; i = tsince(f->tag); if(i < timeout) continue; if(d->nout == d->maxout){ if(d->maxout > 1) d->maxout--; d->lastwadj = Ticks; } a = (Aoeata*)(f->hdr + Aoehsz); if(a->scnt > Dbcnt / Aoesectsz && ++f->nl->lostjumbo > (d->nframes << 1)){ ea = f->dl->eatab[f->eaidx]; eventlog("%æ: jumbo failure on %s:%E; %llud\n", d, f->nl->path, ea, f->lba); d->maxbcnt = Dbcnt; d->flag &= ~Djumbo; } resend(d, f); if(tx++ == 0){ if((l->rttavg <<= 1) > Rtmax) l->rttavg = Rtmax; eventlog("%æ: rtt %ldms\n", d, Tk2ms(l->rttavg)); } } if(d->nout == d->maxout && d->maxout < d->nframes && TK2MS(Ticks-d->lastwadj) > 10*1000){ d->maxout++; d->lastwadj = Ticks; } qunlock(d); } runlock(&devs); i = Nms - TK2MS(Ticks - starttick); if(i > 0 && !waserror()){ tsleep(&up->sleep, return0, 0, i); poperror(); } goto loop; } static int fmtæ(Fmt *f) { char buf[16]; Aoedev *d; d = va_arg(f->args, Aoedev*); snprint(buf, sizeof buf, "aoe%ud.%ud", d->major, d->minor); return fmtstrcpy(f, buf); } static void netbind(char *path); static void aoecfg(void) { char *p, *f[32], buf[24], ifbuf[64]; int n, i; if((p = getconf("aoeif")) == nil) return; strncpy(ifbuf, p, sizeof(ifbuf)-1); ifbuf[sizeof(ifbuf)-1] = 0; if((n = tokenize(ifbuf, f, nelem(f))) < 1) return; /* goo! */ for(i = 0; i < n; i++){ p = f[i]; if(strncmp(p, "ether", 5) == 0) snprint(buf, sizeof buf, "#l%c/ether%c", p[5], p[5]); else if(strncmp(p, "#l", 2) == 0) snprint(buf, sizeof buf, "#l%c/ether%c", p[2], p[2]); else continue; if(!waserror()){ netbind(buf); poperror(); } } } static void aoeinit(void) { static int init; static QLock l; if(!canqlock(&l)) return; if(init == 0){ fmtinstall(L'æ', fmtæ); events.rp = events.wp = events.buf; kproc("aoesweep", aoesweepproc, nil); aoecfg(); init = 1; } qunlock(&l); } static Chan* aoeattach(char *spec) { Chan *c; if(*spec) error(Enonexist); aoeinit(); c = devattach(L'æ', spec); mkqid(&c->qid, Qzero, 0, QTDIR); return c; } static int unitseq(Chan *c, uint unit, Dir *dp) { int i, rv; Qid q; Aoedev *d; i = 0; rv = -1; rlock(&devs); for(d = devs.d; d != nil; d = d->next) if(i++ == unit){ mkqid(&q, QID(d->unit, Qunitdir), 0, QTDIR); devdir(c, q, unitname(d), 0, eve, 0555, dp); rv = 1; break; } runlock(&devs); return rv; } static Aoedev* unit2dev(ulong unit) { Aoedev *d; rlock(&devs); for(d = devs.d; d != nil; d = d->next) if(d->unit == unit){ runlock(&devs); return d; } runlock(&devs); error("unit lookup failure"); return nil; } static int unitgen(Chan *c, ulong type, Dir *dp) { int perm, t; ulong vers; vlong size; char *p; Aoedev *d; Qid q; d = unit2dev(UNIT(c->qid)); perm = 0664; size = 0; vers = d->vers; t = QTFILE; switch(type){ default: return -1; case Qctl: p = "ctl"; break; case Qdata: p = "data"; perm = 0660; if(UP(d)) size = d->bsize; break; case Qconfig: p = "config"; if(UP(d)) size = d->nconfig; break; case Qident: p = "ident"; if(UP(d)) size = sizeof d->ident; break; case Qdevlinkdir: p = "devlink"; t = QTDIR; perm = 0555; break; } mkqid(&q, QID(UNIT(c->qid), type), vers, t); devdir(c, q, p, size, eve, perm, dp); return 1; } static int topgen(Chan *c, ulong type, Dir *d) { int perm; vlong size; char *p; Qid q; perm = 0444; size = 0; switch(type){ default: return -1; case Qtopctl: p = "ctl"; perm = 0664; break; case Qtoplog: p = "log"; size = eventcount(); break; } mkqid(&q, type, 0, QTFILE); devdir(c, q, p, size, eve, perm, d); return 1; } static int aoegen(Chan *c, char *, Dirtab *, int, int s, Dir *dp) { int i; Aoedev *d; Qid q; if(c->qid.path == 0){ switch(s){ case DEVDOTDOT: q.path = 0; q.type = QTDIR; devdir(c, q, "#æ", 0, eve, 0555, dp); break; case 0: q.path = Qtopdir; q.type = QTDIR; devdir(c, q, "aoe", 0, eve, 0555, dp); break; default: return -1; } return 1; } switch(TYPE(c->qid)){ default: return -1; case Qtopdir: if(s == DEVDOTDOT){ mkqid(&q, Qzero, 0, QTDIR); devdir(c, q, "aoe", 0, eve, 0555, dp); return 1; } if(s < Qtopfiles) return topgen(c, Qtopbase + s, dp); s -= Qtopfiles; return unitseq(c, s, dp); case Qtopctl: case Qtoplog: return topgen(c, TYPE(c->qid), dp); case Qunitdir: if(s == DEVDOTDOT){ mkqid(&q, QID(0, Qtopdir), 0, QTDIR); uprint("%uld", UNIT(c->qid)); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } return unitgen(c, Qunitbase+s, dp); case Qctl: case Qdata: case Qconfig: case Qident: return unitgen(c, TYPE(c->qid), dp); case Qdevlinkdir: i = UNIT(c->qid); if(i >= Maxunits || i >= units.ref) return -1; d = unit2dev(i); if(s == DEVDOTDOT){ mkqid(&q, QID(i, Qunitdir), 0, QTDIR); devdir(c, q, unitname(d), 0, eve, 0555, dp); return 1; } if(s >= d->ndl) return -1; uprint("%d", s); mkqid(&q, Q3(s, i, Qdevlink), 0, QTFILE); devdir(c, q, up->genbuf, 0, eve, 0444, dp); return 1; case Qdevlink: uprint("%d", s); mkqid(&q, Q3(s, UNIT(c->qid), Qdevlink), 0, QTFILE); devdir(c, q, up->genbuf, 0, eve, 0444, dp); return 1; } } static Walkqid* aoewalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, nil, 0, aoegen); } static int aoestat(Chan *c, uchar *db, int n) { return devstat(c, db, n, nil, 0, aoegen); } static Chan* aoeopen(Chan *c, int omode) { Aoedev *d; if(TYPE(c->qid) != Qdata) return devopen(c, omode, 0, 0, aoegen); d = unit2dev(UNIT(c->qid)); qlock(d); if(waserror()){ qunlock(d); nexterror(); } if(!UP(d)) error(Enotup); c = devopen(c, omode, 0, 0, aoegen); d->nopen++; poperror(); qunlock(d); return c; } static void aoeclose(Chan *c) { Aoedev *d; if(TYPE(c->qid) != Qdata || (c->flag&COPEN) == 0) return; d = unit2dev(UNIT(c->qid)); qlock(d); if(--d->nopen == 0 && !waserror()){ discover(d->major, d->minor); poperror(); } qunlock(d); } static void atarw(Aoedev *d, Frame *f) { ulong bcnt; char extbit, writebit; Aoeata *ah; Aoehdr *h; Srb *srb; extbit = 0x4; writebit = 0x10; srb = d->inprocess; bcnt = d->maxbcnt; if(bcnt > srb->len) bcnt = srb->len; f->nhdr = Aoehsz + Aoeatasz; memset(f->hdr, 0, f->nhdr); h = (Aoehdr*)f->hdr; if(hset(d, f, h, ACata, 1) == -1){ d->inprocess = nil; return; } ah = (Aoeata*)(f->hdr + Aoehsz); f->dp = srb->dp; f->bcnt = bcnt; f->lba = srb->sector; f->srb = srb; ah->scnt = bcnt / Aoesectsz; putlba(ah, f->lba); if(d->feat & Dllba) ah->aflag |= AAFext; else { extbit = 0; ah->lba[3] &= 0x0f; ah->lba[3] |= 0xe0; /* LBA bit+obsolete 0xa0 */ } if(srb->write){ ah->aflag |= AAFwrite; f->dlen = bcnt; }else{ writebit = 0; f->dlen = 0; } ah->cmdstat = 0x20 | writebit | extbit; /* mark tracking fields and load out */ srb->nout++; srb->dp = (uchar*)srb->dp + bcnt; srb->len -= bcnt; srb->sector += bcnt / Aoesectsz; if(srb->len == 0) d->inprocess = nil; d->nout++; f->dl->npkt++; if(waserror()) frameerror(d, f, "write error"); else{ devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); poperror(); } } static char* aoeerror(Aoehdr *h) { int n; static char *errs[] = { "aoe protocol error: unknown", "aoe protocol error: bad command code", "aoe protocol error: bad argument param", "aoe protocol error: device unavailable", "aoe protocol error: config string present", "aoe protocol error: unsupported version", "aoe protocol error: target is reserved", }; if((h->verflag & AFerr) == 0) return nil; n = h->error; if(n >= nelem(errs)) n = 0; return errs[n]; } static void rtupdate(Devlink *l, int rtt) { int n; n = rtt; if(rtt < 0){ n = -rtt; if(n < Rtmin) n = Rtmin; else if(n > Rtmax) n = Rtmax; l->mintimer += (n - l->mintimer) >> 1; } else if(n < l->mintimer) n = l->mintimer; else if(n > Rtmax) n = Rtmax; /* g == .25; cf. Congestion Avoidance and Control, Jacobson&Karels; 1988 */ n -= l->rttavg; l->rttavg += n >> 2; } static Frame* getframe(Aoedev *d, int tag) { Frame *f, *e; f = d->frames; e = f + d->nframes; for(; f < e; f++) if(f->tag == tag) return f; return nil; } static Frame* freeframe(Aoedev *d) { if(d->nout < d->maxout) return getframe(d, Tfree); return nil; } static void work(Aoedev *d) { Frame *f; while(f = freeframe(d)) { if(d->inprocess == nil){ if(d->head == nil) return; d->inprocess = d->head; d->head = d->head->next; if(d->head == nil) d->tail = nil; } atarw(d, f); } } static void strategy(Aoedev *d, Srb *srb) { qlock(d); if(waserror()){ qunlock(d); nexterror(); } if(!UP(d)) error(Eio); srb->next = nil; if(d->tail != nil) d->tail->next = srb; d->tail = srb; if(d->head == nil) d->head = srb; work(d); poperror(); qunlock(d); while(waserror()){ qlock(d); srberror(d, srb, "interrupted"); qunlock(d); } sleep(srb, srbready, srb); poperror(); } #define iskaddr(a) ((uintptr)(a) > KZERO) static long rw(Aoedev *d, int write, uchar *db, long len, uvlong off) { long n, nlen, copy; enum { Srbsz = 1<<19, }; Srb *srb; if((off|len) & (Aoesectsz-1)) error("offset and length must be sector multiple.\n"); if(!UP(d)) error(Eio); if(off >= d->bsize) return 0; if(off + len > d->bsize) len = d->bsize - off; copy = 0; if(iskaddr(db)){ srb = srbkalloc(db, len); copy = 1; }else srb = srballoc(Srbsz <= len? Srbsz: len); if(waserror()){ srbfree(srb); nexterror(); } srb->write = write; for(nlen = len; nlen; nlen -= n){ srb->sector = off / Aoesectsz; srb->dp = srb->data; n = nlen; if(n > Srbsz) n = Srbsz; srb->len = n; if(write && !copy) memmove(srb->data, db, n); strategy(d, srb); if(srb->error != nil) error(srb->error); if(!write && !copy) memmove(db, srb->data, n); db += n; off += n; } poperror(); srbfree(srb); return len; } static long readmem(ulong off, void *dst, long n, void *src, long size) { if(off >= size) return 0; if(off + n > size) n = size - off; memmove(dst, (uchar*)src + off, n); return n; } static char* aoeflag(char *s, char *e, uchar f) { uchar i; for(i = 0; i < nelem(flagname); i++) if(f & 1 << i) s = seprint(s, e, "%s ", flagname[i]); return seprint(s, e, "\n"); } static int pstat(Aoedev *d, char *db, int len, int off) { int i; char *state, *s, *p, *e; s = p = smalloc(READSTR); e = p + READSTR; state = "down"; if(UP(d)) state = "up"; p = seprint(p, e, "state: %s\n" "nopen: %d\n" "nout: %d\n" "nmaxout: %d\n" "nframes: %d\n" "maxbcnt: %d [maxmtu %d]\n" "fw: %.4ux\n" "model: %s\n" "serial: %s\n" "firmware: %s\n", state, d->nopen, d->nout, d->maxout, d->nframes, d->maxbcnt, d->maxmtu, d->fwver, d->model, d->serial, d->firmware); p = seprint(p, e, "flag: "); p = pflag(p, e, d); p[-1] = ' '; /* horrid */ p = aoeflag(p, e, d->flag); if(p - s < len) len = p - s; i = readstr(off, db, len, s); free(s); return i; } static long unitread(Chan *c, void *db, long len, vlong off) { Aoedev *d; d = unit2dev(UNIT(c->qid)); if(d->vers != c->qid.vers) error(Echange); switch(TYPE(c->qid)){ default: error(Ebadarg); case Qctl: return pstat(d, db, len, off); case Qdata: return rw(d, Read, db, len, off); case Qconfig: if(!UP(d)) error(Enotup); return readmem(off, db, len, d->config, d->nconfig); case Qident: if(!UP(d)) error(Enotup); return readmem(off, db, len, d->ident, sizeof d->ident); } } static int getmtu(Chan *m) { int n, mtu; char buf[36]; mtu = 1514; if(m == nil || waserror()) return mtu; n = devtab[m->type]->read(m, buf, sizeof buf - 1, 0); poperror(); if(n > 12){ buf[n] = 0; mtu = strtoul(buf + 12, 0, 0); } return mtu; } static int devlinkread(Chan *c, void *db, int len, int off) { int i; char *s, *p, *e; Aoedev *d; Devlink *l; d = unit2dev(UNIT(c->qid)); i = L(c->qid); if(i >= d->ndl) return 0; l = d->dl + i; s = p = smalloc(READSTR); e = s + READSTR; p = seprint(p, e, "addr: "); for(i = 0; i < l->nea; i++) p = seprint(p, e, "%E ", l->eatab[i]); p = seprint(p, e, "\n"); p = seprint(p, e, "npkt: %uld\n", l->npkt); p = seprint(p, e, "resent: %uld\n", l->resent); p = seprint(p, e, "flag: "); p = aoeflag(p, e, l->flag); p = seprint(p, e, "rttavg: %uld\n", Tk2ms(l->rttavg)); p = seprint(p, e, "mintimer: %uld\n", Tk2ms(l->mintimer)); p = seprint(p, e, "datamtu: %d\n", l->datamtu); p = seprint(p, e, "nl path: %s\n", l->nl->path); p = seprint(p, e, "nl ea: %E\n", l->nl->ea); p = seprint(p, e, "nl flag: "); p = aoeflag(p, e, l->flag); p = seprint(p, e, "nl lostjumbo: %d\n", l->nl->lostjumbo); p = seprint(p, e, "nl datamtu: %d\n", getmtu(l->nl->mtu)); if(p - s < len) len = p - s; i = readstr(off, db, len, s); free(s); return i; } static long topctlread(Chan *, void *db, int len, int off) { int i; char *s, *p, *e; Netlink *n; s = p = smalloc(READSTR); e = s + READSTR; p = seprint(p, e, "debug: %d\n", debug); p = seprint(p, e, "autodiscover: %d\n", autodiscover); p = seprint(p, e, "rediscover: %d\n", rediscover); for(i = 0; i < Nnetlink; i++){ n = netlinks.nl+i; if(n->cc == nil) continue; p = seprint(p, e, "if%d path: %s\n", i, n->path); p = seprint(p, e, "if%d ea: %E\n", i, n->ea); p = seprint(p, e, "if%d flag: ", i); p = aoeflag(p, e, n->flag); p = seprint(p, e, "if%d lostjumbo: %d\n", i, n->lostjumbo); p = seprint(p, e, "if%d datamtu: %d\n", i, getmtu(n->mtu)); } if(p - s < len) len = p - s; i = readstr(off, db, len, s); free(s); return i; } static long aoeread(Chan *c, void *db, long n, vlong off) { switch(TYPE(c->qid)){ default: error(Eperm); case Qzero: case Qtopdir: case Qunitdir: case Qdevlinkdir: return devdirread(c, db, n, 0, 0, aoegen); case Qtopctl: return topctlread(c, db, n, off); case Qtoplog: return eventlogread(db, n); case Qctl: case Qdata: case Qconfig: case Qident: return unitread(c, db, n, off); case Qdevlink: return devlinkread(c, db, n, off); } } static long configwrite(Aoedev *d, void *db, long len) { char *s; Aoehdr *h; Aoecfg *ch; Frame *f; Srb *srb; if(!UP(d)) error(Enotup); if(len > sizeof d->config) error(Etoobig); srb = srballoc(len); s = smalloc(len); memmove(s, db, len); if(waserror()){ srbfree(srb); free(s); nexterror(); } for (;;) { qlock(d); if(waserror()){ qunlock(d); nexterror(); } f = freeframe(d); if(f != nil) break; poperror(); qunlock(d); if(waserror()) nexterror(); tsleep(&up->sleep, return0, 0, 100); poperror(); } f->nhdr = Aoehsz + Aoecfgsz; memset(f->hdr, 0, f->nhdr); h = (Aoehdr*)f->hdr; if(hset(d, f, h, ACconfig, 1) == -1) return 0; ch = (Aoecfg*)(f->hdr + Aoehsz); f->srb = srb; f->dp = s; ch->verccmd = AQCfset; hnputs(ch->cslen, len); d->nout++; srb->nout++; f->dl->npkt++; f->dlen = len; /* * these refer to qlock & waserror in the above for loop. * there's still the first waserror outstanding. */ poperror(); qunlock(d); devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); sleep(srb, srbready, srb); if(srb->error) error(srb->error); qlock(d); if(waserror()){ qunlock(d); nexterror(); } memmove(d->config, s, len); d->nconfig = len; poperror(); qunlock(d); poperror(); /* pop first waserror */ srbfree(srb); memmove(db, s, len); free(s); return len; } static int devmaxdata(Aoedev *d) { int i, m, mtu, datamtu; Devlink *l; Netlink *n; mtu = 100000; datamtu = 100000; for(i = 0; i < d->ndl; i++){ l = d->dl + i; n = l->nl; if((l->flag & Dup) == 0 || (n->flag & Dup) == 0) continue; m = getmtu(n->mtu); if(l->datamtu < datamtu) datamtu = l->datamtu; if(m < mtu) mtu = m; } if(mtu == 100000) mtu = 1514; mtu -= Aoehsz + Aoeatasz; mtu -= mtu % Aoesectsz; if(mtu > datamtu) mtu = datamtu; return mtu; } static int toggle(char *s, uint f, uint bit) { if(s == nil) f = f^bit; else if(strcmp(s, "on") == 0) f |= bit; else f &= ~bit; return f; } static void ataident(Aoedev*); static long unitctlwrite(Aoedev *d, void *db, long n) { uint maxbcnt, m; uvlong bsize; enum { Failio, Ident, Jumbo, Maxbno, Mtu, Nofailf, Setsize, }; Cmdbuf *cb; Cmdtab *ct; static Cmdtab cmds[] = { {Failio, "failio", 1 }, {Ident, "identify", 1 }, {Jumbo, "jumbo", 0 }, {Maxbno, "maxbno", 0 }, {Mtu, "mtu", 0 }, {Nofailf, "nofail", 0 }, {Setsize, "setsize", 0 }, }; cb = parsecmd(db, n); qlock(d); if(waserror()){ qunlock(d); free(cb); nexterror(); } ct = lookupcmd(cb, cmds, nelem(cmds)); switch(ct->index){ case Failio: downdev(d, "i/o failure"); break; case Ident: ataident(d); break; case Jumbo: d->flag = toggle(cb->f[1], d->flag, Djumbo); break; case Maxbno: case Mtu: maxbcnt = devmaxdata(d); if(cb->nf > 2) error(Ecmdargs); if(cb->nf == 2){ m = strtoul(cb->f[1], 0, 0); if(ct->index == Maxbno) m *= Aoesectsz; else{ m -= Aoehsz + Aoeatasz; m &= ~(Aoesectsz-1); } if(m == 0 || m > maxbcnt) cmderror(cb, "invalid mtu"); maxbcnt = m; d->maxmtu = m; } else d->maxmtu = Maxmtu; d->maxbcnt = maxbcnt; break; case Nofailf: d->flag = toggle(cb->f[1], d->flag, Dnofail); break; case Setsize: bsize = d->realbsize; if(cb->nf > 2) error(Ecmdargs); if(cb->nf == 2){ bsize = strtoull(cb->f[1], 0, 0); if(bsize % Aoesectsz) cmderror(cb, "disk size must be sector aligned"); } d->bsize = bsize; break; } poperror(); qunlock(d); free(cb); return n; } static long unitwrite(Chan *c, void *db, long n, vlong off) { long rv; char *buf; Aoedev *d; d = unit2dev(UNIT(c->qid)); switch(TYPE(c->qid)){ default: error(Ebadarg); case Qctl: return unitctlwrite(d, db, n); case Qident: error(Eperm); case Qdata: return rw(d, Write, db, n, off); case Qconfig: if(off + n > sizeof d->config) error(Etoobig); buf = smalloc(sizeof d->config); if(waserror()){ free(buf); nexterror(); } memmove(buf, d->config, d->nconfig); memmove(buf + off, db, n); rv = configwrite(d, buf, n + off); poperror(); free(buf); return rv; } } static Netlink* addnet(char *path, Chan *cc, Chan *dc, Chan *mtu, uchar *ea) { Netlink *nl, *e; lock(&netlinks); if(waserror()){ unlock(&netlinks); nexterror(); } nl = netlinks.nl; e = nl + nelem(netlinks.nl); for(; nl < e && nl->cc != nil; nl++) continue; if(nl == e) error("out of netlink structures"); nl->cc = cc; nl->dc = dc; nl->mtu = mtu; strncpy(nl->path, path, sizeof(nl->path)-1); nl->path[sizeof(nl->path)-1] = 0; memmove(nl->ea, ea, sizeof nl->ea); poperror(); nl->flag |= Dup; unlock(&netlinks); return nl; } static int newunit(void) { int x; x = incref(&units)-1; if(x >= Maxunits){ decref(&units); x = -1; } return x; } static int dropunit(void) { return decref(&units); } /* * always allocate max frames. maxout may change. */ static Aoedev* newdev(uint major, uint minor, int n) { Aoedev *d; Frame *f, *e; d = malloc(sizeof *d); f = malloc(sizeof *f*Maxframes); if(d == nil || f == nil) { free(d); free(f); error("aoe device allocation failure"); } d->nframes = n; d->frames = f; for (e = f + Maxframes; f < e; f++) f->tag = Tfree; d->maxout = n; d->major = major; d->minor = minor; d->maxbcnt = Dbcnt; d->flag = Djumbo; d->maxmtu = Maxmtu; d->unit = newunit(); /* bzzt. inaccurate if units removed */ if(d->unit == -1){ free(d); free(d->frames); error("too many units"); } d->dl = d->dltab; return d; } static Aoedev* mm2dev(uint major, uint minor) { Aoedev *d; rlock(&devs); for(d = devs.d; d; d = d->next) if(d->major == major && d->minor == minor){ runlock(&devs); return d; } runlock(&devs); eventlog("mm2dev: %ud.%ud not found\n", major, minor); return nil; } /* Find the device in our list. If not known, add it */ static Aoedev* getdev(uint major, uint minor, int n) { Aoedev *d; if(major == 0xffff || minor == 0xff) return 0; wlock(&devs); if(waserror()){ wunlock(&devs); nexterror(); } for(d = devs.d; d; d = d->next) if(d->major == major && d->minor == minor) break; if(d == nil) { d = newdev(major, minor, n); d->next = devs.d; devs.d = d; } poperror(); wunlock(&devs); return d; } static void ataident(Aoedev *d) { Aoeata *a; Aoehdr *h; Frame *f; f = freeframe(d); if(f == nil) return; f->nhdr = Aoehsz + Aoeatasz; memset(f->hdr, 0, f->nhdr); h = (Aoehdr*)f->hdr; if(hset(d, f, h, ACata, 1) == -1) return; a = (Aoeata*)(f->hdr + Aoehsz); f->srb = srbkalloc(0, 0); a->cmdstat = Cid; /* ata 6, page 110 */ a->scnt = 1; a->lba[3] = 0xa0; d->nout++; f->dl->npkt++; f->bcnt = 512; f->dlen = 0; if(waserror()){ srbfree(f->srb); d->nout--; f->tag = Tfree; }else{ devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); poperror(); } } static int newdlea(Devlink *l, uchar *ea) { int i; uchar *t; for(i = 0; i < Nea; i++){ t = l->eatab[i]; if(i == l->nea){ memmove(t, ea, Eaddrlen); return l->nea++; } if(memcmp(t, ea, Eaddrlen) == 0) return i; } return -1; } static Devlink* newdevlink(Aoedev *d, Netlink *n, Aoehdr *h) { int i; Aoecfg *c; Devlink *l; c = (Aoecfg*)((uchar*)h + Aoehsz); for(i = 0; i < Ndevlink; i++){ l = d->dl + i; if(i == d->ndl){ d->ndl++; newdlea(l, h->src); l->datamtu = c->scnt*Aoesectsz; l->nl = n; l->flag |= Dup; l->mintimer = Rtmin; l->rttavg = Rtmax; return l; } if(l->nl == n){ newdlea(l, h->src); l->datamtu = c->scnt*Aoesectsz; l->flag |= Dup; return l; } } eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, h->src); return 0; } static void errrsp(Block *b, char *s) { int n; Aoedev *d; Aoehdr *h; Frame *f; h = (Aoehdr*)b->rp; n = nhgetl(h->tag); if(n == Tmgmt || n == Tfree) return; d = mm2dev(nhgets(h->major), h->minor); if(d == nil) return; if(f = getframe(d, n)) frameerror(d, f, s); } static void qcfgrsp(Block *b, Netlink *nl) { int cmd, cslen, blen; uint n, major; Aoedev *d; Aoehdr *h, *h0; Aoecfg *ch; Devlink *l; Frame *f; Srb *srb; h = (Aoehdr*)b->rp; ch = (Aoecfg*)(b->rp + Aoehsz); major = nhgets(h->major); n = nhgetl(h->tag); if(n != Tmgmt && n != Tfree){ d = mm2dev(major, h->minor); if(d == nil) return; qlock(d); f = getframe(d, n); if(f == nil){ qunlock(d); eventlog("%æ: unknown response tag %ux\n", d, n); return; } h0 = (Aoehdr*)f->hdr; cmd = h0->cmd; if(cmd != ACconfig){ qunlock(d); eventlog("%æ: malicious server got ACconfig want %d; tag %ux\n", d, cmd, n); return; } cslen = nhgets(ch->cslen); blen = BLEN(b) - (Aoehsz + Aoecfgsz); if(cslen < blen && BLEN(b) > 60) eventlog("%æ: cfgrsp: tag %.8ux oversized %d %d\n", d, n, cslen, blen); if(cslen > blen){ eventlog("%æ: cfgrsp: tag %.8ux runt %d %d\n", d, n, cslen, blen); cslen = blen; } memmove(f->dp, b->rp + Aoehsz + Aoecfgsz, cslen); srb = f->srb; f->dp = nil; f->srb = nil; if(srb != nil){ srb->nout--; srbwakeup(srb); d->nout--; f->tag = Tfree; } qunlock(d); return; } cmd = ch->verccmd & 0xf; if(cmd != 0){ eventlog("aoe%ud.%ud: cfgrsp: bad command %d\n", major, h->minor, cmd); return; } n = nhgets(ch->bufcnt); if(n > Maxframes) n = Maxframes; if(waserror()){ eventlog("getdev: %ud.%ud ignored: %s\n", major, h->minor, up->errstr); return; } d = getdev(major, h->minor, n); poperror(); if(d == 0) return; qlock(d); *up->errstr = 0; if(waserror()){ qunlock(d); eventlog("%æ: %s\n", d, up->errstr); nexterror(); } l = newdevlink(d, nl, h); /* add this interface. */ d->fwver = nhgets(ch->fwver); cslen = nhgets(ch->cslen); if(cslen > sizeof d->config) cslen = sizeof d->config; if(Aoehsz + Aoecfgsz + cslen > BLEN(b)) cslen = BLEN(b) - (Aoehsz + Aoecfgsz); d->nconfig = cslen; memmove(d->config, b->rp + Aoehsz + Aoecfgsz, cslen); /* manually set mtu may be reset lower if conditions warrant */ if(l){ n = devmaxdata(d); if((d->flag & Djumbo) == 0) n = Dbcnt; if(n > d->maxmtu) n = d->maxmtu; if(n != d->maxbcnt){ eventlog("%æ: setting %d byte mtu on %s:%E\n", d, n, nl->path, nl->ea); d->maxbcnt = n; } } if(d->nopen == 0) ataident(d); poperror(); qunlock(d); } static vlong aoeidentify(Aoedev *d, ushort *id) { vlong s; s = idfeat(d, id); if(s == -1){ eventlog("%æ: idfeat returns -1\n", d); return -1; } if((d->feat&Dlba) == 0){ eventlog("%æ: no lba support\n", d); return -1; } d->flag |= Dup; memmove(d->ident, id, sizeof d->ident); return s; } static void newvers(Aoedev *d) { d->vers = incref(&drivevers); } static int identify(Aoedev *d, ushort *id) { vlong osectors, s; uchar oserial[21]; s = aoeidentify(d, id); if(s == -1) return -1; osectors = d->realbsize; memmove(oserial, d->serial, sizeof d->serial); idmove(d->serial, id+10, 20); idmove(d->firmware, id+23, 8); idmove(d->model, id+27, 40); /* idss() */ /* d->wwn = idwwn(d, id); */ s *= Aoesectsz; if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){ d->bsize = s; d->realbsize = s; // d->mediachange = 1; newvers(d); } return 0; } static void atarsp(Block *b) { uint n, cmd; ushort major; Aoeata *ahin, *ahout; Aoehdr *h, *h0; Aoedev *d; Frame *f; Srb *srb; h = (Aoehdr*)b->rp; major = nhgets(h->major); d = mm2dev(major, h->minor); if(d == nil) return; ahin = (Aoeata*)(b->rp + Aoehsz); qlock(d); if(waserror()){ qunlock(d); nexterror(); } n = nhgetl(h->tag); if(n == Tfree || n == Tmgmt) goto bail; f = getframe(d, n); if(f == nil){ eventlog("%æ: unexpected response; tag %ux\n", d, n); goto bail; } h0 = (Aoehdr*)f->hdr; cmd = h0->cmd; if(cmd != ACata){ eventlog("%æ: malicious server got ACata want %d; tag %ux\n", d, cmd, n); goto bail; } rtupdate(f->dl, tsince(f->tag)); ahout = (Aoeata*)(f->hdr + Aoehsz); srb = f->srb; if(ahin->cmdstat & 0xa9){ eventlog("%æ: ata error cmd %.2ux stat %.2ux\n", d, ahout->cmdstat, ahin->cmdstat); if(srb != nil) srb->error = Eio; } else { n = ahout->scnt * Aoesectsz; switch(ahout->cmdstat){ case Crd: case Crdext: if(BLEN(b) - (Aoehsz + Aoeatasz) != n){ eventlog("%æ: misread blen %zd expect %d\n", d, BLEN(b), n); goto bail; } memmove(f->dp, b->rp + Aoehsz + Aoeatasz, n); case Cwr: case Cwrext: if(n > Dbcnt) f->nl->lostjumbo = 0; if(f->bcnt -= n){ f->lba += n / Aoesectsz; f->dp = (uchar*)f->dp + n; resend(d, f); goto bail; } break; case Cid: if(BLEN(b) - (Aoehsz + Aoeatasz) < 512){ eventlog("%æ: runt identify blen %zd expect %d\n", d, BLEN(b), 512 + Aoehsz + Aoeatasz); goto bail; } identify(d, (ushort*)(b->rp + Aoehsz + Aoeatasz)); free(srb); /* BOTCH */ srb = nil; break; default: eventlog("%æ: unknown ata command %.2ux \n", d, ahout->cmdstat); } } f->srb = nil; if(srb != nil){ srb->nout--; srbwakeup(srb); } f->tag = Tfree; d->nout--; work(d); bail: poperror(); qunlock(d); } static void netrdaoeproc(void *v) { int idx; char name[Maxpath+1], *s; Aoehdr *h; Block *b; Netlink *nl; nl = (Netlink*)v; idx = nl - netlinks.nl; netlinks.reader[idx] = 1; kstrcpy(name, nl->path, Maxpath); if(waserror()){ eventlog("netrdaoe@%s: exiting: %s\n", name, up->errstr); netlinks.reader[idx] = 0; wakeup(netlinks.rendez + idx); pexit(up->errstr, 1); } if(autodiscover) discover(0xffff, 0xff); for (;;) { if((nl->flag & Dup) == 0) error("netlink is down"); if(nl->dc == nil) panic("netrdaoe: nl->dc == nil"); b = devtab[nl->dc->type]->bread(nl->dc, 1<<16, 0); if(b == nil) error("network read"); h = (Aoehdr*)b->rp; if(h->verflag & AFrsp) if(s = aoeerror(h)){ eventlog("%s: %d.%d %s\n", nl->path, h->major[0]<<8 | h->major[1], h->minor, s); errrsp(b, s); }else if(h->cmd == ACata) atarsp(b); else if(h->cmd == ACconfig) qcfgrsp(b, nl); else if((h->cmd & 0xf0) != 0xf0){ eventlog("%s: unknown cmd %d\n", nl->path, h->cmd); errrsp(b, "unknown command"); } freeb(b); } } static void getaddr(char *path, uchar *ea) { int n; char buf[2*Eaddrlen+1]; Chan *c; uprint("%s/addr", path); c = namec(up->genbuf, Aopen, OREAD, 0); if(waserror()) { cclose(c); nexterror(); } n = devtab[c->type]->read(c, buf, sizeof buf-1, 0); cclose(c); poperror(); buf[n] = 0; if(parseether(ea, buf) < 0) error("parseether failure"); } static void netbind(char *path) { char addr[Maxpath]; uchar ea[2*Eaddrlen+1]; Chan *dc, *cc, *mtu; Netlink *nl; snprint(addr, sizeof addr, "%s!0x%x", path, Aoetype); dc = chandial(addr, nil, nil, &cc); snprint(addr, sizeof addr, "%s/mtu", path); mtu = nil; if(!waserror()){ mtu = namec(addr, Aopen, OREAD, 0); poperror(); } if(waserror()){ cclose(dc); cclose(cc); if(mtu != nil) cclose(mtu); nexterror(); } if(dc == nil || cc == nil) error(Enonexist); getaddr(path, ea); nl = addnet(path, cc, dc, mtu, ea); snprint(addr, sizeof addr, "netrdaoe@%s", path); kproc(addr, netrdaoeproc, nl); poperror(); } static int unbound(void *v) { return *(int*)v != 0; } static void netunbind(char *path) { int i, idx; Aoedev *d, *p, *next; Chan *dc, *cc; Devlink *l; Frame *f; Netlink *n, *e; n = netlinks.nl; e = n + nelem(netlinks.nl); lock(&netlinks); for(; n < e; n++) if(n->dc && strcmp(n->path, path) == 0) break; unlock(&netlinks); if(n == e) error("device not bound"); /* * hunt down devices using this interface; disable * this also terminates the reader. */ idx = n - netlinks.nl; wlock(&devs); for(d = devs.d; d; d = d->next){ qlock(d); for(i = 0; i < d->ndl; i++){ l = d->dl + i; if(l->nl == n) l->flag &= ~Dup; } qunlock(d); } n->flag &= ~Dup; wunlock(&devs); /* confirm reader is down. */ while(waserror()) ; sleep(netlinks.rendez + idx, unbound, netlinks.reader + idx); poperror(); /* reschedule packets. */ wlock(&devs); for(d = devs.d; d != nil; d = d->next){ qlock(d); for(i = 0; i < d->nframes; i++){ f = d->frames + i; if(f->tag != Tfree && f->nl == n) resend(d, f); } qunlock(d); } wunlock(&devs); /* squeeze devlink pool. (we assert nobody is using them now) */ wlock(&devs); for(d = devs.d; d != nil; d = d->next){ qlock(d); for(i = 0; i < d->ndl; i++){ l = d->dl + i; if(l->nl == n) memmove(l, l + 1, sizeof *l * (--d->ndl - i)); } qunlock(d); } wunlock(&devs); /* close device link. */ lock(&netlinks); dc = n->dc; cc = n->cc; if(n->mtu != nil) cclose(n->mtu); memset(n, 0, sizeof *n); unlock(&netlinks); cclose(dc); cclose(cc); /* squeeze orphan devices */ wlock(&devs); for(p = d = devs.d; d != nil; d = next){ next = d->next; if(d->ndl > 0){ p = d; continue; } qlock(d); downdev(d, "orphan"); qunlock(d); if(p != devs.d) p->next = next; else{ devs.d = next; p = devs.d; } free(d->frames); free(d); dropunit(); } wunlock(&devs); } static void strtoss(char *f, uint *shelf, uint *slot) { char *s; *shelf = 0xffff; *slot = 0xff; if(f == nil) return; *shelf = strtol(f, &s, 0); if(s == f || *shelf > 0xffff) error("bad shelf"); f = s; if(*f++ == '.'){ *slot = strtol(f, &s, 0); if(s == f || *slot > 0xff) error("bad slot"); } } static void discoverstr(char *f) { uint shelf, slot; strtoss(f, &shelf, &slot); discover(shelf, slot); } static void removedev(Aoedev *d) { int i; Aoedev *p; wlock(&devs); p = 0; if(d != devs.d) for(p = devs.d; p != nil; p = p->next) if(p->next == d) break; qlock(d); d->flag &= ~Dup; newvers(d); d->ndl = 0; qunlock(d); for(i = 0; i < d->nframes; i++) frameerror(d, d->frames+i, Enotup); if(p != nil) p->next = d->next; else devs.d = d->next; free(d->frames); free(d); dropunit(); wunlock(&devs); } static void aoeremove(Chan *c) { switch(TYPE(c->qid)){ default: case Qzero: case Qtopdir: case Qtoplog: case Qtopctl: case Qctl: case Qdata: case Qconfig: case Qident: error(Eperm); case Qunitdir: removedev(unit2dev(UNIT(c->qid))); break; } } static void removestr(char *f) { uint shelf, slot; Aoedev *d; strtoss(f, &shelf, &slot); wlock(&devs); for(d = devs.d; d != nil; d = d->next) if(shelf == d->major && slot == d->minor){ wunlock(&devs); /* BOTCH */ removedev(d); return; } wunlock(&devs); error("device not bound"); } static long topctlwrite(void *db, long n) { enum { Autodiscover, Bind, Debug, Discover, Rediscover, Remove, Unbind, }; char *f; Cmdbuf *cb; Cmdtab *ct; static Cmdtab cmds[] = { { Autodiscover, "autodiscover", 0 }, { Bind, "bind", 2 }, { Debug, "debug", 0 }, { Discover, "discover", 0 }, { Rediscover, "rediscover", 0 }, { Remove, "remove", 2 }, { Unbind, "unbind", 2 }, }; cb = parsecmd(db, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, cmds, nelem(cmds)); f = cb->f[1]; switch(ct->index){ case Autodiscover: autodiscover = toggle(f, autodiscover, 1); break; case Bind: netbind(f); break; case Debug: debug = toggle(f, debug, 1); break; case Discover: discoverstr(f); break; case Rediscover: rediscover = toggle(f, rediscover, 1); break; case Remove: removestr(f); /* depricated */ break; case Unbind: netunbind(f); break; } poperror(); free(cb); return n; } static long aoewrite(Chan *c, void *db, long n, vlong off) { switch(TYPE(c->qid)){ default: case Qzero: case Qtopdir: case Qunitdir: case Qtoplog: error(Eperm); case Qtopctl: return topctlwrite(db, n); case Qctl: case Qdata: case Qconfig: case Qident: return unitwrite(c, db, n, off); } } Dev aoedevtab = { L'æ', "aoe", devreset, devinit, devshutdown, aoeattach, aoewalk, aoestat, aoeopen, devcreate, aoeclose, aoeread, devbread, aoewrite, devbwrite, aoeremove, devwstat, devpower, devconfig, };