summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2016-06-10 21:42:55 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-08-09 17:14:18 +0300
commit5cce438298a0d2a7a857a4a3c3e26aeb8f77b941 (patch)
tree8b2d2e4d73f6063f76842d6814caf6649942862f /drivers
parent70f7ca9a0262784d0b80727860a63d64ab228e7b (diff)
downloadlinux-5cce438298a0d2a7a857a4a3c3e26aeb8f77b941.tar.xz
USB: remove race condition in usbfs/libusb when using reap-after-disconnect
Hans de Goede has reported a difficulty in the Linux port of libusb. When a device is removed, the poll() system call in usbfs starts returning POLLERR as soon as udev->state is set to USB_STATE_NOTATTACHED, but the outstanding URBs are not available for reaping until some time later (after usbdev_remove() has been called). This is awkward for libusb or other usbfs clients, although not an insuperable problem. At any rate, it's easy to change usbfs so that it returns POLLHUP as soon as the state becomes USB_STATE_NOTATTACHED but it doesn't return POLLERR until after the outstanding URBs have completed. That's what this patch does; it uses the fact that ps->list is always on the dev->filelist list until usbdev_remove() takes it off, which happens after all the outstanding URBs have been cancelled. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/devio.c4
1 files changed, 3 insertions, 1 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 472cbcdf7456..e6a6d67c8705 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -2583,7 +2583,9 @@ static unsigned int usbdev_poll(struct file *file,
if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
mask |= POLLOUT | POLLWRNORM;
if (!connected(ps))
- mask |= POLLERR | POLLHUP;
+ mask |= POLLHUP;
+ if (list_empty(&ps->list))
+ mask |= POLLERR;
return mask;
}