summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormpi <mpi@openbsd.org>2014-05-28 11:20:55 +0000
committermpi <mpi@openbsd.org>2014-05-28 11:20:55 +0000
commitd7fb47546222cf4514e93c2ab60a85be97c2b540 (patch)
tree6e0e5f2c787f5358d97ccc8088fae2650a938b3c
parentremove an errant semicolon. (diff)
downloadwireguard-openbsd-d7fb47546222cf4514e93c2ab60a85be97c2b540.tar.xz
wireguard-openbsd-d7fb47546222cf4514e93c2ab60a85be97c2b540.zip
Apart from the early exploration done to find a console keyboard during
the boot process, USB devices must be attached or detached from the usb task thread in order to avoid races with periodical explorations issued by uhub(4) interrupts. Respect this rule when detaching root hubs during a suspend/resume cycle and avoid some hangs due to the aforementioned race. Tested by Mattieu Baptiste, thanks!
-rw-r--r--sys/dev/usb/uhub.c5
-rw-r--r--sys/dev/usb/usb.c36
-rw-r--r--sys/dev/usb/usbdivar.h3
3 files changed, 27 insertions, 17 deletions
diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c
index 2ef5e6f8d9e..b5974247a7f 100644
--- a/sys/dev/usb/uhub.c
+++ b/sys/dev/usb/uhub.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uhub.c,v 1.66 2014/03/11 10:24:42 mpi Exp $ */
+/* $OpenBSD: uhub.c,v 1.67 2014/05/28 11:20:55 mpi Exp $ */
/* $NetBSD: uhub.c,v 1.64 2003/02/08 03:32:51 ichiro Exp $ */
/* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $ */
@@ -546,6 +546,9 @@ uhub_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
{
struct uhub_softc *sc = addr;
+ if (usbd_is_dying(sc->sc_hub))
+ return;
+
DPRINTF("%s: intr status=%d\n", sc->sc_dev.dv_xname, status);
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->sc_ipipe);
diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c
index ff5c3b3d2a5..2397a2f308f 100644
--- a/sys/dev/usb/usb.c
+++ b/sys/dev/usb/usb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: usb.c,v 1.96 2014/05/11 16:33:21 mpi Exp $ */
+/* $OpenBSD: usb.c,v 1.97 2014/05/28 11:20:55 mpi Exp $ */
/* $NetBSD: usb.c,v 1.77 2003/01/01 00:10:26 thorpej Exp $ */
/*
@@ -260,11 +260,15 @@ usb_attach_roothub(struct usb_softc *sc)
void
usb_detach_roothub(struct usb_softc *sc)
{
- /* Make all devices disconnect. */
- if (sc->sc_port.device != NULL)
- usb_disconnect_port(&sc->sc_port, (struct device *)sc);
+ /*
+ * To avoid races with the usb task thread, mark the root hub
+ * as disconnecting and schedule an exploration task to detach
+ * it.
+ */
+ sc->sc_bus->flags |= USB_BUS_DISCONNECTING;
+ usb_needs_explore(sc->sc_bus->root_hub, 0);
- usb_rem_wait_task(sc->sc_bus->root_hub, &sc->sc_explore_task);
+ usb_wait_task(sc->sc_bus->root_hub, &sc->sc_explore_task);
sc->sc_bus->root_hub = NULL;
}
@@ -840,7 +844,18 @@ usb_explore(void *v)
usb_delay_ms(sc->sc_bus, pwrdly - waited_ms);
}
- sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
+ if (sc->sc_bus->flags & USB_BUS_DISCONNECTING) {
+ /* Prevent new tasks from being scheduled. */
+ sc->sc_bus->dying = 1;
+
+ /* Make all devices disconnect. */
+ if (sc->sc_port.device != NULL)
+ usb_disconnect_port(&sc->sc_port, (struct device *)sc);
+
+ sc->sc_bus->flags &= ~USB_BUS_DISCONNECTING;
+ } else {
+ sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
+ }
if (sc->sc_bus->flags & USB_BUS_CONFIG_PENDING) {
DPRINTF(("%s: %s: first explore done\n", __func__,
@@ -896,7 +911,6 @@ usb_activate(struct device *self, int act)
switch (act) {
case DVACT_QUIESCE:
- sc->sc_bus->dying = 1;
if (sc->sc_bus->root_hub != NULL)
usb_detach_roothub(sc);
break;
@@ -914,10 +928,6 @@ usb_activate(struct device *self, int act)
usb_needs_explore(sc->sc_bus->root_hub, 0);
sc->sc_bus->use_polling--;
break;
- case DVACT_DEACTIVATE:
- rv = config_activate_children(self, act);
- sc->sc_bus->dying = 1;
- break;
default:
rv = config_activate_children(self, act);
break;
@@ -930,10 +940,6 @@ usb_detach(struct device *self, int flags)
{
struct usb_softc *sc = (struct usb_softc *)self;
- DPRINTF(("usb_detach: start\n"));
-
- sc->sc_bus->dying = 1;
-
if (sc->sc_bus->root_hub != NULL) {
usb_detach_roothub(sc);
diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h
index ab2b9742e83..7cbeaafbe53 100644
--- a/sys/dev/usb/usbdivar.h
+++ b/sys/dev/usb/usbdivar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: usbdivar.h,v 1.58 2014/03/29 18:09:31 guenther Exp $ */
+/* $OpenBSD: usbdivar.h,v 1.59 2014/05/28 11:20:55 mpi Exp $ */
/* $NetBSD: usbdivar.h,v 1.70 2002/07/11 21:14:36 augustss Exp $ */
/* $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.11 1999/11/17 22:33:51 n_hibma Exp $ */
@@ -105,6 +105,7 @@ struct usbd_bus {
char dying;
int flags;
#define USB_BUS_CONFIG_PENDING 0x01
+#define USB_BUS_DISCONNECTING 0x02
struct device *usbctl;
struct usb_device_stats stats;
int intr_context;