Always allow abort tasks to be scheduled, even if the device is beeing
authormpi <mpi@openbsd.org>
Tue, 13 Jan 2015 16:03:18 +0000 (16:03 +0000)
committermpi <mpi@openbsd.org>
Tue, 13 Jan 2015 16:03:18 +0000 (16:03 +0000)
detached, in order to prevent a deadlock situation.

This situation can occur if the thread detaching a device is sleeping,
waiting for all submitted transfers to finish, and the device's pipes
have not yet been aborted.  This can happen when a USB Ethernet device
is being detached while a userland program is doing an ioctl(2).

Abort tasks need to be able to run in such case since timed out transfers
rely on them to be properly completed.

ok deraadt@

sys/dev/usb/usb.c

index ab8ce99..ed8502a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: usb.c,v 1.103 2014/12/18 10:44:17 mpi Exp $   */
+/*     $OpenBSD: usb.c,v 1.104 2015/01/13 16:03:18 mpi Exp $   */
 /*     $NetBSD: usb.c,v 1.77 2003/01/01 00:10:26 thorpej Exp $ */
 
 /*
@@ -298,8 +298,13 @@ usb_add_task(struct usbd_device *dev, struct usb_task *task)
 {
        int s;
 
-       /* Don't add task if the device's root hub is dying. */
-       if (usbd_is_dying(dev))
+       /*
+        * If the thread detaching ``dev'' is sleeping, waiting
+        * for all submitted transfers to finish, we must be able
+        * to enqueue abort tasks.  Otherwise timeouts can't give
+        * back submitted transfers to the stack.
+        */
+       if (usbd_is_dying(dev) && (task->type != USB_TASK_TYPE_ABORT))
                return;
 
        DPRINTFN(2,("%s: task=%p state=%d type=%d\n", __func__, task,
@@ -455,12 +460,9 @@ usb_abort_task_thread(void *arg)
                 */
                task->state |= USB_TASK_STATE_RUN;
                task->state &= ~USB_TASK_STATE_ONQ;
-               /* Don't actually execute the task if dying. */
-               if (!usbd_is_dying(task->dev)) {
-                       splx(s);
-                       task->fun(task->arg);
-                       s = splusb();
-               }
+               splx(s);
+               task->fun(task->arg);
+               s = splusb();
                task->state &= ~USB_TASK_STATE_RUN;
                if (task->state == USB_TASK_STATE_NONE)
                        wakeup(task);