Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/pci Improve AEN handling:



details:   https://anonhg.NetBSD.org/src/rev/da44f08f8bab
branches:  trunk
changeset: 552230:da44f08f8bab
user:      thorpej <thorpej%NetBSD.org@localhost>
date:      Mon Sep 22 01:13:02 2003 +0000

description:
Improve AEN handling:
- Make AENs use the generic code table stuff.
- Add a few more AEN codes (from FreeBSD).
- Correct the context of a few AEN codes (some were listed as
  "unit context" when they're really "port context").
- Add a queue of AENs that management tools in userspace can poll
  (from FreeBSD).

diffstat:

 sys/dev/pci/twe.c    |  247 ++++++++++++++++++++++++++++++++------------------
 sys/dev/pci/twevar.h |   13 ++-
 2 files changed, 169 insertions(+), 91 deletions(-)

diffs (truncated from 382 to 300 lines):

diff -r 3c29cc4b3f24 -r da44f08f8bab sys/dev/pci/twe.c
--- a/sys/dev/pci/twe.c Sun Sep 21 21:28:40 2003 +0000
+++ b/sys/dev/pci/twe.c Mon Sep 22 01:13:02 2003 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: twe.c,v 1.44 2003/09/21 19:46:44 thorpej Exp $ */
+/*     $NetBSD: twe.c,v 1.45 2003/09/22 01:13:02 thorpej Exp $ */
 
 /*-
  * Copyright (c) 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc.
@@ -70,7 +70,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: twe.c,v 1.44 2003/09/21 19:46:44 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: twe.c,v 1.45 2003/09/22 01:13:02 thorpej Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -99,6 +99,9 @@
 #define        PCI_CBIO        0x10
 
 static void    twe_aen_handler(struct twe_ccb *, int);
+static void    twe_aen_enqueue(struct twe_softc *sc, uint16_t, int);
+static uint16_t        twe_aen_dequeue(struct twe_softc *);
+
 static void    twe_attach(struct device *, struct device *, void *);
 static int     twe_init_connection(struct twe_softc *);
 static int     twe_intr(void *);
@@ -195,6 +198,50 @@
        { 0,                                    NULL }
 };
 
+/*
+ * Asynchronous event notification messages are qualified:
+ *     a - not unit/port specific
+ *     u - unit specific
+ *     p - port specific
+ */
+const struct twe_code_table twe_table_aen[] = {
+       { 0x00, "a queue empty" },
+       { 0x01, "a soft reset" },
+       { 0x02, "u degraded mode" },
+       { 0x03, "a controller error" },
+       { 0x04, "u rebuild fail" },
+       { 0x05, "u rebuild done" },
+       { 0x06, "u incomplete unit" },
+       { 0x07, "u initialization done" },
+       { 0x08, "u unclean shutdown detected" },
+       { 0x09, "p drive timeout" },
+       { 0x0a, "p drive error" },
+       { 0x0b, "u rebuild started" },
+       { 0x0c, "u initialization started" },
+       { 0x0d, "u logical unit deleted" },
+       { 0x0f, "p SMART threshold exceeded" },
+       { 0x15, "a table undefined" },  /* XXX: Not in FreeBSD's table */
+       { 0x21, "p ATA UDMA downgrade" },
+       { 0x22, "p ATA UDMA upgrade" },
+       { 0x23, "p sector repair occurred" },
+       { 0x24, "a SBUF integrity check failure" },
+       { 0x25, "p lost cached write" },
+       { 0x26, "p drive ECC error detected" },
+       { 0x27, "p DCB checksum error" },
+       { 0x28, "p DCB unsupported version" },
+       { 0x29, "u verify started" },
+       { 0x2a, "u verify failed" },
+       { 0x2b, "u verify complete" },
+       { 0x2c, "p overwrote bad sector during rebuild" },
+       { 0x2d, "p encountered bad sector during rebuild" },
+       { 0x2e, "p replacement drive too small" },
+       { 0x2f, "u array not previously initialized" },
+       { 0x30, "p drive not supported" },
+       { 0xff, "a aen queue full" },
+
+       { 0,    NULL },
+};
+
 const char *
 twe_describe_code(const struct twe_code_table *table, uint32_t code)
 {
@@ -206,55 +253,6 @@
        return (NULL);
 }
 
-struct {
-       const u_int     aen;    /* High byte indicates type of message */
-       const char      *desc;
-} static const twe_aen_names[] = {
-       { 0x0000, "queue empty" },
-       { 0x0001, "soft reset" },
-       { 0x0102, "degraded mirror" },
-       { 0x0003, "controller error" },
-       { 0x0104, "rebuild fail" },
-       { 0x0105, "rebuild done" },
-       { 0x0106, "incompatible unit" },
-       { 0x0107, "initialisation done" },
-       { 0x0108, "unclean shutdown detected" },
-       { 0x0109, "drive timeout" },
-       { 0x010a, "drive error" },
-       { 0x010b, "rebuild started" },
-       { 0x010c, "init started" },
-       { 0x010d, "logical unit deleted" },
-       { 0x020f, "SMART threshold exceeded" },
-       { 0x0015, "table undefined" },  /* XXX: Not in FreeBSD's table */
-       { 0x0221, "ATA UDMA downgrade" },
-       { 0x0222, "ATA UDMA upgrade" },
-       { 0x0222, "ATA UDMA upgrade" },
-       { 0x0223, "Sector repair occurred" },
-       { 0x0024, "SBUF integrity check failure" },
-       { 0x0225, "lost cached write" },
-       { 0x0226, "drive ECC error detected" },
-       { 0x0227, "DCB checksum error" },
-       { 0x0228, "DCB unsupported version" },
-       { 0x0129, "verify started" },
-       { 0x012a, "verify failed" },
-       { 0x012b, "verify complete" },
-       { 0x022c, "overwrote bad sector during rebuild" },
-       { 0x022d, "encountered bad sector during rebuild" },
-       { 0x00ff, "aen queue full" },
-};
-
-/*
- * The high byte of the message above determines the format,
- * currently we know about format 0 (no unit/port specific)
- * format 1 (unit specific message), and format 2 (port specific message).
- */
-static const char * const aenfmt[] = {
-       "",             /* No message */
-       "unit %d: ",    /* Unit message */
-       "port %d: "     /* Port message */
-};
-
-
 static inline u_int32_t
 twe_inl(struct twe_softc *sc, int off)
 {
@@ -540,24 +538,34 @@
            TWE_CTL_CLEAR_ERROR_STS |
            TWE_CTL_DISABLE_INTRS);
 
+       /* Wait for attention... */
        if (twe_status_wait(sc, TWE_STS_ATTN_INTR, 15)) {
                printf("%s: no attention interrupt\n",
                    sc->sc_dv.dv_xname);
                return (-1);
        }
 
-       /* Pull AENs out of the controller; look for a soft reset AEN. */
+       /* ...and ACK it. */
+       twe_outl(sc, TWE_REG_CTL, TWE_CTL_CLEAR_ATTN_INTR);
+
+       /*
+        * Pull AENs out of the controller; look for a soft reset AEN.
+        * Open code this, since we want to detect reset even if the
+        * queue for management tools is full.
+        */
        for (got = 0;;) {
                rv = twe_param_get_2(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode,
                    &aen);
                if (rv != 0)
-                       printf("%s: error %d while draining response queue\n",
+                       printf("%s: error %d while draining event queue\n",
                            sc->sc_dv.dv_xname, rv);
                if (TWE_AEN_CODE(aen) == TWE_AEN_QUEUE_EMPTY)
                        break;
                if (TWE_AEN_CODE(aen) == TWE_AEN_SOFT_RESET)
                        got = 1;
+               twe_aen_enqueue(sc, aen, 1);
        }
+
        if (!got) {
                printf("%s: reset not reported\n", sc->sc_dv.dv_xname);
                return (-1);
@@ -649,18 +657,14 @@
         * state change has occurred.
         */
        if ((status & TWE_STS_ATTN_INTR) != 0) {
-               if ((sc->sc_flags & TWEF_AEN) == 0) {
-                       rv = twe_param_get(sc, TWE_PARAM_AEN,
-                           TWE_PARAM_AEN_UnitCode, 2, twe_aen_handler,
-                           NULL);
-                       if (rv != 0) {
-                               printf("%s: unable to retrieve AEN (%d)\n",
-                                   sc->sc_dv.dv_xname, rv);
-                               twe_outl(sc, TWE_REG_CTL,
-                                   TWE_CTL_CLEAR_ATTN_INTR);
-                       } else
-                               sc->sc_flags |= TWEF_AEN;
-               }
+               rv = twe_param_get(sc, TWE_PARAM_AEN,
+                   TWE_PARAM_AEN_UnitCode, 2, twe_aen_handler,
+                   NULL);
+               if (rv != 0)
+                       printf("%s: unable to retrieve AEN (%d)\n",
+                           sc->sc_dv.dv_xname, rv);
+               else
+                       twe_outl(sc, TWE_REG_CTL, TWE_CTL_CLEAR_ATTN_INTR);
                caught = 1;
        }
 
@@ -694,9 +698,8 @@
 {
        struct twe_softc *sc;
        struct twe_param *tp;
-       const char *str;
-       u_int aen;
-       int i, hu, rv;
+       uint16_t aen;
+       int rv;
 
        sc = (struct twe_softc *)ccb->ccb_tx.tx_dv;
        tp = ccb->ccb_tx.tx_context;
@@ -712,25 +715,10 @@
 
        if (TWE_AEN_CODE(aen) == TWE_AEN_QUEUE_EMPTY) {
                twe_outl(sc, TWE_REG_CTL, TWE_CTL_CLEAR_ATTN_INTR);
-               sc->sc_flags &= ~TWEF_AEN;
                return;
        }
 
-       str = "<unknown>";
-       i = 0;
-       hu = 0;
-               
-       while (i < sizeof(twe_aen_names) / sizeof(twe_aen_names[0])) {
-               if (TWE_AEN_CODE(twe_aen_names[i].aen) == TWE_AEN_CODE(aen)) {
-                       str = twe_aen_names[i].desc;
-                       hu = TWE_AEN_UNIT(twe_aen_names[i].aen);
-                       break;
-               }
-               i++;
-       }
-       printf("%s: ", sc->sc_dv.dv_xname);
-       printf(aenfmt[hu], TWE_AEN_UNIT(aen));
-       printf("AEN 0x%04x (%s) received\n", TWE_AEN_CODE(aen), str);
+       twe_aen_enqueue(sc, aen, 0);
 
        /*
         * Chain another retrieval in case interrupts have been
@@ -743,6 +731,81 @@
                    sc->sc_dv.dv_xname, rv);
 }
 
+static void
+twe_aen_enqueue(struct twe_softc *sc, uint16_t aen, int quiet)
+{
+       const char *str, *msg;
+       int s, next, nextnext;
+
+       /*
+        * First report the AEN on the console.  Maybe.
+        */
+       if (! quiet) {
+               str = twe_describe_code(twe_table_aen, TWE_AEN_CODE(aen));
+               if (str == NULL) {
+                       printf("%s: unknown AEN 0x%04x\n",
+                           sc->sc_dv.dv_xname, aen);
+               } else {
+                       msg = str + 2;
+                       switch (*str) {
+                       case 'u':
+                               printf("%s: unit %d: %s\n",
+                                   sc->sc_dv.dv_xname, TWE_AEN_UNIT(aen), msg);
+                               break;
+
+                       case 'p':
+                               printf("%s: port %d: %s\n",
+                                   sc->sc_dv.dv_xname, TWE_AEN_UNIT(aen), msg);
+                               break;
+
+                       default:
+                               printf("%s: %s\n", sc->sc_dv.dv_xname, msg);
+                       }
+               }
+       }
+
+       /* Now enqueue the AEN for mangement tools. */
+       s = splbio();
+
+       next = (sc->sc_aen_head + 1) % TWE_AEN_Q_LENGTH;
+       nextnext = (sc->sc_aen_head + 2) % TWE_AEN_Q_LENGTH;
+
+       /*
+        * If this is the last free slot, then queue up a "queue
+        * full" message.
+        */
+       if (nextnext == sc->sc_aen_tail)
+               aen = TWE_AEN_QUEUE_FULL;
+
+       if (next != sc->sc_aen_tail) {
+               sc->sc_aen_queue[sc->sc_aen_head] = aen;
+               sc->sc_aen_head = next;
+       }
+
+       if (sc->sc_flags & TWEF_AENQ_WAIT) {
+               sc->sc_flags &= ~TWEF_AENQ_WAIT;
+               wakeup(&sc->sc_aen_queue);
+       }
+
+       splx(s);
+}



Home | Main Index | Thread Index | Old Index