Re: All USB tools hang when one descriptor read fails and needs to timeout

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 8 Feb 2023 at 17:48, Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote:
>
> On Tue, Feb 07, 2023 at 12:52:16PM -0500, Alan Stern wrote:
> > On Tue, Feb 07, 2023 at 09:25:55AM +0100, Troels Liebe Bentsen wrote:
> > > Hi again,
> > >
> > > Did a bit more testing and found another lock that would be nice to remove,
> > > the /dev/bus/usb/$BUS/$DEV file for the hub is also locked:
> > >
> > > Bus 003 Device 016: ID 1a40:0201 Terminus Technology Inc. FE 2.1 7-port Hub
> > >
> > > strace lsusb -v
> > > ...
> > > openat(AT_FDCWD, "/dev/bus/usb/003/016", O_RDWR|O_CLOEXEC...
> > >
> > > The openat can not be canceled from userspace and even kill -9 won't stop
> > > the process until the descriptor read times out.
> >
> > Yes, that really should be an interruptible lock.  In fact, all the
> > locks connected with usbfs should be interruptible.
> >
> > However, it can't be eliminated entirely.  This is a case where two
> > things end up being mutually exclusive with each other because they both
> > need to be mutually exclusive with a third thing.  In other words,
> > opening a hub's usbfs file and probing a hub's children both have to be
> > mutually exclusive with disconnecting the hub.  The three pathways all
> > use the device lock for this purpose, so they are all mutually exclusive
> > with each other.
> >
> > > Also managed to get "lsusb -v" to hang in an unkillable way even after
> > > I unplugged the device and hub:
> > >
> > > ) = 1 ([{fd=5, revents=POLLIN}])
> > > ioctl(8, USBDEVFS_DISCARDURB
> >
> > Making these lock calls interruptible should fix this problem, right?
>
> Here's a patch.  It should fix most of these problems.
>
> Alan Stern
>
Thanks, it will give the patches a try.

But I guess it still means that "lsusb -v" or other tools that try to
read the hub's usbfs file will block until the child device's descriptor read
has timed out as something in that code path takes the hub's device lock.

I tried following the code path up from the read descriptors error and it
looks like locking is done on port level with usb_lock_port until we hit
hub_event where usb_lock_device(hdev) is taken, being new to this
code base I'm not sure it's the same mutex we locked on in
read_descriptors, but do we need to hold it until the end of the
function or is the hub device lock taken somewhere else?

As a little side note we found a rather ugly workaround where we cp
the sysfs read by libusb to /tmp and bind mount them back to sysfs
and remove the read permission from the hubs usbfs file, this
effectively means our testing software won't get disturbed by
descriptor read timeouts happening on other ports.

The way we get the device out of the broken state is by power
cycling it rapidly until it boots up in flashing mode, it normally takes a
couple of seconds and after that we can remove the flashing
pins that cause it to sometimes go into the broken state.

Regards Troels
>
> Index: usb-devel/drivers/usb/core/devio.c
> ===================================================================
> --- usb-devel.orig/drivers/usb/core/devio.c
> +++ usb-devel/drivers/usb/core/devio.c
> @@ -303,13 +303,15 @@ static ssize_t usbdev_read(struct file *
>  {
>         struct usb_dev_state *ps = file->private_data;
>         struct usb_device *dev = ps->dev;
> -       ssize_t ret = 0;
> +       ssize_t ret;
>         unsigned len;
>         loff_t pos;
>         int i;
>
>         pos = *ppos;
> -       usb_lock_device(dev);
> +       ret = usb_lock_device_interruptible(dev);
> +       if (ret < 0)
> +               return ret;
>         if (!connected(ps)) {
>                 ret = -ENODEV;
>                 goto err;
> @@ -1039,7 +1041,9 @@ static int usbdev_open(struct inode *ino
>         if (!dev)
>                 goto out_free_ps;
>
> -       usb_lock_device(dev);
> +       ret = usb_lock_device_interruptible(dev);
> +       if (ret < 0)
> +               goto out_put_device;
>         if (dev->state == USB_STATE_NOTATTACHED)
>                 goto out_unlock_device;
>
> @@ -1071,6 +1075,7 @@ static int usbdev_open(struct inode *ino
>
>   out_unlock_device:
>         usb_unlock_device(dev);
> + out_put_device:
>         usb_put_dev(dev);
>   out_free_ps:
>         kfree(ps);
> @@ -2591,14 +2596,17 @@ static long usbdev_do_ioctl(struct file
>         struct usb_dev_state *ps = file->private_data;
>         struct inode *inode = file_inode(file);
>         struct usb_device *dev = ps->dev;
> -       int ret = -ENOTTY;
> +       int ret;
>
>         if (!(file->f_mode & FMODE_WRITE))
>                 return -EPERM;
>
> -       usb_lock_device(dev);
> +       ret = usb_lock_device_interruptible(dev);
> +       if (ret < 0)
> +               return ret;
>
>         /* Reap operations are allowed even after disconnection */
> +       ret = -ENOTTY;
>         switch (cmd) {
>         case USBDEVFS_REAPURB:
>                 snoop(&dev->dev, "%s: REAPURB\n", __func__);
>



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux