BOs mapped with DRM_V3D_BO_WB_MMAP require CPU cache maintenance around
job execution. ARM64 userspace can do it through userspace cache
operations, but AArch32 cache maintenance is privileged, even on an ARM64
kernel, which means that 32-bit userspace can't do cache mainteinance.
Without a kernel interface, a 32-bit userspace wouldn't be able to use
cached BOs.

Add an ioctl taking an array of sync operations, so userspace can flush or
invalidate multiple BO ranges in a single call.

Signed-off-by: Maíra Canal <[email protected]>
---
 drivers/gpu/drm/v3d/v3d_bo.c  | 64 +++++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/v3d/v3d_drv.c |  1 +
 drivers/gpu/drm/v3d/v3d_drv.h |  2 ++
 include/uapi/drm/v3d_drm.h    | 48 ++++++++++++++++++++++++++++++++
 4 files changed, 115 insertions(+)

diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c
index 0a5a056d4b26..78eb3a1cc794 100644
--- a/drivers/gpu/drm/v3d/v3d_bo.c
+++ b/drivers/gpu/drm/v3d/v3d_bo.c
@@ -280,6 +280,70 @@ int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
+static int v3d_bo_sync(struct drm_gem_object *obj,
+                      const struct drm_v3d_bo_sync_op *op)
+{
+       struct v3d_bo *bo = to_v3d_bo(obj);
+       enum drm_gem_shmem_sync_type type;
+
+       switch (op->type) {
+       case DRM_V3D_BO_SYNC_CPU_CACHE_FLUSH:
+               type = DRM_GEM_SHMEM_SYNC_CPU_CACHE_FLUSH;
+               break;
+       case DRM_V3D_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE:
+               type = DRM_GEM_SHMEM_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return drm_gem_shmem_sync(&bo->base, op->offset, op->size, type);
+}
+
+int v3d_sync_bo_ioctl(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv)
+{
+       struct drm_v3d_sync_bo *args = data;
+       struct drm_v3d_bo_sync_op *ops;
+       struct drm_gem_object *obj;
+       int ret = 0;
+
+       if (args->pad)
+               return -EINVAL;
+
+       if (!args->op_count)
+               return 0;
+
+       /* Limits the number of sync operations requested by userspace. */
+       if (args->op_count > SZ_64K) {
+               drm_dbg(dev, "Too many sync ops: %u\n", args->op_count);
+               return -EINVAL;
+       }
+
+       ops = vmemdup_array_user(u64_to_user_ptr(args->ops), args->op_count,
+                                sizeof(*ops));
+       if (IS_ERR(ops))
+               return PTR_ERR(ops);
+
+       for (u32 i = 0; i < args->op_count; i++) {
+               obj = drm_gem_object_lookup(file_priv, ops[i].handle);
+               if (!obj) {
+                       drm_dbg(dev, "Failed to look up GEM BO %d\n",
+                               ops[i].handle);
+                       ret = -ENOENT;
+                       break;
+               }
+
+               ret = v3d_bo_sync(obj, &ops[i]);
+               drm_gem_object_put(obj);
+               if (ret)
+                       break;
+       }
+
+       kvfree(ops);
+       return ret;
+}
+
 int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data,
                            struct drm_file *file_priv)
 {
diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c
index 7d91c528c1e9..9e6e4f832eee 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.c
+++ b/drivers/gpu/drm/v3d/v3d_drv.c
@@ -267,6 +267,7 @@ static const struct drm_ioctl_desc v3d_drm_ioctls[] = {
        DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CPU, v3d_submit_cpu_ioctl, 
DRM_RENDER_ALLOW | DRM_AUTH),
        DRM_IOCTL_DEF_DRV(V3D_PERFMON_GET_COUNTER, 
v3d_perfmon_get_counter_ioctl, DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF_DRV(V3D_PERFMON_SET_GLOBAL, v3d_perfmon_set_global_ioctl, 
DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(V3D_SYNC_BO, v3d_sync_bo_ioctl, DRM_RENDER_ALLOW),
 };
 
 static const struct drm_driver v3d_drm_driver = {
diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index 4f32fc3ab1de..e5fb3a7b8d96 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -602,6 +602,8 @@ int v3d_get_bo_offset_ioctl(struct drm_device *dev, void 
*data,
                            struct drm_file *file_priv);
 int v3d_wait_bo_ioctl(struct drm_device *dev, void *data,
                      struct drm_file *file_priv);
+int v3d_sync_bo_ioctl(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv);
 struct drm_gem_object *v3d_prime_import_sg_table(struct drm_device *dev,
                                                 struct dma_buf_attachment 
*attach,
                                                 struct sg_table *sgt);
diff --git a/include/uapi/drm/v3d_drm.h b/include/uapi/drm/v3d_drm.h
index 4221c7544d08..c2f7f1409ae4 100644
--- a/include/uapi/drm/v3d_drm.h
+++ b/include/uapi/drm/v3d_drm.h
@@ -44,6 +44,7 @@ extern "C" {
 #define DRM_V3D_SUBMIT_CPU                        0x0b
 #define DRM_V3D_PERFMON_GET_COUNTER               0x0c
 #define DRM_V3D_PERFMON_SET_GLOBAL                0x0d
+#define DRM_V3D_SYNC_BO                           0x0e
 
 #define DRM_IOCTL_V3D_SUBMIT_CL           DRM_IOWR(DRM_COMMAND_BASE + 
DRM_V3D_SUBMIT_CL, struct drm_v3d_submit_cl)
 #define DRM_IOCTL_V3D_WAIT_BO             DRM_IOWR(DRM_COMMAND_BASE + 
DRM_V3D_WAIT_BO, struct drm_v3d_wait_bo)
@@ -64,6 +65,7 @@ extern "C" {
                                                   struct 
drm_v3d_perfmon_get_counter)
 #define DRM_IOCTL_V3D_PERFMON_SET_GLOBAL  DRM_IOW(DRM_COMMAND_BASE + 
DRM_V3D_PERFMON_SET_GLOBAL, \
                                                   struct 
drm_v3d_perfmon_set_global)
+#define DRM_IOCTL_V3D_SYNC_BO             DRM_IOW(DRM_COMMAND_BASE + 
DRM_V3D_SYNC_BO, struct drm_v3d_sync_bo)
 
 #define DRM_V3D_SUBMIT_CL_FLUSH_CACHE             0x01
 #define DRM_V3D_SUBMIT_EXTENSION                 0x02
@@ -796,6 +798,52 @@ struct drm_v3d_perfmon_set_global {
        __u32 id;
 };
 
+/* Valid types to pass to drm_v3d_bo_sync_op */
+#define DRM_V3D_BO_SYNC_CPU_CACHE_FLUSH                0
+#define DRM_V3D_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE 1
+
+/**
+ * struct drm_v3d_bo_sync_op - BO synchronization operation
+ */
+struct drm_v3d_bo_sync_op {
+       /** @handle: Handle of the buffer object to sync. */
+       __u32 handle;
+
+       /** @type: Type of sync operation, DRM_V3D_BO_SYNC_*. */
+       __u32 type;
+
+       /**
+        * @offset: Offset into the BO at which the sync range starts.
+        *
+        * This will be rounded down to the nearest cache line as needed.
+        */
+       __u32 offset;
+
+       /**
+        * @size: Size of the range to sync.
+        *
+        * @size + @offset will be rounded up to the nearest cache line as
+        * needed.
+        */
+       __u32 size;
+};
+
+/**
+ * struct drm_v3d_sync_bo - ioctl argument for CPU cache maintenance on BOs
+ *
+ * Operations on write-combined BOs are no-ops, and imported BOs are rejected.
+ */
+struct drm_v3d_sync_bo {
+       /** @ops: Pointer to an array of struct drm_v3d_bo_sync_op. */
+       __u64 ops;
+
+       /** @op_count: Number of elements in @ops. */
+       __u32 op_count;
+
+       /** @pad: MBZ. */
+       __u32 pad;
+};
+
 #if defined(__cplusplus)
 }
 #endif

-- 
2.56.0

Reply via email to