aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hovold <jhovold@gmail.com>2014-05-26 13:23:38 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-27 18:04:09 -0400
commit140cb81ac8c625942a1d695875932c615767a526 (patch)
treee4e3139dda91615156dc52524b5f4e23d85ac456
parente144ed28bed10684f9aaec6325ed974d53f76110 (diff)
USB: cdc-acm: fix broken runtime suspend
The current ACM runtime-suspend implementation is broken in several ways: Firstly, it buffers only the first write request being made while suspended -- any further writes are silently dropped. Secondly, writes being dropped also leak write urbs, which are never reclaimed (until the device is unbound). Thirdly, even the single buffered write is not cleared at shutdown (which may happen before the device is resumed), something which can lead to another urb leak as well as a PM usage-counter leak. Fix this by implementing a delayed-write queue using urb anchors and making sure to discard the queue properly at shutdown. Fixes: 11ea859d64b6 ("USB: additional power savings for cdc-acm devices that support remote wakeup") Reported-by: Xiao Jin <jin.xiao@intel.com> Cc: <stable@vger.kernel.org> # v2.6.27 Signed-off-by: Johan Hovold <jhovold@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/class/cdc-acm.c32
-rw-r--r--drivers/usb/class/cdc-acm.h2
2 files changed, 23 insertions, 11 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index e72a657a6177..56419254ef75 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -573,6 +573,8 @@ static void acm_port_destruct(struct tty_port *port)
573static void acm_port_shutdown(struct tty_port *port) 573static void acm_port_shutdown(struct tty_port *port)
574{ 574{
575 struct acm *acm = container_of(port, struct acm, port); 575 struct acm *acm = container_of(port, struct acm, port);
576 struct urb *urb;
577 struct acm_wb *wb;
576 int i; 578 int i;
577 579
578 dev_dbg(&acm->control->dev, "%s\n", __func__); 580 dev_dbg(&acm->control->dev, "%s\n", __func__);
@@ -581,6 +583,16 @@ static void acm_port_shutdown(struct tty_port *port)
581 if (!acm->disconnected) { 583 if (!acm->disconnected) {
582 usb_autopm_get_interface(acm->control); 584 usb_autopm_get_interface(acm->control);
583 acm_set_control(acm, acm->ctrlout = 0); 585 acm_set_control(acm, acm->ctrlout = 0);
586
587 for (;;) {
588 urb = usb_get_from_anchor(&acm->delayed);
589 if (!urb)
590 break;
591 wb = urb->context;
592 wb->use = 0;
593 usb_autopm_put_interface_async(acm->control);
594 }
595
584 usb_kill_urb(acm->ctrlurb); 596 usb_kill_urb(acm->ctrlurb);
585 for (i = 0; i < ACM_NW; i++) 597 for (i = 0; i < ACM_NW; i++)
586 usb_kill_urb(acm->wb[i].urb); 598 usb_kill_urb(acm->wb[i].urb);
@@ -648,12 +660,9 @@ static int acm_tty_write(struct tty_struct *tty,
648 660
649 usb_autopm_get_interface_async(acm->control); 661 usb_autopm_get_interface_async(acm->control);
650 if (acm->susp_count) { 662 if (acm->susp_count) {
651 if (!acm->delayed_wb) 663 usb_anchor_urb(wb->urb, &acm->delayed);
652 acm->delayed_wb = wb;
653 else
654 usb_autopm_put_interface_async(acm->control);
655 spin_unlock_irqrestore(&acm->write_lock, flags); 664 spin_unlock_irqrestore(&acm->write_lock, flags);
656 return count; /* A white lie */ 665 return count;
657 } 666 }
658 usb_mark_last_busy(acm->dev); 667 usb_mark_last_busy(acm->dev);
659 668
@@ -1269,6 +1278,7 @@ made_compressed_probe:
1269 acm->bInterval = epread->bInterval; 1278 acm->bInterval = epread->bInterval;
1270 tty_port_init(&acm->port); 1279 tty_port_init(&acm->port);
1271 acm->port.ops = &acm_port_ops; 1280 acm->port.ops = &acm_port_ops;
1281 init_usb_anchor(&acm->delayed);
1272 1282
1273 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); 1283 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
1274 if (!buf) { 1284 if (!buf) {
@@ -1539,7 +1549,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1539static int acm_resume(struct usb_interface *intf) 1549static int acm_resume(struct usb_interface *intf)
1540{ 1550{
1541 struct acm *acm = usb_get_intfdata(intf); 1551 struct acm *acm = usb_get_intfdata(intf);
1542 struct acm_wb *wb; 1552 struct urb *urb;
1543 int rv = 0; 1553 int rv = 0;
1544 1554
1545 spin_lock_irq(&acm->read_lock); 1555 spin_lock_irq(&acm->read_lock);
@@ -1551,10 +1561,12 @@ static int acm_resume(struct usb_interface *intf)
1551 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) { 1561 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
1552 rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC); 1562 rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
1553 1563
1554 if (acm->delayed_wb) { 1564 for (;;) {
1555 wb = acm->delayed_wb; 1565 urb = usb_get_from_anchor(&acm->delayed);
1556 acm->delayed_wb = NULL; 1566 if (!urb)
1557 acm_start_wb(acm, wb); 1567 break;
1568
1569 acm_start_wb(acm, urb->context);
1558 } 1570 }
1559 1571
1560 /* 1572 /*
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index e38dc785808f..80826f843e04 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -120,7 +120,7 @@ struct acm {
120 unsigned int throttled:1; /* actually throttled */ 120 unsigned int throttled:1; /* actually throttled */
121 unsigned int throttle_req:1; /* throttle requested */ 121 unsigned int throttle_req:1; /* throttle requested */
122 u8 bInterval; 122 u8 bInterval;
123 struct acm_wb *delayed_wb; /* write queued for a device about to be woken */ 123 struct usb_anchor delayed; /* writes queued for a device about to be woken */
124}; 124};
125 125
126#define CDC_DATA_INTERFACE_TYPE 0x0a 126#define CDC_DATA_INTERFACE_TYPE 0x0a