Revision: 06ed970d90
Author: Mechiel Lukkien <mec...@ueber.net>
Date: Thu Sep 2 12:09:20 2010
Log: get sata ncq closer to working....
http://code.google.com/p/inferno-kirkwood/source/detail?r=06ed970d90
Revision: 9268c21a5e
Author: Mechiel Lukkien <mec...@ueber.net>
Date: Thu Sep 2 14:12:17 2010
Log: make ncq work properly....
http://code.google.com/p/inferno-kirkwood/source/detail?r=9268c21a5e
==============================================================================
Revision: 06ed970d90
Author: Mechiel Lukkien <mec...@ueber.net>
Date: Thu Sep 2 12:09:20 2010
Log: get sata ncq closer to working.
before, the wrong Resp was read in the interrupt handler, so the
finished command tag wasn't found.
reading up to 32 sectors concurrently clearly shows the disks often
reads them in a different order.
http://code.google.com/p/inferno-kirkwood/source/detail?r=06ed970d90
Modified:
/devsata.c
/mem.h
=======================================
--- /devsata.c Wed Sep 1 14:41:58 2010
+++ /devsata.c Thu Sep 2 12:09:20 2010
@@ -4,7 +4,7 @@
for now we'll assume ncq "first party dma" read/write commands (very old
sata disks won't work).
todo:
-- figure out what to do with the tag in the responses from the response
queue. for ncq.
+- when doing ata commands, verify ata status if ok.
- read ata/atapi signature in registers after reset?
- get interrupt when device sends registers. so we can check for BSY
then, or start reading data, etc.
- detect whether packet command is accepted. try if packet commands work.
@@ -37,6 +37,7 @@
#define dprint if(satadebug)print
char Enodisk[] = "no disk";
+char Etimeout[] = "timeout";
typedef struct Req Req;
typedef struct Resp Resp;
@@ -90,9 +91,9 @@
uvlong sectors;
};
-static volatile ulong satadone;
static QLock reqsl;
static Rendez reqsr[32];
+static volatile ulong reqsdone[32];
static Req *reqs;
static Resp *resps;
static Prd *prds;
@@ -230,7 +231,7 @@
{
SatahcReg *hr = SATAHCREG;
SataReg *sr = SATA1REG;
- ulong v, w;
+ ulong v, tag;
static int count = 0;
ulong in, out;
@@ -243,11 +244,9 @@
if(v & Sata1done) {
diprint("m 1done\n");
}
- if(v & Sata1dmadone) {
- diprint("m 1dmadone\n");
- }
hr->intr = 0;
sr->edma.intre = 0;
+ intrclear(Irqlo, IRQ0sata);
diprint("reqin %#lux reqout %#lux respin %#lux respout %#lux\n",
sr->edma.reqin, sr->edma.reqout, sr->edma.respin, sr->edma.respout);
@@ -257,19 +256,15 @@
for(;;) {
if(in == out)
break;
- w = resps[in].idflags & MASK(5);
- if(w != in)
- iprint("response, in %lud, tag %lud\n", in, w);
+
/* determine which request is done, wakeup its caller. */
- satadone |= 1<<out;
- diprint("new resp out %lud (in %lud), satadone now %#lux\n", out, in,
satadone);
- /* xxx check for error */
- wakeup(&reqsr[out]);
+ tag = resps[out].idflags & MASK(5);
+ /* xxx check for error? */
+ reqsdone[tag] = 1;
+ wakeup(&reqsr[tag]);
out = (out+1)%32;
sr->edma.respout = (ulong)&resps[out];
}
-
- intrclear(Irqlo, IRQ0sata);
}
static void
@@ -642,7 +637,9 @@
{
SatahcReg *hr = SATAHCREG;
SataReg *sr = SATA1REG;
+ AtaReg *ar = ATA1REG;
int n;
+ int i;
diprint("satainit...\n");
@@ -711,7 +708,16 @@
dprint("sr->ifc.sstatus before ata identify %#lux\n", sr->ifc.sstatus);
/* xxx horrible, should properly wait during command execution, until
device no longer busy */
- tsleep(&up->sleep, return0, nil, 1000);
+ i = 0;
+ for(;;) {
+ if((ar->status & (Absy|Adrq)) == 0)
+ break;
+ if(++i == 20) {
+ dprint("disk not ready\n");
+ return;
+ }
+ tsleep(&up->sleep, return0, nil, 100);
+ }
if(identify() < 0) {
dprint("no disk\n");
return;
@@ -926,7 +932,7 @@
isdone(void *p)
{
ulong *v = p;
- return (satadone & *v) != 0;
+ return *v;
}
static long
@@ -956,6 +962,7 @@
}
enum {
+ /* first param for io() */
Read, Write,
};
static ulong
@@ -964,13 +971,12 @@
SataReg *sr = SATA1REG;
Req *rq;
int i;
- ulong v;
ulong ns;
ulong dev;
ulong nslo, nshi;
uvlong lba;
ulong lbalo, lbahi;
- ulong cmds[] = {0x60, 0x61};
+ ulong cmds[] = {0x60, 0x61}; /* xxx this is for sata fpdma only, ncq */
Prd *prd;
if(disk.valid == 0)
@@ -1031,12 +1037,9 @@
dcwbinv(rq, sizeof rq[0]);
diprint("io, using slot %d, off %llud\n", i, off);
- v = 1<<i;
- satadone &= ~v;
+ reqsdone[i] = 0;
sr->edma.reqin = (ulong)&reqs[reqnext];
if((sr->edma.cmd & EdmaEnable) == 0) {
- /* xxx verify that DET in sstatus is 3 */
-
/* clear edma intr error, and satahc interrupt dmaXdone */
sr->edma.cfg = (sr->edma.cfg & ~ECFGqueue) | ECFGncq;
@@ -1050,7 +1053,9 @@
}
qunlock(&reqsl);
- sleep(&reqsr[i], isdone, &v);
+ tsleep(&reqsr[i], isdone, &reqsdone[i], 10*1000);
+ if(!reqsdone[i])
+ error(Etimeout);
/* xxx check for error, raise it */
return ns*512;
@@ -1159,9 +1164,7 @@
free(s);
return n;
case Qdata:
-diprint("dcwbinv0 %ld\n", n);
dcwbinv(buf, n);
-diprint("dcwbinv1 %ld\n", n);
r = 0;
while(r < n) {
nn = io(Read, (uchar*)buf+r, n-r, off+r);
=======================================
--- /mem.h Thu Mar 18 06:11:29 2010
+++ /mem.h Thu Sep 2 12:09:20 2010
@@ -9,7 +9,7 @@
#define PGROUND(s) ROUND(s, BY2PG)
#define BIT(n) (1UL<<n)
#define BITS(a,b) ((1UL<<(b+1))-(1UL<<a))
-#define MASK(n) ((1UL<<(n))-1) /* could use BITS(0, n-1) */
+#define MASK(n) ((1UL<<(n))-1)
#define MAXMACH 1 /* max # cpus system can run */
==============================================================================
Revision: 9268c21a5e
Author: Mechiel Lukkien <mec...@ueber.net>
Date: Thu Sep 2 14:12:17 2010
Log: make ncq work properly.
we now do proper tag accounting, and locking to get at the tags.
the trick is "uchar tags[32]", which is filled with tags. when
someone wants to do io (sataread or satawrite), they take a qlock,
sleep until a tag is available, and use that tag for the request,
sleeping again until the command for that tag is finished (interrupt
handler does the wakeup). a slot to send the ata command to the
disk is always available when a tag is available.
this has been tested by doing many (eg 1024) concurrent satareads.
http://code.google.com/p/inferno-kirkwood/source/detail?r=9268c21a5e
Modified:
/devsata.c
=======================================
--- /devsata.c Thu Sep 2 12:09:20 2010
+++ /devsata.c Thu Sep 2 14:12:17 2010
@@ -5,22 +5,19 @@
todo:
- when doing ata commands, verify ata status if ok.
-- read ata/atapi signature in registers after reset?
- get interrupt when device sends registers. so we can check for BSY
then, or start reading data, etc.
-- detect whether packet command is accepted. try if packet commands work.
-- properly wait until device is no longer busy during startup
-- proper locking, wait for free edma slot.
-- look at cache flushes around dma
- error handling & propagating to caller.
- better detect ata support of drives
+- detect whether packet command is accepted. try if packet commands work.
+- read ata/atapi signature in registers after reset?
+- look at cache flushes around dma
+- hotplug, at least handle disconnects
- interrupt coalescing
-- fix ncq, return early responses (in different order than request queue)
- general ata commands
+- support: pio,dma,ncq with ata commands. perhaps atapi-mmc & atapi-scsi
too? this chip only does pio with atapi...
- use generic sd interface (#S; we need partitions)
-- hotplug, at least handle disconnects
- abstract for multiple ports (add controller struct to functions)
- in satainit(), only start the disk init, don't wait for it to be ready?
faster booting, seems it takes controller/disk some time to init after
reset.
-- support for non-ncq drives, by normal dma commands? should support
setting iomode: pio dma satancq. commands should ata for now. could be
atapi-mmc or atapi-scsi too. not good for this chip because dma with atapi
is not supported by the chip and pio is slow.
*/
#include "u.h"
@@ -91,9 +88,20 @@
uvlong sectors;
};
-static QLock reqsl;
-static Rendez reqsr[32];
+/*
+ * with ncq we get 32 tags. the controller has 32 slots, to write
+ * commands to the device. if a tag is available, there is also always
+ * a slot available. so we administer by tag.
+ */
+static uchar tags[32];
+static int tagnext;
+static int tagsinuse;
+static Rendez tagl; /* protected by reqsl */
+
+static Rendez reqsr[32]; /* protected by requiring to hold tag */
static volatile ulong reqsdone[32];
+static QLock reqsl;
+
static Req *reqs;
static Resp *resps;
static Prd *prds;
@@ -259,7 +267,9 @@
/* determine which request is done, wakeup its caller. */
tag = resps[out].idflags & MASK(5);
+
/* xxx check for error? */
+
reqsdone[tag] = 1;
wakeup(&reqsr[tag]);
out = (out+1)%32;
@@ -643,6 +653,10 @@
diprint("satainit...\n");
+ tagnext = tagsinuse = 0;
+ for(i = 0; i < nelem(tags); i++)
+ tags[i] = i;
+
/* disable interrupts */
hr->intrmainena = 0;
sr->edma.intreena = 0;
@@ -934,6 +948,12 @@
ulong *v = p;
return *v;
}
+
+static int
+tagfree(void *)
+{
+ return tagsinuse < nelem(tags);
+}
static long
min(long a, long b)
@@ -971,6 +991,7 @@
SataReg *sr = SATA1REG;
Req *rq;
int i;
+ ulong tag;
ulong ns;
ulong dev;
ulong nslo, nshi;
@@ -999,7 +1020,11 @@
error(Ebadarg);
qlock(&reqsl);
- /* xxx wait until edma/slot becomes available */
+
+ sleep(&tagl, tagfree, nil);
+ tag = tags[tagnext];
+ tagnext = (tagnext+1)%nelem(tags);
+ tagsinuse++;
i = reqnext;
rq = &reqs[i];
@@ -1022,22 +1047,21 @@
}
if(t == Read)
rq->ctl |= Rdev2mem;
- rq->ctl |= i<<Rdevtagshift;
- rq->ctl |= i<<Rhosttagshift;
-
- lbalo = lba & MASK(24);
- lbahi = (lba>>24) & MASK(24);
- nslo = ns&0xff;
- nshi = (ns>>8)&0xff;
+ rq->ctl |= tag<<Rdevtagshift;
+ rq->ctl |= tag<<Rhosttagshift;
+
+ lbalo = lba>>0 & MASK(24);
+ lbahi = lba>>24 & MASK(24);
+ nslo = ns>>0 & 0xff;
+ nshi = ns>>8 & 0xff;
dev = 1<<6;
- rq->ata[0] = (cmds[t]<<16)|(nslo<<24); /* cmd, feat current */
- rq->ata[1] = (lbalo<<0)|(dev<<24); /* 24 bit lba current, dev */
- rq->ata[2] = (lbahi<<0)|(nshi<<24); /* 24 bit lba previous, feat
ext/previous */
- rq->ata[3] = ((i<<3)<<0)|(0<<8); /* sectors current (tag), previous */
+ rq->ata[0] = cmds[t]<<16 | nslo<<24; /* cmd, feat current */
+ rq->ata[1] = lbalo<<0 | dev<<24; /* 24 bit lba current, dev */
+ rq->ata[2] = lbahi<<0 | nshi<<24; /* 24 bit lba previous, feat
ext/previous */
+ rq->ata[3] = (tag<<3)<<0 | 0<<8; /* sectors current (tag), previous */
dcwbinv(rq, sizeof rq[0]);
-diprint("io, using slot %d, off %llud\n", i, off);
- reqsdone[i] = 0;
+ reqsdone[tag] = 0;
sr->edma.reqin = (ulong)&reqs[reqnext];
if((sr->edma.cmd & EdmaEnable) == 0) {
/* clear edma intr error, and satahc interrupt dmaXdone */
@@ -1053,9 +1077,18 @@
}
qunlock(&reqsl);
- tsleep(&reqsr[i], isdone, &reqsdone[i], 10*1000);
- if(!reqsdone[i])
+ /* xxx have to return tag on interrupt? */
+ tsleep(&reqsr[tag], isdone, &reqsdone[tag], 10*1000);
+
+ if(!reqsdone[tag]) {
+ /* xxx should do ata reset, or return the tag back to pool */
error(Etimeout);
+ }
+
+ tags[(tagnext-tagsinuse+nelem(tags)) % nelem(tags)] = tag;
+ tagsinuse--;
+ wakeup(&tagl);
+
/* xxx check for error, raise it */
return ns*512;