From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Hans de Goede <hdegoede@redhat.com>
Cc: Ricardo Ribalda <ribalda@chromium.org>,
Mauro Carvalho Chehab <mchehab@kernel.org>,
Guennadi Liakhovetski <guennadi.liakhovetski@intel.com>,
linux-media@vger.kernel.org, linux-kernel@vger.kernel.org,
Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Subject: Re: [PATCH v6 3/5] media: uvcvideo: Increase/decrease the PM counter per IOCTL
Date: Mon, 28 Apr 2025 15:31:17 +0300 [thread overview]
Message-ID: <20250428123117.GH3371@pendragon.ideasonboard.com> (raw)
In-Reply-To: <300e71dc-27f0-4193-a99f-3f6880add883@redhat.com>
On Mon, Apr 28, 2025 at 01:54:38PM +0200, Hans de Goede wrote:
> On 23-Apr-25 00:58, Ricardo Ribalda wrote:
> > On Wed, 23 Apr 2025 at 04:37, Laurent Pinchart wrote:
> >> On Thu, Mar 27, 2025 at 09:05:29PM +0000, Ricardo Ribalda wrote:
> >>> Now we call uvc_pm_get/put from the device open/close. This low
> >>> level of granularity might leave the camera powered on in situations
> >>> where it is not needed.
> >>>
> >>> Increase the granularity by increasing and decreasing the Power
> >>> Management counter per ioctl. There are two special cases where the
> >>> power management outlives the ioctl: async controls and streamon. Handle
> >>> those cases as well.
> >>>
> >>> In a future patch, we will remove the uvc_pm_get/put from open/close.
> >>>
> >>> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
> >>> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> >>> ---
> >>> drivers/media/usb/uvc/uvc_ctrl.c | 37 +++++++++++++++++++++++++++----------
> >>> drivers/media/usb/uvc/uvc_v4l2.c | 39 +++++++++++++++++++++++++++++++++++++--
> >>> 2 files changed, 64 insertions(+), 12 deletions(-)
> >>>
> >>> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> >>> index cbf19aa1d82374a08cf79b6a6787fa348b83523a..3fad289e41fd5a757f8dcf30a6238c694fc4250c 100644
> >>> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> >>> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> >>> @@ -1812,38 +1812,49 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
> >>> uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
> >>> }
> >>>
> >>> -static void uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl,
> >>> - struct uvc_fh *new_handle)
> >>> +static int uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl,
> >>> + struct uvc_fh *new_handle)
> >>> {
> >>> lockdep_assert_held(&handle->chain->ctrl_mutex);
> >>>
> >>> if (new_handle) {
> >>> + int ret;
> >>> +
> >>> if (ctrl->handle)
> >>> dev_warn_ratelimited(&handle->stream->dev->udev->dev,
> >>> "UVC non compliance: Setting an async control with a pending operation.");
> >>>
> >>> if (new_handle == ctrl->handle)
> >>> - return;
> >>> + return 0;
> >>>
> >>> if (ctrl->handle) {
> >>> WARN_ON(!ctrl->handle->pending_async_ctrls);
> >>> if (ctrl->handle->pending_async_ctrls)
> >>> ctrl->handle->pending_async_ctrls--;
> >>> + ctrl->handle = new_handle;
> >>> + handle->pending_async_ctrls++;
> >>
> >> As commented previously, your usage of the handle variable is confusing.
> >>
> >> ctrl->handle->pending_async_ctrls++;
> >
> > I believe what makes it confusing is the function arguments.
> >
> > Would you mind if I send a new patch introducing:
> > uvc_ctrl_set_handle() and uvc_ctrl_clear_handle().
It seems worth a try. I would still like the above change to be
addressed. Can you please send a patch for that too ?
> Ricardo, if you do end up making this change, please do so as a follow-up
> patch on top of current uvc/next so that we don't have to drop the whole
> series and then rebuild uvc/next from scratch.
Ack, that's my preference too.
> >>> + return 0;
> >>> }
> >>>
> >>> + ret = uvc_pm_get(handle->chain->dev);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> ctrl->handle = new_handle;
> >>> handle->pending_async_ctrls++;
> >>
> >> ctrl->handle->pending_async_ctrls++;
> >>
> >>> - return;
> >>> + return 0;
> >>> }
> >>>
> >>> /* Cannot clear the handle for a control not owned by us.*/
> >>> if (WARN_ON(ctrl->handle != handle))
> >>> - return;
> >>> + return -EINVAL;
> >>>
> >>> ctrl->handle = NULL;
> >>> if (WARN_ON(!handle->pending_async_ctrls))
> >>> - return;
> >>> + return -EINVAL;
> >>> handle->pending_async_ctrls--;
> >>> + uvc_pm_put(handle->chain->dev);
> >>> + return 0;
> >>> }
> >>>
> >>> void uvc_ctrl_status_event(struct uvc_video_chain *chain,
> >>> @@ -2137,15 +2148,16 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
> >>>
> >>> ctrl->dirty = 0;
> >>>
> >>> + if (!rollback && handle && !ret &&
> >>> + ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
> >>> + ret = uvc_ctrl_set_handle(handle, ctrl, handle);
> >>> +
> >>> if (ret < 0) {
> >>> if (err_ctrl)
> >>> *err_ctrl = ctrl;
> >>> return ret;
> >>> }
> >>>
> >>> - if (!rollback && handle &&
> >>> - ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
> >>> - uvc_ctrl_set_handle(handle, ctrl, handle);
> >>> }
> >>>
> >>> return 0;
> >>> @@ -3222,6 +3234,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
> >>> void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
> >>> {
> >>> struct uvc_entity *entity;
> >>> + int i;
> >>>
> >>> guard(mutex)(&handle->chain->ctrl_mutex);
> >>>
> >>> @@ -3236,7 +3249,11 @@ void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
> >>> }
> >>> }
> >>>
> >>> - WARN_ON(handle->pending_async_ctrls);
> >>> + if (!WARN_ON(handle->pending_async_ctrls))
> >>> + return;
> >>> +
> >>> + for (i = 0; i < handle->pending_async_ctrls; i++)
> >>> + uvc_pm_put(handle->stream->dev);
> >>> }
> >>>
> >>> /*
> >>> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> >>> index 1d5be045d04ecbf17e65e14b390e494a294b735f..8bccf7e17528b62f2594c0dad99405034532973d 100644
> >>> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> >>> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> >>> @@ -697,6 +697,9 @@ static int uvc_v4l2_release(struct file *file)
> >>> if (uvc_has_privileges(handle))
> >>> uvc_queue_release(&stream->queue);
> >>>
> >>> + if (handle->is_streaming)
> >>> + uvc_pm_put(stream->dev);
> >>> +
> >>> /* Release the file handle. */
> >>> uvc_dismiss_privileges(handle);
> >>> v4l2_fh_del(&handle->vfh);
> >>> @@ -862,6 +865,11 @@ static int uvc_ioctl_streamon(struct file *file, void *fh,
> >>> if (ret)
> >>> return ret;
> >>>
> >>> + ret = uvc_pm_get(stream->dev);
> >>
> >> Shouldn't this be done before calling uvc_queue_streamon() ? There's
> >> another PM reference being held by the ioctl handler, but if the code is
> >> refactored later, it would be good to make sure we resume the device
> >> before starting streaming.
> >
> > I was trying to simplify the error handling and, as you say, the ioctl
> > handler already holds a reference. I do not mind sending a follow-up
> > patch changing the order.
One of the two uvc_queue_streamon() or uvc_pm_get() calls will need to
perform cleanup in case of error. As the cleanup should just be
uvc_pm_put(), it seems equally easy. I'd appreciate a follow-up patch.
> >>> + if (ret) {
> >>> + uvc_queue_streamoff(&stream->queue, type);
> >>> + return ret;
> >>> + }
> >>> handle->is_streaming = true;
> >>>
> >>> return 0;
> >>> @@ -879,7 +887,10 @@ static int uvc_ioctl_streamoff(struct file *file, void *fh,
> >>> guard(mutex)(&stream->mutex);
> >>>
> >>> uvc_queue_streamoff(&stream->queue, type);
> >>> - handle->is_streaming = false;
> >>> + if (handle->is_streaming) {
> >>> + handle->is_streaming = false;
> >>> + uvc_pm_put(stream->dev);
> >>> + }
> >>>
> >>> return 0;
> >>> }
> >>> @@ -1378,9 +1389,11 @@ static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
> >>> #define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
> >>> #define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32)
> >>>
> >>> +DEFINE_FREE(uvc_pm_put, struct uvc_device *, if (_T) uvc_pm_put(_T))
> >>> static long uvc_v4l2_compat_ioctl32(struct file *file,
> >>> unsigned int cmd, unsigned long arg)
> >>> {
> >>> + struct uvc_device *uvc_device __free(uvc_pm_put) = NULL;
> >>> struct uvc_fh *handle = file->private_data;
> >>> union {
> >>> struct uvc_xu_control_mapping xmap;
> >>> @@ -1389,6 +1402,12 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
> >>> void __user *up = compat_ptr(arg);
> >>> long ret;
> >>>
> >>> + ret = uvc_pm_get(handle->stream->dev);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + uvc_device = handle->stream->dev;
> >>
> >> Ouch... That's not nice very :-/
> >
> > IIt is nicer than changing the returns with breaks, believe me I tried
> > :), and it is more prone to errors.
> >
> > I thought about the CLASS, but it is not worth it with a single user.
> > I believe the current code is a good compromise, but I might be
> > biased.
Yes, a class may be a bit of an over-engineering. I'd prefer breaks
though.
> >> If you want to use the cleanup API, I think we could use guards with an
> >> init function such as
> >>
> >> struct uvc_device *__uvc_pm_get_init(struct uvc_device *dev, int *ret)
> >> {
> >> *ret = uvc_pm_get(dev);
> >> return *ret ? NULL : dev;
> >> }
> >>
> >> You can use DEFINE_CLASS() instead of DEFINE_GUARD() to control the
> >> arguments to the init function. Users of the guard could do
> >>
> >> int ret;
> >>
> >> guard(uvc_pm)(dev, &ret);
> >> if (ret)
> >> return ret;
> >>
> >> ...
> >>
> >> Another, simpler option would be to replace returns with breaks in
> >> uvc_v4l2_compat_ioctl32(). I'm tempted to do that in this patch, and
> >> switching to the cleanup API as a patch on top if desired.
> >>
> >>> +
> >>> switch (cmd) {
> >>> case UVCIOC_CTRL_MAP32:
> >>> ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
> >>> @@ -1423,6 +1442,22 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
> >>> }
> >>> #endif
> >>>
> >>> +static long uvc_v4l2_unlocked_ioctl(struct file *file,
> >>> + unsigned int cmd, unsigned long arg)
> >>> +{
> >>> + struct uvc_fh *handle = file->private_data;
> >>> + int ret;
> >>> +
> >>> + ret = uvc_pm_get(handle->stream->dev);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + ret = video_ioctl2(file, cmd, arg);
> >>> +
> >>> + uvc_pm_put(handle->stream->dev);
> >>> + return ret;
> >>> +}
> >>> +
> >>> static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
> >>> size_t count, loff_t *ppos)
> >>> {
> >>> @@ -1507,7 +1542,7 @@ const struct v4l2_file_operations uvc_fops = {
> >>> .owner = THIS_MODULE,
> >>> .open = uvc_v4l2_open,
> >>> .release = uvc_v4l2_release,
> >>> - .unlocked_ioctl = video_ioctl2,
> >>> + .unlocked_ioctl = uvc_v4l2_unlocked_ioctl,
> >>> #ifdef CONFIG_COMPAT
> >>> .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
> >>> #endif
--
Regards,
Laurent Pinchart
next prev parent reply other threads:[~2025-04-28 12:31 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-27 21:05 [PATCH v6 0/5] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
2025-03-27 21:05 ` [PATCH v6 1/5] media: uvcvideo: Keep streaming state in the file handle Ricardo Ribalda
2025-03-27 21:05 ` [PATCH v6 2/5] media: uvcvideo: Create uvc_pm_(get|put) functions Ricardo Ribalda
2025-04-22 18:17 ` Laurent Pinchart
2025-03-27 21:05 ` [PATCH v6 3/5] media: uvcvideo: Increase/decrease the PM counter per IOCTL Ricardo Ribalda
2025-04-22 20:37 ` Laurent Pinchart
2025-04-22 22:58 ` Ricardo Ribalda
2025-04-28 11:54 ` Hans de Goede
2025-04-28 12:31 ` Laurent Pinchart [this message]
2025-03-27 21:05 ` [PATCH v6 4/5] media: uvcvideo: Make power management granular Ricardo Ribalda
2025-03-27 21:05 ` [PATCH v6 5/5] media: uvcvideo: Do not turn on the camera for some ioctls Ricardo Ribalda
2025-05-09 13:44 ` Hans Verkuil
2025-05-09 13:51 ` Ricardo Ribalda
2025-05-28 18:05 ` Ricardo Ribalda
2025-04-07 11:41 ` [PATCH v6 0/5] media: uvcvideo: Implement Granular Power Saving Hans de Goede
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=20250428123117.GH3371@pendragon.ideasonboard.com \
--to=laurent.pinchart@ideasonboard.com \
--cc=guennadi.liakhovetski@intel.com \
--cc=hdegoede@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=mchehab+samsung@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.