答复: 答复: [PATCH] xhci: sideband: check vdev liveness before removing endpoints on unregister
From: 胡连勤
Date: Mon Sep 21 2026 - 23:41:18 EST
Hi Wesley,
>
> Sorry for interjecting here, but just wanted to point out the detail that
> when we call qc_usb_audio_offload_suspend() -->
> uaudio_send_disconnect_ind() this will ensure that the audio stream is
> idle/not active. For example, if there is currently audio playback
> happening, then this will issue a command to the aDSP to stop the audio
> playback and disable the offload stream by issuing
> handle_uaudio_stream_req(req_msg->enable = 0). This will invoke the
> sideband remove endpoint path.
>
> If the audio stream is already idle, then we shouldn't have an active
> endpoint registered to xhci sideband.
>
>
> > This is the root design issue: the sideband client assumes the
> > xHCI ring buffers remain valid across suspend/resume cycles. But
> > on reset-resume, usb_reset_and_verify_device() → hub_port_init()
> > → xhci_discover_or_reset_device() frees and reallocates those
> > rings, invalidating the sideband's pointers.
> >
>
> Should be only mapped when audio offload is active. Also, we explicitly
> take a runtime PM reference on enable_audio_stream() using
> snd_usb_autoresume() and remove this reference on disable_audio_stream(),
> so minus a PM suspend transition, we shouldn't see the
> qc_usb_audio_offload_suspend() callback executing when audio playback is
> happening.
>
Thanks for the correction, you're absolutely right. My previous reply
indeed had an incorrect understanding of the code design logic. The
suspend flow does trigger endpoint removal via the QMI disconnect
indication. Thank you for taking the time to clarify this.
On a related note, the real scenario where we encountered the issue is
that the device became unresponsive after resume, and after 4 reset
attempts still remained unresponsive, leading the host to disconnect the
device. The crash occurred in the disconnect path. Below is the actual
crash scenario from a production device with complete logs:
1. Audio offload active (T=157981.714, capture stream enabled)
2. Audio stream disabled (T=157982.357) -- no active playback
3. Device autoresume (T=158077.907):
[158077.907013][T18048] usb 1-1: usb_autoresume_device: cnt 1 -> 0
4. Control transfer failure after resume:
[158078.017530][ C1] xhci-hcd xhci-hcd.3.auto: WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state.
[158078.017562][ C1] xhci-hcd xhci-hcd.3.auto: WARN urb submitted to disabled ep
5. USB core initiates reset recovery (T=158078.017), 4 consecutive
resets all failed:
[158078.017988][T18048] usb 1-1: reset full-speed USB device number 2 using xhci-hcd
[158078.135870][T18048] usb 1-1: device descriptor read/64, error -71
[158078.355897][T18048] usb 1-1: device descriptor read/64, error -71
[158078.567921][T18048] usb 1-1: reset full-speed USB device number 2 using xhci-hcd
[158078.687868][T18048] usb 1-1: device descriptor read/64, error -71
[158078.907917][T18048] usb 1-1: device descriptor read/64, error -71
[158079.119867][T18048] usb 1-1: reset full-speed USB device number 2 using xhci-hcd
[158079.119972][T18048] usb 1-1: Device not responding to setup address.
[158079.320093][T18048] usb 1-1: Device not responding to setup address.
[158079.521887][T18048] usb 1-1: device not accepting address 2, error -71
[158079.522521][T18048] usb 1-1: WARN: invalid context state for evaluate context command.
[158079.632373][T18048] usb 1-1: reset full-speed USB device number 2 using xhci-hcd
[158079.632445][T18048] xhci-hcd xhci-hcd.3.auto: ERROR: unexpected setup address command completion code 0x11.
[158079.832089][T18048] xhci-hcd xhci-hcd.3.auto: ERROR: unexpected setup address command completion code 0x11.
[158080.032345][T18048] usb 1-1: device not accepting address 2, error -22
6. All 4 reset attempts failed, host disconnects the device
(T=158080.032):
[158080.032439][T18048] usb 1-1: USB bus reset recovery triggered
[158080.032741][T18048] usb 1-1: usb_autosuspend_device: cnt 0 -> -16
[158080.032811][T18048] usb 1-1: USB disconnect, device number 2
[158080.032917][T18048] usb 1-1: usb_autoresume_device: cnt 1 -> 0
[158080.032924][T18048] uaudio_send_disconnect_ind: sending qmi indication disconnect
[158080.033367][T30358] handle_uaudio_stream_req: sq_node:5 sq_port:1fc sq_family:2a
[158080.033778][T30358] check_valid_request: card#:0 dev#:0 dir:0 en:0 fmt:10 rate:48000 #ch:2
[158080.033795][T30358] usb 1-1: 1:0: usb_set_interface failed (-19)
[158080.033871][T30358] handle_uaudio_stream_req: release resources: intf# 1 card# 0
[158080.033874][T30358] uaudio_dev_release: for dev 0000000000000000
[158080.033915][T30358] uaudio_event_ring_cleanup_free: all audio devices disconnected
[158080.033942][T18048] uaudio_event_ring_cleanup_free: all audio devices disconnected
7. Crash occurs in xhci_sideband_unregister():
usb_disconnect() -> usb_disable_device() -> device_del() ->
usb_audio_disconnect() -> uaudio_disconnect() ->
xhci_sideband_unregister()
Crash call trace:
xhci_get_ep_ctx+0x0/0x38
xhci_sideband_unregister+0x68/0xf0 (inlined: __xhci_sideband_remove_endpoint + xhci_stop_endpoint_sync)
uaudio_disconnect+0x70/0x144
usb_audio_disconnect+0x7c/0x268
usb_unbind_interface+0x13c/0x340
...
usb_disconnect+0xe8/0x338
hub_event+0xbd8/0x19ac
Crash root cause:
- During the reset recovery process, xhci_setup_device() encountered
COMP_USB_TRANSACTION_ERROR ("Device not responding to setup
address")
- xhci_setup_device() calls xhci_disable_and_free_slot() ->
xhci_free_virt_device(), which frees the vdev memory and endpoint
rings
- However, xhci_free_virt_device() does not clear the sideband
pointer, so sb->vdev still points to the freed memory (dangling
pointer)
- Later, when usb_disconnect() -> uaudio_disconnect() ->
xhci_sideband_unregister() runs, sb->vdev is not NULL (it's a
dangling pointer), so it passes the "if (!vdev) return" check
- __xhci_sideband_remove_endpoint() -> xhci_stop_endpoint_sync() ->
xhci_get_ep_ctx() accesses the freed vdev memory -> crash
The key issue: after the audio stream is disabled (step 2), the sideband
registration remains active because disable_audio_stream() only calls
snd_usb_autosuspend() -- it does not call
xhci_sideband_remove_endpoint() or xhci_sideband_unregister(). The
sideband cleanup only happens later in the disconnect path.
Thanks,
Lianqin