]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devaoe.c
23f621a05aa8a5109c740f647abb8fc648f8d969
[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
245 static Srb*
246 srballoc(ulong sz)
247 {
248         Srb *srb;
249
250         srb = smalloc(sizeof *srb+sz);
251         srb->state = Alloc;
252         srb->dp = srb->data = srb+1;
253         srb->ticksent = Ticks;
254         return srb;
255 }
256
257 static Srb*
258 srbkalloc(void *db, ulong)
259 {
260         Srb *srb;
261
262         srb = smalloc(sizeof *srb);
263         srb->state = Alloc;
264         srb->dp = srb->data = db;
265         srb->ticksent = Ticks;
266         return srb;
267 }
268
269 static int
270 srbready(void *v)
271 {
272         Srb *s;
273
274         s = v;
275         return s->nout == 0 && (s->len == 0 || s->error != nil);
276 }
277
278 static void
279 srbfree(Srb *srb)
280 {
281         int n;
282
283         for(n = 0; srb->state != Free; n++)
284                 sched();
285         free(srb);
286 }
287
288 /* under Aoedev qlock() so setting of srb->state is safe */
289 static void
290 srbwakeup(Srb *srb)
291 {
292         if(srbready(srb)){
293                 assert(srb->state == Alloc);
294                 wakeup(srb);
295                 srb->state = Free;
296         }
297 }
298
299 static void
300 srbcleanout(Aoedev *d, Srb *srb)
301 {
302         Srb *x, **ll;
303
304         if(srb == d->inprocess)
305                 d->inprocess = nil;
306         else
307                 for(ll = &d->head; x = *ll; ll = &x->next){
308                         d->tail = x;
309                         if(x == srb)
310                                 *ll = x->next;
311                 }
312 }
313
314 static void
315 srberror(Aoedev *d, Srb *srb, char *s)
316 {
317         srbcleanout(d, srb);
318         srb->error = s;
319         srbwakeup(srb);
320 }
321
322 static void
323 frameerror(Aoedev *d, Frame *f, char *s)
324 {
325         Srb *srb;
326
327         if(f->tag == Tfree)
328                 return;
329         srb = f->srb;
330         f->srb = nil;
331         f->tag = Tfree;         /* don't get fooled by way-slow responses */
332         if(!srb)
333                 return;
334         srb->nout--;
335         srberror(d, srb, s);
336         d->nout--;
337 }
338
339 static char*
340 unitname(Aoedev *d)
341 {
342         uprint("%ud.%ud", d->major, d->minor);
343         return up->genbuf;
344 }
345
346 static long
347 eventlogread(void *a, long n)
348 {
349         int len;
350         char *p, *buf;
351
352         buf = smalloc(Eventlen);
353         qlock(&events);
354         lock(&events);
355         p = events.rp;
356         len = *p;
357         if(len == 0){
358                 n = 0;
359                 unlock(&events);
360         } else {
361                 if(n > len)
362                         n = len;
363                 /* can't move directly into pageable space with events lock held */
364                 memmove(buf, p+1, n);
365                 *p = 0;
366                 events.rp = p += Eventlen;
367                 if(p >= events.buf + sizeof events.buf)
368                         events.rp = events.buf;
369                 unlock(&events);
370
371                 /* the concern here is page faults in memmove below */
372                 if(waserror()){
373                         free(buf);
374                         qunlock(&events);
375                         nexterror();
376                 }
377                 memmove(a, buf, n);
378                 poperror();
379         }
380         free(buf);
381         qunlock(&events);
382         return n;
383 }
384
385 static int
386 eventlog(char *fmt, ...)
387 {
388         int dragrp, n;
389         char *p;
390         va_list arg;
391
392         lock(&events);
393         p = events.wp;
394         dragrp = *p++;
395         va_start(arg, fmt);
396         n = vsnprint(p, Eventlen-1, fmt, arg);
397         *--p = n;
398         p = events.wp += Eventlen;
399         if(p >= events.buf + sizeof events.buf)
400                 p = events.wp = events.buf;
401         if(dragrp)
402                 events.rp = p;
403         unlock(&events);
404         wakeup(&events);
405         return n;
406 }
407
408 static int
409 eventcount(void)
410 {
411         uint n;
412
413         lock(&events);
414         if(*events.rp == 0)
415                 n = 0;
416         else
417                 n = events.wp - events.rp & Nevents - 1;
418         unlock(&events);
419         return n/Eventlen;
420 }
421
422 static int
423 tsince(int tag)
424 {
425         int n;
426
427         n = Ticks & 0xffff;
428         n -= tag & 0xffff;
429         if(n < 0)
430                 n += 1<<16;
431         return n;
432 }
433
434 static int
435 newtag(Aoedev *d)
436 {
437         int t;
438
439         do {
440                 t = ++d->lasttag << 16;
441                 t |= Ticks & 0xffff;
442         } while (t == Tfree || t == Tmgmt);
443         return t;
444 }
445
446 static void
447 downdev(Aoedev *d, char *err)
448 {
449         Frame *f, *e;
450
451         d->flag &= ~Dup;
452         f = d->frames;
453         e = f + d->nframes;
454         for(; f < e; f++)
455                 frameerror(d, f, Enotup);
456         d->inprocess = nil;
457         eventlog("%æ: removed; %s\n", d, err);
458 }
459
460 static Block*
461 allocfb(Frame *f)
462 {
463         int len;
464         Block *b;
465
466         len = f->nhdr + f->dlen;
467         if(len < ETHERMINTU)
468                 len = ETHERMINTU;
469         b = allocb(len);
470         memmove(b->wp, f->hdr, f->nhdr);
471         if(f->dlen)
472                 memmove(b->wp + f->nhdr, f->dp, f->dlen);
473         b->wp += len;
474         return b;
475 }
476
477 static void
478 putlba(Aoeata *a, vlong lba)
479 {
480         uchar *c;
481
482         c = a->lba;
483         c[0] = lba;
484         c[1] = lba >> 8;
485         c[2] = lba >> 16;
486         c[3] = lba >> 24;
487         c[4] = lba >> 32;
488         c[5] = lba >> 40;
489 }
490
491 static Devlink*
492 pickdevlink(Aoedev *d)
493 {
494         ulong i, n;
495         Devlink *l;
496
497         for(i = 0; i < d->ndl; i++){
498                 n = d->dlidx++ % d->ndl;
499                 l = d->dl + n;
500                 if(l && l->flag & Dup)
501                         return l;
502         }
503         return 0;
504 }
505
506 static int
507 pickea(Devlink *l)
508 {
509         if(l == 0)
510                 return -1;
511         if(l->nea == 0)
512                 return -1;
513         return l->eaidx++ % l->nea;
514 }
515
516 /*
517  * would like this to depend on the chan (srb).
518  * not possible in the current structure.
519  */
520 #define Nofail(d, s)    (((d)->flag&Dnofail) == Dnofail)
521
522 static int
523 hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd)
524 {
525         int i;
526         Devlink *l;
527
528         if(f->srb)
529         if((long)(Ticks-f->srb->ticksent) > Srbtimeout){
530                 eventlog("%æ: srb timeout\n", d);
531                 if(cmd == ACata && Nofail(d, s))
532                         f->srb->ticksent = Ticks;
533                 else
534                         frameerror(d, f, Etimedout);
535                 return -1;
536         }
537         l = pickdevlink(d);
538         i = pickea(l);
539         if(i == -1){
540                 if(!(cmd == ACata && f->srb && Nofail(d, s)))
541                         downdev(d, "resend fails; no netlink/ea");
542                 return -1;
543         }
544         memmove(h->dst, l->eatab[i], Eaddrlen);
545         memmove(h->src, l->nl->ea, sizeof h->src);
546         hnputs(h->type, Aoetype);
547         h->verflag = Aoever << 4;
548         h->error = 0;
549         hnputs(h->major, d->major);
550         h->minor = d->minor;
551         h->cmd = cmd;
552
553         hnputl(h->tag, f->tag = newtag(d));
554         f->dl = l;
555         f->nl = l->nl;
556         f->eaidx = i;
557         f->ticksent = Ticks;
558
559         return f->tag;
560 }
561
562 static int
563 resend(Aoedev *d, Frame *f)
564 {
565         ulong n;
566         Aoeata *a;
567         Aoehdr *h;
568
569         h = (Aoehdr*)f->hdr;
570         if(hset(d, f, h, h->cmd) == -1)
571                 return -1;
572         a = (Aoeata*)(f->hdr + Aoehsz);
573         n = f->bcnt;
574         if(n > d->maxbcnt){
575                 n = d->maxbcnt;         /* mtu mismatch (jumbo fail?) */
576                 if(f->dlen > n)
577                         f->dlen = n;
578         }
579         a->scnt = n / Aoesectsz;
580         f->dl->resent++;
581         f->dl->npkt++;
582         if(waserror())
583                 /* should remove the netlink */
584                 return -1;
585         devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
586         poperror();
587         return 0;
588 }
589
590 static void
591 discover(uint major, uint minor)
592 {
593         Aoehdr *h;
594         Block *b;
595         Netlink *nl, *e;
596
597         nl = netlinks.nl;
598         e = nl + nelem(netlinks.nl);
599         for(; nl < e; nl++){
600                 if(nl->cc == nil)
601                         continue;
602                 b = allocb(ETHERMINTU);
603                 if(waserror()){
604                         freeb(b);
605                         nexterror();
606                 }
607                 b->wp = b->rp + ETHERMINTU;
608                 memset(b->rp, 0, ETHERMINTU);
609                 h = (Aoehdr*)b->rp;
610                 memset(h->dst, 0xff, sizeof h->dst);
611                 memmove(h->src, nl->ea, sizeof h->src);
612                 hnputs(h->type, Aoetype);
613                 h->verflag = Aoever << 4;
614                 hnputs(h->major, major);
615                 h->minor = minor;
616                 h->cmd = ACconfig;
617                 poperror();
618                 devtab[nl->dc->type]->bwrite(nl->dc, b, 0);
619         }
620 }
621
622 /*
623  * Check all frames on device and resend any frames that have been
624  * outstanding for 200% of the device round trip time average.
625  */
626 static void
627 aoesweepproc(void*)
628 {
629         ulong i, tx, timeout, nbc;
630         vlong starttick;
631         enum { Nms = 100, Nbcms = 30*1000, };
632         uchar *ea;
633         Aoeata *a;
634         Aoedev *d;
635         Devlink *l;
636         Frame *f, *e;
637
638         nbc = Nbcms/Nms;
639 loop:
640         if(nbc-- == 0){
641                 if(rediscover && !waserror()){
642                         discover(0xffff, 0xff);
643                         poperror();
644                 }
645                 nbc = Nbcms/Nms;
646         }
647         starttick = Ticks;
648         rlock(&devs);
649         for(d = devs.d; d; d = d->next){
650                 if(!canqlock(d))
651                         continue;
652                 if(!UP(d)){
653                         qunlock(d);
654                         continue;
655                 }
656                 tx = 0;
657                 f = d->frames;
658                 e = f + d->nframes;
659                 for (; f < e; f++){
660                         if(f->tag == Tfree)
661                                 continue;
662                         l = f->dl;
663                         timeout = l->rttavg << 1;
664                         i = tsince(f->tag);
665                         if(i < timeout)
666                                 continue;
667                         if(d->nout == d->maxout){
668                                 if(d->maxout > 1)
669                                         d->maxout--;
670                                 d->lastwadj = Ticks;
671                         }
672                         a = (Aoeata*)(f->hdr + Aoehsz);
673                         if(a->scnt > Dbcnt / Aoesectsz &&
674                            ++f->nl->lostjumbo > (d->nframes << 1)){
675                                 ea = f->dl->eatab[f->eaidx];
676                                 eventlog("%æ: jumbo failure on %s:%E; %llud\n",
677                                         d, f->nl->path, ea, f->lba);
678                                 d->maxbcnt = Dbcnt;
679                                 d->flag &= ~Djumbo;
680                         }
681                         resend(d, f);
682                         if(tx++ == 0){
683                                 if((l->rttavg <<= 1) > Rtmax)
684                                         l->rttavg = Rtmax;
685                                 eventlog("%æ: rtt %ldms\n", d, Tk2ms(l->rttavg));
686                         }
687                 }
688                 if(d->nout == d->maxout && d->maxout < d->nframes &&
689                    TK2MS(Ticks-d->lastwadj) > 10*1000){
690                         d->maxout++;
691                         d->lastwadj = Ticks;
692                 }
693                 qunlock(d);
694         }
695         runlock(&devs);
696         i = Nms - TK2MS(Ticks - starttick);
697         if(i > 0)
698                 tsleep(&up->sleep, return0, 0, i);
699         goto loop;
700 }
701
702 static int
703 fmtæ(Fmt *f)
704 {
705         char buf[16];
706         Aoedev *d;
707
708         d = va_arg(f->args, Aoedev*);
709         snprint(buf, sizeof buf, "aoe%ud.%ud", d->major, d->minor);
710         return fmtstrcpy(f, buf);
711 }
712
713 static void netbind(char *path);
714
715 static void
716 aoecfg(void)
717 {
718         char *p, *f[32], buf[24], ifbuf[64];
719         int n, i;
720
721         if((p = getconf("aoeif")) == nil)
722                 return;
723         strncpy(ifbuf, p, sizeof(ifbuf)-1);
724         ifbuf[sizeof(ifbuf)-1] = 0;
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 = smalloc(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)-1);
1707         nl->path[sizeof(nl->path)-1] = 0;
1708         memmove(nl->ea, ea, sizeof(nl->ea));
1709         poperror();
1710         nl->flag |= Dup;
1711         unlock(&netlinks);
1712         return nl;
1713 }
1714
1715 static int
1716 newunit(void)
1717 {
1718         int x;
1719
1720         lock(&units);
1721         if(units.ref == Maxunits)
1722                 x = -1;
1723         else
1724                 x = units.ref++;
1725         unlock(&units);
1726         return x;
1727 }
1728
1729 static int
1730 dropunit(void)
1731 {
1732         int x;
1733
1734         lock(&units);
1735         x = --units.ref;
1736         unlock(&units);
1737         return x;
1738 }
1739
1740 /*
1741  * always allocate max frames.  maxout may change.
1742  */
1743 static Aoedev*
1744 newdev(uint major, uint minor, int n)
1745 {
1746         Aoedev *d;
1747         Frame *f, *e;
1748
1749         d = malloc(sizeof *d);
1750         f = malloc(sizeof *f*Maxframes);
1751         if(!d || !f) {
1752                 free(d);
1753                 free(f);
1754                 error("aoe device allocation failure");
1755         }
1756         d->nframes = n;
1757         d->frames = f;
1758         for (e = f + Maxframes; f < e; f++)
1759                 f->tag = Tfree;
1760         d->maxout = n;
1761         d->major = major;
1762         d->minor = minor;
1763         d->maxbcnt = Dbcnt;
1764         d->flag = Djumbo;
1765         d->maxmtu = Maxmtu;
1766         d->unit = newunit();            /* bzzt.  inaccurate if units removed */
1767         if(d->unit == -1){
1768                 free(d);
1769                 free(d->frames);
1770                 error("too many units");
1771         }
1772         d->dl = d->dltab;
1773         return d;
1774 }
1775
1776 static Aoedev*
1777 mm2dev(uint major, uint minor)
1778 {
1779         Aoedev *d;
1780
1781         rlock(&devs);
1782         for(d = devs.d; d; d = d->next)
1783                 if(d->major == major && d->minor == minor){
1784                         runlock(&devs);
1785                         return d;
1786                 }
1787         runlock(&devs);
1788         eventlog("mm2dev: %ud.%ud not found\n", major, minor);
1789         return nil;
1790 }
1791
1792 /* Find the device in our list.  If not known, add it */
1793 static Aoedev*
1794 getdev(uint major, uint minor, int n)
1795 {
1796         Aoedev *d;
1797
1798         if(major == 0xffff || minor == 0xff)
1799                 return 0;
1800         wlock(&devs);
1801         if(waserror()){
1802                 wunlock(&devs);
1803                 nexterror();
1804         }
1805         for(d = devs.d; d; d = d->next)
1806                 if(d->major == major && d->minor == minor)
1807                         break;
1808         if(d == nil) {
1809                 d = newdev(major, minor, n);
1810                 d->next = devs.d;
1811                 devs.d = d;
1812         }
1813         poperror();
1814         wunlock(&devs);
1815         return d;
1816 }
1817
1818 static void
1819 ataident(Aoedev *d)
1820 {
1821         Aoeata *a;
1822         Aoehdr *h;
1823         Frame *f;
1824
1825         f = freeframe(d);
1826         if(f == nil)
1827                 return;
1828         f->nhdr = Aoehsz + Aoeatasz;
1829         memset(f->hdr, 0, f->nhdr);
1830         h = (Aoehdr*)f->hdr;
1831         if(hset(d, f, h, ACata) == -1)
1832                 return;
1833         a = (Aoeata*)(f->hdr + Aoehsz);
1834         f->srb = srbkalloc(0, 0);
1835         a->cmdstat = Cid;       /* ata 6, page 110 */
1836         a->scnt = 1;
1837         a->lba[3] = 0xa0;
1838         d->nout++;
1839         f->dl->npkt++;
1840         f->bcnt = 512;
1841         f->dlen = 0;
1842         if(waserror()){
1843                 srbfree(f->srb);
1844                 d->nout--;
1845                 f->tag = Tfree;
1846         }else{
1847                 devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
1848                 poperror();
1849         }
1850 }
1851
1852 static int
1853 newdlea(Devlink *l, uchar *ea)
1854 {
1855         int i;
1856         uchar *t;
1857
1858         for(i = 0; i < Nea; i++){
1859                 t = l->eatab[i];
1860                 if(i == l->nea){
1861                         memmove(t, ea, Eaddrlen);
1862                         return l->nea++;
1863                 }
1864                 if(memcmp(t, ea, Eaddrlen) == 0)
1865                         return i;
1866         }
1867         return -1;
1868 }
1869
1870 static Devlink*
1871 newdevlink(Aoedev *d, Netlink *n, Aoehdr *h)
1872 {
1873         int i;
1874         Aoecfg *c;
1875         Devlink *l;
1876
1877         c = (Aoecfg*)((uchar*)h + Aoehsz);
1878         for(i = 0; i < Ndevlink; i++){
1879                 l = d->dl + i;
1880                 if(i == d->ndl){
1881                         d->ndl++;
1882                         newdlea(l, h->src);
1883                         l->datamtu = c->scnt*Aoesectsz;
1884                         l->nl = n;
1885                         l->flag |= Dup;
1886                         l->mintimer = Rtmin;
1887                         l->rttavg = Rtmax;
1888                         return l;
1889                 }
1890                 if(l->nl == n){
1891                         newdlea(l, h->src);
1892                         l->datamtu = c->scnt*Aoesectsz;
1893                         l->flag |= Dup;
1894                         return l;
1895                 }
1896         }
1897         eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, h->src);
1898         return 0;
1899 }
1900
1901 static void
1902 errrsp(Block *b, char *s)
1903 {
1904         int n;
1905         Aoedev *d;
1906         Aoehdr *h;
1907         Frame *f;
1908
1909         h = (Aoehdr*)b->rp;
1910         n = nhgetl(h->tag);
1911         if(n == Tmgmt || n == Tfree)
1912                 return;
1913         d = mm2dev(nhgets(h->major), h->minor);
1914         if(d == 0)
1915                 return;
1916         if(f = getframe(d, n))
1917                 frameerror(d, f, s);
1918 }
1919
1920 static void
1921 qcfgrsp(Block *b, Netlink *nl)
1922 {
1923         int cmd, cslen, blen;
1924         uint n, major;
1925         Aoedev *d;
1926         Aoehdr *h;
1927         Aoecfg *ch;
1928         Devlink *l;
1929         Frame *f;
1930         Srb *srb;
1931
1932         h = (Aoehdr*)b->rp;
1933         ch = (Aoecfg*)(b->rp + Aoehsz);
1934         major = nhgets(h->major);
1935         n = nhgetl(h->tag);
1936         if(n != Tmgmt){
1937                 d = mm2dev(major, h->minor);
1938                 if(d == nil)
1939                         return;
1940                 qlock(d);
1941                 f = getframe(d, n);
1942                 if(f == nil){
1943                         qunlock(d);
1944                         eventlog("%æ: unknown response tag %ux\n", d, n);
1945                         return;
1946                 }
1947                 cslen = nhgets(ch->cslen);
1948                 blen = BLEN(b) - (Aoehsz + Aoecfgsz);
1949                 if(cslen < blen && BLEN(b) > 60)
1950                         eventlog("%æ: cfgrsp: tag %.8ux oversized %d %d\n",
1951                                 d, n, cslen, blen);
1952                 if(cslen > blen){
1953                         eventlog("%æ: cfgrsp: tag %.8ux runt %d %d\n",
1954                                 d, n, cslen, blen);
1955                         cslen = blen;
1956                 }
1957                 memmove(f->dp, (uchar*)ch + Aoehsz + Aoecfgsz, cslen);
1958                 srb = f->srb;
1959                 f->dp = nil;
1960                 f->srb = nil;
1961                 if(srb){
1962                         srb->nout--;
1963                         srbwakeup(srb);
1964                         d->nout--;
1965                         f->tag = Tfree;
1966                 }
1967                 qunlock(d);
1968                 return;
1969         }
1970
1971         cmd = ch->verccmd & 0xf;
1972         if(cmd != 0){
1973                 eventlog("aoe%ud.%ud: cfgrsp: bad command %d\n", major, h->minor, cmd);
1974                 return;
1975         }
1976         n = nhgets(ch->bufcnt);
1977         if(n > Maxframes)
1978                 n = Maxframes;
1979
1980         if(waserror()){
1981                 eventlog("getdev: %ud.%ud ignored: %s\n", major, h->minor, up->errstr);
1982                 return;
1983         }
1984         d = getdev(major, h->minor, n);
1985         poperror();
1986         if(d == 0)
1987                 return;
1988
1989         qlock(d);
1990         *up->errstr = 0;
1991         if(waserror()){
1992                 qunlock(d);
1993                 eventlog("%æ: %s\n", d, up->errstr);
1994                 nexterror();
1995         }
1996
1997         l = newdevlink(d, nl, h);               /* add this interface. */
1998
1999         d->fwver = nhgets(ch->fwver);
2000         n = nhgets(ch->cslen);
2001         if(n > sizeof d->config)
2002                 n = sizeof d->config;
2003         d->nconfig = n;
2004         memmove(d->config, (uchar*)ch + Aoehsz + Aoecfgsz, n);
2005
2006         /* manually set mtu may be reset lower if conditions warrant */
2007         if(l){
2008                 n = devmaxdata(d);
2009                 if((d->flag & Djumbo) == 0)
2010                         n = Dbcnt;
2011                 if(n > d->maxmtu)
2012                         n = d->maxmtu;
2013                 if(n != d->maxbcnt){
2014                         eventlog("%æ: setting %d byte mtu on %s:%E\n",
2015                                 d, n, nl->path, nl->ea);
2016                         d->maxbcnt = n;
2017                 }
2018         }
2019         if(d->nopen == 0)
2020                 ataident(d);
2021         poperror();
2022         qunlock(d);
2023 }
2024
2025 static vlong
2026 aoeidentify(Aoedev *d, ushort *id)
2027 {
2028         vlong s;
2029
2030         s = idfeat(d, id);
2031         if(s == -1)
2032                 return -1;
2033         if((d->feat&Dlba) == 0){
2034                 dprint("%æ: no lba support\n", d);
2035                 return -1;
2036         }
2037         d->flag |= Dup;
2038         memmove(d->ident, id, sizeof d->ident);
2039         return s;
2040 }
2041
2042 static void
2043 newvers(Aoedev *d)
2044 {
2045         lock(&drivevers);
2046         d->vers = drivevers.ref++;
2047         unlock(&drivevers);
2048 }
2049
2050 static int
2051 identify(Aoedev *d, ushort *id)
2052 {
2053         vlong osectors, s;
2054         uchar oserial[21];
2055
2056         s = aoeidentify(d, id);
2057         if(s == -1)
2058                 return -1;
2059         osectors = d->realbsize;
2060         memmove(oserial, d->serial, sizeof d->serial);
2061
2062         idmove(d->serial, id+10, 20);
2063         idmove(d->firmware, id+23, 8);
2064         idmove(d->model, id+27, 40);
2065         /* idss() */
2066         /* d->wwn = idwwn(d, id); */
2067
2068         s *= Aoesectsz;
2069         if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
2070                 d->bsize = s;
2071                 d->realbsize = s;
2072 //              d->mediachange = 1;
2073                 newvers(d);
2074         }
2075         return 0;
2076 }
2077
2078 static void
2079 atarsp(Block *b)
2080 {
2081         uint n;
2082         ushort major;
2083         Aoeata *ahin, *ahout;
2084         Aoehdr *h;
2085         Aoedev *d;
2086         Frame *f;
2087         Srb *srb;
2088
2089         h = (Aoehdr*)b->rp;
2090         major = nhgets(h->major);
2091         d = mm2dev(major, h->minor);
2092         if(d == nil)
2093                 return;
2094         ahin = (Aoeata*)(b->rp + Aoehsz);
2095         qlock(d);
2096         if(waserror()){
2097                 qunlock(d);
2098                 nexterror();
2099         }
2100         n = nhgetl(h->tag);
2101         f = getframe(d, n);
2102         if(f == nil){
2103                 dprint("%æ: unexpected response; tag %ux\n", d, n);
2104                 goto bail;
2105         }
2106         rtupdate(f->dl, tsince(f->tag));
2107         ahout = (Aoeata*)(f->hdr + Aoehsz);
2108         srb = f->srb;
2109
2110         if(ahin->cmdstat & 0xa9){
2111                 eventlog("%æ: ata error cmd %.2ux stat %.2ux\n",
2112                         d, ahout->cmdstat, ahin->cmdstat);
2113                 if(srb)
2114                         srb->error = Eio;
2115         } else {
2116                 n = ahout->scnt * Aoesectsz;
2117                 switch(ahout->cmdstat){
2118                 case Crd:
2119                 case Crdext:
2120                         if(BLEN(b) - (Aoehsz + Aoeatasz) != n){
2121                                 eventlog("%æ: misread blen %ld expect %d\n",
2122                                         d, BLEN(b), n);
2123                                 goto bail;
2124                         }
2125                         memmove(f->dp, b->rp + Aoehsz + Aoeatasz, n);
2126                 case Cwr:
2127                 case Cwrext:
2128                         if(n > Dbcnt)
2129                                 f->nl->lostjumbo = 0;
2130                         if(f->bcnt -= n){
2131                                 f->lba += n / Aoesectsz;
2132                                 f->dp = (uchar*)f->dp + n;
2133                                 resend(d, f);
2134                                 goto bail;
2135                         }
2136                         break;
2137                 case Cid:
2138                         if(BLEN(b) - (Aoehsz + Aoeatasz) < 512){
2139                                 eventlog("%æ: runt identify blen %ld expect %d\n",
2140                                         d, BLEN(b), 512 + Aoehsz + Aoeatasz);
2141                                 goto bail;
2142                         }
2143                         identify(d, (ushort*)(b->rp + Aoehsz + Aoeatasz));
2144                         free(srb);              /* BOTCH */
2145                         srb = nil;
2146                         break;
2147                 default:
2148                         eventlog("%æ: unknown ata command %.2ux \n",
2149                                 d, ahout->cmdstat);
2150                 }
2151         }
2152
2153         f->srb = nil;
2154         if(srb){
2155                 srb->nout--;
2156                 srbwakeup(srb);
2157         }
2158         f->tag = Tfree;
2159         d->nout--;
2160
2161         work(d);
2162 bail:
2163         poperror();
2164         qunlock(d);
2165 }
2166
2167 static void
2168 netrdaoeproc(void *v)
2169 {
2170         int idx;
2171         char name[Maxpath+1], *s;
2172         Aoehdr *h;
2173         Block *b;
2174         Netlink *nl;
2175
2176         nl = (Netlink*)v;
2177         idx = nl - netlinks.nl;
2178         netlinks.reader[idx] = 1;
2179         kstrcpy(name, nl->path, Maxpath);
2180
2181         if(waserror()){
2182                 eventlog("netrdaoe@%s: exiting: %s\n", name, up->errstr);
2183                 netlinks.reader[idx] = 0;
2184                 wakeup(netlinks.rendez + idx);
2185                 pexit(up->errstr, 1);
2186         }
2187         if(autodiscover)
2188                 discover(0xffff, 0xff);
2189         for (;;) {
2190                 if((nl->flag & Dup) == 0)
2191                         error("netlink is down");
2192                 if(nl->dc == nil)
2193                         panic("netrdaoe: nl->dc == nil");
2194                 b = devtab[nl->dc->type]->bread(nl->dc, 1<<16, 0);
2195                 if(b == nil)
2196                         error("network read");
2197                 h = (Aoehdr*)b->rp;
2198                 if(h->verflag & AFrsp)
2199                         if(s = aoeerror(h)){
2200                                 eventlog("%s: %s\n", nl->path, s);
2201                                 errrsp(b, s);
2202                         }else if(h->cmd == ACata)
2203                                 atarsp(b);
2204                         else if(h->cmd == ACconfig)
2205                                 qcfgrsp(b, nl);
2206                         else if((h->cmd & 0xf0) == 0){
2207                                 eventlog("%s: unknown cmd %d\n",
2208                                         nl->path, h->cmd);
2209                                 errrsp(b, "unknown command");
2210                         }
2211                 freeb(b);
2212         }
2213 }
2214
2215 static void
2216 getaddr(char *path, uchar *ea)
2217 {
2218         int n;
2219         char buf[2*Eaddrlen+1];
2220         Chan *c;
2221
2222         uprint("%s/addr", path);
2223         c = namec(up->genbuf, Aopen, OREAD, 0);
2224         if(waserror()) {
2225                 cclose(c);
2226                 nexterror();
2227         }
2228         if(c == nil)
2229                 panic("æ: getaddr: c == nil");
2230         n = devtab[c->type]->read(c, buf, sizeof buf-1, 0);
2231         poperror();
2232         cclose(c);
2233         buf[n] = 0;
2234         if(parseether(ea, buf) < 0)
2235                 error("parseether failure");
2236 }
2237
2238 static void
2239 netbind(char *path)
2240 {
2241         char addr[Maxpath];
2242         uchar ea[2*Eaddrlen+1];
2243         Chan *dc, *cc, *mtu;
2244         Netlink *nl;
2245
2246         snprint(addr, sizeof addr, "%s!0x%x", path, Aoetype);
2247         dc = chandial(addr, nil, nil, &cc);
2248         snprint(addr, sizeof addr, "%s/mtu", path);
2249         if(waserror())
2250                 mtu = nil;
2251         else {
2252                 mtu = namec(addr, Aopen, OREAD, 0);
2253                 poperror();
2254         }
2255
2256         if(waserror()){
2257                 cclose(dc);
2258                 cclose(cc);
2259                 if(mtu)
2260                         cclose(mtu);
2261                 nexterror();
2262         }
2263         if(dc == nil  || cc == nil)
2264                 error(Enonexist);
2265         getaddr(path, ea);
2266         nl = addnet(path, cc, dc, mtu, ea);
2267         snprint(addr, sizeof addr, "netrdaoe@%s", path);
2268         kproc(addr, netrdaoeproc, nl);
2269         poperror();
2270 }
2271
2272 static int
2273 unbound(void *v)
2274 {
2275         return *(int*)v != 0;
2276 }
2277
2278 static void
2279 netunbind(char *path)
2280 {
2281         int i, idx;
2282         Aoedev *d, *p, *next;
2283         Chan *dc, *cc;
2284         Devlink *l;
2285         Frame *f;
2286         Netlink *n, *e;
2287
2288         n = netlinks.nl;
2289         e = n + nelem(netlinks.nl);
2290
2291         lock(&netlinks);
2292         for(; n < e; n++)
2293                 if(n->dc && strcmp(n->path, path) == 0)
2294                         break;
2295         unlock(&netlinks);
2296         if(n == e)
2297                 error("device not bound");
2298
2299         /*
2300          * hunt down devices using this interface; disable
2301          * this also terminates the reader.
2302          */
2303         idx = n - netlinks.nl;
2304         wlock(&devs);
2305         for(d = devs.d; d; d = d->next){
2306                 qlock(d);
2307                 for(i = 0; i < d->ndl; i++){
2308                         l = d->dl + i;
2309                         if(l->nl == n)
2310                                 l->flag &= ~Dup;
2311                 }
2312                 qunlock(d);
2313         }
2314         n->flag &= ~Dup;
2315         wunlock(&devs);
2316
2317         /* confirm reader is down. */
2318         while(waserror())
2319                 ;
2320         sleep(netlinks.rendez + idx, unbound, netlinks.reader + idx);
2321         poperror();
2322
2323         /* reschedule packets. */
2324         wlock(&devs);
2325         for(d = devs.d; d; d = d->next){
2326                 qlock(d);
2327                 for(i = 0; i < d->nframes; i++){
2328                         f = d->frames + i;
2329                         if(f->tag != Tfree && f->nl == n)
2330                                 resend(d, f);
2331                 }
2332                 qunlock(d);
2333         }
2334         wunlock(&devs);
2335
2336         /* squeeze devlink pool.  (we assert nobody is using them now) */
2337         wlock(&devs);
2338         for(d = devs.d; d; d = d->next){
2339                 qlock(d);
2340                 for(i = 0; i < d->ndl; i++){
2341                         l = d->dl + i;
2342                         if(l->nl == n)
2343                                 memmove(l, l + 1, sizeof *l * (--d->ndl - i));
2344                 }
2345                 qunlock(d);
2346         }
2347         wunlock(&devs);
2348
2349         /* close device link. */
2350         lock(&netlinks);
2351         dc = n->dc;
2352         cc = n->cc;
2353         if(n->mtu)
2354                 cclose(n->mtu);
2355         memset(n, 0, sizeof *n);
2356         unlock(&netlinks);
2357
2358         cclose(dc);
2359         cclose(cc);
2360
2361         /* squeeze orphan devices */
2362         wlock(&devs);
2363         for(p = d = devs.d; d; d = next){
2364                 next = d->next;
2365                 if(d->ndl > 0){
2366                         p = d;
2367                         continue;
2368                 }
2369                 qlock(d);
2370                 downdev(d, "orphan");
2371                 qunlock(d);
2372                 if(p != devs.d)
2373                         p->next = next;
2374                 else{
2375                         devs.d = next;
2376                         p = devs.d;
2377                 }
2378                 free(d->frames);
2379                 free(d);
2380                 dropunit();
2381         }
2382         wunlock(&devs);
2383 }
2384
2385 static void
2386 strtoss(char *f, uint *shelf, uint *slot)
2387 {
2388         char *s;
2389
2390         *shelf = 0xffff;
2391         *slot = 0xff;
2392         if(!f)
2393                 return;
2394         *shelf = strtol(f, &s, 0);
2395         if(s == f || *shelf > 0xffff)
2396                 error("bad shelf");
2397         f = s;
2398         if(*f++ == '.'){
2399                 *slot = strtol(f, &s, 0);
2400                 if(s == f || *slot > 0xff)
2401                         error("bad slot");
2402         }
2403 }
2404
2405 static void
2406 discoverstr(char *f)
2407 {
2408         uint shelf, slot;
2409
2410         strtoss(f, &shelf, &slot);
2411         discover(shelf, slot);
2412 }
2413
2414 static void
2415 removedev(Aoedev *d)
2416 {
2417         int i;
2418         Aoedev *p;
2419
2420         wlock(&devs);
2421         p = 0;
2422         if(d != devs.d)
2423         for(p = devs.d; p; p = p->next)
2424                 if(p->next == d)
2425                         break;
2426         qlock(d);
2427         d->flag &= ~Dup;
2428         newvers(d);
2429         d->ndl = 0;
2430         qunlock(d);
2431         for(i = 0; i < d->nframes; i++)
2432                 frameerror(d, d->frames+i, Enotup);
2433
2434         if(p)
2435                 p->next = d->next;
2436         else
2437                 devs.d = d->next;
2438         free(d->frames);
2439         free(d);
2440         dropunit();
2441         wunlock(&devs);
2442 }
2443
2444
2445 static void
2446 aoeremove(Chan *c)
2447 {
2448         switch(TYPE(c->qid)){
2449         default:
2450         case Qzero:
2451         case Qtopdir:
2452         case Qtoplog:
2453         case Qtopctl:
2454         case Qctl:
2455         case Qdata:
2456         case Qconfig:
2457         case Qident:
2458                 error(Eperm);
2459         case Qunitdir:
2460                 removedev(unit2dev(UNIT(c->qid)));
2461                 break;
2462         }
2463 }
2464
2465 static void
2466 removestr(char *f)
2467 {
2468         uint shelf, slot;
2469         Aoedev *d;
2470
2471         strtoss(f, &shelf, &slot);
2472         wlock(&devs);
2473         for(d = devs.d; d; d = d->next)
2474                 if(shelf == d->major && slot == d->minor){
2475                         wunlock(&devs); /* BOTCH */
2476                         removedev(d);
2477                         return;
2478                 }
2479         wunlock(&devs);
2480         error("device not bound");
2481 }
2482
2483 static long
2484 topctlwrite(void *db, long n)
2485 {
2486         enum {
2487                 Autodiscover,
2488                 Bind,
2489                 Debug,
2490                 Discover,
2491                 Rediscover,
2492                 Remove,
2493                 Unbind,
2494         };
2495         char *f;
2496         Cmdbuf *cb;
2497         Cmdtab *ct;
2498         static Cmdtab cmds[] = {
2499                 { Autodiscover, "autodiscover", 0       },
2500                 { Bind,         "bind",         2       },
2501                 { Debug,        "debug",        0       },
2502                 { Discover,     "discover",     0       },
2503                 { Rediscover,   "rediscover",   0       },
2504                 { Remove,       "remove",       2       },
2505                 { Unbind,       "unbind",       2       },
2506         };
2507
2508         cb = parsecmd(db, n);
2509         if(waserror()){
2510                 free(cb);
2511                 nexterror();
2512         }
2513         ct = lookupcmd(cb, cmds, nelem(cmds));
2514         f = cb->f[1];
2515         switch(ct->index){
2516         case Autodiscover:
2517                 autodiscover = toggle(f, autodiscover, 1);
2518                 break;
2519         case Bind:
2520                 netbind(f);
2521                 break;
2522         case Debug:
2523                 debug = toggle(f, debug, 1);
2524                 break;
2525         case Discover:
2526                 discoverstr(f);
2527                 break;
2528         case Rediscover:
2529                 rediscover = toggle(f, rediscover, 1);
2530                 break;
2531         case Remove:
2532                 removestr(f);   /* depricated */
2533                 break;
2534         case Unbind:
2535                 netunbind(f);
2536                 break;
2537         }
2538         poperror();
2539         free(cb);
2540         return n;
2541 }
2542
2543 static long
2544 aoewrite(Chan *c, void *db, long n, vlong off)
2545 {
2546         switch(TYPE(c->qid)){
2547         default:
2548         case Qzero:
2549         case Qtopdir:
2550         case Qunitdir:
2551         case Qtoplog:
2552                 error(Eperm);
2553         case Qtopctl:
2554                 return topctlwrite(db, n);
2555         case Qctl:
2556         case Qdata:
2557         case Qconfig:
2558         case Qident:
2559                 return unitwrite(c, db, n, off);
2560         }
2561 }
2562
2563 Dev aoedevtab = {
2564         L'æ',
2565         "aoe",
2566
2567         devreset,
2568         devinit,
2569         devshutdown,
2570         aoeattach,
2571         aoewalk,
2572         aoestat,
2573         aoeopen,
2574         devcreate,
2575         aoeclose,
2576         aoeread,
2577         devbread,
2578         aoewrite,
2579         devbwrite,
2580         aoeremove,
2581         devwstat,
2582         devpower,
2583         devconfig,
2584 };