vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

commit 8aa03c4f1dc493f3a62bc88eb7d0c1ac26e24cea
parent 99843ed18b65e16b10f49b73956f7bfd9c495aac
Author: Jesus Galan Lopez (yiyus) <yiyu.jgl@gmail.com>
Date:   Wed,  2 Jun 2010 23:04:57 +0200

add the new files :P

Diffstat:
.hgignore | 2++
src/9vx/a/aoe.h | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/a/devaoe.c | 2575+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/a/devether.c | 534+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/a/etherif.h | 39+++++++++++++++++++++++++++++++++++++++
src/9vx/a/netif.c | 761+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/a/sdaoe.c | 652+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/etherve.c | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 4808 insertions(+), 0 deletions(-)

diff --git a/.hgignore b/.hgignore @@ -25,6 +25,8 @@ src/vxa/bz2/*ebz2 src/vxlinux/vxlinux src/9vx/9vx src/9vx/bootcode.S +src/9vx/fossil.S +src/9vx/venti.S src/9vx/data2s src/9vx/a/errstr.h src/9vx/kerndate.h diff --git a/src/9vx/a/aoe.h b/src/9vx/a/aoe.h @@ -0,0 +1,84 @@ +enum { + ACata, + ACconfig, +}; + +enum { + AQCread, + AQCtest, + AQCprefix, + AQCset, + AQCfset, +}; + +enum { + AEcmd = 1, + AEarg, + AEdev, + AEcfg, + AEver, +}; + +enum { + Aoetype = 0x88a2, + Aoesectsz = 512, + Szaoeata = 24+12, + Szaoeqc = 24+8, + Aoever = 1, + + AFerr = 1<<2, + AFrsp = 1<<3, + + AAFwrite= 1, + AAFext = 1<<6, +}; + +typedef struct { + uchar dst[Eaddrlen]; + uchar src[Eaddrlen]; + uchar type[2]; + uchar verflag; + uchar error; + uchar major[2]; + uchar minor; + uchar cmd; + uchar tag[4]; +} Aoehdr; + +typedef struct { + uchar dst[Eaddrlen]; + uchar src[Eaddrlen]; + uchar type[2]; + uchar verflag; + uchar error; + uchar major[2]; + uchar minor; + uchar cmd; + uchar tag[4]; + uchar aflag; + uchar errfeat; + uchar scnt; + uchar cmdstat; + uchar lba[6]; + uchar res[2]; +} Aoeata; + +typedef struct { + uchar dst[Eaddrlen]; + uchar src[Eaddrlen]; + uchar type[2]; + uchar verflag; + uchar error; + uchar major[2]; + uchar minor; + uchar cmd; + uchar tag[4]; + uchar bufcnt[2]; + uchar fwver[2]; + uchar scnt; + uchar verccmd; + uchar cslen[2]; +} Aoeqc; + +extern char Echange[]; +extern char Enotup[]; diff --git a/src/9vx/a/devaoe.c b/src/9vx/a/devaoe.c @@ -0,0 +1,2575 @@ +/* + * © 2005-8 coraid + * aoe storage initiator + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" +#include "netif.h" +#include "etherif.h" +#include "ip/ip.h" +#include "aoe.h" + +#define WAKEUP(x) wakeup(&((x)->rend)) +#define SLEEP(a,b,c) sleep(&(a->rend), b, c) + +//#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 { + Maxunits = 0xff, + Maxframes = 128, + Maxmtu = 100000, + Ndevlink = 6, + Nea = 6, + Nnetlink = 6, +}; + +#define TYPE(q) ((ulong)(q).path & 0xf) +#define UNIT(q) (((ulong)(q).path>>4) & 0xff) +#define L(q) (((ulong)(q).path>>12) & 0xf) +#define QID(u, t) ((u)<<4 | (t)) +#define Q3(l, u, t) ((l)<<8 | QID(u, t)) +#define UP(d) ((d)->flag & Dup) + +#define Ticks msec() +#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, +}; + +enum { + Read, + Write, +}; + +/* + * unified set of flags + * a Netlink + Aoedev most both be jumbo capable + * to send jumbograms to that interface. + */ +enum { + /* sync with ahci.h */ + Dllba = 1<<0, + Dsmart = 1<<1, + Dpower = 1<<2, + Dnop = 1<<3, + Datapi = 1<<4, + Datapi16= 1<<5, + + /* aoe specific */ + Dup = 1<<6, + Djumbo = 1<<7, +}; + +static char *flagname[] = { + "llba", + "smart", + "power", + "nop", + "atapi", + "atapi16", + + "up", + "jumbo", +}; + +typedef struct { + uchar flag; + uchar lostjumbo; + int datamtu; + + 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 rend; + 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 qlock; + Aoedev *next; + + ulong vers; + + int ndl; + ulong dlidx; + Devlink *dl; + Devlink dltab[Ndevlink]; + + ushort fwver; + uchar flag; + int nopen; + int major; + int 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; + + 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 lk; + QLock qlock; + Rendez rend; + char buf[Eventlen*Nevents]; + char *rp; + char *wp; +} events; + +static struct { + RWlock rwlock; + int nd; + Aoedev *d; +} devs; + +static struct { + Lock lk; + 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; + char Enotup[] = "aoe device is down"; + char Echange[] = "media or partition has changed"; + +static Srb* +srballoc(ulong sz) +{ + Srb *srb; + + srb = malloc(sizeof *srb+sz); + srb->dp = srb->data = srb+1; + srb->ticksent = Ticks; + return srb; +} + +static Srb* +srbkalloc(void *db, ulong dummy) +{ + Srb *srb; + + srb = malloc(sizeof *srb); + srb->dp = srb->data = db; + srb->ticksent = Ticks; + return srb; +} + +#define srbfree(srb) free(srb) + +static void +srberror(Srb *srb, char *s) +{ + srb->error = s; + srb->nout--; + WAKEUP(srb); +} + +static void +frameerror(Aoedev *d, Frame *f, char *s) +{ + Srb *srb; + + srb = f->srb; + if(f->tag == Tfree) + return; + f->srb = nil; + f->tag = Tfree; /* don't get fooled by way-slow responses */ + if(!srb) + return; + srberror(srb, s); + d->nout--; +} + +static char* +unitname(Aoedev *d) +{ + uprint("%d.%d", 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()){ + free(buf); + QUNLOCK(&events); + nexterror(); + } + memmove(a, buf, n); + poperror(); + } + free(buf); + QUNLOCK(&events); + 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) +{ + int n; + + LOCK(&events); + if(*events.rp == 0) + n = 0; + else if(events.wp < events.rp) + n = Nevents - (events.rp - events.wp); + else + n = events.wp - events.rp; + 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->tag = Tfree, f->srb = nil, 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 && l->flag & Dup) + return l; + } + return 0; +} + +static int +pickea(Devlink *l) +{ + if(l == 0) + return -1; + if(l->nea == 0) + return -1; + return l->eaidx++ % l->nea; +} + +static int +hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd) +{ + int i; + Devlink *l; + + if(f->srb) + if((long)(Ticks-f->srb->ticksent) > Srbtimeout){ + eventlog("%æ: srb timeout\n", d); + frameerror(d, f, Etimedout); + return -1; + } + l = pickdevlink(d); + i = pickea(l); + if(i == -1){ + 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; + + hnputl(h->tag, f->tag = newtag(d)); + 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; + + a = (Aoeata*)f->hdr; + if(hset(d, f, (Aoehdr*)a, a->cmd) == -1) + return -1; + 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(int major, int 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 *dummy) +{ + 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; 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; + if(a->scnt > Dbcnt / Aoesectsz && + ++f->nl->lostjumbo > (d->nframes << 1)){ + ea = f->dl->eatab[f->eaidx]; + eventlog("%æ: jumbo failure on %s:%E; lba%lld\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) + tsleep(&up->sleep, return0, 0, i); + goto loop; +} + +static int +fmtaoe(Fmt *f) +{ + char buf[16]; + Aoedev *d; + + d = va_arg(f->args, Aoedev*); + snprint(buf, sizeof buf, "aoe%d.%d", d->major, d->minor); + return fmtstrcpy(f, buf); +} + +static void netbind(char *path); + +static void +aoecfg(void) +{ + int n, i; + char *p, *f[32], buf[24]; + + if(1) +// if((p = getconf("aoeif")) == nil || (n = tokenize(p, 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'æ', fmtaoe); + 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 Aoedev* +unitseq(ulong unit) +{ + int i; + Aoedev *d; + + i = 0; + RLOCK(&devs); + for(d = devs.d; d; d = d->next) + if(i++ == unit) + break; + RUNLOCK(&devs); + return d; +} + +static Aoedev* +unit2dev(ulong unit) +{ + Aoedev *d; + + RLOCK(&devs); + for(d = devs.d; d; 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 = 0644; + size = 0; + vers = d->vers; + t = QTFILE; + + switch(type){ + default: + return -1; + case Qctl: + p = "ctl"; + break; + case Qdata: + p = "data"; + perm = 0640; + 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 = 0644; + 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 *d0, Dirtab *d1, int d2, 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; + if((d = unitseq(s)) == 0) + return -1; + mkqid(&q, QID(d->unit, Qunitdir), 0, QTDIR); + devdir(c, q, unitname(d), 0, eve, 0555, dp); + return 1; + 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(s == DEVDOTDOT){ + mkqid(&q, QID(i, Qunitdir), 0, QTDIR); + devdir(c, q, "devlink", 0, eve, 0555, dp); + return 1; + } + if(i >= units.ref) + return -1; + d = unit2dev(i); + if(s >= d->ndl) + return -1; + uprint("%d", s); + mkqid(&q, Q3(s, i, Qdevlink), 0, QTFILE); + devdir(c, q, up->genbuf, 0, eve, 0755, 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, 0755, 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; + Srb *srb; + + extbit = 0x4; + writebit = 0x10; + + srb = d->inprocess; + bcnt = d->maxbcnt; + if(bcnt > srb->len) + bcnt = srb->len; + f->nhdr = Szaoeata; + memset(f->hdr, 0, f->nhdr); + ah = (Aoeata*)f->hdr; + if(hset(d, f, (Aoehdr*)ah, ACata) == -1) + return; + f->dp = srb->dp; + f->bcnt = bcnt; + f->lba = srb->sector; + f->srb = srb; + + ah->scnt = bcnt / Aoesectsz; + putlba(ah, f->lba); + if(d->flag & 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()){ + f->tag = Tfree; + d->inprocess = nil; + nexterror(); + } + 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", + }; + + if((h->verflag & AFerr) == 0) + return 0; + 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 int +srbready(void *v) +{ + Srb *s; + + s = v; + return s->error || (!s->nout && !s->len); +} + +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(); + } + srb->next = nil; + if(d->tail) + d->tail->next = srb; + d->tail = srb; + if(d->head == nil) + d->head = srb; + work(d); + poperror(); + QUNLOCK(d); + + while(waserror()) + ; + SLEEP(srb, srbready, srb); + poperror(); +} + +#define iskaddr(a) (!up || (uintptr)(a) > up->pmmu.uzero+USTKTOP) + +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(off >= d->bsize) + return 0; + if(off + len > d->bsize) + len = d->bsize - off; + copy = 0; + if(iskaddr(db)){ +panic("iskaddr %p %p\n", 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){ + if(!UP(d)) + error(Eio); + 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) + 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* +pflag(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 = malloc(1024); + e = p + 1024; + + 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->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 +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 = malloc(1024); + e = s + 1024; + + 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 = pflag(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, "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 = pflag(p, e, l->flag); + p = seprint(p, e, "nl lostjumbo: %d\n", l->nl->lostjumbo); + p = seprint(p, e, "nl datamtu: %d\n", l->nl->datamtu); + + if(p - s < len) + len = p - s; + i = readstr(off, db, len, s); + free(s); + return i; +} + +static long +topctlread(Chan *d0, void *db, int len, int off) +{ + int i; + char *s, *p, *e; + Netlink *n; + + s = p = malloc(1024); + e = s + 1024; + + 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 == 0) + 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 = pflag(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, n->datamtu); + } + + 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; + Aoeqc *ch; + Frame *f; + Srb *srb; + + if(!UP(d)) + error(Enotup); + if(len > sizeof d->config) + error(Etoobig); + srb = srballoc(len); + s = malloc(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 = Szaoeqc; + memset(f->hdr, 0, f->nhdr); + ch = (Aoeqc*)f->hdr; + if(hset(d, f, (Aoehdr*)ch, ACconfig) == -1) + return 0; + 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 +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 +devmaxdata(Aoedev *d) +{ + int i, m, mtu; + Devlink *l; + Netlink *n; + + mtu = 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(m > l->datamtu) + m = l->datamtu; + if(m < mtu) + mtu = m; + } + if(mtu == 100000) + mtu = 1514; + mtu -= Szaoeata; + mtu -= mtu % Aoesectsz; + return mtu; +} + +static int +toggle(char *s, int init) +{ + if(s == nil) + return init ^ 1; + return strcmp(s, "on") == 0; +} + +static void ataident(Aoedev*); + +static long +unitctlwrite(Aoedev *d, void *db, long n) +{ + uint maxbcnt, m; + uvlong bsize; + enum { + Failio, + Ident, + Jumbo, + Maxbno, + Mtu, + Setsize, + }; + Cmdbuf *cb; + Cmdtab *ct; + static Cmdtab cmds[] = { + {Failio, "failio", 1 }, + {Ident, "identify", 1 }, + {Jumbo, "jumbo", 0 }, + {Maxbno, "maxbno", 0 }, + {Mtu, "mtu", 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: + m = 0; + if(d->flag & Djumbo) + m = 1; + toggle(cb->f[1], m); + if(m) + d->flag |= Djumbo; + else + 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 -= Szaoeata; + 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 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; + default: + cmderror(cb, "unknown aoe control message"); + } + 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 = malloc(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; 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); + memmove(nl->ea, ea, sizeof nl->ea); + poperror(); + nl->flag |= Dup; + UNLOCK(&netlinks); + return nl; +} + +static int +newunit(void) +{ + int x; + + LOCK(&units); + if(units.ref == Maxunits) + x = -1; + else + x = units.ref++; + UNLOCK(&units); + return x; +} + +static int +dropunit(void) +{ + int x; + + LOCK(&units); + x = --units.ref; + UNLOCK(&units); + return x; +} + +/* + * always allocate max frames. maxout may change. + */ +static Aoedev* +newdev(long major, long minor, int n) +{ + Aoedev *d; + Frame *f, *e; + + d = malloc(sizeof *d); + f = malloc(sizeof *f*Maxframes); + if(!d || !f) { + 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(int major, int 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: %d.%d not found\n", major, minor); + return nil; +} + +/* Find the device in our list. If not known, add it */ +static Aoedev* +getdev(long major, long 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 ushort +gbit16(void *a) +{ + uchar *i; + + i = a; + return i[1] << 8 | i[0]; +} + +static ulong +gbit32(void *a) +{ + ulong j; + uchar *i; + + i = a; + j = i[3] << 24; + j |= i[2] << 16; + j |= i[1] << 8; + j |= i[0]; + return j; +} + +static uvlong +gbit64(void *a) +{ + uchar *i; + + i = a; + return (uvlong)gbit32(i+4) << 32 | gbit32(a); +} + +static void +ataident(Aoedev *d) +{ + Aoeata *a; + Block *b; + Frame *f; + + f = freeframe(d); + if(f == nil) + return; + f->nhdr = Szaoeata; + memset(f->hdr, 0, f->nhdr); + a = (Aoeata*)f->hdr; + if(hset(d, f, (Aoehdr*)a, ACata) == -1) + return; + 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; + b = allocfb(f); + devtab[f->nl->dc->type]->bwrite(f->nl->dc, b, 0); +} + +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, Aoeqc *c) +{ + int i; + Devlink *l; + + for(i = 0; i < Ndevlink; i++){ + l = d->dl + i; + if(i == d->ndl){ + d->ndl++; + newdlea(l, c->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, c->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, c->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 == 0) + return; + if(f = getframe(d, n)) + frameerror(d, f, s); +} + +static void +qcfgrsp(Block *b, Netlink *nl) +{ + int major, cmd, cslen, blen; + unsigned n; + Aoedev *d; + Aoeqc *ch; + Devlink *l; + Frame *f; + + ch = (Aoeqc*)b->rp; + major = nhgets(ch->major); + n = nhgetl(ch->tag); + if(n != Tmgmt){ + d = mm2dev(major, ch->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; + } + cslen = nhgets(ch->cslen); + blen = BLEN(b) - Szaoeqc; + if(cslen < blen) + 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, ch + 1, cslen); + f->srb->nout--; + WAKEUP(f->srb); + d->nout--; + f->srb = nil; + f->tag = Tfree; + QUNLOCK(d); + return; + } + + cmd = ch->verccmd & 0xf; + if(cmd != 0){ + eventlog("aoe%d.%d: cfgrsp: bad command %d\n", major, ch->minor, cmd); + return; + } + n = nhgets(ch->bufcnt); + if(n > Maxframes) + n = Maxframes; + + if(waserror()){ + eventlog("getdev: %d.%d ignored: %s\n", major, ch->minor, up->errstr); + return; + } + d = getdev(major, ch->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, ch); /* add this interface. */ + + d->fwver = nhgets(ch->fwver); + n = nhgets(ch->cslen); + if(n > sizeof d->config) + n = sizeof d->config; + d->nconfig = n; + memmove(d->config, ch + 1, n); + + /* manually set mtu may be reset lower if conditions warrant */ + if(l){ + n = devmaxdata(d); + if(!(d->flag & Djumbo)) + 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 void +idmove(char *p, ushort *a, unsigned n) +{ + int i; + char *op, *e; + + op = p; + for(i = 0; i < n / 2; i++){ + *p++ = a[i] >> 8; + *p++ = a[i]; + } + *p = 0; + while(p > op && *--p == ' ') + *p = 0; + e = p; + p = op; + while(*p == ' ') + p++; + memmove(op, p, n - (e - p)); +} + +static vlong +aoeidentify(Aoedev *d, ushort *id) +{ + int i; + vlong s; + + d->flag &= ~(Dllba|Dpower|Dsmart|Dnop|Dup); + + i = gbit16(id+83) | gbit16(id+86); + if(i & (1<<10)){ + d->flag |= Dllba; + s = gbit64(id+100); + }else + s = gbit32(id+60); + + i = gbit16(id+83); + if((i>>14) == 1) { + if(i & (1<<3)) + d->flag |= Dpower; + i = gbit16(id+82); + if(i & 1) + d->flag |= Dsmart; + if(i & (1<<14)) + d->flag |= Dnop; + } +// eventlog("%æ up\n", d); + d->flag |= Dup; + memmove(d->ident, id, sizeof d->ident); + return s; +} + +static void +newvers(Aoedev *d) +{ + LOCK(&drivevers); + d->vers = drivevers.ref++; + UNLOCK(&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); + + 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) +{ + unsigned n; + short major; + Aoeata *ahin, *ahout; + Aoedev *d; + Frame *f; + Srb *srb; + + ahin = (Aoeata*)b->rp; + major = nhgets(ahin->major); + d = mm2dev(major, ahin->minor); + if(d == nil) + return; + QLOCK(d); + if(waserror()){ + QUNLOCK(d); + nexterror(); + } + n = nhgetl(ahin->tag); + f = getframe(d, n); + if(f == nil){ + dprint("%æ: unexpected response; tag %ux\n", d, n); + goto bail; + } + rtupdate(f->dl, tsince(f->tag)); + ahout = (Aoeata*)f->hdr; + srb = f->srb; + + if(ahin->cmdstat & 0xa9){ + eventlog("%æ: ata error cmd %.2ux stat %.2ux\n", + d, ahout->cmdstat, ahin->cmdstat); + if(srb) + srb->error = Eio; + } else { + n = ahout->scnt * Aoesectsz; + switch(ahout->cmdstat){ + case Crd: + case Crdext: + if(BLEN(b) - Szaoeata < n){ + eventlog("%æ: runt read blen %ld expect %d\n", + d, BLEN(b), n); + goto bail; + } + memmove(f->dp, b->rp + Szaoeata, 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) - Szaoeata < 512){ + eventlog("%æ: runt identify blen %ld expect %d\n", + d, BLEN(b), n); + goto bail; + } + identify(d, (ushort*)(b->rp + Szaoeata)); + break; + default: + eventlog("%æ: unknown ata command %.2ux \n", + d, ahout->cmdstat); + } + } + + if(srb && --srb->nout == 0 && srb->len == 0) + WAKEUP(srb); + f->srb = nil; + 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)) + 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: %s\n", nl->path, s); + errrsp(b, s); + }else if(h->cmd == ACata) + atarsp(b); + else if(h->cmd == ACconfig) + qcfgrsp(b, nl); + else if((h->cmd & 0xf0) == 0){ + 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(); + } + if(c == nil) + panic("æ: getaddr: c == nil"); + n = devtab[c->type]->read(c, buf, sizeof buf-1, 0); + poperror(); + cclose(c); + 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); + if(waserror()) + mtu = nil; + else { + mtu = namec(addr, Aopen, OREAD, 0); + poperror(); + } + + if(waserror()){ + cclose(dc); + cclose(cc); + if(mtu) + 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; 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; 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) + 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; 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, ushort *shelf, ushort *slot) +{ + ulong sh; + char *s; + + *shelf = 0xffff; + *slot = 0xff; + if(!f) + return; + *shelf = sh = strtol(f, &s, 0); + if(s == f || sh > 0xffff) + error("bad shelf"); + f = s; + if(*f++ == '.'){ + *slot = strtol(f, &s, 0); + if(s == f || *slot > 0xff) + error("bad shelf"); + }else + *slot = 0xff; +} + +static void +discoverstr(char *f) +{ + ushort 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; 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) + 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) +{ + ushort shelf, slot; + Aoedev *d; + + strtoss(f, &shelf, &slot); + WLOCK(&devs); + for(d = devs.d; d; 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, + Closewait, + 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); + break; + case Bind: + netbind(f); + break; + case Debug: + debug = toggle(f, debug); + break; + case Discover: + discoverstr(f); + break; + case Rediscover: + rediscover = toggle(f, rediscover); + break; + case Remove: + removestr(f); /* depricated */ + break; + case Unbind: + netunbind(f); + break; + default: + cmderror(cb, "unknown aoe control message"); + } + 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, +}; diff --git a/src/9vx/a/devether.c b/src/9vx/a/devether.c @@ -0,0 +1,534 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" +#include "netif.h" + +#include "etherif.h" + +#define MEMSIZE (256<<20) // same as ../a/devether.c:13 (TODO: var) + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if(etherxx[ctlrno] == 0) + error(Enodev); + + chan = devattach('l', spec); + if(waserror()){ + chanfree(chan); + nexterror(); + } + chan->dev = ctlrno; + if(etherxx[ctlrno]->attach) + etherxx[ctlrno]->attach(etherxx[ctlrno]); + poperror(); + return chan; +} + +static Walkqid* +etherwalk(Chan* chan, Chan* nchan, char** name, int nname) +{ + return netifwalk(&etherxx[chan->dev]->ni, chan, nchan, name, nname); +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + return netifstat(&etherxx[chan->dev]->ni, chan, dp, n); +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + return netifopen(&etherxx[chan->dev]->ni, chan, omode); +} + +static void +ethercreate(Chan* ch, char* c, int i, ulong ul) +{ +} + +static void +etherclose(Chan* chan) +{ + netifclose(&etherxx[chan->dev]->ni, chan); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->dev]; + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid) + return ether->ifstat(ether, buf, n, offset); + else if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + + return netifread(&ether->ni, chan, buf, n, offset); +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + return netifbread(&etherxx[chan->dev]->ni, chan, n, offset); +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + return netifwstat(&etherxx[chan->dev]->ni, chan, dp, n); +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->tscticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->ni.inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = &ether->ni.f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multicast addresses */ + if(multi && memcmp(pkt->d, ether->ni.bcast, sizeof(pkt->d)) != 0 && ether->ni.prom == 0){ + if(!activemulti(&ether->ni, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->ni.f; fp < ep; fp++){ + if((f = *fp) != nil) + if(f->type == type || f->type < 0) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if((xbp = iallocb(len)) != nil){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->ni.soverflows++; + } + else + ether->ni.soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->ni.soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->ni.outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->ni.bcast, sizeof(pkt->d)) == 0 || ether->ni.prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + } else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong v) +{ + Ether *ether; + Block *bp; + int nn, onoff; + Cmdbuf *cb; + + ether = etherxx[chan->dev]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(&ether->ni, chan, buf, n); + if(nn >= 0) + return nn; + cb = parsecmd(buf, n); + if(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + return n; + } + free(cb); + if(ether->ctl!=nil) + return ether->ctl(ether,buf,n); + + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + poperror(); + bp->wp += n; + + return etheroq(ether, bp); +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong u) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + + return etheroq(ether, bp); +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static Ether* +etherprobe(int cardno, int ctlrno) +{ + int i, lg; + ulong mb, bsz; + Ether *ether; + char buf[128], name[32]; + + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->tbdf = BUSUNKNOWN; + ether->ni.mbps = 100; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + + if(cardno < 0){ + for(cardno = 0; cards[cardno].type; cardno++){ + for(i = 0; i < ether->isac.nopt; i++){ + if(strncmp(ether->isac.opt[i], "ea=", 3)) + continue; + if(parseether(ether->ea, &ether->isac.opt[i][3])) + memset(ether->ea, 0, Eaddrlen); + } + break; + } + } + + if(cardno >= MaxEther || cards[cardno].type == nil){ + free(ether); + return nil; + } + if(cards[cardno].reset(ether) < 0){ + free(ether); + return nil; + } + + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(ether->isac.irq == 2) + ether->isac.irq = 9; + snprint(name, sizeof(name), "ether%d", ctlrno); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d", + ctlrno, cards[cardno].type, ether->ni.mbps, ether->isac.port, ether->isac.irq); + if(ether->isac.mem) + i += sprint(buf+i, " addr 0x%luX", ether->isac.mem); + if(ether->isac.size) + i += sprint(buf+i, " size 0x%luX", ether->isac.size); + i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + /* compute log10(ether->ni.mbps) into lg */ + for(lg = 0, mb = ether->ni.mbps; mb >= 10; lg++) + mb /= 10; + if (lg > 0) + lg--; + if (lg > 14) /* 2^(14+17) = 2⁳ⁱ */ + lg = 14; + /* allocate larger output queues for higher-speed interfaces */ + bsz = 1UL << (lg + 17); /* 2ⁱ⁷ = 128K, bsz = 2ⁿ × 128K */ + while (bsz > MEMSIZE && bsz >= 128*1024) + bsz /= 2; + + netifinit(&ether->ni, name, Ntypes, bsz); + while (ether->oq == nil && bsz >= 128*1024) { + bsz /= 2; + ether->oq = qopen(bsz, Qmsg, 0, 0); + ether->ni.limit = bsz; + } + if(ether->oq == nil) + panic("etherreset %s", name); + ether->ni.alen = Eaddrlen; + memmove(ether->ni.addr, ether->ea, Eaddrlen); + memset(ether->ni.bcast, 0xFF, Eaddrlen); + + return ether; +} + +static void +etherreset(void) +{ + Ether *ether; + int cardno, ctlrno; + + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if((ether = etherprobe(-1, ctlrno)) == nil) + continue; + etherxx[ctlrno] = ether; + } + + cardno = ctlrno = 0; + while(cards[cardno].type != nil && ctlrno < MaxEther){ + if(etherxx[ctlrno] != nil){ + ctlrno++; + continue; + } + if((ether = etherprobe(cardno, ctlrno)) == nil){ + cardno++; + continue; + } + etherxx[ctlrno] = ether; + ctlrno++; + } +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i = 0; i < MaxEther; i++){ + ether = etherxx[i]; + if(ether == nil) + continue; + if(ether->shutdown == nil) { + print("#l%d: no shutdown fuction\n", i); + continue; + } + (*ether->shutdown)(ether); + } +} + + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, +}; diff --git a/src/9vx/a/etherif.h b/src/9vx/a/etherif.h @@ -0,0 +1,39 @@ +enum { + MaxEther = 48, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + ISAConf isac; + + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + + Queue* oq; + + Netif ni; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern ulong ethercrc(uchar*, int); +extern int parseether(uchar*, char*); + +#define NEXT(x, l) (((uint)(x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) diff --git a/src/9vx/a/netif.c b/src/9vx/a/netif.c @@ -0,0 +1,761 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "netif.h" + +static int netown(Netfile*, char*, int); +static int openfile(Netif*, int); +static char* matchtoken(char*, char*); +static char* netmulti(Netif*, Netfile*, uchar*, int); +static int parseaddr(uchar*, char*, int); + +int netifdebug; +#define dprint(...) if(netifdebug)print(__VA_ARGS__); else USED(netifdebug) + +/* + * set up a new network interface + */ +void +netifinit(Netif *nif, char *name, int nfile, ulong limit) +{ + strncpy(nif->name, name, KNAMELEN-1); + nif->name[KNAMELEN-1] = 0; + nif->nfile = nfile; + nif->f = xalloc(nfile*sizeof(Netfile*)); + if (nif->f == nil) + panic("netifinit: no memory"); + nif->limit = limit; +} + +#define DD(c,q,nam,n,owner,perm,dp) dprint("%lux.%llux %s\n", q.type, q.path, nam); devdir(c,q,nam,n,owner,perm,dp) + +/* + * generate a 3 level directory + */ +static int +netifgen(Chan *c, char *dummy, Dirtab *vp, int dummy1, int i, Dir *dp) +{ + Qid q; + Netif *nif = (Netif*)vp; + Netfile *f; + int t, perm; + char *o; + + memset(&q, 0, sizeof q); + q.type = QTFILE; + q.vers = 0; + + dprint("gen %d %llud %.2d ", c->dri, c->qid.path, i); + /* top level directory contains the name of the network */ + if(c->qid.path == 0){ + switch(i){ + case DEVDOTDOT: + q.path = 0; + q.type = QTDIR; + DD(c, q, ".", 0, eve, 0555, dp); + break; + case 0: + q.path = N2ndqid; + q.type = QTDIR; + strcpy(up->genbuf, nif->name); + DD(c, q, up->genbuf, 0, eve, 0555, dp); + break; + default: + dprint("-> -1 (top)\n"); + return -1; + } + return 1; + } + + /* second level contains clone plus all the conversations */ + t = NETTYPE(c->qid.path); + if(t == N2ndqid || t == Ncloneqid || t == Naddrqid || t == Nstatqid || t == Nifstatqid){ + switch(i){ + case DEVDOTDOT: + q.type = QTDIR; + q.path = 0; + DD(c, q, ".", 0, eve, DMDIR|0555, dp); + break; + case 0: + q.path = Ncloneqid; + DD(c, q, "clone", 0, eve, 0666, dp); + break; + case 1: + q.path = Naddrqid; + DD(c, q, "addr", 0, eve, 0666, dp); + break; + case 2: + q.path = Nstatqid; + DD(c, q, "stats", 0, eve, 0444, dp); + break; + case 3: + q.path = Nifstatqid; + DD(c, q, "ifstats", 0, eve, 0444, dp); + break; + default: + i -= 4; + if(i >= nif->nfile){ + dprint("-> -1 (2d): %d %d\n", i, nif->nfile); + return -1; + } + if(nif->f[i] == 0){ + dprint("nif->f[%d] -> 0\n", i); + return 0; + } + q.type = QTDIR; + q.path = NETQID(i, N3rdqid); + sprint(up->genbuf, "%d", i); + DD(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); + break; + } + return 1; + } + + /* third level */ + f = nif->f[NETID(c->qid.path)]; + if(f == 0){ + dprint("->f 0\n"); + return -1; + } + if(*f->owner){ + o = f->owner; + perm = f->mode; + } else { + o = eve; + perm = 0666; + } + switch(i){ + case DEVDOTDOT: + q.type = QTDIR; + q.path = N2ndqid; + strcpy(up->genbuf, nif->name); + DD(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); + break; + case 0: + q.path = NETQID(NETID(c->qid.path), Ndataqid); + DD(c, q, "data", 0, o, perm, dp); + break; + case 1: + q.path = NETQID(NETID(c->qid.path), Nctlqid); + DD(c, q, "ctl", 0, o, perm, dp); + break; + case 2: + q.path = NETQID(NETID(c->qid.path), Nstatqid); + DD(c, q, "stats", 0, eve, 0444, dp); + break; + case 3: + q.path = NETQID(NETID(c->qid.path), Ntypeqid); + DD(c, q, "type", 0, eve, 0444, dp); + break; + case 4: + q.path = NETQID(NETID(c->qid.path), Nifstatqid); + DD(c, q, "ifstats", 0, eve, 0444, dp); + break; + default: + dprint("-> -1 (third)\n"); + return -1; + } + return 1; +} + +static void +prwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname) +{ + char buf[512], *e, *p; + + if(netifdebug == 0) + return; + p = buf; + e = p + sizeof buf; + for(int i = 0; i < nname; i++) + p = seprint(p, e, "%s ", name[i]); + if(p > buf) + p--; + *p = 0; + print("netifwalk %lld [%s]\n", c->qid.path, buf); +} + +Walkqid* +netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname) +{ + prwalk(nif, c, nc, name, nname); + return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen); +} + +Chan* +netifopen(Netif *nif, Chan *c, int omode) +{ + int id; + Netfile *f; + + dprint("netifopen %p %d\n", nif, c? c->qid.path: -1); + id = 0; + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else { + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + id = NETID(c->qid.path); + openfile(nif, id); + break; + case Ncloneqid: + id = openfile(nif, -1); + c->qid.path = NETQID(id, Nctlqid); + break; + default: + if(omode != OREAD) + error(Ebadarg); + } + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + f = nif->f[id]; + if(netown(f, up->user, omode&7) < 0) + error(Eperm); + break; + } + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +long +netifread(Netif *nif, Chan *c, void *a, long n, ulong offset) +{ + int i, j; + Netfile *f; + char *p; + + dprint("netifread %lud %lud\n", c->qid.path, NETTYPE(c->qid.path)); + if(c->qid.type&QTDIR) + return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + f = nif->f[NETID(c->qid.path)]; + return qread(f->in, a, n); + case Nctlqid: + return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + dprint("netstatqid\n"); + p = smalloc(READSTR); + j = snprint(p, READSTR, "in: %llud\n", nif->inpackets); + j += snprint(p+j, READSTR-j, "link: %d\n", nif->link); + j += snprint(p+j, READSTR-j, "out: %llud\n", nif->outpackets); + j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs); + j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows); + j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows); + j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames); + j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs); + j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs); + j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom); + j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps); + j += snprint(p+j, READSTR-j, "addr: "); + for(i = 0; i < nif->alen; i++) + j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); + snprint(p+j, READSTR-j, "\n"); + n = readstr(offset, a, n, p); + free(p); + return n; + case Naddrqid: + p = malloc(READSTR); + j = 0; + for(i = 0; i < nif->alen; i++) + j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); + n = readstr(offset, a, n, p); + free(p); + return n; + case Ntypeqid: + f = nif->f[NETID(c->qid.path)]; + return readnum(offset, a, n, f->type, NUMSIZE); + case Nifstatqid: + return 0; + } + error(Ebadarg); + return -1; /* not reached */ +} + +Block* +netifbread(Netif *nif, Chan *c, long n, ulong offset) +{ + if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid) + return devbread(c, n, offset); + + return qbread(nif->f[NETID(c->qid.path)]->in, n); +} + +/* + * make sure this type isn't already in use on this device + */ +static int +typeinuse(Netif *nif, int type) +{ + Netfile *f, **fp, **efp; + + if(type <= 0) + return 0; + + efp = &nif->f[nif->nfile]; + for(fp = nif->f; fp < efp; fp++){ + f = *fp; + if(f == 0) + continue; + if(f->type == type) + return 1; + } + return 0; +} + +/* + * the devxxx.c that calls us handles writing data, it knows best + */ +long +netifwrite(Netif *nif, Chan *c, void *a, long n) +{ + Netfile *f; + int type; + char *p, buf[64]; + uchar binaddr[Nmaxaddr]; + + if(NETTYPE(c->qid.path) != Nctlqid) + error(Eperm); + + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = 0; + + if(waserror()){ + QUNLOCK(nif); + nexterror(); + } + + QLOCK(nif); + f = nif->f[NETID(c->qid.path)]; + if((p = matchtoken(buf, "connect")) != 0){ + type = atoi(p); + if(typeinuse(nif, type)) + error(Einuse); + f->type = type; + if(f->type < 0) + nif->all++; + } else if(matchtoken(buf, "promiscuous")){ + if(f->prom == 0){ + if(nif->prom == 0 && nif->promiscuous != nil) + nif->promiscuous(nif->arg, 1); + f->prom = 1; + nif->prom++; + } + } else if((p = matchtoken(buf, "scanbs")) != 0){ + /* scan for base stations */ + if(f->scan == 0){ + type = atoi(p); + if(type < 5) + type = 5; + if(nif->scanbs != nil) + nif->scanbs(nif->arg, type); + f->scan = type; + nif->scan++; + } + } else if(matchtoken(buf, "bridge")){ + f->bridge = 1; + } else if(matchtoken(buf, "headersonly")){ + f->headersonly = 1; + } else if((p = matchtoken(buf, "addmulti")) != 0){ + if(parseaddr(binaddr, p, nif->alen) < 0) + error("bad address"); + p = netmulti(nif, f, binaddr, 1); + if(p) + error(p); + } else if((p = matchtoken(buf, "remmulti")) != 0){ + if(parseaddr(binaddr, p, nif->alen) < 0) + error("bad address"); + p = netmulti(nif, f, binaddr, 0); + if(p) + error(p); + } else + n = -1; + QUNLOCK(nif); + poperror(); + return n; +} + +int +netifwstat(Netif *nif, Chan *c, uchar *db, int n) +{ + Dir *dir; + Netfile *f; + int m; + + f = nif->f[NETID(c->qid.path)]; + if(f == 0) + error(Enonexist); + + if(netown(f, up->user, OWRITE) < 0) + error(Eperm); + + dir = smalloc(sizeof(Dir)+n); + m = convM2D(db, n, &dir[0], (char*)&dir[1]); + if(m == 0){ + free(dir); + error(Eshortstat); + } + if(!emptystr(dir[0].uid)) + strncpy(f->owner, dir[0].uid, KNAMELEN); + if(dir[0].mode != ~0UL) + f->mode = dir[0].mode; + free(dir); + return m; +} + +int +netifstat(Netif *nif, Chan *c, uchar *db, int n) +{ + dprint("netifstat %s nfile %d %lld type=%d\n", nif->name, nif->nfile, c->qid.path, c->type); + return devstat(c, db, n, (Dirtab *)nif, 0, netifgen); +} + +void +netifclose(Netif *nif, Chan *c) +{ + Netfile *f; + int t; + Netaddr *ap; + + if((c->flag & COPEN) == 0) + return; + + t = NETTYPE(c->qid.path); + if(t != Ndataqid && t != Nctlqid) + return; + + f = nif->f[NETID(c->qid.path)]; + QLOCK(f); + if(--(f->inuse) == 0){ + if(f->prom){ + QLOCK(nif); + if(--(nif->prom) == 0 && nif->promiscuous != nil) + nif->promiscuous(nif->arg, 0); + QUNLOCK(nif); + f->prom = 0; + } + if(f->scan){ + QLOCK(nif); + if(--(nif->scan) == 0 && nif->scanbs != nil) + nif->scanbs(nif->arg, 0); + QUNLOCK(nif); + f->prom = 0; + f->scan = 0; + } + if(f->nmaddr){ + QLOCK(nif); + t = 0; + for(ap = nif->maddr; ap; ap = ap->next){ + if(f->maddr[t/8] & (1<<(t%8))) + netmulti(nif, f, ap->addr, 0); + } + QUNLOCK(nif); + f->nmaddr = 0; + } + if(f->type < 0){ + QLOCK(nif); + --(nif->all); + QUNLOCK(nif); + } + f->owner[0] = 0; +print("drop type %.4ux\n", f->type); + f->type = 0; + f->bridge = 0; + f->headersonly = 0; + qclose(f->in); + } + QUNLOCK(f); +} + +Lock netlock; + +static int +netown(Netfile *p, char *o, int omode) +{ + static int access[] = { 0400, 0200, 0600, 0100 }; + int mode; + int t; + + lock(&netlock); + if(*p->owner){ + if(strncmp(o, p->owner, KNAMELEN) == 0) /* User */ + mode = p->mode; + else if(strncmp(o, eve, KNAMELEN) == 0) /* Bootes is group */ + mode = p->mode<<3; + else + mode = p->mode<<6; /* Other */ + + t = access[omode&3]; + if((t & mode) == t){ + unlock(&netlock); + return 0; + } else { + unlock(&netlock); + return -1; + } + } + strncpy(p->owner, o, KNAMELEN); + p->mode = 0660; + unlock(&netlock); + return 0; +} + +/* + * Increment the reference count of a network device. + * If id < 0, return an unused ether device. + */ +static int +openfile(Netif *nif, int id) +{ + Netfile *f, **fp, **efp; + + if(id >= 0){ + f = nif->f[id]; + if(f == 0) + error(Enodev); + QLOCK(f); + qreopen(f->in); + f->inuse++; + QUNLOCK(f); + return id; + } + + QLOCK(nif); + if(waserror()){ + QUNLOCK(nif); + nexterror(); + } + efp = &nif->f[nif->nfile]; + for(fp = nif->f; fp < efp; fp++){ + f = *fp; + if(f == 0){ + f = malloc(sizeof(Netfile)); + if(f == 0) + exhausted("memory"); + f->in = qopen(nif->limit, Qmsg, 0, 0); + if(f->in == nil){ + free(f); + exhausted("memory"); + } + *fp = f; + QLOCK(f); + } else { + QLOCK(f); + if(f->inuse){ + QUNLOCK(f); + continue; + } + } + f->inuse = 1; + qreopen(f->in); + netown(f, up->user, 0); + QUNLOCK(f); + QUNLOCK(nif); + poperror(); + return fp - nif->f; + } + error(Enodev); + return -1; /* not reached */ +} + +/* + * look for a token starting a string, + * return a pointer to first non-space char after it + */ +static char* +matchtoken(char *p, char *token) +{ + int n; + + n = strlen(token); + if(strncmp(p, token, n)) + return 0; + p += n; + if(*p == 0) + return p; + if(*p != ' ' && *p != '\t' && *p != '\n') + return 0; + while(*p == ' ' || *p == '\t' || *p == '\n') + p++; + return p; +} + +void +hnputv(void *p, uvlong v) +{ + uchar *a; + + a = p; + hnputl(a, v>>32); + hnputl(a+4, v); +} + +void +hnputl(void *p, uint v) +{ + uchar *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +void +hnputs(void *p, ushort v) +{ + uchar *a; + + a = p; + a[0] = v>>8; + a[1] = v; +} + +uvlong +nhgetv(void *p) +{ + uchar *a; + + a = p; + return ((vlong)nhgetl(a) << 32) | nhgetl(a+4); +} + +uint +nhgetl(void *p) +{ + uchar *a; + + a = p; + return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); +} + +ushort +nhgets(void *p) +{ + uchar *a; + + a = p; + return (a[0]<<8)|(a[1]<<0); +} + +static ulong +hash(uchar *a, int len) +{ + ulong sum = 0; + + while(len-- > 0) + sum = (sum << 1) + *a++; + return sum%Nmhash; +} + +int +activemulti(Netif *nif, uchar *addr, int alen) +{ + Netaddr *hp; + + for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext) + if(memcmp(addr, hp->addr, alen) == 0){ + if(hp->ref) + return 1; + else + break; + } + return 0; +} + +static int +parseaddr(uchar *to, char *from, int alen) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < alen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +/* + * keep track of multicast addresses + */ +static char* +netmulti(Netif *nif, Netfile *f, uchar *addr, int add) +{ + Netaddr **l, *ap; + int i; + ulong h; + + if(nif->multicast == nil) + return "interface does not support multicast"; + + l = &nif->maddr; + i = 0; + for(ap = *l; ap; ap = *l){ + if(memcmp(addr, ap->addr, nif->alen) == 0) + break; + i++; + l = &ap->next; + } + + if(add){ + if(ap == 0){ + *l = ap = smalloc(sizeof(*ap)); + memmove(ap->addr, addr, nif->alen); + ap->next = 0; + ap->ref = 1; + h = hash(addr, nif->alen); + ap->hnext = nif->mhash[h]; + nif->mhash[h] = ap; + } else { + ap->ref++; + } + if(ap->ref == 1){ + nif->nmaddr++; + nif->multicast(nif->arg, addr, 1); + } + if(i < 8*sizeof(f->maddr)){ + if((f->maddr[i/8] & (1<<(i%8))) == 0) + f->nmaddr++; + f->maddr[i/8] |= 1<<(i%8); + } + } else { + if(ap == 0 || ap->ref == 0) + return 0; + ap->ref--; + if(ap->ref == 0){ + nif->nmaddr--; + nif->multicast(nif->arg, addr, 0); + } + if(i < 8*sizeof(f->maddr)){ + if((f->maddr[i/8] & (1<<(i%8))) != 0) + f->nmaddr--; + f->maddr[i/8] &= ~(1<<(i%8)); + } + } + return 0; +} diff --git a/src/9vx/a/sdaoe.c b/src/9vx/a/sdaoe.c @@ -0,0 +1,652 @@ +/* + * aoe sd driver, copyright © 2007 coraid + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "error.h" +#include "sd.h" +#include "netif.h" +#include "aoe.h" + +extern char Echange[]; +extern char Enotup[]; + +#define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); + +enum { + Nctlr = 32, + Maxpath = 128, +}; + +enum { + /* sync with ahci.h */ + Dllba = 1<<0, + Dsmart = 1<<1, + Dpower = 1<<2, + Dnop = 1<<3, + Datapi = 1<<4, + Datapi16= 1<<5, +}; + +static char *flagname[] = { + "llba", + "smart", + "power", + "nop", + "atapi", + "atapi16", +}; + +typedef struct Ctlr Ctlr; +struct Ctlr{ + QLock qlock; + + Ctlr *next; + SDunit *unit; + + char path[Maxpath]; + Chan *c; + + ulong vers; + uchar mediachange; + uchar flag; + uchar smart; + uchar smartrs; + uchar feat; + + uvlong sectors; + char serial[20+1]; + char firmware[8+1]; + char model[40+1]; + char ident[0x100]; +}; + +static Lock ctlrlock; +static Ctlr *head; +static Ctlr *tail; + +SDifc sdaoeifc; + +static void +idmove(char *p, ushort *a, int n) +{ + int i; + char *op, *e; + + op = p; + for(i = 0; i < n/2; i++){ + *p++ = a[i] >> 8; + *p++ = a[i]; + } + *p = 0; + while(p > op && *--p == ' ') + *p = 0; + e = p; + p = op; + while(*p == ' ') + p++; + memmove(op, p, n - (e - p)); +} + +static ushort +gbit16(void *a) +{ + uchar *i; + + i = a; + return i[1] << 8 | i[0]; +} + +static ulong +gbit32(void *a) +{ + ulong j; + uchar *i; + + i = a; + j = i[3] << 24; + j |= i[2] << 16; + j |= i[1] << 8; + j |= i[0]; + return j; +} + +static uvlong +gbit64(void *a) +{ + uchar *i; + + i = a; + return (uvlong)gbit32(i+4)<<32 | gbit32(i); +} + +static int +identify(Ctlr *c, ushort *id) +{ + int i; + uchar oserial[21]; + uvlong osectors, s; + + osectors = c->sectors; + memmove(oserial, c->serial, sizeof c->serial); + + c->feat &= ~(Dllba|Dpower|Dsmart|Dnop); + i = gbit16(id+83) | gbit16(id+86); + if(i & (1<<10)){ + c->feat |= Dllba; + s = gbit64(id+100); + }else + s = gbit32(id+60); + + i = gbit16(id+83); + if((i>>14) == 1) { + if(i & (1<<3)) + c->feat |= Dpower; + i = gbit16(id+82); + if(i & 1) + c->feat |= Dsmart; + if(i & (1<<14)) + c->feat |= Dnop; + } + + idmove(c->serial, id+10, 20); + idmove(c->firmware, id+23, 8); + idmove(c->model, id+27, 40); + + if((osectors == 0 || osectors != s) && + memcmp(oserial, c->serial, sizeof oserial) != 0){ + c->sectors = s; + c->mediachange = 1; + c->vers++; + } + return 0; +} + +/* must call with d qlocked */ +static int +aoeidentify(Ctlr *d, SDunit *u) +{ + Chan *c; + + c = nil; + if(waserror()){ + if(c) + cclose(c); + iprint("aoeidentify: %s\n", up->errstr); + nexterror(); + } + + uprint("%s/ident", d->path); + c = namec(up->genbuf, Aopen, OREAD, 0); + devtab[c->type]->read(c, d->ident, sizeof d->ident, 0); + + poperror(); + cclose(c); + + d->feat = 0; + d->smart = 0; + identify(d, (ushort*)d->ident); + + memset(u->inquiry, 0, sizeof u->inquiry); + u->inquiry[2] = 2; + u->inquiry[3] = 2; + u->inquiry[4] = sizeof u->inquiry - 4; + memmove(u->inquiry+8, d->model, 40); + + return 0; +} + +static Ctlr* +ctlrlookup(char *path) +{ + Ctlr *c; + + lock(&ctlrlock); + for(c = head; c; c = c->next) + if(strcmp(c->path, path) == 0) + break; + unlock(&ctlrlock); + return c; +} + +static Ctlr* +newctlr(char *path) +{ + Ctlr *c; + + /* race? */ + if(ctlrlookup(path)) + error(Eexist); + + if((c = malloc(sizeof *c)) == nil) + return 0; + kstrcpy(c->path, path, sizeof c->path); + lock(&ctlrlock); + if(head != nil) + tail->next = c; + else + head = c; + tail = c; + unlock(&ctlrlock); + return c; +} + +static void +delctlr(Ctlr *c) +{ + Ctlr *x, *prev; + + lock(&ctlrlock); + + for(prev = 0, x = head; x; prev = x, x = c->next) + if(strcmp(c->path, x->path) == 0) + break; + if(x == 0){ + unlock(&ctlrlock); + error(Enonexist); + } + + if(prev) + prev->next = x->next; + else + head = x->next; + if(x->next == nil) + tail = prev; + unlock(&ctlrlock); + + if(x->c) + cclose(x->c); + free(x); +} + +static SDev* +aoeprobe(char *path, SDev *s) +{ + int n, i; + char *p; + Chan *c; + Ctlr *ctlr; + + if((p = strrchr(path, '/')) == 0) + error(Ebadarg); + *p = 0; + uprint("%s/ctl", path); + *p = '/'; + + c = namec(up->genbuf, Aopen, OWRITE, 0); + if(waserror()) { + cclose(c); + nexterror(); + } + n = uprint("discover %s", p+1); + devtab[c->type]->write(c, up->genbuf, n, 0); + poperror(); + cclose(c); + + for(i = 0;; i += 200){ + if(i > 8000 || waserror()) + error(Etimedout); + tsleep(&up->sleep, return0, 0, 200); + poperror(); + + uprint("%s/ident", path); + if(waserror()) + continue; + c = namec(up->genbuf, Aopen, OREAD, 0); + poperror(); + cclose(c); + + ctlr = newctlr(path); + break; + } + + if(s == nil && (s = malloc(sizeof *s)) == nil) + return nil; + s->ctlr = ctlr; + s->ifc = &sdaoeifc; + s->nunit = 1; + return s; +} + +static char *probef[32]; +static int nprobe; + +static int +pnpprobeid(char *s) +{ + int id; + + if(strlen(s) < 2) + return 0; + id = 'e'; + if(s[1] == '!') + id = s[0]; + return id; +} + +static SDev* +aoepnp(void) +{ + int i, id; + char *p; + SDev *h, *t, *s; + +// if((p = getconf("aoedev")) == 0) + if(1) + return 0; + nprobe = tokenize(p, probef, nelem(probef)); + h = t = 0; + for(i = 0; i < nprobe; i++){ + id = pnpprobeid(probef[i]); + if(id == 0) + continue; + s = malloc(sizeof *s); + if(s == nil) + break; + s->ctlr = 0; + s->idno = id; + s->ifc = &sdaoeifc; + s->nunit = 1; + + if(h) + t->next = s; + else + h = s; + t = s; + } + return h; +} + +static Ctlr* +pnpprobe(SDev *sd) +{ + int j; + char *p; + static int i; + + if(i > nprobe) + return 0; + p = probef[i++]; + if(strlen(p) < 2) + return 0; + if(p[1] == '!') + p += 2; + + for(j = 0;; j += 200){ + if(j > 8000){ + print("#æ: pnpprobe: %s: %s\n", probef[i-1], up->errstr); + return 0; + } + if(waserror()){ + tsleep(&up->sleep, return0, 0, 200); + continue; + } + sd = aoeprobe(p, sd); + poperror(); + break; + } + print("#æ: pnpprobe establishes %sin %dms\n", probef[i-1], j); + return sd->ctlr; +} + + +static int +aoeverify(SDunit *u) +{ + SDev *s; + Ctlr *c; + + s = u->dev; + c = s->ctlr; + if(c == nil && (s->ctlr = c = pnpprobe(s)) == nil) + return 0; + c->mediachange = 1; + return 1; +} + +static int +aoeconnect(SDunit *u, Ctlr *c) +{ + QLOCK(c); + if(waserror()){ + QUNLOCK(c); + return -1; + } + + aoeidentify(u->dev->ctlr, u); + if(c->c) + cclose(c->c); + c->c = 0; + uprint("%s/data", c->path); + c->c = namec(up->genbuf, Aopen, ORDWR, 0); + QUNLOCK(c); + poperror(); + + return 0; +} + +static int +aoeonline(SDunit *u) +{ + Ctlr *c; + int r; + + c = u->dev->ctlr; + r = 0; + + if((c->feat&Datapi) && c->mediachange){ + if(aoeconnect(u, c) == 0 && (r = scsionline(u)) > 0) + c->mediachange = 0; + return r; + } + + if(c->mediachange){ + if(aoeconnect(u, c) == -1) + return 0; + r = 2; + c->mediachange = 0; + u->sectors = c->sectors; + u->secsize = Aoesectsz; + } else + r = 1; + + return r; +} + +static int +aoerio(SDreq *r) +{ + int i, count; + uvlong lba; + char *name; + uchar *cmd; + long (*rio)(Chan*, void*, long, vlong); + Ctlr *c; + SDunit *unit; + + unit = r->unit; + c = unit->dev->ctlr; +// if(c->feat & Datapi) +// return aoeriopkt(r, d); + + cmd = r->cmd; + name = unit->perm.name; + + if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){ +// QLOCK(c); +// i = flushcache(); +// QUNLOCK(c); +// if(i == 0) +// return sdsetsense(r, SDok, 0, 0, 0); + return sdsetsense(r, SDcheck, 3, 0xc, 2); + } + + if((i = sdfakescsi(r, c->ident, sizeof c->ident)) != SDnostatus){ + r->status = i; + return i; + } + + switch(*cmd){ + case 0x88: + case 0x28: + rio = devtab[c->c->type]->read; + break; + case 0x8a: + case 0x2a: + rio = devtab[c->c->type]->write; + break; + default: + print("%s: bad cmd %#.2ux\n", name, cmd[0]); + r->status = SDcheck; + return SDcheck; + } + + if(r->data == nil) + return SDok; + + if(r->clen == 16){ + if(cmd[2] || cmd[3]) + return sdsetsense(r, SDcheck, 3, 0xc, 2); + lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32; + lba |= cmd[6]<<24 | cmd[7]<<16 | cmd[8]<<8 | cmd[9]; + count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13]; + }else{ + lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5]; + count = cmd[7]<<8 | cmd[8]; + } + + count *= Aoesectsz; + + if(r->dlen < count) + count = r->dlen & ~0x1ff; + + if(waserror()){ + if(strcmp(up->errstr, Echange) == 0 || + strcmp(up->errstr, Enotup) == 0) + unit->sectors = 0; + nexterror(); + } + r->rlen = rio(c->c, r->data, count, Aoesectsz * lba); + poperror(); + r->status = SDok; + return SDok; +} + +static char *smarttab[] = { + "unset", + "error", + "threshold exceeded", + "normal" +}; + +static char * +pflag(char *s, char *e, uchar f) +{ + uchar i, m; + + for(i = 0; i < 8; i++){ + m = 1 << i; + if(f & m) + s = seprint(s, e, "%s ", flagname[i]); + } + return seprint(s, e, "\n"); +} + +static int +aoerctl(SDunit *u, char *p, int l) +{ + Ctlr *c; + char *e, *op; + + if((c = u->dev->ctlr) == nil) + return 0; + e = p+l; + op = p; + + p = seprint(p, e, "model\t%s\n", c->model); + p = seprint(p, e, "serial\t%s\n", c->serial); + p = seprint(p, e, "firm %s\n", c->firmware); + if(c->smartrs == 0xff) + p = seprint(p, e, "smart\tenable error\n"); + else if(c->smartrs == 0) + p = seprint(p, e, "smart\tdisabled\n"); + else + p = seprint(p, e, "smart\t%s\n", smarttab[c->smart]); + p = seprint(p, e, "flag "); + p = pflag(p, e, c->feat); + p = seprint(p, e, "geometry %llud %d\n", c->sectors, Aoesectsz); + return p-op; +} + +static int +aoewctl(SDunit *d1, Cmdbuf *cmd) +{ + cmderror(cmd, Ebadarg); + return 0; +} + +static SDev* +aoeprobew(DevConf *c) +{ + char *p; + + p = strchr(c->type, '/'); + if(p == nil || strlen(p) > Maxpath - 11) + error(Ebadarg); + if(p[1] == '#') + p++; /* hack */ + if(ctlrlookup(p)) + error(Einuse); + return aoeprobe(p, 0); +} + +static void +aoeclear(SDev *s) +{ + delctlr((Ctlr *)s->ctlr); +} + +static char* +aoertopctl(SDev *s, char *p, char *e) +{ + Ctlr *c; + + c = s->ctlr; + return seprint(p, e, "%s aoe %s\n", s->name, c->path); +} + +static int +aoewtopctl(SDev *d1, Cmdbuf *cmd) +{ + switch(cmd->nf){ + default: + cmderror(cmd, Ebadarg); + } + return 0; +} + +SDifc sdaoeifc = { + "aoe", + + aoepnp, + nil, /* legacy */ + nil, /* enable */ + nil, /* disable */ + + aoeverify, + aoeonline, + aoerio, + aoerctl, + aoewctl, + + scsibio, + aoeprobew, /* probe */ + aoeclear, /* clear */ + aoertopctl, + aoewtopctl, +}; diff --git a/src/9vx/etherve.c b/src/9vx/etherve.c @@ -0,0 +1,161 @@ +/* + * etherve - portable Virtual Ethernet driver for 9vx. + * + * Copyright (c) 2008 Devon H. O'Dell + * copyright © 2008 erik quanstrom + * + * Released under 2-clause BSD license. + */ + +#include "u.h" + +#include "a/lib.h" +#include "a/mem.h" +#include "a/dat.h" +#include "a/fns.h" +#include "a/io.h" +#include "a/error.h" +#include "a/netif.h" + +#include "a/etherif.h" + +#include <pcap.h> + +extern char *netdev; +static uvlong txerrs; + +typedef struct Ctlr Ctlr; +struct Ctlr { + pcap_t *pd; +}; + +static pcap_t * +setup(void) +{ + char *filter = "ether dst 00:48:01:23:45:67"; /* XXX */ + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *pd; + struct bpf_program prog; + bpf_u_int32 net; + bpf_u_int32 mask; + + if (!netdev) + netdev = "en1"; /* XXX */ + + if ((pd = pcap_open_live(netdev, 1514, 1, 1, errbuf)) == nil) + return nil; + + pcap_lookupnet(netdev, &net, &mask, errbuf); + pcap_compile(pd, &prog, filter, 0, net); + + if (pcap_setfilter(pd, &prog) == -1) + return nil; + + pcap_freecode(&prog); + + return pd; +} + +static Block * +vepkt(Ctlr *c) +{ + struct pcap_pkthdr hdr; + Block *b; + + if (hdr.caplen) { + b = allocb(1514); + while ((b->rp = pcap_next(c->pd, &hdr)) == nil) ; + + b->wp += hdr.caplen; + + // iprint("Got packet len %d\n", hdr.caplen); + + return b; + } + + return nil; +} + +static void +verecvkproc(void *v) +{ + Ether *e; + Block *b; + + e = v; + while (b = vepkt(e->ctlr)) + if (b != nil) + etheriq(e, b, 1); +} + +static void +vetransmit(Ether* e) +{ + const u_char *u; + Block *b; + Ctlr *c; + + c = e->ctlr; + while ((b = qget(e->oq)) != nil) { + int wlen; + + u = (const u_char*)b->rp; + + wlen = pcap_inject(c->pd, u, BLEN(b)); + // iprint("injected packet len %d\n", wlen); + if (wlen == -1) + txerrs++; + + freeb(b); + } +} + +static long +veifstat(Ether *e, void *a, long n, ulong offset) +{ + char buf[128]; + + snprint(buf, sizeof buf, "txerrors: %lud\n", txerrs); + return readstr(offset, a, n, buf); +} + +static void +veattach(Ether* e) +{ + kproc("verecv", verecvkproc, e); +} + +static uchar ea[6] = {0x00, 0x48, 0x01, 0x23, 0x45, 0x67}; + +static int +vepnp(Ether* e) +{ + Ctlr c; + static int nctlr = 0; + + if (nctlr++ > 0) + return -1; + + memset(&c, 0, sizeof(c)); + c.pd = setup(); + if (c.pd == nil) { + iprint("ve: pcap failed to initialize\n"); + return -1; + } + e->ctlr = malloc(sizeof(c)); + memcpy(e->ctlr, &c, sizeof(c)); + e->tbdf = BUSUNKNOWN; + memcpy(e->ea, ea, sizeof(ea)); + e->attach = veattach; + e->transmit = vetransmit; + e->ifstat = veifstat; + e->ni.arg = e; + e->ni.link = 1; + return 0; +} + +void +ethervelink(void) +{ + addethercard("ve", vepnp); +}