This patch creates a separate instance of the usb_address0 mutex for each host controller, and attaches it to the host controller device struct. This allows devices on separate hosts to be enumerated in parallel; saving time. In the current code, there is a single, global instance of the usb_address0 mutex which is used for all devices on any host. This isn't completely necessary, as this mutex is only needed to prevent address0 collisions for devices on the *same* host (usb 2.0 spec, sec 4.6.1). This superfluous coverage can cause additional delay in system resume on systems with multiple hosts (up to several seconds depending on what devices are attached). For instance, if I have a USB WLAN and a KVM switch attached to two ports, there's a chance that they could be initialized at the same time (e.g. on system resume). They would both be in the Default state and would be responding to requests from the default address (address 00H). If they were on the same host, there'd be no way of differentiating the two devices and thus the mutex is needed. But on separate hosts there's no chance of collision. Signed-off-by: Todd Brandt <todd.e.brandt@xxxxxxxxxxxxxxx> --- drivers/usb/core/hcd.c | 15 +++++++++++++-- drivers/usb/core/hub.c | 6 ++---- include/linux/usb/hcd.h | 1 + 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9c4e292..b5d89eb 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2452,9 +2452,18 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, return NULL; } mutex_init(hcd->bandwidth_mutex); + hcd->usb_address0_mutex = + kmalloc(sizeof(*hcd->usb_address0_mutex), GFP_KERNEL); + if (!hcd->usb_address0_mutex) { + kfree(hcd); + dev_dbg(dev, "hcd usb_address0 mutex alloc failed\n"); + return NULL; + } + mutex_init(hcd->usb_address0_mutex); dev_set_drvdata(dev, hcd); } else { hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; + hcd->usb_address0_mutex = primary_hcd->usb_address0_mutex; hcd->primary_hcd = primary_hcd; primary_hcd->primary_hcd = primary_hcd; hcd->shared_hcd = primary_hcd; @@ -2518,10 +2527,12 @@ static void hcd_release (struct kref *kref) { struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); - if (usb_hcd_is_primary_hcd(hcd)) + if (usb_hcd_is_primary_hcd(hcd)) { kfree(hcd->bandwidth_mutex); - else + kfree(hcd->usb_address0_mutex); + } else { hcd->shared_hcd->shared_hcd = NULL; + } kfree(hcd); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 090469e..804370e 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4016,8 +4016,6 @@ static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter) { - static DEFINE_MUTEX(usb_address0_mutex); - struct usb_device *hdev = hub->hdev; struct usb_hcd *hcd = bus_to_hcd(hdev->bus); int i, j, retval; @@ -4040,7 +4038,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; - mutex_lock(&usb_address0_mutex); + mutex_lock(hcd->usb_address0_mutex); /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ @@ -4317,7 +4315,7 @@ fail: hub_port_disable(hub, port1, 0); update_devnum(udev, devnum); /* for disconnect processing */ } - mutex_unlock(&usb_address0_mutex); + mutex_unlock(hcd->usb_address0_mutex); return retval; } diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 485cd5e..a1f89b4 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -166,6 +166,7 @@ struct usb_hcd { * to the device, or resetting the bandwidth after a failed attempt. */ struct mutex *bandwidth_mutex; + struct mutex *usb_address0_mutex; struct usb_hcd *shared_hcd; struct usb_hcd *primary_hcd; -- 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