Commit 70c17f4a authored by Alan Stern's avatar Alan Stern Committed by Luis Henriques

USB: usbfs: allow URBs to be reaped after disconnection

commit 3f2cee73 upstream.

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Tested-by: default avatarChris Dickens <christopher.a.dickens@gmail.com>
Acked-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 20267e45
...@@ -1690,7 +1690,7 @@ static struct async *reap_as(struct usb_dev_state *ps) ...@@ -1690,7 +1690,7 @@ static struct async *reap_as(struct usb_dev_state *ps)
for (;;) { for (;;) {
__set_current_state(TASK_INTERRUPTIBLE); __set_current_state(TASK_INTERRUPTIBLE);
as = async_getcompleted(ps); as = async_getcompleted(ps);
if (as) if (as || !connected(ps))
break; break;
if (signal_pending(current)) if (signal_pending(current))
break; break;
...@@ -1713,7 +1713,7 @@ static int proc_reapurb(struct usb_dev_state *ps, void __user *arg) ...@@ -1713,7 +1713,7 @@ static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
} }
if (signal_pending(current)) if (signal_pending(current))
return -EINTR; return -EINTR;
return -EIO; return -ENODEV;
} }
static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg) static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
...@@ -1722,10 +1722,11 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg) ...@@ -1722,10 +1722,11 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
struct async *as; struct async *as;
as = async_getcompleted(ps); as = async_getcompleted(ps);
retval = -EAGAIN;
if (as) { if (as) {
retval = processcompl(as, (void __user * __user *)arg); retval = processcompl(as, (void __user * __user *)arg);
free_async(as); free_async(as);
} else {
retval = (connected(ps) ? -EAGAIN : -ENODEV);
} }
return retval; return retval;
} }
...@@ -1855,7 +1856,7 @@ static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg) ...@@ -1855,7 +1856,7 @@ static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
} }
if (signal_pending(current)) if (signal_pending(current))
return -EINTR; return -EINTR;
return -EIO; return -ENODEV;
} }
static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg) static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg)
...@@ -1863,11 +1864,12 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar ...@@ -1863,11 +1864,12 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar
int retval; int retval;
struct async *as; struct async *as;
retval = -EAGAIN;
as = async_getcompleted(ps); as = async_getcompleted(ps);
if (as) { if (as) {
retval = processcompl_compat(as, (void __user * __user *)arg); retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as); free_async(as);
} else {
retval = (connected(ps) ? -EAGAIN : -ENODEV);
} }
return retval; return retval;
} }
...@@ -2039,7 +2041,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg) ...@@ -2039,7 +2041,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
{ {
__u32 caps; __u32 caps;
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
USBDEVFS_CAP_REAP_AFTER_DISCONNECT;
if (!ps->dev->bus->no_stop_on_short) if (!ps->dev->bus->no_stop_on_short)
caps |= USBDEVFS_CAP_BULK_CONTINUATION; caps |= USBDEVFS_CAP_BULK_CONTINUATION;
if (ps->dev->bus->sg_tablesize) if (ps->dev->bus->sg_tablesize)
...@@ -2139,6 +2142,32 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, ...@@ -2139,6 +2142,32 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
return -EPERM; return -EPERM;
usb_lock_device(dev); usb_lock_device(dev);
/* Reap operations are allowed even after disconnection */
switch (cmd) {
case USBDEVFS_REAPURB:
snoop(&dev->dev, "%s: REAPURB\n", __func__);
ret = proc_reapurb(ps, p);
goto done;
case USBDEVFS_REAPURBNDELAY:
snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
ret = proc_reapurbnonblock(ps, p);
goto done;
#ifdef CONFIG_COMPAT
case USBDEVFS_REAPURB32:
snoop(&dev->dev, "%s: REAPURB32\n", __func__);
ret = proc_reapurb_compat(ps, p);
goto done;
case USBDEVFS_REAPURBNDELAY32:
snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
ret = proc_reapurbnonblock_compat(ps, p);
goto done;
#endif
}
if (!connected(ps)) { if (!connected(ps)) {
usb_unlock_device(dev); usb_unlock_device(dev);
return -ENODEV; return -ENODEV;
...@@ -2232,16 +2261,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, ...@@ -2232,16 +2261,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
inode->i_mtime = CURRENT_TIME; inode->i_mtime = CURRENT_TIME;
break; break;
case USBDEVFS_REAPURB32:
snoop(&dev->dev, "%s: REAPURB32\n", __func__);
ret = proc_reapurb_compat(ps, p);
break;
case USBDEVFS_REAPURBNDELAY32:
snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
ret = proc_reapurbnonblock_compat(ps, p);
break;
case USBDEVFS_IOCTL32: case USBDEVFS_IOCTL32:
snoop(&dev->dev, "%s: IOCTL32\n", __func__); snoop(&dev->dev, "%s: IOCTL32\n", __func__);
ret = proc_ioctl_compat(ps, ptr_to_compat(p)); ret = proc_ioctl_compat(ps, ptr_to_compat(p));
...@@ -2253,16 +2272,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, ...@@ -2253,16 +2272,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
ret = proc_unlinkurb(ps, p); ret = proc_unlinkurb(ps, p);
break; break;
case USBDEVFS_REAPURB:
snoop(&dev->dev, "%s: REAPURB\n", __func__);
ret = proc_reapurb(ps, p);
break;
case USBDEVFS_REAPURBNDELAY:
snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
ret = proc_reapurbnonblock(ps, p);
break;
case USBDEVFS_DISCSIGNAL: case USBDEVFS_DISCSIGNAL:
snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__); snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__);
ret = proc_disconnectsignal(ps, p); ret = proc_disconnectsignal(ps, p);
...@@ -2305,6 +2314,8 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, ...@@ -2305,6 +2314,8 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
ret = proc_free_streams(ps, p); ret = proc_free_streams(ps, p);
break; break;
} }
done:
usb_unlock_device(dev); usb_unlock_device(dev);
if (ret >= 0) if (ret >= 0)
inode->i_atime = CURRENT_TIME; inode->i_atime = CURRENT_TIME;
......
...@@ -128,11 +128,12 @@ struct usbdevfs_hub_portinfo { ...@@ -128,11 +128,12 @@ struct usbdevfs_hub_portinfo {
char port [127]; /* e.g. port 3 connects to device 27 */ char port [127]; /* e.g. port 3 connects to device 27 */
}; };
/* Device capability flags */ /* System and bus capability flags */
#define USBDEVFS_CAP_ZERO_PACKET 0x01 #define USBDEVFS_CAP_ZERO_PACKET 0x01
#define USBDEVFS_CAP_BULK_CONTINUATION 0x02 #define USBDEVFS_CAP_BULK_CONTINUATION 0x02
#define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04
#define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 #define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08
#define USBDEVFS_CAP_REAP_AFTER_DISCONNECT 0x10
/* USBDEVFS_DISCONNECT_CLAIM flags & struct */ /* USBDEVFS_DISCONNECT_CLAIM flags & struct */
......
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