usb: cdc-wdm: make reset work with blocking IO
authorBjørn Mork <bjorn@mork.no>
Fri, 10 Feb 2012 08:44:08 +0000 (09:44 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Feb 2012 19:28:18 +0000 (11:28 -0800)
Add a flag to tell wdm_read/wdm_write that a reset is in progress,
and wake any blocking read/write before taking the mutexes.  This
allows the device to reset without waiting for blocking IO to
finish.

Signed-off-by: Bjørn Mork <bjorn@mork.no>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/class/cdc-wdm.c

index f63601a..b27bbb9 100644 (file)
@@ -70,6 +70,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
 #define WDM_POLL_RUNNING       6
 #define WDM_RESPONDING         7
 #define WDM_SUSPENDING         8
+#define WDM_RESETTING          9
 
 #define WDM_MAX                        16
 
@@ -340,6 +341,10 @@ static ssize_t wdm_write
        else
                if (test_bit(WDM_IN_USE, &desc->flags))
                        r = -EAGAIN;
+
+       if (test_bit(WDM_RESETTING, &desc->flags))
+               r = -EIO;
+
        if (r < 0) {
                kfree(buf);
                goto out;
@@ -419,6 +424,10 @@ retry:
                        rv = -ENODEV;
                        goto err;
                }
+               if (test_bit(WDM_RESETTING, &desc->flags)) {
+                       rv = -EIO;
+                       goto err;
+               }
                usb_mark_last_busy(interface_to_usbdev(desc->intf));
                if (rv < 0) {
                        rv = -ERESTARTSYS;
@@ -859,10 +868,6 @@ static int wdm_pre_reset(struct usb_interface *intf)
 {
        struct wdm_device *desc = usb_get_intfdata(intf);
 
-       mutex_lock(&desc->rlock);
-       mutex_lock(&desc->wlock);
-       kill_urbs(desc);
-
        /*
         * we notify everybody using poll of
         * an exceptional situation
@@ -870,9 +875,16 @@ static int wdm_pre_reset(struct usb_interface *intf)
         * message from the device is lost
         */
        spin_lock_irq(&desc->iuspin);
+       set_bit(WDM_RESETTING, &desc->flags);   /* inform read/write */
+       set_bit(WDM_READ, &desc->flags);        /* unblock read */
+       clear_bit(WDM_IN_USE, &desc->flags);    /* unblock write */
        desc->rerr = -EINTR;
        spin_unlock_irq(&desc->iuspin);
        wake_up_all(&desc->wait);
+       mutex_lock(&desc->rlock);
+       mutex_lock(&desc->wlock);
+       kill_urbs(desc);
+       cancel_work_sync(&desc->rxwork);
        return 0;
 }
 
@@ -881,6 +893,7 @@ static int wdm_post_reset(struct usb_interface *intf)
        struct wdm_device *desc = usb_get_intfdata(intf);
        int rv;
 
+       clear_bit(WDM_RESETTING, &desc->flags);
        rv = recover_from_urb_loss(desc);
        mutex_unlock(&desc->wlock);
        mutex_unlock(&desc->rlock);