From: David Herrmann Date: Wed, 26 Oct 2011 09:13:13 +0000 (+0200) Subject: Bluetooth: bcm203x: Fix race condition on disconnect X-Git-Url: https://openfabrics.org/gitweb/?a=commitdiff_plain;h=fc501ad7a10a356819505b1e526079d47fdebc2c;p=~shefty%2Frdma-dev.git 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 Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c index 8b1b643a519..ec743c2ddf9 100644 --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -65,6 +66,7 @@ struct bcm203x_data { unsigned long state; struct work_struct work; + atomic_t shutdown; struct urb *urb; unsigned char *buffer; @@ -97,6 +99,7 @@ static void bcm203x_complete(struct urb *urb) data->state = BCM203X_SELECT_MEMORY; + /* use workqueue to have a small delay */ schedule_work(&data->work); break; @@ -155,6 +158,9 @@ static void bcm203x_work(struct work_struct *work) struct bcm203x_data *data = container_of(work, struct bcm203x_data, work); + if (atomic_read(&data->shutdown)) + return; + if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0) BT_ERR("Can't submit URB"); } @@ -243,6 +249,7 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id usb_set_intfdata(intf, data); + /* use workqueue to have a small delay */ schedule_work(&data->work); return 0; @@ -254,6 +261,9 @@ static void bcm203x_disconnect(struct usb_interface *intf) BT_DBG("intf %p", intf); + atomic_inc(&data->shutdown); + cancel_work_sync(&data->work); + usb_kill_urb(data->urb); usb_set_intfdata(intf, NULL);