| From 6dd433e6cf2475ce8abec1b467720858c24450eb Mon Sep 17 00:00:00 2001 |
| From: Oliver Neukum <oneukum@suse.de> |
| Date: Tue, 6 Aug 2013 14:22:59 +0200 |
| Subject: USB: cdc-wdm: fix race between interrupt handler and tasklet |
| |
| From: Oliver Neukum <oneukum@suse.de> |
| |
| commit 6dd433e6cf2475ce8abec1b467720858c24450eb upstream. |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/class/cdc-wdm.c | 13 +++++++++---- |
| 1 file changed, 9 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/usb/class/cdc-wdm.c |
| +++ b/drivers/usb/class/cdc-wdm.c |
| @@ -209,6 +209,7 @@ skip_error: |
| static void wdm_int_callback(struct urb *urb) |
| { |
| int rv = 0; |
| + int responding; |
| int status = urb->status; |
| struct wdm_device *desc; |
| struct usb_cdc_notification *dr; |
| @@ -262,8 +263,8 @@ static void wdm_int_callback(struct urb |
| |
| spin_lock(&desc->iuspin); |
| clear_bit(WDM_READ, &desc->flags); |
| - set_bit(WDM_RESPONDING, &desc->flags); |
| - if (!test_bit(WDM_DISCONNECTING, &desc->flags) |
| + responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); |
| + if (!responding && !test_bit(WDM_DISCONNECTING, &desc->flags) |
| && !test_bit(WDM_SUSPENDING, &desc->flags)) { |
| rv = usb_submit_urb(desc->response, GFP_ATOMIC); |
| dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", |
| @@ -685,16 +686,20 @@ static void wdm_rxwork(struct work_struc |
| { |
| struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); |
| unsigned long flags; |
| - int rv; |
| + int rv = 0; |
| + int responding; |
| |
| spin_lock_irqsave(&desc->iuspin, flags); |
| if (test_bit(WDM_DISCONNECTING, &desc->flags)) { |
| spin_unlock_irqrestore(&desc->iuspin, flags); |
| } else { |
| + responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); |
| spin_unlock_irqrestore(&desc->iuspin, flags); |
| - rv = usb_submit_urb(desc->response, GFP_KERNEL); |
| + if (!responding) |
| + rv = usb_submit_urb(desc->response, GFP_KERNEL); |
| if (rv < 0 && rv != -EPERM) { |
| spin_lock_irqsave(&desc->iuspin, flags); |
| + clear_bit(WDM_RESPONDING, &desc->flags); |
| if (!test_bit(WDM_DISCONNECTING, &desc->flags)) |
| schedule_work(&desc->rxwork); |
| spin_unlock_irqrestore(&desc->iuspin, flags); |