Re: [RFC PATCH] usb: core: defer leaf-device resume off the S3 critical path
From: Alan Stern
Date: Wed Aug 05 2026 - 11:32:14 EST
On Wed, Aug 05, 2026 at 05:36:41PM +0800, Hongyu Xie wrote:
> Problem:
> System resume (dpm_resume) invokes each device's resume callback
> synchronously on the wake-up critical path.
I thought the PM core used asynchronous wakeups for most devices,
including all USB devices. They can delay the end of the wakeup
transition, but the delays do not add; they can occur concurrently.
> For a USB leaf device
> this means port resume and, when reset_resume is needed, a port
> reset that, for persistent devices, waits up to 2000 ms for the
> device connection (CCS; on SuperSpeed this includes link training)
> to be re-established (wait_for_connected()). Slow link training of
> some U-disks and webcams after S3 power-on therefore stalls the
> entire system wake-up: on an
> arm64 test machine with four leaf devices (bluetooth, UVC camera,
> two U-disks) dpm_resume took 2508 ms, about 2.1 s of it caused by a
> single SanDisk U-disk whose link training stayed in Rx.Detect for
> 2000 ms (worst case: -ENODEV fallback to disconnect +
> re-enumeration). A device compatibility issue should not lengthen
> the system-wide wake-up.
On the other hand, you don't want the system to be awake and running at
a time when a disk drive is still suspended and unusable. What will
happens if a process needs to read from or write to that drive?
> Fix:
> The patch addresses this on four levels:
>
> 1. Defer leaf-device recovery off the critical path. Leaf devices
> (udev->parent && !udev->maxchild) that are port-suspended on
> system resume have their real port resume moved to a per-device
> work item queued from usb_resume_complete(), after all hubs have
> finished resume. The work reuses the same udev (in-place reset,
> devnum preserved); drivers without reset_resume get unbind+rebind.
And what happens if a hub needs a 2-second-long reset-resume?
> 2. Safe abandonment. If a new suspend or a disconnect arrives before
> the work item runs, the reset is abandoned, not flushed: the
> device is still port-suspended, which is exactly what the new
> suspend needs, and the work item then only releases the runtime
> reference it took. The abandon decision is serialized with the
> work item by the device lock, so no cancel_work_sync() (which
> would deadlock against the work item's device_lock()) is needed
> on the suspend path.
>
> 3. Truthful runtime state with lazy resume. The deferred branch of
> usb_resume() reports the device as suspended
> (pm_runtime_set_suspended) until recovery actually completes; the
> work item switches it back to active only after usb_resume_both()
> succeeds. Accesses made in between trigger
> usb_autoresume_device() -> usb_port_resume() and resume the
> device synchronously, so a successful operation is a real
> confirmation of readiness. A failed recovery leaves the device
> suspended: later accesses fail or the device is disconnected,
> both visible to user space.
Runtime-suspend and system-suspend are not the same thing. In
particular, they can have different wakeup settings. You cannot simply
substitute one for the other.
> 4. Port-state gating. A device is only deferred when its port is
> not connected (link training still pending, or the device was
> removed while asleep) -- the case where the long waits happen. A
> connected port resumes in bounded time and keeps the synchronous
> path with unchanged semantics.
What happens if other, non-USB, devices lie below the USB leaf device in
the device tree? How will they resume? What will happen if the system
needs to use them as soon as the system resume is finished?
> Results:
> With four leaf devices (bluetooth, UVC camera, Kingston U-disk,
> SanDisk U-disk), repeated S3 measurements (14 rounds):
> - without a 2000 ms event: dpm_resume drops from ~498-502 ms to
> ~219-223 ms (-56%);
> - with the occasional 2000 ms link-training wait (or the fixed
> 2000 ms CONNECT timeout for a device removed during sleep):
> dpm_resume drops from ~2508-2813 ms to ~219 ms (-91%), the wait
> running in the background workqueue without affecting
> system-wide resume.
> Deferred recovery completes in 0-20 ms in the common case.
>
> Known limitations (no practical impact):
> - The system resume notification (PM_POST_SYSTEM_RESUME) is
> system-wide and carries no device information; between the
> notification and deferred recovery completion an application
> accessing the device may fail. Drivers using usb_autopm trigger
> synchronous recovery instead (lazy resume), and the window is
> milliseconds in practice.
> - usb_resume() returns success before the device is actually
> recovered; the runtime PM state is truthful (suspended), but the
> PM core counts the callback as successful. No kernel path
> depends on that accounting.
> - A recovery failure in the workqueue cannot be reported through
> the dpm error path; it surfaces as device disconnect, which user
> space already handles.
>
> Gated by module parameter usbcore.defer_resume (default off).
Overall this does not sound like a good idea. At the very least, there
are several implications you have not considered.
Also, have you looked into the smart_suspend and may_skip_resume flags
in struct dev_pm_info? Can some combination of them accomplish what you
want with minimal code changes?
Alan Stern