diff options
author | Oliver Neukum <oneukum@suse.de> | 2013-08-06 08:22:59 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-12 18:41:07 -0400 |
commit | 6dd433e6cf2475ce8abec1b467720858c24450eb (patch) | |
tree | fc3359d4bd68a1216835b3ca12221f8632ed602b /drivers/usb/class/cdc-wdm.c | |
parent | e9a088fae5e4f9be729f3f419627abc3886b09de (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.c | 13 |
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: | |||
209 | static void wdm_int_callback(struct urb *urb) | 209 | static 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); |