1. fix for drives with electricial problems on sata link -- "lastseen". 2. improved locking semantics. 3. untested fix for multiple controllers (no hardware). 4. fix for noncontigous ports, allowed by std. Reference: /n/sources/patch/applied/ahciupd Date: Wed Jan 9 00:38:39 CET 2008 Signed-off-by: quanstro@quanstro.net --- /sys/src/9/pc/sdiahci.c Wed Dec 12 15:25:32 2007 +++ /sys/src/9/pc/sdiahci.c Wed Dec 12 15:25:22 2007 @@ -15,8 +15,9 @@ #define dprint(...) if(debug) iprint(__VA_ARGS__); else USED(debug) #define idprint(...) if(prid) print(__VA_ARGS__); else USED(prid) -#define aprint(...) if(datapi) iprint(__VA_ARGS__); else USED(datapi) +#define aprint(...) if(datapi) print(__VA_ARGS__); else USED(datapi) #define Tname(c) tname[(c)->type] +#define Ticks MACHP(0)->ticks enum { NCtlr = 4, @@ -111,6 +112,7 @@ uvlong sectors; ulong intick; + ulong lastseen; int wait; uchar mode; /* DMautoneg, satai or sataii. */ uchar active; @@ -439,7 +441,7 @@ } static int -flushcache(Aportc *pc) +ahciflushcache(Aportc *pc) { uchar *c, llba; Actab *t; @@ -463,7 +465,7 @@ l->ctabhi = 0; if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){ - dprint("flushcache fail %ux\n", pc->p->task); + dprint("ahciflushcache fail %ux\n", pc->p->task); // preg( pc->m->fis.r, 20); return -1; } @@ -758,7 +760,7 @@ ushort s; s = p->sstatus; - if((s & 0xF00) != 0x600) + if((s & 0x700) != 0x600) return; if((s & 7) != 1){ /* not (device, no phy) */ iprint("ahci: slumbering drive unwakeable %ux\n", s); @@ -800,7 +802,7 @@ p->fishi = 0; p->cmd |= Afre|Ast; - if((p->sstatus & 0xF0F) == 0x601) /* drive coming up in slumbering? */ + if((p->sstatus & 0x707) == 0x601) /* drive coming up in slumbering? */ ahciwakeup(p); /* disable power managment sequence from book. */ @@ -991,7 +993,7 @@ d->state = Dmissing; break; case 1: /* device but no phy comm. */ - if((p->sstatus & 0xF00) == 0x600) + if((p->sstatus & 0x700) == 0x600) d->state = Dnew; /* slumbering */ else d->state = Derror; @@ -1085,6 +1087,7 @@ d->portm.flag |= Ferror; clearci(p); /* satisfy sleep condition. */ wakeup(&d->portm); + d->state = Derror; iunlock(d); qlock(&d->portm); @@ -1150,6 +1153,10 @@ return 0; lose: + idprint("%s: cant be initialized\n", d->unit->name); + ilock(d); + d->state = Dnull; + iunlock(d); qunlock(c->m); return -1; } @@ -1165,7 +1172,7 @@ westerndigitalhung(Drive *d) { if((d->portm.feat&Datapi) == 0 && d->active && - TK2MS(MACHP(0)->ticks-d->intick) > 5000){ + TK2MS(Ticks-d->intick) > 5000){ dprint("%s: drive hung; resetting [%ux] ci=%x\n", d->unit->name, d->port->task, d->port->ci); d->state = Dreset; @@ -1200,6 +1207,8 @@ ilock(d); name = d->unit->name; s = d->port->sstatus; + if(s) + d->lastseen = Ticks; if(s != olds[i]){ dprint("%s: status: %04ux -> %04ux: %s\n", name, olds[i], s, diskstates[d->state]); @@ -1339,6 +1348,7 @@ d->unit = u; iunlock(d); iunlock(c); + checkdrive(d, d->driveno); return 1; } @@ -1531,21 +1541,22 @@ static int waitready(Drive *d) { - u32int s, t, i; + u32int s, i, δ; - for(i = 0; i < 120; 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; - t = d->port->task; iunlock(d); - if((s & 0x100) == 0) - return -1; /* not active */ + if((s & 0x700) == 0 && δ > 1500) + return -1; /* no detect */ if(d->state == Dready && (s & 7) == 3) return 0; /* ready, present & phy. comm. */ - if((i+1) % 30 == 0) - print("%s: waitready: [%s] task=%ux sstat=%ux\n", - d->unit->name, diskstates[d->state], t, s); - esleep(1000); + esleep(250); } print("%s: not responding; offline\n", d->unit->name); ilock(d); @@ -1555,6 +1566,33 @@ } static int +lockready(Drive *d) +{ + int i; + +loop: + qlock(&d->portm); + if((i = waitready(d)) == 1){ + qunlock(&d->portm); + esleep(1); + goto loop; + } + return i; +} + +static int +flushcache(Drive *d) +{ + int i; + + i = -1; + if(lockready(d) == 0) + i = ahciflushcache(&d->portc); + qunlock(&d->portm); + return i; +} + +static int iariopkt(SDreq *r, Drive *d) { int n, count, try, max, flag, task; @@ -1577,14 +1615,22 @@ try = 0; retry: - if(waitready(d) == -1) - return SDeio; data = r->data; n = count; if(n > max) n = max; d->active++; ahcibuildpkt(&d->portm, r, data, n); + switch(waitready(d)){ + case -1: + qunlock(&d->portm); + return SDeio; + case 1: + qunlock(&d->portm); + esleep(1); + goto retry; + } + ilock(d); d->portm.flag = 0; iunlock(d); @@ -1592,7 +1638,7 @@ as.p = p; as.i = 1; - d->intick = MACHP(0)->ticks; + d->intick = Ticks; while(waserror()) ; @@ -1605,9 +1651,10 @@ iunlock(d); if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){ - d->port->ci = 0; /* @? */ + d->port->ci = 0; /* clearci? */ ahcirecover(&d->portc); task = d->port->task; + flag &= ~Fdone; /* either an error or doover */ } d->active--; qunlock(&d->portm); @@ -1617,12 +1664,12 @@ r->status = SDcheck; return SDcheck; } - iprint("%s: retry\n", name); - esleep(1000); + print("%s: retry\n", name); goto retry; } if(flag & Ferror){ - iprint("%s: i/o error %ux\n", name, task); + if((task&Eidnf) == 0) + print("%s: i/o error %ux\n", name, task); r->status = SDcheck; return SDcheck; } @@ -1657,10 +1704,7 @@ p = d->port; if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){ - qlock(&d->portm); - i = flushcache(&d->portc); - qunlock(&d->portm); - if(i == 0) + if(flushcache(d) == 0) return sdsetsense(r, SDok, 0, 0, 0); return sdsetsense(r, SDcheck, 3, 0xc, 2); } @@ -1686,8 +1730,6 @@ try = 0; retry: - if(waitready(d) == -1) - return SDeio; data = r->data; while(count > 0){ n = count; @@ -1695,6 +1737,15 @@ n = max; d->active++; ahcibuild(&d->portm, cmd, data, n, lba); + switch(waitready(d)){ + case -1: + qunlock(&d->portm); + return SDeio; + case 1: + qunlock(&d->portm); + esleep(1); + goto retry; + } ilock(d); d->portm.flag = 0; iunlock(d); @@ -1702,7 +1753,7 @@ as.p = p; as.i = 1; - d->intick = MACHP(0)->ticks; + d->intick = Ticks; while(waserror()) ; @@ -1729,7 +1780,6 @@ return SDeio; } iprint("%s: retry %lld\n", name, lba); - esleep(1000); goto retry; } if(flag & Ferror){ @@ -1800,7 +1850,7 @@ type = Tsb600; else continue; - if (p->mem[Abar].bar == 0) + if(p->mem[Abar].bar == 0) continue; if(niactlr == NCtlr){ print("%spnp: too many controllers\n", tname[type]); @@ -1857,8 +1907,8 @@ d->portc.p = d->port; d->portc.m = &d->portm; d->driveno = n++; - c->drive[i] = d; - iadrive[d->driveno] = d; + c->drive[d->driveno] = d; + iadrive[niadrive+d->driveno] = d; } for(i = 0; i < n; i++) if(ahciidle(c->drive[i]->port) == -1){ @@ -1944,11 +1994,10 @@ { long t0; - t0 = MACHP(0)->ticks; - qlock(&d->portm); - flushcache(&d->portc); - qunlock(&d->portm); - dprint("flush in %ldms\n", TK2MS(MACHP(0)->ticks-t0)); + t0 = Ticks; + if(flushcache(d) != 0) + error(Eio); + dprint("flush in %ldms\n", Ticks-t0); } static void @@ -1974,7 +2023,8 @@ d->smartrs = 0; nexterror(); } - qlock(&d->portm); + if(lockready(d) == -1) + error(Eio); d->smartrs = smart(&d->portc, i); d->portm.smart = 0; qunlock(&d->portm); @@ -2008,6 +2058,7 @@ char **f; Ctlr *c; Drive *d; + uint i; c = u->dev->ctlr; d = c->drive[u->subno]; @@ -2016,8 +2067,6 @@ if(strcmp(f[0], "flushcache") == 0) runflushcache(d); else if(strcmp(f[0], "identify") == 0){ - uint i; - i = strtoul(f[1]? f[1]: "0", 0, 0); if(i > 0xff) i = 0; @@ -2026,14 +2075,15 @@ forcemode(d, f[1]? f[1]: "satai"); else if(strcmp(f[0], "nop") == 0){ if((d->portm.feat & Dnop) == 0){ - cmderror(cmd, "nop command not supported"); + cmderror(cmd, "no drive support"); return -1; } if(waserror()){ qunlock(&d->portm); nexterror(); } - qlock(&d->portm); + if(lockready(d) == -1) + error(Eio); nop(&d->portc); qunlock(&d->portm); poperror(); @@ -2049,7 +2099,8 @@ d->smartrs = 0; nexterror(); } - qlock(&d->portm); + if(lockready(d) == -1) + error(Eio); d->portm.smart = 2 + smartrs(&d->portc); qunlock(&d->portm); poperror();