* [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes
@ 2024-09-29 18:59 Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 1/9] usb: gadget: uvc: wake pump everytime we update the free list Michael Grzeschik
` (9 more replies)
0 siblings, 10 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
This patch series is improving the size calculation and allocation of
the uvc requests. Using the selected frame duration of the stream it is
possible to calculate the number of requests based on the interval
length.
It also precalculates the request length based on the actual per frame
size for compressed formats.
For this calculations to work it was needed to rework the request
queueing by moving the encoding to one extra thread (in this case we
chose the qbuf) context.
Next it was needed to move the actual request enqueueing to one extra
thread which is kept busy to fill the isoc queue in the udc.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
Changes in v6:
- fixes in: ("usb: gadget: uvc: add trace of enqueued and completed requests")
- Link to v5: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v5-0-2de78794365c@pengutronix.de
Changes in v5:
- dropped: ('usb: gadget: uvc: remove pump worker and enqueue all buffers per frame in qbuf')
- squashed ('usb: gadget: uvc: rework to enqueue in pump worker from encoded queue') and ('usb: gadget: uvc: remove uvc_video_ep_queue_initial_requests'))
- squashed ('usb: gadget: uvc: set req_size once when the vb2 queue is calculated') and ('usb: gadget: uvc: set req_size and n_requests based on the frame interval')
- replaced ('usb: gadget: uvc: add min g_ctrl vidioc and set min buffs to 4') with ('usb: gadget: uvc: set nbuffers to minimum STREAMING_MIN_BUFFERS in uvc_queue_setup')
- added ('usb: gadget: uvc: wake pump everytime we update the free list')
- added ('usb: gadget: uvc: add trace of enqueued and completed requests')
- added ('usb: gadget: uvc: dont call usb_composite_setup_continue when not streamin')
- some patch reordering
- Link to v4: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v4-0-ca22f334226e@pengutronix.de
Changes in v4:
- fixed exit path in uvc_enqueue_buffer on loop break
- Link to v3: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v3-0-4da7033dd488@pengutronix.de
Changes in v3:
- Added more patches necessary to properly rework the request queueing
- Link to v2: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v2-0-12690f7a2eff@pengutronix.de
Changes in v2:
- added header size into calculation of request size
- Link to v1: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v1-0-9436c4716233@pengutronix.de
---
Michael Grzeschik (9):
usb: gadget: uvc: wake pump everytime we update the free list
usb: gadget: uvc: only enqueue zero length requests in potential underrun
usb: gadget: uvc: rework to enqueue in pump worker from encoded queue
usb: gadget: uvc: add g_parm and s_parm for frame interval
usb: gadget: uvc: set req_size and n_requests based on the frame interval
usb: gadget: uvc: set req_length based on payload by nreqs instead of req_size
usb: gadget: uvc: set nbuffers to minimum STREAMING_MIN_BUFFERS in uvc_queue_setup
usb: gadget: uvc: add trace of enqueued and completed requests
usb: gadget: uvc: dont call usb_composite_setup_continue when not streaming
drivers/usb/gadget/function/Makefile | 3 +
drivers/usb/gadget/function/f_uvc.c | 2 +
drivers/usb/gadget/function/uvc.h | 13 ++
drivers/usb/gadget/function/uvc_queue.c | 26 ++--
drivers/usb/gadget/function/uvc_queue.h | 2 +
drivers/usb/gadget/function/uvc_trace.c | 11 ++
drivers/usb/gadget/function/uvc_trace.h | 60 ++++++++
drivers/usb/gadget/function/uvc_v4l2.c | 55 +++++++
drivers/usb/gadget/function/uvc_video.c | 264 +++++++++++++++++++-------------
9 files changed, 316 insertions(+), 120 deletions(-)
---
base-commit: 68d4209158f43a558c5553ea95ab0c8975eab18c
change-id: 20240403-uvc_request_length_by_interval-a7efd587d963
Best regards,
--
Michael Grzeschik <m.grzeschik@pengutronix.de>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v6 1/9] usb: gadget: uvc: wake pump everytime we update the free list
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
@ 2024-09-29 18:59 ` Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 2/9] usb: gadget: uvc: only enqueue zero length requests in potential underrun Michael Grzeschik
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
Since the req_free list will updated if enqueuing one request was not
possible it will be added back to the free list. With every available
free request in the queue it is a valid case for the pump worker to use
it and continue the pending bufferdata into requests for the req_ready
list.
Fixes: 6acba0345b68 ("usb:gadget:uvc Do not use worker thread to pump isoc usb requests")
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
v5 -> v6: -
v1 -> v5: - new patch
---
drivers/usb/gadget/function/uvc_video.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index a9edd60fbbf77..48fd8d3c50b0c 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -480,6 +480,10 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
* up later.
*/
list_add_tail(&to_queue->list, &video->req_free);
+ /*
+ * There is a new free request - wake up the pump.
+ */
+ queue_work(video->async_wq, &video->pump);
}
spin_unlock_irqrestore(&video->req_lock, flags);
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 2/9] usb: gadget: uvc: only enqueue zero length requests in potential underrun
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 1/9] usb: gadget: uvc: wake pump everytime we update the free list Michael Grzeschik
@ 2024-09-29 18:59 ` Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 3/9] usb: gadget: uvc: rework to enqueue in pump worker from encoded queue Michael Grzeschik
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
The complete handler will at least be called after 16 requests have
completed, but will still handle all finisher requests. Since we have
to maintain a costant filling in the isoc queue we ensure this by
adding zero length requests.
By counting the amount enqueued requests we can ensure that the queue is
never underrun and only need to get active if the queue is running
critical. This patch is setting 32 as the critical level, which
is twice the request amount that is needed to create interrupts.
To properly solve the amount of zero length requests that needs to
be held in the hardware after one interrupt needs to be measured
and depends on the runtime of the first enqueue run after the interrupt
triggered. For now we just use twice the amount of requests between an
interrupt.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
v5 -> v6: -
v4 -> v5:
- using min to limit for UVCG_REQ_MAX_INT_COUNT as the interrupt boundary
- using atomic_inc, atomic_dec on one variable to avoid rollover
- reordered this patch in the series
- added UVCG_REQ_MAX_ZERO_COUNT to set the threshold of zero length
requests in the hw
- added UVCG_REQ_MAX_INT_COUNT as quantifier for the
highest amount of request between an interrupt
v1 -> v4: -
---
drivers/usb/gadget/function/uvc.h | 5 +++++
drivers/usb/gadget/function/uvc_video.c | 17 ++++++++++++++++-
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index cb35687b11e7e..55d796f5f5e8d 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -71,6 +71,9 @@ extern unsigned int uvc_gadget_trace_param;
#define UVCG_REQUEST_HEADER_LEN 12
+#define UVCG_REQ_MAX_INT_COUNT 16
+#define UVCG_REQ_MAX_ZERO_COUNT (2 * UVCG_REQ_MAX_INT_COUNT)
+
/* ------------------------------------------------------------------------
* Structures
*/
@@ -91,6 +94,8 @@ struct uvc_video {
struct work_struct pump;
struct workqueue_struct *async_wq;
+ atomic_t queued;
+
/* Frame parameters */
u8 bpp;
u32 fcc;
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 48fd8d3c50b0c..eb82aaed6ba13 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -269,6 +269,8 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
}
}
+ atomic_inc(&video->queued);
+
return ret;
}
@@ -304,7 +306,7 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video,
*/
if (list_empty(&video->req_free) || ureq->last_buf ||
!(video->req_int_count %
- DIV_ROUND_UP(video->uvc_num_requests, 4))) {
+ min(DIV_ROUND_UP(video->uvc_num_requests, 4), UVCG_REQ_MAX_INT_COUNT))) {
video->req_int_count = 0;
req->no_interrupt = 0;
} else {
@@ -379,6 +381,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
int ret = 0;
spin_lock_irqsave(&video->req_lock, flags);
+ atomic_dec(&video->queued);
if (!video->is_enabled) {
/*
* When is_enabled is false, uvcg_video_disable() ensures
@@ -466,6 +469,16 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
* happen.
*/
queue_work(video->async_wq, &video->pump);
+ } else if (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT) {
+ list_add_tail(&to_queue->list, &video->req_free);
+ /*
+ * There is a new free request - wake up the pump.
+ */
+ queue_work(video->async_wq, &video->pump);
+
+ spin_unlock_irqrestore(&video->req_lock, flags);
+
+ return;
}
/*
* Queue to the endpoint. The actual queueing to ep will
@@ -756,6 +769,8 @@ int uvcg_video_enable(struct uvc_video *video)
video->req_int_count = 0;
+ atomic_set(&video->queued, 0);
+
uvc_video_ep_queue_initial_requests(video);
queue_work(video->async_wq, &video->pump);
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 3/9] usb: gadget: uvc: rework to enqueue in pump worker from encoded queue
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 1/9] usb: gadget: uvc: wake pump everytime we update the free list Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 2/9] usb: gadget: uvc: only enqueue zero length requests in potential underrun Michael Grzeschik
@ 2024-09-29 18:59 ` Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 4/9] usb: gadget: uvc: add g_parm and s_parm for frame interval Michael Grzeschik
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
We install an kthread with pfifo priority that is iterating over all
prepared requests and keeps the isoc queue busy. This way it will be
scheduled with the same priority as the interrupt handler.
As the kthread is triggered with video_enable it will immediately
queue some zero length requests into the hw if there is no buffer data
available. It also watches the level of needed zero length requests in
the hardware not to fall under the UVCG_REQ_MAX_ZERO_COUNT threshold.
This way we can drop the function uvc_video_ep_queue_initial_requests
entirely.
By using the kthread to do the actual request handling the interrupt
handler will not be running into the time consuming and eventually
locking work of actually enqueueing the requests back into its own
pipeline. This work can now even be scheduled on another cpu.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
v5 -> v6: -
v4 -> v5:
- merging patch '(drivers/usb/gadget/function/uvc_queue.h') into this
one
- added initial kthread_queue_work(video->kworker, &video->hw_submit);
to uvcg_video_enable
- fixed error message in uvcg_video_init
- reordered this patch in the series
- renamed the kworker to hw_submit since the pump thread is used again
with the work_queue
v1 -> v4: -
---
drivers/usb/gadget/function/f_uvc.c | 2 +
drivers/usb/gadget/function/uvc.h | 3 +
drivers/usb/gadget/function/uvc_video.c | 180 +++++++++++++++-----------------
3 files changed, 87 insertions(+), 98 deletions(-)
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 40187b7112e79..f04376768bc10 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -991,6 +991,8 @@ static void uvc_function_unbind(struct usb_configuration *c,
uvcg_info(f, "%s()\n", __func__);
+ kthread_cancel_work_sync(&video->hw_submit);
+
if (video->async_wq)
destroy_workqueue(video->async_wq);
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 55d796f5f5e8d..4f44a607d9f5c 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -94,6 +94,9 @@ struct uvc_video {
struct work_struct pump;
struct workqueue_struct *async_wq;
+ struct kthread_worker *kworker;
+ struct kthread_work hw_submit;
+
atomic_t queued;
/* Frame parameters */
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index eb82aaed6ba13..f43bf19218963 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -324,50 +324,6 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video,
return 0;
}
-/*
- * Must only be called from uvcg_video_enable - since after that we only want to
- * queue requests to the endpoint from the uvc_video_complete complete handler.
- * This function is needed in order to 'kick start' the flow of requests from
- * gadget driver to the usb controller.
- */
-static void uvc_video_ep_queue_initial_requests(struct uvc_video *video)
-{
- struct usb_request *req = NULL;
- unsigned long flags = 0;
- unsigned int count = 0;
- int ret = 0;
-
- /*
- * We only queue half of the free list since we still want to have
- * some free usb_requests in the free list for the video_pump async_wq
- * thread to encode uvc buffers into. Otherwise we could get into a
- * situation where the free list does not have any usb requests to
- * encode into - we always end up queueing 0 length requests to the
- * end point.
- */
- unsigned int half_list_size = video->uvc_num_requests / 2;
-
- spin_lock_irqsave(&video->req_lock, flags);
- /*
- * Take these requests off the free list and queue them all to the
- * endpoint. Since we queue 0 length requests with the req_lock held,
- * there isn't any 'data' race involved here with the complete handler.
- */
- while (count < half_list_size) {
- req = list_first_entry(&video->req_free, struct usb_request,
- list);
- list_del(&req->list);
- req->length = 0;
- ret = uvcg_video_ep_queue(video, req);
- if (ret < 0) {
- uvcg_queue_cancel(&video->queue, 0);
- break;
- }
- count++;
- }
- spin_unlock_irqrestore(&video->req_lock, flags);
-}
-
static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
@@ -375,10 +331,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
struct uvc_video *video = ureq->video;
struct uvc_video_queue *queue = &video->queue;
struct uvc_buffer *last_buf;
- struct usb_request *to_queue = req;
unsigned long flags;
- bool is_bulk = video->max_payload_size;
- int ret = 0;
spin_lock_irqsave(&video->req_lock, flags);
atomic_dec(&video->queued);
@@ -441,65 +394,85 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
return;
}
+ list_add_tail(&req->list, &video->req_free);
/*
- * Here we check whether any request is available in the ready
- * list. If it is, queue it to the ep and add the current
- * usb_request to the req_free list - for video_pump to fill in.
- * Otherwise, just use the current usb_request to queue a 0
- * length request to the ep. Since we always add to the req_free
- * list if we dequeue from the ready list, there will never
- * be a situation where the req_free list is completely out of
- * requests and cannot recover.
+ * Queue work to the wq as well since it is possible that a
+ * buffer may not have been completely encoded with the set of
+ * in-flight usb requests for whih the complete callbacks are
+ * firing.
+ * In that case, if we do not queue work to the worker thread,
+ * the buffer will never be marked as complete - and therefore
+ * not be returned to userpsace. As a result,
+ * dequeue -> queue -> dequeue flow of uvc buffers will not
+ * happen. Since there are is a new free request wake up the pump.
*/
- to_queue->length = 0;
- if (!list_empty(&video->req_ready)) {
- to_queue = list_first_entry(&video->req_ready,
- struct usb_request, list);
- list_del(&to_queue->list);
- list_add_tail(&req->list, &video->req_free);
- /*
- * Queue work to the wq as well since it is possible that a
- * buffer may not have been completely encoded with the set of
- * in-flight usb requests for whih the complete callbacks are
- * firing.
- * In that case, if we do not queue work to the worker thread,
- * the buffer will never be marked as complete - and therefore
- * not be returned to userpsace. As a result,
- * dequeue -> queue -> dequeue flow of uvc buffers will not
- * happen.
- */
- queue_work(video->async_wq, &video->pump);
- } else if (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT) {
- list_add_tail(&to_queue->list, &video->req_free);
- /*
- * There is a new free request - wake up the pump.
- */
- queue_work(video->async_wq, &video->pump);
+ queue_work(video->async_wq, &video->pump);
- spin_unlock_irqrestore(&video->req_lock, flags);
+ spin_unlock_irqrestore(&video->req_lock, flags);
- return;
- }
- /*
- * Queue to the endpoint. The actual queueing to ep will
- * only happen on one thread - the async_wq for bulk endpoints
- * and this thread for isoc endpoints.
- */
- ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk);
- if (ret < 0) {
+ kthread_queue_work(video->kworker, &video->hw_submit);
+}
+
+static void uvcg_video_hw_submit(struct kthread_work *work)
+{
+ struct uvc_video *video = container_of(work, struct uvc_video, hw_submit);
+ bool is_bulk = video->max_payload_size;
+ unsigned long flags;
+ struct usb_request *req;
+ int ret = 0;
+
+ while (true) {
+ if (!video->ep->enabled)
+ return;
+ spin_lock_irqsave(&video->req_lock, flags);
/*
- * Endpoint error, but the stream is still enabled.
- * Put request back in req_free for it to be cleaned
- * up later.
+ * Here we check whether any request is available in the ready
+ * list. If it is, queue it to the ep and add the current
+ * usb_request to the req_free list - for video_pump to fill in.
+ * Otherwise, just use the current usb_request to queue a 0
+ * length request to the ep. Since we always add to the req_free
+ * list if we dequeue from the ready list, there will never
+ * be a situation where the req_free list is completely out of
+ * requests and cannot recover.
*/
- list_add_tail(&to_queue->list, &video->req_free);
+ if (!list_empty(&video->req_ready)) {
+ req = list_first_entry(&video->req_ready,
+ struct usb_request, list);
+ } else {
+ if (list_empty(&video->req_free) ||
+ (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT)) {
+ spin_unlock_irqrestore(&video->req_lock, flags);
+
+ return;
+ }
+ req = list_first_entry(&video->req_free, struct usb_request,
+ list);
+ req->length = 0;
+ }
+ list_del(&req->list);
+
/*
- * There is a new free request - wake up the pump.
+ * Queue to the endpoint. The actual queueing to ep will
+ * only happen on one thread - the async_wq for bulk endpoints
+ * and this thread for isoc endpoints.
*/
- queue_work(video->async_wq, &video->pump);
- }
+ ret = uvcg_video_usb_req_queue(video, req, !is_bulk);
+ if (ret < 0) {
+ /*
+ * Endpoint error, but the stream is still enabled.
+ * Put request back in req_free for it to be cleaned
+ * up later.
+ */
+ list_add_tail(&req->list, &video->req_free);
+ /*
+ * There is a new free request - wake up the pump.
+ */
+ queue_work(video->async_wq, &video->pump);
- spin_unlock_irqrestore(&video->req_lock, flags);
+ }
+
+ spin_unlock_irqrestore(&video->req_lock, flags);
+ }
}
static int
@@ -771,7 +744,7 @@ int uvcg_video_enable(struct uvc_video *video)
atomic_set(&video->queued, 0);
- uvc_video_ep_queue_initial_requests(video);
+ kthread_queue_work(video->kworker, &video->hw_submit);
queue_work(video->async_wq, &video->pump);
return ret;
@@ -794,6 +767,17 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
if (!video->async_wq)
return -EINVAL;
+ /* Allocate a kthread for asynchronous hw submit handler. */
+ video->kworker = kthread_create_worker(0, "UVCG");
+ if (IS_ERR(video->kworker)) {
+ uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n");
+ return PTR_ERR(video->kworker);
+ }
+
+ kthread_init_work(&video->hw_submit, uvcg_video_hw_submit);
+
+ sched_set_fifo(video->kworker->task);
+
video->uvc = uvc;
video->fcc = V4L2_PIX_FMT_YUYV;
video->bpp = 16;
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 4/9] usb: gadget: uvc: add g_parm and s_parm for frame interval
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
` (2 preceding siblings ...)
2024-09-29 18:59 ` [PATCH v6 3/9] usb: gadget: uvc: rework to enqueue in pump worker from encoded queue Michael Grzeschik
@ 2024-09-29 18:59 ` Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 5/9] usb: gadget: uvc: set req_size and n_requests based on the " Michael Grzeschik
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
The uvc gadget driver is lacking the information which frame interval
was set by the host. We add this information by implementing the g_parm
and s_parm callbacks.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
v5 -> v6: -
v4 -> v5:
- using !V4L2_TYPE_IS_OUTPUT to check for type
- initialzing interval to same default as in configfs
- reordered this patch in the series
v1 -> v4: -
---
drivers/usb/gadget/function/uvc.h | 1 +
drivers/usb/gadget/function/uvc_v4l2.c | 52 +++++++++++++++++++++++++++++++++
drivers/usb/gadget/function/uvc_video.c | 1 +
3 files changed, 54 insertions(+)
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 4f44a607d9f5c..099038f1088ef 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -105,6 +105,7 @@ struct uvc_video {
unsigned int width;
unsigned int height;
unsigned int imagesize;
+ unsigned int interval;
struct mutex mutex; /* protects frame parameters */
unsigned int uvc_num_requests;
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index de1736f834e6b..ab89f1630acb0 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -314,6 +314,56 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
return ret;
}
+static int uvc_v4l2_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_video *video = &uvc->video;
+ struct v4l2_fract timeperframe;
+
+ if (!V4L2_TYPE_IS_OUTPUT(parm->type))
+ return -EINVAL;
+
+ /* Return the actual frame period. */
+ timeperframe.numerator = video->interval;
+ timeperframe.denominator = 10000000;
+ v4l2_simplify_fraction(&timeperframe.numerator,
+ &timeperframe.denominator, 8, 333);
+
+ uvcg_dbg(&uvc->func, "Getting frame interval of %u/%u (%u)\n",
+ timeperframe.numerator, timeperframe.denominator,
+ video->interval);
+
+ parm->parm.output.timeperframe = timeperframe;
+ parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+
+ return 0;
+}
+
+static int uvc_v4l2_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_video *video = &uvc->video;
+ struct v4l2_fract timeperframe;
+
+ if (!V4L2_TYPE_IS_OUTPUT(parm->type))
+ return -EINVAL;
+
+ timeperframe = parm->parm.output.timeperframe;
+
+ video->interval = v4l2_fraction_to_interval(timeperframe.numerator,
+ timeperframe.denominator);
+
+ uvcg_dbg(&uvc->func, "Setting frame interval to %u/%u (%u)\n",
+ timeperframe.numerator, timeperframe.denominator,
+ video->interval);
+
+ return 0;
+}
+
static int
uvc_v4l2_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
@@ -587,6 +637,8 @@ const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
.vidioc_dqbuf = uvc_v4l2_dqbuf,
.vidioc_streamon = uvc_v4l2_streamon,
.vidioc_streamoff = uvc_v4l2_streamoff,
+ .vidioc_s_parm = uvc_v4l2_s_parm,
+ .vidioc_g_parm = uvc_v4l2_g_parm,
.vidioc_subscribe_event = uvc_v4l2_subscribe_event,
.vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event,
.vidioc_default = uvc_v4l2_ioctl_default,
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index f43bf19218963..19ba18d5a42fb 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -784,6 +784,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
video->width = 320;
video->height = 240;
video->imagesize = 320 * 240 * 2;
+ video->interval = 666666;
/* Initialize the video buffers queue. */
uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent,
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 5/9] usb: gadget: uvc: set req_size and n_requests based on the frame interval
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
` (3 preceding siblings ...)
2024-09-29 18:59 ` [PATCH v6 4/9] usb: gadget: uvc: add g_parm and s_parm for frame interval Michael Grzeschik
@ 2024-09-29 18:59 ` Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 6/9] usb: gadget: uvc: set req_length based on payload by nreqs instead of req_size Michael Grzeschik
` (4 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
This patch is removing the initial imprecise and limited calculation of
requests needed to be used from the queue_setup callback. It instead
introduces the uvc_video_prep_requests function which is called
immediately before the request allocation.
With the information of the usb frame interval length it is possible to
calculate the number of requests needed during one frame duration.
Based on the calculated number of requests and the imagesize we
calculate the actual size per request. This calculation has the benefit
that the frame data is equally distributed over all allocated requests.
When the req_size is not in the range for the actually configured
max_req_size configured for the overall bandwidth we fallback
to use the max_req_size instead.
Since this calculations are only important for isoc transfers we just
use max_request_size for bulk and skip it.
As video->req_size will be recalculated on every video_enable resetting
it to 0 is not necessary anymore.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
v5 -> v6: -
v4 -> v5:
- reordered this patch in the series
- merging previous extra patch: ('usb: gadget: uvc: set req_size once when the vb2 queue is calculated')
- moved the overall request size calculation to one seperate function
- only calculating once before enabling the video ep
v3 -> v4: -
v2 -> v3:
- added the frame duration for full-speed devices into calculation
v1 -> v2:
- add headersize per request into calculation
---
drivers/usb/gadget/function/uvc_queue.c | 13 -------
drivers/usb/gadget/function/uvc_video.c | 68 +++++++++++++++++++++++++++------
2 files changed, 56 insertions(+), 25 deletions(-)
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 0aa3d7e1f3cc3..731e3b9d21acc 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -44,8 +44,6 @@ static int uvc_queue_setup(struct vb2_queue *vq,
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
struct uvc_video *video = container_of(queue, struct uvc_video, queue);
- unsigned int req_size;
- unsigned int nreq;
if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
*nbuffers = UVC_MAX_VIDEO_BUFFERS;
@@ -54,17 +52,6 @@ static int uvc_queue_setup(struct vb2_queue *vq,
sizes[0] = video->imagesize;
- req_size = video->ep->maxpacket
- * max_t(unsigned int, video->ep->maxburst, 1)
- * (video->ep->mult);
-
- /* We divide by two, to increase the chance to run
- * into fewer requests for smaller framesizes.
- */
- nreq = DIV_ROUND_UP(DIV_ROUND_UP(sizes[0], 2), req_size);
- nreq = clamp(nreq, 4U, 64U);
- video->uvc_num_requests = nreq;
-
return 0;
}
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 19ba18d5a42fb..4efd7585d7541 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -486,23 +486,70 @@ uvc_video_free_requests(struct uvc_video *video)
INIT_LIST_HEAD(&video->ureqs);
INIT_LIST_HEAD(&video->req_free);
INIT_LIST_HEAD(&video->req_ready);
- video->req_size = 0;
return 0;
}
+static void
+uvc_video_prep_requests(struct uvc_video *video)
+{
+ struct uvc_device *uvc = container_of(video, struct uvc_device, video);
+ struct usb_composite_dev *cdev = uvc->func.config->cdev;
+ unsigned int interval_duration = video->ep->desc->bInterval * 1250;
+ unsigned int max_req_size, req_size, header_size;
+ unsigned int nreq;
+
+ max_req_size = video->ep->maxpacket
+ * max_t(unsigned int, video->ep->maxburst, 1)
+ * (video->ep->mult);
+
+ if (!usb_endpoint_xfer_isoc(video->ep->desc)) {
+ video->req_size = max_req_size;
+ video->uvc_num_requests =
+ DIV_ROUND_UP(video->imagesize, max_req_size);
+
+ return;
+ }
+
+ if (cdev->gadget->speed < USB_SPEED_HIGH)
+ interval_duration = video->ep->desc->bInterval * 10000;
+
+ nreq = DIV_ROUND_UP(video->interval, interval_duration);
+
+ header_size = nreq * UVCG_REQUEST_HEADER_LEN;
+
+ req_size = DIV_ROUND_UP(video->imagesize + header_size, nreq);
+
+ if (req_size > max_req_size) {
+ /* The prepared interval length and expected buffer size
+ * is not possible to stream with the currently configured
+ * isoc bandwidth. Fallback to the maximum.
+ */
+ req_size = max_req_size;
+ }
+ video->req_size = req_size;
+
+ /* We need to compensate the amount of requests to be
+ * allocated with the maximum amount of zero length requests.
+ * Since it is possible that hw_submit will initially
+ * enqueue some zero length requests and we then will not be
+ * able to fully encode one frame.
+ */
+ video->uvc_num_requests = nreq + UVCG_REQ_MAX_ZERO_COUNT;
+}
+
static int
uvc_video_alloc_requests(struct uvc_video *video)
{
struct uvc_request *ureq;
- unsigned int req_size;
unsigned int i;
int ret = -ENOMEM;
- BUG_ON(video->req_size);
-
- req_size = video->ep->maxpacket
- * max_t(unsigned int, video->ep->maxburst, 1)
- * (video->ep->mult);
+ /*
+ * calculate in uvc_video_prep_requests
+ * - video->uvc_num_requests
+ * - video->req_size
+ */
+ uvc_video_prep_requests(video);
for (i = 0; i < video->uvc_num_requests; i++) {
ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL);
@@ -513,7 +560,7 @@ uvc_video_alloc_requests(struct uvc_video *video)
list_add_tail(&ureq->list, &video->ureqs);
- ureq->req_buffer = kmalloc(req_size, GFP_KERNEL);
+ ureq->req_buffer = kmalloc(video->req_size, GFP_KERNEL);
if (ureq->req_buffer == NULL)
goto error;
@@ -531,12 +578,10 @@ uvc_video_alloc_requests(struct uvc_video *video)
list_add_tail(&ureq->req->list, &video->req_free);
/* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */
sg_alloc_table(&ureq->sgt,
- DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN,
+ DIV_ROUND_UP(video->req_size - UVCG_REQUEST_HEADER_LEN,
PAGE_SIZE) + 2, GFP_KERNEL);
}
- video->req_size = req_size;
-
return 0;
error:
@@ -689,7 +734,6 @@ uvcg_video_disable(struct uvc_video *video)
INIT_LIST_HEAD(&video->ureqs);
INIT_LIST_HEAD(&video->req_free);
INIT_LIST_HEAD(&video->req_ready);
- video->req_size = 0;
spin_unlock_irqrestore(&video->req_lock, flags);
/*
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 6/9] usb: gadget: uvc: set req_length based on payload by nreqs instead of req_size
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
` (4 preceding siblings ...)
2024-09-29 18:59 ` [PATCH v6 5/9] usb: gadget: uvc: set req_size and n_requests based on the " Michael Grzeschik
@ 2024-09-29 18:59 ` Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 7/9] usb: gadget: uvc: set nbuffers to minimum STREAMING_MIN_BUFFERS in uvc_queue_setup Michael Grzeschik
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
Compressed formats generate content depending amount of data that is set
in the vb2 buffer by the payload_size. When streaming those formats it
is better to scatter that smaller data over all requests. This patch is
doing that by introducing the calculated req_payload_size which is
updated by each frame. It the uses this amount of data to fill the
isoc requests instead of the video->req_size.
For uncompressed formats it will not make a difference since the payload
size will be equal to the imagesize. Therefore the code will have no
effecta as req_payload_size will be equal to req_size.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
v5 -> v6: -
v4 -> v5:
- keep using req_size instead of len in encode_isoc_sg to be more
explicit
- using new initialized variable reqs_per_frame instead of two
calculations
- reordered this patch in the series
v1 -> v4: -
---
drivers/usb/gadget/function/uvc.h | 2 ++
drivers/usb/gadget/function/uvc_queue.c | 10 ++++++++--
drivers/usb/gadget/function/uvc_queue.h | 2 ++
drivers/usb/gadget/function/uvc_video.c | 15 ++++++++-------
4 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 099038f1088ef..bedb4ef42864f 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -110,6 +110,8 @@ struct uvc_video {
unsigned int uvc_num_requests;
+ unsigned int reqs_per_frame;
+
/* Requests */
bool is_enabled; /* tracks whether video stream is enabled */
unsigned int req_size;
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 731e3b9d21acc..6757a4e25a743 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -58,6 +58,7 @@ static int uvc_queue_setup(struct vb2_queue *vq,
static int uvc_buffer_prepare(struct vb2_buffer *vb)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+ struct uvc_video *video = container_of(queue, struct uvc_video, queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf);
@@ -78,10 +79,15 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb)
buf->mem = vb2_plane_vaddr(vb, 0);
}
buf->length = vb2_plane_size(vb, 0);
- if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
buf->bytesused = 0;
- else
+ } else {
buf->bytesused = vb2_get_plane_payload(vb, 0);
+ buf->req_payload_size =
+ DIV_ROUND_UP(buf->bytesused +
+ (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN),
+ video->reqs_per_frame);
+ }
return 0;
}
diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h
index 41f87b917f6bc..b54becc570a38 100644
--- a/drivers/usb/gadget/function/uvc_queue.h
+++ b/drivers/usb/gadget/function/uvc_queue.h
@@ -39,6 +39,8 @@ struct uvc_buffer {
unsigned int offset;
unsigned int length;
unsigned int bytesused;
+ /* req_payload_size: only used with isoc */
+ unsigned int req_payload_size;
};
#define UVC_QUEUE_DISCONNECTED (1 << 0)
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 4efd7585d7541..0287a56fa50ad 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -136,7 +136,7 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
unsigned int pending = buf->bytesused - video->queue.buf_used;
struct uvc_request *ureq = req->context;
struct scatterlist *sg, *iter;
- unsigned int len = video->req_size;
+ unsigned int len = buf->req_payload_size;
unsigned int sg_left, part = 0;
unsigned int i;
int header_len;
@@ -146,15 +146,15 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
/* Init the header. */
header_len = uvc_video_encode_header(video, buf, ureq->header,
- video->req_size);
+ buf->req_payload_size);
sg_set_buf(sg, ureq->header, header_len);
len -= header_len;
if (pending <= len)
len = pending;
- req->length = (len == pending) ?
- len + header_len : video->req_size;
+ req->length = (len == pending) ? len + header_len :
+ buf->req_payload_size;
/* Init the pending sgs with payload */
sg = sg_next(sg);
@@ -202,7 +202,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
{
void *mem = req->buf;
struct uvc_request *ureq = req->context;
- int len = video->req_size;
+ int len = buf->req_payload_size;
int ret;
/* Add the header. */
@@ -214,7 +214,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
ret = uvc_video_encode_data(video, buf, mem, len);
len -= ret;
- req->length = video->req_size - len;
+ req->length = buf->req_payload_size - len;
if (buf->bytesused == video->queue.buf_used ||
video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) {
@@ -504,7 +504,7 @@ uvc_video_prep_requests(struct uvc_video *video)
if (!usb_endpoint_xfer_isoc(video->ep->desc)) {
video->req_size = max_req_size;
- video->uvc_num_requests =
+ video->reqs_per_frame = video->uvc_num_requests =
DIV_ROUND_UP(video->imagesize, max_req_size);
return;
@@ -535,6 +535,7 @@ uvc_video_prep_requests(struct uvc_video *video)
* able to fully encode one frame.
*/
video->uvc_num_requests = nreq + UVCG_REQ_MAX_ZERO_COUNT;
+ video->reqs_per_frame = nreq;
}
static int
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 7/9] usb: gadget: uvc: set nbuffers to minimum STREAMING_MIN_BUFFERS in uvc_queue_setup
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
` (5 preceding siblings ...)
2024-09-29 18:59 ` [PATCH v6 6/9] usb: gadget: uvc: set req_length based on payload by nreqs instead of req_size Michael Grzeschik
@ 2024-09-29 18:59 ` Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 8/9] usb: gadget: uvc: add trace of enqueued and completed requests Michael Grzeschik
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
We set the minimum amount of v4l2 buffers that is possibly be pending
to UVCG_STREAMING_MIN_BUFFERS which is two. This way the driver will
always have at least one frame pending to be encoded while the other
is being enqueued in the hardware.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
v5 -> v6: -
v4 -> v5:
- using STREAMING_MIN_BUFFERS set the min nbuffers
- renamed the patch since the function changed
- removed the g_ctrl function
- reordered this patch in the series
v1 -> v4: -
---
drivers/usb/gadget/function/uvc.h | 2 ++
drivers/usb/gadget/function/uvc_queue.c | 3 +++
2 files changed, 5 insertions(+)
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index bedb4ef42864f..6f44dd7323150 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -74,6 +74,8 @@ extern unsigned int uvc_gadget_trace_param;
#define UVCG_REQ_MAX_INT_COUNT 16
#define UVCG_REQ_MAX_ZERO_COUNT (2 * UVCG_REQ_MAX_INT_COUNT)
+#define UVCG_STREAMING_MIN_BUFFERS 2
+
/* ------------------------------------------------------------------------
* Structures
*/
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 6757a4e25a743..5eaeae3e2441c 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -21,6 +21,7 @@
#include <media/videobuf2-vmalloc.h>
#include "uvc.h"
+#include "uvc_video.h"
/* ------------------------------------------------------------------------
* Video buffers queue management.
@@ -47,6 +48,8 @@ static int uvc_queue_setup(struct vb2_queue *vq,
if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
*nbuffers = UVC_MAX_VIDEO_BUFFERS;
+ if (*nbuffers < UVCG_STREAMING_MIN_BUFFERS)
+ *nbuffers = UVCG_STREAMING_MIN_BUFFERS;
*nplanes = 1;
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 8/9] usb: gadget: uvc: add trace of enqueued and completed requests
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
` (6 preceding siblings ...)
2024-09-29 18:59 ` [PATCH v6 7/9] usb: gadget: uvc: set nbuffers to minimum STREAMING_MIN_BUFFERS in uvc_queue_setup Michael Grzeschik
@ 2024-09-29 18:59 ` Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 9/9] usb: gadget: uvc: dont call usb_composite_setup_continue when not streaming Michael Grzeschik
2024-10-16 8:47 ` [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Greg Kroah-Hartman
9 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
This patch is adding trace events for each request that is being
enqueued into the hw and will be completed. This way it is possible
to track the fill status of the gadget hardware and find potential
issues.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
v5 -> v6:
- fixed compile rule as module
- reordered header files
v1 -> v5:
- new patch
---
drivers/usb/gadget/function/Makefile | 3 ++
drivers/usb/gadget/function/uvc_trace.c | 11 ++++++
drivers/usb/gadget/function/uvc_trace.h | 60 +++++++++++++++++++++++++++++++++
drivers/usb/gadget/function/uvc_video.c | 5 +++
4 files changed, 79 insertions(+)
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 87917a7d4a9be..92b695388611b 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -41,6 +41,9 @@ obj-$(CONFIG_USB_F_UAC1_LEGACY) += usb_f_uac1_legacy.o
usb_f_uac2-y := f_uac2.o
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
+ifneq ($(CONFIG_TRACING),)
+ usb_f_uvc-y += uvc_trace.o
+endif
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
diff --git a/drivers/usb/gadget/function/uvc_trace.c b/drivers/usb/gadget/function/uvc_trace.c
new file mode 100644
index 0000000000000..d384f6d8221a5
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_trace.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * trace.c - USB UVC Gadget Trace Support
+ *
+ * Copyright (C) 2024 Pengutronix e.K.
+ *
+ * Author: Michael Grzeschik <m.grzeschik@pengutronix.de>
+ */
+
+#define CREATE_TRACE_POINTS
+#include "uvc_trace.h"
diff --git a/drivers/usb/gadget/function/uvc_trace.h b/drivers/usb/gadget/function/uvc_trace.h
new file mode 100644
index 0000000000000..04c33cf43cc2d
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_trace.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * trace.h - USB UVC Gadget Trace Support
+ *
+ * Copyright (C) 2024 Pengutronix e.K.
+ *
+ * Author: Michael Grzeschik <m.grzeschik@pengutronix.de>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM uvcg
+
+#if !defined(__UVCG_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __UVCG_TRACE_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <linux/usb/gadget.h>
+#include <asm/byteorder.h>
+
+DECLARE_EVENT_CLASS(uvcg_video_req,
+ TP_PROTO(struct usb_request *req, u32 queued),
+ TP_ARGS(req, queued),
+ TP_STRUCT__entry(
+ __field(struct usb_request *, req)
+ __field(u32, length)
+ __field(u32, queued)
+ ),
+ TP_fast_assign(
+ __entry->req = req;
+ __entry->length = req->length;
+ __entry->queued = queued;
+ ),
+ TP_printk("req %p length %u queued %u",
+ __entry->req,
+ __entry->length,
+ __entry->queued)
+);
+
+DEFINE_EVENT(uvcg_video_req, uvcg_video_complete,
+ TP_PROTO(struct usb_request *req, u32 queued),
+ TP_ARGS(req, queued)
+);
+
+DEFINE_EVENT(uvcg_video_req, uvcg_video_queue,
+ TP_PROTO(struct usb_request *req, u32 queued),
+ TP_ARGS(req, queued)
+);
+
+#endif /* __UVCG_TRACE_H */
+
+/* this part has to be here */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE uvc_trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 0287a56fa50ad..115524b79ebd7 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -19,6 +19,7 @@
#include "uvc.h"
#include "uvc_queue.h"
#include "uvc_video.h"
+#include "uvc_trace.h"
/* --------------------------------------------------------------------------
* Video codecs
@@ -271,6 +272,8 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
atomic_inc(&video->queued);
+ trace_uvcg_video_queue(req, atomic_read(&video->queued));
+
return ret;
}
@@ -408,6 +411,8 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
*/
queue_work(video->async_wq, &video->pump);
+ trace_uvcg_video_complete(req, atomic_read(&video->queued));
+
spin_unlock_irqrestore(&video->req_lock, flags);
kthread_queue_work(video->kworker, &video->hw_submit);
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 9/9] usb: gadget: uvc: dont call usb_composite_setup_continue when not streaming
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
` (7 preceding siblings ...)
2024-09-29 18:59 ` [PATCH v6 8/9] usb: gadget: uvc: add trace of enqueued and completed requests Michael Grzeschik
@ 2024-09-29 18:59 ` Michael Grzeschik
2024-10-16 8:47 ` [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Greg Kroah-Hartman
9 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-09-29 18:59 UTC (permalink / raw)
To: Laurent Pinchart, Daniel Scally, Greg Kroah-Hartman,
Avichal Rakesh, Jayant Chowdhary
Cc: linux-usb, linux-kernel, kernel, Michael Grzeschik
If the streamoff call was triggered by some previous disconnect
or userspace application shutdown the uvc_function_setup_continue
should not be called and the state should not be overwritten.
For this situation the set_alt(0) was never called and the streaming ep
has no USB_GADGET_DELAYED_STATUS pending.
Since the state then was already updated before we also omit the state
update.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
v5 -> v6: -
v1 -> v5: - new patch
---
drivers/usb/gadget/function/uvc_v4l2.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index ab89f1630acb0..3492855f0fb29 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -546,6 +546,9 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
if (ret < 0)
return ret;
+ if (uvc->state != UVC_STATE_STREAMING)
+ return 0;
+
uvc->state = UVC_STATE_CONNECTED;
uvc_function_setup_continue(uvc, 1);
return 0;
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
` (8 preceding siblings ...)
2024-09-29 18:59 ` [PATCH v6 9/9] usb: gadget: uvc: dont call usb_composite_setup_continue when not streaming Michael Grzeschik
@ 2024-10-16 8:47 ` Greg Kroah-Hartman
2024-10-16 14:00 ` Michael Grzeschik
9 siblings, 1 reply; 12+ messages in thread
From: Greg Kroah-Hartman @ 2024-10-16 8:47 UTC (permalink / raw)
To: Michael Grzeschik
Cc: Laurent Pinchart, Daniel Scally, Avichal Rakesh, Jayant Chowdhary,
linux-usb, linux-kernel, kernel
On Sun, Sep 29, 2024 at 08:59:20PM +0200, Michael Grzeschik wrote:
> This patch series is improving the size calculation and allocation of
> the uvc requests. Using the selected frame duration of the stream it is
> possible to calculate the number of requests based on the interval
> length.
>
> It also precalculates the request length based on the actual per frame
> size for compressed formats.
>
> For this calculations to work it was needed to rework the request
> queueing by moving the encoding to one extra thread (in this case we
> chose the qbuf) context.
>
> Next it was needed to move the actual request enqueueing to one extra
> thread which is kept busy to fill the isoc queue in the udc.
>
> Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
> ---
> Changes in v6:
> - fixes in: ("usb: gadget: uvc: add trace of enqueued and completed requests")
> - Link to v5: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v5-0-2de78794365c@pengutronix.de
Breaks the build for me:
In file included from drivers/usb/gadget/function/uvc_trace.h:60,
from drivers/usb/gadget/function/uvc_trace.c:11:
./include/trace/define_trace.h:95:42: fatal error: ./uvc_trace.h: No such file or directory
95 | #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
| ^
what did you build this against?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes
2024-10-16 8:47 ` [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Greg Kroah-Hartman
@ 2024-10-16 14:00 ` Michael Grzeschik
0 siblings, 0 replies; 12+ messages in thread
From: Michael Grzeschik @ 2024-10-16 14:00 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Laurent Pinchart, Daniel Scally, Avichal Rakesh, Jayant Chowdhary,
linux-usb, linux-kernel, kernel
[-- Attachment #1: Type: text/plain, Size: 2122 bytes --]
On Wed, Oct 16, 2024 at 10:47:00AM +0200, Greg Kroah-Hartman wrote:
>On Sun, Sep 29, 2024 at 08:59:20PM +0200, Michael Grzeschik wrote:
>> This patch series is improving the size calculation and allocation of
>> the uvc requests. Using the selected frame duration of the stream it is
>> possible to calculate the number of requests based on the interval
>> length.
>>
>> It also precalculates the request length based on the actual per frame
>> size for compressed formats.
>>
>> For this calculations to work it was needed to rework the request
>> queueing by moving the encoding to one extra thread (in this case we
>> chose the qbuf) context.
>>
>> Next it was needed to move the actual request enqueueing to one extra
>> thread which is kept busy to fill the isoc queue in the udc.
>>
>> Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
>> ---
>> Changes in v6:
>> - fixes in: ("usb: gadget: uvc: add trace of enqueued and completed requests")
>> - Link to v5: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v5-0-2de78794365c@pengutronix.de
>
>Breaks the build for me:
>
>In file included from drivers/usb/gadget/function/uvc_trace.h:60,
> from drivers/usb/gadget/function/uvc_trace.c:11:
>./include/trace/define_trace.h:95:42: fatal error: ./uvc_trace.h: No such file or directory
> 95 | #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
> | ^
>
>what did you build this against?
I somehow managed to drop the CFLAGS for the uvc_trace.o
which was "CFLAGS_uvc_trace.o := -I$(src)"
This was just fixed in v7. Please try that one:
https://lore.kernel.org/all/20240403-uvc_request_length_by_interval-v7-0-e224bb1035f0@pengutronix.de/
Thanks for testing this,
Michael
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2024-10-16 14:00 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-29 18:59 [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 1/9] usb: gadget: uvc: wake pump everytime we update the free list Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 2/9] usb: gadget: uvc: only enqueue zero length requests in potential underrun Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 3/9] usb: gadget: uvc: rework to enqueue in pump worker from encoded queue Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 4/9] usb: gadget: uvc: add g_parm and s_parm for frame interval Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 5/9] usb: gadget: uvc: set req_size and n_requests based on the " Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 6/9] usb: gadget: uvc: set req_length based on payload by nreqs instead of req_size Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 7/9] usb: gadget: uvc: set nbuffers to minimum STREAMING_MIN_BUFFERS in uvc_queue_setup Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 8/9] usb: gadget: uvc: add trace of enqueued and completed requests Michael Grzeschik
2024-09-29 18:59 ` [PATCH v6 9/9] usb: gadget: uvc: dont call usb_composite_setup_continue when not streaming Michael Grzeschik
2024-10-16 8:47 ` [PATCH v6 0/9] usb: gadget: uvc: effectively fill the udc isoc pipeline with available video buffers and fixes Greg Kroah-Hartman
2024-10-16 14:00 ` Michael Grzeschik
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).