xref: /plan9/sys/src/9/port/devaoe.c (revision f7c114af05ae4d498891de07f5a043835bd540ab)
121abd8f2SDavid du Colombier /*
22f205b96SDavid du Colombier  *	© 2005-2010 coraid
3060a30c6SDavid du Colombier  *	ATA-over-Ethernet (AoE) storage initiator
421abd8f2SDavid du Colombier  */
521abd8f2SDavid du Colombier 
621abd8f2SDavid du Colombier #include "u.h"
721abd8f2SDavid du Colombier #include "../port/lib.h"
821abd8f2SDavid du Colombier #include "mem.h"
921abd8f2SDavid du Colombier #include "dat.h"
1021abd8f2SDavid du Colombier #include "fns.h"
1121abd8f2SDavid du Colombier #include "io.h"
1221abd8f2SDavid du Colombier #include "ureg.h"
1321abd8f2SDavid du Colombier #include "../port/error.h"
1421abd8f2SDavid du Colombier #include "../port/netif.h"
1521abd8f2SDavid du Colombier #include "etherif.h"
1621abd8f2SDavid du Colombier #include "../ip/ip.h"
1721abd8f2SDavid du Colombier #include "../port/aoe.h"
1821abd8f2SDavid du Colombier 
1921abd8f2SDavid du Colombier #pragma	varargck argpos	eventlog	1
2021abd8f2SDavid du Colombier 
2121abd8f2SDavid du Colombier #define dprint(...)	if(debug) eventlog(__VA_ARGS__); else USED(debug);
2221abd8f2SDavid du Colombier #define uprint(...)	snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
2321abd8f2SDavid du Colombier 
2421abd8f2SDavid du Colombier enum {
25c675ee80SDavid du Colombier 	Maxunits	= 0xff,
2641ac1ab6SDavid du Colombier 	Maxframes	= 128,
2721abd8f2SDavid du Colombier 	Ndevlink	= 6,
2821abd8f2SDavid du Colombier 	Nea		= 6,
2921abd8f2SDavid du Colombier 	Nnetlink	= 6,
3021abd8f2SDavid du Colombier };
3121abd8f2SDavid du Colombier 
3221abd8f2SDavid du Colombier #define TYPE(q)		((ulong)(q).path & 0xf)
33c675ee80SDavid du Colombier #define UNIT(q)		(((ulong)(q).path>>4) & 0xff)
34c675ee80SDavid du Colombier #define L(q)		(((ulong)(q).path>>12) & 0xf)
3521abd8f2SDavid du Colombier #define QID(u, t) 	((u)<<4 | (t))
3621abd8f2SDavid du Colombier #define Q3(l, u, t)	((l)<<8 | QID(u, t))
3721abd8f2SDavid du Colombier #define UP(d)		((d)->flag & Dup)
382f205b96SDavid du Colombier /*
392f205b96SDavid du Colombier  * would like this to depend on the chan (srb).
402f205b96SDavid du Colombier  * not possible in the current structure.
412f205b96SDavid du Colombier  */
422f205b96SDavid du Colombier #define Nofail(d, s)	((d)->flag & Dnofail)
4321abd8f2SDavid du Colombier 
44552c49cfSDavid du Colombier #define	MS2TK(t)	((t)/MS2HZ)
4521abd8f2SDavid du Colombier 
4621abd8f2SDavid du Colombier enum {
4721abd8f2SDavid du Colombier 	Qzero,
4821abd8f2SDavid du Colombier 	Qtopdir		= 1,
4921abd8f2SDavid du Colombier 	Qtopbase,
5021abd8f2SDavid du Colombier 	Qtopctl		= Qtopbase,
5121abd8f2SDavid du Colombier 	Qtoplog,
5221abd8f2SDavid du Colombier 	Qtopend,
5321abd8f2SDavid du Colombier 
5421abd8f2SDavid du Colombier 	Qunitdir,
5521abd8f2SDavid du Colombier 	Qunitbase,
5621abd8f2SDavid du Colombier 	Qctl		= Qunitbase,
5721abd8f2SDavid du Colombier 	Qdata,
5821abd8f2SDavid du Colombier 	Qconfig,
5921abd8f2SDavid du Colombier 	Qident,
6021abd8f2SDavid du Colombier 
6121abd8f2SDavid du Colombier 	Qdevlinkdir,
6221abd8f2SDavid du Colombier 	Qdevlinkbase,
6321abd8f2SDavid du Colombier 	Qdevlink	= Qdevlinkbase,
6421abd8f2SDavid du Colombier 	Qdevlinkend,
6521abd8f2SDavid du Colombier 
6621abd8f2SDavid du Colombier 	Qtopfiles	= Qtopend-Qtopbase,
6721abd8f2SDavid du Colombier 	Qdevlinkfiles	= Qdevlinkend-Qdevlinkbase,
6821abd8f2SDavid du Colombier 
6921abd8f2SDavid du Colombier 	Eventlen 	= 256,
702f205b96SDavid du Colombier 	Nevents 	= 64,			/* must be power of 2 */
7121abd8f2SDavid du Colombier 
7221abd8f2SDavid du Colombier 	Fread		= 0,
7321abd8f2SDavid du Colombier 	Fwrite,
7421abd8f2SDavid du Colombier 	Tfree		= -1,
7521abd8f2SDavid du Colombier 	Tmgmt,
7621abd8f2SDavid du Colombier 
77bc8903feSDavid du Colombier 	/*
78bc8903feSDavid du Colombier 	 * round trip bounds, timeouts, in ticks.
79bc8903feSDavid du Colombier 	 * timeouts should be long enough that rebooting
80552c49cfSDavid du Colombier 	 * the coraid (which usually takes under two minutes)
81bc8903feSDavid du Colombier 	 * doesn't trigger a timeout.
82bc8903feSDavid du Colombier 	 */
83552c49cfSDavid du Colombier 	Rtmax		= MS2TK(320),
84552c49cfSDavid du Colombier 	Rtmin		= MS2TK(20),
85552c49cfSDavid du Colombier 	Maxreqticks	= 4*60*HZ,		/* was 45*HZ */
8621abd8f2SDavid du Colombier 
8721abd8f2SDavid du Colombier 	Dbcnt		= 1024,
8821abd8f2SDavid du Colombier 
8921abd8f2SDavid du Colombier 	Crd		= 0x20,
9021abd8f2SDavid du Colombier 	Crdext		= 0x24,
9121abd8f2SDavid du Colombier 	Cwr		= 0x30,
9221abd8f2SDavid du Colombier 	Cwrext		= 0x34,
9321abd8f2SDavid du Colombier 	Cid		= 0xec,
9421abd8f2SDavid du Colombier };
9521abd8f2SDavid du Colombier 
9699c72adcSDavid du Colombier enum {
9799c72adcSDavid du Colombier 	Read,
9899c72adcSDavid du Colombier 	Write,
9999c72adcSDavid du Colombier };
10099c72adcSDavid du Colombier 
10121abd8f2SDavid du Colombier /*
10221abd8f2SDavid du Colombier  * unified set of flags
10321abd8f2SDavid du Colombier  * a Netlink + Aoedev most both be jumbo capable
10421abd8f2SDavid du Colombier  * to send jumbograms to that interface.
10521abd8f2SDavid du Colombier  */
10621abd8f2SDavid du Colombier enum {
10721abd8f2SDavid du Colombier 	/* sync with ahci.h */
10821abd8f2SDavid du Colombier 	Dllba 	= 1<<0,
10921abd8f2SDavid du Colombier 	Dsmart	= 1<<1,
11021abd8f2SDavid du Colombier 	Dpower	= 1<<2,
11121abd8f2SDavid du Colombier 	Dnop	= 1<<3,
11221abd8f2SDavid du Colombier 	Datapi	= 1<<4,
11321abd8f2SDavid du Colombier 	Datapi16= 1<<5,
11421abd8f2SDavid du Colombier 
11521abd8f2SDavid du Colombier 	/* aoe specific */
11621abd8f2SDavid du Colombier 	Dup	= 1<<6,
11721abd8f2SDavid du Colombier 	Djumbo	= 1<<7,
1182f205b96SDavid du Colombier 	Dnofail	= 1<<8,
11921abd8f2SDavid du Colombier };
12021abd8f2SDavid du Colombier 
12121abd8f2SDavid du Colombier static char *flagname[] = {
12221abd8f2SDavid du Colombier 	"llba",
12321abd8f2SDavid du Colombier 	"smart",
12421abd8f2SDavid du Colombier 	"power",
12521abd8f2SDavid du Colombier 	"nop",
12621abd8f2SDavid du Colombier 	"atapi",
12721abd8f2SDavid du Colombier 	"atapi16",
12821abd8f2SDavid du Colombier 
12921abd8f2SDavid du Colombier 	"up",
13021abd8f2SDavid du Colombier 	"jumbo",
1312f205b96SDavid du Colombier 	"nofail",
13221abd8f2SDavid du Colombier };
13321abd8f2SDavid du Colombier 
13421abd8f2SDavid du Colombier typedef struct {
1352f205b96SDavid du Colombier 	ushort	flag;
1362f205b96SDavid du Colombier 	uint	lostjumbo;
13721abd8f2SDavid du Colombier 	int	datamtu;
13821abd8f2SDavid du Colombier 
13921abd8f2SDavid du Colombier 	Chan	*cc;
14021abd8f2SDavid du Colombier 	Chan	*dc;
14121abd8f2SDavid du Colombier 	Chan	*mtu;		/* open early to prevent bind issues. */
14221abd8f2SDavid du Colombier 	char	path[Maxpath];
14321abd8f2SDavid du Colombier 	uchar	ea[Eaddrlen];
14421abd8f2SDavid du Colombier } Netlink;
14521abd8f2SDavid du Colombier 
14621abd8f2SDavid du Colombier typedef struct {
14721abd8f2SDavid du Colombier 	Netlink	*nl;
14821abd8f2SDavid du Colombier 	int	nea;
14921abd8f2SDavid du Colombier 	ulong	eaidx;
15021abd8f2SDavid du Colombier 	uchar	eatab[Nea][Eaddrlen];
15121abd8f2SDavid du Colombier 	ulong	npkt;
15221abd8f2SDavid du Colombier 	ulong	resent;
1532f205b96SDavid du Colombier 	ushort	flag;
15421abd8f2SDavid du Colombier 
15521abd8f2SDavid du Colombier 	ulong	rttavg;
15621abd8f2SDavid du Colombier 	ulong	mintimer;
15721abd8f2SDavid du Colombier } Devlink;
15821abd8f2SDavid du Colombier 
15921abd8f2SDavid du Colombier typedef struct Srb Srb;
16021abd8f2SDavid du Colombier struct Srb {
16121abd8f2SDavid du Colombier 	Rendez;
16221abd8f2SDavid du Colombier 	Srb	*next;
163*f7c114afSDavid du Colombier 	int	shared;	/* Srb shared with kproc (don't free) */
16421abd8f2SDavid du Colombier 	ulong	ticksent;
16521abd8f2SDavid du Colombier 	ulong	len;
16621abd8f2SDavid du Colombier 	vlong	sector;
16721abd8f2SDavid du Colombier 	short	write;
16821abd8f2SDavid du Colombier 	short	nout;
16921abd8f2SDavid du Colombier 	char	*error;
17021abd8f2SDavid du Colombier 	void	*dp;
17121abd8f2SDavid du Colombier 	void	*data;
17221abd8f2SDavid du Colombier };
17321abd8f2SDavid du Colombier 
17421abd8f2SDavid du Colombier typedef struct {
17521abd8f2SDavid du Colombier 	int	tag;
17621abd8f2SDavid du Colombier 	ulong	bcnt;
177c675ee80SDavid du Colombier 	ulong	dlen;
17821abd8f2SDavid du Colombier 	vlong	lba;
17921abd8f2SDavid du Colombier 	ulong	ticksent;
18021abd8f2SDavid du Colombier 	int	nhdr;
18121abd8f2SDavid du Colombier 	uchar	hdr[ETHERMINTU];
18221abd8f2SDavid du Colombier 	void	*dp;
18321abd8f2SDavid du Colombier 	Devlink	*dl;
18421abd8f2SDavid du Colombier 	Netlink	*nl;
18521abd8f2SDavid du Colombier 	int	eaidx;
18621abd8f2SDavid du Colombier 	Srb	*srb;
18721abd8f2SDavid du Colombier } Frame;
18821abd8f2SDavid du Colombier 
18921abd8f2SDavid du Colombier typedef struct Aoedev Aoedev;
19021abd8f2SDavid du Colombier struct Aoedev {
19121abd8f2SDavid du Colombier 	QLock;
19221abd8f2SDavid du Colombier 	Aoedev	*next;
19321abd8f2SDavid du Colombier 
19421abd8f2SDavid du Colombier 	ulong	vers;
19521abd8f2SDavid du Colombier 
19621abd8f2SDavid du Colombier 	int	ndl;
19721abd8f2SDavid du Colombier 	ulong	dlidx;
19821abd8f2SDavid du Colombier 	Devlink	*dl;
19921abd8f2SDavid du Colombier 	Devlink	dltab[Ndevlink];
20021abd8f2SDavid du Colombier 
20121abd8f2SDavid du Colombier 	ushort	fwver;
2022f205b96SDavid du Colombier 	ushort	flag;
20321abd8f2SDavid du Colombier 	int	nopen;
20421abd8f2SDavid du Colombier 	int	major;
20521abd8f2SDavid du Colombier 	int	minor;
20621abd8f2SDavid du Colombier 	int	unit;
20721abd8f2SDavid du Colombier 	int	lasttag;
20821abd8f2SDavid du Colombier 	int	nframes;
20921abd8f2SDavid du Colombier 	Frame	*frames;
21021abd8f2SDavid du Colombier 	vlong	bsize;
21121abd8f2SDavid du Colombier 	vlong	realbsize;
21221abd8f2SDavid du Colombier 
21321abd8f2SDavid du Colombier 	uint	maxbcnt;
21421abd8f2SDavid du Colombier 	ushort	nout;
21521abd8f2SDavid du Colombier 	ushort	maxout;
21621abd8f2SDavid du Colombier 	ulong	lastwadj;
21721abd8f2SDavid du Colombier 	Srb	*head;
21821abd8f2SDavid du Colombier 	Srb	*tail;
21921abd8f2SDavid du Colombier 	Srb	*inprocess;
22021abd8f2SDavid du Colombier 
22121abd8f2SDavid du Colombier 	/* magic numbers 'R' us */
22221abd8f2SDavid du Colombier 	char	serial[20+1];
22321abd8f2SDavid du Colombier 	char	firmware[8+1];
22421abd8f2SDavid du Colombier 	char	model[40+1];
22521abd8f2SDavid du Colombier 	int	nconfig;
22621abd8f2SDavid du Colombier 	uchar	config[1024];
22721abd8f2SDavid du Colombier 	uchar	ident[512];
22821abd8f2SDavid du Colombier };
22921abd8f2SDavid du Colombier 
23021abd8f2SDavid du Colombier #pragma	varargck type	"æ"	Aoedev*
23121abd8f2SDavid du Colombier 
23221abd8f2SDavid du Colombier static struct {
23321abd8f2SDavid du Colombier 	Lock;
23421abd8f2SDavid du Colombier 	QLock;
23521abd8f2SDavid du Colombier 	Rendez;
23621abd8f2SDavid du Colombier 	char	buf[Eventlen*Nevents];
23721abd8f2SDavid du Colombier 	char	*rp;
23821abd8f2SDavid du Colombier 	char	*wp;
23921abd8f2SDavid du Colombier } events;
24021abd8f2SDavid du Colombier 
24121abd8f2SDavid du Colombier static struct {
24221abd8f2SDavid du Colombier 	RWlock;
24321abd8f2SDavid du Colombier 	int	nd;
24421abd8f2SDavid du Colombier 	Aoedev	*d;
24521abd8f2SDavid du Colombier } devs;
24621abd8f2SDavid du Colombier 
24721abd8f2SDavid du Colombier static struct {
24821abd8f2SDavid du Colombier 	Lock;
24921abd8f2SDavid du Colombier 	int	reader[Nnetlink];	/* reader is running. */
25021abd8f2SDavid du Colombier 	Rendez	rendez[Nnetlink];	/* confirm exit. */
25121abd8f2SDavid du Colombier 	Netlink	nl[Nnetlink];
25221abd8f2SDavid du Colombier } netlinks;
25321abd8f2SDavid du Colombier 
25421abd8f2SDavid du Colombier extern Dev 	aoedevtab;
25521abd8f2SDavid du Colombier static Ref 	units;
256c675ee80SDavid du Colombier static Ref	drivevers;
25721abd8f2SDavid du Colombier static int	debug;
25821abd8f2SDavid du Colombier static int	autodiscover	= 1;
25921abd8f2SDavid du Colombier static int	rediscover;
26021abd8f2SDavid du Colombier 
26121abd8f2SDavid du Colombier static Srb*
srballoc(ulong sz)26221abd8f2SDavid du Colombier srballoc(ulong sz)
26321abd8f2SDavid du Colombier {
26421abd8f2SDavid du Colombier 	Srb *srb;
26521abd8f2SDavid du Colombier 
26621abd8f2SDavid du Colombier 	srb = malloc(sizeof *srb+sz);
267aa72973aSDavid du Colombier 	if(srb == nil)
268aa72973aSDavid du Colombier 		error(Enomem);
26921abd8f2SDavid du Colombier 	srb->dp = srb->data = srb+1;
27099c72adcSDavid du Colombier 	srb->ticksent = MACHP(0)->ticks;
271*f7c114afSDavid du Colombier 	srb->shared = 0;
27221abd8f2SDavid du Colombier 	return srb;
27321abd8f2SDavid du Colombier }
27421abd8f2SDavid du Colombier 
27521abd8f2SDavid du Colombier static Srb*
srbkalloc(void * db,ulong)27621abd8f2SDavid du Colombier srbkalloc(void *db, ulong)
27721abd8f2SDavid du Colombier {
27821abd8f2SDavid du Colombier 	Srb *srb;
27921abd8f2SDavid du Colombier 
28021abd8f2SDavid du Colombier 	srb = malloc(sizeof *srb);
281aa72973aSDavid du Colombier 	if(srb == nil)
282aa72973aSDavid du Colombier 		error(Enomem);
28321abd8f2SDavid du Colombier 	srb->dp = srb->data = db;
28499c72adcSDavid du Colombier 	srb->ticksent = MACHP(0)->ticks;
285*f7c114afSDavid du Colombier 	srb->shared = 0;
28621abd8f2SDavid du Colombier 	return srb;
28721abd8f2SDavid du Colombier }
28821abd8f2SDavid du Colombier 
289*f7c114afSDavid du Colombier static void
srbfree(Srb * srb)290*f7c114afSDavid du Colombier srbfree(Srb *srb)
291*f7c114afSDavid du Colombier {
292*f7c114afSDavid du Colombier 	while(srb->shared)
293*f7c114afSDavid du Colombier 		sched();
294*f7c114afSDavid du Colombier 	free(srb);
295*f7c114afSDavid du Colombier }
29621abd8f2SDavid du Colombier 
29721abd8f2SDavid du Colombier static void
srberror(Srb * srb,char * s)29821abd8f2SDavid du Colombier srberror(Srb *srb, char *s)
29921abd8f2SDavid du Colombier {
30021abd8f2SDavid du Colombier 	srb->error = s;
30121abd8f2SDavid du Colombier 	srb->nout--;
30245dfec46SDavid du Colombier 	if(srb->nout == 0)
30321abd8f2SDavid du Colombier 		wakeup(srb);
30421abd8f2SDavid du Colombier }
30521abd8f2SDavid du Colombier 
30621abd8f2SDavid du Colombier static void
frameerror(Aoedev * d,Frame * f,char * s)30721abd8f2SDavid du Colombier frameerror(Aoedev *d, Frame *f, char *s)
30821abd8f2SDavid du Colombier {
30921abd8f2SDavid du Colombier 	Srb *srb;
31021abd8f2SDavid du Colombier 
31121abd8f2SDavid du Colombier 	srb = f->srb;
31221abd8f2SDavid du Colombier 	if(f->tag == Tfree || !srb)
31321abd8f2SDavid du Colombier 		return;
31421abd8f2SDavid du Colombier 	f->srb = nil;
31521abd8f2SDavid du Colombier 	f->tag = Tfree;		/* don't get fooled by way-slow responses */
31621abd8f2SDavid du Colombier 	srberror(srb, s);
31721abd8f2SDavid du Colombier 	d->nout--;
31821abd8f2SDavid du Colombier }
31921abd8f2SDavid du Colombier 
32021abd8f2SDavid du Colombier static char*
unitname(Aoedev * d)32121abd8f2SDavid du Colombier unitname(Aoedev *d)
32221abd8f2SDavid du Colombier {
32321abd8f2SDavid du Colombier 	uprint("%d.%d", d->major, d->minor);
32421abd8f2SDavid du Colombier 	return up->genbuf;
32521abd8f2SDavid du Colombier }
32621abd8f2SDavid du Colombier 
32721abd8f2SDavid du Colombier static int
eventlogready(void *)32821abd8f2SDavid du Colombier eventlogready(void*)
32921abd8f2SDavid du Colombier {
33021abd8f2SDavid du Colombier 	return *events.rp;
33121abd8f2SDavid du Colombier }
33221abd8f2SDavid du Colombier 
33321abd8f2SDavid du Colombier static long
eventlogread(void * a,long n)33421abd8f2SDavid du Colombier eventlogread(void *a, long n)
33521abd8f2SDavid du Colombier {
33621abd8f2SDavid du Colombier 	int len;
33721abd8f2SDavid du Colombier 	char *p, *buf;
33821abd8f2SDavid du Colombier 
33921abd8f2SDavid du Colombier 	buf = smalloc(Eventlen);
34021abd8f2SDavid du Colombier 	qlock(&events);
34121abd8f2SDavid du Colombier 	lock(&events);
34221abd8f2SDavid du Colombier 	p = events.rp;
34321abd8f2SDavid du Colombier 	len = *p;
34421abd8f2SDavid du Colombier 	if(len == 0){
34521abd8f2SDavid du Colombier 		n = 0;
34621abd8f2SDavid du Colombier 		unlock(&events);
34721abd8f2SDavid du Colombier 	} else {
34821abd8f2SDavid du Colombier 		if(n > len)
34921abd8f2SDavid du Colombier 			n = len;
35021abd8f2SDavid du Colombier 		/* can't move directly into pageable space with events lock held */
35121abd8f2SDavid du Colombier 		memmove(buf, p+1, n);
35221abd8f2SDavid du Colombier 		*p = 0;
35321abd8f2SDavid du Colombier 		events.rp = p += Eventlen;
35421abd8f2SDavid du Colombier 		if(p >= events.buf + sizeof events.buf)
35521abd8f2SDavid du Colombier 			events.rp = events.buf;
35621abd8f2SDavid du Colombier 		unlock(&events);
35721abd8f2SDavid du Colombier 
35821abd8f2SDavid du Colombier 		/* the concern here is page faults in memmove below */
35921abd8f2SDavid du Colombier 		if(waserror()){
36021abd8f2SDavid du Colombier 			free(buf);
36121abd8f2SDavid du Colombier 			qunlock(&events);
36221abd8f2SDavid du Colombier 			nexterror();
36321abd8f2SDavid du Colombier 		}
36421abd8f2SDavid du Colombier 		memmove(a, buf, n);
36521abd8f2SDavid du Colombier 		poperror();
36621abd8f2SDavid du Colombier 	}
36721abd8f2SDavid du Colombier 	free(buf);
36821abd8f2SDavid du Colombier 	qunlock(&events);
36921abd8f2SDavid du Colombier 	return n;
37021abd8f2SDavid du Colombier }
37121abd8f2SDavid du Colombier 
37221abd8f2SDavid du Colombier static int
eventlog(char * fmt,...)37321abd8f2SDavid du Colombier eventlog(char *fmt, ...)
37421abd8f2SDavid du Colombier {
37521abd8f2SDavid du Colombier 	int dragrp, n;
37621abd8f2SDavid du Colombier 	char *p;
37721abd8f2SDavid du Colombier 	va_list arg;
37821abd8f2SDavid du Colombier 
37921abd8f2SDavid du Colombier 	lock(&events);
38021abd8f2SDavid du Colombier 	p = events.wp;
38121abd8f2SDavid du Colombier 	dragrp = *p++;
38221abd8f2SDavid du Colombier 	va_start(arg, fmt);
38321abd8f2SDavid du Colombier 	n = vsnprint(p, Eventlen-1, fmt, arg);
38421abd8f2SDavid du Colombier 	*--p = n;
38521abd8f2SDavid du Colombier 	p = events.wp += Eventlen;
38621abd8f2SDavid du Colombier 	if(p >= events.buf + sizeof events.buf)
38721abd8f2SDavid du Colombier 		p = events.wp = events.buf;
38821abd8f2SDavid du Colombier 	if(dragrp)
38921abd8f2SDavid du Colombier 		events.rp = p;
39021abd8f2SDavid du Colombier 	unlock(&events);
39121abd8f2SDavid du Colombier 	wakeup(&events);
39221abd8f2SDavid du Colombier 	return n;
39321abd8f2SDavid du Colombier }
39421abd8f2SDavid du Colombier 
39521abd8f2SDavid du Colombier static int
eventcount(void)39621abd8f2SDavid du Colombier eventcount(void)
39721abd8f2SDavid du Colombier {
39821abd8f2SDavid du Colombier 	int n;
39921abd8f2SDavid du Colombier 
40021abd8f2SDavid du Colombier 	lock(&events);
40121abd8f2SDavid du Colombier 	if(*events.rp == 0)
40221abd8f2SDavid du Colombier 		n = 0;
40321abd8f2SDavid du Colombier 	else
4042f205b96SDavid du Colombier 		n = (events.wp - events.rp) & (Nevents - 1);
40521abd8f2SDavid du Colombier 	unlock(&events);
40621abd8f2SDavid du Colombier 	return n/Eventlen;
40721abd8f2SDavid du Colombier }
40821abd8f2SDavid du Colombier 
40921abd8f2SDavid du Colombier static int
tsince(int tag)41021abd8f2SDavid du Colombier tsince(int tag)
41121abd8f2SDavid du Colombier {
41221abd8f2SDavid du Colombier 	int n;
41321abd8f2SDavid du Colombier 
41499c72adcSDavid du Colombier 	n = MACHP(0)->ticks & 0xffff;
41521abd8f2SDavid du Colombier 	n -= tag & 0xffff;
41621abd8f2SDavid du Colombier 	if(n < 0)
41721abd8f2SDavid du Colombier 		n += 1<<16;
41821abd8f2SDavid du Colombier 	return n;
41921abd8f2SDavid du Colombier }
42021abd8f2SDavid du Colombier 
42121abd8f2SDavid du Colombier static int
newtag(Aoedev * d)42221abd8f2SDavid du Colombier newtag(Aoedev *d)
42321abd8f2SDavid du Colombier {
42421abd8f2SDavid du Colombier 	int t;
42521abd8f2SDavid du Colombier 
42621abd8f2SDavid du Colombier 	do {
42721abd8f2SDavid du Colombier 		t = ++d->lasttag << 16;
42899c72adcSDavid du Colombier 		t |= MACHP(0)->ticks & 0xffff;
42921abd8f2SDavid du Colombier 	} while (t == Tfree || t == Tmgmt);
43021abd8f2SDavid du Colombier 	return t;
43121abd8f2SDavid du Colombier }
43221abd8f2SDavid du Colombier 
43321abd8f2SDavid du Colombier static void
downdev(Aoedev * d,char * err)43421abd8f2SDavid du Colombier downdev(Aoedev *d, char *err)
43521abd8f2SDavid du Colombier {
43621abd8f2SDavid du Colombier 	Frame *f, *e;
43721abd8f2SDavid du Colombier 
43821abd8f2SDavid du Colombier 	d->flag &= ~Dup;
43921abd8f2SDavid du Colombier 	f = d->frames;
44021abd8f2SDavid du Colombier 	e = f + d->nframes;
44121abd8f2SDavid du Colombier 	for(; f < e; f->tag = Tfree, f->srb = nil, f++)
442a587111cSDavid du Colombier 		frameerror(d, f, Eaoedown);
44321abd8f2SDavid du Colombier 	d->inprocess = nil;
44421abd8f2SDavid du Colombier 	eventlog("%æ: removed; %s\n", d, err);
44521abd8f2SDavid du Colombier }
44621abd8f2SDavid du Colombier 
44721abd8f2SDavid du Colombier static Block*
allocfb(Frame * f)448c675ee80SDavid du Colombier allocfb(Frame *f)
44921abd8f2SDavid du Colombier {
45021abd8f2SDavid du Colombier 	int len;
45121abd8f2SDavid du Colombier 	Block *b;
45221abd8f2SDavid du Colombier 
453c675ee80SDavid du Colombier 	len = f->nhdr + f->dlen;
45421abd8f2SDavid du Colombier 	if(len < ETHERMINTU)
45521abd8f2SDavid du Colombier 		len = ETHERMINTU;
45621abd8f2SDavid du Colombier 	b = allocb(len);
45721abd8f2SDavid du Colombier 	memmove(b->wp, f->hdr, f->nhdr);
458c675ee80SDavid du Colombier 	if(f->dlen)
459c675ee80SDavid du Colombier 		memmove(b->wp + f->nhdr, f->dp, f->dlen);
46021abd8f2SDavid du Colombier 	b->wp += len;
46121abd8f2SDavid du Colombier 	return b;
46221abd8f2SDavid du Colombier }
46321abd8f2SDavid du Colombier 
46421abd8f2SDavid du Colombier static void
putlba(Aoeata * a,vlong lba)46521abd8f2SDavid du Colombier putlba(Aoeata *a, vlong lba)
46621abd8f2SDavid du Colombier {
46721abd8f2SDavid du Colombier 	uchar *c;
46821abd8f2SDavid du Colombier 
46921abd8f2SDavid du Colombier 	c = a->lba;
47021abd8f2SDavid du Colombier 	c[0] = lba;
47121abd8f2SDavid du Colombier 	c[1] = lba >> 8;
47221abd8f2SDavid du Colombier 	c[2] = lba >> 16;
47321abd8f2SDavid du Colombier 	c[3] = lba >> 24;
47421abd8f2SDavid du Colombier 	c[4] = lba >> 32;
47521abd8f2SDavid du Colombier 	c[5] = lba >> 40;
47621abd8f2SDavid du Colombier }
47721abd8f2SDavid du Colombier 
47821abd8f2SDavid du Colombier static Devlink*
pickdevlink(Aoedev * d)47921abd8f2SDavid du Colombier pickdevlink(Aoedev *d)
48021abd8f2SDavid du Colombier {
48121abd8f2SDavid du Colombier 	ulong i, n;
48221abd8f2SDavid du Colombier 	Devlink *l;
48321abd8f2SDavid du Colombier 
48421abd8f2SDavid du Colombier 	for(i = 0; i < d->ndl; i++){
48521abd8f2SDavid du Colombier 		n = d->dlidx++ % d->ndl;
48621abd8f2SDavid du Colombier 		l = d->dl + n;
48721abd8f2SDavid du Colombier 		if(l && l->flag & Dup)
48821abd8f2SDavid du Colombier 			return l;
48921abd8f2SDavid du Colombier 	}
49021abd8f2SDavid du Colombier 	return 0;
49121abd8f2SDavid du Colombier }
49221abd8f2SDavid du Colombier 
49321abd8f2SDavid du Colombier static int
pickea(Devlink * l)49421abd8f2SDavid du Colombier pickea(Devlink *l)
49521abd8f2SDavid du Colombier {
496421195e0SDavid du Colombier 	if(l == 0)
497421195e0SDavid du Colombier 		return -1;
49821abd8f2SDavid du Colombier 	if(l->nea == 0)
49921abd8f2SDavid du Colombier 		return -1;
50021abd8f2SDavid du Colombier 	return l->eaidx++ % l->nea;
50121abd8f2SDavid du Colombier }
50221abd8f2SDavid du Colombier 
50321abd8f2SDavid du Colombier static int
hset(Aoedev * d,Frame * f,Aoehdr * h,int cmd)50421abd8f2SDavid du Colombier hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd)
50521abd8f2SDavid du Colombier {
50621abd8f2SDavid du Colombier 	int i;
50721abd8f2SDavid du Colombier 	Devlink *l;
50821abd8f2SDavid du Colombier 
5092f205b96SDavid du Colombier 	if(f->srb && MACHP(0)->ticks - f->srb->ticksent > Maxreqticks){
5102f205b96SDavid du Colombier 		eventlog("%æ: srb timeout\n", d);
5112f205b96SDavid du Colombier 		if(cmd == ACata && f->srb && Nofail(d, s))
5122f205b96SDavid du Colombier 			f->srb->ticksent = MACHP(0)->ticks;
5132f205b96SDavid du Colombier 		else
5142f205b96SDavid du Colombier 			frameerror(d, f, Etimedout);
5152f205b96SDavid du Colombier 		return -1;
5162f205b96SDavid du Colombier 	}
51721abd8f2SDavid du Colombier 	l = pickdevlink(d);
51821abd8f2SDavid du Colombier 	i = pickea(l);
519421195e0SDavid du Colombier 	if(i == -1){
5202f205b96SDavid du Colombier 		if(cmd != ACata || f->srb == nil || !Nofail(d, s))
521421195e0SDavid du Colombier 			downdev(d, "resend fails; no netlink/ea");
52221abd8f2SDavid du Colombier 		return -1;
52321abd8f2SDavid du Colombier 	}
524544cf74fSDavid du Colombier 	memmove(h->dst, l->eatab[i], Eaddrlen);
52521abd8f2SDavid du Colombier 	memmove(h->src, l->nl->ea, sizeof h->src);
52621abd8f2SDavid du Colombier 	hnputs(h->type, Aoetype);
52721abd8f2SDavid du Colombier 	h->verflag = Aoever << 4;
52821abd8f2SDavid du Colombier 	h->error = 0;
52921abd8f2SDavid du Colombier 	hnputs(h->major, d->major);
53021abd8f2SDavid du Colombier 	h->minor = d->minor;
53121abd8f2SDavid du Colombier 	h->cmd = cmd;
53221abd8f2SDavid du Colombier 
53321abd8f2SDavid du Colombier 	hnputl(h->tag, f->tag = newtag(d));
53421abd8f2SDavid du Colombier 	f->dl = l;
53521abd8f2SDavid du Colombier 	f->nl = l->nl;
53621abd8f2SDavid du Colombier 	f->eaidx = i;
53799c72adcSDavid du Colombier 	f->ticksent = MACHP(0)->ticks;
53821abd8f2SDavid du Colombier 
53921abd8f2SDavid du Colombier 	return f->tag;
54021abd8f2SDavid du Colombier }
54121abd8f2SDavid du Colombier 
54221abd8f2SDavid du Colombier static int
resend(Aoedev * d,Frame * f)54321abd8f2SDavid du Colombier resend(Aoedev *d, Frame *f)
54421abd8f2SDavid du Colombier {
545c675ee80SDavid du Colombier 	ulong n;
54621abd8f2SDavid du Colombier 	Aoeata *a;
54721abd8f2SDavid du Colombier 
54821abd8f2SDavid du Colombier 	a = (Aoeata*)f->hdr;
54921abd8f2SDavid du Colombier 	if(hset(d, f, a, a->cmd) == -1)
55021abd8f2SDavid du Colombier 		return -1;
55121abd8f2SDavid du Colombier 	n = f->bcnt;
552c675ee80SDavid du Colombier 	if(n > d->maxbcnt){
55321abd8f2SDavid du Colombier 		n = d->maxbcnt;		/* mtu mismatch (jumbo fail?) */
554c675ee80SDavid du Colombier 		if(f->dlen > n)
555c675ee80SDavid du Colombier 			f->dlen = n;
556c675ee80SDavid du Colombier 	}
55721abd8f2SDavid du Colombier 	a->scnt = n / Aoesectsz;
55821abd8f2SDavid du Colombier 	f->dl->resent++;
55921abd8f2SDavid du Colombier 	f->dl->npkt++;
560421195e0SDavid du Colombier 	if(waserror())
561421195e0SDavid du Colombier 		return -1;
562c675ee80SDavid du Colombier 	devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
563421195e0SDavid du Colombier 	poperror();
56421abd8f2SDavid du Colombier 	return 0;
56521abd8f2SDavid du Colombier }
56621abd8f2SDavid du Colombier 
56721abd8f2SDavid du Colombier static void
discover(int major,int minor)56821abd8f2SDavid du Colombier discover(int major, int minor)
56921abd8f2SDavid du Colombier {
57021abd8f2SDavid du Colombier 	Aoehdr *h;
57121abd8f2SDavid du Colombier 	Block *b;
57221abd8f2SDavid du Colombier 	Netlink *nl, *e;
57321abd8f2SDavid du Colombier 
57421abd8f2SDavid du Colombier 	nl = netlinks.nl;
57521abd8f2SDavid du Colombier 	e = nl + nelem(netlinks.nl);
57621abd8f2SDavid du Colombier 	for(; nl < e; nl++){
57721abd8f2SDavid du Colombier 		if(nl->cc == nil)
57821abd8f2SDavid du Colombier 			continue;
57921abd8f2SDavid du Colombier 		b = allocb(ETHERMINTU);
58021abd8f2SDavid du Colombier 		if(waserror()){
58121abd8f2SDavid du Colombier 			freeb(b);
58221abd8f2SDavid du Colombier 			nexterror();
58321abd8f2SDavid du Colombier 		}
58421abd8f2SDavid du Colombier 		b->wp = b->rp + ETHERMINTU;
58521abd8f2SDavid du Colombier 		memset(b->rp, 0, ETHERMINTU);
58621abd8f2SDavid du Colombier 		h = (Aoehdr*)b->rp;
58721abd8f2SDavid du Colombier 		memset(h->dst, 0xff, sizeof h->dst);
58821abd8f2SDavid du Colombier 		memmove(h->src, nl->ea, sizeof h->src);
58921abd8f2SDavid du Colombier 		hnputs(h->type, Aoetype);
59021abd8f2SDavid du Colombier 		h->verflag = Aoever << 4;
59121abd8f2SDavid du Colombier 		hnputs(h->major, major);
59221abd8f2SDavid du Colombier 		h->minor = minor;
59321abd8f2SDavid du Colombier 		h->cmd = ACconfig;
59421abd8f2SDavid du Colombier 		poperror();
59599c72adcSDavid du Colombier 		/* send b down the queue */
59621abd8f2SDavid du Colombier 		devtab[nl->dc->type]->bwrite(nl->dc, b, 0);
59721abd8f2SDavid du Colombier 	}
59821abd8f2SDavid du Colombier }
59921abd8f2SDavid du Colombier 
60021abd8f2SDavid du Colombier /*
60121abd8f2SDavid du Colombier  * Check all frames on device and resend any frames that have been
60221abd8f2SDavid du Colombier  * outstanding for 200% of the device round trip time average.
60321abd8f2SDavid du Colombier  */
60421abd8f2SDavid du Colombier static void
aoesweepproc(void *)60599c72adcSDavid du Colombier aoesweepproc(void*)
60621abd8f2SDavid du Colombier {
60721abd8f2SDavid du Colombier 	ulong i, tx, timeout, nbc;
60821abd8f2SDavid du Colombier 	vlong starttick;
609bc8903feSDavid du Colombier 	enum { Nms = 100, Nbcms = 30*1000, };		/* magic */
61021abd8f2SDavid du Colombier 	uchar *ea;
61121abd8f2SDavid du Colombier 	Aoeata *a;
61221abd8f2SDavid du Colombier 	Aoedev *d;
61321abd8f2SDavid du Colombier 	Devlink *l;
61421abd8f2SDavid du Colombier 	Frame *f, *e;
61521abd8f2SDavid du Colombier 
61621abd8f2SDavid du Colombier 	nbc = Nbcms/Nms;
61721abd8f2SDavid du Colombier loop:
61821abd8f2SDavid du Colombier 	if(nbc-- == 0){
619c675ee80SDavid du Colombier 		if(rediscover && !waserror()){
62021abd8f2SDavid du Colombier 			discover(0xffff, 0xff);
621c675ee80SDavid du Colombier 			poperror();
622c675ee80SDavid du Colombier 		}
62321abd8f2SDavid du Colombier 		nbc = Nbcms/Nms;
62421abd8f2SDavid du Colombier 	}
62599c72adcSDavid du Colombier 	starttick = MACHP(0)->ticks;
62621abd8f2SDavid du Colombier 	rlock(&devs);
62721abd8f2SDavid du Colombier 	for(d = devs.d; d; d = d->next){
62821abd8f2SDavid du Colombier 		if(!canqlock(d))
62921abd8f2SDavid du Colombier 			continue;
63021abd8f2SDavid du Colombier 		if(!UP(d)){
63121abd8f2SDavid du Colombier 			qunlock(d);
63221abd8f2SDavid du Colombier 			continue;
63321abd8f2SDavid du Colombier 		}
63421abd8f2SDavid du Colombier 		tx = 0;
63521abd8f2SDavid du Colombier 		f = d->frames;
63621abd8f2SDavid du Colombier 		e = f + d->nframes;
63721abd8f2SDavid du Colombier 		for (; f < e; f++){
63821abd8f2SDavid du Colombier 			if(f->tag == Tfree)
63921abd8f2SDavid du Colombier 				continue;
64021abd8f2SDavid du Colombier 			l = f->dl;
64121abd8f2SDavid du Colombier 			timeout = l->rttavg << 1;
64221abd8f2SDavid du Colombier 			i = tsince(f->tag);
64321abd8f2SDavid du Colombier 			if(i < timeout)
64421abd8f2SDavid du Colombier 				continue;
64521abd8f2SDavid du Colombier 			if(d->nout == d->maxout){
64621abd8f2SDavid du Colombier 				if(d->maxout > 1)
64721abd8f2SDavid du Colombier 					d->maxout--;
64899c72adcSDavid du Colombier 				d->lastwadj = MACHP(0)->ticks;
64921abd8f2SDavid du Colombier 			}
65021abd8f2SDavid du Colombier 			a = (Aoeata*)f->hdr;
65121abd8f2SDavid du Colombier 			if(a->scnt > Dbcnt / Aoesectsz &&
65221abd8f2SDavid du Colombier 			   ++f->nl->lostjumbo > (d->nframes << 1)){
6530343ea0dSDavid du Colombier 				ea = f->dl->eatab[f->eaidx];
65421abd8f2SDavid du Colombier 				eventlog("%æ: jumbo failure on %s:%E; lba%lld\n",
65521abd8f2SDavid du Colombier 					d, f->nl->path, ea, f->lba);
65621abd8f2SDavid du Colombier 				d->maxbcnt = Dbcnt;
65721abd8f2SDavid du Colombier 				d->flag &= ~Djumbo;
65821abd8f2SDavid du Colombier 			}
65921abd8f2SDavid du Colombier 			resend(d, f);
66021abd8f2SDavid du Colombier 			if(tx++ == 0){
66121abd8f2SDavid du Colombier 				if((l->rttavg <<= 1) > Rtmax)
66221abd8f2SDavid du Colombier 					l->rttavg = Rtmax;
663552c49cfSDavid du Colombier 				eventlog("%æ: rtt %ldms\n", d, TK2MS(l->rttavg));
66421abd8f2SDavid du Colombier 			}
66521abd8f2SDavid du Colombier 		}
66621abd8f2SDavid du Colombier 		if(d->nout == d->maxout && d->maxout < d->nframes &&
667bc8903feSDavid du Colombier 		   TK2MS(MACHP(0)->ticks - d->lastwadj) > 10*1000){ /* more magic */
66821abd8f2SDavid du Colombier 			d->maxout++;
66999c72adcSDavid du Colombier 			d->lastwadj = MACHP(0)->ticks;
67021abd8f2SDavid du Colombier 		}
67121abd8f2SDavid du Colombier 		qunlock(d);
67221abd8f2SDavid du Colombier 	}
67321abd8f2SDavid du Colombier 	runlock(&devs);
67499c72adcSDavid du Colombier 	i = Nms - TK2MS(MACHP(0)->ticks - starttick);
67521abd8f2SDavid du Colombier 	if(i > 0)
67621abd8f2SDavid du Colombier 		tsleep(&up->sleep, return0, 0, i);
67721abd8f2SDavid du Colombier 	goto loop;
67821abd8f2SDavid du Colombier }
67921abd8f2SDavid du Colombier 
68021abd8f2SDavid du Colombier static int
68121abd8f2SDavid du Colombier fmtæ(Fmt *f)
68221abd8f2SDavid du Colombier {
683b8a11165SDavid du Colombier 	char buf[16];
68421abd8f2SDavid du Colombier 	Aoedev *d;
68521abd8f2SDavid du Colombier 
68621abd8f2SDavid du Colombier 	d = va_arg(f->args, Aoedev*);
68721abd8f2SDavid du Colombier 	snprint(buf, sizeof buf, "aoe%d.%d", d->major, d->minor);
68821abd8f2SDavid du Colombier 	return fmtstrcpy(f, buf);
68921abd8f2SDavid du Colombier }
69021abd8f2SDavid du Colombier 
69121abd8f2SDavid du Colombier static void netbind(char *path);
69221abd8f2SDavid du Colombier 
69321abd8f2SDavid du Colombier static void
aoecfg(void)69421abd8f2SDavid du Colombier aoecfg(void)
69521abd8f2SDavid du Colombier {
69621abd8f2SDavid du Colombier 	int n, i;
69721abd8f2SDavid du Colombier 	char *p, *f[32], buf[24];
69821abd8f2SDavid du Colombier 
69921abd8f2SDavid du Colombier 	if((p = getconf("aoeif")) == nil || (n = tokenize(p, f, nelem(f))) < 1)
70021abd8f2SDavid du Colombier 		return;
70121abd8f2SDavid du Colombier 	/* goo! */
70221abd8f2SDavid du Colombier 	for(i = 0; i < n; i++){
70321abd8f2SDavid du Colombier 		p = f[i];
70421abd8f2SDavid du Colombier 		if(strncmp(p, "ether", 5) == 0)
70521abd8f2SDavid du Colombier 			snprint(buf, sizeof buf, "#l%c/ether%c", p[5], p[5]);
70621abd8f2SDavid du Colombier 		else if(strncmp(p, "#l", 2) == 0)
70721abd8f2SDavid du Colombier 			snprint(buf, sizeof buf, "#l%c/ether%c", p[2], p[2]);
70821abd8f2SDavid du Colombier 		else
70921abd8f2SDavid du Colombier 			continue;
710c675ee80SDavid du Colombier 		if(!waserror()){
71121abd8f2SDavid du Colombier 			netbind(buf);
712c675ee80SDavid du Colombier 			poperror();
713c675ee80SDavid du Colombier 		}
71421abd8f2SDavid du Colombier 	}
71521abd8f2SDavid du Colombier }
71621abd8f2SDavid du Colombier 
71721abd8f2SDavid du Colombier static void
aoeinit(void)71821abd8f2SDavid du Colombier aoeinit(void)
71921abd8f2SDavid du Colombier {
72021abd8f2SDavid du Colombier 	static int init;
72121abd8f2SDavid du Colombier 	static QLock l;
72221abd8f2SDavid du Colombier 
72321abd8f2SDavid du Colombier 	if(!canqlock(&l))
72421abd8f2SDavid du Colombier 		return;
72599c72adcSDavid du Colombier 	if(init == 0){
72621abd8f2SDavid du Colombier 		fmtinstall(L'æ', fmtæ);
72721abd8f2SDavid du Colombier 		events.rp = events.wp = events.buf;
72899c72adcSDavid du Colombier 		kproc("aoesweep", aoesweepproc, nil);
72921abd8f2SDavid du Colombier 		aoecfg();
73099c72adcSDavid du Colombier 		init = 1;
73199c72adcSDavid du Colombier 	}
73221abd8f2SDavid du Colombier 	qunlock(&l);
73321abd8f2SDavid du Colombier }
73421abd8f2SDavid du Colombier 
73521abd8f2SDavid du Colombier static Chan*
aoeattach(char * spec)73621abd8f2SDavid du Colombier aoeattach(char *spec)
73721abd8f2SDavid du Colombier {
73821abd8f2SDavid du Colombier 	Chan *c;
73921abd8f2SDavid du Colombier 
74021abd8f2SDavid du Colombier 	if(*spec)
74121abd8f2SDavid du Colombier 		error(Enonexist);
74221abd8f2SDavid du Colombier 	aoeinit();
74321abd8f2SDavid du Colombier 	c = devattach(L'æ', spec);
74421abd8f2SDavid du Colombier 	mkqid(&c->qid, Qzero, 0, QTDIR);
74521abd8f2SDavid du Colombier 	return c;
74621abd8f2SDavid du Colombier }
74721abd8f2SDavid du Colombier 
74821abd8f2SDavid du Colombier static Aoedev*
unit2dev(ulong unit)74921abd8f2SDavid du Colombier unit2dev(ulong unit)
75021abd8f2SDavid du Colombier {
75121abd8f2SDavid du Colombier 	int i;
75221abd8f2SDavid du Colombier 	Aoedev *d;
75321abd8f2SDavid du Colombier 
75421abd8f2SDavid du Colombier 	rlock(&devs);
75521abd8f2SDavid du Colombier 	i = 0;
75621abd8f2SDavid du Colombier 	for(d = devs.d; d; d = d->next)
75721abd8f2SDavid du Colombier 		if(i++ == unit){
75821abd8f2SDavid du Colombier 			runlock(&devs);
75921abd8f2SDavid du Colombier 			return d;
76021abd8f2SDavid du Colombier 		}
76121abd8f2SDavid du Colombier 	runlock(&devs);
762567483c8SDavid du Colombier 	uprint("unit lookup failure: %lux pc %#p", unit, getcallerpc(&unit));
76321abd8f2SDavid du Colombier 	error(up->genbuf);
76421abd8f2SDavid du Colombier 	return nil;
76521abd8f2SDavid du Colombier }
76621abd8f2SDavid du Colombier 
76721abd8f2SDavid du Colombier static int
unitgen(Chan * c,ulong type,Dir * dp)76821abd8f2SDavid du Colombier unitgen(Chan *c, ulong type, Dir *dp)
76921abd8f2SDavid du Colombier {
77021abd8f2SDavid du Colombier 	int perm, t;
77121abd8f2SDavid du Colombier 	ulong vers;
77221abd8f2SDavid du Colombier 	vlong size;
77321abd8f2SDavid du Colombier 	char *p;
77421abd8f2SDavid du Colombier 	Aoedev *d;
77521abd8f2SDavid du Colombier 	Qid q;
77621abd8f2SDavid du Colombier 
77721abd8f2SDavid du Colombier 	d = unit2dev(UNIT(c->qid));
7780774058cSDavid du Colombier 	perm = 0644;
77921abd8f2SDavid du Colombier 	size = 0;
78021abd8f2SDavid du Colombier 	vers = d->vers;
78121abd8f2SDavid du Colombier 	t = QTFILE;
78221abd8f2SDavid du Colombier 
78321abd8f2SDavid du Colombier 	switch(type){
78421abd8f2SDavid du Colombier 	default:
78521abd8f2SDavid du Colombier 		return -1;
78621abd8f2SDavid du Colombier 	case Qctl:
78721abd8f2SDavid du Colombier 		p = "ctl";
78821abd8f2SDavid du Colombier 		break;
78921abd8f2SDavid du Colombier 	case Qdata:
79021abd8f2SDavid du Colombier 		p = "data";
7910774058cSDavid du Colombier 		perm = 0640;
79221abd8f2SDavid du Colombier 		if(UP(d))
79321abd8f2SDavid du Colombier 			size = d->bsize;
79421abd8f2SDavid du Colombier 		break;
79521abd8f2SDavid du Colombier 	case Qconfig:
79621abd8f2SDavid du Colombier 		p = "config";
79721abd8f2SDavid du Colombier 		if(UP(d))
79821abd8f2SDavid du Colombier 			size = d->nconfig;
79921abd8f2SDavid du Colombier 		break;
80021abd8f2SDavid du Colombier 	case Qident:
80121abd8f2SDavid du Colombier 		p = "ident";
80221abd8f2SDavid du Colombier 		if(UP(d))
80321abd8f2SDavid du Colombier 			size = sizeof d->ident;
80421abd8f2SDavid du Colombier 		break;
80521abd8f2SDavid du Colombier 	case Qdevlinkdir:
80621abd8f2SDavid du Colombier 		p = "devlink";
80721abd8f2SDavid du Colombier 		t = QTDIR;
80821abd8f2SDavid du Colombier 		perm = 0555;
80921abd8f2SDavid du Colombier 		break;
81021abd8f2SDavid du Colombier 	}
81121abd8f2SDavid du Colombier 	mkqid(&q, QID(UNIT(c->qid), type), vers, t);
81221abd8f2SDavid du Colombier 	devdir(c, q, p, size, eve, perm, dp);
81321abd8f2SDavid du Colombier 	return 1;
81421abd8f2SDavid du Colombier }
81521abd8f2SDavid du Colombier 
81621abd8f2SDavid du Colombier static int
topgen(Chan * c,ulong type,Dir * d)81721abd8f2SDavid du Colombier topgen(Chan *c, ulong type, Dir *d)
81821abd8f2SDavid du Colombier {
81921abd8f2SDavid du Colombier 	int perm;
82021abd8f2SDavid du Colombier 	vlong size;
82121abd8f2SDavid du Colombier 	char *p;
82221abd8f2SDavid du Colombier 	Qid q;
82321abd8f2SDavid du Colombier 
82421abd8f2SDavid du Colombier 	perm = 0444;
82521abd8f2SDavid du Colombier 	size = 0;
82621abd8f2SDavid du Colombier 	switch(type){
82721abd8f2SDavid du Colombier 	default:
82821abd8f2SDavid du Colombier 		return -1;
82921abd8f2SDavid du Colombier 	case Qtopctl:
83021abd8f2SDavid du Colombier 		p = "ctl";
83121abd8f2SDavid du Colombier 		perm = 0644;
83221abd8f2SDavid du Colombier 		break;
83321abd8f2SDavid du Colombier 	case Qtoplog:
83421abd8f2SDavid du Colombier 		p = "log";
83521abd8f2SDavid du Colombier 		size = eventcount();
83621abd8f2SDavid du Colombier 		break;
83721abd8f2SDavid du Colombier 	}
83821abd8f2SDavid du Colombier 	mkqid(&q, type, 0, QTFILE);
83921abd8f2SDavid du Colombier 	devdir(c, q, p, size, eve, perm, d);
84021abd8f2SDavid du Colombier 	return 1;
84121abd8f2SDavid du Colombier }
84221abd8f2SDavid du Colombier 
84321abd8f2SDavid du Colombier static int
aoegen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)84421abd8f2SDavid du Colombier aoegen(Chan *c, char *, Dirtab *, int, int s, Dir *dp)
84521abd8f2SDavid du Colombier {
84621abd8f2SDavid du Colombier 	int i;
84721abd8f2SDavid du Colombier 	Aoedev *d;
84821abd8f2SDavid du Colombier 	Qid q;
84921abd8f2SDavid du Colombier 
85021abd8f2SDavid du Colombier 	if(c->qid.path == 0){
85121abd8f2SDavid du Colombier 		switch(s){
85221abd8f2SDavid du Colombier 		case DEVDOTDOT:
85321abd8f2SDavid du Colombier 			q.path = 0;
85421abd8f2SDavid du Colombier 			q.type = QTDIR;
85521abd8f2SDavid du Colombier 			devdir(c, q, "#æ", 0, eve, 0555, dp);
85621abd8f2SDavid du Colombier 			break;
85721abd8f2SDavid du Colombier 		case 0:
85821abd8f2SDavid du Colombier 			q.path = Qtopdir;
85921abd8f2SDavid du Colombier 			q.type = QTDIR;
86021abd8f2SDavid du Colombier 			devdir(c, q, "aoe", 0, eve, 0555, dp);
86121abd8f2SDavid du Colombier 			break;
86221abd8f2SDavid du Colombier 		default:
86321abd8f2SDavid du Colombier 			return -1;
86421abd8f2SDavid du Colombier 		}
86521abd8f2SDavid du Colombier 		return 1;
86621abd8f2SDavid du Colombier 	}
86721abd8f2SDavid du Colombier 
86821abd8f2SDavid du Colombier 	switch(TYPE(c->qid)){
86921abd8f2SDavid du Colombier 	default:
87021abd8f2SDavid du Colombier 		return -1;
87121abd8f2SDavid du Colombier 	case Qtopdir:
87221abd8f2SDavid du Colombier 		if(s == DEVDOTDOT){
87321abd8f2SDavid du Colombier 			mkqid(&q, Qzero, 0, QTDIR);
87421abd8f2SDavid du Colombier 			devdir(c, q, "aoe", 0, eve, 0555, dp);
87521abd8f2SDavid du Colombier 			return 1;
87621abd8f2SDavid du Colombier 		}
87721abd8f2SDavid du Colombier 		if(s < Qtopfiles)
87821abd8f2SDavid du Colombier 			return topgen(c, Qtopbase + s, dp);
87921abd8f2SDavid du Colombier 		s -= Qtopfiles;
88021abd8f2SDavid du Colombier 		if(s >= units.ref)
88121abd8f2SDavid du Colombier 			return -1;
88221abd8f2SDavid du Colombier 		mkqid(&q, QID(s, Qunitdir), 0, QTDIR);
88321abd8f2SDavid du Colombier 		d = unit2dev(s);
8842f205b96SDavid du Colombier 		assert(d != nil);
88521abd8f2SDavid du Colombier 		devdir(c, q, unitname(d), 0, eve, 0555, dp);
88621abd8f2SDavid du Colombier 		return 1;
88721abd8f2SDavid du Colombier 	case Qtopctl:
88821abd8f2SDavid du Colombier 	case Qtoplog:
88921abd8f2SDavid du Colombier 		return topgen(c, TYPE(c->qid), dp);
89021abd8f2SDavid du Colombier 	case Qunitdir:
89121abd8f2SDavid du Colombier 		if(s == DEVDOTDOT){
89221abd8f2SDavid du Colombier 			mkqid(&q, QID(0, Qtopdir), 0, QTDIR);
89321abd8f2SDavid du Colombier 			uprint("%uld", UNIT(c->qid));
89421abd8f2SDavid du Colombier 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
89521abd8f2SDavid du Colombier 			return 1;
89621abd8f2SDavid du Colombier 		}
89721abd8f2SDavid du Colombier 		return unitgen(c, Qunitbase+s, dp);
89821abd8f2SDavid du Colombier 	case Qctl:
89921abd8f2SDavid du Colombier 	case Qdata:
90021abd8f2SDavid du Colombier 	case Qconfig:
9011e18b896SDavid du Colombier 	case Qident:
90221abd8f2SDavid du Colombier 		return unitgen(c, TYPE(c->qid), dp);
90321abd8f2SDavid du Colombier 	case Qdevlinkdir:
90421abd8f2SDavid du Colombier 		i = UNIT(c->qid);
90521abd8f2SDavid du Colombier 		if(s == DEVDOTDOT){
90621abd8f2SDavid du Colombier 			mkqid(&q, QID(i, Qunitdir), 0, QTDIR);
90721abd8f2SDavid du Colombier 			devdir(c, q, "devlink", 0, eve, 0555, dp);
90821abd8f2SDavid du Colombier 			return 1;
90921abd8f2SDavid du Colombier 		}
91021abd8f2SDavid du Colombier 		if(i >= units.ref)
91121abd8f2SDavid du Colombier 			return -1;
91221abd8f2SDavid du Colombier 		d = unit2dev(i);
91321abd8f2SDavid du Colombier 		if(s >= d->ndl)
91421abd8f2SDavid du Colombier 			return -1;
91521abd8f2SDavid du Colombier 		uprint("%d", s);
91621abd8f2SDavid du Colombier 		mkqid(&q, Q3(s, i, Qdevlink), 0, QTFILE);
91721abd8f2SDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
91821abd8f2SDavid du Colombier 		return 1;
91921abd8f2SDavid du Colombier 	case Qdevlink:
92021abd8f2SDavid du Colombier 		uprint("%d", s);
92121abd8f2SDavid du Colombier 		mkqid(&q, Q3(s, UNIT(c->qid), Qdevlink), 0, QTFILE);
92221abd8f2SDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
92321abd8f2SDavid du Colombier 		return 1;
92421abd8f2SDavid du Colombier 	}
92521abd8f2SDavid du Colombier }
92621abd8f2SDavid du Colombier 
92721abd8f2SDavid du Colombier static Walkqid*
aoewalk(Chan * c,Chan * nc,char ** name,int nname)92821abd8f2SDavid du Colombier aoewalk(Chan *c, Chan *nc, char **name, int nname)
92921abd8f2SDavid du Colombier {
93021abd8f2SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, aoegen);
93121abd8f2SDavid du Colombier }
93221abd8f2SDavid du Colombier 
93321abd8f2SDavid du Colombier static int
aoestat(Chan * c,uchar * db,int n)93421abd8f2SDavid du Colombier aoestat(Chan *c, uchar *db, int n)
93521abd8f2SDavid du Colombier {
93621abd8f2SDavid du Colombier 	return devstat(c, db, n, nil, 0, aoegen);
93721abd8f2SDavid du Colombier }
93821abd8f2SDavid du Colombier 
93921abd8f2SDavid du Colombier static Chan*
aoeopen(Chan * c,int omode)94021abd8f2SDavid du Colombier aoeopen(Chan *c, int omode)
94121abd8f2SDavid du Colombier {
94221abd8f2SDavid du Colombier 	Aoedev *d;
94321abd8f2SDavid du Colombier 
94421abd8f2SDavid du Colombier 	if(TYPE(c->qid) != Qdata)
94521abd8f2SDavid du Colombier 		return devopen(c, omode, 0, 0, aoegen);
94621abd8f2SDavid du Colombier 
94721abd8f2SDavid du Colombier 	d = unit2dev(UNIT(c->qid));
94821abd8f2SDavid du Colombier 	qlock(d);
94921abd8f2SDavid du Colombier 	if(waserror()){
95021abd8f2SDavid du Colombier 		qunlock(d);
95121abd8f2SDavid du Colombier 		nexterror();
95221abd8f2SDavid du Colombier 	}
95321abd8f2SDavid du Colombier 	if(!UP(d))
954a587111cSDavid du Colombier 		error(Eaoedown);
95521abd8f2SDavid du Colombier 	c = devopen(c, omode, 0, 0, aoegen);
95621abd8f2SDavid du Colombier 	d->nopen++;
95721abd8f2SDavid du Colombier 	poperror();
95821abd8f2SDavid du Colombier 	qunlock(d);
95921abd8f2SDavid du Colombier 	return c;
96021abd8f2SDavid du Colombier }
96121abd8f2SDavid du Colombier 
96221abd8f2SDavid du Colombier static void
aoeclose(Chan * c)96321abd8f2SDavid du Colombier aoeclose(Chan *c)
96421abd8f2SDavid du Colombier {
96521abd8f2SDavid du Colombier 	Aoedev *d;
96621abd8f2SDavid du Colombier 
96721abd8f2SDavid du Colombier 	if(TYPE(c->qid) != Qdata || (c->flag&COPEN) == 0)
96821abd8f2SDavid du Colombier 		return;
96921abd8f2SDavid du Colombier 
97021abd8f2SDavid du Colombier 	d = unit2dev(UNIT(c->qid));
97121abd8f2SDavid du Colombier 	qlock(d);
97221abd8f2SDavid du Colombier 	if(--d->nopen == 0 && !waserror()){
97321abd8f2SDavid du Colombier 		discover(d->major, d->minor);
97421abd8f2SDavid du Colombier 		poperror();
97521abd8f2SDavid du Colombier 	}
97621abd8f2SDavid du Colombier 	qunlock(d);
97721abd8f2SDavid du Colombier }
97821abd8f2SDavid du Colombier 
97921abd8f2SDavid du Colombier static void
atarw(Aoedev * d,Frame * f)98021abd8f2SDavid du Colombier atarw(Aoedev *d, Frame *f)
98121abd8f2SDavid du Colombier {
98221abd8f2SDavid du Colombier 	ulong bcnt;
98321abd8f2SDavid du Colombier 	char extbit, writebit;
98421abd8f2SDavid du Colombier 	Aoeata *ah;
98521abd8f2SDavid du Colombier 	Srb *srb;
98621abd8f2SDavid du Colombier 
98721abd8f2SDavid du Colombier 	extbit = 0x4;
98821abd8f2SDavid du Colombier 	writebit = 0x10;
98921abd8f2SDavid du Colombier 
99021abd8f2SDavid du Colombier 	srb = d->inprocess;
99121abd8f2SDavid du Colombier 	bcnt = d->maxbcnt;
99221abd8f2SDavid du Colombier 	if(bcnt > srb->len)
99321abd8f2SDavid du Colombier 		bcnt = srb->len;
99441ac1ab6SDavid du Colombier 	f->nhdr = AOEATASZ;
99521abd8f2SDavid du Colombier 	memset(f->hdr, 0, f->nhdr);
99621abd8f2SDavid du Colombier 	ah = (Aoeata*)f->hdr;
9972f205b96SDavid du Colombier 	if(hset(d, f, ah, ACata) == -1) {
9982f205b96SDavid du Colombier 		d->inprocess = nil;
99921abd8f2SDavid du Colombier 		return;
10002f205b96SDavid du Colombier 	}
100121abd8f2SDavid du Colombier 	f->dp = srb->dp;
100221abd8f2SDavid du Colombier 	f->bcnt = bcnt;
100321abd8f2SDavid du Colombier 	f->lba = srb->sector;
100421abd8f2SDavid du Colombier 	f->srb = srb;
100521abd8f2SDavid du Colombier 
100621abd8f2SDavid du Colombier 	ah->scnt = bcnt / Aoesectsz;
100721abd8f2SDavid du Colombier 	putlba(ah, f->lba);
100821abd8f2SDavid du Colombier 	if(d->flag & Dllba)
100921abd8f2SDavid du Colombier 		ah->aflag |= AAFext;
101021abd8f2SDavid du Colombier 	else {
101121abd8f2SDavid du Colombier 		extbit = 0;
101221abd8f2SDavid du Colombier 		ah->lba[3] &= 0x0f;
101321abd8f2SDavid du Colombier 		ah->lba[3] |= 0xe0;	/* LBA bit+obsolete 0xa0 */
101421abd8f2SDavid du Colombier 	}
101521abd8f2SDavid du Colombier 	if(srb->write){
101621abd8f2SDavid du Colombier 		ah->aflag |= AAFwrite;
1017c675ee80SDavid du Colombier 		f->dlen = bcnt;
101821abd8f2SDavid du Colombier 	}else{
101921abd8f2SDavid du Colombier 		writebit = 0;
1020c675ee80SDavid du Colombier 		f->dlen = 0;
102121abd8f2SDavid du Colombier 	}
102221abd8f2SDavid du Colombier 	ah->cmdstat = 0x20 | writebit | extbit;
102321abd8f2SDavid du Colombier 
102421abd8f2SDavid du Colombier 	/* mark tracking fields and load out */
102521abd8f2SDavid du Colombier 	srb->nout++;
102621abd8f2SDavid du Colombier 	srb->dp = (uchar*)srb->dp + bcnt;
102721abd8f2SDavid du Colombier 	srb->len -= bcnt;
102821abd8f2SDavid du Colombier 	srb->sector += bcnt / Aoesectsz;
102921abd8f2SDavid du Colombier 	if(srb->len == 0)
103021abd8f2SDavid du Colombier 		d->inprocess = nil;
103121abd8f2SDavid du Colombier 	d->nout++;
103221abd8f2SDavid du Colombier 	f->dl->npkt++;
103321abd8f2SDavid du Colombier 	if(waserror()){
103421abd8f2SDavid du Colombier 		f->tag = Tfree;
103521abd8f2SDavid du Colombier 		d->inprocess = nil;
103621abd8f2SDavid du Colombier 		nexterror();
103721abd8f2SDavid du Colombier 	}
1038c675ee80SDavid du Colombier 	devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
103921abd8f2SDavid du Colombier 	poperror();
104021abd8f2SDavid du Colombier }
104121abd8f2SDavid du Colombier 
104221abd8f2SDavid du Colombier static char*
aoeerror(Aoehdr * h)104321abd8f2SDavid du Colombier aoeerror(Aoehdr *h)
104421abd8f2SDavid du Colombier {
104521abd8f2SDavid du Colombier 	int n;
104621abd8f2SDavid du Colombier 	static char *errs[] = {
104721abd8f2SDavid du Colombier 		"aoe protocol error: unknown",
104821abd8f2SDavid du Colombier 		"aoe protocol error: bad command code",
104921abd8f2SDavid du Colombier 		"aoe protocol error: bad argument param",
105021abd8f2SDavid du Colombier 		"aoe protocol error: device unavailable",
105121abd8f2SDavid du Colombier 		"aoe protocol error: config string present",
105221abd8f2SDavid du Colombier 		"aoe protocol error: unsupported version",
10532f205b96SDavid du Colombier 		"aoe protocol error: target is reserved",
105421abd8f2SDavid du Colombier 	};
105521abd8f2SDavid du Colombier 
105621abd8f2SDavid du Colombier 	if((h->verflag & AFerr) == 0)
105721abd8f2SDavid du Colombier 		return 0;
105821abd8f2SDavid du Colombier 	n = h->error;
105921abd8f2SDavid du Colombier 	if(n > nelem(errs))
106021abd8f2SDavid du Colombier 		n = 0;
106121abd8f2SDavid du Colombier 	return errs[n];
106221abd8f2SDavid du Colombier }
106321abd8f2SDavid du Colombier 
106421abd8f2SDavid du Colombier static void
rtupdate(Devlink * l,int rtt)106521abd8f2SDavid du Colombier rtupdate(Devlink *l, int rtt)
106621abd8f2SDavid du Colombier {
106721abd8f2SDavid du Colombier 	int n;
106821abd8f2SDavid du Colombier 
106921abd8f2SDavid du Colombier 	n = rtt;
107021abd8f2SDavid du Colombier 	if(rtt < 0){
107121abd8f2SDavid du Colombier 		n = -rtt;
107221abd8f2SDavid du Colombier 		if(n < Rtmin)
107321abd8f2SDavid du Colombier 			n = Rtmin;
107421abd8f2SDavid du Colombier 		else if(n > Rtmax)
107521abd8f2SDavid du Colombier 			n = Rtmax;
107621abd8f2SDavid du Colombier 		l->mintimer += (n - l->mintimer) >> 1;
107721abd8f2SDavid du Colombier 	} else if(n < l->mintimer)
107821abd8f2SDavid du Colombier 		n = l->mintimer;
107921abd8f2SDavid du Colombier 	else if(n > Rtmax)
108021abd8f2SDavid du Colombier 		n = Rtmax;
108121abd8f2SDavid du Colombier 
108221abd8f2SDavid du Colombier 	/* g == .25; cf. Congestion Avoidance and Control, Jacobson&Karels; 1988 */
108321abd8f2SDavid du Colombier 	n -= l->rttavg;
108421abd8f2SDavid du Colombier 	l->rttavg += n >> 2;
108521abd8f2SDavid du Colombier }
108621abd8f2SDavid du Colombier 
108721abd8f2SDavid du Colombier static int
srbready(void * v)108821abd8f2SDavid du Colombier srbready(void *v)
108921abd8f2SDavid du Colombier {
109021abd8f2SDavid du Colombier 	Srb *s;
109121abd8f2SDavid du Colombier 
109221abd8f2SDavid du Colombier 	s = v;
1093*f7c114afSDavid du Colombier 	return s->error || (s->nout == 0 && s->len == 0);
109421abd8f2SDavid du Colombier }
109521abd8f2SDavid du Colombier 
109621abd8f2SDavid du Colombier static Frame*
getframe(Aoedev * d,int tag)109721abd8f2SDavid du Colombier getframe(Aoedev *d, int tag)
109821abd8f2SDavid du Colombier {
109921abd8f2SDavid du Colombier 	Frame *f, *e;
110021abd8f2SDavid du Colombier 
110121abd8f2SDavid du Colombier 	f = d->frames;
110221abd8f2SDavid du Colombier 	e = f + d->nframes;
110321abd8f2SDavid du Colombier 	for(; f < e; f++)
110421abd8f2SDavid du Colombier 		if(f->tag == tag)
110521abd8f2SDavid du Colombier 			return f;
110621abd8f2SDavid du Colombier 	return nil;
110721abd8f2SDavid du Colombier }
110821abd8f2SDavid du Colombier 
110921abd8f2SDavid du Colombier static Frame*
freeframe(Aoedev * d)111021abd8f2SDavid du Colombier freeframe(Aoedev *d)
111121abd8f2SDavid du Colombier {
111221abd8f2SDavid du Colombier 	if(d->nout < d->maxout)
111321abd8f2SDavid du Colombier 		return getframe(d, Tfree);
111421abd8f2SDavid du Colombier 	return nil;
111521abd8f2SDavid du Colombier }
111621abd8f2SDavid du Colombier 
111721abd8f2SDavid du Colombier static void
work(Aoedev * d)111821abd8f2SDavid du Colombier work(Aoedev *d)
111921abd8f2SDavid du Colombier {
112021abd8f2SDavid du Colombier 	Frame *f;
112121abd8f2SDavid du Colombier 
112221abd8f2SDavid du Colombier 	while ((f = freeframe(d)) != nil) {
112321abd8f2SDavid du Colombier 		if(d->inprocess == nil){
112421abd8f2SDavid du Colombier 			if(d->head == nil)
112521abd8f2SDavid du Colombier 				return;
112621abd8f2SDavid du Colombier 			d->inprocess = d->head;
112721abd8f2SDavid du Colombier 			d->head = d->head->next;
112821abd8f2SDavid du Colombier 			if(d->head == nil)
112921abd8f2SDavid du Colombier 				d->tail = nil;
113021abd8f2SDavid du Colombier 		}
113121abd8f2SDavid du Colombier 		atarw(d, f);
113221abd8f2SDavid du Colombier 	}
113321abd8f2SDavid du Colombier }
113421abd8f2SDavid du Colombier 
113521abd8f2SDavid du Colombier static void
strategy(Aoedev * d,Srb * srb)113621abd8f2SDavid du Colombier strategy(Aoedev *d, Srb *srb)
113721abd8f2SDavid du Colombier {
113821abd8f2SDavid du Colombier 	qlock(d);
113921abd8f2SDavid du Colombier 	if(waserror()){
114021abd8f2SDavid du Colombier 		qunlock(d);
114121abd8f2SDavid du Colombier 		nexterror();
114221abd8f2SDavid du Colombier 	}
114321abd8f2SDavid du Colombier 	srb->next = nil;
114421abd8f2SDavid du Colombier 	if(d->tail)
114521abd8f2SDavid du Colombier 		d->tail->next = srb;
114621abd8f2SDavid du Colombier 	d->tail = srb;
114721abd8f2SDavid du Colombier 	if(d->head == nil)
114821abd8f2SDavid du Colombier 		d->head = srb;
1149*f7c114afSDavid du Colombier 	srb->shared = 1;
115021abd8f2SDavid du Colombier 	work(d);
115121abd8f2SDavid du Colombier 	poperror();
115221abd8f2SDavid du Colombier 	qunlock(d);
115321abd8f2SDavid du Colombier 
115421abd8f2SDavid du Colombier 	while(waserror())
115521abd8f2SDavid du Colombier 		;
115621abd8f2SDavid du Colombier 	sleep(srb, srbready, srb);
115721abd8f2SDavid du Colombier 	poperror();
115821abd8f2SDavid du Colombier }
115921abd8f2SDavid du Colombier 
116021abd8f2SDavid du Colombier #define iskaddr(a)	((uintptr)(a) > KZERO)
116121abd8f2SDavid du Colombier 
116221abd8f2SDavid du Colombier static long
rw(Aoedev * d,int write,uchar * db,long len,uvlong off)116321abd8f2SDavid du Colombier rw(Aoedev *d, int write, uchar *db, long len, uvlong off)
116421abd8f2SDavid du Colombier {
116521abd8f2SDavid du Colombier 	long n, nlen, copy;
11662f205b96SDavid du Colombier 	enum { Srbsz = 1<<19, };	/* magic allocation */
116721abd8f2SDavid du Colombier 	Srb *srb;
116821abd8f2SDavid du Colombier 
116921abd8f2SDavid du Colombier 	if((off|len) & (Aoesectsz-1))
117021abd8f2SDavid du Colombier 		error("offset and length must be sector multiple.\n");
11710774058cSDavid du Colombier 	if(off > d->bsize || len == 0)
117221abd8f2SDavid du Colombier 		return 0;
117321abd8f2SDavid du Colombier 	if(off + len > d->bsize)
117421abd8f2SDavid du Colombier 		len = d->bsize - off;
117521abd8f2SDavid du Colombier 	copy = 0;
117621abd8f2SDavid du Colombier 	if(iskaddr(db)){
117721abd8f2SDavid du Colombier 		srb = srbkalloc(db, len);
117821abd8f2SDavid du Colombier 		copy = 1;
117921abd8f2SDavid du Colombier 	}else
118021abd8f2SDavid du Colombier 		srb = srballoc(Srbsz <= len? Srbsz: len);
118121abd8f2SDavid du Colombier 	if(waserror()){
118221abd8f2SDavid du Colombier 		srbfree(srb);
118321abd8f2SDavid du Colombier 		nexterror();
118421abd8f2SDavid du Colombier 	}
118521abd8f2SDavid du Colombier 	nlen = len;
118621abd8f2SDavid du Colombier 	srb->write = write;
118799c72adcSDavid du Colombier 	do {
118821abd8f2SDavid du Colombier 		if(!UP(d))
118921abd8f2SDavid du Colombier 			error(Eio);
119021abd8f2SDavid du Colombier 		srb->sector = off / Aoesectsz;
119121abd8f2SDavid du Colombier 		srb->dp = srb->data;
119221abd8f2SDavid du Colombier 		n = nlen;
119321abd8f2SDavid du Colombier 		if(n > Srbsz)
119421abd8f2SDavid du Colombier 			n = Srbsz;
119521abd8f2SDavid du Colombier 		srb->len = n;
119699c72adcSDavid du Colombier 		if(write && !copy)
119721abd8f2SDavid du Colombier 			memmove(srb->data, db, n);
119821abd8f2SDavid du Colombier 		strategy(d, srb);
119921abd8f2SDavid du Colombier 		if(srb->error)
120021abd8f2SDavid du Colombier 			error(srb->error);
120199c72adcSDavid du Colombier 		if(!write && !copy)
120221abd8f2SDavid du Colombier 			memmove(db, srb->data, n);
120321abd8f2SDavid du Colombier 		nlen -= n;
120421abd8f2SDavid du Colombier 		db += n;
120521abd8f2SDavid du Colombier 		off += n;
120699c72adcSDavid du Colombier 	} while (nlen > 0);
120721abd8f2SDavid du Colombier 	poperror();
120821abd8f2SDavid du Colombier 	srbfree(srb);
120921abd8f2SDavid du Colombier 	return len;
121021abd8f2SDavid du Colombier }
121121abd8f2SDavid du Colombier 
121221abd8f2SDavid du Colombier static long
readmem(ulong off,void * dst,long n,void * src,long size)121321abd8f2SDavid du Colombier readmem(ulong off, void *dst, long n, void *src, long size)
121421abd8f2SDavid du Colombier {
121521abd8f2SDavid du Colombier 	if(off >= size)
121621abd8f2SDavid du Colombier 		return 0;
121721abd8f2SDavid du Colombier 	if(off + n > size)
121821abd8f2SDavid du Colombier 		n = size - off;
121921abd8f2SDavid du Colombier 	memmove(dst, (uchar*)src + off, n);
122021abd8f2SDavid du Colombier 	return n;
122121abd8f2SDavid du Colombier }
122221abd8f2SDavid du Colombier 
122321abd8f2SDavid du Colombier static char *
pflag(char * s,char * e,uchar f)122421abd8f2SDavid du Colombier pflag(char *s, char *e, uchar f)
122521abd8f2SDavid du Colombier {
1226060a30c6SDavid du Colombier 	uchar i;
122721abd8f2SDavid du Colombier 
1228060a30c6SDavid du Colombier 	for(i = 0; i < 8; i++)
1229060a30c6SDavid du Colombier 		if(f & (1 << i))
123021abd8f2SDavid du Colombier 			s = seprint(s, e, "%s ", flagname[i]? flagname[i]: "oops");
123121abd8f2SDavid du Colombier 	return seprint(s, e, "\n");
123221abd8f2SDavid du Colombier }
123321abd8f2SDavid du Colombier 
123421abd8f2SDavid du Colombier static int
pstat(Aoedev * d,char * db,int len,int off)123521abd8f2SDavid du Colombier pstat(Aoedev *d, char *db, int len, int off)
123621abd8f2SDavid du Colombier {
123721abd8f2SDavid du Colombier 	int i;
123821abd8f2SDavid du Colombier 	char *state, *s, *p, *e;
123921abd8f2SDavid du Colombier 
1240c9b6d007SDavid du Colombier 	s = p = malloc(READSTR);
1241aa72973aSDavid du Colombier 	if(s == nil)
1242aa72973aSDavid du Colombier 		error(Enomem);
1243c9b6d007SDavid du Colombier 	e = p + READSTR;
124421abd8f2SDavid du Colombier 
124521abd8f2SDavid du Colombier 	state = "down";
124621abd8f2SDavid du Colombier 	if(UP(d))
124721abd8f2SDavid du Colombier 		state = "up";
124821abd8f2SDavid du Colombier 
124921abd8f2SDavid du Colombier 	p = seprint(p, e,
125021abd8f2SDavid du Colombier 		"state: %s\n"	"nopen: %d\n"	"nout: %d\n"
125121abd8f2SDavid du Colombier 		"nmaxout: %d\n"	"nframes: %d\n"	"maxbcnt: %d\n"
12520774058cSDavid du Colombier 		"fw: %.4ux\n"
125321abd8f2SDavid du Colombier 		"model: %s\n"	"serial: %s\n"	"firmware: %s\n",
125421abd8f2SDavid du Colombier 		state,		d->nopen,	d->nout,
125521abd8f2SDavid du Colombier 		d->maxout, 	d->nframes,	d->maxbcnt,
125621abd8f2SDavid du Colombier 		d->fwver,
125721abd8f2SDavid du Colombier 		d->model, 	d->serial, 	d->firmware);
125821abd8f2SDavid du Colombier 	p = seprint(p, e, "flag: ");
125921abd8f2SDavid du Colombier 	p = pflag(p, e, d->flag);
126021abd8f2SDavid du Colombier 
126121abd8f2SDavid du Colombier 	if(p - s < len)
126221abd8f2SDavid du Colombier 		len = p - s;
126321abd8f2SDavid du Colombier 	i = readstr(off, db, len, s);
126421abd8f2SDavid du Colombier 	free(s);
126521abd8f2SDavid du Colombier 	return i;
126621abd8f2SDavid du Colombier }
126721abd8f2SDavid du Colombier 
126821abd8f2SDavid du Colombier static long
unitread(Chan * c,void * db,long len,vlong off)126921abd8f2SDavid du Colombier unitread(Chan *c, void *db, long len, vlong off)
127021abd8f2SDavid du Colombier {
127121abd8f2SDavid du Colombier 	Aoedev *d;
127221abd8f2SDavid du Colombier 
127321abd8f2SDavid du Colombier 	d = unit2dev(UNIT(c->qid));
127421abd8f2SDavid du Colombier 	if(d->vers != c->qid.vers)
127521abd8f2SDavid du Colombier 		error(Echange);
127621abd8f2SDavid du Colombier 	switch(TYPE(c->qid)){
127721abd8f2SDavid du Colombier 	default:
127821abd8f2SDavid du Colombier 		error(Ebadarg);
127921abd8f2SDavid du Colombier 	case Qctl:
128021abd8f2SDavid du Colombier 		return pstat(d, db, len, off);
128121abd8f2SDavid du Colombier 	case Qdata:
128299c72adcSDavid du Colombier 		return rw(d, Read, db, len, off);
128321abd8f2SDavid du Colombier 	case Qconfig:
128421abd8f2SDavid du Colombier 		if (!UP(d))
1285a587111cSDavid du Colombier 			error(Eaoedown);
128621abd8f2SDavid du Colombier 		return readmem(off, db, len, d->config, d->nconfig);
128721abd8f2SDavid du Colombier 	case Qident:
128821abd8f2SDavid du Colombier 		if (!UP(d))
1289a587111cSDavid du Colombier 			error(Eaoedown);
129021abd8f2SDavid du Colombier 		return readmem(off, db, len, d->ident, sizeof d->ident);
129121abd8f2SDavid du Colombier 	}
129221abd8f2SDavid du Colombier }
129321abd8f2SDavid du Colombier 
129421abd8f2SDavid du Colombier static int
devlinkread(Chan * c,void * db,int len,int off)129521abd8f2SDavid du Colombier devlinkread(Chan *c, void *db, int len, int off)
129621abd8f2SDavid du Colombier {
129721abd8f2SDavid du Colombier 	int i;
129821abd8f2SDavid du Colombier 	char *s, *p, *e;
129921abd8f2SDavid du Colombier 	Aoedev *d;
130021abd8f2SDavid du Colombier 	Devlink *l;
130121abd8f2SDavid du Colombier 
130221abd8f2SDavid du Colombier 	d = unit2dev(UNIT(c->qid));
130321abd8f2SDavid du Colombier 	i = L(c->qid);
130421abd8f2SDavid du Colombier 	if(i >= d->ndl)
130521abd8f2SDavid du Colombier 		return 0;
130621abd8f2SDavid du Colombier 	l = d->dl + i;
130721abd8f2SDavid du Colombier 
1308c9b6d007SDavid du Colombier 	s = p = malloc(READSTR);
1309aa72973aSDavid du Colombier 	if(s == nil)
1310aa72973aSDavid du Colombier 		error(Enomem);
1311c9b6d007SDavid du Colombier 	e = s + READSTR;
131221abd8f2SDavid du Colombier 
131321abd8f2SDavid du Colombier 	p = seprint(p, e, "addr: ");
131421abd8f2SDavid du Colombier 	for(i = 0; i < l->nea; i++)
131521abd8f2SDavid du Colombier 		p = seprint(p, e, "%E ", l->eatab[i]);
131621abd8f2SDavid du Colombier 	p = seprint(p, e, "\n");
131721abd8f2SDavid du Colombier 	p = seprint(p, e, "npkt: %uld\n", l->npkt);
131821abd8f2SDavid du Colombier 	p = seprint(p, e, "resent: %uld\n", l->resent);
131921abd8f2SDavid du Colombier 	p = seprint(p, e, "flag: "); p = pflag(p, e, l->flag);
1320552c49cfSDavid du Colombier 	p = seprint(p, e, "rttavg: %uld\n", TK2MS(l->rttavg));
1321552c49cfSDavid du Colombier 	p = seprint(p, e, "mintimer: %uld\n", TK2MS(l->mintimer));
132221abd8f2SDavid du Colombier 
132321abd8f2SDavid du Colombier 	p = seprint(p, e, "nl path: %s\n", l->nl->path);
132421abd8f2SDavid du Colombier 	p = seprint(p, e, "nl ea: %E\n", l->nl->ea);
132521abd8f2SDavid du Colombier 	p = seprint(p, e, "nl flag: "); p = pflag(p, e, l->flag);
132621abd8f2SDavid du Colombier 	p = seprint(p, e, "nl lostjumbo: %d\n", l->nl->lostjumbo);
132721abd8f2SDavid du Colombier 	p = seprint(p, e, "nl datamtu: %d\n", l->nl->datamtu);
132821abd8f2SDavid du Colombier 
132921abd8f2SDavid du Colombier 	if(p - s < len)
133021abd8f2SDavid du Colombier 		len = p - s;
133121abd8f2SDavid du Colombier 	i = readstr(off, db, len, s);
133221abd8f2SDavid du Colombier 	free(s);
133321abd8f2SDavid du Colombier 	return i;
133421abd8f2SDavid du Colombier }
133521abd8f2SDavid du Colombier 
133621abd8f2SDavid du Colombier static long
topctlread(Chan *,void * db,int len,int off)133721abd8f2SDavid du Colombier topctlread(Chan *, void *db, int len, int off)
133821abd8f2SDavid du Colombier {
133921abd8f2SDavid du Colombier 	int i;
134021abd8f2SDavid du Colombier 	char *s, *p, *e;
134121abd8f2SDavid du Colombier 	Netlink *n;
134221abd8f2SDavid du Colombier 
1343c9b6d007SDavid du Colombier 	s = p = malloc(READSTR);
1344aa72973aSDavid du Colombier 	if(s == nil)
1345aa72973aSDavid du Colombier 		error(Enomem);
1346c9b6d007SDavid du Colombier 	e = s + READSTR;
134721abd8f2SDavid du Colombier 
134821abd8f2SDavid du Colombier 	p = seprint(p, e, "debug: %d\n", debug);
134921abd8f2SDavid du Colombier 	p = seprint(p, e, "autodiscover: %d\n", autodiscover);
135021abd8f2SDavid du Colombier 	p = seprint(p, e, "rediscover: %d\n", rediscover);
135121abd8f2SDavid du Colombier 
135221abd8f2SDavid du Colombier 	for(i = 0; i < Nnetlink; i++){
135321abd8f2SDavid du Colombier 		n = netlinks.nl+i;
135421abd8f2SDavid du Colombier 		if(n->cc == 0)
135521abd8f2SDavid du Colombier 			continue;
135621abd8f2SDavid du Colombier 		p = seprint(p, e, "if%d path: %s\n", i, n->path);
135721abd8f2SDavid du Colombier 		p = seprint(p, e, "if%d ea: %E\n", i, n->ea);
135821abd8f2SDavid du Colombier 		p = seprint(p, e, "if%d flag: ", i); p = pflag(p, e, n->flag);
13590343ea0dSDavid du Colombier 		p = seprint(p, e, "if%d lostjumbo: %d\n", i, n->lostjumbo);
13600343ea0dSDavid du Colombier 		p = seprint(p, e, "if%d datamtu: %d\n", i, n->datamtu);
136121abd8f2SDavid du Colombier 	}
136221abd8f2SDavid du Colombier 
136321abd8f2SDavid du Colombier 	if(p - s < len)
136421abd8f2SDavid du Colombier 		len = p - s;
136521abd8f2SDavid du Colombier 	i = readstr(off, db, len, s);
136621abd8f2SDavid du Colombier 	free(s);
136721abd8f2SDavid du Colombier 	return i;
136821abd8f2SDavid du Colombier }
136921abd8f2SDavid du Colombier 
137021abd8f2SDavid du Colombier static long
aoeread(Chan * c,void * db,long n,vlong off)137121abd8f2SDavid du Colombier aoeread(Chan *c, void *db, long n, vlong off)
137221abd8f2SDavid du Colombier {
137321abd8f2SDavid du Colombier 	switch(TYPE(c->qid)){
137421abd8f2SDavid du Colombier 	default:
137521abd8f2SDavid du Colombier 		error(Eperm);
137621abd8f2SDavid du Colombier 	case Qzero:
137721abd8f2SDavid du Colombier 	case Qtopdir:
137821abd8f2SDavid du Colombier 	case Qunitdir:
137921abd8f2SDavid du Colombier 	case Qdevlinkdir:
138021abd8f2SDavid du Colombier 		return devdirread(c, db, n, 0, 0, aoegen);
138121abd8f2SDavid du Colombier 	case Qtopctl:
138221abd8f2SDavid du Colombier 		return topctlread(c, db, n, off);
138321abd8f2SDavid du Colombier 	case Qtoplog:
138421abd8f2SDavid du Colombier 		return eventlogread(db, n);
138521abd8f2SDavid du Colombier 	case Qctl:
138621abd8f2SDavid du Colombier 	case Qdata:
138721abd8f2SDavid du Colombier 	case Qconfig:
138821abd8f2SDavid du Colombier 	case Qident:
138921abd8f2SDavid du Colombier 		return unitread(c, db, n, off);
139021abd8f2SDavid du Colombier 	case Qdevlink:
139121abd8f2SDavid du Colombier 		return devlinkread(c, db, n, off);
139221abd8f2SDavid du Colombier 	}
139321abd8f2SDavid du Colombier }
139421abd8f2SDavid du Colombier 
139521abd8f2SDavid du Colombier static long
configwrite(Aoedev * d,void * db,long len)139621abd8f2SDavid du Colombier configwrite(Aoedev *d, void *db, long len)
139721abd8f2SDavid du Colombier {
139821abd8f2SDavid du Colombier 	char *s;
139921abd8f2SDavid du Colombier 	Aoeqc *ch;
140021abd8f2SDavid du Colombier 	Frame *f;
140121abd8f2SDavid du Colombier 	Srb *srb;
140221abd8f2SDavid du Colombier 
140321abd8f2SDavid du Colombier 	if(!UP(d))
1404a587111cSDavid du Colombier 		error(Eaoedown);
140541ac1ab6SDavid du Colombier 	if(len > ETHERMAXTU - AOEQCSZ)
140621abd8f2SDavid du Colombier 		error(Etoobig);
140721abd8f2SDavid du Colombier 	srb = srballoc(len);
140821abd8f2SDavid du Colombier 	s = malloc(len);
1409aa72973aSDavid du Colombier 	if(s == nil)
1410aa72973aSDavid du Colombier 		error(Enomem);
141121abd8f2SDavid du Colombier 	memmove(s, db, len);
1412*f7c114afSDavid du Colombier 
141321abd8f2SDavid du Colombier 	if(waserror()){
141421abd8f2SDavid du Colombier 		srbfree(srb);
141521abd8f2SDavid du Colombier 		free(s);
141621abd8f2SDavid du Colombier 		nexterror();
141721abd8f2SDavid du Colombier 	}
141821abd8f2SDavid du Colombier 	for (;;) {
141921abd8f2SDavid du Colombier 		qlock(d);
14200343ea0dSDavid du Colombier 		if(waserror()){
14210343ea0dSDavid du Colombier 			qunlock(d);
14220343ea0dSDavid du Colombier 			nexterror();
14230343ea0dSDavid du Colombier 		}
142421abd8f2SDavid du Colombier 		f = freeframe(d);
142521abd8f2SDavid du Colombier 		if(f != nil)
142621abd8f2SDavid du Colombier 			break;
14270343ea0dSDavid du Colombier 		poperror();
142821abd8f2SDavid du Colombier 		qunlock(d);
1429*f7c114afSDavid du Colombier 
1430c675ee80SDavid du Colombier 		if(waserror())
1431c675ee80SDavid du Colombier 			nexterror();
143221abd8f2SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 100);
1433c675ee80SDavid du Colombier 		poperror();
143421abd8f2SDavid du Colombier 	}
143541ac1ab6SDavid du Colombier 	f->nhdr = AOEQCSZ;
143621abd8f2SDavid du Colombier 	memset(f->hdr, 0, f->nhdr);
143721abd8f2SDavid du Colombier 	ch = (Aoeqc*)f->hdr;
1438*f7c114afSDavid du Colombier 	if(hset(d, f, ch, ACconfig) == -1) {
1439*f7c114afSDavid du Colombier 		/*
1440*f7c114afSDavid du Colombier 		 * these refer to qlock & waserror in the above for loop.
1441*f7c114afSDavid du Colombier 		 * there's still the first waserror outstanding.
1442*f7c114afSDavid du Colombier 		 */
1443*f7c114afSDavid du Colombier 		poperror();
1444*f7c114afSDavid du Colombier 		qunlock(d);
144521abd8f2SDavid du Colombier 		return 0;
1446*f7c114afSDavid du Colombier 	}
1447*f7c114afSDavid du Colombier 	srb->shared = 1;
144821abd8f2SDavid du Colombier 	f->srb = srb;
144921abd8f2SDavid du Colombier 	f->dp = s;
145021abd8f2SDavid du Colombier 	ch->verccmd = AQCfset;
145121abd8f2SDavid du Colombier 	hnputs(ch->cslen, len);
145221abd8f2SDavid du Colombier 	d->nout++;
145321abd8f2SDavid du Colombier 	srb->nout++;
145421abd8f2SDavid du Colombier 	f->dl->npkt++;
1455c675ee80SDavid du Colombier 	f->dlen = len;
1456*f7c114afSDavid du Colombier 	/* these too */
145721abd8f2SDavid du Colombier 	poperror();
145821abd8f2SDavid du Colombier 	qunlock(d);
145921abd8f2SDavid du Colombier 
1460c675ee80SDavid du Colombier 	devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
146121abd8f2SDavid du Colombier 	sleep(srb, srbready, srb);
146221abd8f2SDavid du Colombier 	if(srb->error)
146321abd8f2SDavid du Colombier 		error(srb->error);
146421abd8f2SDavid du Colombier 
146521abd8f2SDavid du Colombier 	qlock(d);
146621abd8f2SDavid du Colombier 	if(waserror()){
146721abd8f2SDavid du Colombier 		qunlock(d);
146821abd8f2SDavid du Colombier 		nexterror();
146921abd8f2SDavid du Colombier 	}
147021abd8f2SDavid du Colombier 	memmove(d->config, s, len);
147121abd8f2SDavid du Colombier 	d->nconfig = len;
147221abd8f2SDavid du Colombier 	poperror();
147321abd8f2SDavid du Colombier 	qunlock(d);
147421abd8f2SDavid du Colombier 
147521abd8f2SDavid du Colombier 	poperror();			/* pop first waserror */
147621abd8f2SDavid du Colombier 
147721abd8f2SDavid du Colombier 	srbfree(srb);
147821abd8f2SDavid du Colombier 	memmove(db, s, len);
147921abd8f2SDavid du Colombier 	free(s);
148021abd8f2SDavid du Colombier 	return len;
148121abd8f2SDavid du Colombier }
148221abd8f2SDavid du Colombier 
148321abd8f2SDavid du Colombier static int getmtu(Chan*);
148421abd8f2SDavid du Colombier 
148521abd8f2SDavid du Colombier static int
devmaxdata(Aoedev * d)1486060a30c6SDavid du Colombier devmaxdata(Aoedev *d)		/* return aoe mtu (excluding headers) */
148721abd8f2SDavid du Colombier {
1488060a30c6SDavid du Colombier 	int i, nmtu, mtu;
148921abd8f2SDavid du Colombier 	Devlink *l;
149021abd8f2SDavid du Colombier 	Netlink *n;
149121abd8f2SDavid du Colombier 
149221abd8f2SDavid du Colombier 	mtu = 100000;
149321abd8f2SDavid du Colombier 	for(i = 0; i < d->ndl; i++){
149421abd8f2SDavid du Colombier 		l = d->dl + i;
149521abd8f2SDavid du Colombier 		n = l->nl;
149621abd8f2SDavid du Colombier 		if((l->flag & Dup) == 0 || (n->flag & Dup) == 0)
149721abd8f2SDavid du Colombier 			continue;
1498060a30c6SDavid du Colombier 		nmtu = getmtu(n->mtu);
1499060a30c6SDavid du Colombier 		if(mtu > nmtu)
1500060a30c6SDavid du Colombier 			mtu = nmtu;
150121abd8f2SDavid du Colombier 	}
150221abd8f2SDavid du Colombier 	if(mtu == 100000)
1503060a30c6SDavid du Colombier 		mtu = ETHERMAXTU;		/* normal ethernet mtu */
150441ac1ab6SDavid du Colombier 	mtu -= AOEATASZ;
1505060a30c6SDavid du Colombier 	mtu -= (uint)mtu % Aoesectsz;
1506060a30c6SDavid du Colombier 	if(mtu < 2*Aoesectsz)			/* sanity */
1507060a30c6SDavid du Colombier 		mtu = 2*Aoesectsz;
150821abd8f2SDavid du Colombier 	return mtu;
150921abd8f2SDavid du Colombier }
151021abd8f2SDavid du Colombier 
151121abd8f2SDavid du Colombier static int
toggle(char * s,int f,int bit)1512060a30c6SDavid du Colombier toggle(char *s, int f, int bit)
151321abd8f2SDavid du Colombier {
151421abd8f2SDavid du Colombier 	if(s == nil)
1515060a30c6SDavid du Colombier 		f ^= bit;
1516060a30c6SDavid du Colombier 	else if(strcmp(s, "on") == 0)
1517060a30c6SDavid du Colombier 		f |= bit;
1518060a30c6SDavid du Colombier 	else
1519060a30c6SDavid du Colombier 		f &= ~bit;
1520060a30c6SDavid du Colombier 	return f;
152121abd8f2SDavid du Colombier }
152221abd8f2SDavid du Colombier 
152321abd8f2SDavid du Colombier static void ataident(Aoedev*);
152421abd8f2SDavid du Colombier 
152521abd8f2SDavid du Colombier static long
unitctlwrite(Aoedev * d,void * db,long n)152621abd8f2SDavid du Colombier unitctlwrite(Aoedev *d, void *db, long n)
152721abd8f2SDavid du Colombier {
1528060a30c6SDavid du Colombier 	uint maxbcnt, mtu;
152921abd8f2SDavid du Colombier 	uvlong bsize;
153021abd8f2SDavid du Colombier 	enum {
153121abd8f2SDavid du Colombier 		Failio,
153221abd8f2SDavid du Colombier 		Ident,
153321abd8f2SDavid du Colombier 		Jumbo,
153421abd8f2SDavid du Colombier 		Maxbno,
153521abd8f2SDavid du Colombier 		Mtu,
15362f205b96SDavid du Colombier 		Nofailf,
153721abd8f2SDavid du Colombier 		Setsize,
153821abd8f2SDavid du Colombier 	};
153921abd8f2SDavid du Colombier 	Cmdbuf *cb;
154099c72adcSDavid du Colombier 	Cmdtab *ct;
154199c72adcSDavid du Colombier 	static Cmdtab cmds[] = {
154221abd8f2SDavid du Colombier 		{Failio, 	"failio", 	1 },
154321abd8f2SDavid du Colombier 		{Ident, 	"identify", 	1 },
154421abd8f2SDavid du Colombier 		{Jumbo, 	"jumbo", 	0 },
154521abd8f2SDavid du Colombier 		{Maxbno,	"maxbno",	0 },
154621abd8f2SDavid du Colombier 		{Mtu,		"mtu",		0 },
15472f205b96SDavid du Colombier 		{Nofailf,	"nofail",	0 },
154821abd8f2SDavid du Colombier 		{Setsize, 	"setsize", 	0 },
154921abd8f2SDavid du Colombier 	};
155021abd8f2SDavid du Colombier 
155121abd8f2SDavid du Colombier 	cb = parsecmd(db, n);
155221abd8f2SDavid du Colombier 	qlock(d);
155321abd8f2SDavid du Colombier 	if(waserror()){
155421abd8f2SDavid du Colombier 		qunlock(d);
155521abd8f2SDavid du Colombier 		free(cb);
155621abd8f2SDavid du Colombier 		nexterror();
155721abd8f2SDavid du Colombier 	}
155821abd8f2SDavid du Colombier 	ct = lookupcmd(cb, cmds, nelem(cmds));
155921abd8f2SDavid du Colombier 	switch(ct->index){
156021abd8f2SDavid du Colombier 	case Failio:
156121abd8f2SDavid du Colombier 		downdev(d, "i/o failure");
156221abd8f2SDavid du Colombier 		break;
156321abd8f2SDavid du Colombier 	case Ident:
156421abd8f2SDavid du Colombier 		ataident(d);
156521abd8f2SDavid du Colombier 		break;
156621abd8f2SDavid du Colombier 	case Jumbo:
1567060a30c6SDavid du Colombier 		d->flag = toggle(cb->f[1], d->flag, Djumbo);
156821abd8f2SDavid du Colombier 		break;
156921abd8f2SDavid du Colombier 	case Maxbno:
157021abd8f2SDavid du Colombier 	case Mtu:
157121abd8f2SDavid du Colombier 		maxbcnt = devmaxdata(d);
157221abd8f2SDavid du Colombier 		if(cb->nf > 2)
157321abd8f2SDavid du Colombier 			error(Ecmdargs);
157421abd8f2SDavid du Colombier 		if(cb->nf == 2){
1575060a30c6SDavid du Colombier 			mtu = strtoul(cb->f[1], 0, 0);
157621abd8f2SDavid du Colombier 			if(ct->index == Maxbno)
1577060a30c6SDavid du Colombier 				mtu *= Aoesectsz;
157821abd8f2SDavid du Colombier 			else{
1579060a30c6SDavid du Colombier 				mtu -= AOEATASZ;
1580060a30c6SDavid du Colombier 				mtu &= ~(Aoesectsz-1);
158121abd8f2SDavid du Colombier 			}
1582060a30c6SDavid du Colombier 			if(mtu == 0 || mtu > maxbcnt)
15832f205b96SDavid du Colombier 				cmderror(cb, "mtu out of legal range");
1584060a30c6SDavid du Colombier 			maxbcnt = mtu;
158521abd8f2SDavid du Colombier 		}
158621abd8f2SDavid du Colombier 		d->maxbcnt = maxbcnt;
158721abd8f2SDavid du Colombier 		break;
15882f205b96SDavid du Colombier 	case Nofailf:
1589060a30c6SDavid du Colombier 		d->flag = toggle(cb->f[1], d->flag, Dnofail);
15902f205b96SDavid du Colombier 		break;
159121abd8f2SDavid du Colombier 	case Setsize:
159221abd8f2SDavid du Colombier 		bsize = d->realbsize;
159321abd8f2SDavid du Colombier 		if(cb->nf > 2)
159421abd8f2SDavid du Colombier 			error(Ecmdargs);
159521abd8f2SDavid du Colombier 		if(cb->nf == 2){
159621abd8f2SDavid du Colombier 			bsize = strtoull(cb->f[1], 0, 0);
159721abd8f2SDavid du Colombier 			if(bsize % Aoesectsz)
159821abd8f2SDavid du Colombier 				cmderror(cb, "disk size must be sector aligned");
159921abd8f2SDavid du Colombier 		}
160021abd8f2SDavid du Colombier 		d->bsize = bsize;
160121abd8f2SDavid du Colombier 		break;
160221abd8f2SDavid du Colombier 	default:
160321abd8f2SDavid du Colombier 		cmderror(cb, "unknown aoe control message");
160421abd8f2SDavid du Colombier 	}
160521abd8f2SDavid du Colombier 	poperror();
160621abd8f2SDavid du Colombier 	qunlock(d);
160721abd8f2SDavid du Colombier 	free(cb);
160821abd8f2SDavid du Colombier 	return n;
160921abd8f2SDavid du Colombier }
161021abd8f2SDavid du Colombier 
161121abd8f2SDavid du Colombier static long
unitwrite(Chan * c,void * db,long n,vlong off)161221abd8f2SDavid du Colombier unitwrite(Chan *c, void *db, long n, vlong off)
161321abd8f2SDavid du Colombier {
161421abd8f2SDavid du Colombier 	long rv;
161521abd8f2SDavid du Colombier 	char *buf;
161621abd8f2SDavid du Colombier 	Aoedev *d;
161721abd8f2SDavid du Colombier 
161821abd8f2SDavid du Colombier 	d = unit2dev(UNIT(c->qid));
161921abd8f2SDavid du Colombier 	switch(TYPE(c->qid)){
162021abd8f2SDavid du Colombier 	default:
162121abd8f2SDavid du Colombier 		error(Ebadarg);
162221abd8f2SDavid du Colombier 	case Qctl:
162321abd8f2SDavid du Colombier 		return unitctlwrite(d, db, n);
162421abd8f2SDavid du Colombier 	case Qident:
162521abd8f2SDavid du Colombier 		error(Eperm);
162621abd8f2SDavid du Colombier 	case Qdata:
162799c72adcSDavid du Colombier 		return rw(d, Write, db, n, off);
162821abd8f2SDavid du Colombier 	case Qconfig:
162921abd8f2SDavid du Colombier 		if(off + n > sizeof d->config)
163021abd8f2SDavid du Colombier 			error(Etoobig);
163121abd8f2SDavid du Colombier 		buf = malloc(sizeof d->config);
1632aa72973aSDavid du Colombier 		if(buf == nil)
1633aa72973aSDavid du Colombier 			error(Enomem);
16342f205b96SDavid du Colombier 		if(waserror()){
16352f205b96SDavid du Colombier 			free(buf);
16362f205b96SDavid du Colombier 			nexterror();
16372f205b96SDavid du Colombier 		}
163821abd8f2SDavid du Colombier 		memmove(buf, d->config, d->nconfig);
163921abd8f2SDavid du Colombier 		memmove(buf + off, db, n);
164021abd8f2SDavid du Colombier 		rv = configwrite(d, buf, n + off);
16412f205b96SDavid du Colombier 		poperror();
164221abd8f2SDavid du Colombier 		free(buf);
164321abd8f2SDavid du Colombier 		return rv;
164421abd8f2SDavid du Colombier 	}
164521abd8f2SDavid du Colombier }
164621abd8f2SDavid du Colombier 
164721abd8f2SDavid du Colombier static Netlink*
addnet(char * path,Chan * cc,Chan * dc,Chan * mtu,uchar * ea)164821abd8f2SDavid du Colombier addnet(char *path, Chan *cc, Chan *dc, Chan *mtu, uchar *ea)
164921abd8f2SDavid du Colombier {
165021abd8f2SDavid du Colombier 	Netlink *nl, *e;
165121abd8f2SDavid du Colombier 
165221abd8f2SDavid du Colombier 	lock(&netlinks);
165321abd8f2SDavid du Colombier 	if(waserror()){
165421abd8f2SDavid du Colombier 		unlock(&netlinks);
165521abd8f2SDavid du Colombier 		nexterror();
165621abd8f2SDavid du Colombier 	}
165721abd8f2SDavid du Colombier 	nl = netlinks.nl;
165821abd8f2SDavid du Colombier 	e = nl + nelem(netlinks.nl);
165999c72adcSDavid du Colombier 	for(; nl < e && nl->cc; nl++)
166021abd8f2SDavid du Colombier 		continue;
166199c72adcSDavid du Colombier 	if (nl >= e)
166299c72adcSDavid du Colombier 		error("out of netlink structures");
166321abd8f2SDavid du Colombier 	nl->cc = cc;
166421abd8f2SDavid du Colombier 	nl->dc = dc;
166521abd8f2SDavid du Colombier 	nl->mtu = mtu;
166621abd8f2SDavid du Colombier 	strncpy(nl->path, path, sizeof nl->path);
166721abd8f2SDavid du Colombier 	memmove(nl->ea, ea, sizeof nl->ea);
166821abd8f2SDavid du Colombier 	poperror();
166921abd8f2SDavid du Colombier 	nl->flag |= Dup;
167021abd8f2SDavid du Colombier 	unlock(&netlinks);
167121abd8f2SDavid du Colombier 	return nl;
167221abd8f2SDavid du Colombier }
167321abd8f2SDavid du Colombier 
167421abd8f2SDavid du Colombier static int
newunit(void)167521abd8f2SDavid du Colombier newunit(void)
167621abd8f2SDavid du Colombier {
167721abd8f2SDavid du Colombier 	int x;
167821abd8f2SDavid du Colombier 
167921abd8f2SDavid du Colombier 	lock(&units);
1680c675ee80SDavid du Colombier 	if(units.ref == Maxunits)
1681c675ee80SDavid du Colombier 		x = -1;
1682c675ee80SDavid du Colombier 	else
168321abd8f2SDavid du Colombier 		x = units.ref++;
168421abd8f2SDavid du Colombier 	unlock(&units);
168521abd8f2SDavid du Colombier 	return x;
168621abd8f2SDavid du Colombier }
168721abd8f2SDavid du Colombier 
168821abd8f2SDavid du Colombier static int
dropunit(void)168921abd8f2SDavid du Colombier dropunit(void)
169021abd8f2SDavid du Colombier {
169121abd8f2SDavid du Colombier 	int x;
169221abd8f2SDavid du Colombier 
169321abd8f2SDavid du Colombier 	lock(&units);
169421abd8f2SDavid du Colombier 	x = --units.ref;
169521abd8f2SDavid du Colombier 	unlock(&units);
169621abd8f2SDavid du Colombier 	return x;
169721abd8f2SDavid du Colombier }
169821abd8f2SDavid du Colombier 
16992f205b96SDavid du Colombier /*
17002f205b96SDavid du Colombier  * always allocate max frames.  maxout may change.
17012f205b96SDavid du Colombier  */
170221abd8f2SDavid du Colombier static Aoedev*
newdev(long major,long minor,int n)170321abd8f2SDavid du Colombier newdev(long major, long minor, int n)
170421abd8f2SDavid du Colombier {
170521abd8f2SDavid du Colombier 	Aoedev *d;
170621abd8f2SDavid du Colombier 	Frame *f, *e;
170721abd8f2SDavid du Colombier 
170821abd8f2SDavid du Colombier 	d = mallocz(sizeof *d, 1);
17092f205b96SDavid du Colombier 	f = mallocz(sizeof *f * Maxframes, 1);
171021abd8f2SDavid du Colombier 	if (!d || !f) {
171121abd8f2SDavid du Colombier 		free(d);
171221abd8f2SDavid du Colombier 		free(f);
171321abd8f2SDavid du Colombier 		error("aoe device allocation failure");
171421abd8f2SDavid du Colombier 	}
171521abd8f2SDavid du Colombier 	d->nframes = n;
171621abd8f2SDavid du Colombier 	d->frames = f;
171721abd8f2SDavid du Colombier 	for (e = f + n; f < e; f++)
171821abd8f2SDavid du Colombier 		f->tag = Tfree;
171921abd8f2SDavid du Colombier 	d->maxout = n;
172021abd8f2SDavid du Colombier 	d->major = major;
172121abd8f2SDavid du Colombier 	d->minor = minor;
172221abd8f2SDavid du Colombier 	d->maxbcnt = Dbcnt;
172321abd8f2SDavid du Colombier 	d->flag = Djumbo;
172421abd8f2SDavid du Colombier 	d->unit = newunit();		/* bzzt.  inaccurate if units removed */
1725c675ee80SDavid du Colombier 	if(d->unit == -1){
1726c675ee80SDavid du Colombier 		free(d->frames);
1727*f7c114afSDavid du Colombier 		free(d);
1728c675ee80SDavid du Colombier 		error("too many units");
1729c675ee80SDavid du Colombier 	}
173021abd8f2SDavid du Colombier 	d->dl = d->dltab;
173121abd8f2SDavid du Colombier 	return d;
173221abd8f2SDavid du Colombier }
173321abd8f2SDavid du Colombier 
173421abd8f2SDavid du Colombier static Aoedev*
mm2dev(int major,int minor)1735c675ee80SDavid du Colombier mm2dev(int major, int minor)
173621abd8f2SDavid du Colombier {
173721abd8f2SDavid du Colombier 	Aoedev *d;
173821abd8f2SDavid du Colombier 
173921abd8f2SDavid du Colombier 	rlock(&devs);
174021abd8f2SDavid du Colombier 	for(d = devs.d; d; d = d->next)
174121abd8f2SDavid du Colombier 		if(d->major == major && d->minor == minor){
174221abd8f2SDavid du Colombier 			runlock(&devs);
174321abd8f2SDavid du Colombier 			return d;
174421abd8f2SDavid du Colombier 		}
174521abd8f2SDavid du Colombier 	runlock(&devs);
1746c675ee80SDavid du Colombier 	eventlog("mm2dev: %d.%d not found\n", major, minor);
174721abd8f2SDavid du Colombier 	return nil;
174821abd8f2SDavid du Colombier }
174921abd8f2SDavid du Colombier 
175021abd8f2SDavid du Colombier /* Find the device in our list.  If not known, add it */
175121abd8f2SDavid du Colombier static Aoedev*
getdev(long major,long minor,int n)175221abd8f2SDavid du Colombier getdev(long major, long minor, int n)
175321abd8f2SDavid du Colombier {
175421abd8f2SDavid du Colombier 	Aoedev *d;
175521abd8f2SDavid du Colombier 
17562f205b96SDavid du Colombier 	if(major == 0xffff || minor == 0xff)
17572f205b96SDavid du Colombier 		return 0;
175821abd8f2SDavid du Colombier 	wlock(&devs);
175921abd8f2SDavid du Colombier 	if(waserror()){
176021abd8f2SDavid du Colombier 		wunlock(&devs);
176121abd8f2SDavid du Colombier 		nexterror();
176221abd8f2SDavid du Colombier 	}
176321abd8f2SDavid du Colombier 	for(d = devs.d; d; d = d->next)
176421abd8f2SDavid du Colombier 		if(d->major == major && d->minor == minor)
176521abd8f2SDavid du Colombier 			break;
17661e18b896SDavid du Colombier 	if (d == nil) {
176721abd8f2SDavid du Colombier 		d = newdev(major, minor, n);
176821abd8f2SDavid du Colombier 		d->next = devs.d;
176921abd8f2SDavid du Colombier 		devs.d = d;
177021abd8f2SDavid du Colombier 	}
177121abd8f2SDavid du Colombier 	poperror();
177221abd8f2SDavid du Colombier 	wunlock(&devs);
177321abd8f2SDavid du Colombier 	return d;
177421abd8f2SDavid du Colombier }
177521abd8f2SDavid du Colombier 
177621abd8f2SDavid du Colombier static ushort
gbit16(void * a)177721abd8f2SDavid du Colombier gbit16(void *a)
177821abd8f2SDavid du Colombier {
177921abd8f2SDavid du Colombier 	uchar *i;
178021abd8f2SDavid du Colombier 
178121abd8f2SDavid du Colombier 	i = a;
178221abd8f2SDavid du Colombier 	return i[1] << 8 | i[0];
178321abd8f2SDavid du Colombier }
178421abd8f2SDavid du Colombier 
178591b330d9SDavid du Colombier static ulong
gbit32(void * a)178621abd8f2SDavid du Colombier gbit32(void *a)
178721abd8f2SDavid du Colombier {
178891b330d9SDavid du Colombier 	ulong j;
178921abd8f2SDavid du Colombier 	uchar *i;
179021abd8f2SDavid du Colombier 
179121abd8f2SDavid du Colombier 	i = a;
179221abd8f2SDavid du Colombier 	j  = i[3] << 24;
179321abd8f2SDavid du Colombier 	j |= i[2] << 16;
179421abd8f2SDavid du Colombier 	j |= i[1] << 8;
179521abd8f2SDavid du Colombier 	j |= i[0];
179621abd8f2SDavid du Colombier 	return j;
179721abd8f2SDavid du Colombier }
179821abd8f2SDavid du Colombier 
179921abd8f2SDavid du Colombier static uvlong
gbit64(void * a)180021abd8f2SDavid du Colombier gbit64(void *a)
180121abd8f2SDavid du Colombier {
180221abd8f2SDavid du Colombier 	uchar *i;
180321abd8f2SDavid du Colombier 
180421abd8f2SDavid du Colombier 	i = a;
180521abd8f2SDavid du Colombier 	return (uvlong)gbit32(i+4) << 32 | gbit32(a);
180621abd8f2SDavid du Colombier }
180721abd8f2SDavid du Colombier 
180821abd8f2SDavid du Colombier static void
ataident(Aoedev * d)180921abd8f2SDavid du Colombier ataident(Aoedev *d)
181021abd8f2SDavid du Colombier {
181121abd8f2SDavid du Colombier 	Aoeata *a;
181221abd8f2SDavid du Colombier 	Block *b;
181321abd8f2SDavid du Colombier 	Frame *f;
181421abd8f2SDavid du Colombier 
181521abd8f2SDavid du Colombier 	f = freeframe(d);
181621abd8f2SDavid du Colombier 	if(f == nil)
181721abd8f2SDavid du Colombier 		return;
181841ac1ab6SDavid du Colombier 	f->nhdr = AOEATASZ;
181921abd8f2SDavid du Colombier 	memset(f->hdr, 0, f->nhdr);
182021abd8f2SDavid du Colombier 	a = (Aoeata*)f->hdr;
182121abd8f2SDavid du Colombier 	if(hset(d, f, a, ACata) == -1)
182221abd8f2SDavid du Colombier 		return;
182321abd8f2SDavid du Colombier 	a->cmdstat = Cid;	/* ata 6, page 110 */
182421abd8f2SDavid du Colombier 	a->scnt = 1;
182521abd8f2SDavid du Colombier 	a->lba[3] = 0xa0;
182621abd8f2SDavid du Colombier 	d->nout++;
182721abd8f2SDavid du Colombier 	f->dl->npkt++;
1828c675ee80SDavid du Colombier 	f->bcnt = 512;
1829c675ee80SDavid du Colombier 	f->dlen = 0;
1830c675ee80SDavid du Colombier 	b = allocfb(f);
183121abd8f2SDavid du Colombier 	devtab[f->nl->dc->type]->bwrite(f->nl->dc, b, 0);
183221abd8f2SDavid du Colombier }
183321abd8f2SDavid du Colombier 
183421abd8f2SDavid du Colombier static int
getmtu(Chan * mtuch)1835060a30c6SDavid du Colombier getmtu(Chan *mtuch)
183621abd8f2SDavid du Colombier {
183721abd8f2SDavid du Colombier 	int n, mtu;
183821abd8f2SDavid du Colombier 	char buf[36];
183921abd8f2SDavid du Colombier 
1840060a30c6SDavid du Colombier 	mtu = ETHERMAXTU;
1841060a30c6SDavid du Colombier 	if(mtuch == nil || waserror())
184221abd8f2SDavid du Colombier 		return mtu;
1843060a30c6SDavid du Colombier 	n = devtab[mtuch->type]->read(mtuch, buf, sizeof buf - 1, 0);
184421abd8f2SDavid du Colombier 	if(n > 12){
184521abd8f2SDavid du Colombier 		buf[n] = 0;
184621abd8f2SDavid du Colombier 		mtu = strtoul(buf + 12, 0, 0);
184721abd8f2SDavid du Colombier 	}
184821abd8f2SDavid du Colombier 	poperror();
184921abd8f2SDavid du Colombier 	return mtu;
185021abd8f2SDavid du Colombier }
185121abd8f2SDavid du Colombier 
185221abd8f2SDavid du Colombier static int
newdlea(Devlink * l,uchar * ea)185321abd8f2SDavid du Colombier newdlea(Devlink *l, uchar *ea)
185421abd8f2SDavid du Colombier {
185521abd8f2SDavid du Colombier 	int i;
185621abd8f2SDavid du Colombier 	uchar *t;
185721abd8f2SDavid du Colombier 
185821abd8f2SDavid du Colombier 	for(i = 0; i < Nea; i++){
185921abd8f2SDavid du Colombier 		t = l->eatab[i];
186021abd8f2SDavid du Colombier 		if(i == l->nea){
186121abd8f2SDavid du Colombier 			memmove(t, ea, Eaddrlen);
186221abd8f2SDavid du Colombier 			return l->nea++;
186321abd8f2SDavid du Colombier 		}
186421abd8f2SDavid du Colombier 		if(memcmp(t, ea, Eaddrlen) == 0)
186521abd8f2SDavid du Colombier 			return i;
186621abd8f2SDavid du Colombier 	}
186721abd8f2SDavid du Colombier 	return -1;
186821abd8f2SDavid du Colombier }
186921abd8f2SDavid du Colombier 
187021abd8f2SDavid du Colombier static Devlink*
newdevlink(Aoedev * d,Netlink * n,Aoeqc * c)187121abd8f2SDavid du Colombier newdevlink(Aoedev *d, Netlink *n, Aoeqc *c)
187221abd8f2SDavid du Colombier {
187321abd8f2SDavid du Colombier 	int i;
187421abd8f2SDavid du Colombier 	Devlink *l;
187521abd8f2SDavid du Colombier 
187621abd8f2SDavid du Colombier 	for(i = 0; i < Ndevlink; i++){
187721abd8f2SDavid du Colombier 		l = d->dl + i;
187821abd8f2SDavid du Colombier 		if(i == d->ndl){
187921abd8f2SDavid du Colombier 			d->ndl++;
188021abd8f2SDavid du Colombier 			newdlea(l, c->src);
188121abd8f2SDavid du Colombier 			l->nl = n;
188221abd8f2SDavid du Colombier 			l->flag |= Dup;
188321abd8f2SDavid du Colombier 			l->mintimer = Rtmin;
188421abd8f2SDavid du Colombier 			l->rttavg = Rtmax;
188521abd8f2SDavid du Colombier 			return l;
188621abd8f2SDavid du Colombier 		}
18872f205b96SDavid du Colombier 		if(l->nl == n) {
18882f205b96SDavid du Colombier 			newdlea(l, c->src);
18892f205b96SDavid du Colombier 			l->flag |= Dup;
189021abd8f2SDavid du Colombier 			return l;
189121abd8f2SDavid du Colombier 		}
18922f205b96SDavid du Colombier 	}
1893c675ee80SDavid du Colombier 	eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, c->src);
189421abd8f2SDavid du Colombier 	return 0;
189521abd8f2SDavid du Colombier }
189621abd8f2SDavid du Colombier 
189721abd8f2SDavid du Colombier static void
errrsp(Block * b,char * s)189821abd8f2SDavid du Colombier errrsp(Block *b, char *s)
189921abd8f2SDavid du Colombier {
190021abd8f2SDavid du Colombier 	int n;
190121abd8f2SDavid du Colombier 	Aoedev *d;
190221abd8f2SDavid du Colombier 	Aoehdr *h;
190321abd8f2SDavid du Colombier 	Frame *f;
190421abd8f2SDavid du Colombier 
190521abd8f2SDavid du Colombier 	h = (Aoehdr*)b->rp;
190621abd8f2SDavid du Colombier 	n = nhgetl(h->tag);
190721abd8f2SDavid du Colombier 	if(n == Tmgmt || n == Tfree)
190821abd8f2SDavid du Colombier 		return;
190921abd8f2SDavid du Colombier 	d = mm2dev(nhgets(h->major), h->minor);
191021abd8f2SDavid du Colombier 	if(d == 0)
191121abd8f2SDavid du Colombier 		return;
191221abd8f2SDavid du Colombier 	if(f = getframe(d, n))
191321abd8f2SDavid du Colombier 		frameerror(d, f, s);
191421abd8f2SDavid du Colombier }
191521abd8f2SDavid du Colombier 
191621abd8f2SDavid du Colombier static void
qcfgrsp(Block * b,Netlink * nl)191721abd8f2SDavid du Colombier qcfgrsp(Block *b, Netlink *nl)
191821abd8f2SDavid du Colombier {
191921abd8f2SDavid du Colombier 	int major, cmd, cslen, blen;
192021abd8f2SDavid du Colombier 	unsigned n;
192121abd8f2SDavid du Colombier 	Aoedev *d;
192221abd8f2SDavid du Colombier 	Aoeqc *ch;
192321abd8f2SDavid du Colombier 	Devlink *l;
192421abd8f2SDavid du Colombier 	Frame *f;
192521abd8f2SDavid du Colombier 
192621abd8f2SDavid du Colombier 	ch = (Aoeqc*)b->rp;
192721abd8f2SDavid du Colombier 	major = nhgets(ch->major);
192821abd8f2SDavid du Colombier 	n = nhgetl(ch->tag);
192921abd8f2SDavid du Colombier 	if(n != Tmgmt){
193021abd8f2SDavid du Colombier 		d = mm2dev(major, ch->minor);
1931c675ee80SDavid du Colombier 		if(d == nil)
1932c675ee80SDavid du Colombier 			return;
193321abd8f2SDavid du Colombier 		qlock(d);
193421abd8f2SDavid du Colombier 		f = getframe(d, n);
193521abd8f2SDavid du Colombier 		if(f == nil){
193621abd8f2SDavid du Colombier 			qunlock(d);
193721abd8f2SDavid du Colombier 			eventlog("%æ: unknown response tag %ux\n", d, n);
193821abd8f2SDavid du Colombier 			return;
193921abd8f2SDavid du Colombier 		}
194021abd8f2SDavid du Colombier 		cslen = nhgets(ch->cslen);
194141ac1ab6SDavid du Colombier 		blen = BLEN(b) - AOEQCSZ;
19422f205b96SDavid du Colombier 		if(cslen < blen && BLEN(b) > 60)
194321abd8f2SDavid du Colombier 			eventlog("%æ: cfgrsp: tag %.8ux oversized %d %d\n",
194421abd8f2SDavid du Colombier 				d, n, cslen, blen);
194521abd8f2SDavid du Colombier 		if(cslen > blen){
194621abd8f2SDavid du Colombier 			eventlog("%æ: cfgrsp: tag %.8ux runt %d %d\n",
194721abd8f2SDavid du Colombier 				d, n, cslen, blen);
194821abd8f2SDavid du Colombier 			cslen = blen;
194921abd8f2SDavid du Colombier 		}
195021abd8f2SDavid du Colombier 		memmove(f->dp, ch + 1, cslen);
195121abd8f2SDavid du Colombier 		f->srb->nout--;
195221abd8f2SDavid du Colombier 		wakeup(f->srb);
1953*f7c114afSDavid du Colombier 		f->srb->shared = 0;
195421abd8f2SDavid du Colombier 		d->nout--;
195521abd8f2SDavid du Colombier 		f->srb = nil;
195621abd8f2SDavid du Colombier 		f->tag = Tfree;
195721abd8f2SDavid du Colombier 		qunlock(d);
195821abd8f2SDavid du Colombier 		return;
195921abd8f2SDavid du Colombier 	}
196021abd8f2SDavid du Colombier 
196121abd8f2SDavid du Colombier 	cmd = ch->verccmd & 0xf;
196221abd8f2SDavid du Colombier 	if(cmd != 0){
1963421195e0SDavid du Colombier 		eventlog("aoe%d.%d: cfgrsp: bad command %d\n", major, ch->minor, cmd);
196421abd8f2SDavid du Colombier 		return;
196521abd8f2SDavid du Colombier 	}
196621abd8f2SDavid du Colombier 	n = nhgets(ch->bufcnt);
196721abd8f2SDavid du Colombier 	if(n > Maxframes)
196821abd8f2SDavid du Colombier 		n = Maxframes;
1969c675ee80SDavid du Colombier 
1970c675ee80SDavid du Colombier 	if(waserror()){
1971c675ee80SDavid du Colombier 		eventlog("getdev: %d.%d ignored: %s\n", major, ch->minor, up->errstr);
1972c675ee80SDavid du Colombier 		return;
1973c675ee80SDavid du Colombier 	}
197421abd8f2SDavid du Colombier 	d = getdev(major, ch->minor, n);
1975c675ee80SDavid du Colombier 	poperror();
19762f205b96SDavid du Colombier 	if(d == 0)
19772f205b96SDavid du Colombier 		return;
1978c675ee80SDavid du Colombier 
197921abd8f2SDavid du Colombier 	qlock(d);
19802f205b96SDavid du Colombier 	*up->errstr = 0;
198121abd8f2SDavid du Colombier 	if(waserror()){
198221abd8f2SDavid du Colombier 		qunlock(d);
1983c675ee80SDavid du Colombier 		eventlog("%æ: %s\n", d, up->errstr);
1984421195e0SDavid du Colombier 		nexterror();
198521abd8f2SDavid du Colombier 	}
198621abd8f2SDavid du Colombier 
198721abd8f2SDavid du Colombier 	l = newdevlink(d, nl, ch);		/* add this interface. */
198821abd8f2SDavid du Colombier 
198921abd8f2SDavid du Colombier 	d->fwver = nhgets(ch->fwver);
199021abd8f2SDavid du Colombier 	n = nhgets(ch->cslen);
199121abd8f2SDavid du Colombier 	if(n > sizeof d->config)
199221abd8f2SDavid du Colombier 		n = sizeof d->config;
199321abd8f2SDavid du Colombier 	d->nconfig = n;
199421abd8f2SDavid du Colombier 	memmove(d->config, ch + 1, n);
199521abd8f2SDavid du Colombier 	if(l != 0 && d->flag & Djumbo){
199641ac1ab6SDavid du Colombier 		n = getmtu(nl->mtu) - AOEATASZ;
199721abd8f2SDavid du Colombier 		n /= Aoesectsz;
199821abd8f2SDavid du Colombier 		if(n > ch->scnt)
199921abd8f2SDavid du Colombier 			n = ch->scnt;
200021abd8f2SDavid du Colombier 		n = n? n * Aoesectsz: Dbcnt;
200121abd8f2SDavid du Colombier 		if(n != d->maxbcnt){
200221abd8f2SDavid du Colombier 			eventlog("%æ: setting %d byte data frames on %s:%E\n",
200321abd8f2SDavid du Colombier 				d, n, nl->path, nl->ea);
200421abd8f2SDavid du Colombier 			d->maxbcnt = n;
200521abd8f2SDavid du Colombier 		}
200621abd8f2SDavid du Colombier 	}
200721abd8f2SDavid du Colombier 	if(d->nopen == 0)
200821abd8f2SDavid du Colombier 		ataident(d);
200921abd8f2SDavid du Colombier 	poperror();
201021abd8f2SDavid du Colombier 	qunlock(d);
201121abd8f2SDavid du Colombier }
201221abd8f2SDavid du Colombier 
201345dfec46SDavid du Colombier void
aoeidmove(char * p,ushort * u,unsigned n)201445dfec46SDavid du Colombier aoeidmove(char *p, ushort *u, unsigned n)
201521abd8f2SDavid du Colombier {
201621abd8f2SDavid du Colombier 	int i;
201745dfec46SDavid du Colombier 	char *op, *e, *s;
201821abd8f2SDavid du Colombier 
201921abd8f2SDavid du Colombier 	op = p;
202045dfec46SDavid du Colombier 	/*
20212f205b96SDavid du Colombier 	 * the ushort `*u' is sometimes not aligned on a short boundary,
20222f205b96SDavid du Colombier 	 * so dereferencing u[i] causes an alignment exception on
202345dfec46SDavid du Colombier 	 * some machines.
202445dfec46SDavid du Colombier 	 */
202545dfec46SDavid du Colombier 	s = (char *)u;
202645dfec46SDavid du Colombier 	for(i = 0; i < n; i += 2){
202745dfec46SDavid du Colombier 		*p++ = s[i + 1];
202845dfec46SDavid du Colombier 		*p++ = s[i];
202921abd8f2SDavid du Colombier 	}
203021abd8f2SDavid du Colombier 	*p = 0;
203121abd8f2SDavid du Colombier 	while(p > op && *--p == ' ')
203221abd8f2SDavid du Colombier 		*p = 0;
203321abd8f2SDavid du Colombier 	e = p;
203421abd8f2SDavid du Colombier 	p = op;
203521abd8f2SDavid du Colombier 	while(*p == ' ')
203621abd8f2SDavid du Colombier 		p++;
203721abd8f2SDavid du Colombier 	memmove(op, p, n - (e - p));
203821abd8f2SDavid du Colombier }
203921abd8f2SDavid du Colombier 
204021abd8f2SDavid du Colombier static vlong
aoeidentify(Aoedev * d,ushort * id)204121abd8f2SDavid du Colombier aoeidentify(Aoedev *d, ushort *id)
204221abd8f2SDavid du Colombier {
204321abd8f2SDavid du Colombier 	int i;
204421abd8f2SDavid du Colombier 	vlong s;
204521abd8f2SDavid du Colombier 
204621abd8f2SDavid du Colombier 	d->flag &= ~(Dllba|Dpower|Dsmart|Dnop|Dup);
204721abd8f2SDavid du Colombier 
204821abd8f2SDavid du Colombier 	i = gbit16(id+83) | gbit16(id+86);
204921abd8f2SDavid du Colombier 	if(i & (1<<10)){
205021abd8f2SDavid du Colombier 		d->flag |= Dllba;
205121abd8f2SDavid du Colombier 		s = gbit64(id+100);
205221abd8f2SDavid du Colombier 	}else
205321abd8f2SDavid du Colombier 		s = gbit32(id+60);
205421abd8f2SDavid du Colombier 
205521abd8f2SDavid du Colombier 	i = gbit16(id+83);
205621abd8f2SDavid du Colombier 	if((i>>14) == 1) {
205721abd8f2SDavid du Colombier 		if(i & (1<<3))
205821abd8f2SDavid du Colombier 			d->flag  |= Dpower;
205921abd8f2SDavid du Colombier 		i = gbit16(id+82);
206021abd8f2SDavid du Colombier 		if(i & 1)
206121abd8f2SDavid du Colombier 			d->flag  |= Dsmart;
206221abd8f2SDavid du Colombier 		if(i & (1<<14))
206321abd8f2SDavid du Colombier 			d->flag  |= Dnop;
206421abd8f2SDavid du Colombier 	}
206521abd8f2SDavid du Colombier //	eventlog("%æ up\n", d);
206621abd8f2SDavid du Colombier 	d->flag |= Dup;
206721abd8f2SDavid du Colombier 	memmove(d->ident, id, sizeof d->ident);
206821abd8f2SDavid du Colombier 	return s;
206921abd8f2SDavid du Colombier }
207021abd8f2SDavid du Colombier 
2071c675ee80SDavid du Colombier static void
newvers(Aoedev * d)2072c675ee80SDavid du Colombier newvers(Aoedev *d)
2073c675ee80SDavid du Colombier {
2074c675ee80SDavid du Colombier 	lock(&drivevers);
2075c675ee80SDavid du Colombier 	d->vers = drivevers.ref++;
2076c675ee80SDavid du Colombier 	unlock(&drivevers);
2077c675ee80SDavid du Colombier }
2078c675ee80SDavid du Colombier 
207921abd8f2SDavid du Colombier static int
identify(Aoedev * d,ushort * id)208021abd8f2SDavid du Colombier identify(Aoedev *d, ushort *id)
208121abd8f2SDavid du Colombier {
208221abd8f2SDavid du Colombier 	vlong osectors, s;
208321abd8f2SDavid du Colombier 	uchar oserial[21];
208421abd8f2SDavid du Colombier 
208521abd8f2SDavid du Colombier 	s = aoeidentify(d, id);
208621abd8f2SDavid du Colombier 	if(s == -1)
208721abd8f2SDavid du Colombier 		return -1;
208821abd8f2SDavid du Colombier 	osectors = d->realbsize;
208921abd8f2SDavid du Colombier 	memmove(oserial, d->serial, sizeof d->serial);
209021abd8f2SDavid du Colombier 
209145dfec46SDavid du Colombier 	aoeidmove(d->serial, id+10, 20);
209245dfec46SDavid du Colombier 	aoeidmove(d->firmware, id+23, 8);
209345dfec46SDavid du Colombier 	aoeidmove(d->model, id+27, 40);
209421abd8f2SDavid du Colombier 
209521abd8f2SDavid du Colombier 	s *= Aoesectsz;
209621abd8f2SDavid du Colombier 	if((osectors == 0 || osectors != s) &&
209721abd8f2SDavid du Colombier 	    memcmp(oserial, d->serial, sizeof oserial) != 0){
209821abd8f2SDavid du Colombier 		d->bsize = s;
209921abd8f2SDavid du Colombier 		d->realbsize = s;
210021abd8f2SDavid du Colombier //		d->mediachange = 1;
2101c675ee80SDavid du Colombier 		newvers(d);
210221abd8f2SDavid du Colombier 	}
210321abd8f2SDavid du Colombier 	return 0;
210421abd8f2SDavid du Colombier }
210521abd8f2SDavid du Colombier 
210621abd8f2SDavid du Colombier static void
atarsp(Block * b)210721abd8f2SDavid du Colombier atarsp(Block *b)
210821abd8f2SDavid du Colombier {
210921abd8f2SDavid du Colombier 	unsigned n;
211021abd8f2SDavid du Colombier 	short major;
211121abd8f2SDavid du Colombier 	Aoeata *ahin, *ahout;
211221abd8f2SDavid du Colombier 	Aoedev *d;
211321abd8f2SDavid du Colombier 	Frame *f;
211421abd8f2SDavid du Colombier 	Srb *srb;
211521abd8f2SDavid du Colombier 
211621abd8f2SDavid du Colombier 	ahin = (Aoeata*)b->rp;
211721abd8f2SDavid du Colombier 	major = nhgets(ahin->major);
211821abd8f2SDavid du Colombier 	d = mm2dev(major, ahin->minor);
2119c675ee80SDavid du Colombier 	if(d == nil)
2120c675ee80SDavid du Colombier 		return;
212121abd8f2SDavid du Colombier 	qlock(d);
212221abd8f2SDavid du Colombier 	if(waserror()){
212321abd8f2SDavid du Colombier 		qunlock(d);
212421abd8f2SDavid du Colombier 		nexterror();
212521abd8f2SDavid du Colombier 	}
212621abd8f2SDavid du Colombier 	n = nhgetl(ahin->tag);
212721abd8f2SDavid du Colombier 	f = getframe(d, n);
212821abd8f2SDavid du Colombier 	if(f == nil){
212921abd8f2SDavid du Colombier 		dprint("%æ: unexpected response; tag %ux\n", d, n);
213021abd8f2SDavid du Colombier 		goto bail;
213121abd8f2SDavid du Colombier 	}
213221abd8f2SDavid du Colombier 	rtupdate(f->dl, tsince(f->tag));
213321abd8f2SDavid du Colombier 	ahout = (Aoeata*)f->hdr;
213421abd8f2SDavid du Colombier 	srb = f->srb;
213521abd8f2SDavid du Colombier 
213621abd8f2SDavid du Colombier 	if(ahin->cmdstat & 0xa9){
213721abd8f2SDavid du Colombier 		eventlog("%æ: ata error cmd %.2ux stat %.2ux\n",
213821abd8f2SDavid du Colombier 			d, ahout->cmdstat, ahin->cmdstat);
213921abd8f2SDavid du Colombier 		if(srb)
214021abd8f2SDavid du Colombier 			srb->error = Eio;
214121abd8f2SDavid du Colombier 	} else {
214221abd8f2SDavid du Colombier 		n = ahout->scnt * Aoesectsz;
214321abd8f2SDavid du Colombier 		switch(ahout->cmdstat){
214421abd8f2SDavid du Colombier 		case Crd:
214521abd8f2SDavid du Colombier 		case Crdext:
214641ac1ab6SDavid du Colombier 			if(BLEN(b) - AOEATASZ < n){
214721abd8f2SDavid du Colombier 				eventlog("%æ: runt read blen %ld expect %d\n",
214821abd8f2SDavid du Colombier 					d, BLEN(b), n);
214921abd8f2SDavid du Colombier 				goto bail;
215021abd8f2SDavid du Colombier 			}
215141ac1ab6SDavid du Colombier 			memmove(f->dp, (uchar *)ahin + AOEATASZ, n);
215221abd8f2SDavid du Colombier 		case Cwr:
215321abd8f2SDavid du Colombier 		case Cwrext:
215421abd8f2SDavid du Colombier 			if(n > Dbcnt)
215521abd8f2SDavid du Colombier 				f->nl->lostjumbo = 0;
215621abd8f2SDavid du Colombier 			if(f->bcnt -= n){
215721abd8f2SDavid du Colombier 				f->lba += n / Aoesectsz;
215821abd8f2SDavid du Colombier 				f->dp = (uchar*)f->dp + n;
215921abd8f2SDavid du Colombier 				resend(d, f);
216021abd8f2SDavid du Colombier 				goto bail;
216121abd8f2SDavid du Colombier 			}
216221abd8f2SDavid du Colombier 			break;
216321abd8f2SDavid du Colombier 		case Cid:
216441ac1ab6SDavid du Colombier 			if(BLEN(b) - AOEATASZ < 512){
216521abd8f2SDavid du Colombier 				eventlog("%æ: runt identify blen %ld expect %d\n",
216621abd8f2SDavid du Colombier 					d, BLEN(b), n);
216721abd8f2SDavid du Colombier 				goto bail;
216821abd8f2SDavid du Colombier 			}
216941ac1ab6SDavid du Colombier 			identify(d, (ushort*)((uchar *)ahin + AOEATASZ));
217021abd8f2SDavid du Colombier 			break;
217121abd8f2SDavid du Colombier 		default:
217221abd8f2SDavid du Colombier 			eventlog("%æ: unknown ata command %.2ux \n",
217321abd8f2SDavid du Colombier 				d, ahout->cmdstat);
217421abd8f2SDavid du Colombier 		}
217521abd8f2SDavid du Colombier 	}
217621abd8f2SDavid du Colombier 
2177*f7c114afSDavid du Colombier 	if(srb && --srb->nout == 0 && srb->len == 0){
217821abd8f2SDavid du Colombier 		wakeup(srb);
2179*f7c114afSDavid du Colombier 		srb->shared = 0;
2180*f7c114afSDavid du Colombier 	}
218121abd8f2SDavid du Colombier 	f->srb = nil;
218221abd8f2SDavid du Colombier 	f->tag = Tfree;
218321abd8f2SDavid du Colombier 	d->nout--;
218421abd8f2SDavid du Colombier 
218521abd8f2SDavid du Colombier 	work(d);
218621abd8f2SDavid du Colombier bail:
218721abd8f2SDavid du Colombier 	poperror();
218821abd8f2SDavid du Colombier 	qunlock(d);
218921abd8f2SDavid du Colombier }
219021abd8f2SDavid du Colombier 
219121abd8f2SDavid du Colombier static void
netrdaoeproc(void * v)219299c72adcSDavid du Colombier netrdaoeproc(void *v)
219321abd8f2SDavid du Colombier {
219421abd8f2SDavid du Colombier 	int idx;
219521abd8f2SDavid du Colombier 	char name[Maxpath+1], *s;
219621abd8f2SDavid du Colombier 	Aoehdr *h;
219721abd8f2SDavid du Colombier 	Block *b;
219821abd8f2SDavid du Colombier 	Netlink *nl;
219921abd8f2SDavid du Colombier 
220021abd8f2SDavid du Colombier 	nl = (Netlink*)v;
220121abd8f2SDavid du Colombier 	idx = nl - netlinks.nl;
220221abd8f2SDavid du Colombier 	netlinks.reader[idx] = 1;
220321abd8f2SDavid du Colombier 	kstrcpy(name, nl->path, Maxpath);
220421abd8f2SDavid du Colombier 
220521abd8f2SDavid du Colombier 	if(waserror()){
220621abd8f2SDavid du Colombier 		eventlog("netrdaoe exiting: %s\n", up->errstr);
220721abd8f2SDavid du Colombier 		netlinks.reader[idx] = 0;
220821abd8f2SDavid du Colombier 		wakeup(netlinks.rendez + idx);
220921abd8f2SDavid du Colombier 		pexit(up->errstr, 1);
221021abd8f2SDavid du Colombier 	}
221121abd8f2SDavid du Colombier 	if(autodiscover)
221221abd8f2SDavid du Colombier 		discover(0xffff, 0xff);
221321abd8f2SDavid du Colombier 	for (;;) {
221499c72adcSDavid du Colombier 		if(!(nl->flag & Dup)) {
221599c72adcSDavid du Colombier 			uprint("%s: netlink is down", name);
221699c72adcSDavid du Colombier 			error(up->genbuf);
221799c72adcSDavid du Colombier 		}
22181e18b896SDavid du Colombier 		if (nl->dc == nil)
22191e18b896SDavid du Colombier 			panic("netrdaoe: nl->dc == nil");
222021abd8f2SDavid du Colombier 		b = devtab[nl->dc->type]->bread(nl->dc, 1<<16, 0);
222199c72adcSDavid du Colombier 		if(b == nil) {
222299c72adcSDavid du Colombier 			uprint("%s: nil read from network", name);
222399c72adcSDavid du Colombier 			error(up->genbuf);
222499c72adcSDavid du Colombier 		}
222521abd8f2SDavid du Colombier 		h = (Aoehdr*)b->rp;
222621abd8f2SDavid du Colombier 		if(h->verflag & AFrsp)
222721abd8f2SDavid du Colombier 			if(s = aoeerror(h)){
222821abd8f2SDavid du Colombier 				eventlog("%s: %s\n", nl->path, up->errstr);
222921abd8f2SDavid du Colombier 				errrsp(b, s);
223021abd8f2SDavid du Colombier 			}else
223121abd8f2SDavid du Colombier 				switch(h->cmd){
223221abd8f2SDavid du Colombier 				case ACata:
223321abd8f2SDavid du Colombier 					atarsp(b);
223421abd8f2SDavid du Colombier 					break;
223521abd8f2SDavid du Colombier 				case ACconfig:
223621abd8f2SDavid du Colombier 					qcfgrsp(b, nl);
223721abd8f2SDavid du Colombier 					break;
223821abd8f2SDavid du Colombier 				default:
22392f205b96SDavid du Colombier 					if((h->cmd & 0xf0) == 0){
224021abd8f2SDavid du Colombier 						eventlog("%s: unknown cmd %d\n",
224121abd8f2SDavid du Colombier 							nl->path, h->cmd);
224221abd8f2SDavid du Colombier 						errrsp(b, "unknown command");
224321abd8f2SDavid du Colombier 					}
22442f205b96SDavid du Colombier 					break;
22452f205b96SDavid du Colombier 				}
224621abd8f2SDavid du Colombier 		freeb(b);
224721abd8f2SDavid du Colombier 	}
224821abd8f2SDavid du Colombier }
224921abd8f2SDavid du Colombier 
225021abd8f2SDavid du Colombier static void
getaddr(char * path,uchar * ea)225121abd8f2SDavid du Colombier getaddr(char *path, uchar *ea)
225221abd8f2SDavid du Colombier {
225321abd8f2SDavid du Colombier 	int n;
225421abd8f2SDavid du Colombier 	char buf[2*Eaddrlen+1];
225521abd8f2SDavid du Colombier 	Chan *c;
225621abd8f2SDavid du Colombier 
225721abd8f2SDavid du Colombier 	uprint("%s/addr", path);
225821abd8f2SDavid du Colombier 	c = namec(up->genbuf, Aopen, OREAD, 0);
225921abd8f2SDavid du Colombier 	if(waserror()) {
226021abd8f2SDavid du Colombier 		cclose(c);
226121abd8f2SDavid du Colombier 		nexterror();
226221abd8f2SDavid du Colombier 	}
22631e18b896SDavid du Colombier 	if (c == nil)
22641e18b896SDavid du Colombier 		panic("æ: getaddr: c == nil");
226521abd8f2SDavid du Colombier 	n = devtab[c->type]->read(c, buf, sizeof buf-1, 0);
226621abd8f2SDavid du Colombier 	poperror();
226721abd8f2SDavid du Colombier 	cclose(c);
226821abd8f2SDavid du Colombier 	buf[n] = 0;
226921abd8f2SDavid du Colombier 	if(parseether(ea, buf) < 0)
227021abd8f2SDavid du Colombier 		error("parseether failure");
227121abd8f2SDavid du Colombier }
227221abd8f2SDavid du Colombier 
227321abd8f2SDavid du Colombier static void
netbind(char * path)227421abd8f2SDavid du Colombier netbind(char *path)
227521abd8f2SDavid du Colombier {
227621abd8f2SDavid du Colombier 	char addr[Maxpath];
227721abd8f2SDavid du Colombier 	uchar ea[2*Eaddrlen+1];
227821abd8f2SDavid du Colombier 	Chan *dc, *cc, *mtu;
227921abd8f2SDavid du Colombier 	Netlink *nl;
228021abd8f2SDavid du Colombier 
2281567483c8SDavid du Colombier 	snprint(addr, sizeof addr, "%s!%#x", path, Aoetype);
228221abd8f2SDavid du Colombier 	dc = chandial(addr, nil, nil, &cc);
228321abd8f2SDavid du Colombier 	snprint(addr, sizeof addr, "%s/mtu", path);
22841e18b896SDavid du Colombier 	if(waserror())
22851e18b896SDavid du Colombier 		mtu = nil;
22861e18b896SDavid du Colombier 	else {
228721abd8f2SDavid du Colombier 		mtu = namec(addr, Aopen, OREAD, 0);
22881e18b896SDavid du Colombier 		poperror();
22891e18b896SDavid du Colombier 	}
229021abd8f2SDavid du Colombier 
229121abd8f2SDavid du Colombier 	if(waserror()){
229221abd8f2SDavid du Colombier 		cclose(dc);
229321abd8f2SDavid du Colombier 		cclose(cc);
229421abd8f2SDavid du Colombier 		if(mtu)
229521abd8f2SDavid du Colombier 			cclose(mtu);
229621abd8f2SDavid du Colombier 		nexterror();
229721abd8f2SDavid du Colombier 	}
229821abd8f2SDavid du Colombier 	if(dc == nil  || cc == nil)
229921abd8f2SDavid du Colombier 		error(Enonexist);
230021abd8f2SDavid du Colombier 	getaddr(path, ea);
230121abd8f2SDavid du Colombier 	nl = addnet(path, cc, dc, mtu, ea);
230221abd8f2SDavid du Colombier 	snprint(addr, sizeof addr, "netrdaoe@%s", path);
230399c72adcSDavid du Colombier 	kproc(addr, netrdaoeproc, nl);
230421abd8f2SDavid du Colombier 	poperror();
230521abd8f2SDavid du Colombier }
230621abd8f2SDavid du Colombier 
230721abd8f2SDavid du Colombier static int
unbound(void * v)230821abd8f2SDavid du Colombier unbound(void *v)
230921abd8f2SDavid du Colombier {
231021abd8f2SDavid du Colombier 	return *(int*)v != 0;
231121abd8f2SDavid du Colombier }
231221abd8f2SDavid du Colombier 
231321abd8f2SDavid du Colombier static void
netunbind(char * path)231421abd8f2SDavid du Colombier netunbind(char *path)
231521abd8f2SDavid du Colombier {
231621abd8f2SDavid du Colombier 	int i, idx;
231721abd8f2SDavid du Colombier 	Aoedev *d, *p, *next;
231821abd8f2SDavid du Colombier 	Chan *dc, *cc;
231921abd8f2SDavid du Colombier 	Devlink *l;
232021abd8f2SDavid du Colombier 	Frame *f;
232121abd8f2SDavid du Colombier 	Netlink *n, *e;
232221abd8f2SDavid du Colombier 
232321abd8f2SDavid du Colombier 	n = netlinks.nl;
232421abd8f2SDavid du Colombier 	e = n + nelem(netlinks.nl);
232521abd8f2SDavid du Colombier 
232621abd8f2SDavid du Colombier 	lock(&netlinks);
232721abd8f2SDavid du Colombier 	for(; n < e; n++)
232821abd8f2SDavid du Colombier 		if(n->dc && strcmp(n->path, path) == 0)
232921abd8f2SDavid du Colombier 			break;
233021abd8f2SDavid du Colombier 	unlock(&netlinks);
233121abd8f2SDavid du Colombier 	if (n >= e)
233221abd8f2SDavid du Colombier 		error("device not bound");
233321abd8f2SDavid du Colombier 
233421abd8f2SDavid du Colombier 	/*
233521abd8f2SDavid du Colombier 	 * hunt down devices using this interface; disable
233621abd8f2SDavid du Colombier 	 * this also terminates the reader.
233721abd8f2SDavid du Colombier 	 */
233821abd8f2SDavid du Colombier 	idx = n - netlinks.nl;
233921abd8f2SDavid du Colombier 	wlock(&devs);
234021abd8f2SDavid du Colombier 	for(d = devs.d; d; d = d->next){
234121abd8f2SDavid du Colombier 		qlock(d);
234221abd8f2SDavid du Colombier 		for(i = 0; i < d->ndl; i++){
234321abd8f2SDavid du Colombier 			l = d->dl + i;
234421abd8f2SDavid du Colombier 			if(l->nl == n)
234521abd8f2SDavid du Colombier 				l->flag &= ~Dup;
234621abd8f2SDavid du Colombier 		}
234721abd8f2SDavid du Colombier 		qunlock(d);
234821abd8f2SDavid du Colombier 	}
234921abd8f2SDavid du Colombier 	n->flag &= ~Dup;
235021abd8f2SDavid du Colombier 	wunlock(&devs);
235121abd8f2SDavid du Colombier 
235221abd8f2SDavid du Colombier 	/* confirm reader is down. */
235321abd8f2SDavid du Colombier 	while(waserror())
235421abd8f2SDavid du Colombier 		;
235521abd8f2SDavid du Colombier 	sleep(netlinks.rendez + idx, unbound, netlinks.reader + idx);
235621abd8f2SDavid du Colombier 	poperror();
235721abd8f2SDavid du Colombier 
235821abd8f2SDavid du Colombier 	/* reschedule packets. */
235921abd8f2SDavid du Colombier 	wlock(&devs);
236021abd8f2SDavid du Colombier 	for(d = devs.d; d; d = d->next){
236121abd8f2SDavid du Colombier 		qlock(d);
236221abd8f2SDavid du Colombier 		for(i = 0; i < d->nframes; i++){
236321abd8f2SDavid du Colombier 			f = d->frames + i;
236421abd8f2SDavid du Colombier 			if(f->tag != Tfree && f->nl == n)
236521abd8f2SDavid du Colombier 				resend(d, f);
236621abd8f2SDavid du Colombier 		}
236721abd8f2SDavid du Colombier 		qunlock(d);
236821abd8f2SDavid du Colombier 	}
236921abd8f2SDavid du Colombier 	wunlock(&devs);
237021abd8f2SDavid du Colombier 
237121abd8f2SDavid du Colombier 	/* squeeze devlink pool.  (we assert nobody is using them now) */
237221abd8f2SDavid du Colombier 	wlock(&devs);
237321abd8f2SDavid du Colombier 	for(d = devs.d; d; d = d->next){
237421abd8f2SDavid du Colombier 		qlock(d);
237521abd8f2SDavid du Colombier 		for(i = 0; i < d->ndl; i++){
237621abd8f2SDavid du Colombier 			l = d->dl + i;
237721abd8f2SDavid du Colombier 			if(l->nl == n)
237821abd8f2SDavid du Colombier 				memmove(l, l + 1, sizeof *l * (--d->ndl - i));
237921abd8f2SDavid du Colombier 		}
238021abd8f2SDavid du Colombier 		qunlock(d);
238121abd8f2SDavid du Colombier 	}
238221abd8f2SDavid du Colombier 	wunlock(&devs);
238321abd8f2SDavid du Colombier 
238421abd8f2SDavid du Colombier 	/* close device link. */
238521abd8f2SDavid du Colombier 	lock(&netlinks);
238621abd8f2SDavid du Colombier 	dc = n->dc;
238721abd8f2SDavid du Colombier 	cc = n->cc;
238821abd8f2SDavid du Colombier 	if(n->mtu)
238921abd8f2SDavid du Colombier 		cclose(n->mtu);
239021abd8f2SDavid du Colombier 	memset(n, 0, sizeof *n);
239121abd8f2SDavid du Colombier 	unlock(&netlinks);
239221abd8f2SDavid du Colombier 
239321abd8f2SDavid du Colombier 	cclose(dc);
239421abd8f2SDavid du Colombier 	cclose(cc);
239521abd8f2SDavid du Colombier 
239621abd8f2SDavid du Colombier 	/* squeeze orphan devices */
239721abd8f2SDavid du Colombier 	wlock(&devs);
23982f205b96SDavid du Colombier 	for(p = d = devs.d; d; d = next){
239921abd8f2SDavid du Colombier 		next = d->next;
24002f205b96SDavid du Colombier 		if(d->ndl > 0) {
24012f205b96SDavid du Colombier 			p = d;
240221abd8f2SDavid du Colombier 			continue;
24032f205b96SDavid du Colombier 		}
24042f205b96SDavid du Colombier 		qlock(d);
24052f205b96SDavid du Colombier 		downdev(d, "orphan");
24062f205b96SDavid du Colombier 		qunlock(d);
240721abd8f2SDavid du Colombier 		if(p != devs.d)
240821abd8f2SDavid du Colombier 			p->next = next;
24092f205b96SDavid du Colombier 		else{
241021abd8f2SDavid du Colombier 			devs.d = next;
24112f205b96SDavid du Colombier 			p = devs.d;
24122f205b96SDavid du Colombier 		}
241321abd8f2SDavid du Colombier 		free(d->frames);
241421abd8f2SDavid du Colombier 		free(d);
241521abd8f2SDavid du Colombier 		dropunit();
241621abd8f2SDavid du Colombier 	}
241721abd8f2SDavid du Colombier 	wunlock(&devs);
241821abd8f2SDavid du Colombier }
241921abd8f2SDavid du Colombier 
242021abd8f2SDavid du Colombier static void
removeaoedev(Aoedev * d)24212f205b96SDavid du Colombier removeaoedev(Aoedev *d)
242221abd8f2SDavid du Colombier {
242321abd8f2SDavid du Colombier 	int i;
24242f205b96SDavid du Colombier 	Aoedev *p;
242521abd8f2SDavid du Colombier 
242621abd8f2SDavid du Colombier 	wlock(&devs);
24272f205b96SDavid du Colombier 	p = 0;
24282f205b96SDavid du Colombier 	if(d != devs.d)
24292f205b96SDavid du Colombier 		for(p = devs.d; p; p = p->next)
24302f205b96SDavid du Colombier 			if(p->next == d)
24312f205b96SDavid du Colombier 				break;
24322f205b96SDavid du Colombier 	qlock(d);
243321abd8f2SDavid du Colombier 	d->flag &= ~Dup;
2434b65f1be6SDavid du Colombier 
2435b65f1be6SDavid du Colombier 	/*
2436b65f1be6SDavid du Colombier 	 * Changing the version number is, strictly speaking, correct,
2437b65f1be6SDavid du Colombier  	 * but doing so means that deleting a LUN that is not in use
2438b65f1be6SDavid du Colombier 	 * invalidates all other LUNs too.  If your file server has
2439b65f1be6SDavid du Colombier 	 * venti arenas or fossil file systems on 1.0, and you delete 1.1,
2440b65f1be6SDavid du Colombier 	 * since you no longer need it, 1.0 will become inaccessible to your
2441b65f1be6SDavid du Colombier 	 * file server, which will eventually panic.  Note that newdev()
2442b65f1be6SDavid du Colombier 	 * does not change the version number.
2443b65f1be6SDavid du Colombier 	 */
2444b65f1be6SDavid du Colombier 	// newvers(d);
2445b65f1be6SDavid du Colombier 
244621abd8f2SDavid du Colombier 	d->ndl = 0;
24472f205b96SDavid du Colombier 	qunlock(d);
244821abd8f2SDavid du Colombier 	for(i = 0; i < d->nframes; i++)
2449a587111cSDavid du Colombier 		frameerror(d, d->frames+i, Eaoedown);
245021abd8f2SDavid du Colombier 
24512f205b96SDavid du Colombier 	if(p)
245221abd8f2SDavid du Colombier 		p->next = d->next;
245321abd8f2SDavid du Colombier 	else
245421abd8f2SDavid du Colombier 		devs.d = d->next;
245521abd8f2SDavid du Colombier 	free(d->frames);
245621abd8f2SDavid du Colombier 	free(d);
245721abd8f2SDavid du Colombier 	dropunit();
245821abd8f2SDavid du Colombier 	wunlock(&devs);
245921abd8f2SDavid du Colombier }
246021abd8f2SDavid du Colombier 
246121abd8f2SDavid du Colombier static void
removedev(char * name)24622f205b96SDavid du Colombier removedev(char *name)
24632f205b96SDavid du Colombier {
24642f205b96SDavid du Colombier 	Aoedev *d, *p;
24652f205b96SDavid du Colombier 
24662f205b96SDavid du Colombier 	wlock(&devs);
24672f205b96SDavid du Colombier 	for(p = d = devs.d; d; p = d, d = d->next)
24682f205b96SDavid du Colombier 		if(strcmp(name, unitname(d)) == 0) {
24692f205b96SDavid du Colombier 			wunlock(&devs);
24702f205b96SDavid du Colombier 			removeaoedev(p);
24712f205b96SDavid du Colombier 			return;
24722f205b96SDavid du Colombier 		}
24732f205b96SDavid du Colombier 	wunlock(&devs);
24742f205b96SDavid du Colombier 	error("device not bound");
24752f205b96SDavid du Colombier }
24762f205b96SDavid du Colombier 
24772f205b96SDavid du Colombier static void
discoverstr(char * f)247821abd8f2SDavid du Colombier discoverstr(char *f)
247921abd8f2SDavid du Colombier {
248021abd8f2SDavid du Colombier 	ushort shelf, slot;
248121abd8f2SDavid du Colombier 	ulong sh;
248221abd8f2SDavid du Colombier 	char *s;
248321abd8f2SDavid du Colombier 
248421abd8f2SDavid du Colombier 	if(f == 0){
248521abd8f2SDavid du Colombier 		discover(0xffff, 0xff);
248621abd8f2SDavid du Colombier 		return;
248721abd8f2SDavid du Colombier 	}
248821abd8f2SDavid du Colombier 
248921abd8f2SDavid du Colombier 	shelf = sh = strtol(f, &s, 0);
249021abd8f2SDavid du Colombier 	if(s == f || sh > 0xffff)
249121abd8f2SDavid du Colombier 		error("bad shelf");
249221abd8f2SDavid du Colombier 	f = s;
249321abd8f2SDavid du Colombier 	if(*f++ == '.'){
249421abd8f2SDavid du Colombier 		slot = strtol(f, &s, 0);
249521abd8f2SDavid du Colombier 		if(s == f || slot > 0xff)
249621abd8f2SDavid du Colombier 			error("bad shelf");
249721abd8f2SDavid du Colombier 	}else
249821abd8f2SDavid du Colombier 		slot = 0xff;
249921abd8f2SDavid du Colombier 	discover(shelf, slot);
250021abd8f2SDavid du Colombier }
250121abd8f2SDavid du Colombier 
250221abd8f2SDavid du Colombier 
25032f205b96SDavid du Colombier static void
aoeremove(Chan * c)25042f205b96SDavid du Colombier aoeremove(Chan *c)
25052f205b96SDavid du Colombier {
25062f205b96SDavid du Colombier 	switch(TYPE(c->qid)){
25072f205b96SDavid du Colombier 	default:
25082f205b96SDavid du Colombier 		error(Eperm);
25092f205b96SDavid du Colombier 	case Qunitdir:
25102f205b96SDavid du Colombier 		removeaoedev(unit2dev(UNIT(c->qid)));
25112f205b96SDavid du Colombier 		break;
25122f205b96SDavid du Colombier 	}
25132f205b96SDavid du Colombier }
25142f205b96SDavid du Colombier 
251521abd8f2SDavid du Colombier static long
topctlwrite(void * db,long n)251621abd8f2SDavid du Colombier topctlwrite(void *db, long n)
251721abd8f2SDavid du Colombier {
251821abd8f2SDavid du Colombier 	enum {
251921abd8f2SDavid du Colombier 		Autodiscover,
252021abd8f2SDavid du Colombier 		Bind,
252121abd8f2SDavid du Colombier 		Debug,
252221abd8f2SDavid du Colombier 		Discover,
252321abd8f2SDavid du Colombier 		Rediscover,
252421abd8f2SDavid du Colombier 		Remove,
252521abd8f2SDavid du Colombier 		Unbind,
252621abd8f2SDavid du Colombier 	};
252799c72adcSDavid du Colombier 	char *f;
252821abd8f2SDavid du Colombier 	Cmdbuf *cb;
252999c72adcSDavid du Colombier 	Cmdtab *ct;
253099c72adcSDavid du Colombier 	static Cmdtab cmds[] = {
253121abd8f2SDavid du Colombier 		{ Autodiscover,	"autodiscover",	0	},
253221abd8f2SDavid du Colombier 		{ Bind, 	"bind", 	2	},
253321abd8f2SDavid du Colombier 		{ Debug, 	"debug", 	0	},
253421abd8f2SDavid du Colombier 		{ Discover, 	"discover", 	0	},
253521abd8f2SDavid du Colombier 		{ Rediscover,	"rediscover",	0	},
253621abd8f2SDavid du Colombier 		{ Remove,	"remove",	2	},
253721abd8f2SDavid du Colombier 		{ Unbind,	"unbind",	2	},
253821abd8f2SDavid du Colombier 	};
253921abd8f2SDavid du Colombier 
254021abd8f2SDavid du Colombier 	cb = parsecmd(db, n);
254121abd8f2SDavid du Colombier 	if(waserror()){
254221abd8f2SDavid du Colombier 		free(cb);
254321abd8f2SDavid du Colombier 		nexterror();
254421abd8f2SDavid du Colombier 	}
254521abd8f2SDavid du Colombier 	ct = lookupcmd(cb, cmds, nelem(cmds));
254621abd8f2SDavid du Colombier 	f = cb->f[1];
254721abd8f2SDavid du Colombier 	switch(ct->index){
254821abd8f2SDavid du Colombier 	case Autodiscover:
2549060a30c6SDavid du Colombier 		autodiscover = toggle(f, autodiscover, 1);
255021abd8f2SDavid du Colombier 		break;
255121abd8f2SDavid du Colombier 	case Bind:
255221abd8f2SDavid du Colombier 		netbind(f);
255321abd8f2SDavid du Colombier 		break;
255421abd8f2SDavid du Colombier 	case Debug:
2555060a30c6SDavid du Colombier 		debug = toggle(f, debug, 1);
255621abd8f2SDavid du Colombier 		break;
255721abd8f2SDavid du Colombier 	case Discover:
255821abd8f2SDavid du Colombier 		discoverstr(f);
255921abd8f2SDavid du Colombier 		break;
256021abd8f2SDavid du Colombier 	case Rediscover:
2561060a30c6SDavid du Colombier 		rediscover = toggle(f, rediscover, 1);
256221abd8f2SDavid du Colombier 		break;
256321abd8f2SDavid du Colombier 	case Remove:
256421abd8f2SDavid du Colombier 		removedev(f);
256521abd8f2SDavid du Colombier 		break;
256621abd8f2SDavid du Colombier 	case Unbind:
256721abd8f2SDavid du Colombier 		netunbind(f);
256821abd8f2SDavid du Colombier 		break;
256921abd8f2SDavid du Colombier 	default:
257021abd8f2SDavid du Colombier 		cmderror(cb, "unknown aoe control message");
257121abd8f2SDavid du Colombier 	}
257221abd8f2SDavid du Colombier 	poperror();
257321abd8f2SDavid du Colombier 	free(cb);
257421abd8f2SDavid du Colombier 	return n;
257521abd8f2SDavid du Colombier }
257621abd8f2SDavid du Colombier 
257721abd8f2SDavid du Colombier static long
aoewrite(Chan * c,void * db,long n,vlong off)257821abd8f2SDavid du Colombier aoewrite(Chan *c, void *db, long n, vlong off)
257921abd8f2SDavid du Colombier {
258021abd8f2SDavid du Colombier 	switch(TYPE(c->qid)){
258121abd8f2SDavid du Colombier 	default:
258221abd8f2SDavid du Colombier 	case Qzero:
258321abd8f2SDavid du Colombier 	case Qtopdir:
258421abd8f2SDavid du Colombier 	case Qunitdir:
258521abd8f2SDavid du Colombier 	case Qtoplog:
258621abd8f2SDavid du Colombier 		error(Eperm);
258721abd8f2SDavid du Colombier 	case Qtopctl:
258821abd8f2SDavid du Colombier 		return topctlwrite(db, n);
258921abd8f2SDavid du Colombier 	case Qctl:
259021abd8f2SDavid du Colombier 	case Qdata:
259121abd8f2SDavid du Colombier 	case Qconfig:
259221abd8f2SDavid du Colombier 	case Qident:
259321abd8f2SDavid du Colombier 		return unitwrite(c, db, n, off);
259421abd8f2SDavid du Colombier 	}
259521abd8f2SDavid du Colombier }
259621abd8f2SDavid du Colombier 
259721abd8f2SDavid du Colombier Dev aoedevtab = {
259821abd8f2SDavid du Colombier 	L'æ',
259921abd8f2SDavid du Colombier 	"aoe",
260021abd8f2SDavid du Colombier 
260121abd8f2SDavid du Colombier 	devreset,
260221abd8f2SDavid du Colombier 	devinit,
260321abd8f2SDavid du Colombier 	devshutdown,
260421abd8f2SDavid du Colombier 	aoeattach,
260521abd8f2SDavid du Colombier 	aoewalk,
260621abd8f2SDavid du Colombier 	aoestat,
260721abd8f2SDavid du Colombier 	aoeopen,
260821abd8f2SDavid du Colombier 	devcreate,
260921abd8f2SDavid du Colombier 	aoeclose,
261021abd8f2SDavid du Colombier 	aoeread,
261121abd8f2SDavid du Colombier 	devbread,
261221abd8f2SDavid du Colombier 	aoewrite,
261321abd8f2SDavid du Colombier 	devbwrite,
26142f205b96SDavid du Colombier 	aoeremove,
261521abd8f2SDavid du Colombier 	devwstat,
261621abd8f2SDavid du Colombier 	devpower,
261721abd8f2SDavid du Colombier 	devconfig,
261821abd8f2SDavid du Colombier };
2619