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