diff options
Diffstat (limited to 'drivers/media/video/videobuf2-core.c')
-rw-r--r-- | drivers/media/video/videobuf2-core.c | 118 |
1 files changed, 72 insertions, 46 deletions
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 95a3f5e82aef..2e8f1df775b6 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -30,7 +30,7 @@ module_param(debug, int, 0644); printk(KERN_DEBUG "vb2: " fmt, ## arg); \ } while (0) -#define call_memop(q, plane, op, args...) \ +#define call_memop(q, op, args...) \ (((q)->mem_ops->op) ? \ ((q)->mem_ops->op(args)) : 0) @@ -52,7 +52,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) /* Allocate memory for all planes in this buffer */ for (plane = 0; plane < vb->num_planes; ++plane) { - mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane], + mem_priv = call_memop(q, alloc, q->alloc_ctx[plane], q->plane_sizes[plane]); if (IS_ERR_OR_NULL(mem_priv)) goto free; @@ -65,8 +65,10 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) return 0; free: /* Free already allocated memory if one of the allocations failed */ - for (; plane > 0; --plane) - call_memop(q, plane, put, vb->planes[plane - 1].mem_priv); + for (; plane > 0; --plane) { + call_memop(q, put, vb->planes[plane - 1].mem_priv); + vb->planes[plane - 1].mem_priv = NULL; + } return -ENOMEM; } @@ -80,10 +82,10 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb) unsigned int plane; for (plane = 0; plane < vb->num_planes; ++plane) { - call_memop(q, plane, put, vb->planes[plane].mem_priv); + call_memop(q, 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); + dprintk(3, "Freed plane %d of buffer %d\n", plane, + vb->v4l2_buf.index); } } @@ -97,12 +99,9 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb) unsigned int plane; for (plane = 0; plane < vb->num_planes; ++plane) { - void *mem_priv = vb->planes[plane].mem_priv; - - if (mem_priv) { - call_memop(q, plane, put_userptr, mem_priv); - vb->planes[plane].mem_priv = NULL; - } + if (vb->planes[plane].mem_priv) + call_memop(q, put_userptr, vb->planes[plane].mem_priv); + vb->planes[plane].mem_priv = NULL; } } @@ -305,7 +304,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, plane, num_users, mem_priv) > 1) + if (mem_priv && call_memop(q, num_users, mem_priv) > 1) return true; } return false; @@ -731,10 +730,10 @@ 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) + if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv) return NULL; - return call_memop(q, plane_no, vaddr, vb->planes[plane_no].mem_priv); + return call_memop(q, vaddr, vb->planes[plane_no].mem_priv); } EXPORT_SYMBOL_GPL(vb2_plane_vaddr); @@ -754,10 +753,10 @@ 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) + if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv) return NULL; - return call_memop(q, plane_no, cookie, vb->planes[plane_no].mem_priv); + return call_memop(q, cookie, vb->planes[plane_no].mem_priv); } EXPORT_SYMBOL_GPL(vb2_plane_cookie); @@ -883,7 +882,8 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) for (plane = 0; plane < vb->num_planes; ++plane) { /* Skip the plane if already verified */ - if (vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr + if (vb->v4l2_planes[plane].m.userptr && + vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr && vb->v4l2_planes[plane].length == planes[plane].length) continue; @@ -898,27 +898,23 @@ 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, plane, put_userptr, - vb->planes[plane].mem_priv); + call_memop(q, 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 */ - if (q->mem_ops->get_userptr) { - mem_priv = q->mem_ops->get_userptr(q->alloc_ctx[plane], - planes[plane].m.userptr, - planes[plane].length, - write); - if (IS_ERR(mem_priv)) { - dprintk(1, "qbuf: failed acquiring userspace " + mem_priv = call_memop(q, 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); - ret = PTR_ERR(mem_priv); - goto err; - } - vb->planes[plane].mem_priv = mem_priv; + ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL; + goto err; } + vb->planes[plane].mem_priv = mem_priv; } /* @@ -943,8 +939,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, plane, put_userptr, - vb->planes[plane].mem_priv); + call_memop(q, 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; @@ -1081,46 +1076,76 @@ EXPORT_SYMBOL_GPL(vb2_prepare_buf); */ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) { + struct rw_semaphore *mmap_sem = NULL; struct vb2_buffer *vb; - int ret; + int ret = 0; + + /* + * In case of user pointer buffers vb2 allocator needs to get direct + * access to userspace pages. This requires getting read access on + * mmap semaphore in the current process structure. The same + * semaphore is taken before calling mmap operation, while both mmap + * and qbuf are called by the driver or v4l2 core with driver's lock + * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in + * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core + * release driver's lock, takes mmap_sem and then takes again driver's + * lock. + * + * To avoid race with other vb2 calls, which might be called after + * releasing driver's lock, this operation is performed at the + * beggining of qbuf processing. This way the queue status is + * consistent after getting driver's lock back. + */ + if (q->memory == V4L2_MEMORY_USERPTR) { + mmap_sem = ¤t->mm->mmap_sem; + call_qop(q, wait_prepare, q); + down_read(mmap_sem); + call_qop(q, wait_finish, q); + } if (q->fileio) { dprintk(1, "qbuf: file io in progress\n"); - return -EBUSY; + ret = -EBUSY; + goto unlock; } if (b->type != q->type) { dprintk(1, "qbuf: invalid buffer type\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (b->index >= q->num_buffers) { dprintk(1, "qbuf: buffer index out of range\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } vb = q->bufs[b->index]; if (NULL == vb) { /* Should never happen */ dprintk(1, "qbuf: buffer is NULL\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (b->memory != q->memory) { dprintk(1, "qbuf: invalid memory type\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } switch (vb->state) { case VB2_BUF_STATE_DEQUEUED: ret = __buf_prepare(vb, b); if (ret) - return ret; + goto unlock; case VB2_BUF_STATE_PREPARED: break; default: dprintk(1, "qbuf: buffer already in use\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } /* @@ -1141,7 +1166,10 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) __fill_v4l2_buffer(vb, b); dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); - return 0; +unlock: + if (mmap_sem) + up_read(mmap_sem); + return ret; } EXPORT_SYMBOL_GPL(vb2_qbuf); @@ -1521,7 +1549,6 @@ static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) { unsigned long off = vma->vm_pgoff << PAGE_SHIFT; - struct vb2_plane *vb_plane; struct vb2_buffer *vb; unsigned int buffer, plane; int ret; @@ -1558,9 +1585,8 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) return ret; vb = q->bufs[buffer]; - vb_plane = &vb->planes[plane]; - ret = q->mem_ops->mmap(vb_plane->mem_priv, vma); + ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma); if (ret) return ret; |