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 adjust error recovery to similar shape as ...



details:   https://anonhg.NetBSD.org/src/rev/794c04df4231
branches:  jdolecek-ncq
changeset: 822983:794c04df4231
user:      jdolecek <jdolecek%NetBSD.org@localhost>
date:      Sun Jul 30 20:24:45 2017 +0000

description:
adjust error recovery to similar shape as ahcisata; also switch to using
siisata_reset_channel(), as that seems more reliably get the HBA to running
state again after some errors

besides this, change several places checking PORT_READY to use
siisata_reinit_port() and properly timeout - seen several cases
where it would just loop there indefinitely on some errors with no
way to get into ddb and no error message, due to the tight DELAY() loop

diffstat:

 sys/dev/ic/siisata.c |  320 +++++++++++++++++++++++++++++---------------------
 1 files changed, 185 insertions(+), 135 deletions(-)

diffs (truncated from 549 to 300 lines):

diff -r 6a57f508f7f2 -r 794c04df4231 sys/dev/ic/siisata.c
--- a/sys/dev/ic/siisata.c      Sun Jul 30 20:16:29 2017 +0000
+++ b/sys/dev/ic/siisata.c      Sun Jul 30 20:24:45 2017 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: siisata.c,v 1.30.4.27 2017/07/19 20:24:59 jdolecek Exp $ */
+/* $NetBSD: siisata.c,v 1.30.4.28 2017/07/30 20:24:45 jdolecek Exp $ */
 
 /* from ahcisata_core.c */
 
@@ -79,7 +79,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.30.4.27 2017/07/19 20:24:59 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.30.4.28 2017/07/30 20:24:45 jdolecek Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -157,12 +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 *);
 
-static void siisata_reinit_port(struct ata_channel *);
+static void siisata_reinit_port(struct ata_channel *, int);
 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 *, int, void *, size_t, int);
-static void siisata_channel_recover(struct ata_channel *, int, int);
+void siisata_channel_recover(struct ata_channel *, uint32_t);
 
 #if NATAPIBUS > 0
 void siisata_atapibus_attach(struct atabus_softc *);
@@ -266,7 +266,7 @@
        PRWRITE(sc, PRX(chp->ch_channel, PRO_PCC),
            PR_PC_32BA | PR_PC_INCOR | PR_PC_PORT_RESET);
        /* initialize port */
-       siisata_reinit_port(chp);
+       siisata_reinit_port(chp, -1);
        /* enable CmdErrr+CmdCmpl interrupting */
        siisata_enable_port_interrupt(chp);
        /* enable port interrupt */
@@ -288,7 +288,7 @@
        sc->sc_chanarray[port] = chp;
        chp->ch_channel = port;
        chp->ch_atac = &sc->sc_atac;
-       chp->ch_queue = ata_queue_alloc(SIISATA_MAX_SLOTS);
+       chp->ch_queue = ata_queue_alloc(4) ; //SIISATA_MAX_SLOTS);
        if (chp->ch_queue == NULL) {
                aprint_error_dev(sc->sc_atac.atac_dev,
                    "port %d: can't allocate memory "
@@ -471,54 +471,29 @@
 static void
 siisata_intr_port(struct siisata_channel *schp)
 {
-       struct siisata_softc *sc;
-       struct ata_channel *chp;
+       struct siisata_softc *sc =
+           (struct siisata_softc *)schp->ata_channel.ch_atac;
+       struct ata_channel *chp = &schp->ata_channel;
        struct ata_xfer *xfer;
-       u_int slot;
-       uint32_t pss, pis;
-
-       sc = (struct siisata_softc *)schp->ata_channel.ch_atac;
-       chp = &schp->ata_channel;
+       int slot = -1;
+       uint32_t pss, pis, tfd = 0;
+       bool recover = false;
 
        /* get slot status, clearing completion interrupt (PR_PIS_CMDCMPL) */
        pss = PRREAD(sc, PRX(chp->ch_channel, PRO_PSS));
+
        SIISATA_DEBUG_PRINT(("%s: %s port %d, pss 0x%x\n",
            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) {
-                       /* there's nothing executing here, skip */
-                       continue;
-               }
-               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);
-                       continue;
-               }
-               KASSERT(xfer != NULL);
-               KASSERT(xfer->c_intr != NULL);
-               xfer->c_intr(chp, xfer, 0);
-       }
-       /* if no errors, we're done now */
-       if ((pss & PR_PSS_ATTENTION) == 0) {
-               pis = PRREAD(sc, PRX(chp->ch_channel, PRO_PIS));
-               pis &= 0xffff;
-               if (pis) {
-                       PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS),
-                           pis & 0xfffcfffc);
-               }
-               return;
-       }
+       /* if no errors, just process finished commands and we're done */
+       if (__predict_true((pss & PR_PSS_ATTENTION) == 0))
+               goto process;
 
        pis = PRREAD(sc, PRX(chp->ch_channel, PRO_PIS));
 
-       SIISATA_DEBUG_PRINT(("%s: %s port %d, pis 0x%x ", SIISATANAME(sc),
-           __func__, chp->ch_channel, pis), DEBUG_INTR);
+       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);
@@ -530,32 +505,72 @@
                SIISATA_DEBUG_PRINT(("ec %d\n", ec), DEBUG_INTR);
 
                /* emulate a CRC error by default */
-               int tfd = ATACH_ERR_ST(WDCE_CRC, WDCS_ERR);
+               tfd = ATACH_ERR_ST(WDCE_CRC, WDCS_ERR);
+
+               if (ec <= PR_PCE_DATAFISERROR) {
+                       if (ec == PR_PCE_DEVICEERROR
+                           && (chp->ch_flags & ATACH_NCQ) == 0) {
+                               uint32_t ps = PRREAD(sc,
+                                   PRX(chp->ch_channel, PRO_PS));
+                               /* This is only relevant for non-NCQ commands */
+                               slot = PR_PS_ACTIVE_SLOT(ps);
 
-               if (ec <= PR_PCE_DATAFISERROR && !schp->sch_recovering) {
-                       siisata_channel_recover(chp, ec, tfd);
+                               /* read in specific information about error */
+                               uint32_t 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;
+                       }
                } 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);
+                       tfd |= ATACH_ERR_ST(0, WDCS_BSY);
+               }
+
+               if (!schp->sch_recovering)
+                       recover = true;
+       } else {
+               /* Some other event, which we currently ignore */
+       }
+
+       if (__predict_false(recover))
+               ata_channel_freeze(chp);
 
-                       /* 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;
-                               }
+       if (slot >= 0) {
+               if ((schp->sch_active_slots & (1 << slot)) != 0 &&
+                   (pss & (1 << slot)) == 0) {
+                       xfer = ata_queue_hwslot_to_xfer(chp, slot);
+                       xfer->c_intr(chp, xfer, tfd);
+               }
+       } else {
+               /*
+                * For NCQ, HBA halts processing when error is notified,
+                * and any further D2H FISes are ignored until the error
+                * condition is cleared. Hence if a command is inactive,
+                * it means it actually already finished successfully.
+                */
+process:
+               for (slot=0; slot < SIISATA_MAX_SLOTS; slot++) {
+                       if ((schp->sch_active_slots & (1 << slot)) != 0 &&
+                           (pss & PR_PXSS(slot)) == 0) {
                                xfer = ata_queue_hwslot_to_xfer(chp, slot);
-                               xfer->c_intr(chp, xfer, tfd);
+                               xfer->c_intr(chp, xfer, 0);
                        }
                }
        }
 
-       return;
+       if (__predict_false(recover))
+               siisata_channel_recover(chp, tfd);
 }
 
 static void
@@ -573,24 +588,21 @@
 }
 
 /* Recover channel after transfer aborted */
-static void
-siisata_channel_recover(struct ata_channel *chp, int ec, int tfd)
+void
+siisata_channel_recover(struct ata_channel *chp, uint32_t 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;
+       int drive, error;
+       uint8_t eslot, 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));
@@ -600,49 +612,72 @@
 
        drvp = &chp->ch_drive[drive];
 
-       siisata_hold(schp);
+       /*
+        * If BSY or DRQ bits are set, must execute COMRESET to return
+        * device to idle state. Otherwise, commands can be reissued
+        * after reinitalization of port. After that, need to execute
+        * READ LOG EXT for NCQ to unblock device processing if COMRESET
+        * was not done.
+        */
+       if ((ATACH_ST(tfd) & (WDCS_BSY|WDCS_DRQ)) != 0)
+               goto reset;
 
-       siisata_reinit_port(chp);
+       KASSERT(drive >= 0);
+       siisata_reinit_port(chp, drive);
+
+       siisata_hold(schp);
 
        /*
         * 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);
+       error = ata_read_log_ext_ncq(drvp, AT_POLL, &eslot, &st, &err);
 
        siisata_unhold(schp);
 
-       if (error == EOPNOTSUPP) {
+       switch (error) {
+       case 0:
+               /* Error out the particular NCQ xfer, then requeue the others */
+               if ((schp->sch_active_slots & (1 << eslot)) != 0) {
+                       xfer = ata_queue_hwslot_to_xfer(chp, eslot);
+                       xfer->c_intr(chp, xfer, ATACH_ERR_ST(err, st));
+               }
+               break;
+
+       case EOPNOTSUPP:
                /*
-                * Not NCQ command or not NCQ device, there is only
-                * one outstanding tranfer, so find the active one,
-                * and send the error.
+                * Non-NCQ command error, just find the slot and end it with
+                * 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));
+               for (slot = 0; slot < SIISATA_MAX_SLOTS; slot++) {
+                       if ((schp->sch_active_slots & (1 << slot)) != 0) {
+                               xfer = ata_queue_hwslot_to_xfer(chp, slot);
+                               if (xfer->c_drive != drive)
+                                       continue;
 
-                       /* get status and error */
-                       int ntfd = satafis_rdh_parse(chp, (uint8_t *)&prbfis);
-



Home | Main Index | Thread Index | Old Index