aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2015-03-20 04:24:24 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-03-26 05:50:52 -0400
commit36e59e0d70d6150e7a2155c54612ea875e88ce8d (patch)
tree210a79c37d9a262b1d6e8f37db2429c1c21edef7
parentc0ab6bb0597363532f178f3cd7b7fb527eef39e2 (diff)
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>
-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)
417 struct acm_rb *rb = urb->context; 417 struct acm_rb *rb = urb->context;
418 struct acm *acm = rb->instance; 418 struct acm *acm = rb->instance;
419 unsigned long flags; 419 unsigned long flags;
420 int status = urb->status;
420 421
421 dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, 422 dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
422 rb->index, urb->actual_length); 423 rb->index, urb->actual_length);
423 set_bit(rb->index, &acm->read_urbs_free);
424 424
425 if (!acm->dev) { 425 if (!acm->dev) {
426 set_bit(rb->index, &acm->read_urbs_free);
426 dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); 427 dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
427 return; 428 return;
428 } 429 }
429 430
430 if (urb->status) { 431 if (urb->status) {
432 set_bit(rb->index, &acm->read_urbs_free);
431 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", 433 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
432 __func__, urb->status); 434 __func__, status);
433 return; 435 return;
434 } 436 }
435 437
436 usb_mark_last_busy(acm->dev); 438 usb_mark_last_busy(acm->dev);
437 439
438 acm_process_read_urb(acm, urb); 440 acm_process_read_urb(acm, urb);
441 /*
442 * Unthrottle may run on another CPU which needs to see events
443 * in the same order. Submission has an implict barrier
444 */
445 smp_mb__before_atomic();
446 set_bit(rb->index, &acm->read_urbs_free);
439 447
440 /* throttle device if requested by tty */ 448 /* throttle device if requested by tty */
441 spin_lock_irqsave(&acm->read_lock, flags); 449 spin_lock_irqsave(&acm->read_lock, flags);