On 02/14/2014 06:13 AM, Pawel Osciak wrote: > On Thu, Feb 13, 2014 at 6:40 PM, Hans Verkuil <hverkuil@xxxxxxxxx> wrote: >> From: Hans Verkuil <hans.verkuil@xxxxxxxxx> >> >> 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@xxxxxxxxx> >> --- >> 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 >> > > > -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html