| From 6ff10464096540e14d7575a72c50d0316d003714 Mon Sep 17 00:00:00 2001 |
| From: Alan Stern <stern@rowland.harvard.edu> |
| Date: Mon, 9 Mar 2009 13:44:02 -0400 |
| Subject: USB: usbfs: keep async URBs until the device file is closed |
| |
| From: Alan Stern <stern@rowland.harvard.edu> |
| |
| commit 6ff10464096540e14d7575a72c50d0316d003714 upstream. |
| |
| The usbfs driver manages a list of completed asynchronous URBs. But |
| it is too eager to free the entries on this list: destroy_async() gets |
| called whenever an interface is unbound or a device is removed, and it |
| deallocates the outstanding struct async entries for all URBs on that |
| interface or device. This is wrong; the user program should be able |
| to reap an URB any time after it has completed, regardless of whether |
| or not the interface is still bound or the device is still present. |
| |
| This patch (as1222) moves the code for deallocating the completed list |
| entries from destroy_async() to usbdev_release(). The outstanding |
| entries won't be freed until the user program has closed the device |
| file, thereby eliminating any possibility that the remaining URBs |
| might still be reaped. |
| |
| This fixes a bug in which a program can hang in the USBDEVFS_REAPURB |
| ioctl when the device is unplugged. |
| |
| Reported-and-tested-by: Martin Poupe <martin.poupe@upek.com> |
| Signed-off-by: Alan Stern <stern@rowland.harvard.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| drivers/usb/core/devio.c | 12 +++++++----- |
| 1 file changed, 7 insertions(+), 5 deletions(-) |
| |
| --- a/drivers/usb/core/devio.c |
| +++ b/drivers/usb/core/devio.c |
| @@ -359,11 +359,6 @@ static void destroy_async(struct dev_sta |
| spin_lock_irqsave(&ps->lock, flags); |
| } |
| spin_unlock_irqrestore(&ps->lock, flags); |
| - as = async_getcompleted(ps); |
| - while (as) { |
| - free_async(as); |
| - as = async_getcompleted(ps); |
| - } |
| } |
| |
| static void destroy_async_on_interface(struct dev_state *ps, |
| @@ -642,6 +637,7 @@ static int usbdev_release(struct inode * |
| struct dev_state *ps = file->private_data; |
| struct usb_device *dev = ps->dev; |
| unsigned int ifnum; |
| + struct async *as; |
| |
| usb_lock_device(dev); |
| |
| @@ -660,6 +656,12 @@ static int usbdev_release(struct inode * |
| usb_unlock_device(dev); |
| usb_put_dev(dev); |
| put_pid(ps->disc_pid); |
| + |
| + as = async_getcompleted(ps); |
| + while (as) { |
| + free_async(as); |
| + as = async_getcompleted(ps); |
| + } |
| kfree(ps); |
| return 0; |
| } |