Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933096AbeALJTh (ORCPT + 1 other); Fri, 12 Jan 2018 04:19:37 -0500 Received: from o1.7nn.fshared.sendgrid.net ([167.89.55.65]:42895 "EHLO o1.7nn.fshared.sendgrid.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754583AbeALJT1 (ORCPT ); Fri, 12 Jan 2018 04:19:27 -0500 From: Kieran Bingham To: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Guennadi Liakhovetski , Laurent Pinchart , Olivier BRAUN , Troy Kisky , Kieran Bingham Subject: [RFT PATCH v3 5/6] uvcvideo: queue: Support asynchronous buffer handling Date: Fri, 12 Jan 2018 09:19:26 +0000 (UTC) Message-Id: X-Mailer: git-send-email 2.7.4 In-Reply-To: References: In-Reply-To: References: X-SG-EID: UsLXQ589HNP4HLBydmD9pgHURQozLYm9XliudupI8YsPrndul5DqHc5GyOOAvpJT3EWmzTL6g6ZU2e 94LRf8gfzcDMl3XQDpJ+sTSX7fTYM9X/8v4QMpp6tG7LKZjSNGzPyT1JzFrQt25SMMT2NlU6ldN5OY LeMZ3OsVaHjcmyRqt7FfC3cEeI5tfi+wxbzDk56bUZNPpQAag0KfSvPbfPbRBxFqkLiqjbZv+H4sCW OimXuu2PJOKOlJ72gJnoSE Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Return-Path: The buffer queue interface currently operates sequentially, processing buffers after they have fully completed. In preparation for supporting parallel tasks operating on the buffers, we will need to support buffers being processed on multiple CPUs. Adapt the uvc_queue_next_buffer() such that a reference count tracks the active use of the buffer, returning the buffer to the VB2 stack at completion. Signed-off-by: Kieran Bingham --- drivers/media/usb/uvc/uvc_queue.c | 61 ++++++++++++++++++++++++++------ drivers/media/usb/uvc/uvcvideo.h | 4 ++- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index ddac4d89a291..5a9987e547d3 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -131,6 +131,7 @@ static void uvc_buffer_queue(struct vb2_buffer *vb) spin_lock_irqsave(&queue->irqlock, flags); if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) { + kref_init(&buf->ref); list_add_tail(&buf->queue, &queue->irqqueue); } else { /* If the device is disconnected return the buffer to userspace @@ -424,28 +425,66 @@ struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue) return nextbuf; } -struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, +/* + * uvc_queue_requeue: Requeue a buffer on our internal irqqueue + * + * Reuse a buffer through our internal queue without the need to 'prepare' + * The buffer will be returned to userspace through the uvc_buffer_queue call if + * the device has been disconnected + */ +static void uvc_queue_requeue(struct uvc_video_queue *queue, struct uvc_buffer *buf) { - struct uvc_buffer *nextbuf; - unsigned long flags; + buf->error = 0; + buf->state = UVC_BUF_STATE_QUEUED; + buf->bytesused = 0; + vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0); + + uvc_buffer_queue(&buf->buf.vb2_buf); +} + +static void uvc_queue_buffer_complete(struct kref *ref) +{ + struct uvc_buffer *buf = container_of(ref, struct uvc_buffer, ref); + struct vb2_buffer *vb = &buf->buf.vb2_buf; + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) { - buf->error = 0; - buf->state = UVC_BUF_STATE_QUEUED; - buf->bytesused = 0; - vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0); - return buf; + uvc_queue_requeue(queue, buf); + return; } + buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; + vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused); + vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); +} + +/* + * Release a reference on the buffer. Complete the buffer when the last + * reference is released + */ +void uvc_queue_buffer_release(struct uvc_buffer *buf) +{ + kref_put(&buf->ref, uvc_queue_buffer_complete); +} + +/* + * Remove this buffer from the queue. Lifetime will persist while async actions + * are still running (if any), and uvc_queue_buffer_release will give the buffer + * back to VB2 when all users have completed. + */ +struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) +{ + struct uvc_buffer *nextbuf; + unsigned long flags; + spin_lock_irqsave(&queue->irqlock, flags); list_del(&buf->queue); nextbuf = __uvc_queue_get_current_buffer(queue); spin_unlock_irqrestore(&queue->irqlock, flags); - buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; - vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused); - vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); + uvc_queue_buffer_release(buf); return nextbuf; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 5caa1f4de3cb..6a18dbfc3e5b 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -404,6 +404,9 @@ struct uvc_buffer { unsigned int bytesused; u32 pts; + + /* asynchronous buffer handling */ + struct kref ref; }; #define UVC_QUEUE_DISCONNECTED (1 << 0) @@ -696,6 +699,7 @@ extern struct uvc_buffer * uvc_queue_get_current_buffer(struct uvc_video_queue *queue); extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf); +extern void uvc_queue_buffer_release(struct uvc_buffer *buf); extern int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma); extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue, -- git-series 0.9.1