mirror of https://gitee.com/openkylin/linux.git
[media] vb2: fix race condition between REQBUFS and QBUF/PREPARE_BUF
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>
This commit is contained in:
parent
4138111a27
commit
63faabfd89
|
@ -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
|
* 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.
|
* 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;
|
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 */
|
/* Call driver-provided cleanup function for each buffer, if provided */
|
||||||
if (q->ops->buf_cleanup) {
|
if (q->ops->buf_cleanup) {
|
||||||
for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
|
for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
|
||||||
|
@ -326,6 +344,7 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
|
||||||
if (!q->num_buffers)
|
if (!q->num_buffers)
|
||||||
q->memory = 0;
|
q->memory = 0;
|
||||||
INIT_LIST_HEAD(&q->queued_list);
|
INIT_LIST_HEAD(&q->queued_list);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -658,7 +677,9 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
|
||||||
return -EBUSY;
|
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
|
* In case of REQBUFS(0) return immediately without calling
|
||||||
|
|
Loading…
Reference in New Issue