Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/jdolecek-ncq]: src/sys/dev/ic convert over to new error handling world o...
details: https://anonhg.NetBSD.org/src/rev/54fc26165135
branches: jdolecek-ncq
changeset: 822961:54fc26165135
user: jdolecek <jdolecek%NetBSD.org@localhost>
date: Wed Jul 19 20:02:40 2017 +0000
description:
convert over to new error handling world order:
- switch to ata_timeout()
- stop using ch_status/ch_error for passing state/error, stop setting
ATACH_IRQ_WAIT in ch_flags; pass the state via the last parameter
to c_intr() routine
- add NCQ recovery and KILL_REQUEUE
- only call atastart() in c_intr() if there was no error
several siisata specific tweaks:
- improve reset to better handle PM - need to reset couple more registers
- add timeouts for unbusy wait - ATA_DELAY (10s)
- siisata_bio_complete() do not use PRSO_RTC for byte count for NCQ transfers,
they never return partial reads; and that register might contain some random
junk, at least that's the case with ahcisata cmdh_prdbc
diffstat:
sys/dev/ic/siisata.c | 341 +++++++++++++++++++++++++++++++-------------------
1 files changed, 212 insertions(+), 129 deletions(-)
diffs (truncated from 667 to 300 lines):
diff -r 6612777b27b7 -r 54fc26165135 sys/dev/ic/siisata.c
--- a/sys/dev/ic/siisata.c Wed Jul 19 19:46:52 2017 +0000
+++ b/sys/dev/ic/siisata.c Wed Jul 19 20:02:40 2017 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: siisata.c,v 1.30.4.25 2017/06/27 20:13:56 jdolecek Exp $ */
+/* $NetBSD: siisata.c,v 1.30.4.26 2017/07/19 20:02:40 jdolecek Exp $ */
/* from ahcisata_core.c */
@@ -79,7 +79,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.30.4.25 2017/06/27 20:13:56 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.30.4.26 2017/07/19 20:02:40 jdolecek Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -148,7 +148,7 @@
void siisata_cmd_start(struct ata_channel *, struct ata_xfer *);
int siisata_cmd_complete(struct ata_channel *, struct ata_xfer *, int);
-void siisata_cmd_done(struct ata_channel *, struct ata_xfer *);
+void siisata_cmd_done(struct ata_channel *, struct ata_xfer *, int);
static void siisata_cmd_done_end(struct ata_channel *, struct ata_xfer *);
void siisata_cmd_kill_xfer(struct ata_channel *, struct ata_xfer *, int);
@@ -157,14 +157,12 @@
void siisata_bio_kill_xfer(struct ata_channel *, struct ata_xfer *, int);
int siisata_exec_command(struct ata_drive_datas *, struct ata_xfer *);
-void siisata_timeout(void *);
-
static void siisata_reinit_port(struct ata_channel *);
static void siisata_device_reset(struct ata_channel *);
static void siisata_activate_prb(struct siisata_channel *, int);
static void siisata_deactivate_prb(struct siisata_channel *, int);
-static int siisata_dma_setup(struct ata_channel *chp, int, void *,
- size_t, int);
+static int siisata_dma_setup(struct ata_channel *, int, void *, size_t, int);
+static void siisata_channel_recover(struct ata_channel *, int, int);
#if NATAPIBUS > 0
void siisata_atapibus_attach(struct atabus_softc *);
@@ -478,7 +476,6 @@
struct ata_xfer *xfer;
u_int slot;
uint32_t pss, pis;
- uint32_t prbfis;
sc = (struct siisata_softc *)schp->ata_channel.ch_atac;
chp = &schp->ata_channel;
@@ -489,12 +486,15 @@
SIISATANAME(sc), __func__, chp->ch_channel, pss), DEBUG_INTR);
for (slot = 0; slot < SIISATA_MAX_SLOTS; slot++) {
- if (((schp->sch_active_slots >> slot) & 1) == 0)
+ if (((schp->sch_active_slots >> slot) & 1) == 0) {
/* there's nothing executing here, skip */
continue;
- if (((pss >> slot) & 1) != 0)
- /* execution is incomplete or unsuccessful, skip for now */
+ }
+ if (((pss >> slot) & 1) != 0) {
+ /* execution is incomplete or unsuccessful, skip
+ * for now */
continue;
+ }
xfer = ata_queue_hwslot_to_xfer(chp, slot);
if (xfer->c_intr == NULL) {
wakeup(schp);
@@ -520,54 +520,145 @@
SIISATA_DEBUG_PRINT(("%s: %s port %d, pis 0x%x ", SIISATANAME(sc),
__func__, chp->ch_channel, pis), DEBUG_INTR);
+ /* clear */
+ PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), pis);
+
if (pis & PR_PIS_CMDERRR) {
uint32_t ec;
- uint32_t ps;
- ps = PRREAD(sc, PRX(chp->ch_channel, PRO_PS));
ec = PRREAD(sc, PRX(chp->ch_channel, PRO_PCE));
SIISATA_DEBUG_PRINT(("ec %d\n", ec), DEBUG_INTR);
- slot = PR_PS_ACTIVE_SLOT(ps); /* XXX invalid for NCQ? */
-
/* emulate a CRC error by default */
- chp->ch_status = WDCS_ERR;
- chp->ch_error = WDCE_CRC;
+ int tfd = ATACH_ERR_ST(WDCE_CRC, WDCS_ERR);
- if (ec <= PR_PCE_DATAFISERROR) {
- if (ec == PR_PCE_DEVICEERROR) {
- /* read in specific information about error */
- prbfis = bus_space_read_stream_4(
- sc->sc_prt, sc->sc_prh,
- PRSX(chp->ch_channel, slot,
- PRSO_FIS));
- /* set ch_status and ch_error */
- satafis_rdh_parse(chp, (uint8_t *)&prbfis);
- }
- siisata_reinit_port(chp);
+ if (ec <= PR_PCE_DATAFISERROR && !schp->sch_recovering) {
+ siisata_channel_recover(chp, ec, tfd);
} else {
aprint_error_dev(sc->sc_atac.atac_dev, "fatal error %d"
" on channel %d (ctx 0x%x), resetting\n",
ec, chp->ch_channel,
PRREAD(sc, PRX(chp->ch_channel, PRO_PCR)));
+
/* okay, we have a "Fatal Error" */
siisata_device_reset(chp);
- }
- for (slot = 0; slot < SIISATA_MAX_SLOTS; slot++) {
- /* there's nothing executing here, skip */
- if (((schp->sch_active_slots >> slot) & 1) == 0)
- continue;
- xfer = ata_queue_hwslot_to_xfer(chp, slot);
- if (xfer == NULL)
- continue;
- xfer->c_intr(chp, xfer, 0);
+
+ /* Complete any non-finished commands with the error */
+ for (slot = 0; slot < SIISATA_MAX_SLOTS; slot++) {
+ if (((schp->sch_active_slots >> slot) & 1) == 0) {
+ /* nothing executing here, skip */
+ continue;
+ }
+ xfer = ata_queue_hwslot_to_xfer(chp, slot);
+ xfer->c_intr(chp, xfer, tfd);
+ }
}
}
- /* clear */
- PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), pis);
+ return;
+}
+
+static void
+siisata_hold(struct siisata_channel *schp)
+{
+ schp->sch_hold_slots |= schp->sch_active_slots;
+ schp->sch_active_slots = 0;
+}
+
+static void
+siisata_unhold(struct siisata_channel *schp)
+{
+ schp->sch_active_slots = schp->sch_hold_slots;
+ schp->sch_hold_slots = 0;
+}
+
+/* Recover channel after transfer aborted */
+static void
+siisata_channel_recover(struct ata_channel *chp, int ec, int tfd)
+{
+ struct siisata_channel *schp = (struct siisata_channel *)chp;
+ struct siisata_softc *sc =
+ (struct siisata_softc *)schp->ata_channel.ch_atac;
+ struct ata_drive_datas *drvp;
+ int ps, drive, error;
+ uint8_t slot, st, err;
+ struct ata_xfer *xfer;
+ uint32_t prbfis;
+
+ KASSERT(!schp->sch_recovering);
+
+ schp->sch_recovering = true;
+
+ ps = PRREAD(sc, PRX(chp->ch_channel, PRO_PS));
+
+ if (chp->ch_ndrives > PMP_PORT_CTL) {
+ /* Get PM port number for the device in error */
+ int pcr = PRREAD(sc, PRX(chp->ch_channel, PRO_PCR));
+ drive = PRO_PCR_PMP(pcr);
+ } else
+ drive = 0;
+
+ drvp = &chp->ch_drive[drive];
+
+ siisata_hold(schp);
+
+ siisata_reinit_port(chp);
- return;
+ /*
+ * When running NCQ commands, READ LOG EXT is necessary to clear the
+ * error condition and unblock the device.
+ */
+ error = ata_read_log_ext_ncq(drvp, AT_POLL, &slot, &st, &err);
+
+ siisata_unhold(schp);
+
+ if (error == EOPNOTSUPP) {
+ /*
+ * Not NCQ command or not NCQ device, there is only
+ * one outstanding tranfer, so find the active one,
+ * and send the error.
+ */
+ xfer = ata_queue_drive_active_xfer(chp, drive);
+ if (ec == PR_PCE_DEVICEERROR) {
+ slot = PR_PS_ACTIVE_SLOT(ps);
+
+ /* read in specific information about error */
+ prbfis = bus_space_read_stream_4(
+ sc->sc_prt, sc->sc_prh,
+ PRSX(chp->ch_channel, slot,
+ PRSO_FIS));
+
+ /* get status and error */
+ int ntfd = satafis_rdh_parse(chp, (uint8_t *)&prbfis);
+
+ if (ATACH_ST(ntfd) & WDCS_ERR)
+ tfd = ntfd;
+ }
+
+ xfer->c_intr(chp, xfer, tfd);
+ } else if (error == 0) {
+ xfer = ata_queue_hwslot_to_xfer(chp, slot);
+ xfer->c_intr(chp, xfer, ATACH_ERR_ST(err, st));
+ } else {
+ /* Other error, do full device reset */
+ drive = -1;
+ siisata_device_reset(chp);
+ }
+
+ /* Requeue the non-errorred commands */
+ for (slot = 0; slot < SIISATA_MAX_SLOTS; slot++) {
+ if (((schp->sch_active_slots >> slot) & 1) == 0)
+ continue;
+
+ xfer = ata_queue_hwslot_to_xfer(chp, slot);
+ if (drive >= 0 && xfer->c_drive != drive)
+ continue;
+
+ xfer->c_kill_xfer(chp, xfer,
+ drive >= 0 ? KILL_REQUEUE : KILL_RESET);
+ }
+
+ schp->sch_recovering = false;
}
void
@@ -629,10 +720,12 @@
pis = PRREAD(sc, PRX(chp->ch_channel, PRO_PIS));
const uint32_t ps = PRREAD(sc, PRX(chp->ch_channel, PRO_PS));
const u_int slot = PR_PS_ACTIVE_SLOT(ps);
+
if (slot != xfer->c_slot)
device_printf(sc->sc_atac.atac_dev, "%s port %d "
"drive %d slot %d c_slot %d", __func__,
chp->ch_channel, drvp->drive, slot, xfer->c_slot);
+
PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), pis &
PR_PIS_CMDERRR);
}
@@ -657,29 +750,6 @@
ata_free_xfer(chp, xfer);
-#if 1
- /* attempt to downgrade signaling in event of CRC error */
- /* XXX should be part of the MI (S)ATA subsystem */
- if (chp->ch_status == 0x51 && chp->ch_error == 0x84) {
- bus_space_write_4(sc->sc_prt, schp->sch_scontrol, 0,
- SControl_IPM_NONE | SControl_SPD_G1 | SControl_DET_INIT);
- DELAY(10);
- bus_space_write_4(sc->sc_prt, schp->sch_scontrol, 0,
- SControl_IPM_NONE | SControl_SPD_G1);
- DELAY(10);
- for (;;) {
- if ((bus_space_read_4(sc->sc_prt, schp->sch_sstatus, 0)
- & SStatus_DET_mask) == SStatus_DET_DEV)
- break;
- DELAY(10);
- }
- }
-#endif
-
-#if 1
- chp->ch_status = 0;
- chp->ch_error = 0;
-#endif
return;
}
@@ -902,9 +972,6 @@
chp->ch_channel, xfer->c_drive, ata_c->r_command, xfer->c_slot),
DEBUG_FUNCS|DEBUG_XFERS);
- chp->ch_status = 0;
- chp->ch_error = 0;
-
prb = schp->sch_prb[xfer->c_slot];
memset(prb, 0, SIISATA_CMD_SIZE);
@@ -936,9 +1003,8 @@
Home |
Main Index |
Thread Index |
Old Index