Linux Media Controller development
 help / color / mirror / Atom feed
* uvcvideo: deadlock when async control drops the last PM reference from the status-event path
@ 2026-07-16  7:16 Jay Chooi
  2026-07-16  7:26 ` Ricardo Ribalda
  0 siblings, 1 reply; 7+ messages in thread
From: Jay Chooi @ 2026-07-16  7:16 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, hansg, ribalda, mchehab

Hello,

We are hitting a reproducible, unkillable deadlock in uvcvideo's
status-event / power-management path on v7.0.6 (Ubuntu 7.0.0-27-generic);
by code inspection the same paths are present in current mainline.

Hardware: 2x Intel RealSense D405 (8086:0b5b) and 1x D435 (8086:0b07) on
the same host. The trigger is ordinary control access (exposure /
auto-exposure, which these devices expose as asynchronous, auto-update
controls). With camera firmware 5.15.1.55 one unit wedged on most control
accesses and the other rarely; after updating both to firmware 5.17.3.10
the deadlock became deterministic on both — the newer firmware appears to
deliver the control-change status interrupt more reliably, hitting the
race window every time.

Analysis
--------

uvc_ctrl_set_handle() takes a PM reference (uvc_pm_get) for a pending
asynchronous control, released by uvc_ctrl_clear_handle() -> uvc_pm_put()
when the control-change status event arrives. That release runs inside
uvc_ctrl_status_event(), which is reached from two contexts where dropping
the LAST status user deadlocks:

1. From the async worker uvc_ctrl_status_event_work():
   uvc_pm_put -> uvc_status_put (takes status_lock) -> uvc_status_stop ->
   cancel_work_sync(&dev->async_ctrl.work) — cancelling the work item it
   is currently executing in. It waits for itself forever while holding
   status_lock.

2. From uvc_status_stop() itself, via the synchronous flush
   ("if (cancel_work_sync(&w->work)) uvc_ctrl_status_event(...)"):
   the same put chain re-acquires status_lock, which the caller
   (uvc_status_put/uvc_status_suspend) already holds. Self-deadlock on a
   non-recursive mutex.

Once either happens, every subsequent open/ioctl blocks in
uvc_status_get() on status_lock (unkillable D state), and
uvc_status_unregister() -> uvc_status_suspend() blocks the USB disconnect
path, which in turn wedges the hub workqueue (usb_hub_wq in D state) —
after that no device on the hub can enumerate and only a reboot recovers.
System shutdown also hangs indefinitely waiting on the D-state tasks.

Hung-task stacks (Ubuntu 7.0.0-27-generic)
------------------------------------------

Blocked ioctl side:
  uvc_v4l2_unlocked_ioctl+0x97/0x190 [uvcvideo]
  uvc_status_get+0x1d/0x90 [uvcvideo]

Blocked worker side:
  Workqueue: events uvc_ctrl_status_event_work [uvcvideo]
  uvc_ctrl_status_event_work+0x22/0x90 [uvcvideo]
  uvc_ctrl_status_event+0x77/0x180 [uvcvideo]
  uvc_pm_put+0x12/0x40 [uvcvideo]
  uvc_status_put+0x6d/0x80 [uvcvideo]
  uvc_status_stop.part.0+0x22/0xb0 [uvcvideo]

(Full traces available on request.)

Workaround we validated
-----------------------

A local module parameter that skips arming the status interrupt endpoint
in uvc_status_init() (no events -> no worker -> no deadlock) eliminates
the hang across repeated control-access stress on both firmware versions.
Expected side effect confirmed: pending async controls are then never
cleared (GET on them returns -EBUSY indefinitely), so this is a
workaround, not a fix.

Downstream report:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2160752

It looks like dropping the pending-async-control PM reference must not
synchronously stop the status URB from the event-delivery contexts —
e.g. defer the final uvc_status_stop() when current_work() ==
&dev->async_ctrl.work, and avoid re-entering uvc_status_put under
status_lock in the synchronous-flush path. Happy to test patches; the
setup reproduces the deadlock within seconds.

Reported-by: Jay Chooi <jay@robocurve.org>

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-07-17  7:27 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-16  7:16 uvcvideo: deadlock when async control drops the last PM reference from the status-event path Jay Chooi
2026-07-16  7:26 ` Ricardo Ribalda
2026-07-17  1:58   ` Jay Chooi
2026-07-17  6:54     ` Ricardo Ribalda
2026-07-17  7:05       ` Greg KH
2026-07-17  7:07         ` Ricardo Ribalda
2026-07-17  7:27           ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox