diff options
author | Johan Hovold <jhovold@gmail.com> | 2014-05-26 13:23:16 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-05-27 18:04:06 -0400 |
commit | 79eed03e77d481b55d85d1cfe5a1636a0d3897fd (patch) | |
tree | 412cc2dd0d0f05f6dbdabfff9aa333d8926c7ebf /drivers/usb/serial | |
parent | 170fad9e22df0063eba0701adb966786d7a4ec5a (diff) |
USB: usb_wwan: fix urb leak at shutdown
The delayed-write queue was never emptied at shutdown (close), something
which could lead to leaked urbs if the port is closed before being
runtime resumed due to a write.
When this happens the output buffer would not drain on close
(closing_wait timeout), and after consecutive opens, writes could be
corrupted with previously buffered data, transfered with reduced
throughput or completely blocked.
Note that unbusy_queued_urb() was simply moved out of CONFIG_PM.
Fixes: 383cedc3bb43 ("USB: serial: full autosuspend support for the
option driver")
Cc: <stable@vger.kernel.org> # v2.6.32
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r-- | drivers/usb/serial/usb_wwan.c | 34 |
1 files changed, 22 insertions, 12 deletions
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 2b8f02696c00..2ab478b55759 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c | |||
@@ -414,12 +414,26 @@ int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port) | |||
414 | } | 414 | } |
415 | EXPORT_SYMBOL(usb_wwan_open); | 415 | EXPORT_SYMBOL(usb_wwan_open); |
416 | 416 | ||
417 | static void unbusy_queued_urb(struct urb *urb, | ||
418 | struct usb_wwan_port_private *portdata) | ||
419 | { | ||
420 | int i; | ||
421 | |||
422 | for (i = 0; i < N_OUT_URB; i++) { | ||
423 | if (urb == portdata->out_urbs[i]) { | ||
424 | clear_bit(i, &portdata->out_busy); | ||
425 | break; | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | |||
417 | void usb_wwan_close(struct usb_serial_port *port) | 430 | void usb_wwan_close(struct usb_serial_port *port) |
418 | { | 431 | { |
419 | int i; | 432 | int i; |
420 | struct usb_serial *serial = port->serial; | 433 | struct usb_serial *serial = port->serial; |
421 | struct usb_wwan_port_private *portdata; | 434 | struct usb_wwan_port_private *portdata; |
422 | struct usb_wwan_intf_private *intfdata = port->serial->private; | 435 | struct usb_wwan_intf_private *intfdata = port->serial->private; |
436 | struct urb *urb; | ||
423 | 437 | ||
424 | portdata = usb_get_serial_port_data(port); | 438 | portdata = usb_get_serial_port_data(port); |
425 | 439 | ||
@@ -428,6 +442,14 @@ void usb_wwan_close(struct usb_serial_port *port) | |||
428 | portdata->opened = 0; | 442 | portdata->opened = 0; |
429 | spin_unlock_irq(&intfdata->susp_lock); | 443 | spin_unlock_irq(&intfdata->susp_lock); |
430 | 444 | ||
445 | for (;;) { | ||
446 | urb = usb_get_from_anchor(&portdata->delayed); | ||
447 | if (!urb) | ||
448 | break; | ||
449 | unbusy_queued_urb(urb, portdata); | ||
450 | usb_autopm_put_interface_async(serial->interface); | ||
451 | } | ||
452 | |||
431 | for (i = 0; i < N_IN_URB; i++) | 453 | for (i = 0; i < N_IN_URB; i++) |
432 | usb_kill_urb(portdata->in_urbs[i]); | 454 | usb_kill_urb(portdata->in_urbs[i]); |
433 | for (i = 0; i < N_OUT_URB; i++) | 455 | for (i = 0; i < N_OUT_URB; i++) |
@@ -596,18 +618,6 @@ int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message) | |||
596 | } | 618 | } |
597 | EXPORT_SYMBOL(usb_wwan_suspend); | 619 | EXPORT_SYMBOL(usb_wwan_suspend); |
598 | 620 | ||
599 | static void unbusy_queued_urb(struct urb *urb, struct usb_wwan_port_private *portdata) | ||
600 | { | ||
601 | int i; | ||
602 | |||
603 | for (i = 0; i < N_OUT_URB; i++) { | ||
604 | if (urb == portdata->out_urbs[i]) { | ||
605 | clear_bit(i, &portdata->out_busy); | ||
606 | break; | ||
607 | } | ||
608 | } | ||
609 | } | ||
610 | |||
611 | static void play_delayed(struct usb_serial_port *port) | 621 | static void play_delayed(struct usb_serial_port *port) |
612 | { | 622 | { |
613 | struct usb_wwan_intf_private *data; | 623 | struct usb_wwan_intf_private *data; |