From 7dd55ac10c63ea55f61d7117e121042bb4f5a59a Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Tue, 23 Jun 2009 22:25:07 -0700 Subject: [PATCH] usb - More interrupt livelock fixes. Fix additional interrupt livelocks which occur during initialization due to interrupts being enabled too early. --- sys/bus/usb/ehci.c | 16 ++++++++++------ sys/bus/usb/ehci_pci.c | 4 +--- sys/bus/usb/ohci.c | 21 +++++++++++++++++++-- sys/bus/usb/ohci_pci.c | 10 +++++++--- sys/bus/usb/uhci.c | 22 +++++++++++++++++----- sys/bus/usb/uhci_pci.c | 4 +--- 6 files changed, 55 insertions(+), 22 deletions(-) diff --git a/sys/bus/usb/ehci.c b/sys/bus/usb/ehci.c index 39e92b4a6e..0574718173 100644 --- a/sys/bus/usb/ehci.c +++ b/sys/bus/usb/ehci.c @@ -471,9 +471,6 @@ ehci_init(ehci_softc_t *sc) lockinit(&sc->sc_doorbell_lock, "ehcidb", 0, 0); - /* Enable interrupts */ - EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); - /* Turn on controller */ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_ITC_2 | /* 2 microframes interrupt delay */ @@ -496,6 +493,12 @@ ehci_init(ehci_softc_t *sc) return (USBD_IOERROR); } + crit_enter(); + sc->sc_flags |= EHCI_SCFLG_DONEINIT; + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + ehci_intr(sc); + crit_exit(); + return (USBD_NORMAL_COMPLETION); #if 0 @@ -856,11 +859,12 @@ ehci_detach(struct ehci_softc *sc, int flags) { int rv = 0; + crit_enter(); sc->sc_dying = 1; - - EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + EOWRITE4(sc, EHCI_USBINTR, 0); EOWRITE4(sc, EHCI_USBCMD, 0); EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); + crit_exit(); callout_stop(&sc->sc_tmo_intrlist); usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */ @@ -1959,7 +1963,7 @@ ehci_disown(ehci_softc_t *sc, int index, int lowspeed) else device_printf(sc->sc_bus.bdev, "handing over %s speed device on port %d to %s\n", - lowspeed ? "low" : "full", + (lowspeed ? "low" : "full"), index, device_get_nameunit(sc->sc_comps[i]->bdev)); } else { device_printf(sc->sc_bus.bdev, "npcomp == 0\n"); diff --git a/sys/bus/usb/ehci_pci.c b/sys/bus/usb/ehci_pci.c index 2fd6aa1928..4707575113 100644 --- a/sys/bus/usb/ehci_pci.c +++ b/sys/bus/usb/ehci_pci.c @@ -429,10 +429,8 @@ ehci_pci_attach(device_t self) ehci_pci_takecontroller(self); err = ehci_init(sc); - if (!err) { - sc->sc_flags |= EHCI_SCFLG_DONEINIT; + if (err == 0) err = device_probe_and_attach(sc->sc_bus.bdev); - } if (err) { device_printf(self, "USB init failed err=%d\n", err); diff --git a/sys/bus/usb/ohci.c b/sys/bus/usb/ohci.c index 23116ebcdc..1c0703188a 100644 --- a/sys/bus/usb/ohci.c +++ b/sys/bus/usb/ohci.c @@ -330,12 +330,14 @@ ohci_detach(struct ohci_softc *sc, int flags) { int i, rv = 0; + crit_enter(); sc->sc_dying = 1; callout_stop(&sc->sc_tmo_rhsc); OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); + crit_exit(); usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */ @@ -748,6 +750,18 @@ ohci_init(ohci_softc_t *sc) callout_init(&sc->sc_tmo_rhsc); + /* + * Finish up w/ interlocked done flag (the interrupt handler could + * be called due to other shared interrupts), enable interrupts, + * and run the handler in case a pending interrupt got cleared + * before we finished. + */ + crit_enter(); + sc->sc_flags |= OHCI_SCFLG_DONEINIT; + OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs); + ohci_intr(sc); + crit_exit(); + return (USBD_NORMAL_COMPLETION); bad5: @@ -879,7 +893,9 @@ ohci_controller_init(ohci_softc_t *sc) /* * Enable desired interrupts */ - OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE); + sc->sc_eintrs |= OHCI_MIE; + if (sc->sc_flags & OHCI_SCFLG_DONEINIT) + OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs); #ifdef USB_DEBUG if (ohcidebug > 5) @@ -1202,7 +1218,8 @@ ohci_rhsc_able(ohci_softc_t *sc, int on) DPRINTFN(4, ("ohci_rhsc_able: on=%d\n", on)); if (on) { sc->sc_eintrs |= OHCI_RHSC; - OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC); + if (sc->sc_flags & OHCI_SCFLG_DONEINIT) + OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC); } else { sc->sc_eintrs &= ~OHCI_RHSC; OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC); diff --git a/sys/bus/usb/ohci_pci.c b/sys/bus/usb/ohci_pci.c index 8ea1a35a92..df38511e4e 100644 --- a/sys/bus/usb/ohci_pci.c +++ b/sys/bus/usb/ohci_pci.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include @@ -321,11 +322,14 @@ ohci_pci_attach(device_t self) ohci_pci_detach(self); return ENXIO; } + + /* + * OHCI interrupts which occur early will leave them disabled, + * so run the interrupt manually once we're done with the init. + */ err = ohci_init(sc); - if (!err) { - sc->sc_flags |= OHCI_SCFLG_DONEINIT; + if (err == 0) err = device_probe_and_attach(sc->sc_bus.bdev); - } if (err) { device_printf(self, "USB init failed\n"); diff --git a/sys/bus/usb/uhci.c b/sys/bus/usb/uhci.c index cd7fb3f65a..97e3da3f36 100644 --- a/sys/bus/usb/uhci.c +++ b/sys/bus/usb/uhci.c @@ -506,10 +506,19 @@ uhci_init(uhci_softc_t *sc) UHCICMD(sc, UHCI_CMD_MAXP); /* Assume 64 byte packets at frame end */ DPRINTFN(1,("uhci_init: enabling\n")); - UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | - UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* enable interrupts */ - return (uhci_run(sc, 1)); /* and here we go... */ + err = uhci_run(sc, 1); /* and here we go... */ + + if (err == 0) { + crit_enter(); + sc->sc_flags |= UHCI_SCFLG_DONEINIT; + UWRITE2(sc, UHCI_INTR, + UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | + UHCI_INTR_IOCE | UHCI_INTR_SPIE); + uhci_intr(sc); + crit_exit(); + } + return (err); } int @@ -676,8 +685,11 @@ uhci_power(int why, void *v) UHCICMD(sc, cmd | UHCI_CMD_FGR); /* force global resume */ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY); UHCICMD(sc, cmd & ~UHCI_CMD_EGSM); /* back to normal */ - UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | - UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* re-enable intrs */ + + /* re-enable intrs */ + UWRITE2(sc, UHCI_INTR, + UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | + UHCI_INTR_IOCE | UHCI_INTR_SPIE); UHCICMD(sc, UHCI_CMD_MAXP); uhci_run(sc, 1); /* and start traffic again */ usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY); diff --git a/sys/bus/usb/uhci_pci.c b/sys/bus/usb/uhci_pci.c index fe51f6449b..b00f028248 100644 --- a/sys/bus/usb/uhci_pci.c +++ b/sys/bus/usb/uhci_pci.c @@ -422,10 +422,8 @@ uhci_pci_attach(device_t self) pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2); err = uhci_init(sc); - if (!err) { - sc->sc_flags |= UHCI_SCFLG_DONEINIT; + if (err == 0) err = device_probe_and_attach(sc->sc_bus.bdev); - } if (err) { device_printf(self, "USB init failed\n"); -- 2.41.0