diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2009-05-14 21:10:34 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2009-05-14 21:10:34 +0000 |
commit | 8e6ece9578fb915805c3800696ef37c65159828c (patch) | |
tree | 82806925d118f4f3fb6b8ad03bd4ccc259c4e929 | |
parent | 8691bc85cce8d920ca23e2346da52b1133188b02 (diff) |
More interrupt deadlock tomfoolery; turns out the non-XBridge workaround
doesn't appear to trigger the expected interrupt, so use a fugly nanotimeout
instead.
This makes Origin 200 disks not stall as soon as some serious I/O is intended.
They now run multiuser.
-rw-r--r-- | sys/arch/sgi/xbow/xbridge.c | 112 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xbridgereg.h | 5 |
2 files changed, 96 insertions, 21 deletions
diff --git a/sys/arch/sgi/xbow/xbridge.c b/sys/arch/sgi/xbow/xbridge.c index bc655ee5511..b4b38175121 100644 --- a/sys/arch/sgi/xbow/xbridge.c +++ b/sys/arch/sgi/xbow/xbridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: xbridge.c,v 1.16 2009/05/08 18:37:28 miod Exp $ */ +/* $OpenBSD: xbridge.c,v 1.17 2009/05/14 21:10:33 miod Exp $ */ /* * Copyright (c) 2008, 2009 Miodrag Vallat. @@ -27,6 +27,7 @@ #include <sys/evcount.h> #include <sys/malloc.h> #include <sys/extent.h> +#include <sys/timeout.h> #include <machine/atomic.h> #include <machine/autoconf.h> @@ -499,14 +500,24 @@ struct xbridge_intr { struct xbridge_softc *xi_bridge; int xi_intrsrc; /* interrupt source on interrupt widget */ int xi_intrbit; /* interrupt source on BRIDGE */ + int xi_device; /* device slot number */ int (*xi_func)(void *); void *xi_arg; struct evcount xi_count; int xi_level; + + struct timeout xi_tmo; /* XXX deadlock bad workaround */ }; +void xbridge_timeout(void *); /* XXX */ + +/* how our pci_intr_handle_t are constructed... */ +#define XBRIDGE_INTR_HANDLE(d,b) (0x100 | ((d) << 3) | (b)) +#define XBRIDGE_INTR_DEVICE(h) (((h) >> 3) & 07) +#define XBRIDGE_INTR_BIT(h) ((h) & 07) + int xbridge_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp) { @@ -532,7 +543,7 @@ xbridge_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp) else intr = device ^ 4; - *ihp = intr; + *ihp = XBRIDGE_INTR_HANDLE(device, intr); return 0; } @@ -542,7 +553,7 @@ xbridge_intr_string(void *cookie, pci_intr_handle_t ih) { static char str[16]; - snprintf(str, sizeof(str), "irq %d", ih); + snprintf(str, sizeof(str), "irq %d", XBRIDGE_INTR_BIT(ih)); return(str); } @@ -553,7 +564,8 @@ xbridge_intr_establish(void *cookie, pci_intr_handle_t ih, int level, struct xbridge_softc *sc = cookie; struct xbridge_intr *xi; uint32_t int_addr; - int intrbit = ih & 0x07; + int intrbit = XBRIDGE_INTR_BIT(ih); + int device = XBRIDGE_INTR_DEVICE(ih); int intrsrc; if (sc->sc_intr[intrbit] != NULL) { @@ -592,10 +604,12 @@ xbridge_intr_establish(void *cookie, pci_intr_handle_t ih, int level, xi->xi_bridge = sc; xi->xi_intrsrc = intrsrc; xi->xi_intrbit = intrbit; + xi->xi_device = device; xi->xi_func = func; xi->xi_arg = arg; xi->xi_level = level; evcount_attach(&xi->xi_count, name, &xi->xi_level, &evcount_intr); + timeout_set(&xi->xi_tmo, xbridge_timeout, xi); sc->sc_intr[intrbit] = xi; int_addr = ((xbow_intr_widget_register >> 30) & 0x0003ff00) | intrsrc; @@ -605,23 +619,34 @@ xbridge_intr_establish(void *cookie, pci_intr_handle_t ih, int level, bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER, bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER) | (1 << intrbit)); + /* + * INT_MODE register controls which interrupt pins cause + * ``interrupt clear'' packets to be sent for high->low + * transition. + * We do not want such packets to be sent because we clear + * interrupts ourselves and this would cause interrupts to + * be missed. + */ +#if 0 bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE, bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE) | (1 << intrbit)); +#endif bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV, bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV) | - (intrbit << (intrbit * 3))); + (device << (intrbit * 3))); (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH); - return (void *)((uint64_t)ih | 8); /* XXX don't return zero */ + return (void *)((uint64_t)ih); } void -xbridge_intr_disestablish(void *cookie, void *ih) +xbridge_intr_disestablish(void *cookie, void *vih) { struct xbridge_softc *sc = cookie; struct xbridge_intr *xi; - int intrbit = (uint64_t)ih & 0x07; + pci_intr_handle_t ih = (pci_intr_handle_t)(uint64_t)vih; + int intrbit = XBRIDGE_INTR_BIT(ih); /* should not happen */ if ((xi = sc->sc_intr[intrbit]) == NULL) @@ -652,43 +677,90 @@ xbridge_intr_handler(void *v) { struct xbridge_intr *xi = v; struct xbridge_softc *sc = xi->xi_bridge; +#if 0 uint16_t nasid = 0; /* XXX */ +#endif int rc; + int spurious; if (xi == NULL) { - printf("%s: spurious interrupt on source %d\n", - sc->sc_dev.dv_xname, xi->xi_intrsrc); + printf("%s: spurious irq %d\n", + sc->sc_dev.dv_xname, xi->xi_intrbit); return 0; } + if (!ISSET(sc->sc_flags, XBRIDGE_FLAGS_XBRIDGE)) + timeout_del(&xi->xi_tmo); + + /* + * Flush PCI write buffers before servicing the interrupt. + */ + bus_space_read_4(sc->sc_iot, sc->sc_regh, + BRIDGE_DEVICE_WBFLUSH(xi->xi_device)); + + if ((bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_ISR) & + (1 << xi->xi_intrbit)) == 0) { + spurious = 1; +#ifdef DEBUG + printf("%s: irq %d but not pending in ISR %08x\n", + sc->sc_dev.dv_xname, xi->xi_intrbit, + bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_ISR)); +#endif + } else + spurious = 0; + if ((rc = (*xi->xi_func)(xi->xi_arg)) != 0) xi->xi_count.ec_count++; + if (rc == 0 && spurious == 0) + printf("%s: spurious irq %d\n", + sc->sc_dev.dv_xname, xi->xi_intrbit); /* * There is a known BRIDGE race in which, if two interrupts * on two different pins occur within 60nS of each other, - * further interrupts on the first pin do not cause an interrupt - * to be sent. + * further interrupts on the first pin do not cause an + * interrupt to be sent. * - * The workaround against this is to check if our interrupt source - * is still active (i.e. another interrupt is pending), in which - * case we force an interrupt anyway. + * The workaround against this is to check if our interrupt + * source is still active (i.e. another interrupt is pending), + * in which case we force an interrupt anyway. * - * The XBridge even has a nice facility to do this, where we do not - * even have to check if our interrupt is pending. + * The XBridge even has a nice facility to do this, where we + * do not even have to check if our interrupt is pending. */ if (ISSET(sc->sc_flags, XBRIDGE_FLAGS_XBRIDGE)) { bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_FORCE_PIN(xi->xi_intrbit), 1); } else { - if (bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_ISR) & - (1 << xi->xi_intrbit)) + if (bus_space_read_4(sc->sc_iot, sc->sc_regh, + BRIDGE_ISR) & (1 << xi->xi_intrbit)) { +#if 0 + /* XXX This doesn't appear to work */ IP27_RHUB_PI_S(nasid, 0, HUB_IR_CHANGE, HUB_IR_SET | xi->xi_intrsrc); +#else + timeout_add(&xi->xi_tmo, 1); +#endif + } } - return rc; + return 1; +} + +/* + * Bridge (not XBridge) ugly workaround for the interrupt deadlock + * problem mentioned above. + */ +void +xbridge_timeout(void *v) +{ + struct xbridge_intr *xi = v; + struct xbridge_softc *sc = xi->xi_bridge; + + if ((bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_ISR) & + (1 << xi->xi_intrbit)) != 0) + xbridge_intr_handler(v); } /* diff --git a/sys/arch/sgi/xbow/xbridgereg.h b/sys/arch/sgi/xbow/xbridgereg.h index e0a176cce03..9025bac47a0 100644 --- a/sys/arch/sgi/xbow/xbridgereg.h +++ b/sys/arch/sgi/xbow/xbridgereg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: xbridgereg.h,v 1.4 2009/05/06 20:08:47 miod Exp $ */ +/* $OpenBSD: xbridgereg.h,v 1.5 2009/05/14 21:10:33 miod Exp $ */ /* * Copyright (c) 2008 Miodrag Vallat. @@ -108,6 +108,9 @@ #define BRIDGE_DEVIO_SIZE(d) \ ((d) < 2 ? BRIDGE_DEVIO_LARGE : BRIDGE_DEVIO_SHORT) + +#define BRIDGE_DEVICE_WBFLUSH(d) (0x00000244 + 8 * (d)) + /* * Read Response Buffer configuration registers * |