[PATCH v2] usb: cdc-wdm: cannot wait forever for blocking read/write on reset

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



We need to ensure that no URBs are submitted during reset.  Taking any of
the mutexes can deadlock against either wdm_read or wdm_write. Define a
new WDM_RESETTING flag indicating to read/write that a reset is in progress.

Signed-off-by: Bjørn Mork <bjorn@xxxxxxx>
---
Something like this then, maybe?  Now I have absolutely no idea about what 
errors to expect from read and write in this situation, so that may need
fixing. But the overall strategy should work.  Somewhat tested, and it
works better than the current code and does not break anything obvious
for me.


Bjørn


 drivers/usb/class/cdc-wdm.c |   24 ++++++++++++++++++------
 1 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index f63601a..786cd4f 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -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,13 @@ static ssize_t wdm_write
 	else
 		if (test_bit(WDM_IN_USE, &desc->flags))
 			r = -EAGAIN;
+
+	if (test_bit(WDM_RESETTING, &desc->flags)) {
+		kfree(buf);
+		rv = file->f_flags & O_NONBLOCK ? -EAGAIN : -EIO;
+		goto out;
+	}
+
 	if (r < 0) {
 		kfree(buf);
 		goto out;
@@ -419,6 +427,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 +871,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 +878,14 @@ 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);
+	kill_urbs(desc);
+	cancel_work_sync(&desc->rxwork);
 	return 0;
 }
 
@@ -881,9 +894,8 @@ 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);
 	return 0;
 }
 
-- 
1.7.8.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux