Re: [PATCH] usb: host: xhci: add mutex for non-thread-safe data

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

 



On Wed 2015-04-29 09:18:50, Chris Bainbridge wrote:
> Regression in commit 638139eb95d2d241781330a321e88c8dafe46078
> Author: Petr Mladek <pmladek@xxxxxxx>
> Date:   Fri Sep 19 17:32:24 2014 +0200
> 
>     usb: hub: allow to process more usb hub events in parallel

Anyway, I would suggest to revert this commit. I wonder where I had my
head when I sent it.

It seems that the other operations with udev->portnum and
bus->devnum_next are not guared by bus->usb_address0_mutex.
And it seems that this is not the only operation that need to
be serialized.
 
> The regression resulted in intermittent failure to initialise a 10-port
> hub (with three internal VL812 4-port hub controllers) on boot, with a
> failure rate of around 8%, due to multiple race conditions when
> accessing addr_dev and slot_id in struct xhci_hcd.
> 
> This regression also exposed a problem with xhci_setup_device, which
> "should be protected by the usb_address0_mutex" but no longer is due to
> commit 6fecd4f:
> Author: Todd E Brandt <todd.e.brandt@xxxxxxxxxxxxxxx>
> Date:   Mon May 19 10:55:32 2014 -0700
> 
>     USB: separate usb_address0 mutexes for each bus
> 
> With separate buses (and locks) it is no longer the case that a single
> lock will protect xhci_setup_device from accesses by two parallel
> threads processing events on the two buses.
> 
> Fix this by adding a mutex to protect addr_dev and slot_id in struct
> xhci_hcd, and by making the assignment of slot_id atomic.
> 
> Fixes multiple boot errors:
> 
>  [    0.583008] xhci_hcd 0000:00:14.0: Bad Slot ID 2
>  [    0.583009] xhci_hcd 0000:00:14.0: Could not allocate xHCI USB device data structures
>  [    0.583012] usb usb1-port3: couldn't allocate usb_device
> 
> And:
> 
>  [    0.637409] xhci_hcd 0000:00:14.0: Error while assigning device slot ID
>  [    0.637417] xhci_hcd 0000:00:14.0: Max number of devices this xHCI host supports is 32.
>  [    0.637421] usb usb1-port1: couldn't allocate usb_device
> 
> And:
> 
>  [    0.753372] xhci_hcd 0000:00:14.0: ERROR: unexpected setup context command completion code 0x0.
>  [    0.753373] usb 1-3: hub failed to enable device, error -22
>  [    0.753400] xhci_hcd 0000:00:14.0: Error while assigning device slot ID
>  [    0.753402] xhci_hcd 0000:00:14.0: Max number of devices this xHCI host supports is 32.
>  [    0.753403] usb usb1-port3: couldn't allocate usb_device
> 
> And:
> 
> [   11.018386] usb 1-3: device descriptor read/all, error -110
> 
> And:
> 
> [    5.753838] xhci_hcd 0000:00:14.0: Timeout while waiting for setup device command
> 
> Tested with 200 reboots, resulting in no USB hub init related errors.
> 
> Fixes: 638139eb95d2 ("usb: hub: allow to process more usb hub events in parallel")
> Link: https://lkml.kernel.org/g/CAP-bSRb=A0iEYobdGCLpwynS7pkxpt_9ZnwyZTPVAoy0Y=Zo3Q@xxxxxxxxxxxxxx
> Signed-off-by: Chris Bainbridge <chris.bainbridge@xxxxxxxxx>
> ---
>  drivers/usb/host/xhci.c | 52 ++++++++++++++++++++++++++++++-------------------
>  drivers/usb/host/xhci.h |  2 ++
>  2 files changed, 34 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index ec8ac16..b59d576 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -3682,18 +3682,21 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
>  {
>  	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
>  	unsigned long flags;
> -	int ret;
> +	int ret, slot_id;
>  	struct xhci_command *command;
>  
>  	command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
>  	if (!command)
>  		return 0;
>  
> +	/* xhci->slot_id and xhci->addr_dev are not thread-safe */
> +	mutex_lock(&xhci->mutex);
>  	spin_lock_irqsave(&xhci->lock, flags);
>  	command->completion = &xhci->addr_dev;
>  	ret = xhci_queue_slot_control(xhci, command, TRB_ENABLE_SLOT, 0);
>  	if (ret) {
>  		spin_unlock_irqrestore(&xhci->lock, flags);
> +		mutex_unlock(&xhci->mutex);
>  		xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
>  		kfree(command);
>  		return 0;
> @@ -3702,8 +3705,10 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
>  	spin_unlock_irqrestore(&xhci->lock, flags);
>  
>  	wait_for_completion(command->completion);
> +	slot_id = xhci->slot_id;
> +	mutex_unlock(&xhci->mutex);
>  
> -	if (!xhci->slot_id || command->status != COMP_SUCCESS) {
> +	if (!slot_id || command->status != COMP_SUCCESS) {
>  		xhci_err(xhci, "Error while assigning device slot ID\n");
>  		xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
>  				HCS_MAX_SLOTS(
> @@ -3728,11 +3733,11 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
>  	 * xhci_discover_or_reset_device(), which may be called as part of
>  	 * mass storage driver error handling.
>  	 */
> -	if (!xhci_alloc_virt_device(xhci, xhci->slot_id, udev, GFP_NOIO)) {
> +	if (!xhci_alloc_virt_device(xhci, slot_id, udev, GFP_NOIO)) {
>  		xhci_warn(xhci, "Could not allocate xHCI USB device data structures\n");
>  		goto disable_slot;
>  	}
> -	udev->slot_id = xhci->slot_id;
> +	udev->slot_id = slot_id;
>  
>  #ifndef CONFIG_USB_DEFAULT_PERSIST
>  	/*
> @@ -3778,12 +3783,15 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
>  	struct xhci_slot_ctx *slot_ctx;
>  	struct xhci_input_control_ctx *ctrl_ctx;
>  	u64 temp_64;
> -	struct xhci_command *command;
> +	struct xhci_command *command = NULL;
> +
> +	mutex_lock(&xhci->mutex);
>  
>  	if (!udev->slot_id) {
>  		xhci_dbg_trace(xhci, trace_xhci_dbg_address,
>  				"Bad Slot ID %d", udev->slot_id);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto out;
>  	}
>  
>  	virt_dev = xhci->devs[udev->slot_id];
> @@ -3796,7 +3804,8 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
>  		 */
>  		xhci_warn(xhci, "Virt dev invalid for slot_id 0x%x!\n",
>  			udev->slot_id);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto out;
>  	}
>  
>  	if (setup == SETUP_CONTEXT_ONLY) {
> @@ -3804,13 +3813,15 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
>  		if (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state)) ==
>  		    SLOT_STATE_DEFAULT) {
>  			xhci_dbg(xhci, "Slot already in default state\n");
> -			return 0;
> +			goto out;
>  		}
>  	}
>  
>  	command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
> -	if (!command)
> -		return -ENOMEM;
> +	if (!command) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
>  
>  	command->in_ctx = virt_dev->in_ctx;
>  	command->completion = &xhci->addr_dev;
> @@ -3820,8 +3831,8 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
>  	if (!ctrl_ctx) {
>  		xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
>  				__func__);
> -		kfree(command);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto out;
>  	}
>  	/*
>  	 * If this is the first Set Address since device plug-in or
> @@ -3848,8 +3859,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
>  		spin_unlock_irqrestore(&xhci->lock, flags);
>  		xhci_dbg_trace(xhci, trace_xhci_dbg_address,
>  				"FIXME: allocate a command ring segment");
> -		kfree(command);
> -		return ret;
> +		goto out;
>  	}
>  	xhci_ring_cmd_db(xhci);
>  	spin_unlock_irqrestore(&xhci->lock, flags);
> @@ -3896,10 +3906,8 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
>  		ret = -EINVAL;
>  		break;
>  	}
> -	if (ret) {
> -		kfree(command);
> -		return ret;
> -	}
> +	if (ret)
> +		goto out;
>  	temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
>  	xhci_dbg_trace(xhci, trace_xhci_dbg_address,
>  			"Op regs DCBAA ptr = %#016llx", temp_64);
> @@ -3932,8 +3940,11 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
>  	xhci_dbg_trace(xhci, trace_xhci_dbg_address,
>  		       "Internal device address = %d",
>  		       le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
> -	kfree(command);
> -	return 0;
> +out:
> +	mutex_unlock(&xhci->mutex);
> +	if (command)
> +		kfree(command);
> +	return ret;
>  }
>  
>  int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
> @@ -4855,6 +4866,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
>  		return 0;
>  	}
>  
> +	mutex_init(&xhci->mutex);
>  	xhci->cap_regs = hcd->regs;
>  	xhci->op_regs = hcd->regs +
>  		HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 8e421b8..bde69ec 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1497,6 +1497,8 @@ struct xhci_hcd {
>  	struct list_head	lpm_failed_devs;
>  
>  	/* slot enabling and address device helpers */
> +	/* these are not thread safe so use mutex */
> +	struct mutex mutex;
>  	struct completion	addr_dev;
>  	int slot_id;
>  	/* For USB 3.0 LPM enable/disable. */
> -- 
> 2.1.4
> 
--
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