Skip to content

Commit 6ae6dc2

Browse files
matnymangregkh
authored andcommitted
usb: hub: Fix usb enumeration issue due to address0 race
xHC hardware can only have one slot in default state with address 0 waiting for a unique address at a time, otherwise "undefined behavior may occur" according to xhci spec 5.4.3.4 The address0_mutex exists to prevent this across both xhci roothubs. If hub_port_init() fails, it may unlock the mutex and exit with a xhci slot in default state. If the other xhci roothub calls hub_port_init() at this point we end up with two slots in default state. Make sure the address0_mutex protects the slot default state across hub_port_init() retries, until slot is addressed or disabled. Note, one known minor case is not fixed by this patch. If device needs to be reset during resume, but fails all hub_port_init() retries in usb_reset_and_verify_device(), then it's possible the slot is still left in default state when address0_mutex is unlocked. Cc: <[email protected]> Fixes: 638139e ("usb: hub: allow to process more usb hub events in parallel") Signed-off-by: Mathias Nyman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 3624688 commit 6ae6dc2

File tree

1 file changed

+11
-3
lines changed

1 file changed

+11
-3
lines changed

drivers/usb/core/hub.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4700,8 +4700,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
47004700
if (oldspeed == USB_SPEED_LOW)
47014701
delay = HUB_LONG_RESET_TIME;
47024702

4703-
mutex_lock(hcd->address0_mutex);
4704-
47054703
/* Reset the device; full speed may morph to high speed */
47064704
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
47074705
retval = hub_port_reset(hub, port1, udev, delay, false);
@@ -5016,7 +5014,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
50165014
hub_port_disable(hub, port1, 0);
50175015
update_devnum(udev, devnum); /* for disconnect processing */
50185016
}
5019-
mutex_unlock(hcd->address0_mutex);
50205017
return retval;
50215018
}
50225019

@@ -5246,6 +5243,9 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
52465243
unit_load = 100;
52475244

52485245
status = 0;
5246+
5247+
mutex_lock(hcd->address0_mutex);
5248+
52495249
for (i = 0; i < PORT_INIT_TRIES; i++) {
52505250

52515251
/* reallocate for each attempt, since references
@@ -5282,6 +5282,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
52825282
if (status < 0)
52835283
goto loop;
52845284

5285+
mutex_unlock(hcd->address0_mutex);
5286+
52855287
if (udev->quirks & USB_QUIRK_DELAY_INIT)
52865288
msleep(2000);
52875289

@@ -5370,6 +5372,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
53705372

53715373
loop_disable:
53725374
hub_port_disable(hub, port1, 1);
5375+
mutex_lock(hcd->address0_mutex);
53735376
loop:
53745377
usb_ep0_reinit(udev);
53755378
release_devnum(udev);
@@ -5396,6 +5399,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
53965399
}
53975400

53985401
done:
5402+
mutex_unlock(hcd->address0_mutex);
5403+
53995404
hub_port_disable(hub, port1, 1);
54005405
if (hcd->driver->relinquish_port && !hub->hdev->parent) {
54015406
if (status != -ENOTCONN && status != -ENODEV)
@@ -5915,6 +5920,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
59155920
bos = udev->bos;
59165921
udev->bos = NULL;
59175922

5923+
mutex_lock(hcd->address0_mutex);
5924+
59185925
for (i = 0; i < PORT_INIT_TRIES; ++i) {
59195926

59205927
/* ep0 maxpacket size may change; let the HCD know about it.
@@ -5924,6 +5931,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
59245931
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
59255932
break;
59265933
}
5934+
mutex_unlock(hcd->address0_mutex);
59275935

59285936
if (ret < 0)
59295937
goto re_enumerate;

0 commit comments

Comments
 (0)