Drivers must bracket CPU access to imported DMA-BUFs with
dma_buf_begin_cpu_access() and dma_buf_end_cpu_access().

Add a queue flag to do so from buf_prepare() through buf_finish(). Use
read access for output and write access for capture. Map imports
bidirectionally because CPU and device access directions are opposite.
Pass the mapping direction to attach_dmabuf() so USERPTR and MMAP keep
the queue's normal direction.

Keep this opt-in because some vb2-vmalloc drivers pass buffers to
hardware. End access in process context because DMA-BUF callbacks may
sleep.

Assisted-by: LLM
Signed-off-by: Karl Mehltretter <[email protected]>
---
 .../media/common/videobuf2/videobuf2-core.c   | 83 ++++++++++++++++++-
 .../common/videobuf2/videobuf2-dma-contig.c   |  5 +-
 .../media/common/videobuf2/videobuf2-dma-sg.c |  5 +-
 .../common/videobuf2/videobuf2-vmalloc.c      |  5 +-
 include/media/videobuf2-core.h                | 12 ++-
 5 files changed, 101 insertions(+), 9 deletions(-)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c 
b/drivers/media/common/videobuf2/videobuf2-core.c
index 460eaf4e82f78..d2a78cd0a86bb 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -371,6 +371,65 @@ static void __vb2_buf_mem_finish(struct vb2_buffer *vb)
                call_void_memop(vb, finish, vb->planes[plane].mem_priv);
 }
 
+/* Output buffers are read by the driver; capture buffers are written. */
+static enum dma_data_direction vb2_cpu_access_dir(const struct vb2_queue *q)
+{
+       return q->is_output ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+}
+
+static bool __vb2_dmabuf_is_duplicate(const struct vb2_buffer *vb,
+                                     unsigned int plane)
+{
+       unsigned int i;
+
+       for (i = 0; i < plane; i++)
+               if (vb->planes[i].dbuf == vb->planes[plane].dbuf)
+                       return true;
+
+       return false;
+}
+
+static void __vb2_buf_end_cpu_access(struct vb2_buffer *vb,
+                                    unsigned int num_planes)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+       enum dma_data_direction dir = vb2_cpu_access_dir(q);
+       unsigned int plane;
+
+       for (plane = 0; plane < num_planes; plane++) {
+               int ret;
+
+               if (__vb2_dmabuf_is_duplicate(vb, plane))
+                       continue;
+
+               ret = dma_buf_end_cpu_access(vb->planes[plane].dbuf, dir);
+               if (ret)
+                       pr_warn_ratelimited("[%s] failed to end CPU access to 
dmabuf plane %u: %d\n",
+                                           q->name, plane, ret);
+       }
+}
+
+static int __vb2_buf_begin_cpu_access(struct vb2_buffer *vb)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+       enum dma_data_direction dir = vb2_cpu_access_dir(q);
+       unsigned int plane;
+       int ret;
+
+       for (plane = 0; plane < vb->num_planes; plane++) {
+               if (__vb2_dmabuf_is_duplicate(vb, plane))
+                       continue;
+
+               ret = dma_buf_begin_cpu_access(vb->planes[plane].dbuf, dir);
+               if (ret) {
+                       __vb2_buf_end_cpu_access(vb, plane);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * __setup_offsets() - setup unique offsets ("cookies") for every plane in
  * the buffer.
@@ -1423,6 +1482,9 @@ static int __prepare_dmabuf(struct vb2_buffer *vb)
        }
 
        if (reacquired) {
+               enum dma_data_direction attach_dir = q->uses_cpu_access ?
+                       DMA_BIDIRECTIONAL : q->dma_dir;
+
                if (vb->planes[0].mem_priv) {
                        vb->copied_timestamp = 0;
                        call_void_vb_qop(vb, buf_cleanup, vb);
@@ -1453,7 +1515,8 @@ static int __prepare_dmabuf(struct vb2_buffer *vb)
                                                  vb,
                                                  q->alloc_devs[plane] ? : 
q->dev,
                                                  planes[plane].dbuf,
-                                                 planes[plane].length);
+                                                 planes[plane].length,
+                                                 attach_dir);
                        if (IS_ERR(mem_priv)) {
                                dprintk(q, 1, "failed to attach dmabuf\n");
                                ret = PTR_ERR(mem_priv);
@@ -1504,9 +1567,21 @@ static int __prepare_dmabuf(struct vb2_buffer *vb)
                }
        }
 
+       if (q->uses_cpu_access) {
+               ret = __vb2_buf_begin_cpu_access(vb);
+               if (ret) {
+                       dprintk(q, 1, "failed to begin CPU access to dmabuf: 
%d\n",
+                               ret);
+                       call_void_vb_qop(vb, buf_cleanup, vb);
+                       goto err_put_vb2_buf;
+               }
+       }
+
        ret = call_vb_qop(vb, buf_prepare, vb);
        if (ret) {
                dprintk(q, 1, "buffer preparation failed\n");
+               if (q->uses_cpu_access)
+                       __vb2_buf_end_cpu_access(vb, vb->num_planes);
                call_void_vb_qop(vb, buf_cleanup, vb);
                goto err_put_vb2_buf;
        }
@@ -1756,6 +1831,8 @@ int vb2_core_remove_bufs(struct vb2_queue *q, unsigned 
int start, unsigned int c
 
                __vb2_buf_mem_finish(vb);
                call_void_vb_qop(vb, buf_finish, vb);
+               if (q->memory == VB2_MEMORY_DMABUF && q->uses_cpu_access)
+                       __vb2_buf_end_cpu_access(vb, vb->num_planes);
                vb->prepared = 0;
        }
 
@@ -2160,6 +2237,8 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int 
*pindex, void *pb,
        }
 
        call_void_vb_qop(vb, buf_finish, vb);
+       if (q->memory == VB2_MEMORY_DMABUF && q->uses_cpu_access)
+               __vb2_buf_end_cpu_access(vb, vb->num_planes);
        vb->prepared = 0;
 
        if (pindex)
@@ -2296,6 +2375,8 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
 
                if (vb->prepared) {
                        call_void_vb_qop(vb, buf_finish, vb);
+                       if (q->memory == VB2_MEMORY_DMABUF && 
q->uses_cpu_access)
+                               __vb2_buf_end_cpu_access(vb, vb->num_planes);
                        vb->prepared = 0;
                }
                __vb2_dqbuf(vb);
diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c 
b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
index 9ce6284cd5f26..68c146c043fb1 100644
--- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c
+++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
@@ -768,7 +768,8 @@ static void vb2_dc_detach_dmabuf(void *mem_priv)
 }
 
 static void *vb2_dc_attach_dmabuf(struct vb2_buffer *vb, struct device *dev,
-                                 struct dma_buf *dbuf, unsigned long size)
+                                 struct dma_buf *dbuf, unsigned long size,
+                                 enum dma_data_direction dma_dir)
 {
        struct vb2_dc_buf *buf;
        struct dma_buf_attachment *dba;
@@ -794,7 +795,7 @@ static void *vb2_dc_attach_dmabuf(struct vb2_buffer *vb, 
struct device *dev,
                return dba;
        }
 
-       buf->dma_dir = vb->vb2_queue->dma_dir;
+       buf->dma_dir = dma_dir;
        buf->size = size;
        buf->db_attach = dba;
 
diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c 
b/drivers/media/common/videobuf2/videobuf2-dma-sg.c
index b1d0695cda260..ab03f9d31391a 100644
--- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c
+++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c
@@ -616,7 +616,8 @@ static void vb2_dma_sg_detach_dmabuf(void *mem_priv)
 }
 
 static void *vb2_dma_sg_attach_dmabuf(struct vb2_buffer *vb, struct device 
*dev,
-                                     struct dma_buf *dbuf, unsigned long size)
+                                     struct dma_buf *dbuf, unsigned long size,
+                                     enum dma_data_direction dma_dir)
 {
        struct vb2_dma_sg_buf *buf;
        struct dma_buf_attachment *dba;
@@ -640,7 +641,7 @@ static void *vb2_dma_sg_attach_dmabuf(struct vb2_buffer 
*vb, struct device *dev,
                return dba;
        }
 
-       buf->dma_dir = vb->vb2_queue->dma_dir;
+       buf->dma_dir = dma_dir;
        buf->size = size;
        buf->db_attach = dba;
        buf->vb = vb;
diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c 
b/drivers/media/common/videobuf2/videobuf2-vmalloc.c
index 63f023696f3ee..c38faa0188bc9 100644
--- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c
+++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c
@@ -402,7 +402,8 @@ static void vb2_vmalloc_detach_dmabuf(void *mem_priv)
 static void *vb2_vmalloc_attach_dmabuf(struct vb2_buffer *vb,
                                       struct device *dev,
                                       struct dma_buf *dbuf,
-                                      unsigned long size)
+                                      unsigned long size,
+                                      enum dma_data_direction dma_dir)
 {
        struct vb2_vmalloc_buf *buf;
 
@@ -414,7 +415,7 @@ static void *vb2_vmalloc_attach_dmabuf(struct vb2_buffer 
*vb,
                return ERR_PTR(-ENOMEM);
 
        buf->dbuf = dbuf;
-       buf->dma_dir = vb->vb2_queue->dma_dir;
+       buf->dma_dir = dma_dir;
        buf->size = size;
 
        return buf;
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 4b4f4c15c53a7..8dfe22fa4423c 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -78,7 +78,8 @@ struct vb2_buffer;
  *             to the userspace, also optional.
  * @attach_dmabuf: attach a shared &struct dma_buf for a hardware operation;
  *                used for DMABUF memory types; dev is the alloc device
- *                dbuf is the shared dma_buf; returns ERR_PTR() on failure;
+ *                dbuf is the shared dma_buf; dma_dir is the DMA mapping
+ *                direction; returns ERR_PTR() on failure;
  *                allocator private per-buffer structure on success;
  *                this needs to be used for further accesses to the buffer.
  * @detach_dmabuf: inform the exporter of the buffer that the current DMABUF
@@ -136,7 +137,8 @@ struct vb2_mem_ops {
        void            *(*attach_dmabuf)(struct vb2_buffer *vb,
                                          struct device *dev,
                                          struct dma_buf *dbuf,
-                                         unsigned long size);
+                                         unsigned long size,
+                                         enum dma_data_direction dma_dir);
        void            (*detach_dmabuf)(void *buf_priv);
        int             (*map_dmabuf)(void *buf_priv);
        void            (*unmap_dmabuf)(void *buf_priv);
@@ -510,6 +512,11 @@ struct vb2_buf_ops {
  *             ->finish().
  * @non_coherent_mem: when set queue will attempt to allocate buffers using
  *             non-coherent memory.
+ * @uses_cpu_access: the driver transfers buffer data with the CPU instead of
+ *             DMA. For imported dma-bufs, videobuf2 brackets the interval from
+ *             buf_prepare through buf_finish with dma_buf_begin_cpu_access()
+ *             and dma_buf_end_cpu_access(). Imported attachments use
+ *             bidirectional DMA mappings.
  * @lock:      pointer to a mutex that protects the &struct vb2_queue. The
  *             driver must set this to a mutex to let the v4l2 core serialize
  *             the queuing ioctls. This lock is used when waiting for a new
@@ -614,6 +621,7 @@ struct vb2_queue {
        unsigned int                    uses_requests:1;
        unsigned int                    allow_cache_hints:1;
        unsigned int                    non_coherent_mem:1;
+       unsigned int                    uses_cpu_access:1;
 
        struct mutex                    *lock;
        void                            *owner;
-- 
2.47.3

Reply via email to