diff options
author | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2007-03-28 06:40:20 +0000 |
---|---|---|
committer | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2007-03-28 06:40:20 +0000 |
commit | d1ed4fa10b4ed08d741cc2f0430e0e15c5e6c564 (patch) | |
tree | 6883e999d9d13c73b2f2a2e84554f2b635d3406e | |
parent | c32b43144a0a0722b74d8f459eef61d6b3b0811c (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@
-rw-r--r-- | sys/dev/pci/ahci.c | 104 |
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) { |