Commit 6ff10464 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: usbfs: keep async URBs until the device file is closed

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: default avatarMartin Poupe <martin.poupe@upek.com>
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 228dd05d
...@@ -359,11 +359,6 @@ static void destroy_async(struct dev_state *ps, struct list_head *list) ...@@ -359,11 +359,6 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
spin_lock_irqsave(&ps->lock, flags); spin_lock_irqsave(&ps->lock, flags);
} }
spin_unlock_irqrestore(&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, static void destroy_async_on_interface(struct dev_state *ps,
...@@ -643,6 +638,7 @@ static int usbdev_release(struct inode *inode, struct file *file) ...@@ -643,6 +638,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
struct dev_state *ps = file->private_data; struct dev_state *ps = file->private_data;
struct usb_device *dev = ps->dev; struct usb_device *dev = ps->dev;
unsigned int ifnum; unsigned int ifnum;
struct async *as;
usb_lock_device(dev); usb_lock_device(dev);
...@@ -661,6 +657,12 @@ static int usbdev_release(struct inode *inode, struct file *file) ...@@ -661,6 +657,12 @@ static int usbdev_release(struct inode *inode, struct file *file)
usb_unlock_device(dev); usb_unlock_device(dev);
usb_put_dev(dev); usb_put_dev(dev);
put_pid(ps->disc_pid); put_pid(ps->disc_pid);
as = async_getcompleted(ps);
while (as) {
free_async(as);
as = async_getcompleted(ps);
}
kfree(ps); kfree(ps);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment