Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <oneukum@suse.com>
Cc: stable <stable@vger.kernel.org>
Tested-by: Jonas Karlsson <jonas.karlsson@actia.se>
Link: https://lore.kernel.org/r/20200415151358.32664-2-oneukum@suse.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Oliver Neukum and committed by
Greg Kroah-Hartman
a4e7279c 0afccd76

+32 -3
+28 -2
drivers/usb/class/cdc-acm.c
··· 412 412 413 413 exit: 414 414 retval = usb_submit_urb(urb, GFP_ATOMIC); 415 - if (retval && retval != -EPERM) 415 + if (retval && retval != -EPERM && retval != -ENODEV) 416 416 dev_err(&acm->control->dev, 417 417 "%s - usb_submit_urb failed: %d\n", __func__, retval); 418 + else 419 + dev_vdbg(&acm->control->dev, 420 + "control resubmission terminated %d\n", retval); 418 421 } 419 422 420 423 static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) ··· 433 430 dev_err(&acm->data->dev, 434 431 "urb %d failed submission with %d\n", 435 432 index, res); 433 + } else { 434 + dev_vdbg(&acm->data->dev, "intended failure %d\n", res); 436 435 } 437 436 set_bit(index, &acm->read_urbs_free); 438 437 return res; ··· 476 471 int status = urb->status; 477 472 bool stopped = false; 478 473 bool stalled = false; 474 + bool cooldown = false; 479 475 480 476 dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n", 481 477 rb->index, urb->actual_length, status); ··· 503 497 __func__, status); 504 498 stopped = true; 505 499 break; 500 + case -EOVERFLOW: 501 + case -EPROTO: 502 + dev_dbg(&acm->data->dev, 503 + "%s - cooling babbling device\n", __func__); 504 + usb_mark_last_busy(acm->dev); 505 + set_bit(rb->index, &acm->urbs_in_error_delay); 506 + cooldown = true; 507 + break; 506 508 default: 507 509 dev_dbg(&acm->data->dev, 508 510 "%s - nonzero urb status received: %d\n", ··· 532 518 */ 533 519 smp_mb__after_atomic(); 534 520 535 - if (stopped || stalled) { 521 + if (stopped || stalled || cooldown) { 536 522 if (stalled) 537 523 schedule_work(&acm->work); 524 + else if (cooldown) 525 + schedule_delayed_work(&acm->dwork, HZ / 2); 538 526 return; 539 527 } 540 528 ··· 581 565 acm_submit_read_urbs(acm, GFP_KERNEL); 582 566 clear_bit(EVENT_RX_STALL, &acm->flags); 583 567 } 568 + } 569 + 570 + if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) { 571 + for (i = 0; i < ACM_NR; i++) 572 + if (test_and_clear_bit(i, &acm->urbs_in_error_delay)) 573 + acm_submit_read_urb(acm, i, GFP_NOIO); 584 574 } 585 575 586 576 if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags)) ··· 1355 1333 acm->readsize = readsize; 1356 1334 acm->rx_buflimit = num_rx_buf; 1357 1335 INIT_WORK(&acm->work, acm_softint); 1336 + INIT_DELAYED_WORK(&acm->dwork, acm_softint); 1358 1337 init_waitqueue_head(&acm->wioctl); 1359 1338 spin_lock_init(&acm->write_lock); 1360 1339 spin_lock_init(&acm->read_lock); ··· 1565 1542 1566 1543 acm_kill_urbs(acm); 1567 1544 cancel_work_sync(&acm->work); 1545 + cancel_delayed_work_sync(&acm->dwork); 1568 1546 1569 1547 tty_unregister_device(acm_tty_driver, acm->minor); 1570 1548 ··· 1608 1584 1609 1585 acm_kill_urbs(acm); 1610 1586 cancel_work_sync(&acm->work); 1587 + cancel_delayed_work_sync(&acm->dwork); 1588 + acm->urbs_in_error_delay = 0; 1611 1589 1612 1590 return 0; 1613 1591 }
+4 -1
drivers/usb/class/cdc-acm.h
··· 109 109 # define EVENT_TTY_WAKEUP 0 110 110 # define EVENT_RX_STALL 1 111 111 # 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 */ 112 114 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 */ 114 117 unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ 115 118 unsigned int ctrlout; /* output control lines (DTR, RTS) */ 116 119 struct async_icount iocount; /* counters for control line changes */