Skip to content

Commit a4e7279

Browse files
oneukumgregkh
authored andcommitted
cdc-acm: introduce a cool down
Immediate submission in case of a babbling device can lead to a busy loop. Introducing a delayed work. Signed-off-by: Oliver Neukum <[email protected]> Cc: stable <[email protected]> Tested-by: Jonas Karlsson <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0afccd7 commit a4e7279

File tree

2 files changed

+32
-3
lines changed

2 files changed

+32
-3
lines changed

drivers/usb/class/cdc-acm.c

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,12 @@ static void acm_ctrl_irq(struct urb *urb)
412412

413413
exit:
414414
retval = usb_submit_urb(urb, GFP_ATOMIC);
415-
if (retval && retval != -EPERM)
415+
if (retval && retval != -EPERM && retval != -ENODEV)
416416
dev_err(&acm->control->dev,
417417
"%s - usb_submit_urb failed: %d\n", __func__, retval);
418+
else
419+
dev_vdbg(&acm->control->dev,
420+
"control resubmission terminated %d\n", retval);
418421
}
419422

420423
static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
@@ -430,6 +433,8 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
430433
dev_err(&acm->data->dev,
431434
"urb %d failed submission with %d\n",
432435
index, res);
436+
} else {
437+
dev_vdbg(&acm->data->dev, "intended failure %d\n", res);
433438
}
434439
set_bit(index, &acm->read_urbs_free);
435440
return res;
@@ -471,6 +476,7 @@ static void acm_read_bulk_callback(struct urb *urb)
471476
int status = urb->status;
472477
bool stopped = false;
473478
bool stalled = false;
479+
bool cooldown = false;
474480

475481
dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
476482
rb->index, urb->actual_length, status);
@@ -497,6 +503,14 @@ static void acm_read_bulk_callback(struct urb *urb)
497503
__func__, status);
498504
stopped = true;
499505
break;
506+
case -EOVERFLOW:
507+
case -EPROTO:
508+
dev_dbg(&acm->data->dev,
509+
"%s - cooling babbling device\n", __func__);
510+
usb_mark_last_busy(acm->dev);
511+
set_bit(rb->index, &acm->urbs_in_error_delay);
512+
cooldown = true;
513+
break;
500514
default:
501515
dev_dbg(&acm->data->dev,
502516
"%s - nonzero urb status received: %d\n",
@@ -518,9 +532,11 @@ static void acm_read_bulk_callback(struct urb *urb)
518532
*/
519533
smp_mb__after_atomic();
520534

521-
if (stopped || stalled) {
535+
if (stopped || stalled || cooldown) {
522536
if (stalled)
523537
schedule_work(&acm->work);
538+
else if (cooldown)
539+
schedule_delayed_work(&acm->dwork, HZ / 2);
524540
return;
525541
}
526542

@@ -567,6 +583,12 @@ static void acm_softint(struct work_struct *work)
567583
}
568584
}
569585

586+
if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) {
587+
for (i = 0; i < ACM_NR; i++)
588+
if (test_and_clear_bit(i, &acm->urbs_in_error_delay))
589+
acm_submit_read_urb(acm, i, GFP_NOIO);
590+
}
591+
570592
if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags))
571593
tty_port_tty_wakeup(&acm->port);
572594
}
@@ -1333,6 +1355,7 @@ static int acm_probe(struct usb_interface *intf,
13331355
acm->readsize = readsize;
13341356
acm->rx_buflimit = num_rx_buf;
13351357
INIT_WORK(&acm->work, acm_softint);
1358+
INIT_DELAYED_WORK(&acm->dwork, acm_softint);
13361359
init_waitqueue_head(&acm->wioctl);
13371360
spin_lock_init(&acm->write_lock);
13381361
spin_lock_init(&acm->read_lock);
@@ -1542,6 +1565,7 @@ static void acm_disconnect(struct usb_interface *intf)
15421565

15431566
acm_kill_urbs(acm);
15441567
cancel_work_sync(&acm->work);
1568+
cancel_delayed_work_sync(&acm->dwork);
15451569

15461570
tty_unregister_device(acm_tty_driver, acm->minor);
15471571

@@ -1584,6 +1608,8 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
15841608

15851609
acm_kill_urbs(acm);
15861610
cancel_work_sync(&acm->work);
1611+
cancel_delayed_work_sync(&acm->dwork);
1612+
acm->urbs_in_error_delay = 0;
15871613

15881614
return 0;
15891615
}

drivers/usb/class/cdc-acm.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,11 @@ struct acm {
109109
# define EVENT_TTY_WAKEUP 0
110110
# define EVENT_RX_STALL 1
111111
# define ACM_THROTTLED 2
112+
# define ACM_ERROR_DELAY 3
113+
unsigned long urbs_in_error_delay; /* these need to be restarted after a delay */
112114
struct usb_cdc_line_coding line; /* bits, stop, parity */
113-
struct work_struct work; /* work queue entry for line discipline waking up */
115+
struct work_struct work; /* work queue entry for various purposes*/
116+
struct delayed_work dwork; /* for cool downs needed in error recovery */
114117
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
115118
unsigned int ctrlout; /* output control lines (DTR, RTS) */
116119
struct async_icount iocount; /* counters for control line changes */

0 commit comments

Comments
 (0)