All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks
@ 2014-02-13  9:40 Hans Verkuil
  2014-02-13  9:40 ` [RFCv3 PATCH 01/10] vb2: add debugging code to check for unbalanced ops Hans Verkuil
                   ` (9 more replies)
  0 siblings, 10 replies; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski

This patch series is v3 of the previous series:

http://www.spinics.net/lists/linux-media/msg72465.html

The only difference is that the dropped buf_finish return code check
in patch 4 was moved to patch 2, otherwise the compile would break after
applying patch 2. Thanks to Pawel Osciak for pointing that out to me.

This patch series fixes a series of bugs in vb2. Recently I have been
converting the saa7134 driver to vb2 and as part of that work I discovered
that the op calls were not properly balanced, which caused saa7134 to
fail.

Based on that I did more debugging and I found lots of different issues
with vb2 when it comes to balancing ops. This patch series fixes them.
Many thanks to Pawel Osciak for a good brainstorm session.

Patch 1 adds debugging code to check for unbalanced calls. I used this
when testing since without this it is very hard to verify correctness.
It is currently turned on when CONFIG_VIDEO_ADV_DEBUG is set, but perhaps
this should be placed under a vb2 specific debug option?

The next patch changes the buf_finish return type to void. It must not
fail, and you can't do anything useful if it does anyway.

Note that I really would like to do the same for stop_streaming. The
stop_streaming error is currently ignored, and there are some drivers
that can actually return an error (waiting for an interruptible mutex
for example). Opinions are welcome.

Patch 3 just improves some comments.

Patches 4 and 5 fix several unbalanced ops.

Patch 6 fixes a regression (must go to 3.14!).

Patch 7 just renames queue_count to owned_by_drv_count. The old name
suggests the number of buffers in the queue_list, but that's not what
it is. Since the next patch will actually add a real count for the
number of buffers in the queue_list I decided to just rename this,
thus freeing up the name 'queue_count'.

Patch 8 fixes a bug in the handling of start_streaming: before that op
is called any prequeued buffers are handed over to the driver. But if
start_streaming returns an error, then those buffers are not reclaimed.
Since start_streaming failed, q->streaming is 0, so stop_streaming isn't
called either.

There are two reasons for an error in start_streaming: either a real
error when setting up the DMA engine occurred or there were not enough
buffers queued for the DMA engine to start (start_streaming returns
-ENOBUFS in that case). It makes sense to require that drivers return
queued buffers back to vb2 in case of an error, but not if they also 
have to do that in case of insufficient buffers. So this patch replaces
the -ENOBUFS mechanism by a vb2_queue field that tells vb2 what the
minimum number of buffers is.

Now if start_streaming returns an error the vb2 core will check if there
are still buffers owned by the driver and if so produce a warning and
reclaim those buffers. The same is done when the vb2_queue is freed.

This ensures that the prepare/finish memops are correctly called and
the state of all the buffers is consistent.

Patch 9 fixes vivi for this start_streaming issue. Note that there are
many drivers that do not clean up properly in case of an error during
start_streaming.

The final patch attempts to fix another issue: I noticed that in the few
drivers that implement VIDIOC_CREATE_BUFS the v4l2_format is just used as-is,
i.e. no checks are done whether the contents of the struct makes sense or not.

This patch adds a number of sanity check to see if the buffer size related
values make sense.

I have been testing these vb2 changes with vivi (vmalloc based), saa7134 and
an out-of-tree PCI driver (dma-sg based), with the mmap/userptr and read/write
streaming modes. I hope to test this also for a dma-contig based driver.
I have no way of testing with dmabuf, though. Does anyone know of a simple
way to obtain dmabufs from somewhere so they can be passed to a v4l driver?
It would be great to add a --stream-dmabuf option for v4l2-ctl.

I have to admit that I was a bit disappointed by all these vb2 issues...

Regards,

	Hans



^ permalink raw reply	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 01/10] vb2: add debugging code to check for unbalanced ops.
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  2014-02-13  9:40 ` [RFCv3 PATCH 02/10] vb2: change result code of buf_finish to void Hans Verkuil
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

When a vb2_queue is freed check if all the mem_ops and queue ops were balanced.
So the number of calls to e.g. buf_finish has to match the number of calls to
buf_prepare, etc.

This code is only enabled if CONFIG_VIDEO_ADV_DEBUG is set.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/v4l2-core/videobuf2-core.c | 233 ++++++++++++++++++++++++-------
 include/media/videobuf2-core.h           |  43 ++++++
 2 files changed, 226 insertions(+), 50 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 5a5fb7f..07b58bd 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -33,12 +33,63 @@ module_param(debug, int, 0644);
 			printk(KERN_DEBUG "vb2: " fmt, ## arg);		\
 	} while (0)
 
-#define call_memop(q, op, args...)					\
-	(((q)->mem_ops->op) ?						\
-		((q)->mem_ops->op(args)) : 0)
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+/*
+ * If advanced debugging is on, then count how often each op is called,
+ * which can either be per-buffer or per-queue.
+ *
+ * If the op failed then the 'fail_' variant is called to decrease the
+ * counter. That makes it easy to check that the 'init' and 'cleanup'
+ * (and variations thereof) stay balanced.
+ */
+
+#define call_memop(vb, op, args...)					\
+({									\
+	struct vb2_queue *_q = (vb)->vb2_queue;				\
+	dprintk(2, "call_memop(%p, %d, %s)%s\n",			\
+		_q, (vb)->v4l2_buf.index, #op,				\
+		_q->mem_ops->op ? "" : " (nop)");			\
+	(vb)->cnt_mem_ ## op++;						\
+	_q->mem_ops->op ? _q->mem_ops->op(args) : 0;			\
+})
+#define fail_memop(vb, op) ((vb)->cnt_mem_ ## op--)
+
+#define call_qop(q, op, args...)					\
+({									\
+	dprintk(2, "call_qop(%p, %s)%s\n", q, #op,			\
+		(q)->ops->op ? "" : " (nop)");				\
+	(q)->cnt_ ## op++;						\
+	(q)->ops->op ? (q)->ops->op(args) : 0;				\
+})
+#define fail_qop(q, op) ((q)->cnt_ ## op--)
+
+#define call_vb_qop(vb, op, args...)					\
+({									\
+	struct vb2_queue *_q = (vb)->vb2_queue;				\
+	dprintk(2, "call_vb_qop(%p, %d, %s)%s\n",			\
+		_q, (vb)->v4l2_buf.index, #op,				\
+		_q->ops->op ? "" : " (nop)");				\
+	(vb)->cnt_ ## op++;						\
+	_q->ops->op ? _q->ops->op(args) : 0;				\
+})
+#define fail_vb_qop(vb, op) ((vb)->cnt_ ## op--)
+
+#else
+
+#define call_memop(vb, op, args...)					\
+	((vb)->vb2_queue->mem_ops->op ? (vb)->vb2_queue->mem_ops->op(args) : 0)
+#define fail_memop(vb, op)
 
 #define call_qop(q, op, args...)					\
-	(((q)->ops->op) ? ((q)->ops->op(args)) : 0)
+	((q)->ops->op ? (q)->ops->op(args) : 0)
+#define fail_qop(q, op)
+
+#define call_vb_qop(vb, op, args...)					\
+	((vb)->vb2_queue->ops->op ? (vb)->vb2_queue->ops->op(args) : 0)
+#define fail_vb_qop(vb, op)
+
+#endif
 
 #define V4L2_BUFFER_MASK_FLAGS	(V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
 				 V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
@@ -61,7 +112,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
 	for (plane = 0; plane < vb->num_planes; ++plane) {
 		unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]);
 
-		mem_priv = call_memop(q, alloc, q->alloc_ctx[plane],
+		mem_priv = call_memop(vb, alloc, q->alloc_ctx[plane],
 				      size, q->gfp_flags);
 		if (IS_ERR_OR_NULL(mem_priv))
 			goto free;
@@ -73,9 +124,10 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
 
 	return 0;
 free:
+	fail_memop(vb, alloc);
 	/* Free already allocated memory if one of the allocations failed */
 	for (; plane > 0; --plane) {
-		call_memop(q, put, vb->planes[plane - 1].mem_priv);
+		call_memop(vb, put, vb->planes[plane - 1].mem_priv);
 		vb->planes[plane - 1].mem_priv = NULL;
 	}
 
@@ -87,11 +139,10 @@ free:
  */
 static void __vb2_buf_mem_free(struct vb2_buffer *vb)
 {
-	struct vb2_queue *q = vb->vb2_queue;
 	unsigned int plane;
 
 	for (plane = 0; plane < vb->num_planes; ++plane) {
-		call_memop(q, put, vb->planes[plane].mem_priv);
+		call_memop(vb, put, vb->planes[plane].mem_priv);
 		vb->planes[plane].mem_priv = NULL;
 		dprintk(3, "Freed plane %d of buffer %d\n", plane,
 			vb->v4l2_buf.index);
@@ -104,12 +155,11 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb)
  */
 static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
 {
-	struct vb2_queue *q = vb->vb2_queue;
 	unsigned int plane;
 
 	for (plane = 0; plane < vb->num_planes; ++plane) {
 		if (vb->planes[plane].mem_priv)
-			call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+			call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
 		vb->planes[plane].mem_priv = NULL;
 	}
 }
@@ -118,15 +168,15 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
  * __vb2_plane_dmabuf_put() - release memory associated with
  * a DMABUF shared plane
  */
-static void __vb2_plane_dmabuf_put(struct vb2_queue *q, struct vb2_plane *p)
+static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p)
 {
 	if (!p->mem_priv)
 		return;
 
 	if (p->dbuf_mapped)
-		call_memop(q, unmap_dmabuf, p->mem_priv);
+		call_memop(vb, unmap_dmabuf, p->mem_priv);
 
-	call_memop(q, detach_dmabuf, p->mem_priv);
+	call_memop(vb, detach_dmabuf, p->mem_priv);
 	dma_buf_put(p->dbuf);
 	memset(p, 0, sizeof(*p));
 }
@@ -137,11 +187,10 @@ static void __vb2_plane_dmabuf_put(struct vb2_queue *q, struct vb2_plane *p)
  */
 static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb)
 {
-	struct vb2_queue *q = vb->vb2_queue;
 	unsigned int plane;
 
 	for (plane = 0; plane < vb->num_planes; ++plane)
-		__vb2_plane_dmabuf_put(q, &vb->planes[plane]);
+		__vb2_plane_dmabuf_put(vb, &vb->planes[plane]);
 }
 
 /**
@@ -246,10 +295,11 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
 			 * callback, if given. An error in initialization
 			 * results in queue setup failure.
 			 */
-			ret = call_qop(q, buf_init, vb);
+			ret = call_vb_qop(vb, buf_init, vb);
 			if (ret) {
 				dprintk(1, "Buffer %d %p initialization"
 					" failed\n", buffer, vb);
+				fail_vb_qop(vb, buf_init);
 				__vb2_buf_mem_free(vb);
 				kfree(vb);
 				break;
@@ -321,18 +371,77 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
 	}
 
 	/* Call driver-provided cleanup function for each buffer, if provided */
-	if (q->ops->buf_cleanup) {
-		for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
-		     ++buffer) {
-			if (NULL == q->bufs[buffer])
-				continue;
-			q->ops->buf_cleanup(q->bufs[buffer]);
-		}
+	for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+	     ++buffer) {
+		if (q->bufs[buffer])
+			call_vb_qop(q->bufs[buffer], buf_cleanup, q->bufs[buffer]);
 	}
 
 	/* Release video buffer memory */
 	__vb2_free_mem(q, buffers);
 
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	/*
+	 * Check that all the calls were balances during the life-time of this
+	 * queue. If not (or if the debug level is 1 or up), then dump the
+	 * counters to the kernel log.
+	 */
+	if (q->num_buffers) {
+		bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming ||
+				  q->cnt_wait_prepare != q->cnt_wait_finish;
+
+		if (unbalanced || debug) {
+			pr_info("vb2: counters for queue %p:%s\n", q,
+				unbalanced ? " UNBALANCED!" : "");
+			pr_info("vb2:     setup: %u start_streaming: %u stop_streaming: %u\n",
+				q->cnt_queue_setup, q->cnt_start_streaming,
+				q->cnt_stop_streaming);
+			pr_info("vb2:     wait_prepare: %u wait_finish: %u\n",
+				q->cnt_wait_prepare, q->cnt_wait_finish);
+		}
+		q->cnt_queue_setup = 0;
+		q->cnt_wait_prepare = 0;
+		q->cnt_wait_finish = 0;
+		q->cnt_start_streaming = 0;
+		q->cnt_stop_streaming = 0;
+	}
+	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+		struct vb2_buffer *vb = q->bufs[buffer];
+		bool unbalanced = vb->cnt_mem_alloc != vb->cnt_mem_put ||
+				  vb->cnt_mem_prepare != vb->cnt_mem_finish ||
+				  vb->cnt_mem_get_userptr != vb->cnt_mem_put_userptr ||
+				  vb->cnt_mem_attach_dmabuf != vb->cnt_mem_detach_dmabuf ||
+				  vb->cnt_mem_map_dmabuf != vb->cnt_mem_unmap_dmabuf ||
+				  vb->cnt_buf_queue != vb->cnt_buf_done ||
+				  vb->cnt_buf_prepare != vb->cnt_buf_finish ||
+				  vb->cnt_buf_init != vb->cnt_buf_cleanup;
+
+		if (unbalanced || debug) {
+			pr_info("vb2:   counters for queue %p, buffer %d:%s\n",
+				q, buffer, unbalanced ? " UNBALANCED!" : "");
+			pr_info("vb2:     buf_init: %u buf_cleanup: %u buf_prepare: %u buf_finish: %u\n",
+				vb->cnt_buf_init, vb->cnt_buf_cleanup,
+				vb->cnt_buf_prepare, vb->cnt_buf_finish);
+			pr_info("vb2:     buf_queue: %u buf_done: %u\n",
+				vb->cnt_buf_queue, vb->cnt_buf_done);
+			pr_info("vb2:     alloc: %u put: %u prepare: %u finish: %u mmap: %u\n",
+				vb->cnt_mem_alloc, vb->cnt_mem_put,
+				vb->cnt_mem_prepare, vb->cnt_mem_finish,
+				vb->cnt_mem_mmap);
+			pr_info("vb2:     get_userptr: %u put_userptr: %u\n",
+				vb->cnt_mem_get_userptr, vb->cnt_mem_put_userptr);
+			pr_info("vb2:     attach_dmabuf: %u detach_dmabuf: %u map_dmabuf: %u unmap_dmabuf: %u\n",
+				vb->cnt_mem_attach_dmabuf, vb->cnt_mem_detach_dmabuf,
+				vb->cnt_mem_map_dmabuf, vb->cnt_mem_unmap_dmabuf);
+			pr_info("vb2:     get_dmabuf: %u num_users: %u vaddr: %u cookie: %u\n",
+				vb->cnt_mem_get_dmabuf,
+				vb->cnt_mem_num_users,
+				vb->cnt_mem_vaddr,
+				vb->cnt_mem_cookie);
+		}
+	}
+#endif
+
 	/* Free videobuf buffers */
 	for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
 	     ++buffer) {
@@ -424,7 +533,7 @@ static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
 		 * case anyway. If num_users() returns more than 1,
 		 * we are not the only user of the plane's memory.
 		 */
-		if (mem_priv && call_memop(q, num_users, mem_priv) > 1)
+		if (mem_priv && call_memop(vb, num_users, mem_priv) > 1)
 			return true;
 	}
 	return false;
@@ -703,8 +812,10 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 	 */
 	ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
 		       q->plane_sizes, q->alloc_ctx);
-	if (ret)
+	if (ret) {
+		fail_qop(q, queue_setup);
 		return ret;
+	}
 
 	/* Finally, allocate buffers and video memory */
 	ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
@@ -723,6 +834,8 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 
 		ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
 			       &num_planes, q->plane_sizes, q->alloc_ctx);
+		if (ret)
+			fail_qop(q, queue_setup);
 
 		if (!ret && allocated_buffers < num_buffers)
 			ret = -ENOMEM;
@@ -803,8 +916,10 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
 	 */
 	ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
 		       &num_planes, q->plane_sizes, q->alloc_ctx);
-	if (ret)
+	if (ret) {
+		fail_qop(q, queue_setup);
 		return ret;
+	}
 
 	/* Finally, allocate buffers and video memory */
 	ret = __vb2_queue_alloc(q, create->memory, num_buffers,
@@ -828,6 +943,8 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
 		 */
 		ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
 			       &num_planes, q->plane_sizes, q->alloc_ctx);
+		if (ret)
+			fail_qop(q, queue_setup);
 
 		if (!ret && allocated_buffers < num_buffers)
 			ret = -ENOMEM;
@@ -882,12 +999,10 @@ EXPORT_SYMBOL_GPL(vb2_create_bufs);
  */
 void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
 {
-	struct vb2_queue *q = vb->vb2_queue;
-
 	if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
 		return NULL;
 
-	return call_memop(q, vaddr, vb->planes[plane_no].mem_priv);
+	return call_memop(vb, vaddr, vb->planes[plane_no].mem_priv);
 
 }
 EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
@@ -905,12 +1020,10 @@ EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
  */
 void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
 {
-	struct vb2_queue *q = vb->vb2_queue;
-
 	if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
 		return NULL;
 
-	return call_memop(q, cookie, vb->planes[plane_no].mem_priv);
+	return call_memop(vb, cookie, vb->planes[plane_no].mem_priv);
 }
 EXPORT_SYMBOL_GPL(vb2_plane_cookie);
 
@@ -938,12 +1051,19 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
 	if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
 		return;
 
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	/*
+	 * Although this is not a callback, it still does have to balance
+	 * with the buf_queue op. So update this counter manually.
+	 */
+	vb->cnt_buf_done++;
+#endif
 	dprintk(4, "Done processing on buffer %d, state: %d\n",
 			vb->v4l2_buf.index, state);
 
 	/* sync buffers */
 	for (plane = 0; plane < vb->num_planes; ++plane)
-		call_memop(q, finish, vb->planes[plane].mem_priv);
+		call_memop(vb, finish, vb->planes[plane].mem_priv);
 
 	/* Add the buffer to the done buffers list */
 	spin_lock_irqsave(&q->done_lock, flags);
@@ -1067,19 +1187,20 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 
 		/* Release previously acquired memory if present */
 		if (vb->planes[plane].mem_priv)
-			call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+			call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
 
 		vb->planes[plane].mem_priv = NULL;
 		vb->v4l2_planes[plane].m.userptr = 0;
 		vb->v4l2_planes[plane].length = 0;
 
 		/* Acquire each plane's memory */
-		mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane],
+		mem_priv = call_memop(vb, get_userptr, q->alloc_ctx[plane],
 				      planes[plane].m.userptr,
 				      planes[plane].length, write);
 		if (IS_ERR_OR_NULL(mem_priv)) {
 			dprintk(1, "qbuf: failed acquiring userspace "
 						"memory for plane %d\n", plane);
+			fail_memop(vb, get_userptr);
 			ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL;
 			goto err;
 		}
@@ -1090,9 +1211,10 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 	 * Call driver-specific initialization on the newly acquired buffer,
 	 * if provided.
 	 */
-	ret = call_qop(q, buf_init, vb);
+	ret = call_vb_qop(vb, buf_init, vb);
 	if (ret) {
 		dprintk(1, "qbuf: buffer initialization failed\n");
+		fail_vb_qop(vb, buf_init);
 		goto err;
 	}
 
@@ -1108,7 +1230,7 @@ err:
 	/* In case of errors, release planes that were already acquired */
 	for (plane = 0; plane < vb->num_planes; ++plane) {
 		if (vb->planes[plane].mem_priv)
-			call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+			call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
 		vb->planes[plane].mem_priv = NULL;
 		vb->v4l2_planes[plane].m.userptr = 0;
 		vb->v4l2_planes[plane].length = 0;
@@ -1173,14 +1295,15 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		dprintk(1, "qbuf: buffer for plane %d changed\n", plane);
 
 		/* Release previously acquired memory if present */
-		__vb2_plane_dmabuf_put(q, &vb->planes[plane]);
+		__vb2_plane_dmabuf_put(vb, &vb->planes[plane]);
 		memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
 
 		/* Acquire each plane's memory */
-		mem_priv = call_memop(q, attach_dmabuf, q->alloc_ctx[plane],
+		mem_priv = call_memop(vb, attach_dmabuf, q->alloc_ctx[plane],
 			dbuf, planes[plane].length, write);
 		if (IS_ERR(mem_priv)) {
 			dprintk(1, "qbuf: failed to attach dmabuf\n");
+			fail_memop(vb, attach_dmabuf);
 			ret = PTR_ERR(mem_priv);
 			dma_buf_put(dbuf);
 			goto err;
@@ -1195,10 +1318,11 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 	 * the buffer(s)..
 	 */
 	for (plane = 0; plane < vb->num_planes; ++plane) {
-		ret = call_memop(q, map_dmabuf, vb->planes[plane].mem_priv);
+		ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv);
 		if (ret) {
 			dprintk(1, "qbuf: failed to map dmabuf for plane %d\n",
 				plane);
+			fail_memop(vb, map_dmabuf);
 			goto err;
 		}
 		vb->planes[plane].dbuf_mapped = 1;
@@ -1208,9 +1332,10 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 	 * Call driver-specific initialization on the newly acquired buffer,
 	 * if provided.
 	 */
-	ret = call_qop(q, buf_init, vb);
+	ret = call_vb_qop(vb, buf_init, vb);
 	if (ret) {
 		dprintk(1, "qbuf: buffer initialization failed\n");
+		fail_vb_qop(vb, buf_init);
 		goto err;
 	}
 
@@ -1242,9 +1367,9 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 
 	/* sync buffers */
 	for (plane = 0; plane < vb->num_planes; ++plane)
-		call_memop(q, prepare, vb->planes[plane].mem_priv);
+		call_memop(vb, prepare, vb->planes[plane].mem_priv);
 
-	q->ops->buf_queue(vb);
+	call_vb_qop(vb, buf_queue, vb);
 }
 
 static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
@@ -1295,8 +1420,11 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		ret = -EINVAL;
 	}
 
-	if (!ret)
-		ret = call_qop(q, buf_prepare, vb);
+	if (!ret) {
+		ret = call_vb_qop(vb, buf_prepare, vb);
+		if (ret)
+			fail_vb_qop(vb, buf_prepare);
+	}
 	if (ret)
 		dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
 	vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;
@@ -1393,6 +1521,8 @@ static int vb2_start_streaming(struct vb2_queue *q)
 
 	/* Tell the driver to start streaming */
 	ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
+	if (ret)
+		fail_qop(q, start_streaming);
 
 	/*
 	 * If there are not enough buffers queued to start streaming, then
@@ -1639,7 +1769,7 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
 		for (i = 0; i < vb->num_planes; ++i) {
 			if (!vb->planes[i].dbuf_mapped)
 				continue;
-			call_memop(q, unmap_dmabuf, vb->planes[i].mem_priv);
+			call_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
 			vb->planes[i].dbuf_mapped = 0;
 		}
 }
@@ -1657,7 +1787,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
 	if (ret < 0)
 		return ret;
 
-	ret = call_qop(q, buf_finish, vb);
+	ret = call_vb_qop(vb, buf_finish, vb);
 	if (ret) {
 		dprintk(1, "dqbuf: buffer finish failed\n");
 		return ret;
@@ -1945,10 +2075,11 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb)
 
 	vb_plane = &vb->planes[eb->plane];
 
-	dbuf = call_memop(q, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE);
+	dbuf = call_memop(vb, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE);
 	if (IS_ERR_OR_NULL(dbuf)) {
 		dprintk(1, "Failed to export buffer %d, plane %d\n",
 			eb->index, eb->plane);
+		fail_memop(vb, get_dmabuf);
 		return -EINVAL;
 	}
 
@@ -2040,9 +2171,11 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
 		return -EINVAL;
 	}
 
-	ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma);
-	if (ret)
+	ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma);
+	if (ret) {
+		fail_memop(vb, mmap);
 		return ret;
+	}
 
 	dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
 	return 0;
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index bef53ce..f04eb28 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -203,6 +203,37 @@ struct vb2_buffer {
 	struct list_head	done_entry;
 
 	struct vb2_plane	planes[VIDEO_MAX_PLANES];
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	/*
+	 * Counters for how often these buffer-related ops are
+	 * called. Used to check for unbalanced ops.
+	 */
+	u32		cnt_mem_alloc;
+	u32		cnt_mem_put;
+	u32		cnt_mem_get_dmabuf;
+	u32		cnt_mem_get_userptr;
+	u32		cnt_mem_put_userptr;
+	u32		cnt_mem_prepare;
+	u32		cnt_mem_finish;
+	u32		cnt_mem_attach_dmabuf;
+	u32		cnt_mem_detach_dmabuf;
+	u32		cnt_mem_map_dmabuf;
+	u32		cnt_mem_unmap_dmabuf;
+	u32		cnt_mem_vaddr;
+	u32		cnt_mem_cookie;
+	u32		cnt_mem_num_users;
+	u32		cnt_mem_mmap;
+
+	u32		cnt_buf_init;
+	u32		cnt_buf_prepare;
+	u32		cnt_buf_finish;
+	u32		cnt_buf_cleanup;
+	u32		cnt_buf_queue;
+
+	/* This counts the number of calls to vb2_buffer_done() */
+	u32		cnt_buf_done;
+#endif
 };
 
 /**
@@ -364,6 +395,18 @@ struct vb2_queue {
 	unsigned int			retry_start_streaming:1;
 
 	struct vb2_fileio_data		*fileio;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	/*
+	 * Counters for how often these queue-related ops are
+	 * called. Used to check for unbalanced ops.
+	 */
+	u32				cnt_queue_setup;
+	u32				cnt_wait_prepare;
+	u32				cnt_wait_finish;
+	u32				cnt_start_streaming;
+	u32				cnt_stop_streaming;
+#endif
 };
 
 void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 02/10] vb2: change result code of buf_finish to void.
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
  2014-02-13  9:40 ` [RFCv3 PATCH 01/10] vb2: add debugging code to check for unbalanced ops Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  2014-02-14  1:30   ` Pawel Osciak
  2014-02-13  9:40 ` [RFCv3 PATCH 03/10] vb2: add note that buf_finish can be called with !vb2_is_streaming() Hans Verkuil
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The buf_finish op should always work, so change the return type to void.
Update the few drivers that use it. Note that buf_finish can be called
both when the DMA is streaming and when it isn't (during queue_cancel).
Update drivers to check that where appropriate.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/parport/bw-qcam.c                 |  6 ++++--
 drivers/media/pci/sta2x11/sta2x11_vip.c         |  7 +++----
 drivers/media/platform/marvell-ccic/mcam-core.c |  3 +--
 drivers/media/usb/pwc/pwc-if.c                  | 16 +++++++++-------
 drivers/media/usb/uvc/uvc_queue.c               |  6 +++---
 drivers/media/v4l2-core/videobuf2-core.c        |  6 +-----
 drivers/staging/media/go7007/go7007-v4l2.c      |  3 +--
 include/media/videobuf2-core.h                  |  2 +-
 8 files changed, 23 insertions(+), 26 deletions(-)

diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c
index d12bd33..8d69bfb 100644
--- a/drivers/media/parport/bw-qcam.c
+++ b/drivers/media/parport/bw-qcam.c
@@ -667,13 +667,16 @@ static void buffer_queue(struct vb2_buffer *vb)
 	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
 }
 
-static int buffer_finish(struct vb2_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
 {
 	struct qcam *qcam = vb2_get_drv_priv(vb->vb2_queue);
 	void *vbuf = vb2_plane_vaddr(vb, 0);
 	int size = vb->vb2_queue->plane_sizes[0];
 	int len;
 
+	if (!vb2_is_streaming(vb->vb2_queue))
+		return;
+
 	mutex_lock(&qcam->lock);
 	parport_claim_or_block(qcam->pdev);
 
@@ -691,7 +694,6 @@ static int buffer_finish(struct vb2_buffer *vb)
 	if (len != size)
 		vb->state = VB2_BUF_STATE_ERROR;
 	vb2_set_plane_payload(vb, 0, len);
-	return 0;
 }
 
 static struct vb2_ops qcam_video_qops = {
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index e5cfb6c..bb11443 100644
--- a/drivers/media/pci/sta2x11/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -327,7 +327,7 @@ static void buffer_queue(struct vb2_buffer *vb)
 	}
 	spin_unlock(&vip->lock);
 }
-static int buffer_finish(struct vb2_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
 {
 	struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue);
 	struct vip_buffer *vip_buf = to_vip_buffer(vb);
@@ -337,9 +337,8 @@ static int buffer_finish(struct vb2_buffer *vb)
 	list_del_init(&vip_buf->list);
 	spin_unlock(&vip->lock);
 
-	vip_active_buf_next(vip);
-
-	return 0;
+	if (vb2_is_streaming(vb->vb2_queue))
+		vip_active_buf_next(vip);
 }
 
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 32fab30..8b34c48 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -1238,7 +1238,7 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
 	return 0;
 }
 
-static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
+static void mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
 {
 	struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
 	struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);
@@ -1246,7 +1246,6 @@ static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
 	if (sg_table)
 		dma_unmap_sg(cam->dev, sg_table->sgl,
 				sg_table->nents, DMA_FROM_DEVICE);
-	return 0;
 }
 
 static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb)
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index abf365a..b9c9f10 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -614,17 +614,19 @@ static int buffer_prepare(struct vb2_buffer *vb)
 	return 0;
 }
 
-static int buffer_finish(struct vb2_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
 {
 	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
 	struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
 
-	/*
-	 * Application has called dqbuf and is getting back a buffer we've
-	 * filled, take the pwc data we've stored in buf->data and decompress
-	 * it into a usable format, storing the result in the vb2_buffer
-	 */
-	return pwc_decompress(pdev, buf);
+	if (vb->state == VB2_BUF_STATE_DONE) {
+		/*
+		 * Application has called dqbuf and is getting back a buffer we've
+		 * filled, take the pwc data we've stored in buf->data and decompress
+		 * it into a usable format, storing the result in the vb2_buffer
+		 */
+		pwc_decompress(pdev, buf);
+	}
 }
 
 static void buffer_cleanup(struct vb2_buffer *vb)
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index cd962be..db5984e 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -104,15 +104,15 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
 	spin_unlock_irqrestore(&queue->irqlock, flags);
 }
 
-static int uvc_buffer_finish(struct vb2_buffer *vb)
+static void uvc_buffer_finish(struct vb2_buffer *vb)
 {
 	struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
 	struct uvc_streaming *stream =
 			container_of(queue, struct uvc_streaming, queue);
 	struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
 
-	uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
-	return 0;
+	if (vb2_is_streaming(vb->vb2_queue))
+		uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
 }
 
 static void uvc_wait_prepare(struct vb2_queue *vq)
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 07b58bd..1f037de 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1787,11 +1787,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
 	if (ret < 0)
 		return ret;
 
-	ret = call_vb_qop(vb, buf_finish, vb);
-	if (ret) {
-		dprintk(1, "dqbuf: buffer finish failed\n");
-		return ret;
-	}
+	call_vb_qop(vb, buf_finish, vb);
 
 	switch (vb->state) {
 	case VB2_BUF_STATE_DONE:
diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c
index edc52e2..3a01576 100644
--- a/drivers/staging/media/go7007/go7007-v4l2.c
+++ b/drivers/staging/media/go7007/go7007-v4l2.c
@@ -470,7 +470,7 @@ static int go7007_buf_prepare(struct vb2_buffer *vb)
 	return 0;
 }
 
-static int go7007_buf_finish(struct vb2_buffer *vb)
+static void go7007_buf_finish(struct vb2_buffer *vb)
 {
 	struct vb2_queue *vq = vb->vb2_queue;
 	struct go7007 *go = vb2_get_drv_priv(vq);
@@ -483,7 +483,6 @@ static int go7007_buf_finish(struct vb2_buffer *vb)
 			V4L2_BUF_FLAG_PFRAME);
 	buf->flags |= frame_type_flag;
 	buf->field = V4L2_FIELD_NONE;
-	return 0;
 }
 
 static int go7007_start_streaming(struct vb2_queue *q, unsigned int count)
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index f04eb28..f443ce0 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -311,7 +311,7 @@ struct vb2_ops {
 
 	int (*buf_init)(struct vb2_buffer *vb);
 	int (*buf_prepare)(struct vb2_buffer *vb);
-	int (*buf_finish)(struct vb2_buffer *vb);
+	void (*buf_finish)(struct vb2_buffer *vb);
 	void (*buf_cleanup)(struct vb2_buffer *vb);
 
 	int (*start_streaming)(struct vb2_queue *q, unsigned int count);
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 03/10] vb2: add note that buf_finish can be called with !vb2_is_streaming()
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
  2014-02-13  9:40 ` [RFCv3 PATCH 01/10] vb2: add debugging code to check for unbalanced ops Hans Verkuil
  2014-02-13  9:40 ` [RFCv3 PATCH 02/10] vb2: change result code of buf_finish to void Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  2014-02-14  1:32   ` Pawel Osciak
  2014-02-13  9:40 ` [RFCv3 PATCH 04/10] vb2: call buf_finish from __dqbuf Hans Verkuil
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Drivers need to be aware that buf_finish can be called when there is no
streaming going on, so make a note of that.

Also add a bunch of missing periods at the end of sentences.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 include/media/videobuf2-core.h | 44 ++++++++++++++++++++++--------------------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index f443ce0..82b7f0f 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -34,49 +34,49 @@ struct vb2_fileio_data;
  *		usually will result in the allocator freeing the buffer (if
  *		no other users of this buffer are present); the buf_priv
  *		argument is the allocator private per-buffer structure
- *		previously returned from the alloc callback
+ *		previously returned from the alloc callback.
  * @get_userptr: acquire userspace memory for a hardware operation; used for
  *		 USERPTR memory types; vaddr is the address passed to the
  *		 videobuf layer when queuing a video buffer of USERPTR type;
  *		 should return an allocator private per-buffer structure
  *		 associated with the buffer on success, NULL on failure;
  *		 the returned private structure will then be passed as buf_priv
- *		 argument to other ops in this structure
+ *		 argument to other ops in this structure.
  * @put_userptr: inform the allocator that a USERPTR buffer will no longer
- *		 be used
+ *		 be used.
  * @attach_dmabuf: attach a shared struct dma_buf for a hardware operation;
  *		   used for DMABUF memory types; alloc_ctx is the alloc context
  *		   dbuf is the shared dma_buf; returns NULL on failure;
  *		   allocator private per-buffer structure on success;
- *		   this needs to be used for further accesses to the buffer
+ *		   this needs to be used for further accesses to the buffer.
  * @detach_dmabuf: inform the exporter of the buffer that the current DMABUF
  *		   buffer is no longer used; the buf_priv argument is the
  *		   allocator private per-buffer structure previously returned
- *		   from the attach_dmabuf callback
+ *		   from the attach_dmabuf callback.
  * @map_dmabuf: request for access to the dmabuf from allocator; the allocator
  *		of dmabuf is informed that this driver is going to use the
- *		dmabuf
+ *		dmabuf.
  * @unmap_dmabuf: releases access control to the dmabuf - allocator is notified
- *		  that this driver is done using the dmabuf for now
+ *		  that this driver is done using the dmabuf for now.
  * @prepare:	called every time the buffer is passed from userspace to the
- *		driver, useful for cache synchronisation, optional
+ *		driver, useful for cache synchronisation, optional.
  * @finish:	called every time the buffer is passed back from the driver
- *		to the userspace, also optional
+ *		to the userspace, also optional.
  * @vaddr:	return a kernel virtual address to a given memory buffer
  *		associated with the passed private structure or NULL if no
- *		such mapping exists
+ *		such mapping exists.
  * @cookie:	return allocator specific cookie for a given memory buffer
  *		associated with the passed private structure or NULL if not
- *		available
+ *		available.
  * @num_users:	return the current number of users of a memory buffer;
  *		return 1 if the videobuf layer (or actually the driver using
- *		it) is the only user
+ *		it) is the only user.
  * @mmap:	setup a userspace mapping for a given memory buffer under
- *		the provided virtual memory region
+ *		the provided virtual memory region.
  *
  * Required ops for USERPTR types: get_userptr, put_userptr.
  * Required ops for MMAP types: alloc, put, num_users, mmap.
- * Required ops for read/write access types: alloc, put, num_users, vaddr
+ * Required ops for read/write access types: alloc, put, num_users, vaddr.
  * Required ops for DMABUF types: attach_dmabuf, detach_dmabuf, map_dmabuf,
  *				  unmap_dmabuf.
  */
@@ -258,27 +258,29 @@ struct vb2_buffer {
  * @wait_prepare:	release any locks taken while calling vb2 functions;
  *			it is called before an ioctl needs to wait for a new
  *			buffer to arrive; required to avoid a deadlock in
- *			blocking access type
+ *			blocking access type.
  * @wait_finish:	reacquire all locks released in the previous callback;
  *			required to continue operation after sleeping while
- *			waiting for a new buffer to arrive
+ *			waiting for a new buffer to arrive.
  * @buf_init:		called once after allocating a buffer (in MMAP case)
  *			or after acquiring a new USERPTR buffer; drivers may
  *			perform additional buffer-related initialization;
  *			initialization failure (return != 0) will prevent
- *			queue setup from completing successfully; optional
+ *			queue setup from completing successfully; optional.
  * @buf_prepare:	called every time the buffer is queued from userspace
  *			and from the VIDIOC_PREPARE_BUF ioctl; drivers may
  *			perform any initialization required before each hardware
  *			operation in this callback; drivers that support
  *			VIDIOC_CREATE_BUFS must also validate the buffer size;
  *			if an error is returned, the buffer will not be queued
- *			in driver; optional
+ *			in driver; optional.
  * @buf_finish:		called before every dequeue of the buffer back to
  *			userspace; drivers may perform any operations required
- *			before userspace accesses the buffer; optional
+ *			before userspace accesses the buffer; optional. Note:
+ *			this op can be called as well when vb2_is_streaming()
+ *			returns false!
  * @buf_cleanup:	called once before the buffer is freed; drivers may
- *			perform any additional cleanup; optional
+ *			perform any additional cleanup; optional.
  * @start_streaming:	called once to enter 'streaming' state; the driver may
  *			receive buffers with @buf_queue callback before
  *			@start_streaming is called; the driver gets the number
@@ -299,7 +301,7 @@ struct vb2_buffer {
  *			the buffer back by calling vb2_buffer_done() function;
  *			it is allways called after calling STREAMON ioctl;
  *			might be called before start_streaming callback if user
- *			pre-queued buffers before calling STREAMON
+ *			pre-queued buffers before calling STREAMON.
  */
 struct vb2_ops {
 	int (*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt,
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 04/10] vb2: call buf_finish from __dqbuf
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
                   ` (2 preceding siblings ...)
  2014-02-13  9:40 ` [RFCv3 PATCH 03/10] vb2: add note that buf_finish can be called with !vb2_is_streaming() Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  2014-02-14  1:35   ` Pawel Osciak
  2014-02-13  9:40 ` [RFCv3 PATCH 05/10] vb2: fix buf_init/buf_cleanup call sequences Hans Verkuil
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

This ensures that it is also called from queue_cancel, which also calls
__dqbuf(). Without this change any time queue_cancel is called while
streaming the buf_finish op will not be called and any driver cleanup
will not happen.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/videobuf2-core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 1f037de..3756378 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1762,6 +1762,8 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
 	if (vb->state == VB2_BUF_STATE_DEQUEUED)
 		return;
 
+	call_vb_qop(vb, buf_finish, vb);
+
 	vb->state = VB2_BUF_STATE_DEQUEUED;
 
 	/* unmap DMABUF buffer */
@@ -1787,8 +1789,6 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
 	if (ret < 0)
 		return ret;
 
-	call_vb_qop(vb, buf_finish, vb);
-
 	switch (vb->state) {
 	case VB2_BUF_STATE_DONE:
 		dprintk(3, "dqbuf: Returning done buffer\n");
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 05/10] vb2: fix buf_init/buf_cleanup call sequences
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
                   ` (3 preceding siblings ...)
  2014-02-13  9:40 ` [RFCv3 PATCH 04/10] vb2: call buf_finish from __dqbuf Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  2014-02-14  4:40   ` Pawel Osciak
  2014-02-13  9:40 ` [RFCv3 PATCH 06/10] vb2: fix read/write regression Hans Verkuil
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Ensure that these ops are properly balanced.

There two scenarios:

1) for MMAP buf_init is called when the buffers are created and buf_cleanup
   must be called when the queue is finally freed. This scenario was always
   working.

2) for USERPTR and DMABUF it is more complicated. When a buffer is queued
   the code checks if all planes of this buffer have been acquired before.
   If that's the case, then only buf_prepare has to be called. Otherwise
   buf_clean needs to be called if the buffer was acquired before, then,
   once all changed planes have been (re)acquired, buf_init has to be
   called again followed by buf_prepare. Should buf_prepare fail, then
   buf_cleanup must be called again because all planes will be released
   in case of an error.

Finally, in __vb2_queue_free we have to check if the buffer was actually
acquired before calling buf_cleanup. While that it always true for MMAP
mode, it is not necessarily true for the other modes. E.g. if you just
call REQBUFS and close the filehandle, then buffers were ever queued and
so no buf_init was ever called.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/videobuf2-core.c | 100 +++++++++++++++++++++----------
 1 file changed, 67 insertions(+), 33 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 3756378..7766bf5 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -373,8 +373,10 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
 	/* Call driver-provided cleanup function for each buffer, if provided */
 	for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
 	     ++buffer) {
-		if (q->bufs[buffer])
-			call_vb_qop(q->bufs[buffer], buf_cleanup, q->bufs[buffer]);
+		struct vb2_buffer *vb = q->bufs[buffer];
+
+		if (vb && vb->planes[0].mem_priv)
+			call_vb_qop(vb, buf_cleanup, vb);
 	}
 
 	/* Release video buffer memory */
@@ -1161,6 +1163,7 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 	unsigned int plane;
 	int ret;
 	int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+	bool reacquired = vb->planes[0].mem_priv == NULL;
 
 	/* Copy relevant information provided by the userspace */
 	__fill_vb2_buffer(vb, b, planes);
@@ -1186,12 +1189,16 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		}
 
 		/* Release previously acquired memory if present */
-		if (vb->planes[plane].mem_priv)
+		if (vb->planes[plane].mem_priv) {
+			if (!reacquired) {
+				reacquired = true;
+				call_vb_qop(vb, buf_cleanup, vb);
+			}
 			call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
+		}
 
 		vb->planes[plane].mem_priv = NULL;
-		vb->v4l2_planes[plane].m.userptr = 0;
-		vb->v4l2_planes[plane].length = 0;
+		memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
 
 		/* Acquire each plane's memory */
 		mem_priv = call_memop(vb, get_userptr, q->alloc_ctx[plane],
@@ -1208,23 +1215,34 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 	}
 
 	/*
-	 * Call driver-specific initialization on the newly acquired buffer,
-	 * if provided.
-	 */
-	ret = call_vb_qop(vb, buf_init, vb);
-	if (ret) {
-		dprintk(1, "qbuf: buffer initialization failed\n");
-		fail_vb_qop(vb, buf_init);
-		goto err;
-	}
-
-	/*
 	 * Now that everything is in order, copy relevant information
 	 * provided by userspace.
 	 */
 	for (plane = 0; plane < vb->num_planes; ++plane)
 		vb->v4l2_planes[plane] = planes[plane];
 
+	if (reacquired) {
+		/*
+		 * One or more planes changed, so we must call buf_init to do
+		 * the driver-specific initialization on the newly acquired
+		 * buffer, if provided.
+		 */
+		ret = call_vb_qop(vb, buf_init, vb);
+		if (ret) {
+			dprintk(1, "qbuf: buffer initialization failed\n");
+			fail_vb_qop(vb, buf_init);
+			goto err;
+		}
+	}
+
+	ret = call_vb_qop(vb, buf_prepare, vb);
+	if (ret) {
+		dprintk(1, "qbuf: buffer preparation failed\n");
+		fail_vb_qop(vb, buf_prepare);
+		call_vb_qop(vb, buf_cleanup, vb);
+		goto err;
+	}
+
 	return 0;
 err:
 	/* In case of errors, release planes that were already acquired */
@@ -1244,8 +1262,13 @@ err:
  */
 static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 {
+	int ret;
+
 	__fill_vb2_buffer(vb, b, vb->v4l2_planes);
-	return 0;
+	ret = call_vb_qop(vb, buf_prepare, vb);
+	if (ret)
+		fail_vb_qop(vb, buf_prepare);
+	return ret;
 }
 
 /**
@@ -1259,6 +1282,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 	unsigned int plane;
 	int ret;
 	int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+	bool reacquired = vb->planes[0].mem_priv == NULL;
 
 	/* Copy relevant information provided by the userspace */
 	__fill_vb2_buffer(vb, b, planes);
@@ -1294,6 +1318,11 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 
 		dprintk(1, "qbuf: buffer for plane %d changed\n", plane);
 
+		if (!reacquired) {
+			reacquired = true;
+			call_vb_qop(vb, buf_cleanup, vb);
+		}
+
 		/* Release previously acquired memory if present */
 		__vb2_plane_dmabuf_put(vb, &vb->planes[plane]);
 		memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
@@ -1329,23 +1358,33 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 	}
 
 	/*
-	 * Call driver-specific initialization on the newly acquired buffer,
-	 * if provided.
-	 */
-	ret = call_vb_qop(vb, buf_init, vb);
-	if (ret) {
-		dprintk(1, "qbuf: buffer initialization failed\n");
-		fail_vb_qop(vb, buf_init);
-		goto err;
-	}
-
-	/*
 	 * Now that everything is in order, copy relevant information
 	 * provided by userspace.
 	 */
 	for (plane = 0; plane < vb->num_planes; ++plane)
 		vb->v4l2_planes[plane] = planes[plane];
 
+	if (reacquired) {
+		/*
+		 * Call driver-specific initialization on the newly acquired buffer,
+		 * if provided.
+		 */
+		ret = call_vb_qop(vb, buf_init, vb);
+		if (ret) {
+			dprintk(1, "qbuf: buffer initialization failed\n");
+			fail_vb_qop(vb, buf_init);
+			goto err;
+		}
+	}
+
+	ret = call_vb_qop(vb, buf_prepare, vb);
+	if (ret) {
+		dprintk(1, "qbuf: buffer preparation failed\n");
+		fail_vb_qop(vb, buf_prepare);
+		call_vb_qop(vb, buf_cleanup, vb);
+		goto err;
+	}
+
 	return 0;
 err:
 	/* In case of errors, release planes that were already acquired */
@@ -1420,11 +1459,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		ret = -EINVAL;
 	}
 
-	if (!ret) {
-		ret = call_vb_qop(vb, buf_prepare, vb);
-		if (ret)
-			fail_vb_qop(vb, buf_prepare);
-	}
 	if (ret)
 		dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
 	vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 06/10] vb2: fix read/write regression
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
                   ` (4 preceding siblings ...)
  2014-02-13  9:40 ` [RFCv3 PATCH 05/10] vb2: fix buf_init/buf_cleanup call sequences Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  2014-02-14  4:49   ` Pawel Osciak
  2014-02-13  9:40 ` [RFCv3 PATCH 07/10] vb2: rename queued_count to owned_by_drv_count Hans Verkuil
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil, Andy Walls

From: Hans Verkuil <hans.verkuil@cisco.com>

Commit 88e268702bfba78448abd20a31129458707383aa ("vb2: Improve file I/O
emulation to handle buffers in any order") broke read/write support if
the size of the buffer being read/written is less than the size of the
image.

When the commit was tested originally I used qv4l2, which call read()
with exactly the size of the image. But if you try 'cat /dev/video0'
then it will fail and typically hang after reading two buffers.

This patch fixes the behavior by adding a new buf_index field that
contains the index of the field currently being filled/read, or it
is num_buffers in which case a new buffer needs to be dequeued.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Cc: Andy Walls <awalls@md.metrocast.net>
---
 drivers/media/v4l2-core/videobuf2-core.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 7766bf5..a3b4b4c 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -2418,6 +2418,7 @@ struct vb2_fileio_data {
 	struct v4l2_requestbuffers req;
 	struct v4l2_buffer b;
 	struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
+	unsigned int buf_index;
 	unsigned int index;
 	unsigned int q_count;
 	unsigned int dq_count;
@@ -2519,6 +2520,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
 			fileio->bufs[i].queued = 1;
 		}
 		fileio->index = q->num_buffers;
+		fileio->buf_index = q->num_buffers;
 	}
 
 	/*
@@ -2597,7 +2599,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
 	/*
 	 * Check if we need to dequeue the buffer.
 	 */
-	index = fileio->index;
+	index = fileio->buf_index;
 	if (index >= q->num_buffers) {
 		/*
 		 * Call vb2_dqbuf to get buffer back.
@@ -2611,7 +2613,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
 			return ret;
 		fileio->dq_count += 1;
 
-		index = fileio->b.index;
+		fileio->buf_index = index = fileio->b.index;
 		buf = &fileio->bufs[index];
 
 		/*
@@ -2689,6 +2691,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
 		fileio->q_count += 1;
 		if (fileio->index < q->num_buffers)
 			fileio->index++;
+		fileio->buf_index = fileio->index;
 	}
 
 	/*
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 07/10] vb2: rename queued_count to owned_by_drv_count
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
                   ` (5 preceding siblings ...)
  2014-02-13  9:40 ` [RFCv3 PATCH 06/10] vb2: fix read/write regression Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  2014-02-14  4:49   ` Pawel Osciak
  2014-02-13  9:40 ` [RFCv3 PATCH 08/10] vb2: only call start_streaming if sufficient buffers are queued Hans Verkuil
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

'queued_count' is a bit vague since it is not clear to which queue it
refers to: the vb2 internal list of buffers or the driver-owned list
of buffers.

Rename to make it explicit.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/videobuf2-core.c | 10 +++++-----
 include/media/videobuf2-core.h           |  4 ++--
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index a3b4b4c..6af76ee 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1071,7 +1071,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
 	spin_lock_irqsave(&q->done_lock, flags);
 	vb->state = state;
 	list_add_tail(&vb->done_entry, &q->done_list);
-	atomic_dec(&q->queued_count);
+	atomic_dec(&q->owned_by_drv_count);
 	spin_unlock_irqrestore(&q->done_lock, flags);
 
 	/* Inform any processes that may be waiting for buffers */
@@ -1402,7 +1402,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 	unsigned int plane;
 
 	vb->state = VB2_BUF_STATE_ACTIVE;
-	atomic_inc(&q->queued_count);
+	atomic_inc(&q->owned_by_drv_count);
 
 	/* sync buffers */
 	for (plane = 0; plane < vb->num_planes; ++plane)
@@ -1554,7 +1554,7 @@ static int vb2_start_streaming(struct vb2_queue *q)
 	int ret;
 
 	/* Tell the driver to start streaming */
-	ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
+	ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count));
 	if (ret)
 		fail_qop(q, start_streaming);
 
@@ -1779,7 +1779,7 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q)
 	}
 
 	if (!q->retry_start_streaming)
-		wait_event(q->done_wq, !atomic_read(&q->queued_count));
+		wait_event(q->done_wq, !atomic_read(&q->owned_by_drv_count));
 	return 0;
 }
 EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
@@ -1911,7 +1911,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
 	 * has not already dequeued before initiating cancel.
 	 */
 	INIT_LIST_HEAD(&q->done_list);
-	atomic_set(&q->queued_count, 0);
+	atomic_set(&q->owned_by_drv_count, 0);
 	wake_up_all(&q->done_wq);
 
 	/*
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 82b7f0f..adaffed 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -353,7 +353,7 @@ struct v4l2_fh;
  * @bufs:	videobuf buffer structures
  * @num_buffers: number of allocated/used buffers
  * @queued_list: list of buffers currently queued from userspace
- * @queued_count: number of buffers owned by the driver
+ * @owned_by_drv_count: number of buffers owned by the driver
  * @done_list:	list of buffers ready to be dequeued to userspace
  * @done_lock:	lock to protect done_list list
  * @done_wq:	waitqueue for processes waiting for buffers ready to be dequeued
@@ -385,7 +385,7 @@ struct vb2_queue {
 
 	struct list_head		queued_list;
 
-	atomic_t			queued_count;
+	atomic_t			owned_by_drv_count;
 	struct list_head		done_list;
 	spinlock_t			done_lock;
 	wait_queue_head_t		done_wq;
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 08/10] vb2: only call start_streaming if sufficient buffers are queued
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
                   ` (6 preceding siblings ...)
  2014-02-13  9:40 ` [RFCv3 PATCH 07/10] vb2: rename queued_count to owned_by_drv_count Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  2014-02-14  5:13   ` Pawel Osciak
  2014-02-13  9:40 ` [RFCv3 PATCH 09/10] vivi: correctly cleanup after a start_streaming failure Hans Verkuil
  2014-02-13  9:40 ` [RFCv3 PATCH 10/10] v4l2-ioctl: add CREATE_BUFS sanity checks Hans Verkuil
  9 siblings, 1 reply; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

In commit 02f142ecd24aaf891324ffba8527284c1731b561 support was added to
start_streaming to return -ENOBUFS if insufficient buffers were queued
for the DMA engine to start. The vb2 core would attempt calling
start_streaming again if another buffer would be queued up.

Later analysis uncovered problems with the queue management if start_streaming
would return an error: the buffers are enqueued to the driver before the
start_streaming op is called, so after an error they are never returned to
the vb2 core. The solution for this is to let the driver return them to
the vb2 core in case of an error while starting the DMA engine. However,
in the case of -ENOBUFS that would be weird: it is not a real error, it
just says that more buffers are needed. Requiring start_streaming to give
them back only to have them requeued again the next time the application
calls QBUF is inefficient.

This patch changes this mechanism: it adds a 'min_buffers_needed' field
to vb2_queue that drivers can set with the minimum number of buffers
required to start the DMA engine. The start_streaming op is only called
if enough buffers are queued. The -ENOBUFS handling has been dropped in
favor of this new method.

Drivers are expected to return buffers back to vb2 core with state QUEUED
if start_streaming would return an error. The vb2 core checks for this
and produces a warning if that didn't happen and it will forcefully
reclaim such buffers to ensure that the internal vb2 core state remains
consistent and all buffer-related resources have been correctly freed
and all op callss have been balanced.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/platform/davinci/vpbe_display.c   |   6 +-
 drivers/media/platform/davinci/vpif_capture.c   |   7 +-
 drivers/media/platform/davinci/vpif_display.c   |   7 +-
 drivers/media/platform/s5p-tv/mixer_video.c     |   6 +-
 drivers/media/v4l2-core/videobuf2-core.c        | 130 ++++++++++++++++--------
 drivers/staging/media/davinci_vpfe/vpfe_video.c |   3 +-
 include/media/videobuf2-core.h                  |  14 ++-
 7 files changed, 100 insertions(+), 73 deletions(-)

diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index b02aba4..9231e48 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -344,11 +344,6 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
 	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
 	int ret;
 
-	/* If buffer queue is empty, return error */
-	if (list_empty(&layer->dma_queue)) {
-		v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
-		return -ENOBUFS;
-	}
 	/* Get the next frame from the buffer queue */
 	layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
 				struct vpbe_disp_buffer, list);
@@ -1416,6 +1411,7 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,
 	q->mem_ops = &vb2_dma_contig_memops;
 	q->buf_struct_size = sizeof(struct vpbe_disp_buffer);
 	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 1;
 
 	ret = vb2_queue_init(q);
 	if (ret) {
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 735ec47..9731ad4 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -272,13 +272,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
 	unsigned long flags;
 	int ret;
 
-	/* If buffer queue is empty, return error */
 	spin_lock_irqsave(&common->irqlock, flags);
-	if (list_empty(&common->dma_queue)) {
-		spin_unlock_irqrestore(&common->irqlock, flags);
-		vpif_dbg(1, debug, "buffer queue is empty\n");
-		return -ENOBUFS;
-	}
 
 	/* Get the next frame from the buffer queue */
 	common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
@@ -1024,6 +1018,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
 	q->mem_ops = &vb2_dma_contig_memops;
 	q->buf_struct_size = sizeof(struct vpif_cap_buffer);
 	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 1;
 
 	ret = vb2_queue_init(q);
 	if (ret) {
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 9d115cd..af660f5 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -234,13 +234,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
 	unsigned long flags;
 	int ret;
 
-	/* If buffer queue is empty, return error */
 	spin_lock_irqsave(&common->irqlock, flags);
-	if (list_empty(&common->dma_queue)) {
-		spin_unlock_irqrestore(&common->irqlock, flags);
-		vpif_err("buffer queue is empty\n");
-		return -ENOBUFS;
-	}
 
 	/* Get the next frame from the buffer queue */
 	common->next_frm = common->cur_frm =
@@ -984,6 +978,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
 	q->mem_ops = &vb2_dma_contig_memops;
 	q->buf_struct_size = sizeof(struct vpif_disp_buffer);
 	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 1;
 
 	ret = vb2_queue_init(q);
 	if (ret) {
diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
index c5059ba..a1ce55f 100644
--- a/drivers/media/platform/s5p-tv/mixer_video.c
+++ b/drivers/media/platform/s5p-tv/mixer_video.c
@@ -946,11 +946,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
 
 	mxr_dbg(mdev, "%s\n", __func__);
 
-	if (count == 0) {
-		mxr_dbg(mdev, "no output buffers queued\n");
-		return -ENOBUFS;
-	}
-
 	/* block any changes in output configuration */
 	mxr_output_get(mdev);
 
@@ -1124,6 +1119,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
 		.drv_priv = layer,
 		.buf_struct_size = sizeof(struct mxr_buffer),
 		.ops = &mxr_video_qops,
+		.min_buffers_needed = 1,
 		.mem_ops = &vb2_dma_contig_memops,
 	};
 
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 6af76ee..1dd2b05 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1033,13 +1033,20 @@ EXPORT_SYMBOL_GPL(vb2_plane_cookie);
  * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
  * @vb:		vb2_buffer returned from the driver
  * @state:	either VB2_BUF_STATE_DONE if the operation finished successfully
- *		or VB2_BUF_STATE_ERROR if the operation finished with an error
+ *		or VB2_BUF_STATE_ERROR if the operation finished with an error.
+ *		If start_streaming fails then it should return buffers with state
+ *		VB2_BUF_STATE_QUEUED to put them back into the queue.
  *
  * This function should be called by the driver after a hardware operation on
  * a buffer is finished and the buffer may be returned to userspace. The driver
  * cannot use this buffer anymore until it is queued back to it by videobuf
  * by the means of buf_queue callback. Only buffers previously queued to the
  * driver by buf_queue can be passed to this function.
+ *
+ * While streaming a buffer can only be returned in state DONE or ERROR.
+ * The start_streaming op can also return them in case the DMA engine cannot
+ * be started for some reason. In that case the buffers should be returned with
+ * state QUEUED.
  */
 void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
 {
@@ -1047,11 +1054,17 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
 	unsigned long flags;
 	unsigned int plane;
 
-	if (vb->state != VB2_BUF_STATE_ACTIVE)
+	if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE))
 		return;
 
-	if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
-		return;
+	if (!q->start_streaming_called) {
+		if (WARN_ON(state != VB2_BUF_STATE_QUEUED))
+			state = VB2_BUF_STATE_QUEUED;
+	} else if (!WARN_ON(!q->start_streaming_called)) {
+		if (WARN_ON(state != VB2_BUF_STATE_DONE &&
+			    state != VB2_BUF_STATE_ERROR))
+			state = VB2_BUF_STATE_ERROR;
+	}
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	/*
@@ -1070,12 +1083,14 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
 	/* Add the buffer to the done buffers list */
 	spin_lock_irqsave(&q->done_lock, flags);
 	vb->state = state;
-	list_add_tail(&vb->done_entry, &q->done_list);
+	if (state != VB2_BUF_STATE_QUEUED)
+		list_add_tail(&vb->done_entry, &q->done_list);
 	atomic_dec(&q->owned_by_drv_count);
 	spin_unlock_irqrestore(&q->done_lock, flags);
 
 	/* Inform any processes that may be waiting for buffers */
-	wake_up(&q->done_wq);
+	if (state != VB2_BUF_STATE_QUEUED)
+		wake_up(&q->done_wq);
 }
 EXPORT_SYMBOL_GPL(vb2_buffer_done);
 
@@ -1544,34 +1559,49 @@ EXPORT_SYMBOL_GPL(vb2_prepare_buf);
  * vb2_start_streaming() - Attempt to start streaming.
  * @q:		videobuf2 queue
  *
- * If there are not enough buffers, then retry_start_streaming is set to
- * 1 and 0 is returned. The next time a buffer is queued and
- * retry_start_streaming is 1, this function will be called again to
- * retry starting the DMA engine.
+ * Attempt to start streaming. When this function is called there must be
+ * at least q->min_buffers_needed buffers queued up (i.e. the minimum
+ * number of buffers required for the DMA engine to function). If the
+ * @start_streaming op fails it is supposed to return all the driver-owned
+ * buffers back to vb2 in state QUEUED. Check if that happened and if
+ * not warn and reclaim them forcefully.
  */
 static int vb2_start_streaming(struct vb2_queue *q)
 {
+	struct vb2_buffer *vb;
 	int ret;
 
-	/* Tell the driver to start streaming */
-	ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count));
-	if (ret)
-		fail_qop(q, start_streaming);
-
 	/*
-	 * If there are not enough buffers queued to start streaming, then
-	 * the start_streaming operation will return -ENOBUFS and you have to
-	 * retry when the next buffer is queued.
+	 * If any buffers were queued before streamon,
+	 * we can now pass them to driver for processing.
 	 */
-	if (ret == -ENOBUFS) {
-		dprintk(1, "qbuf: not enough buffers, retry when more buffers are queued.\n");
-		q->retry_start_streaming = 1;
+	list_for_each_entry(vb, &q->queued_list, queued_entry)
+		__enqueue_in_driver(vb);
+
+	/* Tell the driver to start streaming */
+	ret = call_qop(q, start_streaming, q,
+		       atomic_read(&q->owned_by_drv_count));
+	q->start_streaming_called = ret == 0;
+	if (!ret)
 		return 0;
+
+	fail_qop(q, start_streaming);
+	dprintk(1, "qbuf: driver refused to start streaming\n");
+	if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
+		unsigned i;
+
+		/*
+		 * Forcefully reclaim buffers if the driver did not
+		 * correctly return them to vb2.
+		 */
+		for (i = 0; i < q->num_buffers; ++i) {
+			vb = q->bufs[i];
+			if (vb->state == VB2_BUF_STATE_ACTIVE)
+				vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
+		}
+		/* Must be zero now */
+		WARN_ON(atomic_read(&q->owned_by_drv_count));
 	}
-	if (ret)
-		dprintk(1, "qbuf: driver refused to start streaming\n");
-	else
-		q->retry_start_streaming = 0;
 	return ret;
 }
 
@@ -1611,19 +1641,26 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
 	 * dequeued in dqbuf.
 	 */
 	list_add_tail(&vb->queued_entry, &q->queued_list);
+	q->queued_count++;
 	vb->state = VB2_BUF_STATE_QUEUED;
 
 	/*
 	 * If already streaming, give the buffer to driver for processing.
 	 * If not, the buffer will be given to driver on next streamon.
 	 */
-	if (q->streaming)
+	if (q->start_streaming_called)
 		__enqueue_in_driver(vb);
 
 	/* Fill buffer information for the userspace */
 	__fill_v4l2_buffer(vb, b);
 
-	if (q->retry_start_streaming) {
+	/*
+	 * If we haven't yet started streaming and if we have the
+	 * minimum number of required buffers available, then start
+	 * streaming.
+	 */
+	if (q->streaming && !q->start_streaming_called &&
+	    q->queued_count >= q->min_buffers_needed) {
 		ret = vb2_start_streaming(q);
 		if (ret)
 			return ret;
@@ -1778,7 +1815,7 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q)
 		return -EINVAL;
 	}
 
-	if (!q->retry_start_streaming)
+	if (q->start_streaming_called)
 		wait_event(q->done_wq, !atomic_read(&q->owned_by_drv_count));
 	return 0;
 }
@@ -1839,6 +1876,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
 	__fill_v4l2_buffer(vb, b);
 	/* Remove from videobuf queue */
 	list_del(&vb->queued_entry);
+	q->queued_count--;
 	/* go back to dequeued state */
 	__vb2_dqbuf(vb);
 
@@ -1889,18 +1927,23 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
 {
 	unsigned int i;
 
-	if (q->retry_start_streaming) {
-		q->retry_start_streaming = 0;
-		q->streaming = 0;
-	}
-
 	/*
 	 * Tell driver to stop all transactions and release all queued
 	 * buffers.
 	 */
-	if (q->streaming)
+	if (q->start_streaming_called)
 		call_qop(q, stop_streaming, q);
 	q->streaming = 0;
+	q->start_streaming_called = 0;
+	q->queued_count = 0;
+
+	if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
+		for (i = 0; i < q->num_buffers; ++i)
+			if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE)
+				vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR);
+		/* Must be zero now */
+		WARN_ON(atomic_read(&q->owned_by_drv_count));
+	}
 
 	/*
 	 * Remove all buffers from videobuf's list...
@@ -1923,7 +1966,6 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
 
 static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
 {
-	struct vb2_buffer *vb;
 	int ret;
 
 	if (type != q->type) {
@@ -1937,17 +1979,15 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
 	}
 
 	/*
-	 * If any buffers were queued before streamon,
-	 * we can now pass them to driver for processing.
+	 * Tell driver to start streaming provided sufficient buffers
+	 * are available.
 	 */
-	list_for_each_entry(vb, &q->queued_list, queued_entry)
-		__enqueue_in_driver(vb);
-
-	/* Tell driver to start streaming. */
-	ret = vb2_start_streaming(q);
-	if (ret) {
-		__vb2_queue_cancel(q);
-		return ret;
+	if (q->queued_count >= q->min_buffers_needed) {
+		ret = vb2_start_streaming(q);
+		if (ret) {
+			__vb2_queue_cancel(q);
+			return ret;
+		}
 	}
 
 	q->streaming = 1;
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index 1f3b0f9..8c101cb 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -1201,8 +1201,6 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
 	unsigned long addr;
 	int ret;
 
-	if (count == 0)
-		return -ENOBUFS;
 	ret = mutex_lock_interruptible(&video->lock);
 	if (ret)
 		goto streamoff;
@@ -1327,6 +1325,7 @@ static int vpfe_reqbufs(struct file *file, void *priv,
 	q->type = req_buf->type;
 	q->io_modes = VB2_MMAP | VB2_USERPTR;
 	q->drv_priv = fh;
+	q->min_buffers_needed = 1;
 	q->ops = &video_qops;
 	q->mem_ops = &vb2_dma_contig_memops;
 	q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index adaffed..2ada71b 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -348,20 +348,24 @@ struct v4l2_fh;
  * @gfp_flags:	additional gfp flags used when allocating the buffers.
  *		Typically this is 0, but it may be e.g. GFP_DMA or __GFP_DMA32
  *		to force the buffer allocation to a specific memory zone.
+ * @min_buffers_needed: the minimum number of buffers needed before
+ *		start_streaming() can be called. Used when a DMA engine
+ *		cannot be started unless at least this number of buffers
+ *		have been queued into the driver.
  *
  * @memory:	current memory type used
  * @bufs:	videobuf buffer structures
  * @num_buffers: number of allocated/used buffers
  * @queued_list: list of buffers currently queued from userspace
+ * @queued_count: number of buffers queued and ready for streaming.
  * @owned_by_drv_count: number of buffers owned by the driver
  * @done_list:	list of buffers ready to be dequeued to userspace
  * @done_lock:	lock to protect done_list list
  * @done_wq:	waitqueue for processes waiting for buffers ready to be dequeued
  * @alloc_ctx:	memory type/allocator-specific contexts for each plane
  * @streaming:	current streaming state
- * @retry_start_streaming: start_streaming() was called, but there were not enough
- *		buffers queued. If set, then retry calling start_streaming when
- *		queuing a new buffer.
+ * @start_streaming_called: start_streaming() was called successfully and we
+ *		started streaming.
  * @fileio:	file io emulator internal data, used only if emulator is active
  */
 struct vb2_queue {
@@ -377,6 +381,7 @@ struct vb2_queue {
 	unsigned int			buf_struct_size;
 	u32				timestamp_type;
 	gfp_t				gfp_flags;
+	u32				min_buffers_needed;
 
 /* private: internal use only */
 	enum v4l2_memory		memory;
@@ -384,6 +389,7 @@ struct vb2_queue {
 	unsigned int			num_buffers;
 
 	struct list_head		queued_list;
+	unsigned int			queued_count;
 
 	atomic_t			owned_by_drv_count;
 	struct list_head		done_list;
@@ -394,7 +400,7 @@ struct vb2_queue {
 	unsigned int			plane_sizes[VIDEO_MAX_PLANES];
 
 	unsigned int			streaming:1;
-	unsigned int			retry_start_streaming:1;
+	unsigned int			start_streaming_called:1;
 
 	struct vb2_fileio_data		*fileio;
 
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 09/10] vivi: correctly cleanup after a start_streaming failure.
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
                   ` (7 preceding siblings ...)
  2014-02-13  9:40 ` [RFCv3 PATCH 08/10] vb2: only call start_streaming if sufficient buffers are queued Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  2014-02-13  9:40 ` [RFCv3 PATCH 10/10] v4l2-ioctl: add CREATE_BUFS sanity checks Hans Verkuil
  9 siblings, 0 replies; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

If start_streaming fails then any queued buffers must be given back
to the vb2 core.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/platform/vivi.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c
index 2d4e73b..6085e2f 100644
--- a/drivers/media/platform/vivi.c
+++ b/drivers/media/platform/vivi.c
@@ -901,8 +901,19 @@ static void buffer_queue(struct vb2_buffer *vb)
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
 	struct vivi_dev *dev = vb2_get_drv_priv(vq);
+	int err;
+
 	dprintk(dev, 1, "%s\n", __func__);
-	return vivi_start_generating(dev);
+	err = vivi_start_generating(dev);
+	if (err) {
+		struct vivi_buffer *buf, *tmp;
+
+		list_for_each_entry_safe(buf, tmp, &dev->vidq.active, list) {
+			list_del(&buf->list);
+			vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+		}
+	}
+	return err;
 }
 
 /* abort streaming and wait for last buffer */
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFCv3 PATCH 10/10] v4l2-ioctl: add CREATE_BUFS sanity checks.
  2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
                   ` (8 preceding siblings ...)
  2014-02-13  9:40 ` [RFCv3 PATCH 09/10] vivi: correctly cleanup after a start_streaming failure Hans Verkuil
@ 2014-02-13  9:40 ` Hans Verkuil
  9 siblings, 0 replies; 21+ messages in thread
From: Hans Verkuil @ 2014-02-13  9:40 UTC (permalink / raw)
  To: linux-media; +Cc: pawel, s.nawrocki, m.szyprowski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Many drivers do not check anything. At least make sure that the various
buffer size related fields are not obviously wrong.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-ioctl.c | 52 ++++++++++++++++++++++++++++++++++--
 1 file changed, 50 insertions(+), 2 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 707aef7..69a1948 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1444,9 +1444,57 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
 	struct v4l2_create_buffers *create = arg;
-	int ret = check_fmt(file, create->format.type);
+	const struct v4l2_format *fmt = &create->format;
+	const struct v4l2_pix_format *pix = &fmt->fmt.pix;
+	const struct v4l2_pix_format_mplane *mp = &fmt->fmt.pix_mp;
+	const struct v4l2_plane_pix_format *p;
+	int ret = check_fmt(file, fmt->type);
+	unsigned i;
+
+	if (ret)
+		return ret;
 
-	return ret ? ret : ops->vidioc_create_bufs(file, fh, create);
+	/* Sanity checks */
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		if (pix->sizeimage == 0 || pix->width == 0 || pix->height == 0)
+			return -EINVAL;
+		/* Note: bytesperline is 0 for compressed formats */
+		if (pix->bytesperline &&
+		    pix->height * pix->bytesperline > pix->sizeimage)
+			return -EINVAL;
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (mp->num_planes == 0 || mp->width == 0 || mp->height == 0)
+			return -EINVAL;
+		for (i = 0; i < mp->num_planes; i++) {
+			p = &mp->plane_fmt[i];
+
+			if (p->sizeimage == 0)
+				return -EINVAL;
+			/* Note: bytesperline is 0 for compressed formats */
+			if (p->bytesperline &&
+			    p->bytesperline * mp->height > p->sizeimage)
+				return -EINVAL;
+		}
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+	case V4L2_BUF_TYPE_VBI_OUTPUT:
+		if (fmt->fmt.vbi.count[0] + fmt->fmt.vbi.count[1] == 0)
+			return -EINVAL;
+		break;
+	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+		if (fmt->fmt.sliced.io_size == 0)
+			return -EINVAL;
+		break;
+	default:
+		/* Overlay formats are invalid */
+		return -EINVAL;
+	}
+	return ops->vidioc_create_bufs(file, fh, create);
 }
 
 static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
-- 
1.8.4.rc3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 02/10] vb2: change result code of buf_finish to void.
  2014-02-13  9:40 ` [RFCv3 PATCH 02/10] vb2: change result code of buf_finish to void Hans Verkuil
@ 2014-02-14  1:30   ` Pawel Osciak
  0 siblings, 0 replies; 21+ messages in thread
From: Pawel Osciak @ 2014-02-14  1:30 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil

Thanks!

vb2 parts:
Acked-by: Pawel Osciak <pawel@osciak.com>

others:
Reviewed-by: Pawel Osciak <pawel@osciak.com>

On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> The buf_finish op should always work, so change the return type to void.
> Update the few drivers that use it. Note that buf_finish can be called
> both when the DMA is streaming and when it isn't (during queue_cancel).
> Update drivers to check that where appropriate.
>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/parport/bw-qcam.c                 |  6 ++++--
>  drivers/media/pci/sta2x11/sta2x11_vip.c         |  7 +++----
>  drivers/media/platform/marvell-ccic/mcam-core.c |  3 +--
>  drivers/media/usb/pwc/pwc-if.c                  | 16 +++++++++-------
>  drivers/media/usb/uvc/uvc_queue.c               |  6 +++---
>  drivers/media/v4l2-core/videobuf2-core.c        |  6 +-----
>  drivers/staging/media/go7007/go7007-v4l2.c      |  3 +--
>  include/media/videobuf2-core.h                  |  2 +-
>  8 files changed, 23 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c
> index d12bd33..8d69bfb 100644
> --- a/drivers/media/parport/bw-qcam.c
> +++ b/drivers/media/parport/bw-qcam.c
> @@ -667,13 +667,16 @@ static void buffer_queue(struct vb2_buffer *vb)
>         vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
>  }
>
> -static int buffer_finish(struct vb2_buffer *vb)
> +static void buffer_finish(struct vb2_buffer *vb)
>  {
>         struct qcam *qcam = vb2_get_drv_priv(vb->vb2_queue);
>         void *vbuf = vb2_plane_vaddr(vb, 0);
>         int size = vb->vb2_queue->plane_sizes[0];
>         int len;
>
> +       if (!vb2_is_streaming(vb->vb2_queue))
> +               return;
> +
>         mutex_lock(&qcam->lock);
>         parport_claim_or_block(qcam->pdev);
>
> @@ -691,7 +694,6 @@ static int buffer_finish(struct vb2_buffer *vb)
>         if (len != size)
>                 vb->state = VB2_BUF_STATE_ERROR;
>         vb2_set_plane_payload(vb, 0, len);
> -       return 0;
>  }
>
>  static struct vb2_ops qcam_video_qops = {
> diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
> index e5cfb6c..bb11443 100644
> --- a/drivers/media/pci/sta2x11/sta2x11_vip.c
> +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
> @@ -327,7 +327,7 @@ static void buffer_queue(struct vb2_buffer *vb)
>         }
>         spin_unlock(&vip->lock);
>  }
> -static int buffer_finish(struct vb2_buffer *vb)
> +static void buffer_finish(struct vb2_buffer *vb)
>  {
>         struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue);
>         struct vip_buffer *vip_buf = to_vip_buffer(vb);
> @@ -337,9 +337,8 @@ static int buffer_finish(struct vb2_buffer *vb)
>         list_del_init(&vip_buf->list);
>         spin_unlock(&vip->lock);
>
> -       vip_active_buf_next(vip);
> -
> -       return 0;
> +       if (vb2_is_streaming(vb->vb2_queue))
> +               vip_active_buf_next(vip);
>  }
>
>  static int start_streaming(struct vb2_queue *vq, unsigned int count)
> diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
> index 32fab30..8b34c48 100644
> --- a/drivers/media/platform/marvell-ccic/mcam-core.c
> +++ b/drivers/media/platform/marvell-ccic/mcam-core.c
> @@ -1238,7 +1238,7 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
>         return 0;
>  }
>
> -static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
> +static void mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
>  {
>         struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
>         struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);
> @@ -1246,7 +1246,6 @@ static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
>         if (sg_table)
>                 dma_unmap_sg(cam->dev, sg_table->sgl,
>                                 sg_table->nents, DMA_FROM_DEVICE);
> -       return 0;
>  }
>
>  static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb)
> diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
> index abf365a..b9c9f10 100644
> --- a/drivers/media/usb/pwc/pwc-if.c
> +++ b/drivers/media/usb/pwc/pwc-if.c
> @@ -614,17 +614,19 @@ static int buffer_prepare(struct vb2_buffer *vb)
>         return 0;
>  }
>
> -static int buffer_finish(struct vb2_buffer *vb)
> +static void buffer_finish(struct vb2_buffer *vb)
>  {
>         struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
>         struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
>
> -       /*
> -        * Application has called dqbuf and is getting back a buffer we've
> -        * filled, take the pwc data we've stored in buf->data and decompress
> -        * it into a usable format, storing the result in the vb2_buffer
> -        */
> -       return pwc_decompress(pdev, buf);
> +       if (vb->state == VB2_BUF_STATE_DONE) {
> +               /*
> +                * Application has called dqbuf and is getting back a buffer we've
> +                * filled, take the pwc data we've stored in buf->data and decompress
> +                * it into a usable format, storing the result in the vb2_buffer
> +                */
> +               pwc_decompress(pdev, buf);
> +       }
>  }
>
>  static void buffer_cleanup(struct vb2_buffer *vb)
> diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
> index cd962be..db5984e 100644
> --- a/drivers/media/usb/uvc/uvc_queue.c
> +++ b/drivers/media/usb/uvc/uvc_queue.c
> @@ -104,15 +104,15 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
>         spin_unlock_irqrestore(&queue->irqlock, flags);
>  }
>
> -static int uvc_buffer_finish(struct vb2_buffer *vb)
> +static void uvc_buffer_finish(struct vb2_buffer *vb)
>  {
>         struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
>         struct uvc_streaming *stream =
>                         container_of(queue, struct uvc_streaming, queue);
>         struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
>
> -       uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
> -       return 0;
> +       if (vb2_is_streaming(vb->vb2_queue))
> +               uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
>  }
>
>  static void uvc_wait_prepare(struct vb2_queue *vq)
> diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
> index 07b58bd..1f037de 100644
> --- a/drivers/media/v4l2-core/videobuf2-core.c
> +++ b/drivers/media/v4l2-core/videobuf2-core.c
> @@ -1787,11 +1787,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
>         if (ret < 0)
>                 return ret;
>
> -       ret = call_vb_qop(vb, buf_finish, vb);
> -       if (ret) {
> -               dprintk(1, "dqbuf: buffer finish failed\n");
> -               return ret;
> -       }
> +       call_vb_qop(vb, buf_finish, vb);
>
>         switch (vb->state) {
>         case VB2_BUF_STATE_DONE:
> diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c
> index edc52e2..3a01576 100644
> --- a/drivers/staging/media/go7007/go7007-v4l2.c
> +++ b/drivers/staging/media/go7007/go7007-v4l2.c
> @@ -470,7 +470,7 @@ static int go7007_buf_prepare(struct vb2_buffer *vb)
>         return 0;
>  }
>
> -static int go7007_buf_finish(struct vb2_buffer *vb)
> +static void go7007_buf_finish(struct vb2_buffer *vb)
>  {
>         struct vb2_queue *vq = vb->vb2_queue;
>         struct go7007 *go = vb2_get_drv_priv(vq);
> @@ -483,7 +483,6 @@ static int go7007_buf_finish(struct vb2_buffer *vb)
>                         V4L2_BUF_FLAG_PFRAME);
>         buf->flags |= frame_type_flag;
>         buf->field = V4L2_FIELD_NONE;
> -       return 0;
>  }
>
>  static int go7007_start_streaming(struct vb2_queue *q, unsigned int count)
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index f04eb28..f443ce0 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -311,7 +311,7 @@ struct vb2_ops {
>
>         int (*buf_init)(struct vb2_buffer *vb);
>         int (*buf_prepare)(struct vb2_buffer *vb);
> -       int (*buf_finish)(struct vb2_buffer *vb);
> +       void (*buf_finish)(struct vb2_buffer *vb);
>         void (*buf_cleanup)(struct vb2_buffer *vb);
>
>         int (*start_streaming)(struct vb2_queue *q, unsigned int count);
> --
> 1.8.4.rc3
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 03/10] vb2: add note that buf_finish can be called with !vb2_is_streaming()
  2014-02-13  9:40 ` [RFCv3 PATCH 03/10] vb2: add note that buf_finish can be called with !vb2_is_streaming() Hans Verkuil
@ 2014-02-14  1:32   ` Pawel Osciak
  0 siblings, 0 replies; 21+ messages in thread
From: Pawel Osciak @ 2014-02-14  1:32 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil

Thanks Hans.

Acked-by: Pawel Osciak <pawel@osciak.com>

On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> Drivers need to be aware that buf_finish can be called when there is no
> streaming going on, so make a note of that.
>
> Also add a bunch of missing periods at the end of sentences.
>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  include/media/videobuf2-core.h | 44 ++++++++++++++++++++++--------------------
>  1 file changed, 23 insertions(+), 21 deletions(-)
>
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index f443ce0..82b7f0f 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -34,49 +34,49 @@ struct vb2_fileio_data;
>   *             usually will result in the allocator freeing the buffer (if
>   *             no other users of this buffer are present); the buf_priv
>   *             argument is the allocator private per-buffer structure
> - *             previously returned from the alloc callback
> + *             previously returned from the alloc callback.
>   * @get_userptr: acquire userspace memory for a hardware operation; used for
>   *              USERPTR memory types; vaddr is the address passed to the
>   *              videobuf layer when queuing a video buffer of USERPTR type;
>   *              should return an allocator private per-buffer structure
>   *              associated with the buffer on success, NULL on failure;
>   *              the returned private structure will then be passed as buf_priv
> - *              argument to other ops in this structure
> + *              argument to other ops in this structure.
>   * @put_userptr: inform the allocator that a USERPTR buffer will no longer
> - *              be used
> + *              be used.
>   * @attach_dmabuf: attach a shared struct dma_buf for a hardware operation;
>   *                used for DMABUF memory types; alloc_ctx is the alloc context
>   *                dbuf is the shared dma_buf; returns NULL on failure;
>   *                allocator private per-buffer structure on success;
> - *                this needs to be used for further accesses to the buffer
> + *                this needs to be used for further accesses to the buffer.
>   * @detach_dmabuf: inform the exporter of the buffer that the current DMABUF
>   *                buffer is no longer used; the buf_priv argument is the
>   *                allocator private per-buffer structure previously returned
> - *                from the attach_dmabuf callback
> + *                from the attach_dmabuf callback.
>   * @map_dmabuf: request for access to the dmabuf from allocator; the allocator
>   *             of dmabuf is informed that this driver is going to use the
> - *             dmabuf
> + *             dmabuf.
>   * @unmap_dmabuf: releases access control to the dmabuf - allocator is notified
> - *               that this driver is done using the dmabuf for now
> + *               that this driver is done using the dmabuf for now.
>   * @prepare:   called every time the buffer is passed from userspace to the
> - *             driver, useful for cache synchronisation, optional
> + *             driver, useful for cache synchronisation, optional.
>   * @finish:    called every time the buffer is passed back from the driver
> - *             to the userspace, also optional
> + *             to the userspace, also optional.
>   * @vaddr:     return a kernel virtual address to a given memory buffer
>   *             associated with the passed private structure or NULL if no
> - *             such mapping exists
> + *             such mapping exists.
>   * @cookie:    return allocator specific cookie for a given memory buffer
>   *             associated with the passed private structure or NULL if not
> - *             available
> + *             available.
>   * @num_users: return the current number of users of a memory buffer;
>   *             return 1 if the videobuf layer (or actually the driver using
> - *             it) is the only user
> + *             it) is the only user.
>   * @mmap:      setup a userspace mapping for a given memory buffer under
> - *             the provided virtual memory region
> + *             the provided virtual memory region.
>   *
>   * Required ops for USERPTR types: get_userptr, put_userptr.
>   * Required ops for MMAP types: alloc, put, num_users, mmap.
> - * Required ops for read/write access types: alloc, put, num_users, vaddr
> + * Required ops for read/write access types: alloc, put, num_users, vaddr.
>   * Required ops for DMABUF types: attach_dmabuf, detach_dmabuf, map_dmabuf,
>   *                               unmap_dmabuf.
>   */
> @@ -258,27 +258,29 @@ struct vb2_buffer {
>   * @wait_prepare:      release any locks taken while calling vb2 functions;
>   *                     it is called before an ioctl needs to wait for a new
>   *                     buffer to arrive; required to avoid a deadlock in
> - *                     blocking access type
> + *                     blocking access type.
>   * @wait_finish:       reacquire all locks released in the previous callback;
>   *                     required to continue operation after sleeping while
> - *                     waiting for a new buffer to arrive
> + *                     waiting for a new buffer to arrive.
>   * @buf_init:          called once after allocating a buffer (in MMAP case)
>   *                     or after acquiring a new USERPTR buffer; drivers may
>   *                     perform additional buffer-related initialization;
>   *                     initialization failure (return != 0) will prevent
> - *                     queue setup from completing successfully; optional
> + *                     queue setup from completing successfully; optional.
>   * @buf_prepare:       called every time the buffer is queued from userspace
>   *                     and from the VIDIOC_PREPARE_BUF ioctl; drivers may
>   *                     perform any initialization required before each hardware
>   *                     operation in this callback; drivers that support
>   *                     VIDIOC_CREATE_BUFS must also validate the buffer size;
>   *                     if an error is returned, the buffer will not be queued
> - *                     in driver; optional
> + *                     in driver; optional.
>   * @buf_finish:                called before every dequeue of the buffer back to
>   *                     userspace; drivers may perform any operations required
> - *                     before userspace accesses the buffer; optional
> + *                     before userspace accesses the buffer; optional. Note:
> + *                     this op can be called as well when vb2_is_streaming()
> + *                     returns false!
>   * @buf_cleanup:       called once before the buffer is freed; drivers may
> - *                     perform any additional cleanup; optional
> + *                     perform any additional cleanup; optional.
>   * @start_streaming:   called once to enter 'streaming' state; the driver may
>   *                     receive buffers with @buf_queue callback before
>   *                     @start_streaming is called; the driver gets the number
> @@ -299,7 +301,7 @@ struct vb2_buffer {
>   *                     the buffer back by calling vb2_buffer_done() function;
>   *                     it is allways called after calling STREAMON ioctl;
>   *                     might be called before start_streaming callback if user
> - *                     pre-queued buffers before calling STREAMON
> + *                     pre-queued buffers before calling STREAMON.
>   */
>  struct vb2_ops {
>         int (*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt,
> --
> 1.8.4.rc3
>



-- 
Best regards,
Pawel Osciak

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 04/10] vb2: call buf_finish from __dqbuf
  2014-02-13  9:40 ` [RFCv3 PATCH 04/10] vb2: call buf_finish from __dqbuf Hans Verkuil
@ 2014-02-14  1:35   ` Pawel Osciak
  0 siblings, 0 replies; 21+ messages in thread
From: Pawel Osciak @ 2014-02-14  1:35 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil

On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> This ensures that it is also called from queue_cancel, which also calls
> __dqbuf(). Without this change any time queue_cancel is called while
> streaming the buf_finish op will not be called and any driver cleanup
> will not happen.
>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Acked-by: Pawel Osciak <pawel@osciak.com>

> ---
>  drivers/media/v4l2-core/videobuf2-core.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
> index 1f037de..3756378 100644
> --- a/drivers/media/v4l2-core/videobuf2-core.c
> +++ b/drivers/media/v4l2-core/videobuf2-core.c
> @@ -1762,6 +1762,8 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
>         if (vb->state == VB2_BUF_STATE_DEQUEUED)
>                 return;
>
> +       call_vb_qop(vb, buf_finish, vb);
> +
>         vb->state = VB2_BUF_STATE_DEQUEUED;
>
>         /* unmap DMABUF buffer */
> @@ -1787,8 +1789,6 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
>         if (ret < 0)
>                 return ret;
>
> -       call_vb_qop(vb, buf_finish, vb);
> -
>         switch (vb->state) {
>         case VB2_BUF_STATE_DONE:
>                 dprintk(3, "dqbuf: Returning done buffer\n");
> --
> 1.8.4.rc3
>



-- 
Best regards,
Pawel Osciak

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 05/10] vb2: fix buf_init/buf_cleanup call sequences
  2014-02-13  9:40 ` [RFCv3 PATCH 05/10] vb2: fix buf_init/buf_cleanup call sequences Hans Verkuil
@ 2014-02-14  4:40   ` Pawel Osciak
  2014-02-14 10:28     ` Hans Verkuil
  0 siblings, 1 reply; 21+ messages in thread
From: Pawel Osciak @ 2014-02-14  4:40 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil

On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> Ensure that these ops are properly balanced.
>
> There two scenarios:

s/There two/There are two/

>
> 1) for MMAP buf_init is called when the buffers are created and buf_cleanup
>    must be called when the queue is finally freed. This scenario was always
>    working.
>
> 2) for USERPTR and DMABUF it is more complicated. When a buffer is queued
>    the code checks if all planes of this buffer have been acquired before.
>    If that's the case, then only buf_prepare has to be called. Otherwise
>    buf_clean needs to be called if the buffer was acquired before, then,

s/buf_clean/buf_cleanup/

>    once all changed planes have been (re)acquired, buf_init has to be
>    called again followed by buf_prepare. Should buf_prepare fail, then

s/again//

I know what you mean, but there is only one call to buf_init in this
particular sequence.

>    buf_cleanup must be called again because all planes will be released

s/again because all planes will be released/on the newly acquired
planes to release them/

>    in case of an error.
>
> Finally, in __vb2_queue_free we have to check if the buffer was actually
> acquired before calling buf_cleanup. While that it always true for MMAP
> mode, it is not necessarily true for the other modes. E.g. if you just
> call REQBUFS and close the filehandle, then buffers were ever queued and

s/ever/never/
s/filehandle/file handle/

> so no buf_init was ever called.
>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/videobuf2-core.c | 100 +++++++++++++++++++++----------
>  1 file changed, 67 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
> index 3756378..7766bf5 100644
> --- a/drivers/media/v4l2-core/videobuf2-core.c
> +++ b/drivers/media/v4l2-core/videobuf2-core.c
> @@ -373,8 +373,10 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
>         /* Call driver-provided cleanup function for each buffer, if provided */
>         for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
>              ++buffer) {
> -               if (q->bufs[buffer])
> -                       call_vb_qop(q->bufs[buffer], buf_cleanup, q->bufs[buffer]);
> +               struct vb2_buffer *vb = q->bufs[buffer];
> +
> +               if (vb && vb->planes[0].mem_priv)
> +                       call_vb_qop(vb, buf_cleanup, vb);
>         }
>
>         /* Release video buffer memory */
> @@ -1161,6 +1163,7 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>         unsigned int plane;
>         int ret;
>         int write = !V4L2_TYPE_IS_OUTPUT(q->type);
> +       bool reacquired = vb->planes[0].mem_priv == NULL;

This requires a comment I feel. In general, I'm not especially happy
with the fact that we are making mem_priv != NULL equivalent to
buf_init() called and succeeded. It is true right now, but it'll be
very hard to make the association without previously seeing this very
patch I feel.

I don't see a perfect way to do this, but I'm strongly leaning towards
having a bool inited in the vb2_buffer. Really.

>
>         /* Copy relevant information provided by the userspace */
>         __fill_vb2_buffer(vb, b, planes);
> @@ -1186,12 +1189,16 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>                 }
>
>                 /* Release previously acquired memory if present */
> -               if (vb->planes[plane].mem_priv)
> +               if (vb->planes[plane].mem_priv) {
> +                       if (!reacquired) {
> +                               reacquired = true;
> +                               call_vb_qop(vb, buf_cleanup, vb);
> +                       }
>                         call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
> +               }
>
>                 vb->planes[plane].mem_priv = NULL;
> -               vb->v4l2_planes[plane].m.userptr = 0;
> -               vb->v4l2_planes[plane].length = 0;
> +               memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
>
>                 /* Acquire each plane's memory */
>                 mem_priv = call_memop(vb, get_userptr, q->alloc_ctx[plane],
> @@ -1208,23 +1215,34 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>         }
>
>         /*
> -        * Call driver-specific initialization on the newly acquired buffer,
> -        * if provided.
> -        */
> -       ret = call_vb_qop(vb, buf_init, vb);
> -       if (ret) {
> -               dprintk(1, "qbuf: buffer initialization failed\n");
> -               fail_vb_qop(vb, buf_init);
> -               goto err;
> -       }
> -
> -       /*
>          * Now that everything is in order, copy relevant information
>          * provided by userspace.
>          */
>         for (plane = 0; plane < vb->num_planes; ++plane)
>                 vb->v4l2_planes[plane] = planes[plane];
>
> +       if (reacquired) {l
> +               /*
> +                * One or more planes changed, so we must call buf_init to do
> +                * the driver-specific initialization on the newly acquired
> +                * buffer, if provided.
> +                */
> +               ret = call_vb_qop(vb, buf_init, vb);
> +               if (ret) {
> +                       dprintk(1, "qbuf: buffer initialization failed\n");
> +                       fail_vb_qop(vb, buf_init);
> +                       goto err;
> +               }
> +       }
> +
> +       ret = call_vb_qop(vb, buf_prepare, vb);
> +       if (ret) {
> +               dprintk(1, "qbuf: buffer preparation failed\n");
> +               fail_vb_qop(vb, buf_prepare);
> +               call_vb_qop(vb, buf_cleanup, vb);
> +               goto err;
> +       }
> +
>         return 0;
>  err:
>         /* In case of errors, release planes that were already acquired */
> @@ -1244,8 +1262,13 @@ err:
>   */
>  static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>  {
> +       int ret;
> +
>         __fill_vb2_buffer(vb, b, vb->v4l2_planes);
> -       return 0;
> +       ret = call_vb_qop(vb, buf_prepare, vb);
> +       if (ret)
> +               fail_vb_qop(vb, buf_prepare);
> +       return ret;
>  }
>
>  /**
> @@ -1259,6 +1282,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>         unsigned int plane;
>         int ret;
>         int write = !V4L2_TYPE_IS_OUTPUT(q->type);
> +       bool reacquired = vb->planes[0].mem_priv == NULL;
>
>         /* Copy relevant information provided by the userspace */
>         __fill_vb2_buffer(vb, b, planes);
> @@ -1294,6 +1318,11 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>
>                 dprintk(1, "qbuf: buffer for plane %d changed\n", plane);
>
> +               if (!reacquired) {
> +                       reacquired = true;
> +                       call_vb_qop(vb, buf_cleanup, vb);
> +               }
> +
>                 /* Release previously acquired memory if present */
>                 __vb2_plane_dmabuf_put(vb, &vb->planes[plane]);
>                 memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
> @@ -1329,23 +1358,33 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>         }
>
>         /*
> -        * Call driver-specific initialization on the newly acquired buffer,
> -        * if provided.
> -        */
> -       ret = call_vb_qop(vb, buf_init, vb);
> -       if (ret) {
> -               dprintk(1, "qbuf: buffer initialization failed\n");
> -               fail_vb_qop(vb, buf_init);
> -               goto err;
> -       }
> -
> -       /*
>          * Now that everything is in order, copy relevant information
>          * provided by userspace.
>          */
>         for (plane = 0; plane < vb->num_planes; ++plane)
>                 vb->v4l2_planes[plane] = planes[plane];
>
> +       if (reacquired) {

This sequence until 'return 0" is an exact same code as in
__qbuf_userptr. Could we extract this?
I'm thinking that we could do this:

- rename __qbuf_userptr and __qbuf_dmabuf to __acquire_userptr/dmabuf
and have them return whether reacquired
- extract the sequence above and call buf_prepare from __buf_prepare
for userptr and dmabuf after calling __acquire_*
- get rid of qbuf_dmabuf, instead just call buf_prepare from
__buf_prepare directly like for useptr and dmabuf

> +               /*
> +                * Call driver-specific initialization on the newly acquired buffer,
> +                * if provided.
> +                */
> +               ret = call_vb_qop(vb, buf_init, vb);
> +               if (ret) {
> +                       dprintk(1, "qbuf: buffer initialization failed\n");
> +                       fail_vb_qop(vb, buf_init);
> +                       goto err;
> +               }
> +       }
> +
> +       ret = call_vb_qop(vb, buf_prepare, vb);
> +       if (ret) {
> +               dprintk(1, "qbuf: buffer preparation failed\n");
> +               fail_vb_qop(vb, buf_prepare);
> +               call_vb_qop(vb, buf_cleanup, vb);

Should we explicitly set mem_priv to NULL here? This is one the issues
I have with using mem_priv, it's hard to reason if it's ok to do
buf_cleanup and expect mem_priv to be NULL here always.
Could we please add that inited bool into vb2_buffer to make it simpler?

> +               goto err;
> +       }
> +
>         return 0;
>  err:
>         /* In case of errors, release planes that were already acquired */
> @@ -1420,11 +1459,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>                 ret = -EINVAL;
>         }
>
> -       if (!ret) {
> -               ret = call_vb_qop(vb, buf_prepare, vb);
> -               if (ret)
> -                       fail_vb_qop(vb, buf_prepare);
> -       }
>         if (ret)
>                 dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
>         vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;
> --
> 1.8.4.rc3
>



-- 
Best regards,
Pawel Osciak

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 06/10] vb2: fix read/write regression
  2014-02-13  9:40 ` [RFCv3 PATCH 06/10] vb2: fix read/write regression Hans Verkuil
@ 2014-02-14  4:49   ` Pawel Osciak
  2014-02-14 10:29     ` Hans Verkuil
  0 siblings, 1 reply; 21+ messages in thread
From: Pawel Osciak @ 2014-02-14  4:49 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil, Andy Walls

On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> Commit 88e268702bfba78448abd20a31129458707383aa ("vb2: Improve file I/O
> emulation to handle buffers in any order") broke read/write support if
> the size of the buffer being read/written is less than the size of the
> image.
>
> When the commit was tested originally I used qv4l2, which call read()
> with exactly the size of the image. But if you try 'cat /dev/video0'
> then it will fail and typically hang after reading two buffers.
>
> This patch fixes the behavior by adding a new buf_index field that
> contains the index of the field currently being filled/read, or it
> is num_buffers in which case a new buffer needs to be dequeued.
>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Cc: Andy Walls <awalls@md.metrocast.net>
> ---
>  drivers/media/v4l2-core/videobuf2-core.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
> index 7766bf5..a3b4b4c 100644
> --- a/drivers/media/v4l2-core/videobuf2-core.c
> +++ b/drivers/media/v4l2-core/videobuf2-core.c
> @@ -2418,6 +2418,7 @@ struct vb2_fileio_data {
>         struct v4l2_requestbuffers req;
>         struct v4l2_buffer b;
>         struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
> +       unsigned int buf_index;
>         unsigned int index;

The two index fields are now confusing, especially because there is no
documentation. Perhaps we could call index "cur_index" and also add
documentation please?

>         unsigned int q_count;
>         unsigned int dq_count;
> @@ -2519,6 +2520,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
>                         fileio->bufs[i].queued = 1;
>                 }
>                 fileio->index = q->num_buffers;
> +               fileio->buf_index = q->num_buffers;
>         }
>
>         /*
> @@ -2597,7 +2599,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
>         /*
>          * Check if we need to dequeue the buffer.
>          */
> -       index = fileio->index;
> +       index = fileio->buf_index;
>         if (index >= q->num_buffers) {
>                 /*
>                  * Call vb2_dqbuf to get buffer back.
> @@ -2611,7 +2613,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
>                         return ret;
>                 fileio->dq_count += 1;
>
> -               index = fileio->b.index;
> +               fileio->buf_index = index = fileio->b.index;
>                 buf = &fileio->bufs[index];
>
>                 /*
> @@ -2689,6 +2691,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
>                 fileio->q_count += 1;
>                 if (fileio->index < q->num_buffers)
>                         fileio->index++;
> +               fileio->buf_index = fileio->index;
>         }
>
>         /*
> --
> 1.8.4.rc3
>



-- 
Best regards,
Pawel Osciak

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 07/10] vb2: rename queued_count to owned_by_drv_count
  2014-02-13  9:40 ` [RFCv3 PATCH 07/10] vb2: rename queued_count to owned_by_drv_count Hans Verkuil
@ 2014-02-14  4:49   ` Pawel Osciak
  0 siblings, 0 replies; 21+ messages in thread
From: Pawel Osciak @ 2014-02-14  4:49 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil

On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> 'queued_count' is a bit vague since it is not clear to which queue it
> refers to: the vb2 internal list of buffers or the driver-owned list
> of buffers.
>
> Rename to make it explicit.
>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Acked-by: Pawel Osciak <pawel@osciak.com>

> ---
>  drivers/media/v4l2-core/videobuf2-core.c | 10 +++++-----
>  include/media/videobuf2-core.h           |  4 ++--
>  2 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
> index a3b4b4c..6af76ee 100644
> --- a/drivers/media/v4l2-core/videobuf2-core.c
> +++ b/drivers/media/v4l2-core/videobuf2-core.c
> @@ -1071,7 +1071,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>         spin_lock_irqsave(&q->done_lock, flags);
>         vb->state = state;
>         list_add_tail(&vb->done_entry, &q->done_list);
> -       atomic_dec(&q->queued_count);
> +       atomic_dec(&q->owned_by_drv_count);
>         spin_unlock_irqrestore(&q->done_lock, flags);
>
>         /* Inform any processes that may be waiting for buffers */
> @@ -1402,7 +1402,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
>         unsigned int plane;
>
>         vb->state = VB2_BUF_STATE_ACTIVE;
> -       atomic_inc(&q->queued_count);
> +       atomic_inc(&q->owned_by_drv_count);
>
>         /* sync buffers */
>         for (plane = 0; plane < vb->num_planes; ++plane)
> @@ -1554,7 +1554,7 @@ static int vb2_start_streaming(struct vb2_queue *q)
>         int ret;
>
>         /* Tell the driver to start streaming */
> -       ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
> +       ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count));
>         if (ret)
>                 fail_qop(q, start_streaming);
>
> @@ -1779,7 +1779,7 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q)
>         }
>
>         if (!q->retry_start_streaming)
> -               wait_event(q->done_wq, !atomic_read(&q->queued_count));
> +               wait_event(q->done_wq, !atomic_read(&q->owned_by_drv_count));
>         return 0;
>  }
>  EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
> @@ -1911,7 +1911,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
>          * has not already dequeued before initiating cancel.
>          */
>         INIT_LIST_HEAD(&q->done_list);
> -       atomic_set(&q->queued_count, 0);
> +       atomic_set(&q->owned_by_drv_count, 0);
>         wake_up_all(&q->done_wq);
>
>         /*
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index 82b7f0f..adaffed 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -353,7 +353,7 @@ struct v4l2_fh;
>   * @bufs:      videobuf buffer structures
>   * @num_buffers: number of allocated/used buffers
>   * @queued_list: list of buffers currently queued from userspace
> - * @queued_count: number of buffers owned by the driver
> + * @owned_by_drv_count: number of buffers owned by the driver
>   * @done_list: list of buffers ready to be dequeued to userspace
>   * @done_lock: lock to protect done_list list
>   * @done_wq:   waitqueue for processes waiting for buffers ready to be dequeued
> @@ -385,7 +385,7 @@ struct vb2_queue {
>
>         struct list_head                queued_list;
>
> -       atomic_t                        queued_count;
> +       atomic_t                        owned_by_drv_count;
>         struct list_head                done_list;
>         spinlock_t                      done_lock;
>         wait_queue_head_t               done_wq;
> --
> 1.8.4.rc3
>



-- 
Best regards,
Pawel Osciak

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 08/10] vb2: only call start_streaming if sufficient buffers are queued
  2014-02-13  9:40 ` [RFCv3 PATCH 08/10] vb2: only call start_streaming if sufficient buffers are queued Hans Verkuil
@ 2014-02-14  5:13   ` Pawel Osciak
  2014-02-14 10:31     ` Hans Verkuil
  0 siblings, 1 reply; 21+ messages in thread
From: Pawel Osciak @ 2014-02-14  5:13 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil

On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> In commit 02f142ecd24aaf891324ffba8527284c1731b561 support was added to
> start_streaming to return -ENOBUFS if insufficient buffers were queued
> for the DMA engine to start. The vb2 core would attempt calling
> start_streaming again if another buffer would be queued up.
>
> Later analysis uncovered problems with the queue management if start_streaming
> would return an error: the buffers are enqueued to the driver before the
> start_streaming op is called, so after an error they are never returned to
> the vb2 core. The solution for this is to let the driver return them to
> the vb2 core in case of an error while starting the DMA engine. However,
> in the case of -ENOBUFS that would be weird: it is not a real error, it
> just says that more buffers are needed. Requiring start_streaming to give
> them back only to have them requeued again the next time the application
> calls QBUF is inefficient.
>
> This patch changes this mechanism: it adds a 'min_buffers_needed' field
> to vb2_queue that drivers can set with the minimum number of buffers
> required to start the DMA engine. The start_streaming op is only called
> if enough buffers are queued. The -ENOBUFS handling has been dropped in
> favor of this new method.
>
> Drivers are expected to return buffers back to vb2 core with state QUEUED
> if start_streaming would return an error. The vb2 core checks for this
> and produces a warning if that didn't happen and it will forcefully
> reclaim such buffers to ensure that the internal vb2 core state remains
> consistent and all buffer-related resources have been correctly freed
> and all op callss have been balanced.

s/callss/calls/

>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/platform/davinci/vpbe_display.c   |   6 +-
>  drivers/media/platform/davinci/vpif_capture.c   |   7 +-
>  drivers/media/platform/davinci/vpif_display.c   |   7 +-
>  drivers/media/platform/s5p-tv/mixer_video.c     |   6 +-
>  drivers/media/v4l2-core/videobuf2-core.c        | 130 ++++++++++++++++--------
>  drivers/staging/media/davinci_vpfe/vpfe_video.c |   3 +-
>  include/media/videobuf2-core.h                  |  14 ++-
>  7 files changed, 100 insertions(+), 73 deletions(-)
>
> diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
> index b02aba4..9231e48 100644
> --- a/drivers/media/platform/davinci/vpbe_display.c
> +++ b/drivers/media/platform/davinci/vpbe_display.c
> @@ -344,11 +344,6 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
>         struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
>         int ret;
>
> -       /* If buffer queue is empty, return error */
> -       if (list_empty(&layer->dma_queue)) {
> -               v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
> -               return -ENOBUFS;
> -       }
>         /* Get the next frame from the buffer queue */
>         layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
>                                 struct vpbe_disp_buffer, list);
> @@ -1416,6 +1411,7 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,
>         q->mem_ops = &vb2_dma_contig_memops;
>         q->buf_struct_size = sizeof(struct vpbe_disp_buffer);
>         q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +       q->min_buffers_needed = 1;
>
>         ret = vb2_queue_init(q);
>         if (ret) {
> diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
> index 735ec47..9731ad4 100644
> --- a/drivers/media/platform/davinci/vpif_capture.c
> +++ b/drivers/media/platform/davinci/vpif_capture.c
> @@ -272,13 +272,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
>         unsigned long flags;
>         int ret;
>
> -       /* If buffer queue is empty, return error */
>         spin_lock_irqsave(&common->irqlock, flags);
> -       if (list_empty(&common->dma_queue)) {
> -               spin_unlock_irqrestore(&common->irqlock, flags);
> -               vpif_dbg(1, debug, "buffer queue is empty\n");
> -               return -ENOBUFS;
> -       }
>
>         /* Get the next frame from the buffer queue */
>         common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
> @@ -1024,6 +1018,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
>         q->mem_ops = &vb2_dma_contig_memops;
>         q->buf_struct_size = sizeof(struct vpif_cap_buffer);
>         q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +       q->min_buffers_needed = 1;
>
>         ret = vb2_queue_init(q);
>         if (ret) {
> diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
> index 9d115cd..af660f5 100644
> --- a/drivers/media/platform/davinci/vpif_display.c
> +++ b/drivers/media/platform/davinci/vpif_display.c
> @@ -234,13 +234,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
>         unsigned long flags;
>         int ret;
>
> -       /* If buffer queue is empty, return error */
>         spin_lock_irqsave(&common->irqlock, flags);
> -       if (list_empty(&common->dma_queue)) {
> -               spin_unlock_irqrestore(&common->irqlock, flags);
> -               vpif_err("buffer queue is empty\n");
> -               return -ENOBUFS;
> -       }
>
>         /* Get the next frame from the buffer queue */
>         common->next_frm = common->cur_frm =
> @@ -984,6 +978,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
>         q->mem_ops = &vb2_dma_contig_memops;
>         q->buf_struct_size = sizeof(struct vpif_disp_buffer);
>         q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +       q->min_buffers_needed = 1;
>
>         ret = vb2_queue_init(q);
>         if (ret) {
> diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
> index c5059ba..a1ce55f 100644
> --- a/drivers/media/platform/s5p-tv/mixer_video.c
> +++ b/drivers/media/platform/s5p-tv/mixer_video.c
> @@ -946,11 +946,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
>
>         mxr_dbg(mdev, "%s\n", __func__);
>
> -       if (count == 0) {
> -               mxr_dbg(mdev, "no output buffers queued\n");
> -               return -ENOBUFS;
> -       }
> -
>         /* block any changes in output configuration */
>         mxr_output_get(mdev);
>
> @@ -1124,6 +1119,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
>                 .drv_priv = layer,
>                 .buf_struct_size = sizeof(struct mxr_buffer),
>                 .ops = &mxr_video_qops,
> +               .min_buffers_needed = 1,
>                 .mem_ops = &vb2_dma_contig_memops,
>         };
>
> diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
> index 6af76ee..1dd2b05 100644
> --- a/drivers/media/v4l2-core/videobuf2-core.c
> +++ b/drivers/media/v4l2-core/videobuf2-core.c
> @@ -1033,13 +1033,20 @@ EXPORT_SYMBOL_GPL(vb2_plane_cookie);
>   * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
>   * @vb:                vb2_buffer returned from the driver
>   * @state:     either VB2_BUF_STATE_DONE if the operation finished successfully
> - *             or VB2_BUF_STATE_ERROR if the operation finished with an error
> + *             or VB2_BUF_STATE_ERROR if the operation finished with an error.
> + *             If start_streaming fails then it should return buffers with state
> + *             VB2_BUF_STATE_QUEUED to put them back into the queue.
>   *
>   * This function should be called by the driver after a hardware operation on
>   * a buffer is finished and the buffer may be returned to userspace. The driver
>   * cannot use this buffer anymore until it is queued back to it by videobuf
>   * by the means of buf_queue callback. Only buffers previously queued to the
>   * driver by buf_queue can be passed to this function.
> + *
> + * While streaming a buffer can only be returned in state DONE or ERROR.
> + * The start_streaming op can also return them in case the DMA engine cannot
> + * be started for some reason. In that case the buffers should be returned with
> + * state QUEUED.
>   */
>  void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>  {
> @@ -1047,11 +1054,17 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>         unsigned long flags;
>         unsigned int plane;
>
> -       if (vb->state != VB2_BUF_STATE_ACTIVE)
> +       if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE))
>                 return;
>
> -       if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
> -               return;
> +       if (!q->start_streaming_called) {
> +               if (WARN_ON(state != VB2_BUF_STATE_QUEUED))
> +                       state = VB2_BUF_STATE_QUEUED;
> +       } else if (!WARN_ON(!q->start_streaming_called)) {
> +               if (WARN_ON(state != VB2_BUF_STATE_DONE &&
> +                           state != VB2_BUF_STATE_ERROR))
> +                       state = VB2_BUF_STATE_ERROR;
> +       }
>
>  #ifdef CONFIG_VIDEO_ADV_DEBUG
>         /*
> @@ -1070,12 +1083,14 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>         /* Add the buffer to the done buffers list */
>         spin_lock_irqsave(&q->done_lock, flags);
>         vb->state = state;
> -       list_add_tail(&vb->done_entry, &q->done_list);
> +       if (state != VB2_BUF_STATE_QUEUED)
> +               list_add_tail(&vb->done_entry, &q->done_list);
>         atomic_dec(&q->owned_by_drv_count);
>         spin_unlock_irqrestore(&q->done_lock, flags);
>
>         /* Inform any processes that may be waiting for buffers */
> -       wake_up(&q->done_wq);
> +       if (state != VB2_BUF_STATE_QUEUED)
> +               wake_up(&q->done_wq);

Perhaps:

       if (state == VB2_BUF_STATE_QUEUED)
              return;

       /* Inform any processes that may be waiting for buffers */
       wake_up(&q->done_wq);

>  }
>  EXPORT_SYMBOL_GPL(vb2_buffer_done);
>
> @@ -1544,34 +1559,49 @@ EXPORT_SYMBOL_GPL(vb2_prepare_buf);
>   * vb2_start_streaming() - Attempt to start streaming.
>   * @q:         videobuf2 queue
>   *
> - * If there are not enough buffers, then retry_start_streaming is set to
> - * 1 and 0 is returned. The next time a buffer is queued and
> - * retry_start_streaming is 1, this function will be called again to
> - * retry starting the DMA engine.
> + * Attempt to start streaming. When this function is called there must be
> + * at least q->min_buffers_needed buffers queued up (i.e. the minimum
> + * number of buffers required for the DMA engine to function). If the
> + * @start_streaming op fails it is supposed to return all the driver-owned
> + * buffers back to vb2 in state QUEUED. Check if that happened and if
> + * not warn and reclaim them forcefully.
>   */
>  static int vb2_start_streaming(struct vb2_queue *q)
>  {
> +       struct vb2_buffer *vb;
>         int ret;
>
> -       /* Tell the driver to start streaming */
> -       ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count));
> -       if (ret)
> -               fail_qop(q, start_streaming);
> -
>         /*
> -        * If there are not enough buffers queued to start streaming, then
> -        * the start_streaming operation will return -ENOBUFS and you have to
> -        * retry when the next buffer is queued.
> +        * If any buffers were queued before streamon,
> +        * we can now pass them to driver for processing.
>          */
> -       if (ret == -ENOBUFS) {
> -               dprintk(1, "qbuf: not enough buffers, retry when more buffers are queued.\n");
> -               q->retry_start_streaming = 1;
> +       list_for_each_entry(vb, &q->queued_list, queued_entry)
> +               __enqueue_in_driver(vb);
> +
> +       /* Tell the driver to start streaming */
> +       ret = call_qop(q, start_streaming, q,
> +                      atomic_read(&q->owned_by_drv_count));
> +       q->start_streaming_called = ret == 0;
> +       if (!ret)
>                 return 0;
> +
> +       fail_qop(q, start_streaming);
> +       dprintk(1, "qbuf: driver refused to start streaming\n");
> +       if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
> +               unsigned i;
> +
> +               /*
> +                * Forcefully reclaim buffers if the driver did not
> +                * correctly return them to vb2.
> +                */
> +               for (i = 0; i < q->num_buffers; ++i) {
> +                       vb = q->bufs[i];
> +                       if (vb->state == VB2_BUF_STATE_ACTIVE)
> +                               vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
> +               }
> +               /* Must be zero now */
> +               WARN_ON(atomic_read(&q->owned_by_drv_count));
>         }
> -       if (ret)
> -               dprintk(1, "qbuf: driver refused to start streaming\n");
> -       else
> -               q->retry_start_streaming = 0;
>         return ret;
>  }
>
> @@ -1611,19 +1641,26 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
>          * dequeued in dqbuf.
>          */
>         list_add_tail(&vb->queued_entry, &q->queued_list);
> +       q->queued_count++;
>         vb->state = VB2_BUF_STATE_QUEUED;
>
>         /*
>          * If already streaming, give the buffer to driver for processing.
>          * If not, the buffer will be given to driver on next streamon.
>          */
> -       if (q->streaming)
> +       if (q->start_streaming_called)
>                 __enqueue_in_driver(vb);
>
>         /* Fill buffer information for the userspace */
>         __fill_v4l2_buffer(vb, b);
>
> -       if (q->retry_start_streaming) {
> +       /*
> +        * If we haven't yet started streaming and if we have the

The comment about "starting streaming" is confusing. Please say: "if
streamon was called, but we couldn't call start_streaming due to
insufficient number of buffers queued, do so now", or something
similar.

> +        * minimum number of required buffers available, then start
> +        * streaming.
> +        */
> +       if (q->streaming && !q->start_streaming_called &&
> +           q->queued_count >= q->min_buffers_needed) {
>                 ret = vb2_start_streaming(q);
>                 if (ret)
>                         return ret;
> @@ -1778,7 +1815,7 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q)
>                 return -EINVAL;
>         }
>
> -       if (!q->retry_start_streaming)
> +       if (q->start_streaming_called)
>                 wait_event(q->done_wq, !atomic_read(&q->owned_by_drv_count));
>         return 0;
>  }
> @@ -1839,6 +1876,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
>         __fill_v4l2_buffer(vb, b);
>         /* Remove from videobuf queue */
>         list_del(&vb->queued_entry);
> +       q->queued_count--;
>         /* go back to dequeued state */
>         __vb2_dqbuf(vb);
>
> @@ -1889,18 +1927,23 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
>  {
>         unsigned int i;
>
> -       if (q->retry_start_streaming) {
> -               q->retry_start_streaming = 0;
> -               q->streaming = 0;
> -       }
> -
>         /*
>          * Tell driver to stop all transactions and release all queued
>          * buffers.
>          */
> -       if (q->streaming)
> +       if (q->start_streaming_called)
>                 call_qop(q, stop_streaming, q);
>         q->streaming = 0;
> +       q->start_streaming_called = 0;
> +       q->queued_count = 0;
> +
> +       if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
> +               for (i = 0; i < q->num_buffers; ++i)
> +                       if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE)
> +                               vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR);
> +               /* Must be zero now */
> +               WARN_ON(atomic_read(&q->owned_by_drv_count));
> +       }
>
>         /*
>          * Remove all buffers from videobuf's list...
> @@ -1923,7 +1966,6 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
>
>  static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
>  {
> -       struct vb2_buffer *vb;
>         int ret;
>
>         if (type != q->type) {
> @@ -1937,17 +1979,15 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
>         }
>
>         /*
> -        * If any buffers were queued before streamon,
> -        * we can now pass them to driver for processing.
> +        * Tell driver to start streaming provided sufficient buffers
> +        * are available.
>          */
> -       list_for_each_entry(vb, &q->queued_list, queued_entry)
> -               __enqueue_in_driver(vb);
> -
> -       /* Tell driver to start streaming. */
> -       ret = vb2_start_streaming(q);
> -       if (ret) {
> -               __vb2_queue_cancel(q);
> -               return ret;
> +       if (q->queued_count >= q->min_buffers_needed) {
> +               ret = vb2_start_streaming(q);
> +               if (ret) {
> +                       __vb2_queue_cancel(q);
> +                       return ret;
> +               }
>         }
>
>         q->streaming = 1;
> diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
> index 1f3b0f9..8c101cb 100644
> --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
> +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
> @@ -1201,8 +1201,6 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
>         unsigned long addr;
>         int ret;
>
> -       if (count == 0)
> -               return -ENOBUFS;
>         ret = mutex_lock_interruptible(&video->lock);
>         if (ret)
>                 goto streamoff;
> @@ -1327,6 +1325,7 @@ static int vpfe_reqbufs(struct file *file, void *priv,
>         q->type = req_buf->type;
>         q->io_modes = VB2_MMAP | VB2_USERPTR;
>         q->drv_priv = fh;
> +       q->min_buffers_needed = 1;
>         q->ops = &video_qops;
>         q->mem_ops = &vb2_dma_contig_memops;
>         q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index adaffed..2ada71b 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -348,20 +348,24 @@ struct v4l2_fh;
>   * @gfp_flags: additional gfp flags used when allocating the buffers.
>   *             Typically this is 0, but it may be e.g. GFP_DMA or __GFP_DMA32
>   *             to force the buffer allocation to a specific memory zone.
> + * @min_buffers_needed: the minimum number of buffers needed before
> + *             start_streaming() can be called. Used when a DMA engine
> + *             cannot be started unless at least this number of buffers
> + *             have been queued into the driver.
>   *
>   * @memory:    current memory type used
>   * @bufs:      videobuf buffer structures
>   * @num_buffers: number of allocated/used buffers
>   * @queued_list: list of buffers currently queued from userspace
> + * @queued_count: number of buffers queued and ready for streaming.
>   * @owned_by_drv_count: number of buffers owned by the driver
>   * @done_list: list of buffers ready to be dequeued to userspace
>   * @done_lock: lock to protect done_list list
>   * @done_wq:   waitqueue for processes waiting for buffers ready to be dequeued
>   * @alloc_ctx: memory type/allocator-specific contexts for each plane
>   * @streaming: current streaming state
> - * @retry_start_streaming: start_streaming() was called, but there were not enough
> - *             buffers queued. If set, then retry calling start_streaming when
> - *             queuing a new buffer.
> + * @start_streaming_called: start_streaming() was called successfully and we
> + *             started streaming.
>   * @fileio:    file io emulator internal data, used only if emulator is active
>   */
>  struct vb2_queue {
> @@ -377,6 +381,7 @@ struct vb2_queue {
>         unsigned int                    buf_struct_size;
>         u32                             timestamp_type;
>         gfp_t                           gfp_flags;
> +       u32                             min_buffers_needed;

Perhaps add a WARN_ON or even clamp this if queue_setup requests less
buffers than this?

>
>  /* private: internal use only */
>         enum v4l2_memory                memory;
> @@ -384,6 +389,7 @@ struct vb2_queue {
>         unsigned int                    num_buffers;
>
>         struct list_head                queued_list;
> +       unsigned int                    queued_count;
>
>         atomic_t                        owned_by_drv_count;
>         struct list_head                done_list;
> @@ -394,7 +400,7 @@ struct vb2_queue {
>         unsigned int                    plane_sizes[VIDEO_MAX_PLANES];
>
>         unsigned int                    streaming:1;
> -       unsigned int                    retry_start_streaming:1;
> +       unsigned int                    start_streaming_called:1;
>
>         struct vb2_fileio_data          *fileio;
>
> --
> 1.8.4.rc3
>



-- 
Best regards,
Pawel Osciak

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 05/10] vb2: fix buf_init/buf_cleanup call sequences
  2014-02-14  4:40   ` Pawel Osciak
@ 2014-02-14 10:28     ` Hans Verkuil
  0 siblings, 0 replies; 21+ messages in thread
From: Hans Verkuil @ 2014-02-14 10:28 UTC (permalink / raw)
  To: Pawel Osciak; +Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil

Hi Pawel,

Thanks for the review!

On 02/14/2014 05:40 AM, Pawel Osciak wrote:
> On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Ensure that these ops are properly balanced.
>>
>> There two scenarios:
> 
> s/There two/There are two/
> 
>>
>> 1) for MMAP buf_init is called when the buffers are created and buf_cleanup
>>    must be called when the queue is finally freed. This scenario was always
>>    working.
>>
>> 2) for USERPTR and DMABUF it is more complicated. When a buffer is queued
>>    the code checks if all planes of this buffer have been acquired before.
>>    If that's the case, then only buf_prepare has to be called. Otherwise
>>    buf_clean needs to be called if the buffer was acquired before, then,
> 
> s/buf_clean/buf_cleanup/
> 
>>    once all changed planes have been (re)acquired, buf_init has to be
>>    called again followed by buf_prepare. Should buf_prepare fail, then
> 
> s/again//
> 
> I know what you mean, but there is only one call to buf_init in this
> particular sequence.
> 
>>    buf_cleanup must be called again because all planes will be released
> 
> s/again because all planes will be released/on the newly acquired
> planes to release them/
> 
>>    in case of an error.
>>
>> Finally, in __vb2_queue_free we have to check if the buffer was actually
>> acquired before calling buf_cleanup. While that it always true for MMAP
>> mode, it is not necessarily true for the other modes. E.g. if you just
>> call REQBUFS and close the filehandle, then buffers were ever queued and
> 
> s/ever/never/
> s/filehandle/file handle/

Thanks for spell-checking my commit log! That's slightly embarrassing...

> 
>> so no buf_init was ever called.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/v4l2-core/videobuf2-core.c | 100 +++++++++++++++++++++----------
>>  1 file changed, 67 insertions(+), 33 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
>> index 3756378..7766bf5 100644
>> --- a/drivers/media/v4l2-core/videobuf2-core.c
>> +++ b/drivers/media/v4l2-core/videobuf2-core.c
>> @@ -373,8 +373,10 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
>>         /* Call driver-provided cleanup function for each buffer, if provided */
>>         for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
>>              ++buffer) {
>> -               if (q->bufs[buffer])
>> -                       call_vb_qop(q->bufs[buffer], buf_cleanup, q->bufs[buffer]);
>> +               struct vb2_buffer *vb = q->bufs[buffer];
>> +
>> +               if (vb && vb->planes[0].mem_priv)
>> +                       call_vb_qop(vb, buf_cleanup, vb);
>>         }
>>
>>         /* Release video buffer memory */
>> @@ -1161,6 +1163,7 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>>         unsigned int plane;
>>         int ret;
>>         int write = !V4L2_TYPE_IS_OUTPUT(q->type);
>> +       bool reacquired = vb->planes[0].mem_priv == NULL;
> 
> This requires a comment I feel. In general, I'm not especially happy
> with the fact that we are making mem_priv != NULL equivalent to
> buf_init() called and succeeded. It is true right now, but it'll be
> very hard to make the association without previously seeing this very
> patch I feel.
> 
> I don't see a perfect way to do this, but I'm strongly leaning towards
> having a bool inited in the vb2_buffer. Really.

I agree that it isn't all that great how it is done today. However, I would like
to postpone changing this. Mostly because I do not feel all that confident yet
changing it :-)

I like to do some more testing first, especially with dma-buf.

> 
>>
>>         /* Copy relevant information provided by the userspace */
>>         __fill_vb2_buffer(vb, b, planes);
>> @@ -1186,12 +1189,16 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>>                 }
>>
>>                 /* Release previously acquired memory if present */
>> -               if (vb->planes[plane].mem_priv)
>> +               if (vb->planes[plane].mem_priv) {
>> +                       if (!reacquired) {
>> +                               reacquired = true;
>> +                               call_vb_qop(vb, buf_cleanup, vb);
>> +                       }
>>                         call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
>> +               }
>>
>>                 vb->planes[plane].mem_priv = NULL;
>> -               vb->v4l2_planes[plane].m.userptr = 0;
>> -               vb->v4l2_planes[plane].length = 0;
>> +               memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
>>
>>                 /* Acquire each plane's memory */
>>                 mem_priv = call_memop(vb, get_userptr, q->alloc_ctx[plane],
>> @@ -1208,23 +1215,34 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>>         }
>>
>>         /*
>> -        * Call driver-specific initialization on the newly acquired buffer,
>> -        * if provided.
>> -        */
>> -       ret = call_vb_qop(vb, buf_init, vb);
>> -       if (ret) {
>> -               dprintk(1, "qbuf: buffer initialization failed\n");
>> -               fail_vb_qop(vb, buf_init);
>> -               goto err;
>> -       }
>> -
>> -       /*
>>          * Now that everything is in order, copy relevant information
>>          * provided by userspace.
>>          */
>>         for (plane = 0; plane < vb->num_planes; ++plane)
>>                 vb->v4l2_planes[plane] = planes[plane];
>>
>> +       if (reacquired) {l
>> +               /*
>> +                * One or more planes changed, so we must call buf_init to do
>> +                * the driver-specific initialization on the newly acquired
>> +                * buffer, if provided.
>> +                */
>> +               ret = call_vb_qop(vb, buf_init, vb);
>> +               if (ret) {
>> +                       dprintk(1, "qbuf: buffer initialization failed\n");
>> +                       fail_vb_qop(vb, buf_init);
>> +                       goto err;
>> +               }
>> +       }
>> +
>> +       ret = call_vb_qop(vb, buf_prepare, vb);
>> +       if (ret) {
>> +               dprintk(1, "qbuf: buffer preparation failed\n");
>> +               fail_vb_qop(vb, buf_prepare);
>> +               call_vb_qop(vb, buf_cleanup, vb);
>> +               goto err;
>> +       }
>> +
>>         return 0;
>>  err:
>>         /* In case of errors, release planes that were already acquired */
>> @@ -1244,8 +1262,13 @@ err:
>>   */
>>  static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>>  {
>> +       int ret;
>> +
>>         __fill_vb2_buffer(vb, b, vb->v4l2_planes);
>> -       return 0;
>> +       ret = call_vb_qop(vb, buf_prepare, vb);
>> +       if (ret)
>> +               fail_vb_qop(vb, buf_prepare);
>> +       return ret;
>>  }
>>
>>  /**
>> @@ -1259,6 +1282,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>>         unsigned int plane;
>>         int ret;
>>         int write = !V4L2_TYPE_IS_OUTPUT(q->type);
>> +       bool reacquired = vb->planes[0].mem_priv == NULL;
>>
>>         /* Copy relevant information provided by the userspace */
>>         __fill_vb2_buffer(vb, b, planes);
>> @@ -1294,6 +1318,11 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>>
>>                 dprintk(1, "qbuf: buffer for plane %d changed\n", plane);
>>
>> +               if (!reacquired) {
>> +                       reacquired = true;
>> +                       call_vb_qop(vb, buf_cleanup, vb);
>> +               }
>> +
>>                 /* Release previously acquired memory if present */
>>                 __vb2_plane_dmabuf_put(vb, &vb->planes[plane]);
>>                 memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
>> @@ -1329,23 +1358,33 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>>         }
>>
>>         /*
>> -        * Call driver-specific initialization on the newly acquired buffer,
>> -        * if provided.
>> -        */
>> -       ret = call_vb_qop(vb, buf_init, vb);
>> -       if (ret) {
>> -               dprintk(1, "qbuf: buffer initialization failed\n");
>> -               fail_vb_qop(vb, buf_init);
>> -               goto err;
>> -       }
>> -
>> -       /*
>>          * Now that everything is in order, copy relevant information
>>          * provided by userspace.
>>          */
>>         for (plane = 0; plane < vb->num_planes; ++plane)
>>                 vb->v4l2_planes[plane] = planes[plane];
>>
>> +       if (reacquired) {
> 
> This sequence until 'return 0" is an exact same code as in
> __qbuf_userptr. Could we extract this?
> I'm thinking that we could do this:
> 
> - rename __qbuf_userptr and __qbuf_dmabuf to __acquire_userptr/dmabuf
> and have them return whether reacquired
> - extract the sequence above and call buf_prepare from __buf_prepare
> for userptr and dmabuf after calling __acquire_*
> - get rid of qbuf_dmabuf, instead just call buf_prepare from
> __buf_prepare directly like for useptr and dmabuf

I agree with this as well, but again I'd like to postpone that until I feel
confident I actually know what I'm doing :-)

> 
>> +               /*
>> +                * Call driver-specific initialization on the newly acquired buffer,
>> +                * if provided.
>> +                */
>> +               ret = call_vb_qop(vb, buf_init, vb);
>> +               if (ret) {
>> +                       dprintk(1, "qbuf: buffer initialization failed\n");
>> +                       fail_vb_qop(vb, buf_init);
>> +                       goto err;
>> +               }
>> +       }
>> +
>> +       ret = call_vb_qop(vb, buf_prepare, vb);
>> +       if (ret) {
>> +               dprintk(1, "qbuf: buffer preparation failed\n");
>> +               fail_vb_qop(vb, buf_prepare);
>> +               call_vb_qop(vb, buf_cleanup, vb);
> 
> Should we explicitly set mem_priv to NULL here? This is one the issues
> I have with using mem_priv, it's hard to reason if it's ok to do
> buf_cleanup and expect mem_priv to be NULL here always.

It is: __vb2_buf_dmabuf_put calls __vb2_plane_dmabuf_put which memsets the whole
vb2_plane struct which includes mem_priv.

> Could we please add that inited bool into vb2_buffer to make it simpler?

I agree, but I want to postpone that for a later patch.

Regards,

	Hans

> 
>> +               goto err;
>> +       }
>> +
>>         return 0;
>>  err:
>>         /* In case of errors, release planes that were already acquired */
>> @@ -1420,11 +1459,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>>                 ret = -EINVAL;
>>         }
>>
>> -       if (!ret) {
>> -               ret = call_vb_qop(vb, buf_prepare, vb);
>> -               if (ret)
>> -                       fail_vb_qop(vb, buf_prepare);
>> -       }
>>         if (ret)
>>                 dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
>>         vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;
>> --
>> 1.8.4.rc3
>>
> 
> 
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 06/10] vb2: fix read/write regression
  2014-02-14  4:49   ` Pawel Osciak
@ 2014-02-14 10:29     ` Hans Verkuil
  0 siblings, 0 replies; 21+ messages in thread
From: Hans Verkuil @ 2014-02-14 10:29 UTC (permalink / raw)
  To: Pawel Osciak
  Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil, Andy Walls

On 02/14/2014 05:49 AM, Pawel Osciak wrote:
> On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Commit 88e268702bfba78448abd20a31129458707383aa ("vb2: Improve file I/O
>> emulation to handle buffers in any order") broke read/write support if
>> the size of the buffer being read/written is less than the size of the
>> image.
>>
>> When the commit was tested originally I used qv4l2, which call read()
>> with exactly the size of the image. But if you try 'cat /dev/video0'
>> then it will fail and typically hang after reading two buffers.
>>
>> This patch fixes the behavior by adding a new buf_index field that
>> contains the index of the field currently being filled/read, or it
>> is num_buffers in which case a new buffer needs to be dequeued.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> Cc: Andy Walls <awalls@md.metrocast.net>
>> ---
>>  drivers/media/v4l2-core/videobuf2-core.c | 7 +++++--
>>  1 file changed, 5 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
>> index 7766bf5..a3b4b4c 100644
>> --- a/drivers/media/v4l2-core/videobuf2-core.c
>> +++ b/drivers/media/v4l2-core/videobuf2-core.c
>> @@ -2418,6 +2418,7 @@ struct vb2_fileio_data {
>>         struct v4l2_requestbuffers req;
>>         struct v4l2_buffer b;
>>         struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
>> +       unsigned int buf_index;
>>         unsigned int index;
> 
> The two index fields are now confusing, especially because there is no
> documentation. Perhaps we could call index "cur_index" and also add
> documentation please?

I agree, I'll post an updated version today.

Regards,

	Hans

> 
>>         unsigned int q_count;
>>         unsigned int dq_count;
>> @@ -2519,6 +2520,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
>>                         fileio->bufs[i].queued = 1;
>>                 }
>>                 fileio->index = q->num_buffers;
>> +               fileio->buf_index = q->num_buffers;
>>         }
>>
>>         /*
>> @@ -2597,7 +2599,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
>>         /*
>>          * Check if we need to dequeue the buffer.
>>          */
>> -       index = fileio->index;
>> +       index = fileio->buf_index;
>>         if (index >= q->num_buffers) {
>>                 /*
>>                  * Call vb2_dqbuf to get buffer back.
>> @@ -2611,7 +2613,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
>>                         return ret;
>>                 fileio->dq_count += 1;
>>
>> -               index = fileio->b.index;
>> +               fileio->buf_index = index = fileio->b.index;
>>                 buf = &fileio->bufs[index];
>>
>>                 /*
>> @@ -2689,6 +2691,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
>>                 fileio->q_count += 1;
>>                 if (fileio->index < q->num_buffers)
>>                         fileio->index++;
>> +               fileio->buf_index = fileio->index;
>>         }
>>
>>         /*
>> --
>> 1.8.4.rc3
>>
> 
> 
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFCv3 PATCH 08/10] vb2: only call start_streaming if sufficient buffers are queued
  2014-02-14  5:13   ` Pawel Osciak
@ 2014-02-14 10:31     ` Hans Verkuil
  0 siblings, 0 replies; 21+ messages in thread
From: Hans Verkuil @ 2014-02-14 10:31 UTC (permalink / raw)
  To: Pawel Osciak; +Cc: LMML, Sylwester Nawrocki, Marek Szyprowski, Hans Verkuil

On 02/14/2014 06:13 AM, Pawel Osciak wrote:
> On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> In commit 02f142ecd24aaf891324ffba8527284c1731b561 support was added to
>> start_streaming to return -ENOBUFS if insufficient buffers were queued
>> for the DMA engine to start. The vb2 core would attempt calling
>> start_streaming again if another buffer would be queued up.
>>
>> Later analysis uncovered problems with the queue management if start_streaming
>> would return an error: the buffers are enqueued to the driver before the
>> start_streaming op is called, so after an error they are never returned to
>> the vb2 core. The solution for this is to let the driver return them to
>> the vb2 core in case of an error while starting the DMA engine. However,
>> in the case of -ENOBUFS that would be weird: it is not a real error, it
>> just says that more buffers are needed. Requiring start_streaming to give
>> them back only to have them requeued again the next time the application
>> calls QBUF is inefficient.
>>
>> This patch changes this mechanism: it adds a 'min_buffers_needed' field
>> to vb2_queue that drivers can set with the minimum number of buffers
>> required to start the DMA engine. The start_streaming op is only called
>> if enough buffers are queued. The -ENOBUFS handling has been dropped in
>> favor of this new method.
>>
>> Drivers are expected to return buffers back to vb2 core with state QUEUED
>> if start_streaming would return an error. The vb2 core checks for this
>> and produces a warning if that didn't happen and it will forcefully
>> reclaim such buffers to ensure that the internal vb2 core state remains
>> consistent and all buffer-related resources have been correctly freed
>> and all op callss have been balanced.
> 
> s/callss/calls/
> 
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/platform/davinci/vpbe_display.c   |   6 +-
>>  drivers/media/platform/davinci/vpif_capture.c   |   7 +-
>>  drivers/media/platform/davinci/vpif_display.c   |   7 +-
>>  drivers/media/platform/s5p-tv/mixer_video.c     |   6 +-
>>  drivers/media/v4l2-core/videobuf2-core.c        | 130 ++++++++++++++++--------
>>  drivers/staging/media/davinci_vpfe/vpfe_video.c |   3 +-
>>  include/media/videobuf2-core.h                  |  14 ++-
>>  7 files changed, 100 insertions(+), 73 deletions(-)
>>
>> diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
>> index b02aba4..9231e48 100644
>> --- a/drivers/media/platform/davinci/vpbe_display.c
>> +++ b/drivers/media/platform/davinci/vpbe_display.c
>> @@ -344,11 +344,6 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
>>         struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
>>         int ret;
>>
>> -       /* If buffer queue is empty, return error */
>> -       if (list_empty(&layer->dma_queue)) {
>> -               v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
>> -               return -ENOBUFS;
>> -       }
>>         /* Get the next frame from the buffer queue */
>>         layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
>>                                 struct vpbe_disp_buffer, list);
>> @@ -1416,6 +1411,7 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,
>>         q->mem_ops = &vb2_dma_contig_memops;
>>         q->buf_struct_size = sizeof(struct vpbe_disp_buffer);
>>         q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
>> +       q->min_buffers_needed = 1;
>>
>>         ret = vb2_queue_init(q);
>>         if (ret) {
>> diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
>> index 735ec47..9731ad4 100644
>> --- a/drivers/media/platform/davinci/vpif_capture.c
>> +++ b/drivers/media/platform/davinci/vpif_capture.c
>> @@ -272,13 +272,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
>>         unsigned long flags;
>>         int ret;
>>
>> -       /* If buffer queue is empty, return error */
>>         spin_lock_irqsave(&common->irqlock, flags);
>> -       if (list_empty(&common->dma_queue)) {
>> -               spin_unlock_irqrestore(&common->irqlock, flags);
>> -               vpif_dbg(1, debug, "buffer queue is empty\n");
>> -               return -ENOBUFS;
>> -       }
>>
>>         /* Get the next frame from the buffer queue */
>>         common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
>> @@ -1024,6 +1018,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
>>         q->mem_ops = &vb2_dma_contig_memops;
>>         q->buf_struct_size = sizeof(struct vpif_cap_buffer);
>>         q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
>> +       q->min_buffers_needed = 1;
>>
>>         ret = vb2_queue_init(q);
>>         if (ret) {
>> diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
>> index 9d115cd..af660f5 100644
>> --- a/drivers/media/platform/davinci/vpif_display.c
>> +++ b/drivers/media/platform/davinci/vpif_display.c
>> @@ -234,13 +234,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
>>         unsigned long flags;
>>         int ret;
>>
>> -       /* If buffer queue is empty, return error */
>>         spin_lock_irqsave(&common->irqlock, flags);
>> -       if (list_empty(&common->dma_queue)) {
>> -               spin_unlock_irqrestore(&common->irqlock, flags);
>> -               vpif_err("buffer queue is empty\n");
>> -               return -ENOBUFS;
>> -       }
>>
>>         /* Get the next frame from the buffer queue */
>>         common->next_frm = common->cur_frm =
>> @@ -984,6 +978,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
>>         q->mem_ops = &vb2_dma_contig_memops;
>>         q->buf_struct_size = sizeof(struct vpif_disp_buffer);
>>         q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
>> +       q->min_buffers_needed = 1;
>>
>>         ret = vb2_queue_init(q);
>>         if (ret) {
>> diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
>> index c5059ba..a1ce55f 100644
>> --- a/drivers/media/platform/s5p-tv/mixer_video.c
>> +++ b/drivers/media/platform/s5p-tv/mixer_video.c
>> @@ -946,11 +946,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
>>
>>         mxr_dbg(mdev, "%s\n", __func__);
>>
>> -       if (count == 0) {
>> -               mxr_dbg(mdev, "no output buffers queued\n");
>> -               return -ENOBUFS;
>> -       }
>> -
>>         /* block any changes in output configuration */
>>         mxr_output_get(mdev);
>>
>> @@ -1124,6 +1119,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
>>                 .drv_priv = layer,
>>                 .buf_struct_size = sizeof(struct mxr_buffer),
>>                 .ops = &mxr_video_qops,
>> +               .min_buffers_needed = 1,
>>                 .mem_ops = &vb2_dma_contig_memops,
>>         };
>>
>> diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
>> index 6af76ee..1dd2b05 100644
>> --- a/drivers/media/v4l2-core/videobuf2-core.c
>> +++ b/drivers/media/v4l2-core/videobuf2-core.c
>> @@ -1033,13 +1033,20 @@ EXPORT_SYMBOL_GPL(vb2_plane_cookie);
>>   * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
>>   * @vb:                vb2_buffer returned from the driver
>>   * @state:     either VB2_BUF_STATE_DONE if the operation finished successfully
>> - *             or VB2_BUF_STATE_ERROR if the operation finished with an error
>> + *             or VB2_BUF_STATE_ERROR if the operation finished with an error.
>> + *             If start_streaming fails then it should return buffers with state
>> + *             VB2_BUF_STATE_QUEUED to put them back into the queue.
>>   *
>>   * This function should be called by the driver after a hardware operation on
>>   * a buffer is finished and the buffer may be returned to userspace. The driver
>>   * cannot use this buffer anymore until it is queued back to it by videobuf
>>   * by the means of buf_queue callback. Only buffers previously queued to the
>>   * driver by buf_queue can be passed to this function.
>> + *
>> + * While streaming a buffer can only be returned in state DONE or ERROR.
>> + * The start_streaming op can also return them in case the DMA engine cannot
>> + * be started for some reason. In that case the buffers should be returned with
>> + * state QUEUED.
>>   */
>>  void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>>  {
>> @@ -1047,11 +1054,17 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>>         unsigned long flags;
>>         unsigned int plane;
>>
>> -       if (vb->state != VB2_BUF_STATE_ACTIVE)
>> +       if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE))
>>                 return;
>>
>> -       if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
>> -               return;
>> +       if (!q->start_streaming_called) {
>> +               if (WARN_ON(state != VB2_BUF_STATE_QUEUED))
>> +                       state = VB2_BUF_STATE_QUEUED;
>> +       } else if (!WARN_ON(!q->start_streaming_called)) {
>> +               if (WARN_ON(state != VB2_BUF_STATE_DONE &&
>> +                           state != VB2_BUF_STATE_ERROR))
>> +                       state = VB2_BUF_STATE_ERROR;
>> +       }
>>
>>  #ifdef CONFIG_VIDEO_ADV_DEBUG
>>         /*
>> @@ -1070,12 +1083,14 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>>         /* Add the buffer to the done buffers list */
>>         spin_lock_irqsave(&q->done_lock, flags);
>>         vb->state = state;
>> -       list_add_tail(&vb->done_entry, &q->done_list);
>> +       if (state != VB2_BUF_STATE_QUEUED)
>> +               list_add_tail(&vb->done_entry, &q->done_list);
>>         atomic_dec(&q->owned_by_drv_count);
>>         spin_unlock_irqrestore(&q->done_lock, flags);
>>
>>         /* Inform any processes that may be waiting for buffers */
>> -       wake_up(&q->done_wq);
>> +       if (state != VB2_BUF_STATE_QUEUED)
>> +               wake_up(&q->done_wq);
> 
> Perhaps:
> 
>        if (state == VB2_BUF_STATE_QUEUED)
>               return;
> 
>        /* Inform any processes that may be waiting for buffers */
>        wake_up(&q->done_wq);

Done.

> 
>>  }
>>  EXPORT_SYMBOL_GPL(vb2_buffer_done);
>>
>> @@ -1544,34 +1559,49 @@ EXPORT_SYMBOL_GPL(vb2_prepare_buf);
>>   * vb2_start_streaming() - Attempt to start streaming.
>>   * @q:         videobuf2 queue
>>   *
>> - * If there are not enough buffers, then retry_start_streaming is set to
>> - * 1 and 0 is returned. The next time a buffer is queued and
>> - * retry_start_streaming is 1, this function will be called again to
>> - * retry starting the DMA engine.
>> + * Attempt to start streaming. When this function is called there must be
>> + * at least q->min_buffers_needed buffers queued up (i.e. the minimum
>> + * number of buffers required for the DMA engine to function). If the
>> + * @start_streaming op fails it is supposed to return all the driver-owned
>> + * buffers back to vb2 in state QUEUED. Check if that happened and if
>> + * not warn and reclaim them forcefully.
>>   */
>>  static int vb2_start_streaming(struct vb2_queue *q)
>>  {
>> +       struct vb2_buffer *vb;
>>         int ret;
>>
>> -       /* Tell the driver to start streaming */
>> -       ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count));
>> -       if (ret)
>> -               fail_qop(q, start_streaming);
>> -
>>         /*
>> -        * If there are not enough buffers queued to start streaming, then
>> -        * the start_streaming operation will return -ENOBUFS and you have to
>> -        * retry when the next buffer is queued.
>> +        * If any buffers were queued before streamon,
>> +        * we can now pass them to driver for processing.
>>          */
>> -       if (ret == -ENOBUFS) {
>> -               dprintk(1, "qbuf: not enough buffers, retry when more buffers are queued.\n");
>> -               q->retry_start_streaming = 1;
>> +       list_for_each_entry(vb, &q->queued_list, queued_entry)
>> +               __enqueue_in_driver(vb);
>> +
>> +       /* Tell the driver to start streaming */
>> +       ret = call_qop(q, start_streaming, q,
>> +                      atomic_read(&q->owned_by_drv_count));
>> +       q->start_streaming_called = ret == 0;
>> +       if (!ret)
>>                 return 0;
>> +
>> +       fail_qop(q, start_streaming);
>> +       dprintk(1, "qbuf: driver refused to start streaming\n");
>> +       if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
>> +               unsigned i;
>> +
>> +               /*
>> +                * Forcefully reclaim buffers if the driver did not
>> +                * correctly return them to vb2.
>> +                */
>> +               for (i = 0; i < q->num_buffers; ++i) {
>> +                       vb = q->bufs[i];
>> +                       if (vb->state == VB2_BUF_STATE_ACTIVE)
>> +                               vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
>> +               }
>> +               /* Must be zero now */
>> +               WARN_ON(atomic_read(&q->owned_by_drv_count));
>>         }
>> -       if (ret)
>> -               dprintk(1, "qbuf: driver refused to start streaming\n");
>> -       else
>> -               q->retry_start_streaming = 0;
>>         return ret;
>>  }
>>
>> @@ -1611,19 +1641,26 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
>>          * dequeued in dqbuf.
>>          */
>>         list_add_tail(&vb->queued_entry, &q->queued_list);
>> +       q->queued_count++;
>>         vb->state = VB2_BUF_STATE_QUEUED;
>>
>>         /*
>>          * If already streaming, give the buffer to driver for processing.
>>          * If not, the buffer will be given to driver on next streamon.
>>          */
>> -       if (q->streaming)
>> +       if (q->start_streaming_called)
>>                 __enqueue_in_driver(vb);
>>
>>         /* Fill buffer information for the userspace */
>>         __fill_v4l2_buffer(vb, b);
>>
>> -       if (q->retry_start_streaming) {
>> +       /*
>> +        * If we haven't yet started streaming and if we have the
> 
> The comment about "starting streaming" is confusing. Please say: "if
> streamon was called, but we couldn't call start_streaming due to
> insufficient number of buffers queued, do so now", or something
> similar.

I've improved this.

> 
>> +        * minimum number of required buffers available, then start
>> +        * streaming.
>> +        */
>> +       if (q->streaming && !q->start_streaming_called &&
>> +           q->queued_count >= q->min_buffers_needed) {
>>                 ret = vb2_start_streaming(q);
>>                 if (ret)
>>                         return ret;
>> @@ -1778,7 +1815,7 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q)
>>                 return -EINVAL;
>>         }
>>
>> -       if (!q->retry_start_streaming)
>> +       if (q->start_streaming_called)
>>                 wait_event(q->done_wq, !atomic_read(&q->owned_by_drv_count));
>>         return 0;
>>  }
>> @@ -1839,6 +1876,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
>>         __fill_v4l2_buffer(vb, b);
>>         /* Remove from videobuf queue */
>>         list_del(&vb->queued_entry);
>> +       q->queued_count--;
>>         /* go back to dequeued state */
>>         __vb2_dqbuf(vb);
>>
>> @@ -1889,18 +1927,23 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
>>  {
>>         unsigned int i;
>>
>> -       if (q->retry_start_streaming) {
>> -               q->retry_start_streaming = 0;
>> -               q->streaming = 0;
>> -       }
>> -
>>         /*
>>          * Tell driver to stop all transactions and release all queued
>>          * buffers.
>>          */
>> -       if (q->streaming)
>> +       if (q->start_streaming_called)
>>                 call_qop(q, stop_streaming, q);
>>         q->streaming = 0;
>> +       q->start_streaming_called = 0;
>> +       q->queued_count = 0;
>> +
>> +       if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
>> +               for (i = 0; i < q->num_buffers; ++i)
>> +                       if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE)
>> +                               vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR);
>> +               /* Must be zero now */
>> +               WARN_ON(atomic_read(&q->owned_by_drv_count));
>> +       }
>>
>>         /*
>>          * Remove all buffers from videobuf's list...
>> @@ -1923,7 +1966,6 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
>>
>>  static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
>>  {
>> -       struct vb2_buffer *vb;
>>         int ret;
>>
>>         if (type != q->type) {
>> @@ -1937,17 +1979,15 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
>>         }
>>
>>         /*
>> -        * If any buffers were queued before streamon,
>> -        * we can now pass them to driver for processing.
>> +        * Tell driver to start streaming provided sufficient buffers
>> +        * are available.
>>          */
>> -       list_for_each_entry(vb, &q->queued_list, queued_entry)
>> -               __enqueue_in_driver(vb);
>> -
>> -       /* Tell driver to start streaming. */
>> -       ret = vb2_start_streaming(q);
>> -       if (ret) {
>> -               __vb2_queue_cancel(q);
>> -               return ret;
>> +       if (q->queued_count >= q->min_buffers_needed) {
>> +               ret = vb2_start_streaming(q);
>> +               if (ret) {
>> +                       __vb2_queue_cancel(q);
>> +                       return ret;
>> +               }
>>         }
>>
>>         q->streaming = 1;
>> diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
>> index 1f3b0f9..8c101cb 100644
>> --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
>> +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
>> @@ -1201,8 +1201,6 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
>>         unsigned long addr;
>>         int ret;
>>
>> -       if (count == 0)
>> -               return -ENOBUFS;
>>         ret = mutex_lock_interruptible(&video->lock);
>>         if (ret)
>>                 goto streamoff;
>> @@ -1327,6 +1325,7 @@ static int vpfe_reqbufs(struct file *file, void *priv,
>>         q->type = req_buf->type;
>>         q->io_modes = VB2_MMAP | VB2_USERPTR;
>>         q->drv_priv = fh;
>> +       q->min_buffers_needed = 1;
>>         q->ops = &video_qops;
>>         q->mem_ops = &vb2_dma_contig_memops;
>>         q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
>> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
>> index adaffed..2ada71b 100644
>> --- a/include/media/videobuf2-core.h
>> +++ b/include/media/videobuf2-core.h
>> @@ -348,20 +348,24 @@ struct v4l2_fh;
>>   * @gfp_flags: additional gfp flags used when allocating the buffers.
>>   *             Typically this is 0, but it may be e.g. GFP_DMA or __GFP_DMA32
>>   *             to force the buffer allocation to a specific memory zone.
>> + * @min_buffers_needed: the minimum number of buffers needed before
>> + *             start_streaming() can be called. Used when a DMA engine
>> + *             cannot be started unless at least this number of buffers
>> + *             have been queued into the driver.
>>   *
>>   * @memory:    current memory type used
>>   * @bufs:      videobuf buffer structures
>>   * @num_buffers: number of allocated/used buffers
>>   * @queued_list: list of buffers currently queued from userspace
>> + * @queued_count: number of buffers queued and ready for streaming.
>>   * @owned_by_drv_count: number of buffers owned by the driver
>>   * @done_list: list of buffers ready to be dequeued to userspace
>>   * @done_lock: lock to protect done_list list
>>   * @done_wq:   waitqueue for processes waiting for buffers ready to be dequeued
>>   * @alloc_ctx: memory type/allocator-specific contexts for each plane
>>   * @streaming: current streaming state
>> - * @retry_start_streaming: start_streaming() was called, but there were not enough
>> - *             buffers queued. If set, then retry calling start_streaming when
>> - *             queuing a new buffer.
>> + * @start_streaming_called: start_streaming() was called successfully and we
>> + *             started streaming.
>>   * @fileio:    file io emulator internal data, used only if emulator is active
>>   */
>>  struct vb2_queue {
>> @@ -377,6 +381,7 @@ struct vb2_queue {
>>         unsigned int                    buf_struct_size;
>>         u32                             timestamp_type;
>>         gfp_t                           gfp_flags;
>> +       u32                             min_buffers_needed;
> 
> Perhaps add a WARN_ON or even clamp this if queue_setup requests less
> buffers than this?

Good idea. I've clamped it and if __reqbufs or __createbufs cannot allocate at
least that number of buffers they will return -ENOMEM as continuing would be
pointless.

Also found a nasty little bug in when q->num_buffers is updated: I've added a
new patch fixing that.

Regards,

	Hans

> 
>>
>>  /* private: internal use only */
>>         enum v4l2_memory                memory;
>> @@ -384,6 +389,7 @@ struct vb2_queue {
>>         unsigned int                    num_buffers;
>>
>>         struct list_head                queued_list;
>> +       unsigned int                    queued_count;
>>
>>         atomic_t                        owned_by_drv_count;
>>         struct list_head                done_list;
>> @@ -394,7 +400,7 @@ struct vb2_queue {
>>         unsigned int                    plane_sizes[VIDEO_MAX_PLANES];
>>
>>         unsigned int                    streaming:1;
>> -       unsigned int                    retry_start_streaming:1;
>> +       unsigned int                    start_streaming_called:1;
>>
>>         struct vb2_fileio_data          *fileio;
>>
>> --
>> 1.8.4.rc3
>>
> 
> 
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2014-02-14 10:32 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-13  9:40 [RFCv3 PATCH 00/10] vb2: fixes, balancing callbacks Hans Verkuil
2014-02-13  9:40 ` [RFCv3 PATCH 01/10] vb2: add debugging code to check for unbalanced ops Hans Verkuil
2014-02-13  9:40 ` [RFCv3 PATCH 02/10] vb2: change result code of buf_finish to void Hans Verkuil
2014-02-14  1:30   ` Pawel Osciak
2014-02-13  9:40 ` [RFCv3 PATCH 03/10] vb2: add note that buf_finish can be called with !vb2_is_streaming() Hans Verkuil
2014-02-14  1:32   ` Pawel Osciak
2014-02-13  9:40 ` [RFCv3 PATCH 04/10] vb2: call buf_finish from __dqbuf Hans Verkuil
2014-02-14  1:35   ` Pawel Osciak
2014-02-13  9:40 ` [RFCv3 PATCH 05/10] vb2: fix buf_init/buf_cleanup call sequences Hans Verkuil
2014-02-14  4:40   ` Pawel Osciak
2014-02-14 10:28     ` Hans Verkuil
2014-02-13  9:40 ` [RFCv3 PATCH 06/10] vb2: fix read/write regression Hans Verkuil
2014-02-14  4:49   ` Pawel Osciak
2014-02-14 10:29     ` Hans Verkuil
2014-02-13  9:40 ` [RFCv3 PATCH 07/10] vb2: rename queued_count to owned_by_drv_count Hans Verkuil
2014-02-14  4:49   ` Pawel Osciak
2014-02-13  9:40 ` [RFCv3 PATCH 08/10] vb2: only call start_streaming if sufficient buffers are queued Hans Verkuil
2014-02-14  5:13   ` Pawel Osciak
2014-02-14 10:31     ` Hans Verkuil
2014-02-13  9:40 ` [RFCv3 PATCH 09/10] vivi: correctly cleanup after a start_streaming failure Hans Verkuil
2014-02-13  9:40 ` [RFCv3 PATCH 10/10] v4l2-ioctl: add CREATE_BUFS sanity checks Hans Verkuil

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.