diff options
author | David Herrmann <dh.herrmann@googlemail.com> | 2011-10-26 05:13:13 -0400 |
---|---|---|
committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-10-31 15:51:37 -0400 |
commit | fc501ad7a10a356819505b1e526079d47fdebc2c (patch) | |
tree | 3eae55cf4945ac834e40c5cde95555b610084ded /drivers/bluetooth/bcm203x.c | |
parent | 0ba22bb258341783939ed07bf7e7a9d92f3b0edf (diff) |
Bluetooth: bcm203x: Fix race condition on disconnect
When disconnecting a bcm203x device we kill and destroy the usb-urb, however,
there might still be a pending work-structure which resubmits the now invalid
urb. To avoid this race condition, we simply set a shutdown-flag and
synchronously kill the worker first.
This also adds a comment to all schedule_work()s, as it is really not clear
that they are used as replacement for short timers (which can be seen in the git
history).
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
Diffstat (limited to 'drivers/bluetooth/bcm203x.c')
-rw-r--r-- | drivers/bluetooth/bcm203x.c | 10 |
1 files changed, 10 insertions, 0 deletions
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c index 8b1b643a519b..ec743c2ddf9d 100644 --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | 26 | ||
27 | #include <linux/atomic.h> | ||
27 | #include <linux/kernel.h> | 28 | #include <linux/kernel.h> |
28 | #include <linux/init.h> | 29 | #include <linux/init.h> |
29 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
@@ -65,6 +66,7 @@ struct bcm203x_data { | |||
65 | unsigned long state; | 66 | unsigned long state; |
66 | 67 | ||
67 | struct work_struct work; | 68 | struct work_struct work; |
69 | atomic_t shutdown; | ||
68 | 70 | ||
69 | struct urb *urb; | 71 | struct urb *urb; |
70 | unsigned char *buffer; | 72 | unsigned char *buffer; |
@@ -97,6 +99,7 @@ static void bcm203x_complete(struct urb *urb) | |||
97 | 99 | ||
98 | data->state = BCM203X_SELECT_MEMORY; | 100 | data->state = BCM203X_SELECT_MEMORY; |
99 | 101 | ||
102 | /* use workqueue to have a small delay */ | ||
100 | schedule_work(&data->work); | 103 | schedule_work(&data->work); |
101 | break; | 104 | break; |
102 | 105 | ||
@@ -155,6 +158,9 @@ static void bcm203x_work(struct work_struct *work) | |||
155 | struct bcm203x_data *data = | 158 | struct bcm203x_data *data = |
156 | container_of(work, struct bcm203x_data, work); | 159 | container_of(work, struct bcm203x_data, work); |
157 | 160 | ||
161 | if (atomic_read(&data->shutdown)) | ||
162 | return; | ||
163 | |||
158 | if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0) | 164 | if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0) |
159 | BT_ERR("Can't submit URB"); | 165 | BT_ERR("Can't submit URB"); |
160 | } | 166 | } |
@@ -243,6 +249,7 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id | |||
243 | 249 | ||
244 | usb_set_intfdata(intf, data); | 250 | usb_set_intfdata(intf, data); |
245 | 251 | ||
252 | /* use workqueue to have a small delay */ | ||
246 | schedule_work(&data->work); | 253 | schedule_work(&data->work); |
247 | 254 | ||
248 | return 0; | 255 | return 0; |
@@ -254,6 +261,9 @@ static void bcm203x_disconnect(struct usb_interface *intf) | |||
254 | 261 | ||
255 | BT_DBG("intf %p", intf); | 262 | BT_DBG("intf %p", intf); |
256 | 263 | ||
264 | atomic_inc(&data->shutdown); | ||
265 | cancel_work_sync(&data->work); | ||
266 | |||
257 | usb_kill_urb(data->urb); | 267 | usb_kill_urb(data->urb); |
258 | 268 | ||
259 | usb_set_intfdata(intf, NULL); | 269 | usb_set_intfdata(intf, NULL); |