From: Sean Anderson <sean.anderson@linux.dev>
To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Hans de Goede <hansg@kernel.org>,
Ricardo Ribalda <ribalda@chromium.org>,
linux-media@vger.kernel.org,
Mauro Carvalho Chehab <mchehab@kernel.org>,
linux-kernel@vger.kernel.org, Hans Verkuil <hverkuil@kernel.org>
Subject: Re: [PATCH] media: uvcvideo: Fix deadlock if uvc_status_stop is called from async_ctrl.work
Date: Fri, 13 Mar 2026 14:48:50 -0400 [thread overview]
Message-ID: <04c8029a-8636-4757-b997-e115f466b42f@linux.dev> (raw)
In-Reply-To: <20260313174514.GC672609@killaraus.ideasonboard.com>
On 3/13/26 13:45, Laurent Pinchart wrote:
> On Tue, Mar 10, 2026 at 06:22:59PM -0400, Sean Anderson wrote:
>> If a UVC camera has an asynchronous control, uvc_status_stop may be
>> called from async_ctrl.work:
>>
>> uvc_ctrl_status_event_work()
>> uvc_ctrl_status_event()
>> uvc_ctrl_clear_handle()
>> uvc_pm_put()
>> uvc_status_put()
>> uvc_status_stop()
>> cancel_work_sync()
>>
>> This will cause a deadlock, since cancel_work_sync will wait for
>> uvc_ctrl_status_event_work to complete before returning.
>>
>> Fix this by returning early from uvc_status_stop if we are currently in
>> the work function. flush_status now remains false until uvc_status_start
>> is called again, ensuring that uvc_ctrl_status_event_work won't resubmit
>> the URB.
>>
>> Fixes: a32d9c41bdb8 ("media: uvcvideo: Make power management granular")
>> Closes: https://lore.kernel.org/all/6733bdfb-3e88-479f-8956-ab09c04c433e@linux.dev/
>> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
>> ---
>>
>> drivers/media/usb/uvc/uvc_status.c | 25 ++++++++++++++++---------
>> 1 file changed, 16 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
>> index 231cfee8e7c2c..2a23606c7f4c6 100644
>> --- a/drivers/media/usb/uvc/uvc_status.c
>> +++ b/drivers/media/usb/uvc/uvc_status.c
>> @@ -316,6 +316,14 @@ static int uvc_status_start(struct uvc_device *dev, gfp_t flags)
>> if (!dev->int_urb)
>> return 0;
>>
>> + /*
>> + * If the work called uvc_status_stop it may still be running. Wait for
>> + * it to finish before we submit the urb.
>
> I don't like this much. The code is becoming really convoluted and hard
> to follow. Would there be a way to solve the issue with a broader
> refactoring that would simplify the implementation ?
I don't think so. The complexity is fundamental to how the URB and work
schedule each other and that there are multiple opportunities for one to
"resurrect" the other.
As noted on the other reply, I think this call can be loosened to a
flush_work.
>> + */
>> + cancel_work_sync(&dev->async_ctrl.work);
>> +
>> + /* Clear the flush status if we were previously stopped */
>
> s/stopped/stopped./
>
>> + smp_store_release(&dev->flush_status, false);
>
> Add a blank line here.
>
>> return usb_submit_urb(dev->int_urb, flags);
>> }
>>
>> @@ -336,6 +344,14 @@ static void uvc_status_stop(struct uvc_device *dev)
>> */
>> smp_store_release(&dev->flush_status, true);
>>
>> + /*
>> + * We will deadlock if we are currently in the work function.
>> + * Fortunately, we know that the URB is already dead and that no
>
> The URB hasn't been killed, at has just completed and not been
> resubmitted.
That's why I said "dead" and not "killed" :)
> I'd write
>
> /*
> * If we are called from the event work function, the URB is guaranteed
> * to not be in flight as it has completed and has not been resubmitted.
> * There's no need to cancel the work (which would deadlock), or to kill
> * the URB.
> */
Fine by me
>> + * further work can be queued, so there's nothing left for us to do.
>> + */
>> + if (current_work() == &w->work)
>> + return;
>> +
>> /*
>> * Cancel any pending asynchronous work. If any status event was queued,
>> * process it synchronously.
>> @@ -354,15 +370,6 @@ static void uvc_status_stop(struct uvc_device *dev)
>> */
>> if (cancel_work_sync(&w->work))
>> uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
>> -
>> - /*
>> - * From this point, there are no events on the queue and the status URB
>> - * is dead. No events will be queued until uvc_status_start() is called.
>> - * The barrier is needed to make sure that flush_status is visible to
>> - * uvc_ctrl_status_event_work() when uvc_status_start() will be called
>> - * again.
>> - */
>> - smp_store_release(&dev->flush_status, false);
>> }
>>
>> int uvc_status_resume(struct uvc_device *dev)
>
prev parent reply other threads:[~2026-03-13 18:49 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-10 22:22 [PATCH] media: uvcvideo: Fix deadlock if uvc_status_stop is called from async_ctrl.work Sean Anderson
2026-03-11 16:06 ` Ricardo Ribalda
2026-03-12 17:33 ` Sean Anderson
2026-03-13 17:45 ` Laurent Pinchart
2026-03-13 18:48 ` Sean Anderson [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=04c8029a-8636-4757-b997-e115f466b42f@linux.dev \
--to=sean.anderson@linux.dev \
--cc=hansg@kernel.org \
--cc=hverkuil@kernel.org \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@kernel.org \
--cc=ribalda@chromium.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox