Commit b18a8ff2 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

[media] vb2: push the mmap semaphore down to __buf_prepare()

Rather than taking the mmap semaphore at a relatively high-level function,
push it down to the place where it is really needed.

It was placed in vb2_queue_or_prepare_buf() to prevent racing with other
vb2 calls. The only way I can see that a race can happen is when two
threads queue the same buffer. The solution for that it to introduce
a PREPARING state.

Moving it down offers opportunities to simplify the code.
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Acked-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent b4fcdaf7
...@@ -481,6 +481,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) ...@@ -481,6 +481,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
case VB2_BUF_STATE_PREPARED: case VB2_BUF_STATE_PREPARED:
b->flags |= V4L2_BUF_FLAG_PREPARED; b->flags |= V4L2_BUF_FLAG_PREPARED;
break; break;
case VB2_BUF_STATE_PREPARING:
case VB2_BUF_STATE_DEQUEUED: case VB2_BUF_STATE_DEQUEUED:
/* nothing */ /* nothing */
break; break;
...@@ -1228,6 +1229,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) ...@@ -1228,6 +1229,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{ {
struct vb2_queue *q = vb->vb2_queue; struct vb2_queue *q = vb->vb2_queue;
struct rw_semaphore *mmap_sem;
int ret; int ret;
ret = __verify_length(vb, b); ret = __verify_length(vb, b);
...@@ -1237,12 +1239,31 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ...@@ -1237,12 +1239,31 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
return ret; return ret;
} }
vb->state = VB2_BUF_STATE_PREPARING;
switch (q->memory) { switch (q->memory) {
case V4L2_MEMORY_MMAP: case V4L2_MEMORY_MMAP:
ret = __qbuf_mmap(vb, b); ret = __qbuf_mmap(vb, b);
break; break;
case V4L2_MEMORY_USERPTR: case V4L2_MEMORY_USERPTR:
/*
* In case of user pointer buffers vb2 allocators need to get direct
* access to userspace pages. This requires getting the mmap semaphore
* for read access in the current process structure. The same semaphore
* is taken before calling mmap operation, while both qbuf/prepare_buf
* and mmap are called by the driver or v4l2 core with the driver's lock
* held. To avoid an AB-BA deadlock (mmap_sem then driver's lock in mmap
* and driver's lock then mmap_sem in qbuf/prepare_buf) the videobuf2
* core releases the driver's lock, takes mmap_sem and then takes the
* driver's lock again.
*/
mmap_sem = &current->mm->mmap_sem;
call_qop(q, wait_prepare, q);
down_read(mmap_sem);
call_qop(q, wait_finish, q);
ret = __qbuf_userptr(vb, b); ret = __qbuf_userptr(vb, b);
up_read(mmap_sem);
break; break;
case V4L2_MEMORY_DMABUF: case V4L2_MEMORY_DMABUF:
ret = __qbuf_dmabuf(vb, b); ret = __qbuf_dmabuf(vb, b);
...@@ -1256,8 +1277,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ...@@ -1256,8 +1277,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
ret = call_qop(q, buf_prepare, vb); ret = call_qop(q, buf_prepare, vb);
if (ret) if (ret)
dprintk(1, "qbuf: buffer preparation failed: %d\n", ret); dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
else vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;
vb->state = VB2_BUF_STATE_PREPARED;
return ret; return ret;
} }
...@@ -1268,80 +1288,47 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, ...@@ -1268,80 +1288,47 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
struct v4l2_buffer *, struct v4l2_buffer *,
struct vb2_buffer *)) struct vb2_buffer *))
{ {
struct rw_semaphore *mmap_sem = NULL;
struct vb2_buffer *vb; struct vb2_buffer *vb;
int ret; int ret;
/*
* In case of user pointer buffers vb2 allocators need to get direct
* access to userspace pages. This requires getting the mmap semaphore
* for read access in the current process structure. The same semaphore
* is taken before calling mmap operation, while both qbuf/prepare_buf
* and mmap are called by the driver or v4l2 core with the driver's lock
* held. To avoid an AB-BA deadlock (mmap_sem then driver's lock in mmap
* and driver's lock then mmap_sem in qbuf/prepare_buf) the videobuf2
* core releases the driver's lock, takes mmap_sem and then takes the
* driver's lock again.
*
* To avoid racing with other vb2 calls, which might be called after
* releasing the driver's lock, this operation is performed at the
* beginning of qbuf/prepare_buf processing. This way the queue status
* is consistent after getting the driver's lock back.
*/
if (q->memory == V4L2_MEMORY_USERPTR) {
mmap_sem = &current->mm->mmap_sem;
call_qop(q, wait_prepare, q);
down_read(mmap_sem);
call_qop(q, wait_finish, q);
}
if (q->fileio) { if (q->fileio) {
dprintk(1, "%s(): file io in progress\n", opname); dprintk(1, "%s(): file io in progress\n", opname);
ret = -EBUSY; return -EBUSY;
goto unlock;
} }
if (b->type != q->type) { if (b->type != q->type) {
dprintk(1, "%s(): invalid buffer type\n", opname); dprintk(1, "%s(): invalid buffer type\n", opname);
ret = -EINVAL; return -EINVAL;
goto unlock;
} }
if (b->index >= q->num_buffers) { if (b->index >= q->num_buffers) {
dprintk(1, "%s(): buffer index out of range\n", opname); dprintk(1, "%s(): buffer index out of range\n", opname);
ret = -EINVAL; return -EINVAL;
goto unlock;
} }
vb = q->bufs[b->index]; vb = q->bufs[b->index];
if (NULL == vb) { if (NULL == vb) {
/* Should never happen */ /* Should never happen */
dprintk(1, "%s(): buffer is NULL\n", opname); dprintk(1, "%s(): buffer is NULL\n", opname);
ret = -EINVAL; return -EINVAL;
goto unlock;
} }
if (b->memory != q->memory) { if (b->memory != q->memory) {
dprintk(1, "%s(): invalid memory type\n", opname); dprintk(1, "%s(): invalid memory type\n", opname);
ret = -EINVAL; return -EINVAL;
goto unlock;
} }
ret = __verify_planes_array(vb, b); ret = __verify_planes_array(vb, b);
if (ret) if (ret)
goto unlock; return ret;
ret = handler(q, b, vb); ret = handler(q, b, vb);
if (ret) if (!ret) {
goto unlock; /* Fill buffer information for the userspace */
__fill_v4l2_buffer(vb, b);
/* Fill buffer information for the userspace */
__fill_v4l2_buffer(vb, b);
dprintk(1, "%s() of buffer %d succeeded\n", opname, vb->v4l2_buf.index); dprintk(1, "%s() of buffer %d succeeded\n", opname, vb->v4l2_buf.index);
unlock: }
if (mmap_sem)
up_read(mmap_sem);
return ret; return ret;
} }
...@@ -1390,6 +1377,9 @@ static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b, ...@@ -1390,6 +1377,9 @@ static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b,
return ret; return ret;
case VB2_BUF_STATE_PREPARED: case VB2_BUF_STATE_PREPARED:
break; break;
case VB2_BUF_STATE_PREPARING:
dprintk(1, "qbuf: buffer still being prepared\n");
return -EINVAL;
default: default:
dprintk(1, "qbuf: buffer already in use\n"); dprintk(1, "qbuf: buffer already in use\n");
return -EINVAL; return -EINVAL;
......
...@@ -142,6 +142,7 @@ enum vb2_fileio_flags { ...@@ -142,6 +142,7 @@ enum vb2_fileio_flags {
/** /**
* enum vb2_buffer_state - current video buffer state * enum vb2_buffer_state - current video buffer state
* @VB2_BUF_STATE_DEQUEUED: buffer under userspace control * @VB2_BUF_STATE_DEQUEUED: buffer under userspace control
* @VB2_BUF_STATE_PREPARING: buffer is being prepared in videobuf
* @VB2_BUF_STATE_PREPARED: buffer prepared in videobuf and by the driver * @VB2_BUF_STATE_PREPARED: buffer prepared in videobuf and by the driver
* @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver * @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver
* @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used * @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used
...@@ -154,6 +155,7 @@ enum vb2_fileio_flags { ...@@ -154,6 +155,7 @@ enum vb2_fileio_flags {
*/ */
enum vb2_buffer_state { enum vb2_buffer_state {
VB2_BUF_STATE_DEQUEUED, VB2_BUF_STATE_DEQUEUED,
VB2_BUF_STATE_PREPARING,
VB2_BUF_STATE_PREPARED, VB2_BUF_STATE_PREPARED,
VB2_BUF_STATE_QUEUED, VB2_BUF_STATE_QUEUED,
VB2_BUF_STATE_ACTIVE, VB2_BUF_STATE_ACTIVE,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment