Re: [PATCH] USB: fix deadlock in bConfigurationValue attribute method

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

 



On Tue, Apr 17, 2012 at 03:22:39PM -0400, Alan Stern wrote:
> This patch (as154) fixes a self-deadlock that occurs when userspace
> writes to the bConfigurationValue sysfs attribute for a hub with
> children.  The task tries to lock the bandwidth_mutex at a time when
> it already owns the lock:
> 
> 	The attribute's method calls usb_set_configuration(),
> 	which calls usb_disable_device() with the bandwidth_mutex
> 	held.
> 
> 	usb_disable_device() unregisters the existing interfaces,
> 	which causes the hub driver to be unbound.
> 
> 	The hub_disconnect() routine calls hub_quiesce(), which
> 	calls usb_disconnect() for each of the hub's children.
> 
> 	usb_disconnect() attempts to acquire the bandwidth_mutex
> 	around a call to usb_disable_device().
> 
> The solution is to make usb_disable_device() acquire the mutex for
> itself instead of requiring the caller to hold it.  Then the mutex can
> cover only the bandwidth deallocation operation and not the region
> where the interfaces are unregistered.
> 
> This has the potential to change system behavior slightly when a
> config change races with another config or altsetting change.  Some of
> the bandwidth released from the old config might get claimed by the
> other config or altsetting, make it impossible to restore the old
> config in case of a failure.  But since we don't try to recover from
> config-change failures anyway, this doesn't matter.

This should be marked for stable kernels that contain the commit
fccf4e86200b8f5edd9a65da26f150e32ba79808 "USB: Free bandwidth when
usb_disable_device is called."

That commit was marked for stable kernels as old as 2.6.32.

> Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
> CC: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>

> CC: <stable@xxxxxxxxxxxxxxx>
> 
> ---
> 
>  drivers/usb/core/hub.c     |    3 ---
>  drivers/usb/core/message.c |    6 +++---
>  2 files changed, 3 insertions(+), 6 deletions(-)
> 
> Index: usb-3.4/drivers/usb/core/hub.c
> ===================================================================
> --- usb-3.4.orig/drivers/usb/core/hub.c
> +++ usb-3.4/drivers/usb/core/hub.c
> @@ -1667,7 +1667,6 @@ void usb_disconnect(struct usb_device **
>  {
>  	struct usb_device	*udev = *pdev;
>  	int			i;
> -	struct usb_hcd		*hcd = bus_to_hcd(udev->bus);
>  
>  	/* mark the device as inactive, so any further urb submissions for
>  	 * this device (and any of its children) will fail immediately.
> @@ -1690,9 +1689,7 @@ void usb_disconnect(struct usb_device **
>  	 * so that the hardware is now fully quiesced.
>  	 */
>  	dev_dbg (&udev->dev, "unregistering device\n");
> -	mutex_lock(hcd->bandwidth_mutex);
>  	usb_disable_device(udev, 0);
> -	mutex_unlock(hcd->bandwidth_mutex);
>  	usb_hcd_synchronize_unlinks(udev);
>  
>  	usb_remove_ep_devs(&udev->ep0);
> Index: usb-3.4/drivers/usb/core/message.c
> ===================================================================
> --- usb-3.4.orig/drivers/usb/core/message.c
> +++ usb-3.4/drivers/usb/core/message.c
> @@ -1136,8 +1136,6 @@ void usb_disable_interface(struct usb_de
>   * Deallocates hcd/hardware state for the endpoints (nuking all or most
>   * pending urbs) and usbcore state for the interfaces, so that usbcore
>   * must usb_set_configuration() before any interfaces could be used.
> - *
> - * Must be called with hcd->bandwidth_mutex held.
>   */
>  void usb_disable_device(struct usb_device *dev, int skip_ep0)
>  {
> @@ -1190,7 +1188,9 @@ void usb_disable_device(struct usb_devic
>  			usb_disable_endpoint(dev, i + USB_DIR_IN, false);
>  		}
>  		/* Remove endpoints from the host controller internal state */
> +		mutex_lock(hcd->bandwidth_mutex);
>  		usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
> +		mutex_unlock(hcd->bandwidth_mutex);
>  		/* Second pass: remove endpoint pointers */
>  	}
>  	for (i = skip_ep0; i < 16; ++i) {
> @@ -1750,7 +1750,6 @@ free_interfaces:
>  	/* if it's already configured, clear out old state first.
>  	 * getting rid of old interfaces means unbinding their drivers.
>  	 */
> -	mutex_lock(hcd->bandwidth_mutex);
>  	if (dev->state != USB_STATE_ADDRESS)
>  		usb_disable_device(dev, 1);	/* Skip ep0 */
>  
> @@ -1763,6 +1762,7 @@ free_interfaces:
>  	 * host controller will not allow submissions to dropped endpoints.  If
>  	 * this call fails, the device state is unchanged.
>  	 */
> +	mutex_lock(hcd->bandwidth_mutex);
>  	ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
>  	if (ret < 0) {
>  		mutex_unlock(hcd->bandwidth_mutex);
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

  Powered by Linux