* [PATCH 1/9] media: uvcvideo: Do not set an async control owned by other fh
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
@ 2024-11-26 16:18 ` Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 2/9] media: uvcvideo: Remove dangling pointers Ricardo Ribalda
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-26 16:18 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab,
Ricardo Ribalda, stable
If a file handle is waiting for a response from an async control, avoid
that other file handle operate with it.
Without this patch, the first file handle will never get the event
associated to that operation.
Cc: stable@vger.kernel.org
Fixes: e5225c820c05 ("media: uvcvideo: Send a control event when a Control Change interrupt arrives")
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvc_ctrl.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 4fe26e82e3d1..5d3a28edf7f0 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1950,6 +1950,10 @@ int uvc_ctrl_set(struct uvc_fh *handle,
if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
return -EACCES;
+ /* Other file handle is waiting a response from this async control. */
+ if (ctrl->handle && ctrl->handle != handle)
+ return -EBUSY;
+
/* Clamp out of range values. */
switch (mapping->v4l2_type) {
case V4L2_CTRL_TYPE_INTEGER:
--
2.47.0.338.g60cca15819-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 2/9] media: uvcvideo: Remove dangling pointers
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 1/9] media: uvcvideo: Do not set an async control owned by other fh Ricardo Ribalda
@ 2024-11-26 16:18 ` Ricardo Ribalda
2024-11-27 7:26 ` Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 3/9] media: uvcvideo: Keep streaming state in the file handle Ricardo Ribalda
` (7 subsequent siblings)
9 siblings, 1 reply; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-26 16:18 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab,
Ricardo Ribalda, stable
When an async control is written, we copy a pointer to the file handle
that started the operation. That pointer will be used when the device is
done. Which could be anytime in the future.
If the user closes that file descriptor, its structure will be freed,
and there will be one dangling pointer per pending async control, that
the driver will try to use.
Keep a counter of all the pending async controls and clean all the
dangling pointers during release().
Cc: stable@vger.kernel.org
Fixes: e5225c820c05 ("media: uvcvideo: Send a control event when a Control Change interrupt arrives")
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvc_ctrl.c | 40 ++++++++++++++++++++++++++++++++++++++--
drivers/media/usb/uvc/uvc_v4l2.c | 2 ++
drivers/media/usb/uvc/uvcvideo.h | 3 +++
3 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 5d3a28edf7f0..11287e81d91c 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1589,7 +1589,12 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain,
mutex_lock(&chain->ctrl_mutex);
handle = ctrl->handle;
- ctrl->handle = NULL;
+ if (handle) {
+ ctrl->handle = NULL;
+ WARN_ON(!handle->pending_async_ctrls)
+ if (handle->pending_async_ctrls)
+ handle->pending_async_ctrls--;
+ }
list_for_each_entry(mapping, &ctrl->info.mappings, list) {
s32 value = __uvc_ctrl_get_value(mapping, data);
@@ -2050,8 +2055,11 @@ int uvc_ctrl_set(struct uvc_fh *handle,
mapping->set(mapping, value,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
- if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
+ if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) {
+ if (!ctrl->handle)
+ handle->pending_async_ctrls++;
ctrl->handle = handle;
+ }
ctrl->dirty = 1;
ctrl->modified = 1;
@@ -2774,6 +2782,34 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
return 0;
}
+void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
+{
+ struct uvc_entity *entity;
+
+ guard(mutex)(&handle->chain->ctrl_mutex);
+
+ if (!handle->pending_async_ctrls)
+ return;
+
+ list_for_each_entry(entity, &handle->chain->dev->entities, list) {
+ int i;
+
+ for (i = 0; i < entity->ncontrols; ++i) {
+ struct uvc_control *ctrl = &entity->controls[i];
+
+ if (!ctrl->handle || ctrl->handle != handle)
+ continue;
+
+ ctrl->handle = NULL;
+ if (WARN_ON(!handle->pending_async_ctrls))
+ continue;
+ handle->pending_async_ctrls--;
+ }
+ }
+
+ WARN_ON(handle->pending_async_ctrls);
+}
+
/*
* Cleanup device controls.
*/
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 97c5407f6603..b425306a3b8c 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -652,6 +652,8 @@ static int uvc_v4l2_release(struct file *file)
uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
+ uvc_ctrl_cleanup_fh(handle);
+
/* Only free resources if this is a privileged handle. */
if (uvc_has_privileges(handle))
uvc_queue_release(&stream->queue);
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 07f9921d83f2..2f8a9c48e32a 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -612,6 +612,7 @@ struct uvc_fh {
struct uvc_video_chain *chain;
struct uvc_streaming *stream;
enum uvc_handle_state state;
+ unsigned int pending_async_ctrls; /* Protected by ctrl_mutex. */
};
struct uvc_driver {
@@ -797,6 +798,8 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry);
+void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
+
/* Utility functions */
struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
u8 epaddr);
--
2.47.0.338.g60cca15819-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH 2/9] media: uvcvideo: Remove dangling pointers
2024-11-26 16:18 ` [PATCH 2/9] media: uvcvideo: Remove dangling pointers Ricardo Ribalda
@ 2024-11-27 7:26 ` Ricardo Ribalda
0 siblings, 0 replies; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-27 7:26 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab, stable
[Resending in plain text, seem like today is not may day]
On Tue, 26 Nov 2024 at 17:20, Ricardo Ribalda <ribalda@chromium.org> wrote:
>
> When an async control is written, we copy a pointer to the file handle
> that started the operation. That pointer will be used when the device is
> done. Which could be anytime in the future.
>
> If the user closes that file descriptor, its structure will be freed,
> and there will be one dangling pointer per pending async control, that
> the driver will try to use.
>
> Keep a counter of all the pending async controls and clean all the
> dangling pointers during release().
>
> Cc: stable@vger.kernel.org
> Fixes: e5225c820c05 ("media: uvcvideo: Send a control event when a Control Change interrupt arrives")
> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
> drivers/media/usb/uvc/uvc_ctrl.c | 40 ++++++++++++++++++++++++++++++++++++++--
> drivers/media/usb/uvc/uvc_v4l2.c | 2 ++
> drivers/media/usb/uvc/uvcvideo.h | 3 +++
> 3 files changed, 43 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 5d3a28edf7f0..11287e81d91c 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1589,7 +1589,12 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain,
> mutex_lock(&chain->ctrl_mutex);
>
> handle = ctrl->handle;
> - ctrl->handle = NULL;
> + if (handle) {
> + ctrl->handle = NULL;
> + WARN_ON(!handle->pending_async_ctrls)
There is obviously a missing semicolon here.
I screwed it up when I reordered the patches to move it to the first
part of the set.
Luckily we have CI :).
You can see the fixed version here:
https://gitlab.freedesktop.org/linux-media/users/ribalda/-/commits/b4/uvc-granpower-ng
I do not plan to resend the whole series until there are more comments.
But I am sending the first two patches as a new set, they are fixes. I
will also send the last patch alone, it is unrelated to this.
> + if (handle->pending_async_ctrls)
> + handle->pending_async_ctrls--;
> + }
>
> list_for_each_entry(mapping, &ctrl->info.mappings, list) {
> s32 value = __uvc_ctrl_get_value(mapping, data);
> @@ -2050,8 +2055,11 @@ int uvc_ctrl_set(struct uvc_fh *handle,
> mapping->set(mapping, value,
> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
>
> - if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
> + if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) {
> + if (!ctrl->handle)
> + handle->pending_async_ctrls++;
> ctrl->handle = handle;
> + }
>
> ctrl->dirty = 1;
> ctrl->modified = 1;
> @@ -2774,6 +2782,34 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
> return 0;
> }
>
> +void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
> +{
> + struct uvc_entity *entity;
> +
> + guard(mutex)(&handle->chain->ctrl_mutex);
> +
> + if (!handle->pending_async_ctrls)
> + return;
> +
> + list_for_each_entry(entity, &handle->chain->dev->entities, list) {
> + int i;
> +
> + for (i = 0; i < entity->ncontrols; ++i) {
> + struct uvc_control *ctrl = &entity->controls[i];
> +
> + if (!ctrl->handle || ctrl->handle != handle)
> + continue;
> +
> + ctrl->handle = NULL;
> + if (WARN_ON(!handle->pending_async_ctrls))
> + continue;
> + handle->pending_async_ctrls--;
> + }
> + }
> +
> + WARN_ON(handle->pending_async_ctrls);
> +}
> +
> /*
> * Cleanup device controls.
> */
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 97c5407f6603..b425306a3b8c 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -652,6 +652,8 @@ static int uvc_v4l2_release(struct file *file)
>
> uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
>
> + uvc_ctrl_cleanup_fh(handle);
> +
> /* Only free resources if this is a privileged handle. */
> if (uvc_has_privileges(handle))
> uvc_queue_release(&stream->queue);
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 07f9921d83f2..2f8a9c48e32a 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -612,6 +612,7 @@ struct uvc_fh {
> struct uvc_video_chain *chain;
> struct uvc_streaming *stream;
> enum uvc_handle_state state;
> + unsigned int pending_async_ctrls; /* Protected by ctrl_mutex. */
> };
>
> struct uvc_driver {
> @@ -797,6 +798,8 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
> struct uvc_xu_control_query *xqry);
>
> +void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
> +
> /* Utility functions */
> struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
> u8 epaddr);
>
> --
> 2.47.0.338.g60cca15819-goog
>
--
Ricardo Ribalda
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/9] media: uvcvideo: Keep streaming state in the file handle
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 1/9] media: uvcvideo: Do not set an async control owned by other fh Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 2/9] media: uvcvideo: Remove dangling pointers Ricardo Ribalda
@ 2024-11-26 16:18 ` Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 4/9] media: uvcvideo: Move usb_autopm_(get|put)_interface to status_get Ricardo Ribalda
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-26 16:18 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab, Ricardo Ribalda
Add a variable in the file handle state to figure out if a camera is in
the streaming state or not.
This variable will be used in the future for power management policies.
Now that we are at it, make use of guards to simplify the code.
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvc_v4l2.c | 15 +++++++++++----
drivers/media/usb/uvc/uvcvideo.h | 1 +
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index b425306a3b8c..712a531e52f9 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -888,11 +888,17 @@ static int uvc_ioctl_streamon(struct file *file, void *fh,
if (!uvc_has_privileges(handle))
return -EBUSY;
- mutex_lock(&stream->mutex);
+ guard(mutex)(&stream->mutex);
+
+ if (handle->is_streaming)
+ return 0;
+
ret = uvc_queue_streamon(&stream->queue, type);
- mutex_unlock(&stream->mutex);
+ if (!ret)
+ handle->is_streaming = true;
return ret;
+
}
static int uvc_ioctl_streamoff(struct file *file, void *fh,
@@ -904,9 +910,10 @@ static int uvc_ioctl_streamoff(struct file *file, void *fh,
if (!uvc_has_privileges(handle))
return -EBUSY;
- mutex_lock(&stream->mutex);
+ guard(mutex)(&stream->mutex);
+
uvc_queue_streamoff(&stream->queue, type);
- mutex_unlock(&stream->mutex);
+ handle->is_streaming = false;
return 0;
}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 2f8a9c48e32a..b63194a9c64e 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -613,6 +613,7 @@ struct uvc_fh {
struct uvc_streaming *stream;
enum uvc_handle_state state;
unsigned int pending_async_ctrls; /* Protected by ctrl_mutex. */
+ bool is_streaming;
};
struct uvc_driver {
--
2.47.0.338.g60cca15819-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 4/9] media: uvcvideo: Move usb_autopm_(get|put)_interface to status_get
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
` (2 preceding siblings ...)
2024-11-26 16:18 ` [PATCH 3/9] media: uvcvideo: Keep streaming state in the file handle Ricardo Ribalda
@ 2024-11-26 16:18 ` Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 5/9] media: uvcvideo: Add a uvc_status guard Ricardo Ribalda
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-26 16:18 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab, Ricardo Ribalda
Right now PM operations are always called at the same locations as
uvc_status_(get|put).
Combine them into uvc_status_(get|put). This simplifies the current
code and future PM changes in the driver.
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvc_status.c | 38 +++++++++++++++++++++++++++++++++-----
drivers/media/usb/uvc/uvc_v4l2.c | 11 +----------
2 files changed, 34 insertions(+), 15 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index 06c867510c8f..f7531ed0fa84 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -378,7 +378,7 @@ void uvc_status_suspend(struct uvc_device *dev)
uvc_status_stop(dev);
}
-int uvc_status_get(struct uvc_device *dev)
+static int _uvc_status_get(struct uvc_device *dev)
{
int ret;
@@ -395,13 +395,41 @@ int uvc_status_get(struct uvc_device *dev)
return 0;
}
-void uvc_status_put(struct uvc_device *dev)
+int uvc_status_get(struct uvc_device *dev)
+{
+ int ret;
+
+ ret = usb_autopm_get_interface(dev->intf);
+ if (ret)
+ return ret;
+
+ ret = _uvc_status_get(dev);
+
+ if (ret)
+ usb_autopm_put_interface(dev->intf);
+
+ return ret;
+}
+
+static int _uvc_status_put(struct uvc_device *dev)
{
guard(mutex)(&dev->status_lock);
if (dev->status_users == 1)
uvc_status_stop(dev);
- WARN_ON(!dev->status_users);
- if (dev->status_users)
- dev->status_users--;
+
+ if (WARN_ON(!dev->status_users))
+ return -EIO;
+
+ dev->status_users--;
+ return 0;
+}
+
+void uvc_status_put(struct uvc_device *dev)
+{
+ int ret;
+
+ ret = _uvc_status_put(dev);
+ if (!ret)
+ usb_autopm_put_interface(dev->intf);
}
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 712a531e52f9..1d4db712137e 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -617,20 +617,13 @@ static int uvc_v4l2_open(struct file *file)
stream = video_drvdata(file);
uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
- ret = usb_autopm_get_interface(stream->dev->intf);
- if (ret < 0)
- return ret;
-
/* Create the device handle. */
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
- if (handle == NULL) {
- usb_autopm_put_interface(stream->dev->intf);
+ if (!handle)
return -ENOMEM;
- }
ret = uvc_status_get(stream->dev);
if (ret) {
- usb_autopm_put_interface(stream->dev->intf);
kfree(handle);
return ret;
}
@@ -666,8 +659,6 @@ static int uvc_v4l2_release(struct file *file)
file->private_data = NULL;
uvc_status_put(stream->dev);
-
- usb_autopm_put_interface(stream->dev->intf);
return 0;
}
--
2.47.0.338.g60cca15819-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 5/9] media: uvcvideo: Add a uvc_status guard
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
` (3 preceding siblings ...)
2024-11-26 16:18 ` [PATCH 4/9] media: uvcvideo: Move usb_autopm_(get|put)_interface to status_get Ricardo Ribalda
@ 2024-11-26 16:18 ` Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 6/9] media: uvcvideo: Increase/decrease the PM counter per IOCTL Ricardo Ribalda
` (4 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-26 16:18 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab, Ricardo Ribalda
It helps will help will the error handling of PM functions.
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvcvideo.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index b63194a9c64e..c38ddd10307c 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -759,6 +759,8 @@ void uvc_status_suspend(struct uvc_device *dev);
int uvc_status_get(struct uvc_device *dev);
void uvc_status_put(struct uvc_device *dev);
+DEFINE_GUARD(uvc_status, struct uvc_device *, uvc_status_get(_T), uvc_status_put(_T))
+
/* Controls */
extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
--
2.47.0.338.g60cca15819-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 6/9] media: uvcvideo: Increase/decrease the PM counter per IOCTL
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
` (4 preceding siblings ...)
2024-11-26 16:18 ` [PATCH 5/9] media: uvcvideo: Add a uvc_status guard Ricardo Ribalda
@ 2024-11-26 16:18 ` Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 7/9] media: uvcvideo: Make power management granular Ricardo Ribalda
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-26 16:18 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab, Ricardo Ribalda
Now we call uvc_status_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_status_get/put from open/close.
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvc_ctrl.c | 18 +++++++++++++-----
drivers/media/usb/uvc/uvc_v4l2.c | 26 +++++++++++++++++++++++---
2 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 11287e81d91c..2e2aa1b2f53d 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1592,8 +1592,10 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain,
if (handle) {
ctrl->handle = NULL;
WARN_ON(!handle->pending_async_ctrls)
- if (handle->pending_async_ctrls)
+ if (handle->pending_async_ctrls) {
handle->pending_async_ctrls--;
+ uvc_status_put(chain->dev);
+ }
}
list_for_each_entry(mapping, &ctrl->info.mappings, list) {
@@ -2056,8 +2058,10 @@ int uvc_ctrl_set(struct uvc_fh *handle,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) {
- if (!ctrl->handle)
+ if (!ctrl->handle) {
handle->pending_async_ctrls++;
+ uvc_status_get(chain->dev);
+ }
ctrl->handle = handle;
}
@@ -2785,6 +2789,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);
@@ -2792,8 +2797,6 @@ void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
return;
list_for_each_entry(entity, &handle->chain->dev->entities, list) {
- int i;
-
for (i = 0; i < entity->ncontrols; ++i) {
struct uvc_control *ctrl = &entity->controls[i];
@@ -2804,10 +2807,15 @@ void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
if (WARN_ON(!handle->pending_async_ctrls))
continue;
handle->pending_async_ctrls--;
+ uvc_status_put(handle->stream->dev);
}
}
- WARN_ON(handle->pending_async_ctrls);
+ if (!WARN_ON(handle->pending_async_ctrls))
+ return;
+
+ for (i = 0; i < handle->pending_async_ctrls; i++)
+ uvc_status_put(handle->stream->dev);
}
/*
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 1d4db712137e..1972ede38c70 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -651,6 +651,9 @@ static int uvc_v4l2_release(struct file *file)
if (uvc_has_privileges(handle))
uvc_queue_release(&stream->queue);
+ if (handle->is_streaming)
+ uvc_status_put(stream->dev);
+
/* Release the file handle. */
uvc_dismiss_privileges(handle);
v4l2_fh_del(&handle->vfh);
@@ -885,8 +888,10 @@ static int uvc_ioctl_streamon(struct file *file, void *fh,
return 0;
ret = uvc_queue_streamon(&stream->queue, type);
- if (!ret)
+ if (!ret) {
handle->is_streaming = true;
+ uvc_status_get(stream->dev);
+ }
return ret;
@@ -904,7 +909,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_status_put(stream->dev);
+ }
return 0;
}
@@ -1450,6 +1458,16 @@ static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
return 0;
}
+static long uvc_v4l2_video_ioctl2(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct uvc_fh *handle = file->private_data;
+
+ guard(uvc_status)(handle->stream->dev);
+
+ return video_ioctl2(file, cmd, arg);
+}
+
#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32)
@@ -1464,6 +1482,8 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
void __user *up = compat_ptr(arg);
long ret;
+ guard(uvc_status)(handle->stream->dev);
+
switch (cmd) {
case UVCIOC_CTRL_MAP32:
ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
@@ -1583,7 +1603,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_video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = uvc_v4l2_compat_ioctl32,
#endif
--
2.47.0.338.g60cca15819-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 7/9] media: uvcvideo: Make power management granular
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
` (5 preceding siblings ...)
2024-11-26 16:18 ` [PATCH 6/9] media: uvcvideo: Increase/decrease the PM counter per IOCTL Ricardo Ribalda
@ 2024-11-26 16:18 ` Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 8/9] media: uvcvideo: Do not turn on the camera for some ioctls Ricardo Ribalda
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-26 16:18 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab, Ricardo Ribalda
Now that every ioctl takes care of their power management we can remove
the "global" power management.
Despite its size, this is a relatively big change. We hope that there
are no size effects of it. If there are some specific devices that
miss-behave, we can add a small quirk for them.
This patch introduces a behavioral change for the uvc "trigger" button.
It will not work unless the camera is streaming. We consider that this
the most common (if not the only) usecase and therefore we do not consider
it a regression.
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvc_v4l2.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 1972ede38c70..3595f67fd7e2 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -612,7 +612,6 @@ static int uvc_v4l2_open(struct file *file)
{
struct uvc_streaming *stream;
struct uvc_fh *handle;
- int ret = 0;
stream = video_drvdata(file);
uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
@@ -622,12 +621,6 @@ static int uvc_v4l2_open(struct file *file)
if (!handle)
return -ENOMEM;
- ret = uvc_status_get(stream->dev);
- if (ret) {
- kfree(handle);
- return ret;
- }
-
v4l2_fh_init(&handle->vfh, &stream->vdev);
v4l2_fh_add(&handle->vfh);
handle->chain = stream->chain;
@@ -661,7 +654,6 @@ static int uvc_v4l2_release(struct file *file)
kfree(handle);
file->private_data = NULL;
- uvc_status_put(stream->dev);
return 0;
}
--
2.47.0.338.g60cca15819-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 8/9] media: uvcvideo: Do not turn on the camera for some ioctls
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
` (6 preceding siblings ...)
2024-11-26 16:18 ` [PATCH 7/9] media: uvcvideo: Make power management granular Ricardo Ribalda
@ 2024-11-26 16:18 ` Ricardo Ribalda
2024-11-26 16:18 ` [PATCH 9/9] media: uvcvideo: Remove duplicated cap/out code Ricardo Ribalda
2024-12-02 12:10 ` [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Hans de Goede
9 siblings, 0 replies; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-26 16:18 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab, Ricardo Ribalda
There are some ioctls that do not need to turn on the camera. Do not
call uvc_status_get in those cases.
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvc_v4l2.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 3595f67fd7e2..0a058798d075 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1455,6 +1455,26 @@ static long uvc_v4l2_video_ioctl2(struct file *file,
{
struct uvc_fh *handle = file->private_data;
+ /* The following IOCTLs do not need to turn on the camera. */
+ switch (cmd) {
+ case VIDIOC_CREATE_BUFS:
+ case VIDIOC_DQBUF:
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ case VIDIOC_ENUM_FRAMESIZES:
+ case VIDIOC_ENUMINPUT:
+ case VIDIOC_EXPBUF:
+ case VIDIOC_G_FMT:
+ case VIDIOC_G_PARM:
+ case VIDIOC_G_SELECTION:
+ case VIDIOC_QBUF:
+ case VIDIOC_QUERYCAP:
+ case VIDIOC_REQBUFS:
+ case VIDIOC_SUBSCRIBE_EVENT:
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ return video_ioctl2(file, cmd, arg);
+ }
+
guard(uvc_status)(handle->stream->dev);
return video_ioctl2(file, cmd, arg);
--
2.47.0.338.g60cca15819-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 9/9] media: uvcvideo: Remove duplicated cap/out code
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
` (7 preceding siblings ...)
2024-11-26 16:18 ` [PATCH 8/9] media: uvcvideo: Do not turn on the camera for some ioctls Ricardo Ribalda
@ 2024-11-26 16:18 ` Ricardo Ribalda
2024-12-02 12:10 ` [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Hans de Goede
9 siblings, 0 replies; 12+ messages in thread
From: Ricardo Ribalda @ 2024-11-26 16:18 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab, Ricardo Ribalda
The *_vid_cap and *_vid_out helpers seem to be identical. Remove all the
duplicated code.
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
drivers/media/usb/uvc/uvc_v4l2.c | 112 ++++++++-------------------------------
1 file changed, 22 insertions(+), 90 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 0a058798d075..cc32ce3275da 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -361,9 +361,11 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
return ret;
}
-static int uvc_v4l2_get_format(struct uvc_streaming *stream,
- struct v4l2_format *fmt)
+static int uvc_ioctl_g_fmt(struct file *file, void *fh,
+ struct v4l2_format *fmt)
{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
const struct uvc_format *format;
const struct uvc_frame *frame;
int ret = 0;
@@ -395,9 +397,11 @@ static int uvc_v4l2_get_format(struct uvc_streaming *stream,
return ret;
}
-static int uvc_v4l2_set_format(struct uvc_streaming *stream,
- struct v4l2_format *fmt)
+static int uvc_ioctl_s_fmt(struct file *file, void *fh,
+ struct v4l2_format *fmt)
{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
struct uvc_streaming_control probe;
const struct uvc_format *format;
const struct uvc_frame *frame;
@@ -673,11 +677,13 @@ static int uvc_ioctl_querycap(struct file *file, void *fh,
return 0;
}
-static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
+static int uvc_ioctl_enum_fmt(struct file *file, void *fh,
struct v4l2_fmtdesc *fmt)
{
- const struct uvc_format *format;
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
enum v4l2_buf_type type = fmt->type;
+ const struct uvc_format *format;
u32 index = fmt->index;
if (fmt->type != stream->type || fmt->index >= stream->nformats)
@@ -695,82 +701,8 @@ static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
return 0;
}
-static int uvc_ioctl_enum_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_fmtdesc *fmt)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- return uvc_ioctl_enum_fmt(stream, fmt);
-}
-
-static int uvc_ioctl_enum_fmt_vid_out(struct file *file, void *fh,
- struct v4l2_fmtdesc *fmt)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- return uvc_ioctl_enum_fmt(stream, fmt);
-}
-
-static int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- return uvc_v4l2_get_format(stream, fmt);
-}
-
-static int uvc_ioctl_g_fmt_vid_out(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- return uvc_v4l2_get_format(stream, fmt);
-}
-
-static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
- int ret;
-
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
- return uvc_v4l2_set_format(stream, fmt);
-}
-
-static int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
- int ret;
-
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
- return uvc_v4l2_set_format(stream, fmt);
-}
-
-static int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
- struct uvc_streaming_control probe;
-
- return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
-}
-
-static int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh,
- struct v4l2_format *fmt)
+static int uvc_ioctl_try_fmt(struct file *file, void *fh,
+ struct v4l2_format *fmt)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
@@ -1576,14 +1508,14 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_querycap = uvc_ioctl_querycap,
- .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap,
- .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap,
- .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out,
- .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,
- .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out,
- .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,
- .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out,
+ .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt,
+ .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt,
+ .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt,
+ .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt,
+ .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt,
+ .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt,
+ .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt,
+ .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt,
.vidioc_reqbufs = uvc_ioctl_reqbufs,
.vidioc_querybuf = uvc_ioctl_querybuf,
.vidioc_qbuf = uvc_ioctl_qbuf,
--
2.47.0.338.g60cca15819-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving
2024-11-26 16:18 [PATCH 0/9] media: uvcvideo: Implement Granular Power Saving Ricardo Ribalda
` (8 preceding siblings ...)
2024-11-26 16:18 ` [PATCH 9/9] media: uvcvideo: Remove duplicated cap/out code Ricardo Ribalda
@ 2024-12-02 12:10 ` Hans de Goede
9 siblings, 0 replies; 12+ messages in thread
From: Hans de Goede @ 2024-12-02 12:10 UTC (permalink / raw)
To: Ricardo Ribalda, Laurent Pinchart, Mauro Carvalho Chehab,
Guennadi Liakhovetski
Cc: linux-media, linux-kernel, Mauro Carvalho Chehab, stable
Hi Ricardo,
On 26-Nov-24 5:18 PM, Ricardo Ribalda wrote:
> Right now we power-up the device when a user open() the device and we
> power it off when the last user close() the first video node.
>
> This behaviour affects the power consumption of the device is multiple
> use cases, such as:
> - Polling the privacy gpio
> - udev probing the device
>
> This patchset introduces a more granular power saving behaviour where
> the camera is only awaken when needed. It is compatible with
> asynchronous controls.
>
> While developing this patchset, two bugs were found. The patchset has
> been developed so these fixes can be taken independently.
Thank you for your patch series. For now lets focus on fixing the
async-controls ctrl->handle setting / dangling ptr issue and then
we can look into the rest of this later (after we have also landed
the privacy GPIO and UVC 1.5 ROi series).
Regards,
Hans
> ---
> Ricardo Ribalda (9):
> media: uvcvideo: Do not set an async control owned by other fh
> media: uvcvideo: Remove dangling pointers
> media: uvcvideo: Keep streaming state in the file handle
> media: uvcvideo: Move usb_autopm_(get|put)_interface to status_get
> media: uvcvideo: Add a uvc_status guard
> media: uvcvideo: Increase/decrease the PM counter per IOCTL
> media: uvcvideo: Make power management granular
> media: uvcvideo: Do not turn on the camera for some ioctls
> media: uvcvideo: Remove duplicated cap/out code
>
> drivers/media/usb/uvc/uvc_ctrl.c | 52 +++++++++-
> drivers/media/usb/uvc/uvc_status.c | 38 +++++++-
> drivers/media/usb/uvc/uvc_v4l2.c | 190 +++++++++++++++----------------------
> drivers/media/usb/uvc/uvcvideo.h | 6 ++
> 4 files changed, 166 insertions(+), 120 deletions(-)
> ---
> base-commit: 72ad4ff638047bbbdf3232178fea4bec1f429319
> change-id: 20241126-uvc-granpower-ng-069185a6d474
>
> Best regards,
^ permalink raw reply [flat|nested] 12+ messages in thread