提交 373fef2e 编写于 作者: M Mathias Nyman 提交者: Zheng Zengkai

usb: hub: Fix usb enumeration issue due to address0 race

stable inclusion
from stable-v5.10.83
commit 5bf3a0c7789e6fb4b1d9733a8cb62f407abf3c25
bugzilla: 185879 https://gitee.com/openeuler/kernel/issues/I4QUVG

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=5bf3a0c7789e6fb4b1d9733a8cb62f407abf3c25

--------------------------------

commit 6ae6dc22 upstream.

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: <stable@vger.kernel.org>
Fixes: 638139eb ("usb: hub: allow to process more usb hub events in parallel")
Signed-off-by: NMathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20211115221630.871204-1-mathias.nyman@linux.intel.comSigned-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: NChen Jun <chenjun102@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 a452ccac
......@@ -4628,8 +4628,6 @@ 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(hcd->address0_mutex);
/* Reset the device; full speed may morph to high speed */
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
retval = hub_port_reset(hub, port1, udev, delay, false);
......@@ -4940,7 +4938,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
hub_port_disable(hub, port1, 0);
update_devnum(udev, devnum); /* for disconnect processing */
}
mutex_unlock(hcd->address0_mutex);
return retval;
}
......@@ -5170,6 +5167,9 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
unit_load = 100;
status = 0;
mutex_lock(hcd->address0_mutex);
for (i = 0; i < PORT_INIT_TRIES; i++) {
/* reallocate for each attempt, since references
......@@ -5206,6 +5206,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
if (status < 0)
goto loop;
mutex_unlock(hcd->address0_mutex);
if (udev->quirks & USB_QUIRK_DELAY_INIT)
msleep(2000);
......@@ -5294,6 +5296,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
loop_disable:
hub_port_disable(hub, port1, 1);
mutex_lock(hcd->address0_mutex);
loop:
usb_ep0_reinit(udev);
release_devnum(udev);
......@@ -5320,6 +5323,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
}
done:
mutex_unlock(hcd->address0_mutex);
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent) {
if (status != -ENOTCONN && status != -ENODEV)
......@@ -5839,6 +5844,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
bos = udev->bos;
udev->bos = NULL;
mutex_lock(hcd->address0_mutex);
for (i = 0; i < PORT_INIT_TRIES; ++i) {
/* ep0 maxpacket size may change; let the HCD know about it.
......@@ -5848,6 +5855,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
break;
}
mutex_unlock(hcd->address0_mutex);
if (ret < 0)
goto re_enumerate;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册