git: 9front

Download patch

ref: 90c56ca4f206ebb0c0b8469834f5f94688e3391f
parent: ce4807990098c074050d28f63a1e0915dc894bbe
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Mon Dec 24 13:07:18 EST 2012

adiahci: drive onlining, task file error (atapi) handling, missed interrupts, bios handoff, idle, cleanup

wait for the drives to become ready or missing in iaonline()
and iaverify() to prevent nobootprompt= race.

handle task file error status (this can happen for atapi)
under some circumstances and would hang the io if not
handled.

preventively poll interrupts from the checkdrive kproc in
case we loose interrupts (bad via machine).

implement bios handoff procedure.

make sure the port is idle before programming the port dma
regios in configdrive(), do not start command processing
on the port unless phylink has been established.

--- a/sys/src/9/pc/ahci.h
+++ b/sys/src/9/pc/ahci.h
@@ -57,6 +57,12 @@
 	Boh	= 1<<0,	/* bios/os handoff supported */
 };
 
+/* bios bits */
+enum {
+	Bos	= 1<<0,
+	Oos	= 1<<1,
+};
+
 /* emctl bits */
 enum {
 	Pm	= 1<<27,	/* port multiplier support */
@@ -108,7 +114,7 @@
 
 	IEM	= Acpds|Atfes|Ahbds|Ahbfs|Ahbds|Aifs|Ainfs|Aprcs|Apcs|Adps|
 			Aufs|Asdbs|Adss|Adhrs,
-	Ifatal	= Atfes|Ahbfs|Ahbds|Aifs,
+	Ifatal	= Ahbfs|Ahbds|Aifs,
 };
 
 /* serror bits */
--- a/sys/src/9/pc/sdiahci.c
+++ b/sys/src/9/pc/sdiahci.c
@@ -16,10 +16,10 @@
 #include "../port/led.h"
 
 #pragma	varargck	type	"T"	int
-#define	dprint(...)	if(debug)	print(__VA_ARGS__); else USED(debug)
-#define	idprint(...)	if(prid)		print(__VA_ARGS__); else USED(prid)
+#define	dprint(...)	if(debug)	iprint(__VA_ARGS__); else USED(debug)
+#define	idprint(...)	if(prid)	print(__VA_ARGS__); else USED(prid)
 #define	aprint(...)	if(datapi)	print(__VA_ARGS__); else USED(datapi)
-#define	ledprint(...)	if(dled)		print(__VA_ARGS__); else USED(dled)
+#define	ledprint(...)	if(dled)	print(__VA_ARGS__); else USED(dled)
 #define	Pciwaddrh(a)	0
 #define Tname(c)	tname[(c)->type]
 #define	Ticks		MACHP(0)->ticks
@@ -164,6 +164,8 @@
 	Drive	rawdrive[NCtlrdrv];
 	Drive*	drive[NCtlrdrv];
 	int	ndrive;
+
+	uint	missirq;
 };
 
 static	Ctlr	iactlr[NCtlr];
@@ -343,7 +345,7 @@
 static void
 asleep(int ms)
 {
-	if(up == nil)
+	if(up == nil || !islo())
 		delay(ms);
 	else
 		esleep(ms);
@@ -363,6 +365,8 @@
 			break;
 		asleep(25);
 	}
+	if((*cmd & Apwr) != Apwr)
+		*cmd |= Apwr;
 	p->sctl = 3*Aipm | 0*Aspd | Adet;
 	delay(1);
 	p->sctl = 3*Aipm | mode*Aspd;
@@ -379,7 +383,7 @@
 	mkalist(pc->m, Lwrite, 0, 0);
 
 	if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
-		dprint("ahciflushcache fail %lux\n", pc->p->task);
+		dprint("ahciflushcache fail [task %lux]\n", pc->p->task);
 //		preg(pc->m->fis.r, 20);
 		return -1;
 	}
@@ -456,7 +460,7 @@
 	return -1;
 stop1:
 	/* extra check */
-	dprint("ahci: clo clear %lux\n", a->task);
+	dprint("ahci: clo clear [task %lux]\n", a->task);
 	if(a->task & ASbsy)
 		return -1;
 	*p |= Afre | Ast;
@@ -577,16 +581,18 @@
 ahciwakeup(Aportc *c, uint mode)
 {
 	ushort s;
+	Aport *p;
 
-	s = c->p->sstatus;
-	if((s & Isleepy) == 0)
+	p = c->p;
+	s = p->sstatus;
+	if((s & Isleepy) != 0)
 		return;
 	if((s & Smask) != Spresent){
-		print("ahci: slumbering drive missing %.3ux\n", s);
+		dprint("ahci: slumbering drive missing [ss %.3ux]\n", s);
 		return;
 	}
 	ahciportreset(c, mode);
-//	iprint("ahci: wake %.3ux -> %.3lux\n", s, c->p->sstatus);
+	dprint("ahci: wake %.3ux -> %.3lux\n", s, c->p->sstatus);
 }
 
 static int
@@ -594,6 +600,7 @@
 {
 	Aportm *m;
 	Aport *p;
+	int i;
 
 	p = c->p;
 	m = c->m;
@@ -604,12 +611,16 @@
 		m->ctab = malign(sizeof *m->ctab, 128);
 	}
 
+	if(ahciidle(p) == -1){
+		dprint("ahci: port not idle\n");
+		return -1;
+	}
+
 	p->list = PCIWADDR(m->list);
 	p->listhi = Pciwaddrh(m->list);
 	p->fis = PCIWADDR(m->fis.base);
 	p->fishi = Pciwaddrh(m->fis.base);
 
-	dprint("ahci: configdrive cmd=%lux sstatus=%lux\n", p->cmd, p->sstatus);
 	p->cmd |= Afre;
 
 	if((p->cmd & Apwr) != Apwr)
@@ -617,7 +628,7 @@
 
 	if((h->cap & Hss) != 0){
 		dprint("ahci: spin up ... [%.3lux]\n", p->sstatus);
-		for(int i = 0; i < 1400; i += 50){
+		for(i = 0; i < 1400; i += 50){
 			if((p->sstatus & Sbist) != 0)
 				break;
 			if((p->sstatus & Sphylink) == Sphylink)
@@ -625,17 +636,22 @@
 			asleep(50);
 		}
 	}
-	p->serror = SerrAll;
 
 	if((p->sstatus & SSmask) == (Isleepy | Spresent))
 		ahciwakeup(c, mode);
 
+	p->serror = SerrAll;
+	p->ie = IEM;
+
+	/* we will get called again once phylink has been established */
+	if((p->sstatus & Sphylink) != Sphylink)
+		return 0;
+
 	/* disable power managment sequence from book. */
 	p->sctl = 3*Aipm | mode*Aspd | 0*Adet;
 	p->cmd &= ~Aalpe;
 
-	p->cmd |= Ast;
-	p->ie = IEM;
+	p->cmd |= Afre | Ast;
 
 	return 0;
 }
@@ -678,15 +694,27 @@
 	if((u & Ham) == 0)
 		h->ghc |= Hae;
 
-//	print("#S/sd%c: ahci %s port %#p: sss %d ncs %d coal %d "
-//		"mport %d led %d clo %d ems %d\n",
-//		c->sdev->idno, Tname(c), h,
-//		(u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1, u & 0x1f, (u>>25) & 1,
-//		(u>>24) & 1, (u>>6) & 1);
 	return countbits(h->pi);
 }
 
 static int
+ahcihandoff(Ahba *h)
+{
+	int wait;
+
+	if((h->cap2 & Boh) == 0)
+		return 0;
+	h->bios |= Oos;
+	for(wait = 0; wait < 2000; wait += 100){
+		if((h->bios & Bos) == 0)
+			return 0;
+		delay(100);
+	}
+	iprint("ahci: bios handoff timed out\n");
+	return -1;
+}
+
+static int
 ahcihbareset(Ahba *h)
 {
 	int wait;
@@ -721,10 +749,8 @@
 
 	id = d->info;
 	s = ahciidentify(&d->portc, id, &d->secsize, dnam(d));
-	if(s == -1){
-		d->state = Derror;
+	if(s == -1)
 		return -1;
-	}
 	osectors = d->sectors;
 	memmove(oserial, d->serial, sizeof d->serial);
 
@@ -790,8 +816,13 @@
 	if(p->ci == 0){
 		f |= Fdone;
 		pr = 0;
-	}else if(cause & Adps)
+	}else if(cause & Adps){
 		pr = 0;
+	}else if(cause & Atfes){
+		f |= Ferror;
+		ewake = 1;
+		pr = 0;
+	}
 	if(cause & Ifatal){
 		ewake = 1;
 		dprint("%s: fatal\n", dnam(d));
@@ -834,10 +865,10 @@
 			d->state = Doffline;
 			break;
 		}
-		dprint("%s: %s → %s [Apcrs] %.3lux\n", dnam(d), diskstates[s0],
-			diskstates[d->state], p->sstatus);
+		dprint("%s: updatedrive: %s → %s [ss %.3lux]\n",
+			dnam(d), diskstates[s0], diskstates[d->state], p->sstatus);
 		if(s0 == Dready && d->state != Dready)
-			idprint("%s: pulled\n", dnam(d));
+			dprint("%s: pulled\n", dnam(d));
 		if(d->state != Dready)
 			f |= Ferror;
 		if(d->state != Dready || p->ci)
@@ -854,15 +885,23 @@
 }
 
 static void
-pstatus(Drive *d, ulong s)
+dstatus(Drive *d, int s)
 {
-	/*
-	 * bogus code because the first interrupt is currently dropped.
-	 * likely my fault.  serror is maybe cleared at the wrong time.
-	 */
-	switch(s){
-	default:
-		print("%s: pstatus: bad status %.3lux\n", dnam(d), s);
+	ilock(d);
+	d->state = s;
+	iunlock(d);
+}
+
+static void
+configdrive(Drive *d)
+{
+	if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1){
+		dstatus(d, Dportreset);
+		return;
+	}
+
+	ilock(d);
+	switch(d->port->sstatus & Smask){
 	case Smissing:
 		d->state = Dmissing;
 		break;
@@ -869,6 +908,8 @@
 	case Spresent:
 		break;
 	case Sphylink:
+		if(d->state == Dready)
+			break;
 		d->wait = 0;
 		d->state = Dnew;
 		break;
@@ -876,17 +917,7 @@
 		d->state = Doffline;
 		break;
 	}
-}
-
-static int
-configdrive(Drive *d)
-{
-	if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1)
-		return -1;
-	ilock(d);
-	pstatus(d, d->port->sstatus & Smask);
 	iunlock(d);
-	return 0;
 }
 
 static void
@@ -899,36 +930,27 @@
 	det = p->sctl & 7;
 	stat = p->sstatus & Smask;
 	state = (p->cmd>>28) & 0xf;
-	dprint("%s: resetdisk: icc %ux  det %.3ux sdet %.3ux\n", dnam(d), state, det, stat);
+	dprint("%s: resetdisk [icc %ux; det %.3ux; sdet %.3ux]\n", dnam(d), state, det, stat);
 
 	ilock(d);
-	state = d->state;
-	if(d->state != Dready || d->state != Dnew)
+	if(d->state != Dready && d->state != Dnew)
 		d->portm.flag |= Ferror;
+	if(stat != Sphylink)
+		d->state = Dportreset;
+	else
+		d->state = Dreset;
 	clearci(p);			/* satisfy sleep condition. */
 	wakeup(&d->portm);
-	d->state = Derror;
 	iunlock(d);
 
-	if(stat != Sphylink){
-		ilock(d);
-		d->state = Dportreset;
-		iunlock(d);
+	if(stat != Sphylink)
 		return;
-	}
 
 	qlock(&d->portm);
-	if(p->cmd&Ast && ahciswreset(&d->portc) == -1){
-		ilock(d);
-		d->state = Dportreset;	/* get a bigger stick. */
-		iunlock(d);
-	}else{
-		ilock(d);
-		d->state = Dmissing;
-		iunlock(d);
+	if(p->cmd&Ast && ahciswreset(&d->portc) == -1)
+		dstatus(d, Dportreset);	/* get a bigger stick. */
+	else
 		configdrive(d);
-	}
-	dprint("%s: resetdisk: %s → %s\n", dnam(d), diskstates[state], diskstates[d->state]);
 	qunlock(&d->portm);
 }
 
@@ -953,15 +975,12 @@
 		goto lose;
 	}
 	if(m->feat & Dpower && setfeatures(c, 0x85, 3*1000) == -1){
+		dprint("%s: can't disable apm\n", dnam(d));
 		m->feat &= ~Dpower;
 		if(ahcirecover(c) == -1)
 			goto lose;
 	}
-
-	ilock(d);
-	d->state = Dready;
-	iunlock(d);
-
+	dstatus(d, Dready);
 	qunlock(c->m);
 
 	s = "";
@@ -974,9 +993,7 @@
 
 lose:
 	idprint("%s: can't be initialized\n", dnam(d));
-	ilock(d);
-	d->state = Dnull;
-	iunlock(d);
+	dstatus(d, Dnull);
 	qunlock(c->m);
 	return -1;
 }
@@ -993,8 +1010,8 @@
 {
 	if((d->portm.feat & Datapi) == 0 && d->active &&
 	    d->totick != 0 && (long)(Ticks - d->totick) > 0){
-		dprint("%s: drive hung; resetting [%lux] ci %lux\n",
-			dnam(d), d->port->task, d->port->ci);
+		dprint("%s: drive hung [task %lux; ci %lux; serr %lux]\n",
+			dnam(d), d->port->task, d->port->ci, d->port->serror);
 		d->state = Dreset;
 	}
 }
@@ -1001,21 +1018,15 @@
 
 static ushort olds[NCtlr*NCtlrdrv];
 
-static int
+static void
 doportreset(Drive *d)
 {
-	int i;
-
-	i = -1;
 	qlock(&d->portm);
-	if(ahciportreset(&d->portc, d->mode) == -1)
-		dprint("ahci: ahciportreset fails\n");
-	else
-		i = 0;
+	ahciportreset(&d->portc, d->mode);
 	qunlock(&d->portm);
-	dprint("ahci: portreset → %s  [task %.4lux ss %.3lux]\n",
+
+	dprint("ahci: portreset: %s [task %lux; ss %.3lux]\n",
 		diskstates[d->state], d->port->task, d->port->sstatus);
-	return i;
 }
 
 /* drive must be locked */
@@ -1041,11 +1052,18 @@
 	return (c->hba->cap & 0xf*Hiss)/Hiss;
 }
 
+static void iainterrupt(Ureg*, void *);
+
 static void
 checkdrive(Drive *d, int i)
 {
 	ushort s, sig;
 
+	if(d->ctlr->enabled == 0)
+		return;
+	if(d->driveno == 0)
+		iainterrupt(0, d->ctlr);	/* check for missed irq's */
+
 	ilock(d);
 	s = d->port->sstatus;
 	if(s)
@@ -1093,8 +1111,8 @@
 			if(d->unit == nil)
 				break;
 			if((++d->wait&Midwait) == 0){
-				dprint("%s: slow reset %.3ux task=%lux; %d\n",
-					dnam(d), s, d->port->task, d->wait);
+				dprint("%s: slow reset [task %lux; ss %.3ux; wait %d]\n",
+					dnam(d), d->port->task, s, d->wait);
 				goto reset;
 			}
 			s = (uchar)d->port->task;
@@ -1129,7 +1147,7 @@
 		d->portm.flag |= Ferror;
 		clearci(d->port);
 		wakeup(&d->portm);
-		if((s & Smask) == 0){
+		if((s & Smask) == Smissing){
 			d->state = Dmissing;
 			break;
 		}
@@ -1155,7 +1173,7 @@
 }
 
 static void
-iainterrupt(Ureg*, void *a)
+iainterrupt(Ureg *u, void *a)
 {
 	int i;
 	ulong cause, m;
@@ -1177,6 +1195,8 @@
 		c->hba->isr = m;
 		iunlock(d);
 	}
+	if(u == 0 && i > 0)
+		c->missirq++;
 	iunlock(c);
 }
 
@@ -1404,6 +1424,29 @@
 }
 
 static int
+waitready(Drive *d)
+{
+	ulong s, i, δ;
+
+	for(i = 0; i < 15000; i += 250){
+		if(d->state == Dreset || d->state == Dportreset || d->state == Dnew)
+			return 1;
+		δ = Ticks - d->lastseen;
+		if(d->state == Dnull || δ > 10*1000)
+			return -1;
+		s = d->port->sstatus;
+		if((s & Imask) == 0 && δ > 1500)
+			return -1;
+		if(d->state == Dready && (s & Smask) == Sphylink)
+			return 0;
+		esleep(250);
+	}
+	print("%s: not responding; offline\n", dnam(d));
+	dstatus(d, Doffline);
+	return -1;
+}
+
+static int
 iaverify(SDunit *u)
 {
 	Ctlr *c;
@@ -1420,12 +1463,53 @@
 	}
 	iunlock(d);
 	iunlock(c);
+
 	checkdrive(d, d->driveno);		/* c->d0 + d->driveno */
-	scsiverify(u);
+
+	while(waitready(d) == 1)
+		esleep(1);
+
+	if(d->portm.feat & Datapi)
+		scsiverify(u);
+
 	return 1;
 }
 
 static int
+iaonline(SDunit *u)
+{
+	int r;
+	Ctlr *c;
+	Drive *d;
+
+	c = u->dev->ctlr;
+	d = c->drive[u->subno];
+
+	while(waitready(d) == 1)
+		esleep(1);
+
+	ilock(d);
+	if(d->portm.feat & Datapi){
+		d->drivechange = 0;
+		iunlock(d);
+		return scsionline(u);
+	}
+	r = 0;
+	if(d->drivechange){
+		d->drivechange = 0;
+		r = 2;
+	}else if(d->state == Dready)
+		r = 1;
+	if(r){
+		u->sectors = d->sectors;
+		u->secsize = d->secsize;
+	}
+	iunlock(d);
+
+	return r;
+}
+
+static int
 iaenable(SDev *s)
 {
 	char name[32];
@@ -1468,35 +1552,6 @@
 	return 1;
 }
 
-static int
-iaonline(SDunit *u)
-{
-	int r;
-	Ctlr *c;
-	Drive *d;
-
-	c = u->dev->ctlr;
-	d = c->drive[u->subno];
-	ilock(d);
-	if(d->portm.feat & Datapi){
-		d->drivechange = 0;
-		iunlock(d);
-		return scsionline(u);
-	}
-	r = 0;
-	if(d->drivechange){
-		d->drivechange = 0;
-		r = 2;
-	}else if(d->state == Dready)
-		r = 1;
-	if(r){
-		u->sectors = d->sectors;
-		u->secsize = d->secsize;
-	}
-	iunlock(d);
-	return r;
-}
-
 static Alist*
 ahcibuild(Aportm *m, int rw, void *data, uint n, vlong lba)
 {
@@ -1559,34 +1614,6 @@
 }
 
 static int
-waitready(Drive *d)
-{
-	ulong s, i, δ;
-
-	for(i = 0; i < 15000; i += 250){
-		if(d->state == Dreset || d->state == Dportreset ||
-		    d->state == Dnew)
-			return 1;
-		δ = Ticks - d->lastseen;
-		if(d->state == Dnull || δ > 10*1000)
-			return -1;
-		ilock(d);
-		s = d->port->sstatus;
-		iunlock(d);
-		if((s & Imask) == 0 && δ > 1500)
-			return -1;
-		if(d->state == Dready && (s & Smask) == Sphylink)
-			return 0;
-		esleep(250);
-	}
-	print("%s: not responding; offline\n", dnam(d));
-	ilock(d);
-	d->state = Doffline;
-	iunlock(d);
-	return -1;
-}
-
-static int
 lockready(Drive *d)
 {
 	int i;
@@ -1643,13 +1670,11 @@
 		if(interrupt){
 			d->active--;
 			d->port->ci = 0;
-			if(ahcicomreset(&d->portc) == -1){
-				ilock(d);
-				d->state = Dreset;
-				iunlock(d);
-			}
+			if(ahcicomreset(&d->portc) == -1)
+				dstatus(d, Dreset);
 			return SDtimeout;
 		}
+	
 	sleep(&d->portm, ahciclear, &as);
 	poperror();
 
@@ -1737,6 +1762,8 @@
 	}
 	rw = write? SDwrite: SDread;
 	data = a;
+	dprint("%s: bio: %llud %c %lud (max %d) %p\n",
+		dnam(d), lba, "rw"[rw], count, max, data);
 	for(try = 0; try < 10;){
 		n = count;
 		if(n > max)
@@ -1803,8 +1830,7 @@
 static uchar bogusrfis[16] = {
 [Ftype]		0x34,
 [Fioport]	0x40,
-[Fstatus]		0x50,
-
+[Fstatus]	0x50,
 [Fdev]		0xa0,
 };
 
@@ -1894,8 +1920,7 @@
 		return sdr(r, d, r->status);
 	}
 	print("%s: bad disk\n", dnam(d));
-	r->status = SDeio;
-	return SDeio;
+	return r->status = SDeio;
 }
 
 /*
@@ -2116,6 +2141,7 @@
 		s->ctlr = c;
 		c->sdev = s;
 
+		ahcihandoff((Ahba*)c->mmio);
 		if(intel(c) && p->did != 0x2681)
 			iasetupahci(c);
 //		ahcihbareset((Ahba*)c->mmio);
@@ -2238,6 +2264,7 @@
 	p = seprint(p, e, "geometry %llud %lud\n", d->sectors, u->secsize);
 	p = seprint(p, e, "alignment %d %d\n", 
 		d->secsize<<d->portm.physshift, d->portm.physalign);
+	p = seprint(p, e, "missirq\t%ud\n", c->missirq);
 	return p - op;
 }
 
@@ -2266,10 +2293,7 @@
 			break;
 	if(i == nelem(diskstates))
 		error(Ebadctl);
-	ilock(d);
-	d->state = i;
-//	statechange(d);
-	iunlock(d);
+	dstatus(d, i);
 }
 
 static int
--