]> Pileus Git - ~andy/linux/blobdiff - drivers/media/video/videobuf2-core.c
Merge branch 'rc-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild
[~andy/linux] / drivers / media / video / videobuf2-core.c
index 95a3f5e82aef14c74a81045baa26874eb008e7c1..2e8f1df775b622ef9f877be16916fd7d9d0e0c80 100644 (file)
@@ -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 = &current->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;