diff options
author | Oliver Neukum <oliver@neukum.org> | 2010-02-27 14:54:59 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-03-19 10:24:15 -0400 |
commit | 922a5eadd5a3aa0b806be0c18694b618d41d0784 (patch) | |
tree | 25d6a630e5bd92ff0c900d94125481f53e9b377a /drivers/usb/class | |
parent | 860e41a71c1731e79e1920dc42676bafc925af5e (diff) |
usb: cdc-wdm: Fix race between autosuspend and reading from the device
While an available response is read the device must not
be autosuspended. This requires a flag dedicated to that
purpose.
Signed-off-by: Oliver Neukum <neukum@b1-systems.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/class')
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 14 |
1 files changed, 10 insertions, 4 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index cf1c5fb918dc..940b17af162c 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c | |||
@@ -52,6 +52,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); | |||
52 | #define WDM_READ 4 | 52 | #define WDM_READ 4 |
53 | #define WDM_INT_STALL 5 | 53 | #define WDM_INT_STALL 5 |
54 | #define WDM_POLL_RUNNING 6 | 54 | #define WDM_POLL_RUNNING 6 |
55 | #define WDM_RESPONDING 7 | ||
55 | 56 | ||
56 | 57 | ||
57 | #define WDM_MAX 16 | 58 | #define WDM_MAX 16 |
@@ -115,21 +116,22 @@ static void wdm_in_callback(struct urb *urb) | |||
115 | int status = urb->status; | 116 | int status = urb->status; |
116 | 117 | ||
117 | spin_lock(&desc->iuspin); | 118 | spin_lock(&desc->iuspin); |
119 | clear_bit(WDM_RESPONDING, &desc->flags); | ||
118 | 120 | ||
119 | if (status) { | 121 | if (status) { |
120 | switch (status) { | 122 | switch (status) { |
121 | case -ENOENT: | 123 | case -ENOENT: |
122 | dev_dbg(&desc->intf->dev, | 124 | dev_dbg(&desc->intf->dev, |
123 | "nonzero urb status received: -ENOENT"); | 125 | "nonzero urb status received: -ENOENT"); |
124 | break; | 126 | goto skip_error; |
125 | case -ECONNRESET: | 127 | case -ECONNRESET: |
126 | dev_dbg(&desc->intf->dev, | 128 | dev_dbg(&desc->intf->dev, |
127 | "nonzero urb status received: -ECONNRESET"); | 129 | "nonzero urb status received: -ECONNRESET"); |
128 | break; | 130 | goto skip_error; |
129 | case -ESHUTDOWN: | 131 | case -ESHUTDOWN: |
130 | dev_dbg(&desc->intf->dev, | 132 | dev_dbg(&desc->intf->dev, |
131 | "nonzero urb status received: -ESHUTDOWN"); | 133 | "nonzero urb status received: -ESHUTDOWN"); |
132 | break; | 134 | goto skip_error; |
133 | case -EPIPE: | 135 | case -EPIPE: |
134 | dev_err(&desc->intf->dev, | 136 | dev_err(&desc->intf->dev, |
135 | "nonzero urb status received: -EPIPE\n"); | 137 | "nonzero urb status received: -EPIPE\n"); |
@@ -145,6 +147,7 @@ static void wdm_in_callback(struct urb *urb) | |||
145 | desc->reslength = urb->actual_length; | 147 | desc->reslength = urb->actual_length; |
146 | memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); | 148 | memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); |
147 | desc->length += desc->reslength; | 149 | desc->length += desc->reslength; |
150 | skip_error: | ||
148 | wake_up(&desc->wait); | 151 | wake_up(&desc->wait); |
149 | 152 | ||
150 | set_bit(WDM_READ, &desc->flags); | 153 | set_bit(WDM_READ, &desc->flags); |
@@ -227,6 +230,7 @@ static void wdm_int_callback(struct urb *urb) | |||
227 | desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 230 | desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
228 | spin_lock(&desc->iuspin); | 231 | spin_lock(&desc->iuspin); |
229 | clear_bit(WDM_READ, &desc->flags); | 232 | clear_bit(WDM_READ, &desc->flags); |
233 | set_bit(WDM_RESPONDING, &desc->flags); | ||
230 | if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { | 234 | if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { |
231 | rv = usb_submit_urb(desc->response, GFP_ATOMIC); | 235 | rv = usb_submit_urb(desc->response, GFP_ATOMIC); |
232 | dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", | 236 | dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", |
@@ -234,6 +238,7 @@ static void wdm_int_callback(struct urb *urb) | |||
234 | } | 238 | } |
235 | spin_unlock(&desc->iuspin); | 239 | spin_unlock(&desc->iuspin); |
236 | if (rv < 0) { | 240 | if (rv < 0) { |
241 | clear_bit(WDM_RESPONDING, &desc->flags); | ||
237 | if (rv == -EPERM) | 242 | if (rv == -EPERM) |
238 | return; | 243 | return; |
239 | if (rv == -ENOMEM) { | 244 | if (rv == -ENOMEM) { |
@@ -795,7 +800,8 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) | |||
795 | mutex_lock(&desc->lock); | 800 | mutex_lock(&desc->lock); |
796 | #ifdef CONFIG_PM | 801 | #ifdef CONFIG_PM |
797 | if ((message.event & PM_EVENT_AUTO) && | 802 | if ((message.event & PM_EVENT_AUTO) && |
798 | test_bit(WDM_IN_USE, &desc->flags)) { | 803 | (test_bit(WDM_IN_USE, &desc->flags) |
804 | || test_bit(WDM_RESPONDING, &desc->flags))) { | ||
799 | rv = -EBUSY; | 805 | rv = -EBUSY; |
800 | } else { | 806 | } else { |
801 | #endif | 807 | #endif |