summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2007-03-28 06:40:20 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2007-03-28 06:40:20 +0000
commitd1ed4fa10b4ed08d741cc2f0430e0e15c5e6c564 (patch)
tree6883e999d9d13c73b2f2a2e84554f2b635d3406e /sys
parentc32b43144a0a0722b74d8f459eef61d6b3b0811c (diff)
While we are determining which NCQ command failed, we will need to ensure
that we don't accidentally complete any other outstanding commands. This introduces wrappers around get_ccb and put_ccb that can temporarily stash our outstanding command state while we are issuing recovery commands. This is just the first step in NCQ recovery - for now we will reset the port on error which will permit further commands to be issued after a NCQ error. ok dlg@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pci/ahci.c104
1 files changed, 94 insertions, 10 deletions
diff --git a/sys/dev/pci/ahci.c b/sys/dev/pci/ahci.c
index fc67de0d0d8..a427f369877 100644
--- a/sys/dev/pci/ahci.c
+++ b/sys/dev/pci/ahci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ahci.c,v 1.102 2007/03/28 06:29:27 pascoe Exp $ */
+/* $OpenBSD: ahci.c,v 1.103 2007/03/28 06:40:19 pascoe Exp $ */
/*
* Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
@@ -349,6 +349,14 @@ struct ahci_port {
#define AP_S_NORMAL 0
#define AP_S_FATAL_ERROR 1
+ /* For error recovery. */
+#ifdef DIAGNOSTIC
+ int ap_err_busy;
+#endif
+ u_int32_t ap_err_saved_sactive;
+ u_int32_t ap_err_saved_active;
+ u_int32_t ap_err_saved_active_cnt;
+
#ifdef AHCI_DEBUG
char ap_name[16];
#define PORTNAME(_ap) ((_ap)->ap_name)
@@ -448,6 +456,9 @@ u_int32_t ahci_port_intr(struct ahci_port *, u_int32_t);
struct ahci_ccb *ahci_get_ccb(struct ahci_port *);
void ahci_put_ccb(struct ahci_ccb *);
+struct ahci_ccb *ahci_get_err_ccb(struct ahci_port *);
+void ahci_put_err_ccb(struct ahci_ccb *);
+
struct ahci_dmamem *ahci_dmamem_alloc(struct ahci_softc *, size_t);
void ahci_dmamem_free(struct ahci_softc *,
struct ahci_dmamem *);
@@ -1089,11 +1100,7 @@ ahci_port_softreset(struct ahci_port *ap)
DPRINTF(AHCI_D_VERBOSE, "%s: soft reset\n", PORTNAME(ap));
s = splbio();
- ccb = ahci_get_ccb(ap);
- splx(s);
- if (ccb == NULL)
- goto err;
- ccb->ccb_done = ahci_empty_done;
+ ccb = ahci_get_err_ccb(ap);
/* Save previous command register state */
cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC;
@@ -1178,15 +1185,15 @@ err:
"still active.\n", PORTNAME(ap), ccb->ccb_slot);
ahci_port_stop(ap, 0);
}
- s = splbio();
ccb->ccb_xa.state = ATA_S_ERROR;
- ahci_put_ccb(ccb);
- splx(s);
+ ahci_put_err_ccb(ccb);
}
/* Restore saved CMD register state */
ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
+ splx(s);
+
return (rc);
}
@@ -1607,8 +1614,17 @@ ahci_port_intr(struct ahci_port *ap, u_int32_t ci_mask)
/* Didn't reset, could gather extended info from log. */
}
- /* If we couldn't determine the errored slot, fail all. */
+ /*
+ * If we couldn't determine the errored slot, reset the port
+ * and fail all the active slots.
+ */
if (err_slot == -1) {
+ if (ahci_port_softreset(ap) != 0 &&
+ ahci_port_portreset(ap) != 0) {
+ printf("%s: couldn't reset after NCQ error, "
+ "disabling device.\n", PORTNAME(ap));
+ goto fatal;
+ }
printf("%s: couldn't recover from NCQ error, failing "
"all outstanding commands.\n", PORTNAME(ap));
goto failall;
@@ -1769,6 +1785,74 @@ ahci_put_ccb(struct ahci_ccb *ccb)
TAILQ_INSERT_TAIL(&ap->ap_ccb_free, ccb, ccb_entry);
}
+struct ahci_ccb *
+ahci_get_err_ccb(struct ahci_port *ap)
+{
+ struct ahci_ccb *err_ccb;
+ u_int32_t sact;
+
+ splassert(IPL_BIO);
+
+ /* No commands may be active on the chip. */
+ sact = ahci_pread(ap, AHCI_PREG_SACT);
+ if (sact != 0)
+ printf("ahci_get_err_ccb but SACT %08x != 0?\n", sact);
+ KASSERT(ahci_pread(ap, AHCI_PREG_CI) == 0);
+
+#ifdef DIAGNOSTIC
+ KASSERT(ap->ap_err_busy == 0);
+ ap->ap_err_busy = 1;
+#endif
+ /* Save outstanding command state. */
+ ap->ap_err_saved_active = ap->ap_active;
+ ap->ap_err_saved_active_cnt = ap->ap_active_cnt;
+ ap->ap_err_saved_sactive = ap->ap_sactive;
+
+ /*
+ * Pretend we have no commands outstanding, so that completions won't
+ * run prematurely.
+ */
+ ap->ap_active = ap->ap_active_cnt = ap->ap_sactive = 0;
+
+ /*
+ * Grab a CCB to use for error recovery. This should never fail, as
+ * we ask atascsi to reserve one for us at init time.
+ */
+ err_ccb = ahci_get_ccb(ap);
+ KASSERT(err_ccb != NULL);
+ err_ccb->ccb_xa.flags = 0;
+ err_ccb->ccb_done = ahci_empty_done;
+
+ return err_ccb;
+}
+
+void
+ahci_put_err_ccb(struct ahci_ccb *ccb)
+{
+ struct ahci_port *ap = ccb->ccb_port;
+ u_int32_t sact;
+
+ splassert(IPL_BIO);
+
+#ifdef DIAGNOSTIC
+ KASSERT(ap->ap_err_busy);
+#endif
+ /* No commands may be active on the chip */
+ sact = ahci_pread(ap, AHCI_PREG_SACT);
+ if (sact != 0)
+ printf("ahci_port_err_ccb_restore but SACT %08x != 0?\n", sact);
+ KASSERT(ahci_pread(ap, AHCI_PREG_CI) == 0);
+
+ /* Restore outstanding command state */
+ ap->ap_sactive = ap->ap_err_saved_sactive;
+ ap->ap_active_cnt = ap->ap_err_saved_active_cnt;
+ ap->ap_active = ap->ap_err_saved_active;
+
+#ifdef DIAGNOSTIC
+ ap->ap_err_busy = 0;
+#endif
+}
+
struct ahci_dmamem *
ahci_dmamem_alloc(struct ahci_softc *sc, size_t size)
{