Re: [PATCH v6 part1 4/8] usb: assign default peer ports for root hubs

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

 



On Fri, 28 Feb 2014, Dan Williams wrote:

> Assume that the peer of a superspeed port is the port with the same id
> on the shared_hcd root hub.  This identification scheme is required of
> external hubs by the USB3 spec [1].  However, for root hubs, tier mismatch
> may be in effect [2].  Tier mismatch can only be enumerated via platform
> firmware.  For now, simply perform the nominal association.
> 
> Once the root hub is marked as unregistered we block attempts to walk
> the ->shared_hcd pointer to find a root-peer port.
> 
> [1]: usb 3.1 section 10.3.3
> [2]: xhci 1.1 appendix D
> 
> Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>

Okay, I think the locking still isn't right.  Below is an example patch
showing how it ought to work.  Basically, all the locking can be done
in hub.c; port.c doesn't have to worry about it at all.

> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> index 2518c3250750..e380b8a80830 100644
> --- a/drivers/usb/core/hcd.c
> +++ b/drivers/usb/core/hcd.c
> @@ -2778,9 +2778,17 @@ void usb_remove_hcd(struct usb_hcd *hcd)
>  		hcd->state = HC_STATE_QUIESCING;
>  
>  	dev_dbg(hcd->self.controller, "roothub graceful disconnect\n");
> +
> +	/*
> +	 * Flush + disable peering operations, and atomically update the
> +	 * hcd state relative to other root hub state
> +	 * changes/evaluations
> +	 */
> +	mutex_lock(&usb_port_peer_mutex);
>  	spin_lock_irq (&hcd_root_hub_lock);
>  	hcd->rh_registered = 0;
>  	spin_unlock_irq (&hcd_root_hub_lock);
> +	mutex_unlock(&usb_port_peer_mutex);

Not needed.  We don't care whether the root hub device is registered; 
we only care whether it qualifies as a hub according to the tests in 
usb_hub_to_struct_hub.

> @@ -171,7 +235,15 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
>  	port_dev->dev.driver = &usb_port_driver;
>  	dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev),
>  			port1);
> +
> +	mutex_lock(&usb_port_peer_mutex);
> +	hub->ports[port1 - 1] = port_dev;
>  	retval = device_register(&port_dev->dev);
> +	if (retval == 0)
> +		peer = find_default_peer(hub, port1);
> +	if (peer)
> +		link_peers(port_dev, peer);
> +	mutex_unlock(&usb_port_peer_mutex);
>  	if (retval)
>  		goto error_register;

The mutex stuff won't be needed here.  The other changes are good.

> @@ -196,9 +268,15 @@ exit:
>  	return retval;
>  }
>  
> -void usb_hub_remove_port_device(struct usb_hub *hub,
> -				       int port1)
> +void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
>  {
> -	device_unregister(&hub->ports[port1 - 1]->dev);
> +	struct usb_port *port_dev = hub->ports[port1 - 1];
> +	struct usb_port *peer;
> +
> +	mutex_lock(&usb_port_peer_mutex);
> +	peer = port_dev->peer;
> +	if (peer)
> +		unlink_peers(port_dev, peer);
> +	device_unregister(&port_dev->dev);
> +	mutex_unlock(&usb_port_peer_mutex);
>  }

Likewise here.

This is how I think the locking should work.  I haven't carefully
thought through all the possibilities, so please double-check that this
really will do what we want.

Alan Stern



Index: usb-3.14/drivers/usb/core/hub.c
===================================================================
--- usb-3.14.orig/drivers/usb/core/hub.c
+++ usb-3.14/drivers/usb/core/hub.c
@@ -55,6 +55,9 @@ static DECLARE_WAIT_QUEUE_HEAD(khubd_wai
 
 static struct task_struct *khubd_task;
 
+/* synchronize hub-port add/remove and peering operations */
+static DEFINE_MUTEX(usb_port_peer_mutex);
+
 /* cycle leds on hubs that aren't blinking for attention */
 static bool blinkenlights = 0;
 module_param (blinkenlights, bool, S_IRUGO);
@@ -1300,6 +1303,7 @@ static int hub_configure(struct usb_hub
 	char *message = "out of memory";
 	unsigned unit_load;
 	unsigned full_load;
+	unsigned maxchild;
 
 	hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
 	if (!hub->buffer) {
@@ -1338,12 +1342,11 @@ static int hub_configure(struct usb_hub
 		goto fail;
 	}
 
-	hdev->maxchild = hub->descriptor->bNbrPorts;
-	dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
-		(hdev->maxchild == 1) ? "" : "s");
+	maxchild = hub->descriptor->bNbrPorts;
+	dev_info (hub_dev, "%d port%s detected\n", maxchild,
+		(maxchild == 1) ? "" : "s");
 
-	hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *),
-			     GFP_KERNEL);
+	hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL);
 	if (!hub->ports) {
 		ret = -ENOMEM;
 		goto fail;
@@ -1364,11 +1367,11 @@ static int hub_configure(struct usb_hub
 		int	i;
 		char	portstr[USB_MAXCHILDREN + 1];
 
-		for (i = 0; i < hdev->maxchild; i++)
+		for (i = 0; i < maxchild; i++)
 			portstr[i] = hub->descriptor->u.hs.DeviceRemovable
 				    [((i + 1) / 8)] & (1 << ((i + 1) % 8))
 				? 'F' : 'R';
-		portstr[hdev->maxchild] = 0;
+		portstr[maxchild] = 0;
 		dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
 	} else
 		dev_dbg(hub_dev, "standalone hub\n");
@@ -1480,7 +1483,7 @@ static int hub_configure(struct usb_hub
 		if (hcd->power_budget > 0)
 			hdev->bus_mA = hcd->power_budget;
 		else
-			hdev->bus_mA = full_load * hdev->maxchild;
+			hdev->bus_mA = full_load * maxchild;
 		if (hdev->bus_mA >= full_load)
 			hub->mA_per_port = full_load;
 		else {
@@ -1495,7 +1498,7 @@ static int hub_configure(struct usb_hub
 			hub->descriptor->bHubContrCurrent);
 		hub->limited_power = 1;
 
-		if (remaining < hdev->maxchild * unit_load)
+		if (remaining < maxchild * unit_load)
 			dev_warn(hub_dev,
 					"insufficient power available "
 					"to use all downstream ports\n");
@@ -1563,15 +1566,19 @@ static int hub_configure(struct usb_hub
 	if (hub->has_indicators && blinkenlights)
 		hub->indicator[0] = INDICATOR_CYCLE;
 
-	for (i = 0; i < hdev->maxchild; i++) {
+	mutex_lock(&usb_port_peer_mutex);
+	for (i = 0; i < maxchild; i++) {
 		ret = usb_hub_create_port_device(hub, i + 1);
 		if (ret < 0) {
 			dev_err(hub->intfdev,
 				"couldn't create port%d device.\n", i + 1);
-			hdev->maxchild = i;
-			goto fail_keep_maxchild;
+			break;
 		}
 	}
+	hdev->maxchild = i;
+	mutex_unlock(&usb_port_peer_mutex);
+	if (ret < 0)
+		goto fail;
 
 	usb_hub_adjust_deviceremovable(hdev, hub->descriptor);
 
@@ -1579,8 +1586,6 @@ static int hub_configure(struct usb_hub
 	return 0;
 
 fail:
-	hdev->maxchild = 0;
-fail_keep_maxchild:
 	dev_err (hub_dev, "config failed, %s (err %d)\n",
 			message, ret);
 	/* hub_disconnect() frees urb and descriptor */
@@ -1616,6 +1621,8 @@ static void hub_disconnect(struct usb_in
 	hub->error = 0;
 	hub_quiesce(hub, HUB_DISCONNECT);
 
+	mutex_lock(&usb_port_peer_mutex);
+
 	/* Avoid races with recursively_mark_NOTATTACHED() */
 	spin_lock_irq(&device_state_lock);
 	port1 = hdev->maxchild;
@@ -1626,6 +1633,8 @@ static void hub_disconnect(struct usb_in
 	for (; port1 > 0; --port1)
 		usb_hub_remove_port_device(hub, port1);
 
+	mutex_unlock(&usb_port_peer_mutex);
+
 	if (hub->hdev->speed == USB_SPEED_HIGH)
 		highspeed_hubs--;
 

--
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