summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-08 04:45:41 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-08 04:45:41 +0000
commit147fdb9c3f11a026483b682975d44cef1ac63cd4 (patch)
tree5546d650fa7fef70133239f693c019c06a5ff7dc /sys
parent61baf770443cf98d387006251a690c72169e80d1 (diff)
Track the order that CCBs are submitted in, so that after an error we are
sure to reissue commands in the correct order. Defer completion of commands when we an error has occurred so that new commands don't go active before queeud ones.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ic/sili.c69
1 files changed, 51 insertions, 18 deletions
diff --git a/sys/dev/ic/sili.c b/sys/dev/ic/sili.c
index 505b4d4a78f..941f6280cec 100644
--- a/sys/dev/ic/sili.c
+++ b/sys/dev/ic/sili.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sili.c,v 1.27 2007/04/08 00:47:50 pascoe Exp $ */
+/* $OpenBSD: sili.c,v 1.28 2007/04/08 04:45:40 pascoe Exp $ */
/*
* Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
@@ -77,6 +77,8 @@ struct sili_port {
TAILQ_HEAD(, sili_ccb) sp_free_ccbs;
volatile u_int32_t sp_active;
+ TAILQ_HEAD(, sili_ccb) sp_active_ccbs;
+ TAILQ_HEAD(, sili_ccb) sp_deferred_ccbs;
#ifdef SILI_DEBUG
char sp_name[16];
@@ -160,7 +162,7 @@ struct atascsi_methods sili_atascsi_methods = {
};
/* completion paths */
-void sili_ata_cmd_done(struct sili_ccb *);
+void sili_ata_cmd_done(struct sili_ccb *, int);
void sili_ata_cmd_timeout(void *);
int
@@ -233,13 +235,13 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
ccb = &sp->sp_ccbs[err_slot];
- DPRINTF(SILI_D_VERBOSE, "%s: error, code %d, slot %d\n",
- PORTNAME(sp), sili_pread(sp, SILI_PREG_CE), err_slot);
+ DPRINTF(SILI_D_VERBOSE, "%s: error, code %d, slot %d, "
+ "active %08x\n", PORTNAME(sp), sili_pread(sp, SILI_PREG_CE),
+ err_slot, sp->sp_active);
switch (sili_pread(sp, SILI_PREG_CE)) {
case SILI_PREG_CE_DEVICEERROR:
case SILI_PREG_CE_DATAFISERROR:
- need_restart = 1;
/* Extract error from command slot in LRAM. */
r = SILI_PREG_SLOT(err_slot) + 8;
bus_space_barrier(sp->sp_sc->sc_iot_port, sp->sp_ioh,
@@ -259,11 +261,13 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
break;
}
- /* Clear the failed commmand in saved PSS so completion runs. */
+ /* Clear the failed commmand in saved PSS so cmd_done runs. */
pss_saved &= ~(1 << err_slot);
KASSERT(ccb->ccb_xa.state == ATA_S_ONCHIP);
ccb->ccb_xa.state = ATA_S_ERROR;
+
+ need_restart = 1;
}
/* Command slot is complete if its bit in PSS is 0 but 1 in active. */
@@ -277,14 +281,13 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
PORTNAME(sp), slot, ccb->ccb_xa.state == ATA_S_ERROR ?
" (error)" : "");
- sp->sp_active &= ~(1 << slot);
- sili_ata_cmd_done(ccb);
+ sili_ata_cmd_done(ccb, need_restart);
processed |= 1 << slot;
}
- /* Restart port and reissue outstanding commands. */
if (need_restart) {
+ /* Re-enable transfers on port. */
sili_pwrite(sp, SILI_PREG_PCS, SILI_PREG_PCS_PORTINIT);
if (!sili_pwait_eq(sp, SILI_PREG_PCS, SILI_PREG_PCS_PORTRDY,
SILI_PREG_PCS_PORTRDY, 1000)) {
@@ -292,16 +295,37 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
PORTNAME(sp));
}
- while (pss_saved) {
- slot = ffs(pss_saved) - 1;
- ccb = &sp->sp_ccbs[slot];
- pss_saved &= ~(1 << slot);
-
+ /* Restart CCBs in the order they were originally queued. */
+ pss_masked = pss_saved;
+ TAILQ_FOREACH(ccb, &sp->sp_active_ccbs, ccb_entry) {
DPRINTF(SILI_D_VERBOSE, "%s: restarting slot %d "
- "after error\n", PORTNAME(sp), slot);
+ "after error, state %02x\n", PORTNAME(sp),
+ ccb->ccb_xa.tag, ccb->ccb_xa.state);
+ if (!(pss_masked & (1 << ccb->ccb_xa.tag))) {
+ panic("sili_intr: slot %d not active in "
+ "pss_masked: %08x, state %02x",
+ ccb->ccb_xa.tag, pss_masked,
+ ccb->ccb_xa.state);
+ }
+ pss_masked &= ~(1 << ccb->ccb_xa.tag);
+
KASSERT(ccb->ccb_xa.state == ATA_S_ONCHIP);
sili_post_indirect(sp, ccb);
}
+ KASSERT(pss_masked == 0);
+
+ /*
+ * Finally, run atascsi completion for any finished CCBs. If
+ * we had run these during cmd_done above, any ccbs that their
+ * completion generated would have been activated out of order.
+ */
+ while ((ccb = TAILQ_FIRST(&sp->sp_deferred_ccbs)) != NULL) {
+ TAILQ_REMOVE(&sp->sp_deferred_ccbs, ccb, ccb_entry);
+
+ KASSERT(ccb->ccb_xa.state == ATA_S_COMPLETE ||
+ ccb->ccb_xa.state == ATA_S_ERROR);
+ ccb->ccb_xa.complete(&ccb->ccb_xa);
+ }
}
return processed;
@@ -391,6 +415,8 @@ sili_ccb_alloc(struct sili_port *sp)
int i;
TAILQ_INIT(&sp->sp_free_ccbs);
+ TAILQ_INIT(&sp->sp_active_ccbs);
+ TAILQ_INIT(&sp->sp_deferred_ccbs);
sp->sp_ccbs = malloc(sizeof(struct sili_ccb) * SILI_MAX_CMDS,
M_DEVBUF, M_WAITOK);
@@ -620,7 +646,6 @@ sili_post_direct(struct sili_port *sp, u_int slot, void *buf, size_t buflen)
void
sili_post_indirect(struct sili_port *sp, struct sili_ccb *ccb)
{
- ccb->ccb_xa.state = ATA_S_ONCHIP;
sili_pwrite(sp, SILI_PREG_CAR_LO(ccb->ccb_xa.tag),
(u_int32_t)ccb->ccb_cmd_dva);
sili_pwrite(sp, SILI_PREG_CAR_HI(ccb->ccb_xa.tag),
@@ -766,7 +791,7 @@ failcmd:
}
void
-sili_ata_cmd_done(struct sili_ccb *ccb)
+sili_ata_cmd_done(struct sili_ccb *ccb, int defer_completion)
{
struct sili_port *sp = ccb->ccb_port;
struct sili_softc *sc = sp->sp_sc;
@@ -781,6 +806,9 @@ sili_ata_cmd_done(struct sili_ccb *ccb)
sili_unload(ccb);
+ TAILQ_REMOVE(&sp->sp_active_ccbs, ccb, ccb_entry);
+ sp->sp_active &= ~(1 << xa->tag);
+
if (xa->state == ATA_S_ONCHIP)
xa->state = ATA_S_COMPLETE;
#ifdef DIAGNOSTIC
@@ -788,7 +816,9 @@ sili_ata_cmd_done(struct sili_ccb *ccb)
printf("%s: invalid ata_xfer state %02x in sili_ata_cmd_done, "
"slot %d\n", PORTNAME(sp), xa->state, xa->tag);
#endif
- if (xa->state != ATA_S_TIMEOUT)
+ if (defer_completion)
+ TAILQ_INSERT_TAIL(&sp->sp_deferred_ccbs, ccb, ccb_entry);
+ else if (xa->state == ATA_S_COMPLETE)
xa->complete(xa);
}
@@ -919,7 +949,10 @@ sili_start(struct sili_port *sp, struct sili_ccb *ccb)
splassert(IPL_BIO);
KASSERT(ccb->ccb_xa.state == ATA_S_PENDING);
+ TAILQ_INSERT_TAIL(&sp->sp_active_ccbs, ccb, ccb_entry);
sp->sp_active |= 1 << slot;
+ ccb->ccb_xa.state = ATA_S_ONCHIP;
+
sili_post_indirect(sp, ccb);
}