On Thu Sep 26 05:59:06 2024 +0000, Ricardo Ribalda wrote: > uvc_unregister_video() can be called asynchronously from > uvc_disconnect(). If the device is still streaming when that happens, a > plethora of race conditions can occur. > > Make sure that the device has stopped streaming before exiting this > function. > > If the user still holds handles to the driver's file descriptors, any > ioctl will return -ENODEV from the v4l2 core. > > This change makes uvc more consistent with the rest of the v4l2 drivers > using the vb2_fop_* and vb2_ioctl_* helpers. > > This driver (and many other usb drivers) always had this problem, but it > wasn't possible to easily fix this until the vb2_video_unregister_device() > helper was added. So the Fixes tag points to the creation of that helper. > > Reviewed-by: Hans Verkuil <hverk...@xs4all.nl> > Suggested-by: Hans Verkuil <hverk...@xs4all.nl> > Signed-off-by: Ricardo Ribalda <riba...@chromium.org> > Reviewed-by: Mauro Carvalho Chehab <mchehab+hua...@kernel.org> > Fixes: f729ef5796d8 ("media: videobuf2-v4l2.c: add > vb2_video_unregister_device helper function") > Cc: sta...@vger.kernel.org # 5.10.x > [hverkuil: add note regarding Fixes version] > Signed-off-by: Hans Verkuil <hverk...@xs4all.nl>
Patch committed. Thanks, Hans Verkuil drivers/media/usb/uvc/uvc_driver.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) --- diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index ab9cdb50e74e..a96f6ca0889f 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1935,11 +1935,41 @@ static void uvc_unregister_video(struct uvc_device *dev) struct uvc_streaming *stream; list_for_each_entry(stream, &dev->streams, list) { + /* Nothing to do here, continue. */ if (!video_is_registered(&stream->vdev)) continue; + /* + * For stream->vdev we follow the same logic as: + * vb2_video_unregister_device(). + */ + + /* 1. Take a reference to vdev */ + get_device(&stream->vdev.dev); + + /* 2. Ensure that no new ioctls can be called. */ video_unregister_device(&stream->vdev); - video_unregister_device(&stream->meta.vdev); + + /* 3. Wait for old ioctls to finish. */ + mutex_lock(&stream->mutex); + + /* 4. Stop streaming. */ + uvc_queue_release(&stream->queue); + + mutex_unlock(&stream->mutex); + + put_device(&stream->vdev.dev); + + /* + * For stream->meta.vdev we can directly call: + * vb2_video_unregister_device(). + */ + vb2_video_unregister_device(&stream->meta.vdev); + + /* + * Now both vdevs are not streaming and all the ioctls will + * return -ENODEV. + */ uvc_debugfs_cleanup_stream(stream); }