On Wed, Feb 26, 2025 at 07:23:16AM +0000, Pawel Laszczak wrote: > The xHC resources allocated for USB devices are not released in correct > order after resuming in case when while suspend device was reconnected. > > This issue has been detected during the fallowing scenario: > - connect hub HS to root port > - connect LS/FS device to hub port > - wait for enumeration to finish > - force host to suspend > - reconnect hub attached to root port > - wake host > > For this scenario during enumeration of USB LS/FS device the Cadence xHC > reports completion error code for xHC commands because the xHC resources > used for devices has not been property released. s/property/properly/ > XHCI specification doesn't mention that device can be reset in any order > so, we should not treat this issue as Cadence xHC controller bug. > Similar as during disconnecting in this case the device resources should > be cleared starting form the last usb device in tree toward the root hub. > To fix this issue usbcore driver should call hcd->driver->reset_device > for all USB devices connected to hub which was reconnected while > suspending. > > Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver") > cc: <stable@xxxxxxxxxxxxxxx> > Signed-off-by: Pawel Laszczak <pawell@xxxxxxxxxxx> > > --- > Changelog: > v2: > - Replaced disconnection procedure with releasing only the xHC resources > > drivers/usb/core/hub.c | 33 +++++++++++++++++++++++++++++++++ > 1 file changed, 33 insertions(+) > > diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c > index a76bb50b6202..d3f89528a414 100644 > --- a/drivers/usb/core/hub.c > +++ b/drivers/usb/core/hub.c > @@ -6065,6 +6065,36 @@ void usb_hub_cleanup(void) > usb_deregister(&hub_driver); > } /* usb_hub_cleanup() */ > > +/** > + * hub_hc_release_resources - clear resources used by host controller > + * @pdev: pointer to device being released > + * > + * Context: task context, might sleep > + * > + * Function releases the host controller resources in correct order before > + * making any operation on resuming usb device. The host controller resources > + * allocated for devices in tree should be released starting from the last > + * usb device in tree toward the root hub. This function is used only during > + * resuming device when usb device require reinitialization - that is, when > + * flag udev->reset_resume is set. > + * > + * This call is synchronous, and may not be used in an interrupt context. > + */ > +static void hub_hc_release_resources(struct usb_device *udev) > +{ > + struct usb_hub *hub = usb_hub_to_struct_hub(udev); > + struct usb_hcd *hcd = bus_to_hcd(udev->bus); > + int i; > + > + /* Release up resources for all children before this device */ > + for (i = 0; i < udev->maxchild; i++) > + if (hub->ports[i]->child) > + hub_hc_release_resources(hub->ports[i]->child); > + > + if (hcd->driver->reset_device) > + hcd->driver->reset_device(hcd, udev); > +} > + > /** > * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device > * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) > @@ -6131,6 +6161,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev) > > mutex_lock(hcd->address0_mutex); > > + if (udev->reset_resume) > + hub_hc_release_resources(udev); Don't you want to do this before taking the address0_mutex lock? > + > for (i = 0; i < PORT_INIT_TRIES; ++i) { > if (hub_port_stop_enumerate(parent_hub, port1, i)) { > ret = -ENODEV; Doing it this way, you will call hcd->driver->reset_device() multiple times for the same device: once for the hub(s) above the device and then once for the device itself. But since this isn't a hot path, maybe that doesn't matter. Alan Stern