| From 6cca13de26eea6d32a98d96d916a048d16a12822 Mon Sep 17 00:00:00 2001 |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Tue, 23 Nov 2021 12:16:56 +0200 |
| Subject: usb: hub: Fix locking issues with address0_mutex |
| |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| |
| commit 6cca13de26eea6d32a98d96d916a048d16a12822 upstream. |
| |
| Fix the circular lock dependency and unbalanced unlock of addess0_mutex |
| introduced when fixing an address0_mutex enumeration retry race in commit |
| ae6dc22d2d1 ("usb: hub: Fix usb enumeration issue due to address0 race") |
| |
| Make sure locking order between port_dev->status_lock and address0_mutex |
| is correct, and that address0_mutex is not unlocked in hub_port_connect |
| "done:" codepath which may be reached without locking address0_mutex |
| |
| Fixes: 6ae6dc22d2d1 ("usb: hub: Fix usb enumeration issue due to address0 race") |
| Cc: <stable@vger.kernel.org> |
| Reported-by: Marek Szyprowski <m.szyprowski@samsung.com> |
| Tested-by: Hans de Goede <hdegoede@redhat.com> |
| Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> |
| Acked-by: Hans de Goede <hdegoede@redhat.com> |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Link: https://lore.kernel.org/r/20211123101656.1113518-1-mathias.nyman@linux.intel.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/usb/core/hub.c | 19 ++++++++++++------- |
| 1 file changed, 12 insertions(+), 7 deletions(-) |
| |
| --- a/drivers/usb/core/hub.c |
| +++ b/drivers/usb/core/hub.c |
| @@ -4768,6 +4768,7 @@ static void hub_port_connect(struct usb_ |
| struct usb_port *port_dev = hub->ports[port1 - 1]; |
| struct usb_device *udev = port_dev->child; |
| static int unreliable_port = -1; |
| + bool retry_locked; |
| |
| /* Disconnect any existing devices under this port */ |
| if (udev) { |
| @@ -4824,9 +4825,10 @@ static void hub_port_connect(struct usb_ |
| |
| status = 0; |
| |
| - mutex_lock(hcd->address0_mutex); |
| - |
| for (i = 0; i < SET_CONFIG_TRIES; i++) { |
| + usb_lock_port(port_dev); |
| + mutex_lock(hcd->address0_mutex); |
| + retry_locked = true; |
| |
| /* reallocate for each attempt, since references |
| * to the previous one can escape in various ways |
| @@ -4835,6 +4837,8 @@ static void hub_port_connect(struct usb_ |
| if (!udev) { |
| dev_err(&port_dev->dev, |
| "couldn't allocate usb_device\n"); |
| + mutex_unlock(hcd->address0_mutex); |
| + usb_unlock_port(port_dev); |
| goto done; |
| } |
| |
| @@ -4856,13 +4860,13 @@ static void hub_port_connect(struct usb_ |
| } |
| |
| /* reset (non-USB 3.0 devices) and get descriptor */ |
| - usb_lock_port(port_dev); |
| status = hub_port_init(hub, udev, port1, i); |
| - usb_unlock_port(port_dev); |
| if (status < 0) |
| goto loop; |
| |
| mutex_unlock(hcd->address0_mutex); |
| + usb_unlock_port(port_dev); |
| + retry_locked = false; |
| |
| if (udev->quirks & USB_QUIRK_DELAY_INIT) |
| msleep(2000); |
| @@ -4952,11 +4956,14 @@ static void hub_port_connect(struct usb_ |
| |
| loop_disable: |
| hub_port_disable(hub, port1, 1); |
| - mutex_lock(hcd->address0_mutex); |
| loop: |
| usb_ep0_reinit(udev); |
| release_devnum(udev); |
| hub_free_dev(udev); |
| + if (retry_locked) { |
| + mutex_unlock(hcd->address0_mutex); |
| + usb_unlock_port(port_dev); |
| + } |
| usb_put_dev(udev); |
| if ((status == -ENOTCONN) || (status == -ENOTSUPP)) |
| break; |
| @@ -4979,8 +4986,6 @@ loop: |
| } |
| |
| 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) |