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