Skip to content

Commit

Permalink
[media] vb2: fix race condition between REQBUFS and QBUF/PREPARE_BUF
Browse files Browse the repository at this point in the history
When preparing a buffer the queue lock is released for a short while
if the memory mode is USERPTR (see __buf_prepare for the details), which
would allow a race with a REQBUFS which can free the buffers. Removing the
buffers from underneath __buf_prepare is obviously a bad idea, so we
check if any of the buffers is in the state PREPARING, and if so we
just return -EAGAIN.

If this happens, then the application does something really strange. The
REQBUFS call can be retried safely, since this situation is transient.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
  • Loading branch information
Hans Verkuil authored and Mauro Carvalho Chehab committed Jan 7, 2014
1 parent 4138111 commit 63faabf
Showing 1 changed file with 23 additions and 2 deletions.
25 changes: 23 additions & 2 deletions drivers/media/v4l2-core/videobuf2-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,28 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
* related information, if no buffers are left return the queue to an
* uninitialized state. Might be called even if the queue has already been freed.
*/
static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
{
unsigned int buffer;

/*
* Sanity check: when preparing a buffer the queue lock is released for
* a short while (see __buf_prepare for the details), which would allow
* a race with a reqbufs which can call this function. Removing the
* buffers from underneath __buf_prepare is obviously a bad idea, so we
* check if any of the buffers is in the state PREPARING, and if so we
* just return -EAGAIN.
*/
for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
++buffer) {
if (q->bufs[buffer] == NULL)
continue;
if (q->bufs[buffer]->state == VB2_BUF_STATE_PREPARING) {
dprintk(1, "reqbufs: preparing buffers, cannot free\n");
return -EAGAIN;
}
}

/* 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;
Expand All @@ -326,6 +344,7 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
if (!q->num_buffers)
q->memory = 0;
INIT_LIST_HEAD(&q->queued_list);
return 0;
}

/**
Expand Down Expand Up @@ -658,7 +677,9 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
return -EBUSY;
}

__vb2_queue_free(q, q->num_buffers);
ret = __vb2_queue_free(q, q->num_buffers);
if (ret)
return ret;

/*
* In case of REQBUFS(0) return immediately without calling
Expand Down

0 comments on commit 63faabf

Please sign in to comment.