summaryrefslogtreecommitdiff
path: root/drivers/usb/class
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2015-03-20 11:24:24 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-03-26 12:50:52 +0300
commit36e59e0d70d6150e7a2155c54612ea875e88ce8d (patch)
tree210a79c37d9a262b1d6e8f37db2429c1c21edef7 /drivers/usb/class
parentc0ab6bb0597363532f178f3cd7b7fb527eef39e2 (diff)
downloadlinux-36e59e0d70d6150e7a2155c54612ea875e88ce8d.tar.xz
cdc-acm: fix race between callback and unthrottle
Abn URB may be may marked free only after the buffer has been processed or there is a small window during which it could be submitted on another CPU and overwrite an unprocessed buffer Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/class')
-rw-r--r--drivers/usb/class/cdc-acm.c12
1 files changed, 10 insertions, 2 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 683617714e7c..58241ec1e91d 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -417,25 +417,33 @@ static void acm_read_bulk_callback(struct urb *urb)
struct acm_rb *rb = urb->context;
struct acm *acm = rb->instance;
unsigned long flags;
+ int status = urb->status;
dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
rb->index, urb->actual_length);
- set_bit(rb->index, &acm->read_urbs_free);
if (!acm->dev) {
+ set_bit(rb->index, &acm->read_urbs_free);
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
return;
}
if (urb->status) {
+ set_bit(rb->index, &acm->read_urbs_free);
dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
- __func__, urb->status);
+ __func__, status);
return;
}
usb_mark_last_busy(acm->dev);
acm_process_read_urb(acm, urb);
+ /*
+ * Unthrottle may run on another CPU which needs to see events
+ * in the same order. Submission has an implict barrier
+ */
+ smp_mb__before_atomic();
+ set_bit(rb->index, &acm->read_urbs_free);
/* throttle device if requested by tty */
spin_lock_irqsave(&acm->read_lock, flags);