media: videobuf2-core: take mmap_lock in vb2_get_unmapped_area()
authorHans Verkuil <hverkuil-cisco@xs4all.nl>
Wed, 7 Dec 2022 13:04:34 +0000 (13:04 +0000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 7 Dec 2022 19:25:40 +0000 (11:25 -0800)
While vb2_mmap took the mmap_lock mutex, vb2_get_unmapped_area didn't.
Add this.

Also take this opportunity to move the 'q->memory != VB2_MEMORY_MMAP'
check and vb2_fileio_is_active() check into __find_plane_by_offset() so
both vb2_mmap and vb2_get_unmapped_area do the same checks.

Since q->memory is checked while mmap_lock is held, also take that lock
in reqbufs and create_bufs when it is set, and set it back to
MEMORY_UNKNOWN on error.

Fixes: f035eb4e976e ("[media] videobuf2: fix lockdep warning")
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Acked-by: Tomasz Figa <tfiga@chromium.org>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/media/common/videobuf2/videobuf2-core.c

index ab9697f..92efc46 100644 (file)
@@ -813,7 +813,13 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
        num_buffers = max_t(unsigned int, *count, q->min_buffers_needed);
        num_buffers = min_t(unsigned int, num_buffers, VB2_MAX_FRAME);
        memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
+       /*
+        * Set this now to ensure that drivers see the correct q->memory value
+        * in the queue_setup op.
+        */
+       mutex_lock(&q->mmap_lock);
        q->memory = memory;
+       mutex_unlock(&q->mmap_lock);
        set_queue_coherency(q, non_coherent_mem);
 
        /*
@@ -823,22 +829,27 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
        ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
                       plane_sizes, q->alloc_devs);
        if (ret)
-               return ret;
+               goto error;
 
        /* Check that driver has set sane values */
-       if (WARN_ON(!num_planes))
-               return -EINVAL;
+       if (WARN_ON(!num_planes)) {
+               ret = -EINVAL;
+               goto error;
+       }
 
        for (i = 0; i < num_planes; i++)
-               if (WARN_ON(!plane_sizes[i]))
-                       return -EINVAL;
+               if (WARN_ON(!plane_sizes[i])) {
+                       ret = -EINVAL;
+                       goto error;
+               }
 
        /* Finally, allocate buffers and video memory */
        allocated_buffers =
                __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);
        if (allocated_buffers == 0) {
                dprintk(q, 1, "memory allocation failed\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto error;
        }
 
        /*
@@ -879,7 +890,8 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
        if (ret < 0) {
                /*
                 * Note: __vb2_queue_free() will subtract 'allocated_buffers'
-                * from q->num_buffers.
+                * from q->num_buffers and it will reset q->memory to
+                * VB2_MEMORY_UNKNOWN.
                 */
                __vb2_queue_free(q, allocated_buffers);
                mutex_unlock(&q->mmap_lock);
@@ -895,6 +907,12 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
        q->waiting_for_buffers = !q->is_output;
 
        return 0;
+
+error:
+       mutex_lock(&q->mmap_lock);
+       q->memory = VB2_MEMORY_UNKNOWN;
+       mutex_unlock(&q->mmap_lock);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(vb2_core_reqbufs);
 
@@ -906,6 +924,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
        unsigned int num_planes = 0, num_buffers, allocated_buffers;
        unsigned plane_sizes[VB2_MAX_PLANES] = { };
        bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT;
+       bool no_previous_buffers = !q->num_buffers;
        int ret;
 
        if (q->num_buffers == VB2_MAX_FRAME) {
@@ -913,13 +932,19 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
                return -ENOBUFS;
        }
 
-       if (!q->num_buffers) {
+       if (no_previous_buffers) {
                if (q->waiting_in_dqbuf && *count) {
                        dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n");
                        return -EBUSY;
                }
                memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
+               /*
+                * Set this now to ensure that drivers see the correct q->memory
+                * value in the queue_setup op.
+                */
+               mutex_lock(&q->mmap_lock);
                q->memory = memory;
+               mutex_unlock(&q->mmap_lock);
                q->waiting_for_buffers = !q->is_output;
                set_queue_coherency(q, non_coherent_mem);
        } else {
@@ -945,14 +970,15 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
        ret = call_qop(q, queue_setup, q, &num_buffers,
                       &num_planes, plane_sizes, q->alloc_devs);
        if (ret)
-               return ret;
+               goto error;
 
        /* Finally, allocate buffers and video memory */
        allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers,
                                num_planes, plane_sizes);
        if (allocated_buffers == 0) {
                dprintk(q, 1, "memory allocation failed\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto error;
        }
 
        /*
@@ -983,7 +1009,8 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
        if (ret < 0) {
                /*
                 * Note: __vb2_queue_free() will subtract 'allocated_buffers'
-                * from q->num_buffers.
+                * from q->num_buffers and it will reset q->memory to
+                * VB2_MEMORY_UNKNOWN.
                 */
                __vb2_queue_free(q, allocated_buffers);
                mutex_unlock(&q->mmap_lock);
@@ -998,6 +1025,14 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
        *count = allocated_buffers;
 
        return 0;
+
+error:
+       if (no_previous_buffers) {
+               mutex_lock(&q->mmap_lock);
+               q->memory = VB2_MEMORY_UNKNOWN;
+               mutex_unlock(&q->mmap_lock);
+       }
+       return ret;
 }
 EXPORT_SYMBOL_GPL(vb2_core_create_bufs);
 
@@ -2165,6 +2200,22 @@ static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off,
        unsigned int buffer, plane;
 
        /*
+        * Sanity checks to ensure the lock is held, MEMORY_MMAP is
+        * used and fileio isn't active.
+        */
+       lockdep_assert_held(&q->mmap_lock);
+
+       if (q->memory != VB2_MEMORY_MMAP) {
+               dprintk(q, 1, "queue is not currently set up for mmap\n");
+               return -EINVAL;
+       }
+
+       if (vb2_fileio_is_active(q)) {
+               dprintk(q, 1, "file io in progress\n");
+               return -EBUSY;
+       }
+
+       /*
         * Go over all buffers and their planes, comparing the given offset
         * with an offset assigned to each plane. If a match is found,
         * return its buffer and plane numbers.
@@ -2265,11 +2316,6 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
        int ret;
        unsigned long length;
 
-       if (q->memory != VB2_MEMORY_MMAP) {
-               dprintk(q, 1, "queue is not currently set up for mmap\n");
-               return -EINVAL;
-       }
-
        /*
         * Check memory area access mode.
         */
@@ -2291,14 +2337,9 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
 
        mutex_lock(&q->mmap_lock);
 
-       if (vb2_fileio_is_active(q)) {
-               dprintk(q, 1, "mmap: file io in progress\n");
-               ret = -EBUSY;
-               goto unlock;
-       }
-
        /*
-        * Find the plane corresponding to the offset passed by userspace.
+        * Find the plane corresponding to the offset passed by userspace. This
+        * will return an error if not MEMORY_MMAP or file I/O is in progress.
         */
        ret = __find_plane_by_offset(q, off, &buffer, &plane);
        if (ret)
@@ -2351,22 +2392,25 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
        void *vaddr;
        int ret;
 
-       if (q->memory != VB2_MEMORY_MMAP) {
-               dprintk(q, 1, "queue is not currently set up for mmap\n");
-               return -EINVAL;
-       }
+       mutex_lock(&q->mmap_lock);
 
        /*
-        * Find the plane corresponding to the offset passed by userspace.
+        * Find the plane corresponding to the offset passed by userspace. This
+        * will return an error if not MEMORY_MMAP or file I/O is in progress.
         */
        ret = __find_plane_by_offset(q, off, &buffer, &plane);
        if (ret)
-               return ret;
+               goto unlock;
 
        vb = q->bufs[buffer];
 
        vaddr = vb2_plane_vaddr(vb, plane);
+       mutex_unlock(&q->mmap_lock);
        return vaddr ? (unsigned long)vaddr : -EINVAL;
+
+unlock:
+       mutex_unlock(&q->mmap_lock);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
 #endif