diff options
| author | Alan Stern <stern@rowland.harvard.edu> | 2015-01-29 11:29:13 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <greg@kroah.com> | 2015-01-31 12:05:06 -0500 |
| commit | 3f2cee73b650921b2e214bf487b2061a1c266504 (patch) | |
| tree | d1dddbfd38a52fc0b66b7fca36dc8ad8efb671ca | |
| parent | 17136d4984b37d2a9acb67e1da1a3e95e3fed918 (diff) | |
USB: usbfs: allow URBs to be reaped after disconnection
The usbfs API has a peculiar hole: Users are not allowed to reap their
URBs after the device has been disconnected. There doesn't seem to be
any good reason for this; it is an ad-hoc inconsistency.
The patch allows users to issue the USBDEVFS_REAPURB and
USBDEVFS_REAPURBNDELAY ioctls (together with their 32-bit counterparts
on 64-bit systems) even after the device is gone. If no URBs are
pending for a disconnected device then the ioctls will return -ENODEV
rather than -EAGAIN, because obviously no new URBs will ever be able
to complete.
The patch also adds a new capability flag for
USBDEVFS_GET_CAPABILITIES to indicate that the reap-after-disconnect
feature is supported.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Tested-by: Chris Dickens <christopher.a.dickens@gmail.com>
Acked-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
| -rw-r--r-- | drivers/usb/core/devio.c | 63 | ||||
| -rw-r--r-- | include/uapi/linux/usbdevice_fs.h | 3 |
2 files changed, 39 insertions, 27 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 0b59731c3021..66abdbcfbfa5 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c | |||
| @@ -1689,7 +1689,7 @@ static struct async *reap_as(struct usb_dev_state *ps) | |||
| 1689 | for (;;) { | 1689 | for (;;) { |
| 1690 | __set_current_state(TASK_INTERRUPTIBLE); | 1690 | __set_current_state(TASK_INTERRUPTIBLE); |
| 1691 | as = async_getcompleted(ps); | 1691 | as = async_getcompleted(ps); |
| 1692 | if (as) | 1692 | if (as || !connected(ps)) |
| 1693 | break; | 1693 | break; |
| 1694 | if (signal_pending(current)) | 1694 | if (signal_pending(current)) |
| 1695 | break; | 1695 | break; |
| @@ -1712,7 +1712,7 @@ static int proc_reapurb(struct usb_dev_state *ps, void __user *arg) | |||
| 1712 | } | 1712 | } |
| 1713 | if (signal_pending(current)) | 1713 | if (signal_pending(current)) |
| 1714 | return -EINTR; | 1714 | return -EINTR; |
| 1715 | return -EIO; | 1715 | return -ENODEV; |
| 1716 | } | 1716 | } |
| 1717 | 1717 | ||
| 1718 | static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg) | 1718 | static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg) |
| @@ -1721,10 +1721,11 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg) | |||
| 1721 | struct async *as; | 1721 | struct async *as; |
| 1722 | 1722 | ||
| 1723 | as = async_getcompleted(ps); | 1723 | as = async_getcompleted(ps); |
| 1724 | retval = -EAGAIN; | ||
| 1725 | if (as) { | 1724 | if (as) { |
| 1726 | retval = processcompl(as, (void __user * __user *)arg); | 1725 | retval = processcompl(as, (void __user * __user *)arg); |
| 1727 | free_async(as); | 1726 | free_async(as); |
| 1727 | } else { | ||
| 1728 | retval = (connected(ps) ? -EAGAIN : -ENODEV); | ||
| 1728 | } | 1729 | } |
| 1729 | return retval; | 1730 | return retval; |
| 1730 | } | 1731 | } |
| @@ -1854,7 +1855,7 @@ static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg) | |||
| 1854 | } | 1855 | } |
| 1855 | if (signal_pending(current)) | 1856 | if (signal_pending(current)) |
| 1856 | return -EINTR; | 1857 | return -EINTR; |
| 1857 | return -EIO; | 1858 | return -ENODEV; |
| 1858 | } | 1859 | } |
| 1859 | 1860 | ||
| 1860 | static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg) | 1861 | static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg) |
| @@ -1862,11 +1863,12 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar | |||
| 1862 | int retval; | 1863 | int retval; |
| 1863 | struct async *as; | 1864 | struct async *as; |
| 1864 | 1865 | ||
| 1865 | retval = -EAGAIN; | ||
| 1866 | as = async_getcompleted(ps); | 1866 | as = async_getcompleted(ps); |
| 1867 | if (as) { | 1867 | if (as) { |
| 1868 | retval = processcompl_compat(as, (void __user * __user *)arg); | 1868 | retval = processcompl_compat(as, (void __user * __user *)arg); |
| 1869 | free_async(as); | 1869 | free_async(as); |
| 1870 | } else { | ||
| 1871 | retval = (connected(ps) ? -EAGAIN : -ENODEV); | ||
| 1870 | } | 1872 | } |
| 1871 | return retval; | 1873 | return retval; |
| 1872 | } | 1874 | } |
| @@ -2038,7 +2040,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg) | |||
| 2038 | { | 2040 | { |
| 2039 | __u32 caps; | 2041 | __u32 caps; |
| 2040 | 2042 | ||
| 2041 | caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; | 2043 | caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM | |
| 2044 | USBDEVFS_CAP_REAP_AFTER_DISCONNECT; | ||
| 2042 | if (!ps->dev->bus->no_stop_on_short) | 2045 | if (!ps->dev->bus->no_stop_on_short) |
| 2043 | caps |= USBDEVFS_CAP_BULK_CONTINUATION; | 2046 | caps |= USBDEVFS_CAP_BULK_CONTINUATION; |
| 2044 | if (ps->dev->bus->sg_tablesize) | 2047 | if (ps->dev->bus->sg_tablesize) |
| @@ -2138,6 +2141,32 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, | |||
| 2138 | return -EPERM; | 2141 | return -EPERM; |
| 2139 | 2142 | ||
| 2140 | usb_lock_device(dev); | 2143 | usb_lock_device(dev); |
| 2144 | |||
| 2145 | /* Reap operations are allowed even after disconnection */ | ||
| 2146 | switch (cmd) { | ||
| 2147 | case USBDEVFS_REAPURB: | ||
| 2148 | snoop(&dev->dev, "%s: REAPURB\n", __func__); | ||
| 2149 | ret = proc_reapurb(ps, p); | ||
| 2150 | goto done; | ||
| 2151 | |||
| 2152 | case USBDEVFS_REAPURBNDELAY: | ||
| 2153 | snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__); | ||
| 2154 | ret = proc_reapurbnonblock(ps, p); | ||
| 2155 | goto done; | ||
| 2156 | |||
| 2157 | #ifdef CONFIG_COMPAT | ||
| 2158 | case USBDEVFS_REAPURB32: | ||
| 2159 | snoop(&dev->dev, "%s: REAPURB32\n", __func__); | ||
| 2160 | ret = proc_reapurb_compat(ps, p); | ||
| 2161 | goto done; | ||
| 2162 | |||
| 2163 | case USBDEVFS_REAPURBNDELAY32: | ||
| 2164 | snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__); | ||
| 2165 | ret = proc_reapurbnonblock_compat(ps, p); | ||
| 2166 | goto done; | ||
| 2167 | #endif | ||
| 2168 | } | ||
| 2169 | |||
| 2141 | if (!connected(ps)) { | 2170 | if (!connected(ps)) { |
| 2142 | usb_unlock_device(dev); | 2171 | usb_unlock_device(dev); |
| 2143 | return -ENODEV; | 2172 | return -ENODEV; |
| @@ -2231,16 +2260,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, | |||
| 2231 | inode->i_mtime = CURRENT_TIME; | 2260 | inode->i_mtime = CURRENT_TIME; |
| 2232 | break; | 2261 | break; |
| 2233 | 2262 | ||
| 2234 | case USBDEVFS_REAPURB32: | ||
| 2235 | snoop(&dev->dev, "%s: REAPURB32\n", __func__); | ||
| 2236 | ret = proc_reapurb_compat(ps, p); | ||
| 2237 | break; | ||
| 2238 | |||
| 2239 | case USBDEVFS_REAPURBNDELAY32: | ||
| 2240 | snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__); | ||
| 2241 | ret = proc_reapurbnonblock_compat(ps, p); | ||
| 2242 | break; | ||
| 2243 | |||
| 2244 | case USBDEVFS_IOCTL32: | 2263 | case USBDEVFS_IOCTL32: |
| 2245 | snoop(&dev->dev, "%s: IOCTL32\n", __func__); | 2264 | snoop(&dev->dev, "%s: IOCTL32\n", __func__); |
| 2246 | ret = proc_ioctl_compat(ps, ptr_to_compat(p)); | 2265 | ret = proc_ioctl_compat(ps, ptr_to_compat(p)); |
| @@ -2252,16 +2271,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, | |||
| 2252 | ret = proc_unlinkurb(ps, p); | 2271 | ret = proc_unlinkurb(ps, p); |
| 2253 | break; | 2272 | break; |
| 2254 | 2273 | ||
| 2255 | case USBDEVFS_REAPURB: | ||
| 2256 | snoop(&dev->dev, "%s: REAPURB\n", __func__); | ||
| 2257 | ret = proc_reapurb(ps, p); | ||
| 2258 | break; | ||
| 2259 | |||
| 2260 | case USBDEVFS_REAPURBNDELAY: | ||
| 2261 | snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__); | ||
| 2262 | ret = proc_reapurbnonblock(ps, p); | ||
| 2263 | break; | ||
| 2264 | |||
| 2265 | case USBDEVFS_DISCSIGNAL: | 2274 | case USBDEVFS_DISCSIGNAL: |
| 2266 | snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__); | 2275 | snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__); |
| 2267 | ret = proc_disconnectsignal(ps, p); | 2276 | ret = proc_disconnectsignal(ps, p); |
| @@ -2304,6 +2313,8 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, | |||
| 2304 | ret = proc_free_streams(ps, p); | 2313 | ret = proc_free_streams(ps, p); |
| 2305 | break; | 2314 | break; |
| 2306 | } | 2315 | } |
| 2316 | |||
| 2317 | done: | ||
| 2307 | usb_unlock_device(dev); | 2318 | usb_unlock_device(dev); |
| 2308 | if (ret >= 0) | 2319 | if (ret >= 0) |
| 2309 | inode->i_atime = CURRENT_TIME; | 2320 | inode->i_atime = CURRENT_TIME; |
diff --git a/include/uapi/linux/usbdevice_fs.h b/include/uapi/linux/usbdevice_fs.h index abe5f4bd4d82..019ba1e0799a 100644 --- a/include/uapi/linux/usbdevice_fs.h +++ b/include/uapi/linux/usbdevice_fs.h | |||
| @@ -128,11 +128,12 @@ struct usbdevfs_hub_portinfo { | |||
| 128 | char port [127]; /* e.g. port 3 connects to device 27 */ | 128 | char port [127]; /* e.g. port 3 connects to device 27 */ |
| 129 | }; | 129 | }; |
| 130 | 130 | ||
| 131 | /* Device capability flags */ | 131 | /* System and bus capability flags */ |
| 132 | #define USBDEVFS_CAP_ZERO_PACKET 0x01 | 132 | #define USBDEVFS_CAP_ZERO_PACKET 0x01 |
| 133 | #define USBDEVFS_CAP_BULK_CONTINUATION 0x02 | 133 | #define USBDEVFS_CAP_BULK_CONTINUATION 0x02 |
| 134 | #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 | 134 | #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 |
| 135 | #define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 | 135 | #define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 |
| 136 | #define USBDEVFS_CAP_REAP_AFTER_DISCONNECT 0x10 | ||
| 136 | 137 | ||
| 137 | /* USBDEVFS_DISCONNECT_CLAIM flags & struct */ | 138 | /* USBDEVFS_DISCONNECT_CLAIM flags & struct */ |
| 138 | 139 | ||
