]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devaoe.c
devshr: fixed crash
[plan9front.git] / sys / src / 9 / port / devaoe.c
1 /*
2  *      © 2005-10 coraid
3  *      aoe storage initiator
4  */
5
6 #include "u.h"
7 #include "../port/lib.h"
8 #include "mem.h"
9 #include "dat.h"
10 #include "fns.h"
11 #include "io.h"
12 #include "ureg.h"
13 #include "../port/error.h"
14 #include "../port/netif.h"
15 #include "etherif.h"
16 #include "../ip/ip.h"
17 #include "../port/aoe.h"
18 #include <fis.h>
19
20 #pragma varargck argpos eventlog        1
21
22 #define dprint(...)     if(debug) eventlog(__VA_ARGS__); else USED(debug);
23 #define uprint(...)     snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
24
25 enum {
26         Maxunits        = 0xff,
27         Maxframes       = 128,
28         Maxmtu          = 100000,
29         Ndevlink        = 6,
30         Nea             = 6,
31         Nnetlink        = 6,
32 };
33
34 #define TYPE(q)         ((ulong)(q).path & 0xf)
35 #define UNIT(q)         (((ulong)(q).path>>4) & 0xff)
36 #define L(q)            (((ulong)(q).path>>12) & 0xf)
37 #define QID(u, t)       ((u)<<4 | (t))
38 #define Q3(l, u, t)     ((l)<<8 | QID(u, t))
39 #define UP(d)           ((d)->flag & Dup)
40
41 #define Ticks           MACHP(0)->ticks
42 #define Ms2tk(t)        (((t)*HZ)/1000)
43 #define Tk2ms(t)        (((t)*1000)/HZ)
44
45 enum {
46         Qzero,
47         Qtopdir         = 1,
48         Qtopbase,
49         Qtopctl         = Qtopbase,
50         Qtoplog,
51         Qtopend,
52
53         Qunitdir,
54         Qunitbase,
55         Qctl            = Qunitbase,
56         Qdata,
57         Qconfig,
58         Qident,
59
60         Qdevlinkdir,
61         Qdevlinkbase,
62         Qdevlink        = Qdevlinkbase,
63         Qdevlinkend,
64
65         Qtopfiles       = Qtopend-Qtopbase,
66         Qdevlinkfiles   = Qdevlinkend-Qdevlinkbase,
67
68         Eventlen        = 256,
69         Nevents         = 64,
70
71         Fread           = 0,
72         Fwrite,
73         Tfree           = -1,
74         Tmgmt,
75
76         /* round trip bounds, timeouts, in ticks */
77         Rtmax           = Ms2tk(320),
78         Rtmin           = Ms2tk(20),
79         Srbtimeout      = 45*HZ,
80
81         Dbcnt           = 1024,
82
83         Crd             = 0x20,
84         Crdext          = 0x24,
85         Cwr             = 0x30,
86         Cwrext          = 0x34,
87         Cid             = 0xec,
88
89         Alloc           = 0x01234567,
90         Free            = 0x89abcdef,
91 };
92
93 enum {
94         Read,
95         Write,
96 };
97
98 /*
99  * unified set of flags
100  * a Netlink + Aoedev most both be jumbo capable
101  * to send jumbograms to that interface.
102  */
103 enum {
104         Dup     = 1<<0,
105         Djumbo  = 1<<1,
106         Dnofail = 1<<2,
107 };
108
109 static char *flagname[] = {
110         "up",
111         "jumbo",
112         "nofail",
113 };
114
115 typedef struct {
116         uchar   flag;
117         uint    lostjumbo;
118
119         Chan    *cc;
120         Chan    *dc;
121         Chan    *mtu;           /* open early to prevent bind issues. */
122         char    path[Maxpath];
123         uchar   ea[Eaddrlen];
124 } Netlink;
125
126 typedef struct {
127         Netlink *nl;
128         int     nea;
129         ulong   eaidx;
130         uchar   eatab[Nea][Eaddrlen];
131         int     datamtu;
132         ulong   npkt;
133         ulong   resent;
134         uchar   flag;
135
136         ulong   rttavg;
137         ulong   mintimer;
138 } Devlink;
139
140 typedef struct Srb Srb;
141 struct Srb {
142         Rendez;
143         uint    state;
144         Srb     *next;
145         ulong   ticksent;
146         ulong   len;
147         vlong   sector;
148         short   write;
149         short   nout;
150         char    *error;
151         void    *dp;
152         void    *data;
153 };
154
155 typedef struct {
156         int     tag;
157         ulong   bcnt;
158         ulong   dlen;
159         vlong   lba;
160         ulong   ticksent;
161         int     nhdr;
162         uchar   hdr[ETHERMINTU];
163         void    *dp;
164         Devlink *dl;
165         Netlink *nl;
166         int     eaidx;
167         Srb     *srb;
168 } Frame;
169
170 typedef struct Aoedev Aoedev;
171 struct Aoedev {
172         QLock;
173         Aoedev  *next;
174
175         ulong   vers;
176
177         int     ndl;
178         ulong   dlidx;
179         Devlink *dl;
180         Devlink dltab[Ndevlink];
181
182         uchar   flag;
183         ushort  fwver;
184         int     nopen;
185         uint    major;
186         uint    minor;
187         int     unit;
188         int     lasttag;
189         int     nframes;
190         Frame   *frames;
191         vlong   bsize;
192         vlong   realbsize;
193
194         uint    maxbcnt;
195         uint    maxmtu;
196         ulong   lostjumbo;
197         ushort  nout;
198         ushort  maxout;
199         ulong   lastwadj;
200         Srb     *head;
201         Srb     *tail;
202         Srb     *inprocess;
203
204         Sfis;
205         char    serial[20+1];
206         char    firmware[8+1];
207         char    model[40+1];
208         int     nconfig;
209         uchar   config[1024];
210         uchar   ident[512];
211 };
212
213 #pragma varargck type   "æ"    Aoedev*
214
215 static struct {
216         Lock;
217         QLock;
218         Rendez;
219         char    buf[Eventlen*Nevents];
220         char    *rp;
221         char    *wp;
222 } events;
223
224 static struct {
225         RWlock;
226         int     nd;
227         Aoedev  *d;
228 } devs;
229
230 static struct {
231         Lock;
232         int     reader[Nnetlink];       /* reader is running. */
233         Rendez  rendez[Nnetlink];       /* confirm exit. */
234         Netlink nl[Nnetlink];
235 } netlinks;
236
237 extern  Dev     aoedevtab;
238 static  Ref     units;
239 static  Ref     drivevers;
240 static  int     debug;
241 static  int     autodiscover    = 1;
242 static  int     rediscover;
243 extern  char    Enotup[]        = "aoe device is down";
244 extern  char    Echange[]       = "media or partition has changed";
245
246 static Srb*
247 srballoc(ulong sz)
248 {
249         Srb *srb;
250
251         srb = malloc(sizeof *srb+sz);
252         srb->state = Alloc;
253         srb->dp = srb->data = srb+1;
254         srb->ticksent = Ticks;
255         return srb;
256 }
257
258 static Srb*
259 srbkalloc(void *db, ulong)
260 {
261         Srb *srb;
262
263         srb = malloc(sizeof *srb);
264         srb->state = Alloc;
265         srb->dp = srb->data = db;
266         srb->ticksent = Ticks;
267         return srb;
268 }
269
270 static int
271 srbready(void *v)
272 {
273         Srb *s;
274
275         s = v;
276         return s->nout == 0 && (s->len == 0 || s->error != nil);
277 }
278
279 static void
280 srbfree(Srb *srb)
281 {
282         int n;
283
284         for(n = 0; srb->state != Free; n++)
285                 sched();
286         free(srb);
287 }
288
289 /* under Aoedev qlock() so setting of srb->state is safe */
290 static void
291 srbwakeup(Srb *srb)
292 {
293         if(srbready(srb)){
294                 assert(srb->state == Alloc);
295                 wakeup(srb);
296                 srb->state = Free;
297         }
298 }
299
300 static void
301 srbcleanout(Aoedev *d, Srb *srb)
302 {
303         Srb *x, **ll;
304
305         if(srb == d->inprocess)
306                 d->inprocess = nil;
307         else
308                 for(ll = &d->head; x = *ll; ll = &x->next){
309                         d->tail = x;
310                         if(x == srb)
311                                 *ll = x->next;
312                 }
313 }
314
315 static void
316 srberror(Aoedev *d, Srb *srb, char *s)
317 {
318         srbcleanout(d, srb);
319         srb->error = s;
320         srbwakeup(srb);
321 }
322
323 static void
324 frameerror(Aoedev *d, Frame *f, char *s)
325 {
326         Srb *srb;
327
328         if(f->tag == Tfree)
329                 return;
330         srb = f->srb;
331         f->srb = nil;
332         f->tag = Tfree;         /* don't get fooled by way-slow responses */
333         if(!srb)
334                 return;
335         srb->nout--;
336         srberror(d, srb, s);
337         d->nout--;
338 }
339
340 static char*
341 unitname(Aoedev *d)
342 {
343         uprint("%ud.%ud", d->major, d->minor);
344         return up->genbuf;
345 }
346
347 static long
348 eventlogread(void *a, long n)
349 {
350         int len;
351         char *p, *buf;
352
353         buf = smalloc(Eventlen);
354         qlock(&events);
355         lock(&events);
356         p = events.rp;
357         len = *p;
358         if(len == 0){
359                 n = 0;
360                 unlock(&events);
361         } else {
362                 if(n > len)
363                         n = len;
364                 /* can't move directly into pageable space with events lock held */
365                 memmove(buf, p+1, n);
366                 *p = 0;
367                 events.rp = p += Eventlen;
368                 if(p >= events.buf + sizeof events.buf)
369                         events.rp = events.buf;
370                 unlock(&events);
371
372                 /* the concern here is page faults in memmove below */
373                 if(waserror()){
374                         free(buf);
375                         qunlock(&events);
376                         nexterror();
377                 }
378                 memmove(a, buf, n);
379                 poperror();
380         }
381         free(buf);
382         qunlock(&events);
383         return n;
384 }
385
386 static int
387 eventlog(char *fmt, ...)
388 {
389         int dragrp, n;
390         char *p;
391         va_list arg;
392
393         lock(&events);
394         p = events.wp;
395         dragrp = *p++;
396         va_start(arg, fmt);
397         n = vsnprint(p, Eventlen-1, fmt, arg);
398         *--p = n;
399         p = events.wp += Eventlen;
400         if(p >= events.buf + sizeof events.buf)
401                 p = events.wp = events.buf;
402         if(dragrp)
403                 events.rp = p;
404         unlock(&events);
405         wakeup(&events);
406         return n;
407 }
408
409 static int
410 eventcount(void)
411 {
412         uint n;
413
414         lock(&events);
415         if(*events.rp == 0)
416                 n = 0;
417         else
418                 n = events.wp - events.rp & Nevents - 1;
419         unlock(&events);
420         return n/Eventlen;
421 }
422
423 static int
424 tsince(int tag)
425 {
426         int n;
427
428         n = Ticks & 0xffff;
429         n -= tag & 0xffff;
430         if(n < 0)
431                 n += 1<<16;
432         return n;
433 }
434
435 static int
436 newtag(Aoedev *d)
437 {
438         int t;
439
440         do {
441                 t = ++d->lasttag << 16;
442                 t |= Ticks & 0xffff;
443         } while (t == Tfree || t == Tmgmt);
444         return t;
445 }
446
447 static void
448 downdev(Aoedev *d, char *err)
449 {
450         Frame *f, *e;
451
452         d->flag &= ~Dup;
453         f = d->frames;
454         e = f + d->nframes;
455         for(; f < e; f++)
456                 frameerror(d, f, Enotup);
457         d->inprocess = nil;
458         eventlog("%æ: removed; %s\n", d, err);
459 }
460
461 static Block*
462 allocfb(Frame *f)
463 {
464         int len;
465         Block *b;
466
467         len = f->nhdr + f->dlen;
468         if(len < ETHERMINTU)
469                 len = ETHERMINTU;
470         b = allocb(len);
471         memmove(b->wp, f->hdr, f->nhdr);
472         if(f->dlen)
473                 memmove(b->wp + f->nhdr, f->dp, f->dlen);
474         b->wp += len;
475         return b;
476 }
477
478 static void
479 putlba(Aoeata *a, vlong lba)
480 {
481         uchar *c;
482
483         c = a->lba;
484         c[0] = lba;
485         c[1] = lba >> 8;
486         c[2] = lba >> 16;
487         c[3] = lba >> 24;
488         c[4] = lba >> 32;
489         c[5] = lba >> 40;
490 }
491
492 static Devlink*
493 pickdevlink(Aoedev *d)
494 {
495         ulong i, n;
496         Devlink *l;
497
498         for(i = 0; i < d->ndl; i++){
499                 n = d->dlidx++ % d->ndl;
500                 l = d->dl + n;
501                 if(l && l->flag & Dup)
502                         return l;
503         }
504         return 0;
505 }
506
507 static int
508 pickea(Devlink *l)
509 {
510         if(l == 0)
511                 return -1;
512         if(l->nea == 0)
513                 return -1;
514         return l->eaidx++ % l->nea;
515 }
516
517 /*
518  * would like this to depend on the chan (srb).
519  * not possible in the current structure.
520  */
521 #define Nofail(d, s)    (((d)->flag&Dnofail) == Dnofail)
522
523 static int
524 hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd)
525 {
526         int i;
527         Devlink *l;
528
529         if(f->srb)
530         if((long)(Ticks-f->srb->ticksent) > Srbtimeout){
531                 eventlog("%æ: srb timeout\n", d);
532                 if(cmd == ACata && Nofail(d, s))
533                         f->srb->ticksent = Ticks;
534                 else
535                         frameerror(d, f, Etimedout);
536                 return -1;
537         }
538         l = pickdevlink(d);
539         i = pickea(l);
540         if(i == -1){
541                 if(!(cmd == ACata && f->srb && Nofail(d, s)))
542                         downdev(d, "resend fails; no netlink/ea");
543                 return -1;
544         }
545         memmove(h->dst, l->eatab[i], Eaddrlen);
546         memmove(h->src, l->nl->ea, sizeof h->src);
547         hnputs(h->type, Aoetype);
548         h->verflag = Aoever << 4;
549         h->error = 0;
550         hnputs(h->major, d->major);
551         h->minor = d->minor;
552         h->cmd = cmd;
553
554         hnputl(h->tag, f->tag = newtag(d));
555         f->dl = l;
556         f->nl = l->nl;
557         f->eaidx = i;
558         f->ticksent = Ticks;
559
560         return f->tag;
561 }
562
563 static int
564 resend(Aoedev *d, Frame *f)
565 {
566         ulong n;
567         Aoeata *a;
568         Aoehdr *h;
569
570         h = (Aoehdr*)f->hdr;
571         if(hset(d, f, h, h->cmd) == -1)
572                 return -1;
573         a = (Aoeata*)(f->hdr + Aoehsz);
574         n = f->bcnt;
575         if(n > d->maxbcnt){
576                 n = d->maxbcnt;         /* mtu mismatch (jumbo fail?) */
577                 if(f->dlen > n)
578                         f->dlen = n;
579         }
580         a->scnt = n / Aoesectsz;
581         f->dl->resent++;
582         f->dl->npkt++;
583         if(waserror())
584                 /* should remove the netlink */
585                 return -1;
586         devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
587         poperror();
588         return 0;
589 }
590
591 static void
592 discover(uint major, uint minor)
593 {
594         Aoehdr *h;
595         Block *b;
596         Netlink *nl, *e;
597
598         nl = netlinks.nl;
599         e = nl + nelem(netlinks.nl);
600         for(; nl < e; nl++){
601                 if(nl->cc == nil)
602                         continue;
603                 b = allocb(ETHERMINTU);
604                 if(waserror()){
605                         freeb(b);
606                         nexterror();
607                 }
608                 b->wp = b->rp + ETHERMINTU;
609                 memset(b->rp, 0, ETHERMINTU);
610                 h = (Aoehdr*)b->rp;
611                 memset(h->dst, 0xff, sizeof h->dst);
612                 memmove(h->src, nl->ea, sizeof h->src);
613                 hnputs(h->type, Aoetype);
614                 h->verflag = Aoever << 4;
615                 hnputs(h->major, major);
616                 h->minor = minor;
617                 h->cmd = ACconfig;
618                 poperror();
619                 devtab[nl->dc->type]->bwrite(nl->dc, b, 0);
620         }
621 }
622
623 /*
624  * Check all frames on device and resend any frames that have been
625  * outstanding for 200% of the device round trip time average.
626  */
627 static void
628 aoesweepproc(void*)
629 {
630         ulong i, tx, timeout, nbc;
631         vlong starttick;
632         enum { Nms = 100, Nbcms = 30*1000, };
633         uchar *ea;
634         Aoeata *a;
635         Aoedev *d;
636         Devlink *l;
637         Frame *f, *e;
638
639         nbc = Nbcms/Nms;
640 loop:
641         if(nbc-- == 0){
642                 if(rediscover && !waserror()){
643                         discover(0xffff, 0xff);
644                         poperror();
645                 }
646                 nbc = Nbcms/Nms;
647         }
648         starttick = Ticks;
649         rlock(&devs);
650         for(d = devs.d; d; d = d->next){
651                 if(!canqlock(d))
652                         continue;
653                 if(!UP(d)){
654                         qunlock(d);
655                         continue;
656                 }
657                 tx = 0;
658                 f = d->frames;
659                 e = f + d->nframes;
660                 for (; f < e; f++){
661                         if(f->tag == Tfree)
662                                 continue;
663                         l = f->dl;
664                         timeout = l->rttavg << 1;
665                         i = tsince(f->tag);
666                         if(i < timeout)
667                                 continue;
668                         if(d->nout == d->maxout){
669                                 if(d->maxout > 1)
670                                         d->maxout--;
671                                 d->lastwadj = Ticks;
672                         }
673                         a = (Aoeata*)(f->hdr + Aoehsz);
674                         if(a->scnt > Dbcnt / Aoesectsz &&
675                            ++f->nl->lostjumbo > (d->nframes << 1)){
676                                 ea = f->dl->eatab[f->eaidx];
677                                 eventlog("%æ: jumbo failure on %s:%E; %llud\n",
678                                         d, f->nl->path, ea, f->lba);
679                                 d->maxbcnt = Dbcnt;
680                                 d->flag &= ~Djumbo;
681                         }
682                         resend(d, f);
683                         if(tx++ == 0){
684                                 if((l->rttavg <<= 1) > Rtmax)
685                                         l->rttavg = Rtmax;
686                                 eventlog("%æ: rtt %ldms\n", d, Tk2ms(l->rttavg));
687                         }
688                 }
689                 if(d->nout == d->maxout && d->maxout < d->nframes &&
690                    TK2MS(Ticks-d->lastwadj) > 10*1000){
691                         d->maxout++;
692                         d->lastwadj = Ticks;
693                 }
694                 qunlock(d);
695         }
696         runlock(&devs);
697         i = Nms - TK2MS(Ticks - starttick);
698         if(i > 0)
699                 tsleep(&up->sleep, return0, 0, i);
700         goto loop;
701 }
702
703 static int
704 fmtæ(Fmt *f)
705 {
706         char buf[16];
707         Aoedev *d;
708
709         d = va_arg(f->args, Aoedev*);
710         snprint(buf, sizeof buf, "aoe%ud.%ud", d->major, d->minor);
711         return fmtstrcpy(f, buf);
712 }
713
714 static void netbind(char *path);
715
716 static void
717 aoecfg(void)
718 {
719         char *p, *f[32], buf[24], ifbuf[64];
720         int n, i;
721
722         if((p = getconf("aoeif")) == nil)
723                 return;
724         strncpy(ifbuf, p, sizeof buf);
725         if((n = tokenize(ifbuf, f, nelem(f))) < 1)
726                 return;
727         /* goo! */
728         for(i = 0; i < n; i++){
729                 p = f[i];
730                 if(strncmp(p, "ether", 5) == 0)
731                         snprint(buf, sizeof buf, "#l%c/ether%c", p[5], p[5]);
732                 else if(strncmp(p, "#l", 2) == 0)
733                         snprint(buf, sizeof buf, "#l%c/ether%c", p[2], p[2]);
734                 else
735                         continue;
736                 if(!waserror()){
737                         netbind(buf);
738                         poperror();
739                 }
740         }
741 }
742
743 static void
744 aoeinit(void)
745 {
746         static int init;
747         static QLock l;
748
749         if(!canqlock(&l))
750                 return;
751         if(init == 0){
752                 fmtinstall(L'æ', fmtæ);
753                 events.rp = events.wp = events.buf;
754                 kproc("aoesweep", aoesweepproc, nil);
755                 aoecfg();
756                 init = 1;
757         }
758         qunlock(&l);
759 }
760
761 static Chan*
762 aoeattach(char *spec)
763 {
764         Chan *c;
765
766         if(*spec)
767                 error(Enonexist);
768         aoeinit();
769         c = devattach(L'æ', spec);
770         mkqid(&c->qid, Qzero, 0, QTDIR);
771         return c;
772 }
773
774 static Aoedev*
775 unitseq(ulong unit)
776 {
777         int i;
778         Aoedev *d;
779
780         i = 0;
781         rlock(&devs);
782         for(d = devs.d; d; d = d->next)
783                 if(i++ == unit)
784                         break;
785         runlock(&devs);
786         return d;
787 }
788
789 static Aoedev*
790 unit2dev(ulong unit)
791 {
792         Aoedev *d;
793
794         rlock(&devs);
795         for(d = devs.d; d; d = d->next)
796                 if(d->unit == unit){
797                         runlock(&devs);
798                         return d;
799                 }
800         runlock(&devs);
801         error("unit lookup failure");
802         return nil;
803 }
804
805 static int
806 unitgen(Chan *c, ulong type, Dir *dp)
807 {
808         int perm, t;
809         ulong vers;
810         vlong size;
811         char *p;
812         Aoedev *d;
813         Qid q;
814
815         d = unit2dev(UNIT(c->qid));
816         perm = 0644;
817         size = 0;
818         vers = d->vers;
819         t = QTFILE;
820
821         switch(type){
822         default:
823                 return -1;
824         case Qctl:
825                 p = "ctl";
826                 break;
827         case Qdata:
828                 p = "data";
829                 perm = 0640;
830                 if(UP(d))
831                         size = d->bsize;
832                 break;
833         case Qconfig:
834                 p = "config";
835                 if(UP(d))
836                         size = d->nconfig;
837                 break;
838         case Qident:
839                 p = "ident";
840                 if(UP(d))
841                         size = sizeof d->ident;
842                 break;
843         case Qdevlinkdir:
844                 p = "devlink";
845                 t = QTDIR;
846                 perm = 0555;
847                 break;
848         }
849         mkqid(&q, QID(UNIT(c->qid), type), vers, t);
850         devdir(c, q, p, size, eve, perm, dp);
851         return 1;
852 }
853
854 static int
855 topgen(Chan *c, ulong type, Dir *d)
856 {
857         int perm;
858         vlong size;
859         char *p;
860         Qid q;
861
862         perm = 0444;
863         size = 0;
864         switch(type){
865         default:
866                 return -1;
867         case Qtopctl:
868                 p = "ctl";
869                 perm = 0644;
870                 break;
871         case Qtoplog:
872                 p = "log";
873                 size = eventcount();
874                 break;
875         }
876         mkqid(&q, type, 0, QTFILE);
877         devdir(c, q, p, size, eve, perm, d);
878         return 1;
879 }
880
881 static int
882 aoegen(Chan *c, char *, Dirtab *, int, int s, Dir *dp)
883 {
884         int i;
885         Aoedev *d;
886         Qid q;
887
888         if(c->qid.path == 0){
889                 switch(s){
890                 case DEVDOTDOT:
891                         q.path = 0;
892                         q.type = QTDIR;
893                         devdir(c, q, "#æ", 0, eve, 0555, dp);
894                         break;
895                 case 0:
896                         q.path = Qtopdir;
897                         q.type = QTDIR;
898                         devdir(c, q, "aoe", 0, eve, 0555, dp);
899                         break;
900                 default:
901                         return -1;
902                 }
903                 return 1;
904         }
905
906         switch(TYPE(c->qid)){
907         default:
908                 return -1;
909         case Qtopdir:
910                 if(s == DEVDOTDOT){
911                         mkqid(&q, Qzero, 0, QTDIR);
912                         devdir(c, q, "aoe", 0, eve, 0555, dp);
913                         return 1;
914                 }
915                 if(s < Qtopfiles)
916                         return topgen(c, Qtopbase + s, dp);
917                 s -= Qtopfiles;
918                 if((d = unitseq(s)) == 0)
919                         return -1;
920                 mkqid(&q, QID(d->unit, Qunitdir), 0, QTDIR);
921                 devdir(c, q, unitname(d), 0, eve, 0555, dp);
922                 return 1;
923         case Qtopctl:
924         case Qtoplog:
925                 return topgen(c, TYPE(c->qid), dp);
926         case Qunitdir:
927                 if(s == DEVDOTDOT){
928                         mkqid(&q, QID(0, Qtopdir), 0, QTDIR);
929                         uprint("%uld", UNIT(c->qid));
930                         devdir(c, q, up->genbuf, 0, eve, 0555, dp);
931                         return 1;
932                 }
933                 return unitgen(c, Qunitbase+s, dp);
934         case Qctl:
935         case Qdata:
936         case Qconfig:
937         case Qident:
938                 return unitgen(c, TYPE(c->qid), dp);
939         case Qdevlinkdir:
940                 i = UNIT(c->qid);
941                 if(s == DEVDOTDOT){
942                         mkqid(&q, QID(i, Qunitdir), 0, QTDIR);
943                         devdir(c, q, "devlink", 0, eve, 0555, dp);
944                         return 1;
945                 }
946                 if(i >= units.ref)
947                         return -1;
948                 d = unit2dev(i);
949                 if(s >= d->ndl)
950                         return -1;
951                 uprint("%d", s);
952                 mkqid(&q, Q3(s, i, Qdevlink), 0, QTFILE);
953                 devdir(c, q, up->genbuf, 0, eve, 0755, dp);
954                 return 1;
955         case Qdevlink:
956                 uprint("%d", s);
957                 mkqid(&q, Q3(s, UNIT(c->qid), Qdevlink), 0, QTFILE);
958                 devdir(c, q, up->genbuf, 0, eve, 0755, dp);
959                 return 1;
960         }
961 }
962
963 static Walkqid*
964 aoewalk(Chan *c, Chan *nc, char **name, int nname)
965 {
966         return devwalk(c, nc, name, nname, nil, 0, aoegen);
967 }
968
969 static int
970 aoestat(Chan *c, uchar *db, int n)
971 {
972         return devstat(c, db, n, nil, 0, aoegen);
973 }
974
975 static Chan*
976 aoeopen(Chan *c, int omode)
977 {
978         Aoedev *d;
979
980         if(TYPE(c->qid) != Qdata)
981                 return devopen(c, omode, 0, 0, aoegen);
982
983         d = unit2dev(UNIT(c->qid));
984         qlock(d);
985         if(waserror()){
986                 qunlock(d);
987                 nexterror();
988         }
989         if(!UP(d))
990                 error(Enotup);
991         c = devopen(c, omode, 0, 0, aoegen);
992         d->nopen++;
993         poperror();
994         qunlock(d);
995         return c;
996 }
997
998 static void
999 aoeclose(Chan *c)
1000 {
1001         Aoedev *d;
1002
1003         if(TYPE(c->qid) != Qdata || (c->flag&COPEN) == 0)
1004                 return;
1005
1006         d = unit2dev(UNIT(c->qid));
1007         qlock(d);
1008         if(--d->nopen == 0 && !waserror()){
1009                 discover(d->major, d->minor);
1010                 poperror();
1011         }
1012         qunlock(d);
1013 }
1014
1015 static void
1016 atarw(Aoedev *d, Frame *f)
1017 {
1018         ulong bcnt;
1019         char extbit, writebit;
1020         Aoeata *ah;
1021         Aoehdr *h;
1022         Srb *srb;
1023
1024         extbit = 0x4;
1025         writebit = 0x10;
1026
1027         srb = d->inprocess;
1028         bcnt = d->maxbcnt;
1029         if(bcnt > srb->len)
1030                 bcnt = srb->len;
1031         f->nhdr = Aoehsz + Aoeatasz;
1032         memset(f->hdr, 0, f->nhdr);
1033         h = (Aoehdr*)f->hdr;
1034         if(hset(d, f, h, ACata) == -1){
1035                 d->inprocess = nil;
1036                 return;
1037         }
1038         ah = (Aoeata*)(f->hdr + Aoehsz);
1039         f->dp = srb->dp;
1040         f->bcnt = bcnt;
1041         f->lba = srb->sector;
1042         f->srb = srb;
1043
1044         ah->scnt = bcnt / Aoesectsz;
1045         putlba(ah, f->lba);
1046         if(d->feat & Dllba)
1047                 ah->aflag |= AAFext;
1048         else {
1049                 extbit = 0;
1050                 ah->lba[3] &= 0x0f;
1051                 ah->lba[3] |= 0xe0;     /* LBA bit+obsolete 0xa0 */
1052         }
1053         if(srb->write){
1054                 ah->aflag |= AAFwrite;
1055                 f->dlen = bcnt;
1056         }else{
1057                 writebit = 0;
1058                 f->dlen = 0;
1059         }
1060         ah->cmdstat = 0x20 | writebit | extbit;
1061
1062         /* mark tracking fields and load out */
1063         srb->nout++;
1064         srb->dp = (uchar*)srb->dp + bcnt;
1065         srb->len -= bcnt;
1066         srb->sector += bcnt / Aoesectsz;
1067         if(srb->len == 0)
1068                 d->inprocess = nil;
1069         d->nout++;
1070         f->dl->npkt++;
1071         if(waserror())
1072                 frameerror(d, f, "write error");
1073         else{
1074                 devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
1075                 poperror();
1076         }
1077 }
1078
1079 static char*
1080 aoeerror(Aoehdr *h)
1081 {
1082         int n;
1083         static char *errs[] = {
1084                 "aoe protocol error: unknown",
1085                 "aoe protocol error: bad command code",
1086                 "aoe protocol error: bad argument param",
1087                 "aoe protocol error: device unavailable",
1088                 "aoe protocol error: config string present",
1089                 "aoe protocol error: unsupported version",
1090                 "aoe protocol error: target is reserved",
1091         };
1092
1093         if((h->verflag & AFerr) == 0)
1094                 return 0;
1095         n = h->error;
1096         if(n > nelem(errs))
1097                 n = 0;
1098         return errs[n];
1099 }
1100
1101 static void
1102 rtupdate(Devlink *l, int rtt)
1103 {
1104         int n;
1105
1106         n = rtt;
1107         if(rtt < 0){
1108                 n = -rtt;
1109                 if(n < Rtmin)
1110                         n = Rtmin;
1111                 else if(n > Rtmax)
1112                         n = Rtmax;
1113                 l->mintimer += (n - l->mintimer) >> 1;
1114         } else if(n < l->mintimer)
1115                 n = l->mintimer;
1116         else if(n > Rtmax)
1117                 n = Rtmax;
1118
1119         /* g == .25; cf. Congestion Avoidance and Control, Jacobson&Karels; 1988 */
1120         n -= l->rttavg;
1121         l->rttavg += n >> 2;
1122 }
1123
1124 static Frame*
1125 getframe(Aoedev *d, int tag)
1126 {
1127         Frame *f, *e;
1128
1129         f = d->frames;
1130         e = f + d->nframes;
1131         for(; f < e; f++)
1132                 if(f->tag == tag)
1133                         return f;
1134         return nil;
1135 }
1136
1137 static Frame*
1138 freeframe(Aoedev *d)
1139 {
1140         if(d->nout < d->maxout)
1141                 return getframe(d, Tfree);
1142         return nil;
1143 }
1144
1145 static void
1146 work(Aoedev *d)
1147 {
1148         Frame *f;
1149
1150         while(f = freeframe(d)) {
1151                 if(d->inprocess == nil){
1152                         if(d->head == nil)
1153                                 return;
1154                         d->inprocess = d->head;
1155                         d->head = d->head->next;
1156                         if(d->head == nil)
1157                                 d->tail = nil;
1158                 }
1159                 atarw(d, f);
1160         }
1161 }
1162
1163 static void
1164 strategy(Aoedev *d, Srb *srb)
1165 {
1166         qlock(d);
1167         if(waserror()){
1168                 qunlock(d);
1169                 nexterror();
1170         }
1171         if(!UP(d))
1172                 error(Eio);
1173         srb->next = nil;
1174         if(d->tail)
1175                 d->tail->next = srb;
1176         d->tail = srb;
1177         if(d->head == nil)
1178                 d->head = srb;
1179         work(d);
1180         poperror();
1181         qunlock(d);
1182
1183         while(waserror()){
1184                 qlock(d);
1185                 srberror(d, srb, "interrupted");
1186                 qunlock(d);
1187         }
1188         sleep(srb, srbready, srb);
1189         poperror();
1190 }
1191
1192 #define iskaddr(a)      ((uintptr)(a) > KZERO)
1193
1194 static long
1195 rw(Aoedev *d, int write, uchar *db, long len, uvlong off)
1196 {
1197         long n, nlen, copy;
1198         enum { Srbsz = 1<<19, };
1199         Srb *srb;
1200
1201         if((off|len) & (Aoesectsz-1))
1202                 error("offset and length must be sector multiple.\n");
1203         if(!UP(d))
1204                 error(Eio);
1205         if(off >= d->bsize)
1206                 return 0;
1207         if(off + len > d->bsize)
1208                 len = d->bsize - off;
1209         copy = 0;
1210         if(iskaddr(db)){
1211                 srb = srbkalloc(db, len);
1212                 copy = 1;
1213         }else
1214                 srb = srballoc(Srbsz <= len? Srbsz: len);
1215         if(waserror()){
1216                 srbfree(srb);
1217                 nexterror();
1218         }
1219         srb->write = write;
1220         for(nlen = len; nlen; nlen -= n){
1221                 srb->sector = off / Aoesectsz;
1222                 srb->dp = srb->data;
1223                 n = nlen;
1224                 if(n > Srbsz)
1225                         n = Srbsz;
1226                 srb->len = n;
1227                 if(write && !copy)
1228                         memmove(srb->data, db, n);
1229                 strategy(d, srb);
1230                 if(srb->error)
1231                         error(srb->error);
1232                 if(!write && !copy)
1233                         memmove(db, srb->data, n);
1234                 db += n;
1235                 off += n;
1236         }
1237         poperror();
1238         srbfree(srb);
1239         return len;
1240 }
1241
1242 static long
1243 readmem(ulong off, void *dst, long n, void *src, long size)
1244 {
1245         if(off >= size)
1246                 return 0;
1247         if(off + n > size)
1248                 n = size - off;
1249         memmove(dst, (uchar*)src + off, n);
1250         return n;
1251 }
1252
1253 static char*
1254 aoeflag(char *s, char *e, uchar f)
1255 {
1256         uchar i;
1257
1258         for(i = 0; i < nelem(flagname); i++)
1259                 if(f & 1 << i)
1260                         s = seprint(s, e, "%s ", flagname[i]);
1261         return seprint(s, e, "\n");
1262 }
1263
1264 static int
1265 pstat(Aoedev *d, char *db, int len, int off)
1266 {
1267         int i;
1268         char *state, *s, *p, *e;
1269
1270         s = p = malloc(READSTR);
1271         e = p + READSTR;
1272
1273         state = "down";
1274         if(UP(d))
1275                 state = "up";
1276
1277         p = seprint(p, e,
1278                 "state: %s\n"   "nopen: %d\n"   "nout: %d\n"
1279                 "nmaxout: %d\n" "nframes: %d\n" "maxbcnt: %d [maxmtu %d]\n"
1280                 "fw: %.4ux\n"
1281                 "model: %s\n"   "serial: %s\n"  "firmware: %s\n",
1282                 state,          d->nopen,       d->nout,
1283                 d->maxout,      d->nframes,     d->maxbcnt, d->maxmtu,
1284                 d->fwver,
1285                 d->model,       d->serial,      d->firmware);
1286         p = seprint(p, e, "flag: ");
1287         p = pflag(p, e, d);
1288         p[-1] = ' ';    /* horrid */
1289         p = aoeflag(p, e, d->flag);
1290
1291         if(p - s < len)
1292                 len = p - s;
1293         i = readstr(off, db, len, s);
1294         free(s);
1295         return i;
1296 }
1297
1298 static long
1299 unitread(Chan *c, void *db, long len, vlong off)
1300 {
1301         Aoedev *d;
1302
1303         d = unit2dev(UNIT(c->qid));
1304         if(d->vers != c->qid.vers)
1305                 error(Echange);
1306         switch(TYPE(c->qid)){
1307         default:
1308                 error(Ebadarg);
1309         case Qctl:
1310                 return pstat(d, db, len, off);
1311         case Qdata:
1312                 return rw(d, Read, db, len, off);
1313         case Qconfig:
1314                 if(!UP(d))
1315                         error(Enotup);
1316                 return readmem(off, db, len, d->config, d->nconfig);
1317         case Qident:
1318                 if(!UP(d))
1319                         error(Enotup);
1320                 return readmem(off, db, len, d->ident, sizeof d->ident);
1321         }
1322 }
1323
1324 static int
1325 getmtu(Chan *m)
1326 {
1327         int n, mtu;
1328         char buf[36];
1329
1330         mtu = 1514;
1331         if(m == nil || waserror())
1332                 return mtu;
1333         n = devtab[m->type]->read(m, buf, sizeof buf - 1, 0);
1334         poperror();
1335         if(n > 12){
1336                 buf[n] = 0;
1337                 mtu = strtoul(buf + 12, 0, 0);
1338         }
1339         return mtu;
1340 }
1341
1342 static int
1343 devlinkread(Chan *c, void *db, int len, int off)
1344 {
1345         int i;
1346         char *s, *p, *e;
1347         Aoedev *d;
1348         Devlink *l;
1349
1350         d = unit2dev(UNIT(c->qid));
1351         i = L(c->qid);
1352         if(i >= d->ndl)
1353                 return 0;
1354         l = d->dl + i;
1355
1356         s = p = malloc(READSTR);
1357         e = s + READSTR;
1358
1359         p = seprint(p, e, "addr: ");
1360         for(i = 0; i < l->nea; i++)
1361                 p = seprint(p, e, "%E ", l->eatab[i]);
1362         p = seprint(p, e, "\n");
1363         p = seprint(p, e, "npkt: %uld\n", l->npkt);
1364         p = seprint(p, e, "resent: %uld\n", l->resent);
1365         p = seprint(p, e, "flag: ");
1366         p = aoeflag(p, e, l->flag);
1367         p = seprint(p, e, "rttavg: %uld\n", Tk2ms(l->rttavg));
1368         p = seprint(p, e, "mintimer: %uld\n", Tk2ms(l->mintimer));
1369         p = seprint(p, e, "datamtu: %d\n", l->datamtu);
1370
1371         p = seprint(p, e, "nl path: %s\n", l->nl->path);
1372         p = seprint(p, e, "nl ea: %E\n", l->nl->ea);
1373         p = seprint(p, e, "nl flag: ");
1374         p = aoeflag(p, e, l->flag);
1375         p = seprint(p, e, "nl lostjumbo: %d\n", l->nl->lostjumbo);
1376         p = seprint(p, e, "nl datamtu: %d\n", getmtu(l->nl->mtu));
1377
1378         if(p - s < len)
1379                 len = p - s;
1380         i = readstr(off, db, len, s);
1381         free(s);
1382         return i;
1383 }
1384
1385 static long
1386 topctlread(Chan *, void *db, int len, int off)
1387 {
1388         int i;
1389         char *s, *p, *e;
1390         Netlink *n;
1391
1392         s = p = malloc(READSTR);
1393         e = s + READSTR;
1394
1395         p = seprint(p, e, "debug: %d\n", debug);
1396         p = seprint(p, e, "autodiscover: %d\n", autodiscover);
1397         p = seprint(p, e, "rediscover: %d\n", rediscover);
1398
1399         for(i = 0; i < Nnetlink; i++){
1400                 n = netlinks.nl+i;
1401                 if(n->cc == 0)
1402                         continue;
1403                 p = seprint(p, e, "if%d path: %s\n", i, n->path);
1404                 p = seprint(p, e, "if%d ea: %E\n", i, n->ea);
1405                 p = seprint(p, e, "if%d flag: ", i);
1406                 p = aoeflag(p, e, n->flag);
1407                 p = seprint(p, e, "if%d lostjumbo: %d\n", i, n->lostjumbo);
1408                 p = seprint(p, e, "if%d datamtu: %d\n", i, getmtu(n->mtu));
1409         }
1410
1411         if(p - s < len)
1412                 len = p - s;
1413         i = readstr(off, db, len, s);
1414         free(s);
1415         return i;
1416 }
1417
1418 static long
1419 aoeread(Chan *c, void *db, long n, vlong off)
1420 {
1421         switch(TYPE(c->qid)){
1422         default:
1423                 error(Eperm);
1424         case Qzero:
1425         case Qtopdir:
1426         case Qunitdir:
1427         case Qdevlinkdir:
1428                 return devdirread(c, db, n, 0, 0, aoegen);
1429         case Qtopctl:
1430                 return topctlread(c, db, n, off);
1431         case Qtoplog:
1432                 return eventlogread(db, n);
1433         case Qctl:
1434         case Qdata:
1435         case Qconfig:
1436         case Qident:
1437                 return unitread(c, db, n, off);
1438         case Qdevlink:
1439                 return devlinkread(c, db, n, off);
1440         }
1441 }
1442
1443 static long
1444 configwrite(Aoedev *d, void *db, long len)
1445 {
1446         char *s;
1447         Aoehdr *h;
1448         Aoecfg *ch;
1449         Frame *f;
1450         Srb *srb;
1451
1452         if(!UP(d))
1453                 error(Enotup);
1454         if(len > sizeof d->config)
1455                 error(Etoobig);
1456         srb = srballoc(len);
1457         s = malloc(len);
1458         memmove(s, db, len);
1459         if(waserror()){
1460                 srbfree(srb);
1461                 free(s);
1462                 nexterror();
1463         }
1464         for (;;) {
1465                 qlock(d);
1466                 if(waserror()){
1467                         qunlock(d);
1468                         nexterror();
1469                 }
1470                 f = freeframe(d);
1471                 if(f != nil)
1472                         break;
1473                 poperror();
1474                 qunlock(d);
1475                 if(waserror())
1476                         nexterror();
1477                 tsleep(&up->sleep, return0, 0, 100);
1478                 poperror();
1479         }
1480         f->nhdr = Aoehsz + Aoecfgsz;
1481         memset(f->hdr, 0, f->nhdr);
1482         h = (Aoehdr*)f->hdr;
1483         if(hset(d, f, h, ACconfig) == -1)
1484                 return 0;
1485         ch = (Aoecfg*)(f->hdr + Aoehsz);
1486         f->srb = srb;
1487         f->dp = s;
1488         ch->verccmd = AQCfset;
1489         hnputs(ch->cslen, len);
1490         d->nout++;
1491         srb->nout++;
1492         f->dl->npkt++;
1493         f->dlen = len;
1494         /*
1495          * these refer to qlock & waserror in the above for loop.
1496          * there's still the first waserror outstanding.
1497          */
1498         poperror();
1499         qunlock(d);
1500
1501         devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
1502         sleep(srb, srbready, srb);
1503         if(srb->error)
1504                 error(srb->error);
1505
1506         qlock(d);
1507         if(waserror()){
1508                 qunlock(d);
1509                 nexterror();
1510         }
1511         memmove(d->config, s, len);
1512         d->nconfig = len;
1513         poperror();
1514         qunlock(d);
1515
1516         poperror();                     /* pop first waserror */
1517
1518         srbfree(srb);
1519         memmove(db, s, len);
1520         free(s);
1521         return len;
1522 }
1523
1524 static int
1525 devmaxdata(Aoedev *d)
1526 {
1527         int i, m, mtu, datamtu;
1528         Devlink *l;
1529         Netlink *n;
1530
1531         mtu = 100000;
1532         datamtu = 100000;
1533         for(i = 0; i < d->ndl; i++){
1534                 l = d->dl + i;
1535                 n = l->nl;
1536                 if((l->flag & Dup) == 0 || (n->flag & Dup) == 0)
1537                         continue;
1538                 m = getmtu(n->mtu);
1539                 if(l->datamtu < datamtu)
1540                         datamtu = l->datamtu;
1541                 if(m < mtu)
1542                         mtu = m;
1543         }
1544         if(mtu == 100000)
1545                 mtu = 1514;
1546         mtu -= Aoehsz + Aoeatasz;
1547         mtu -= mtu % Aoesectsz;
1548         if(mtu > datamtu)
1549                 mtu = datamtu;
1550         return mtu;
1551 }
1552
1553 static int
1554 toggle(char *s, uint f, uint bit)
1555 {
1556         if(s == nil)
1557                 f = f^bit;
1558         else if(strcmp(s, "on") == 0)
1559                 f |= bit;
1560         else
1561                 f &= ~bit;
1562         return f;
1563 }
1564
1565 static void ataident(Aoedev*);
1566
1567 static long
1568 unitctlwrite(Aoedev *d, void *db, long n)
1569 {
1570         uint maxbcnt, m;
1571         uvlong bsize;
1572         enum {
1573                 Failio,
1574                 Ident,
1575                 Jumbo,
1576                 Maxbno,
1577                 Mtu,
1578                 Nofailf,
1579                 Setsize,
1580         };
1581         Cmdbuf *cb;
1582         Cmdtab *ct;
1583         static Cmdtab cmds[] = {
1584                 {Failio,        "failio",       1 },
1585                 {Ident,         "identify",     1 },
1586                 {Jumbo,         "jumbo",        0 },
1587                 {Maxbno,        "maxbno",       0 },
1588                 {Mtu,           "mtu",          0 },
1589                 {Nofailf,               "nofail",               0 },
1590                 {Setsize,       "setsize",      0 },
1591         };
1592
1593         cb = parsecmd(db, n);
1594         qlock(d);
1595         if(waserror()){
1596                 qunlock(d);
1597                 free(cb);
1598                 nexterror();
1599         }
1600         ct = lookupcmd(cb, cmds, nelem(cmds));
1601         switch(ct->index){
1602         case Failio:
1603                 downdev(d, "i/o failure");
1604                 break;
1605         case Ident:
1606                 ataident(d);
1607                 break;
1608         case Jumbo:
1609                 d->flag = toggle(cb->f[1], d->flag, Djumbo);
1610                 break;
1611         case Maxbno:
1612         case Mtu:
1613                 maxbcnt = devmaxdata(d);
1614                 if(cb->nf > 2)
1615                         error(Ecmdargs);
1616                 if(cb->nf == 2){
1617                         m = strtoul(cb->f[1], 0, 0);
1618                         if(ct->index == Maxbno)
1619                                 m *= Aoesectsz;
1620                         else{
1621                                 m -= Aoehsz + Aoeatasz;
1622                                 m &= ~(Aoesectsz-1);
1623                         }
1624                         if(m == 0 || m > maxbcnt)
1625                                 cmderror(cb, "invalid mtu");
1626                         maxbcnt = m;
1627                         d->maxmtu = m;
1628                 } else
1629                         d->maxmtu = Maxmtu;
1630                 d->maxbcnt = maxbcnt;
1631                 break;
1632         case Nofailf:
1633                 d->flag = toggle(cb->f[1], d->flag, Dnofail);
1634                 break;
1635         case Setsize:
1636                 bsize = d->realbsize;
1637                 if(cb->nf > 2)
1638                         error(Ecmdargs);
1639                 if(cb->nf == 2){
1640                         bsize = strtoull(cb->f[1], 0, 0);
1641                         if(bsize % Aoesectsz)
1642                                 cmderror(cb, "disk size must be sector aligned");
1643                 }
1644                 d->bsize = bsize;
1645                 break;
1646         }
1647         poperror();
1648         qunlock(d);
1649         free(cb);
1650         return n;
1651 }
1652
1653 static long
1654 unitwrite(Chan *c, void *db, long n, vlong off)
1655 {
1656         long rv;
1657         char *buf;
1658         Aoedev *d;
1659
1660         d = unit2dev(UNIT(c->qid));
1661         switch(TYPE(c->qid)){
1662         default:
1663                 error(Ebadarg);
1664         case Qctl:
1665                 return unitctlwrite(d, db, n);
1666         case Qident:
1667                 error(Eperm);
1668         case Qdata:
1669                 return rw(d, Write, db, n, off);
1670         case Qconfig:
1671                 if(off + n > sizeof d->config)
1672                         error(Etoobig);
1673                 buf = malloc(sizeof d->config);
1674                 if(waserror()){
1675                         free(buf);
1676                         nexterror();
1677                 }
1678                 memmove(buf, d->config, d->nconfig);
1679                 memmove(buf + off, db, n);
1680                 rv = configwrite(d, buf, n + off);
1681                 poperror();
1682                 free(buf);
1683                 return rv;
1684         }
1685 }
1686
1687 static Netlink*
1688 addnet(char *path, Chan *cc, Chan *dc, Chan *mtu, uchar *ea)
1689 {
1690         Netlink *nl, *e;
1691
1692         lock(&netlinks);
1693         if(waserror()){
1694                 unlock(&netlinks);
1695                 nexterror();
1696         }
1697         nl = netlinks.nl;
1698         e = nl + nelem(netlinks.nl);
1699         for(; nl < e && nl->cc; nl++)
1700                 continue;
1701         if(nl == e)
1702                 error("out of netlink structures");
1703         nl->cc = cc;
1704         nl->dc = dc;
1705         nl->mtu = mtu;
1706         strncpy(nl->path, path, sizeof nl->path);
1707         memmove(nl->ea, ea, sizeof nl->ea);
1708         poperror();
1709         nl->flag |= Dup;
1710         unlock(&netlinks);
1711         return nl;
1712 }
1713
1714 static int
1715 newunit(void)
1716 {
1717         int x;
1718
1719         lock(&units);
1720         if(units.ref == Maxunits)
1721                 x = -1;
1722         else
1723                 x = units.ref++;
1724         unlock(&units);
1725         return x;
1726 }
1727
1728 static int
1729 dropunit(void)
1730 {
1731         int x;
1732
1733         lock(&units);
1734         x = --units.ref;
1735         unlock(&units);
1736         return x;
1737 }
1738
1739 /*
1740  * always allocate max frames.  maxout may change.
1741  */
1742 static Aoedev*
1743 newdev(uint major, uint minor, int n)
1744 {
1745         Aoedev *d;
1746         Frame *f, *e;
1747
1748         d = malloc(sizeof *d);
1749         f = malloc(sizeof *f*Maxframes);
1750         if(!d || !f) {
1751                 free(d);
1752                 free(f);
1753                 error("aoe device allocation failure");
1754         }
1755         d->nframes = n;
1756         d->frames = f;
1757         for (e = f + Maxframes; f < e; f++)
1758                 f->tag = Tfree;
1759         d->maxout = n;
1760         d->major = major;
1761         d->minor = minor;
1762         d->maxbcnt = Dbcnt;
1763         d->flag = Djumbo;
1764         d->maxmtu = Maxmtu;
1765         d->unit = newunit();            /* bzzt.  inaccurate if units removed */
1766         if(d->unit == -1){
1767                 free(d);
1768                 free(d->frames);
1769                 error("too many units");
1770         }
1771         d->dl = d->dltab;
1772         return d;
1773 }
1774
1775 static Aoedev*
1776 mm2dev(uint major, uint minor)
1777 {
1778         Aoedev *d;
1779
1780         rlock(&devs);
1781         for(d = devs.d; d; d = d->next)
1782                 if(d->major == major && d->minor == minor){
1783                         runlock(&devs);
1784                         return d;
1785                 }
1786         runlock(&devs);
1787         eventlog("mm2dev: %ud.%ud not found\n", major, minor);
1788         return nil;
1789 }
1790
1791 /* Find the device in our list.  If not known, add it */
1792 static Aoedev*
1793 getdev(uint major, uint minor, int n)
1794 {
1795         Aoedev *d;
1796
1797         if(major == 0xffff || minor == 0xff)
1798                 return 0;
1799         wlock(&devs);
1800         if(waserror()){
1801                 wunlock(&devs);
1802                 nexterror();
1803         }
1804         for(d = devs.d; d; d = d->next)
1805                 if(d->major == major && d->minor == minor)
1806                         break;
1807         if(d == nil) {
1808                 d = newdev(major, minor, n);
1809                 d->next = devs.d;
1810                 devs.d = d;
1811         }
1812         poperror();
1813         wunlock(&devs);
1814         return d;
1815 }
1816
1817 static void
1818 ataident(Aoedev *d)
1819 {
1820         Aoeata *a;
1821         Aoehdr *h;
1822         Frame *f;
1823
1824         f = freeframe(d);
1825         if(f == nil)
1826                 return;
1827         f->nhdr = Aoehsz + Aoeatasz;
1828         memset(f->hdr, 0, f->nhdr);
1829         h = (Aoehdr*)f->hdr;
1830         if(hset(d, f, h, ACata) == -1)
1831                 return;
1832         a = (Aoeata*)(f->hdr + Aoehsz);
1833         f->srb = srbkalloc(0, 0);
1834         a->cmdstat = Cid;       /* ata 6, page 110 */
1835         a->scnt = 1;
1836         a->lba[3] = 0xa0;
1837         d->nout++;
1838         f->dl->npkt++;
1839         f->bcnt = 512;
1840         f->dlen = 0;
1841         if(waserror()){
1842                 srbfree(f->srb);
1843                 d->nout--;
1844                 f->tag = Tfree;
1845         }else{
1846                 devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
1847                 poperror();
1848         }
1849 }
1850
1851 static int
1852 newdlea(Devlink *l, uchar *ea)
1853 {
1854         int i;
1855         uchar *t;
1856
1857         for(i = 0; i < Nea; i++){
1858                 t = l->eatab[i];
1859                 if(i == l->nea){
1860                         memmove(t, ea, Eaddrlen);
1861                         return l->nea++;
1862                 }
1863                 if(memcmp(t, ea, Eaddrlen) == 0)
1864                         return i;
1865         }
1866         return -1;
1867 }
1868
1869 static Devlink*
1870 newdevlink(Aoedev *d, Netlink *n, Aoehdr *h)
1871 {
1872         int i;
1873         Aoecfg *c;
1874         Devlink *l;
1875
1876         c = (Aoecfg*)((uchar*)h + Aoehsz);
1877         for(i = 0; i < Ndevlink; i++){
1878                 l = d->dl + i;
1879                 if(i == d->ndl){
1880                         d->ndl++;
1881                         newdlea(l, h->src);
1882                         l->datamtu = c->scnt*Aoesectsz;
1883                         l->nl = n;
1884                         l->flag |= Dup;
1885                         l->mintimer = Rtmin;
1886                         l->rttavg = Rtmax;
1887                         return l;
1888                 }
1889                 if(l->nl == n){
1890                         newdlea(l, h->src);
1891                         l->datamtu = c->scnt*Aoesectsz;
1892                         l->flag |= Dup;
1893                         return l;
1894                 }
1895         }
1896         eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, h->src);
1897         return 0;
1898 }
1899
1900 static void
1901 errrsp(Block *b, char *s)
1902 {
1903         int n;
1904         Aoedev *d;
1905         Aoehdr *h;
1906         Frame *f;
1907
1908         h = (Aoehdr*)b->rp;
1909         n = nhgetl(h->tag);
1910         if(n == Tmgmt || n == Tfree)
1911                 return;
1912         d = mm2dev(nhgets(h->major), h->minor);
1913         if(d == 0)
1914                 return;
1915         if(f = getframe(d, n))
1916                 frameerror(d, f, s);
1917 }
1918
1919 static void
1920 qcfgrsp(Block *b, Netlink *nl)
1921 {
1922         int cmd, cslen, blen;
1923         uint n, major;
1924         Aoedev *d;
1925         Aoehdr *h;
1926         Aoecfg *ch;
1927         Devlink *l;
1928         Frame *f;
1929         Srb *srb;
1930
1931         h = (Aoehdr*)b->rp;
1932         ch = (Aoecfg*)(b->rp + Aoehsz);
1933         major = nhgets(h->major);
1934         n = nhgetl(h->tag);
1935         if(n != Tmgmt){
1936                 d = mm2dev(major, h->minor);
1937                 if(d == nil)
1938                         return;
1939                 qlock(d);
1940                 f = getframe(d, n);
1941                 if(f == nil){
1942                         qunlock(d);
1943                         eventlog("%æ: unknown response tag %ux\n", d, n);
1944                         return;
1945                 }
1946                 cslen = nhgets(ch->cslen);
1947                 blen = BLEN(b) - (Aoehsz + Aoecfgsz);
1948                 if(cslen < blen && BLEN(b) > 60)
1949                         eventlog("%æ: cfgrsp: tag %.8ux oversized %d %d\n",
1950                                 d, n, cslen, blen);
1951                 if(cslen > blen){
1952                         eventlog("%æ: cfgrsp: tag %.8ux runt %d %d\n",
1953                                 d, n, cslen, blen);
1954                         cslen = blen;
1955                 }
1956                 memmove(f->dp, (uchar*)ch + Aoehsz + Aoecfgsz, cslen);
1957                 srb = f->srb;
1958                 f->dp = nil;
1959                 f->srb = nil;
1960                 if(srb){
1961                         srb->nout--;
1962                         srbwakeup(srb);
1963                         d->nout--;
1964                         f->tag = Tfree;
1965                 }
1966                 qunlock(d);
1967                 return;
1968         }
1969
1970         cmd = ch->verccmd & 0xf;
1971         if(cmd != 0){
1972                 eventlog("aoe%ud.%ud: cfgrsp: bad command %d\n", major, h->minor, cmd);
1973                 return;
1974         }
1975         n = nhgets(ch->bufcnt);
1976         if(n > Maxframes)
1977                 n = Maxframes;
1978
1979         if(waserror()){
1980                 eventlog("getdev: %ud.%ud ignored: %s\n", major, h->minor, up->errstr);
1981                 return;
1982         }
1983         d = getdev(major, h->minor, n);
1984         poperror();
1985         if(d == 0)
1986                 return;
1987
1988         qlock(d);
1989         *up->errstr = 0;
1990         if(waserror()){
1991                 qunlock(d);
1992                 eventlog("%æ: %s\n", d, up->errstr);
1993                 nexterror();
1994         }
1995
1996         l = newdevlink(d, nl, h);               /* add this interface. */
1997
1998         d->fwver = nhgets(ch->fwver);
1999         n = nhgets(ch->cslen);
2000         if(n > sizeof d->config)
2001                 n = sizeof d->config;
2002         d->nconfig = n;
2003         memmove(d->config, (uchar*)ch + Aoehsz + Aoecfgsz, n);
2004
2005         /* manually set mtu may be reset lower if conditions warrant */
2006         if(l){
2007                 n = devmaxdata(d);
2008                 if((d->flag & Djumbo) == 0)
2009                         n = Dbcnt;
2010                 if(n > d->maxmtu)
2011                         n = d->maxmtu;
2012                 if(n != d->maxbcnt){
2013                         eventlog("%æ: setting %d byte mtu on %s:%E\n",
2014                                 d, n, nl->path, nl->ea);
2015                         d->maxbcnt = n;
2016                 }
2017         }
2018         if(d->nopen == 0)
2019                 ataident(d);
2020         poperror();
2021         qunlock(d);
2022 }
2023
2024 static vlong
2025 aoeidentify(Aoedev *d, ushort *id)
2026 {
2027         vlong s;
2028
2029         s = idfeat(d, id);
2030         if(s == -1)
2031                 return -1;
2032         if((d->feat&Dlba) == 0){
2033                 dprint("%æ: no lba support\n", d);
2034                 return -1;
2035         }
2036         d->flag |= Dup;
2037         memmove(d->ident, id, sizeof d->ident);
2038         return s;
2039 }
2040
2041 static void
2042 newvers(Aoedev *d)
2043 {
2044         lock(&drivevers);
2045         d->vers = drivevers.ref++;
2046         unlock(&drivevers);
2047 }
2048
2049 static int
2050 identify(Aoedev *d, ushort *id)
2051 {
2052         vlong osectors, s;
2053         uchar oserial[21];
2054
2055         s = aoeidentify(d, id);
2056         if(s == -1)
2057                 return -1;
2058         osectors = d->realbsize;
2059         memmove(oserial, d->serial, sizeof d->serial);
2060
2061         idmove(d->serial, id+10, 20);
2062         idmove(d->firmware, id+23, 8);
2063         idmove(d->model, id+27, 40);
2064         /* idss() */
2065         /* d->wwn = idwwn(d, id); */
2066
2067         s *= Aoesectsz;
2068         if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
2069                 d->bsize = s;
2070                 d->realbsize = s;
2071 //              d->mediachange = 1;
2072                 newvers(d);
2073         }
2074         return 0;
2075 }
2076
2077 static void
2078 atarsp(Block *b)
2079 {
2080         uint n;
2081         ushort major;
2082         Aoeata *ahin, *ahout;
2083         Aoehdr *h;
2084         Aoedev *d;
2085         Frame *f;
2086         Srb *srb;
2087
2088         h = (Aoehdr*)b->rp;
2089         major = nhgets(h->major);
2090         d = mm2dev(major, h->minor);
2091         if(d == nil)
2092                 return;
2093         ahin = (Aoeata*)(b->rp + Aoehsz);
2094         qlock(d);
2095         if(waserror()){
2096                 qunlock(d);
2097                 nexterror();
2098         }
2099         n = nhgetl(h->tag);
2100         f = getframe(d, n);
2101         if(f == nil){
2102                 dprint("%æ: unexpected response; tag %ux\n", d, n);
2103                 goto bail;
2104         }
2105         rtupdate(f->dl, tsince(f->tag));
2106         ahout = (Aoeata*)(f->hdr + Aoehsz);
2107         srb = f->srb;
2108
2109         if(ahin->cmdstat & 0xa9){
2110                 eventlog("%æ: ata error cmd %.2ux stat %.2ux\n",
2111                         d, ahout->cmdstat, ahin->cmdstat);
2112                 if(srb)
2113                         srb->error = Eio;
2114         } else {
2115                 n = ahout->scnt * Aoesectsz;
2116                 switch(ahout->cmdstat){
2117                 case Crd:
2118                 case Crdext:
2119                         if(BLEN(b) - (Aoehsz + Aoeatasz) != n){
2120                                 eventlog("%æ: misread blen %ld expect %d\n",
2121                                         d, BLEN(b), n);
2122                                 goto bail;
2123                         }
2124                         memmove(f->dp, b->rp + Aoehsz + Aoeatasz, n);
2125                 case Cwr:
2126                 case Cwrext:
2127                         if(n > Dbcnt)
2128                                 f->nl->lostjumbo = 0;
2129                         if(f->bcnt -= n){
2130                                 f->lba += n / Aoesectsz;
2131                                 f->dp = (uchar*)f->dp + n;
2132                                 resend(d, f);
2133                                 goto bail;
2134                         }
2135                         break;
2136                 case Cid:
2137                         if(BLEN(b) - (Aoehsz + Aoeatasz) < 512){
2138                                 eventlog("%æ: runt identify blen %ld expect %d\n",
2139                                         d, BLEN(b), 512 + Aoehsz + Aoeatasz);
2140                                 goto bail;
2141                         }
2142                         identify(d, (ushort*)(b->rp + Aoehsz + Aoeatasz));
2143                         free(srb);              /* BOTCH */
2144                         srb = nil;
2145                         break;
2146                 default:
2147                         eventlog("%æ: unknown ata command %.2ux \n",
2148                                 d, ahout->cmdstat);
2149                 }
2150         }
2151
2152         f->srb = nil;
2153         if(srb){
2154                 srb->nout--;
2155                 srbwakeup(srb);
2156         }
2157         f->tag = Tfree;
2158         d->nout--;
2159
2160         work(d);
2161 bail:
2162         poperror();
2163         qunlock(d);
2164 }
2165
2166 static void
2167 netrdaoeproc(void *v)
2168 {
2169         int idx;
2170         char name[Maxpath+1], *s;
2171         Aoehdr *h;
2172         Block *b;
2173         Netlink *nl;
2174
2175         nl = (Netlink*)v;
2176         idx = nl - netlinks.nl;
2177         netlinks.reader[idx] = 1;
2178         kstrcpy(name, nl->path, Maxpath);
2179
2180         if(waserror()){
2181                 eventlog("netrdaoe@%s: exiting: %s\n", name, up->errstr);
2182                 netlinks.reader[idx] = 0;
2183                 wakeup(netlinks.rendez + idx);
2184                 pexit(up->errstr, 1);
2185         }
2186         if(autodiscover)
2187                 discover(0xffff, 0xff);
2188         for (;;) {
2189                 if((nl->flag & Dup) == 0)
2190                         error("netlink is down");
2191                 if(nl->dc == nil)
2192                         panic("netrdaoe: nl->dc == nil");
2193                 b = devtab[nl->dc->type]->bread(nl->dc, 1<<16, 0);
2194                 if(b == nil)
2195                         error("network read");
2196                 h = (Aoehdr*)b->rp;
2197                 if(h->verflag & AFrsp)
2198                         if(s = aoeerror(h)){
2199                                 eventlog("%s: %s\n", nl->path, s);
2200                                 errrsp(b, s);
2201                         }else if(h->cmd == ACata)
2202                                 atarsp(b);
2203                         else if(h->cmd == ACconfig)
2204                                 qcfgrsp(b, nl);
2205                         else if((h->cmd & 0xf0) == 0){
2206                                 eventlog("%s: unknown cmd %d\n",
2207                                         nl->path, h->cmd);
2208                                 errrsp(b, "unknown command");
2209                         }
2210                 freeb(b);
2211         }
2212 }
2213
2214 static void
2215 getaddr(char *path, uchar *ea)
2216 {
2217         int n;
2218         char buf[2*Eaddrlen+1];
2219         Chan *c;
2220
2221         uprint("%s/addr", path);
2222         c = namec(up->genbuf, Aopen, OREAD, 0);
2223         if(waserror()) {
2224                 cclose(c);
2225                 nexterror();
2226         }
2227         if(c == nil)
2228                 panic("æ: getaddr: c == nil");
2229         n = devtab[c->type]->read(c, buf, sizeof buf-1, 0);
2230         poperror();
2231         cclose(c);
2232         buf[n] = 0;
2233         if(parseether(ea, buf) < 0)
2234                 error("parseether failure");
2235 }
2236
2237 static void
2238 netbind(char *path)
2239 {
2240         char addr[Maxpath];
2241         uchar ea[2*Eaddrlen+1];
2242         Chan *dc, *cc, *mtu;
2243         Netlink *nl;
2244
2245         snprint(addr, sizeof addr, "%s!0x%x", path, Aoetype);
2246         dc = chandial(addr, nil, nil, &cc);
2247         snprint(addr, sizeof addr, "%s/mtu", path);
2248         if(waserror())
2249                 mtu = nil;
2250         else {
2251                 mtu = namec(addr, Aopen, OREAD, 0);
2252                 poperror();
2253         }
2254
2255         if(waserror()){
2256                 cclose(dc);
2257                 cclose(cc);
2258                 if(mtu)
2259                         cclose(mtu);
2260                 nexterror();
2261         }
2262         if(dc == nil  || cc == nil)
2263                 error(Enonexist);
2264         getaddr(path, ea);
2265         nl = addnet(path, cc, dc, mtu, ea);
2266         snprint(addr, sizeof addr, "netrdaoe@%s", path);
2267         kproc(addr, netrdaoeproc, nl);
2268         poperror();
2269 }
2270
2271 static int
2272 unbound(void *v)
2273 {
2274         return *(int*)v != 0;
2275 }
2276
2277 static void
2278 netunbind(char *path)
2279 {
2280         int i, idx;
2281         Aoedev *d, *p, *next;
2282         Chan *dc, *cc;
2283         Devlink *l;
2284         Frame *f;
2285         Netlink *n, *e;
2286
2287         n = netlinks.nl;
2288         e = n + nelem(netlinks.nl);
2289
2290         lock(&netlinks);
2291         for(; n < e; n++)
2292                 if(n->dc && strcmp(n->path, path) == 0)
2293                         break;
2294         unlock(&netlinks);
2295         if(n == e)
2296                 error("device not bound");
2297
2298         /*
2299          * hunt down devices using this interface; disable
2300          * this also terminates the reader.
2301          */
2302         idx = n - netlinks.nl;
2303         wlock(&devs);
2304         for(d = devs.d; d; d = d->next){
2305                 qlock(d);
2306                 for(i = 0; i < d->ndl; i++){
2307                         l = d->dl + i;
2308                         if(l->nl == n)
2309                                 l->flag &= ~Dup;
2310                 }
2311                 qunlock(d);
2312         }
2313         n->flag &= ~Dup;
2314         wunlock(&devs);
2315
2316         /* confirm reader is down. */
2317         while(waserror())
2318                 ;
2319         sleep(netlinks.rendez + idx, unbound, netlinks.reader + idx);
2320         poperror();
2321
2322         /* reschedule packets. */
2323         wlock(&devs);
2324         for(d = devs.d; d; d = d->next){
2325                 qlock(d);
2326                 for(i = 0; i < d->nframes; i++){
2327                         f = d->frames + i;
2328                         if(f->tag != Tfree && f->nl == n)
2329                                 resend(d, f);
2330                 }
2331                 qunlock(d);
2332         }
2333         wunlock(&devs);
2334
2335         /* squeeze devlink pool.  (we assert nobody is using them now) */
2336         wlock(&devs);
2337         for(d = devs.d; d; d = d->next){
2338                 qlock(d);
2339                 for(i = 0; i < d->ndl; i++){
2340                         l = d->dl + i;
2341                         if(l->nl == n)
2342                                 memmove(l, l + 1, sizeof *l * (--d->ndl - i));
2343                 }
2344                 qunlock(d);
2345         }
2346         wunlock(&devs);
2347
2348         /* close device link. */
2349         lock(&netlinks);
2350         dc = n->dc;
2351         cc = n->cc;
2352         if(n->mtu)
2353                 cclose(n->mtu);
2354         memset(n, 0, sizeof *n);
2355         unlock(&netlinks);
2356
2357         cclose(dc);
2358         cclose(cc);
2359
2360         /* squeeze orphan devices */
2361         wlock(&devs);
2362         for(p = d = devs.d; d; d = next){
2363                 next = d->next;
2364                 if(d->ndl > 0){
2365                         p = d;
2366                         continue;
2367                 }
2368                 qlock(d);
2369                 downdev(d, "orphan");
2370                 qunlock(d);
2371                 if(p != devs.d)
2372                         p->next = next;
2373                 else{
2374                         devs.d = next;
2375                         p = devs.d;
2376                 }
2377                 free(d->frames);
2378                 free(d);
2379                 dropunit();
2380         }
2381         wunlock(&devs);
2382 }
2383
2384 static void
2385 strtoss(char *f, uint *shelf, uint *slot)
2386 {
2387         char *s;
2388
2389         *shelf = 0xffff;
2390         *slot = 0xff;
2391         if(!f)
2392                 return;
2393         *shelf = strtol(f, &s, 0);
2394         if(s == f || *shelf > 0xffff)
2395                 error("bad shelf");
2396         f = s;
2397         if(*f++ == '.'){
2398                 *slot = strtol(f, &s, 0);
2399                 if(s == f || *slot > 0xff)
2400                         error("bad slot");
2401         }
2402 }
2403
2404 static void
2405 discoverstr(char *f)
2406 {
2407         uint shelf, slot;
2408
2409         strtoss(f, &shelf, &slot);
2410         discover(shelf, slot);
2411 }
2412
2413 static void
2414 removedev(Aoedev *d)
2415 {
2416         int i;
2417         Aoedev *p;
2418
2419         wlock(&devs);
2420         p = 0;
2421         if(d != devs.d)
2422         for(p = devs.d; p; p = p->next)
2423                 if(p->next == d)
2424                         break;
2425         qlock(d);
2426         d->flag &= ~Dup;
2427         newvers(d);
2428         d->ndl = 0;
2429         qunlock(d);
2430         for(i = 0; i < d->nframes; i++)
2431                 frameerror(d, d->frames+i, Enotup);
2432
2433         if(p)
2434                 p->next = d->next;
2435         else
2436                 devs.d = d->next;
2437         free(d->frames);
2438         free(d);
2439         dropunit();
2440         wunlock(&devs);
2441 }
2442
2443
2444 static void
2445 aoeremove(Chan *c)
2446 {
2447         switch(TYPE(c->qid)){
2448         default:
2449         case Qzero:
2450         case Qtopdir:
2451         case Qtoplog:
2452         case Qtopctl:
2453         case Qctl:
2454         case Qdata:
2455         case Qconfig:
2456         case Qident:
2457                 error(Eperm);
2458         case Qunitdir:
2459                 removedev(unit2dev(UNIT(c->qid)));
2460                 break;
2461         }
2462 }
2463
2464 static void
2465 removestr(char *f)
2466 {
2467         uint shelf, slot;
2468         Aoedev *d;
2469
2470         strtoss(f, &shelf, &slot);
2471         wlock(&devs);
2472         for(d = devs.d; d; d = d->next)
2473                 if(shelf == d->major && slot == d->minor){
2474                         wunlock(&devs); /* BOTCH */
2475                         removedev(d);
2476                         return;
2477                 }
2478         wunlock(&devs);
2479         error("device not bound");
2480 }
2481
2482 static long
2483 topctlwrite(void *db, long n)
2484 {
2485         enum {
2486                 Autodiscover,
2487                 Bind,
2488                 Debug,
2489                 Discover,
2490                 Rediscover,
2491                 Remove,
2492                 Unbind,
2493         };
2494         char *f;
2495         Cmdbuf *cb;
2496         Cmdtab *ct;
2497         static Cmdtab cmds[] = {
2498                 { Autodiscover, "autodiscover", 0       },
2499                 { Bind,         "bind",         2       },
2500                 { Debug,        "debug",        0       },
2501                 { Discover,     "discover",     0       },
2502                 { Rediscover,   "rediscover",   0       },
2503                 { Remove,       "remove",       2       },
2504                 { Unbind,       "unbind",       2       },
2505         };
2506
2507         cb = parsecmd(db, n);
2508         if(waserror()){
2509                 free(cb);
2510                 nexterror();
2511         }
2512         ct = lookupcmd(cb, cmds, nelem(cmds));
2513         f = cb->f[1];
2514         switch(ct->index){
2515         case Autodiscover:
2516                 autodiscover = toggle(f, autodiscover, 1);
2517                 break;
2518         case Bind:
2519                 netbind(f);
2520                 break;
2521         case Debug:
2522                 debug = toggle(f, debug, 1);
2523                 break;
2524         case Discover:
2525                 discoverstr(f);
2526                 break;
2527         case Rediscover:
2528                 rediscover = toggle(f, rediscover, 1);
2529                 break;
2530         case Remove:
2531                 removestr(f);   /* depricated */
2532                 break;
2533         case Unbind:
2534                 netunbind(f);
2535                 break;
2536         }
2537         poperror();
2538         free(cb);
2539         return n;
2540 }
2541
2542 static long
2543 aoewrite(Chan *c, void *db, long n, vlong off)
2544 {
2545         switch(TYPE(c->qid)){
2546         default:
2547         case Qzero:
2548         case Qtopdir:
2549         case Qunitdir:
2550         case Qtoplog:
2551                 error(Eperm);
2552         case Qtopctl:
2553                 return topctlwrite(db, n);
2554         case Qctl:
2555         case Qdata:
2556         case Qconfig:
2557         case Qident:
2558                 return unitwrite(c, db, n, off);
2559         }
2560 }
2561
2562 Dev aoedevtab = {
2563         L'æ',
2564         "aoe",
2565
2566         devreset,
2567         devinit,
2568         devshutdown,
2569         aoeattach,
2570         aoewalk,
2571         aoestat,
2572         aoeopen,
2573         devcreate,
2574         aoeclose,
2575         aoeread,
2576         devbread,
2577         aoewrite,
2578         devbwrite,
2579         aoeremove,
2580         devwstat,
2581         devpower,
2582         devconfig,
2583 };