By delaying root-hub interrupt handling to the soft interrupt we can makr ehci(4)'s interrupt handler as IPL_MPSAFE.
Index: dev/pci/ehci_pci.c =================================================================== RCS file: /cvs/src/sys/dev/pci/ehci_pci.c,v retrieving revision 1.27 diff -u -p -r1.27 ehci_pci.c --- dev/pci/ehci_pci.c 16 May 2014 18:17:03 -0000 1.27 +++ dev/pci/ehci_pci.c 27 Oct 2015 15:39:48 -0000 @@ -171,7 +171,8 @@ ehci_pci_attach(struct device *parent, s goto unmap_ret; } intrstr = pci_intr_string(pc, ih); - sc->sc_ih = pci_intr_establish(pc, ih, IPL_USB, ehci_intr, sc, devname); + sc->sc_ih = pci_intr_establish(pc, ih, IPL_USB | IPL_MPSAFE, + ehci_intr, sc, devname); if (sc->sc_ih == NULL) { printf(": couldn't establish interrupt"); if (intrstr != NULL) @@ -204,7 +205,7 @@ ehci_pci_attach(struct device *parent, s else snprintf(sc->sc.sc_vendor, sizeof(sc->sc.sc_vendor), "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); - + /* Enable workaround for dropped interrupts as required */ if (sc->sc.sc_id_vendor == PCI_VENDOR_VIATECH) sc->sc.sc_flags |= EHCIF_DROPPED_INTR_WORKAROUND; Index: dev/usb/ehci.c =================================================================== RCS file: /cvs/src/sys/dev/usb/ehci.c,v retrieving revision 1.187 diff -u -p -r1.187 ehci.c --- dev/usb/ehci.c 26 Jun 2015 11:17:34 -0000 1.187 +++ dev/usb/ehci.c 27 Oct 2015 15:40:29 -0000 @@ -540,13 +540,12 @@ ehci_intr1(struct ehci_softc *sc) return (0); EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ - sc->sc_bus.intr_context++; sc->sc_bus.no_intrs++; + if (eintrs & EHCI_STS_HSE) { printf("%s: unrecoverable error, controller halted\n", sc->sc_bus.bdev.dv_xname); sc->sc_bus.dying = 1; - sc->sc_bus.intr_context--; return (1); } if (eintrs & EHCI_STS_IAA) { @@ -558,12 +557,11 @@ ehci_intr1(struct ehci_softc *sc) eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT); } if (eintrs & EHCI_STS_PCD) { - ehci_pcd(sc, sc->sc_intrxfer); + atomic_setbits_int(&sc->sc_flags, EHCIF_PCB_INTR); + usb_schedsoftintr(&sc->sc_bus); eintrs &= ~EHCI_STS_PCD; } - sc->sc_bus.intr_context--; - if (eintrs != 0) { /* Block unprocessed interrupts. */ sc->sc_eintrs &= ~eintrs; @@ -644,6 +642,11 @@ ehci_softintr(void *v) return; sc->sc_bus.intr_context++; + + if (sc->sc_flags & EHCIF_PCB_INTR) { + atomic_clearbits_int(&sc->sc_flags, EHCIF_PCB_INTR); + ehci_pcd(sc, sc->sc_intrxfer); + } /* * The only explanation I can think of for why EHCI is as brain dead Index: dev/usb/ehcivar.h =================================================================== RCS file: /cvs/src/sys/dev/usb/ehcivar.h,v retrieving revision 1.35 diff -u -p -r1.35 ehcivar.h --- dev/usb/ehcivar.h 10 Apr 2015 13:56:42 -0000 1.35 +++ dev/usb/ehcivar.h 27 Oct 2015 15:35:26 -0000 @@ -129,6 +129,7 @@ struct ehci_softc { u_int sc_offs; /* offset to operational regs */ int sc_flags; /* misc flags */ #define EHCIF_DROPPED_INTR_WORKAROUND 0x01 +#define EHCIF_PCB_INTR 0x02 char sc_vendor[16]; /* vendor string for root hub */ int sc_id_vendor; /* vendor ID for root hub */