aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/class/cdc-wdm.c
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2013-08-06 08:22:59 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-12 18:41:07 -0400
commit6dd433e6cf2475ce8abec1b467720858c24450eb (patch)
treefc3359d4bd68a1216835b3ca12221f8632ed602b /drivers/usb/class/cdc-wdm.c
parente9a088fae5e4f9be729f3f419627abc3886b09de (diff)
USB: cdc-wdm: fix race between interrupt handler and tasklet
Both could want to submit the same URB. Some checks of the flag intended to prevent that were missing. Signed-off-by: Oliver Neukum <oneukum@suse.de> CC: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/class/cdc-wdm.c')
-rw-r--r--drivers/usb/class/cdc-wdm.c13
1 files changed, 9 insertions, 4 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 8a230f0ef77c..d3318a0df8ee 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -209,6 +209,7 @@ skip_error:
209static void wdm_int_callback(struct urb *urb) 209static void wdm_int_callback(struct urb *urb)
210{ 210{
211 int rv = 0; 211 int rv = 0;
212 int responding;
212 int status = urb->status; 213 int status = urb->status;
213 struct wdm_device *desc; 214 struct wdm_device *desc;
214 struct usb_cdc_notification *dr; 215 struct usb_cdc_notification *dr;
@@ -262,8 +263,8 @@ static void wdm_int_callback(struct urb *urb)
262 263
263 spin_lock(&desc->iuspin); 264 spin_lock(&desc->iuspin);
264 clear_bit(WDM_READ, &desc->flags); 265 clear_bit(WDM_READ, &desc->flags);
265 set_bit(WDM_RESPONDING, &desc->flags); 266 responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
266 if (!test_bit(WDM_DISCONNECTING, &desc->flags) 267 if (!responding && !test_bit(WDM_DISCONNECTING, &desc->flags)
267 && !test_bit(WDM_SUSPENDING, &desc->flags)) { 268 && !test_bit(WDM_SUSPENDING, &desc->flags)) {
268 rv = usb_submit_urb(desc->response, GFP_ATOMIC); 269 rv = usb_submit_urb(desc->response, GFP_ATOMIC);
269 dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", 270 dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
@@ -685,16 +686,20 @@ static void wdm_rxwork(struct work_struct *work)
685{ 686{
686 struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); 687 struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
687 unsigned long flags; 688 unsigned long flags;
688 int rv; 689 int rv = 0;
690 int responding;
689 691
690 spin_lock_irqsave(&desc->iuspin, flags); 692 spin_lock_irqsave(&desc->iuspin, flags);
691 if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 693 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
692 spin_unlock_irqrestore(&desc->iuspin, flags); 694 spin_unlock_irqrestore(&desc->iuspin, flags);
693 } else { 695 } else {
696 responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
694 spin_unlock_irqrestore(&desc->iuspin, flags); 697 spin_unlock_irqrestore(&desc->iuspin, flags);
695 rv = usb_submit_urb(desc->response, GFP_KERNEL); 698 if (!responding)
699 rv = usb_submit_urb(desc->response, GFP_KERNEL);
696 if (rv < 0 && rv != -EPERM) { 700 if (rv < 0 && rv != -EPERM) {
697 spin_lock_irqsave(&desc->iuspin, flags); 701 spin_lock_irqsave(&desc->iuspin, flags);
702 clear_bit(WDM_RESPONDING, &desc->flags);
698 if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 703 if (!test_bit(WDM_DISCONNECTING, &desc->flags))
699 schedule_work(&desc->rxwork); 704 schedule_work(&desc->rxwork);
700 spin_unlock_irqrestore(&desc->iuspin, flags); 705 spin_unlock_irqrestore(&desc->iuspin, flags);