diff options
| author | thorpej <thorpej@NetBSD.org> | 2003-09-22 01:13:02 +0000 |
|---|---|---|
| committer | thorpej <thorpej@NetBSD.org> | 2003-09-22 01:13:02 +0000 |
| commit | 0448a55234b6f2d367d295b101d49faa557ff66e (patch) | |
| tree | d6c86dea248dfbbe0f327262c773b7a28c36fe61 /sys/dev | |
| parent | 3a95f8617920e2f15180213aed133b6b93b568c5 (diff) | |
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 (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/pci/twe.c | 247 | ||||
| -rw-r--r-- | sys/dev/pci/twevar.h | 13 |
2 files changed, 169 insertions, 91 deletions
diff --git a/sys/dev/pci/twe.c b/sys/dev/pci/twe.c index 18ba1e0d7f1..3fa83ac53b0 100644 --- a/sys/dev/pci/twe.c +++ b/sys/dev/pci/twe.c @@ -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 @@ __KERNEL_RCSID(0, "$NetBSD: twe.c,v 1.44 2003/09/21 19:46:44 thorpej Exp $"); #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 @@ const struct twe_code_table twe_table_stripedepth[] = { { 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 @@ twe_describe_code(const struct twe_code_table *table, uint32_t code) 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_reset(struct twe_softc *sc) 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 @@ twe_intr(void *arg) * 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 @@ twe_aen_handler(struct twe_ccb *ccb, int error) { 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 @@ twe_aen_handler(struct twe_ccb *ccb, int error) 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 @@ twe_aen_handler(struct twe_ccb *ccb, int error) 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); +} + +/* NOTE: Must be called at splbio(). */ +static uint16_t +twe_aen_dequeue(struct twe_softc *sc) +{ + uint16_t aen; + + if (sc->sc_aen_tail == sc->sc_aen_head) + aen = TWE_AEN_QUEUE_EMPTY; + else { + aen = sc->sc_aen_queue[sc->sc_aen_tail]; + sc->sc_aen_tail = (sc->sc_aen_tail + 1) & TWE_AEN_Q_LENGTH; + } + + return (aen); +} + /* * These are short-hand functions that execute TWE_OP_GET_PARAM to * fetch 1, 2, and 4 byte parameter values, respectively. @@ -1408,14 +1471,22 @@ tweioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) return (ENOENT); case TWEIO_AEN_POLL: - if ((twe->sc_flags & TWEF_AEN) == 0) - return (ENOENT); + s = splbio(); + *(u_int *)data = twe_aen_dequeue(twe); + splx(s); return (0); case TWEIO_AEN_WAIT: s = splbio(); - while ((twe->sc_flags & TWEF_AEN) == 0) { - /* tsleep(); */ + while ((*(u_int *)data = + twe_aen_dequeue(twe)) == TWE_AEN_QUEUE_EMPTY) { + twe->sc_flags |= TWEF_AENQ_WAIT; + error = tsleep(&twe->sc_aen_queue, PRIBIO | PCATCH, + "tweaen", 0); + if (error == EINTR) { + splx(s); + return (error); + } } splx(s); return (0); diff --git a/sys/dev/pci/twevar.h b/sys/dev/pci/twevar.h index 3bddb4b204b..286d45d8838 100644 --- a/sys/dev/pci/twevar.h +++ b/sys/dev/pci/twevar.h @@ -1,4 +1,4 @@ -/* $NetBSD: twevar.h,v 1.16 2003/09/21 19:27:28 thorpej Exp $ */ +/* $NetBSD: twevar.h,v 1.17 2003/09/22 01:13:03 thorpej Exp $ */ /*- * Copyright (c) 2000, 2001, 2002 The NetBSD Foundation, Inc. @@ -69,9 +69,15 @@ struct twe_softc { int sc_flags; int sc_nunits; struct twe_drive sc_units[TWE_MAX_UNITS]; + + /* Asynchronous event notification queue for management tools. */ +#define TWE_AEN_Q_LENGTH 256 + uint16_t sc_aen_queue[TWE_AEN_Q_LENGTH]; + int sc_aen_head; + int sc_aen_tail; }; -#define TWEF_AEN 0x01 /* retrieving an AEN */ -#define TWEF_OPEN 0x02 /* control device is opened */ +#define TWEF_OPEN 0x01 /* control device is opened */ +#define TWEF_AENQ_WAIT 0x02 /* someone waiting for AENs */ /* Optional per-command context. */ struct twe_context { @@ -146,6 +152,7 @@ extern const struct twe_code_table twe_table_status[]; extern const struct twe_code_table twe_table_unitstate[]; extern const struct twe_code_table twe_table_unittype[]; extern const struct twe_code_table twe_table_stripedepth[]; +extern const struct twe_code_table twe_table_aen[]; const char *twe_describe_code(const struct twe_code_table *, uint32_t); |
