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.willi...@intel.com>

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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to