mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Gyeyoung Baek <gye976@gmail.com>
To: "Alessio Belle" <alessio.belle@imgtec.com>,
	"Luigi Santivetti" <luigi.santivetti@imgtec.com>,
	"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
	"Maxime Ripard" <mripard@kernel.org>,
	"Thomas Zimmermann" <tzimmermann@suse.de>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Sumit Semwal" <sumit.semwal@linaro.org>,
	"Christian König" <christian.koenig@amd.com>,
	"Philipp Stanner" <phasta@kernel.org>,
	"Donald Robson" <donald.robson@imgtec.com>,
	"Sarah Walker" <sarah.walker@imgtec.com>,
	"Matt Coster" <opensource@mtcoster.net>,
	"Jonathan Corbet" <corbet@lwn.net>,
	"Shuah Khan" <skhan@linuxfoundation.org>,
	"Randy Dunlap" <rdunlap@infradead.org>
Cc: imagination@lists.freedesktop.org,
	dri-devel@lists.freedesktop.org,  linux-kernel@vger.kernel.org,
	linux-media@vger.kernel.org,  linaro-mm-sig@lists.linaro.org,
	linux-doc@vger.kernel.org,  Gyeyoung Baek <gye976@gmail.com>
Subject: [PATCH v2 3/6] drm/imagination: Route map and unmap through pvr_vm_bind()
Date: Sun, 08 Nov 2026 14:16:25 +0900	[thread overview]
Message-ID: <20261108-pvr-vm-bind-v2-3-730103d2cc9a@gmail.com> (raw)
In-Reply-To: <20261108-pvr-vm-bind-v2-0-730103d2cc9a@gmail.com>

Give the VM layer a single kernel-side bind entry point, and move every
existing map and unmap path onto it.

FW mappings and the VM_MAP/VM_UNMAP ioctls each had their own copy of
the same sequence (prepare, lock, execute, clean up). Fold them into one
pvr_vm_bind() taking an array of struct pvr_vm_bind_desc. The VM_BIND
ioctl that follows uses the same entry point.

Before:

  pvr_meta_vm_map() ----+
  pvr_riscv_vm_map() ---+--> pvr_vm_map()
  pvr_ioctl_vm_map() ---+      map_init -> lock -> exec -> unlock -> fini

  pvr_meta_vm_unmap() --+
  pvr_riscv_vm_unmap() -+--> pvr_vm_unmap_obj()
                               unmap_init -> lock -> exec -> unlock -> fini

  pvr_ioctl_vm_unmap() ----> pvr_vm_unmap()
                               unmap_init -> lock -> find -> exec
                                          -> unlock -> fini

After:

  pvr_meta_vm_map() ----+
  pvr_riscv_vm_map() ---+--> pvr_vm_map() ---+
  pvr_ioctl_vm_map() ---+                    |
                                             +--> pvr_vm_bind()
  pvr_meta_vm_unmap() --+                    |      init -> lock -> exec
  pvr_riscv_vm_unmap() -+--> pvr_vm_unmap() -+      -> unlock -> fini
  pvr_ioctl_vm_unmap() -+

- pvr_vm_unmap() sets the exact flag of its descriptor, since VM_UNMAP
  only removes a mapping that exactly matches the range. An unmap
  through pvr_vm_bind() otherwise removes everything in the range,
  because the VM_BIND ioctl that follows takes an arbitrary range, like
  Vulkan sparse unbinding, rather than a previous binding.
- pvr_vm_unmap_all() is the exception and does not go through
  pvr_vm_bind().
  - pvr_vm_bind(): with the asynchronous mode the VM_BIND ioctl gains
    afterwards, a queued request that fails part way through leaves the
    address space in a state nobody can reason about. The VM context is
    then marked unusable, and pvr_vm_bind() refuses it.
  - pvr_vm_unmap_all(): it empties a VM context, so it has to succeed
    whatever state the context is in.
- The only userspace-visible change: VM_UNMAP returns -EINVAL instead of
  -ENOENT for an invalid address or size.

Signed-off-by: Gyeyoung Baek <gye976@gmail.com>
---
 drivers/gpu/drm/imagination/pvr_fw_meta.c  |   6 +-
 drivers/gpu/drm/imagination/pvr_fw_riscv.c |   6 +-
 drivers/gpu/drm/imagination/pvr_vm.c       | 304 +++++++++++++++--------------
 drivers/gpu/drm/imagination/pvr_vm.h       |  46 ++++-
 4 files changed, 205 insertions(+), 157 deletions(-)

diff --git a/drivers/gpu/drm/imagination/pvr_fw_meta.c b/drivers/gpu/drm/imagination/pvr_fw_meta.c
index 38a91dcb66c..a1de51afde1 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_meta.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_meta.c
@@ -542,10 +542,8 @@ pvr_meta_vm_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj)
 static void
 pvr_meta_vm_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj)
 {
-	struct pvr_gem_object *pvr_obj = fw_obj->gem;
-
-	pvr_vm_unmap_obj(pvr_dev->kernel_vm_ctx, pvr_obj,
-			 fw_obj->fw_mm_node.start, fw_obj->fw_mm_node.size);
+	pvr_vm_unmap(pvr_dev->kernel_vm_ctx, fw_obj->fw_mm_node.start,
+		     fw_obj->fw_mm_node.size);
 }
 
 static bool
diff --git a/drivers/gpu/drm/imagination/pvr_fw_riscv.c b/drivers/gpu/drm/imagination/pvr_fw_riscv.c
index cd84635bb88..a22b3f99218 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_riscv.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_riscv.c
@@ -149,10 +149,8 @@ pvr_riscv_vm_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj)
 static void
 pvr_riscv_vm_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj)
 {
-	struct pvr_gem_object *pvr_obj = fw_obj->gem;
-
-	pvr_vm_unmap_obj(pvr_dev->kernel_vm_ctx, pvr_obj,
-			 fw_obj->fw_mm_node.start, fw_obj->fw_mm_node.size);
+	pvr_vm_unmap(pvr_dev->kernel_vm_ctx, fw_obj->fw_mm_node.start,
+		     fw_obj->fw_mm_node.size);
 }
 
 static bool
diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c
index 12ebf0daf88..2f959cd5e90 100644
--- a/drivers/gpu/drm/imagination/pvr_vm.c
+++ b/drivers/gpu/drm/imagination/pvr_vm.c
@@ -21,6 +21,8 @@
 #include <linux/gfp_types.h>
 #include <linux/kref.h>
 #include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
 #include <linux/stddef.h>
 
 /**
@@ -117,11 +119,6 @@ struct pvr_vm_gpuva {
 
 #define to_pvr_vm_gpuva(va) container_of_const(va, struct pvr_vm_gpuva, base)
 
-enum pvr_vm_bind_type {
-	PVR_VM_BIND_TYPE_MAP,
-	PVR_VM_BIND_TYPE_UNMAP,
-};
-
 /**
  * struct pvr_vm_bind_op - Context of a map/unmap operation.
  */
@@ -132,6 +129,9 @@ struct pvr_vm_bind_op {
 	/** @pvr_obj: Object associated with mapping (map only). */
 	struct pvr_gem_object *pvr_obj;
 
+	/** @exact: See &pvr_vm_bind_desc.exact. */
+	bool exact;
+
 	/**
 	 * @vm_ctx: VM context where the mapping will be created or destroyed.
 	 */
@@ -198,6 +198,11 @@ static int pvr_vm_bind_op_exec(struct pvr_vm_bind_op *bind_op)
 	}
 
 	case PVR_VM_BIND_TYPE_UNMAP:
+		if (bind_op->exact &&
+		    !drm_gpuva_find(&bind_op->vm_ctx->gpuvm_mgr,
+				    bind_op->device_addr, bind_op->size))
+			return -ENOENT;
+
 		return drm_gpuvm_sm_unmap(&bind_op->vm_ctx->gpuvm_mgr,
 					  bind_op, bind_op->device_addr,
 					  bind_op->size);
@@ -229,9 +234,9 @@ static void pvr_vm_bind_op_fini(struct pvr_vm_bind_op *bind_op)
 static int
 pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_op,
 			struct pvr_vm_context *vm_ctx,
-			struct pvr_gem_object *pvr_obj, u64 offset,
-			u64 device_addr, u64 size)
+			const struct pvr_vm_bind_desc *desc)
 {
+	struct pvr_gem_object *pvr_obj = desc->obj;
 	struct drm_gem_object *obj = gem_from_pvr_gem(pvr_obj);
 	const bool is_user = vm_ctx != vm_ctx->pvr_dev->kernel_vm_ctx;
 	const u64 pvr_obj_size = pvr_gem_object_size(pvr_obj);
@@ -239,17 +244,19 @@ pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_op,
 	u64 offset_plus_size;
 	int err;
 
-	if (check_add_overflow(offset, size, &offset_plus_size))
+	if (check_add_overflow(desc->offset, desc->size, &offset_plus_size))
 		return -EINVAL;
 
 	if (is_user &&
-	    !pvr_find_heap_containing(vm_ctx->pvr_dev, device_addr, size)) {
+	    !pvr_find_heap_containing(vm_ctx->pvr_dev, desc->device_addr,
+				      desc->size)) {
 		return -EINVAL;
 	}
 
-	if (!pvr_device_addr_and_size_are_valid(vm_ctx, device_addr, size) ||
-	    offset & ~PAGE_MASK || size & ~PAGE_MASK ||
-	    offset >= pvr_obj_size || offset_plus_size > pvr_obj_size)
+	if (!pvr_device_addr_and_size_are_valid(vm_ctx, desc->device_addr,
+						desc->size) ||
+	    desc->offset & ~PAGE_MASK || desc->size & ~PAGE_MASK ||
+	    desc->offset >= pvr_obj_size || offset_plus_size > pvr_obj_size)
 		return -EINVAL;
 
 	bind_op->type = PVR_VM_BIND_TYPE_MAP;
@@ -275,7 +282,9 @@ pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_op,
 		goto err_bind_op_fini;
 
 	bind_op->mmu_op_ctx =
-		pvr_mmu_op_context_create(vm_ctx->mmu_ctx, sgt, device_addr, offset, size);
+		pvr_mmu_op_context_create(vm_ctx->mmu_ctx, sgt,
+					  desc->device_addr, desc->offset,
+					  desc->size);
 	err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx);
 	if (err) {
 		bind_op->mmu_op_ctx = NULL;
@@ -283,17 +292,16 @@ pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_op,
 	}
 
 	bind_op->pvr_obj = pvr_obj;
+	pvr_gem_object_get(pvr_obj);
 	bind_op->vm_ctx = vm_ctx;
-	bind_op->device_addr = device_addr;
-	bind_op->size = size;
-	bind_op->offset = offset;
+	bind_op->device_addr = desc->device_addr;
+	bind_op->size = desc->size;
+	bind_op->offset = desc->offset;
 
 	return 0;
 
 err_bind_op_fini:
 	pvr_vm_bind_op_fini(bind_op);
-	/* Drain what fini() deferred. */
-	drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr);
 
 	return err;
 }
@@ -301,12 +309,12 @@ pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_op,
 static int
 pvr_vm_bind_op_unmap_init(struct pvr_vm_bind_op *bind_op,
 			  struct pvr_vm_context *vm_ctx,
-			  struct pvr_gem_object *pvr_obj,
-			  u64 device_addr, u64 size)
+			  const struct pvr_vm_bind_desc *desc)
 {
 	int err;
 
-	if (!pvr_device_addr_and_size_are_valid(vm_ctx, device_addr, size))
+	if (!pvr_device_addr_and_size_are_valid(vm_ctx, desc->device_addr,
+						desc->size))
 		return -EINVAL;
 
 	bind_op->type = PVR_VM_BIND_TYPE_UNMAP;
@@ -319,17 +327,18 @@ pvr_vm_bind_op_unmap_init(struct pvr_vm_bind_op *bind_op,
 	}
 
 	bind_op->mmu_op_ctx =
-		pvr_mmu_op_context_create(vm_ctx->mmu_ctx, NULL, device_addr, 0, 0);
+		pvr_mmu_op_context_create(vm_ctx->mmu_ctx, NULL,
+					  desc->device_addr, 0, 0);
 	err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx);
 	if (err) {
 		bind_op->mmu_op_ctx = NULL;
 		goto err_bind_op_fini;
 	}
 
-	bind_op->pvr_obj = pvr_obj;
 	bind_op->vm_ctx = vm_ctx;
-	bind_op->device_addr = device_addr;
-	bind_op->size = size;
+	bind_op->exact = desc->exact;
+	bind_op->device_addr = desc->device_addr;
+	bind_op->size = desc->size;
 
 	return 0;
 
@@ -683,188 +692,191 @@ void pvr_destroy_vm_contexts_for_file(struct pvr_file *pvr_file)
 	}
 }
 
-/**
- * pvr_vm_map() - Map a section of physical memory into a section of
- * device-virtual memory.
- * @vm_ctx: Target VM context.
- * @pvr_obj: Target PowerVR memory object.
- * @pvr_obj_offset: Offset into @pvr_obj to map from.
- * @device_addr: Virtual device address at the start of the requested mapping.
- * @size: Size of the requested mapping.
- *
- * No handle is returned to represent the mapping. Instead, callers should
- * remember @device_addr and use that as a handle.
- *
- * Return:
- *  * 0 on success,
- *  * -%EINVAL if @device_addr is not a valid page-aligned device-virtual
- *    address; the region specified by @pvr_obj_offset and @size does not fall
- *    entirely within @pvr_obj, or any part of the specified region of @pvr_obj
- *    is not device-virtual page-aligned,
- *  * Any error encountered while performing internal operations required to
- *    destroy the mapping (returned from pvr_vm_gpuva_map or
- *    pvr_vm_gpuva_remap).
- */
-int
-pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj,
-	   u64 pvr_obj_offset, u64 device_addr, u64 size)
+static int
+pvr_vm_bind_op_init(struct pvr_vm_bind_op *bind_op,
+		    struct pvr_vm_context *vm_ctx,
+		    const struct pvr_vm_bind_desc *desc)
 {
-	struct pvr_vm_bind_op bind_op = {0};
+	switch (desc->type) {
+	case PVR_VM_BIND_TYPE_MAP:
+		if (WARN_ON(!desc->obj || desc->exact))
+			return -EINVAL;
 
-	int err = pvr_vm_bind_op_map_init(&bind_op, vm_ctx, pvr_obj,
-					  pvr_obj_offset, device_addr,
-					  size);
+		return pvr_vm_bind_op_map_init(bind_op, vm_ctx, desc);
 
-	if (err)
-		return err;
+	case PVR_VM_BIND_TYPE_UNMAP:
+		if (WARN_ON(desc->obj))
+			return -EINVAL;
 
-	pvr_gem_object_get(pvr_obj);
+		return pvr_vm_bind_op_unmap_init(bind_op, vm_ctx, desc);
+	}
 
-	mutex_lock(&vm_ctx->lock);
-	err = pvr_vm_bind_op_exec(&bind_op);
-	mutex_unlock(&vm_ctx->lock);
+	return -EINVAL;
+}
 
-	pvr_vm_bind_op_fini(&bind_op);
-	drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr);
+/**
+ * pvr_vm_bind_ops_free() - Release an array of prepared bind operations.
+ * @bind_ops: Array to release.
+ * @count: Number of prepared entries in @bind_ops.
+ */
+static void pvr_vm_bind_ops_free(struct pvr_vm_bind_op *bind_ops, u32 count)
+{
+	for (u32 i = 0; i < count; i++)
+		pvr_vm_bind_op_fini(&bind_ops[i]);
 
-	return err;
+	kvfree(bind_ops);
 }
 
 /**
- * pvr_vm_unmap_obj_locked() - Unmap an already mapped section of device-virtual
- * memory.
+ * pvr_vm_bind_ops_create() - Prepare an array of bind operations.
  * @vm_ctx: Target VM context.
- * @pvr_obj: Target PowerVR memory object.
- * @device_addr: Virtual device address at the start of the target mapping.
- * @size: Size of the target mapping.
+ * @descs: Descriptions of the operations.
+ * @desc_count: Number of entries in @descs.
  *
- * Return:
- *  * 0 on success,
- *  * -%EINVAL if @device_addr is not a valid page-aligned device-virtual
- *    address,
- *  * Any error encountered while performing internal operations required to
- *    destroy the mapping (returned from pvr_vm_gpuva_unmap or
- *    pvr_vm_gpuva_remap).
+ * Performs every allocation the operations need, so that applying them cannot
+ * fail for want of memory.
  *
- * The vm_ctx->lock must be held when calling this function.
+ * Return: The new array, or an ERR_PTR on failure.
  */
-static int
-pvr_vm_unmap_obj_locked(struct pvr_vm_context *vm_ctx,
-			struct pvr_gem_object *pvr_obj,
-			u64 device_addr, u64 size)
+static struct pvr_vm_bind_op *
+pvr_vm_bind_ops_create(struct pvr_vm_context *vm_ctx,
+		       const struct pvr_vm_bind_desc *descs, u32 desc_count)
 {
-	struct pvr_vm_bind_op bind_op = {0};
-
-	int err = pvr_vm_bind_op_unmap_init(&bind_op, vm_ctx, pvr_obj,
-					    device_addr, size);
-	if (err)
-		return err;
+	struct pvr_vm_bind_op *bind_ops;
+	int err;
 
-	pvr_gem_object_get(pvr_obj);
+	bind_ops = kvzalloc_objs(*bind_ops, desc_count, GFP_KERNEL_ACCOUNT);
+	if (!bind_ops)
+		return ERR_PTR(-ENOMEM);
 
-	err = pvr_vm_bind_op_exec(&bind_op);
+	for (u32 prepared = 0; prepared < desc_count; prepared++) {
+		err = pvr_vm_bind_op_init(&bind_ops[prepared], vm_ctx,
+					  &descs[prepared]);
+		if (err) {
+			pvr_vm_bind_ops_free(bind_ops, prepared);
+			/* Release the vm_bos that fini() deferred. */
+			drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr);
+			return ERR_PTR(err);
+		}
 
-	pvr_vm_bind_op_fini(&bind_op);
+		cond_resched();
+	}
 
-	return err;
+	return bind_ops;
 }
 
 /**
- * pvr_vm_unmap_obj() - Unmap an already mapped section of device-virtual
- * memory.
+ * pvr_vm_bind() - Apply a batch of bind operations to a VM context.
  * @vm_ctx: Target VM context.
- * @pvr_obj: Target PowerVR memory object.
- * @device_addr: Virtual device address at the start of the target mapping.
- * @size: Size of the target mapping.
+ * @descs: Operations to apply, in array order.
+ * @desc_count: Number of entries in @descs.
+ *
+ * Not atomic: if an operation fails, the earlier ones stay applied.
  *
  * Return:
- *  * 0 on success,
- *  * Any error encountered by pvr_vm_unmap_obj_locked.
+ *  * 0 on success, or
+ *  * A negative error code on failure.
  */
-int
-pvr_vm_unmap_obj(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj,
-		 u64 device_addr, u64 size)
+int pvr_vm_bind(struct pvr_vm_context *vm_ctx,
+		const struct pvr_vm_bind_desc *descs, u32 desc_count)
 {
-	int err;
+	struct pvr_vm_bind_op *bind_ops;
+	int err = 0;
+
+	bind_ops = pvr_vm_bind_ops_create(vm_ctx, descs, desc_count);
+	if (IS_ERR(bind_ops))
+		return PTR_ERR(bind_ops);
 
 	mutex_lock(&vm_ctx->lock);
-	err = pvr_vm_unmap_obj_locked(vm_ctx, pvr_obj, device_addr, size);
+
+	for (u32 i = 0; !err && i < desc_count; i++)
+		err = pvr_vm_bind_op_exec(&bind_ops[i]);
+
 	mutex_unlock(&vm_ctx->lock);
 
+	pvr_vm_bind_ops_free(bind_ops, desc_count);
 	drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr);
 
 	return err;
 }
 
 /**
- * pvr_vm_unmap() - Unmap an already mapped section of device-virtual memory.
+ * pvr_vm_map() - Map part of an object into a VM context.
+ * @vm_ctx: Target VM context.
+ * @pvr_obj: Target PowerVR memory object.
+ * @pvr_obj_offset: Offset into @pvr_obj to map from.
+ * @device_addr: Virtual device address at the start of the requested mapping.
+ * @size: Size of the requested mapping.
+ *
+ * A wrapper around pvr_vm_bind() with a single map operation.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+int
+pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj,
+	   u64 pvr_obj_offset, u64 device_addr, u64 size)
+{
+	const struct pvr_vm_bind_desc desc = {
+		.type = PVR_VM_BIND_TYPE_MAP,
+		.obj = pvr_obj,
+		.offset = pvr_obj_offset,
+		.device_addr = device_addr,
+		.size = size,
+	};
+
+	return pvr_vm_bind(vm_ctx, &desc, 1);
+}
+
+/**
+ * pvr_vm_unmap() - Remove a mapping from a VM context.
  * @vm_ctx: Target VM context.
  * @device_addr: Virtual device address at the start of the target mapping.
  * @size: Size of the target mapping.
  *
+ * A wrapper around pvr_vm_bind() with a single exact unmap operation.
+ *
  * Return:
  *  * 0 on success,
- *  * Any error encountered by drm_gpuva_find,
- *  * Any error encountered by pvr_vm_unmap_obj_locked.
+ *  * -%ENOENT if no mapping starts at @device_addr and spans exactly @size, or
+ *  * Any other error returned by pvr_vm_bind().
  */
 int
 pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device_addr, u64 size)
 {
-	struct pvr_gem_object *pvr_obj;
-	struct drm_gpuva *va;
-	int err;
-
-	mutex_lock(&vm_ctx->lock);
-
-	va = drm_gpuva_find(&vm_ctx->gpuvm_mgr, device_addr, size);
-	if (va) {
-		pvr_obj = gem_to_pvr_gem(va->gem.obj);
-		err = pvr_vm_unmap_obj_locked(vm_ctx, pvr_obj,
-					      va->va.addr, va->va.range);
-	} else {
-		err = -ENOENT;
-	}
-
-	mutex_unlock(&vm_ctx->lock);
-
-	drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr);
-
-	return err;
+	const struct pvr_vm_bind_desc desc = {
+		.type = PVR_VM_BIND_TYPE_UNMAP,
+		.exact = true,
+		.device_addr = device_addr,
+		.size = size,
+	};
+
+	return pvr_vm_bind(vm_ctx, &desc, 1);
 }
 
 /**
  * pvr_vm_unmap_all() - Unmap all mappings associated with a VM context.
  * @vm_ctx: Target VM context.
  *
- * This function ensures that no mappings are left dangling by unmapping them
- * all in order of ascending device-virtual address.
+ * Unmaps the whole device-virtual address space at once.
  */
 void
 pvr_vm_unmap_all(struct pvr_vm_context *vm_ctx)
 {
-	u64 addr = vm_ctx->gpuvm_mgr.mm_start;
-	u64 end = vm_ctx->gpuvm_mgr.mm_start + vm_ctx->gpuvm_mgr.mm_range;
-	u64 va_addr, va_range;
-
-	mutex_lock(&vm_ctx->lock);
-
-	for (; addr < end; addr = va_addr + va_range) {
-		struct pvr_gem_object *pvr_obj;
-		struct drm_gpuva *va;
-
-		va = drm_gpuva_find_first(&vm_ctx->gpuvm_mgr, addr, end - addr);
-		if (!va)
-			break;
-
-		pvr_obj = gem_to_pvr_gem(va->gem.obj);
-		va_addr = va->va.addr;
-		va_range = va->va.range;
+	const struct pvr_vm_bind_desc desc = {
+		.type = PVR_VM_BIND_TYPE_UNMAP,
+		.device_addr = vm_ctx->gpuvm_mgr.mm_start,
+		.size = vm_ctx->gpuvm_mgr.mm_range,
+	};
+	struct pvr_vm_bind_op bind_op = {0};
 
-		WARN_ON(pvr_vm_unmap_obj_locked(vm_ctx, pvr_obj, va_addr, va_range));
-	}
+	if (WARN_ON(pvr_vm_bind_op_unmap_init(&bind_op, vm_ctx, &desc)))
+		return;
 
+	mutex_lock(&vm_ctx->lock);
+	WARN_ON(pvr_vm_bind_op_exec(&bind_op));
 	mutex_unlock(&vm_ctx->lock);
 
+	pvr_vm_bind_op_fini(&bind_op);
 	drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr);
 }
 
diff --git a/drivers/gpu/drm/imagination/pvr_vm.h b/drivers/gpu/drm/imagination/pvr_vm.h
index b0528dffa7f..bf6d5e802d9 100644
--- a/drivers/gpu/drm/imagination/pvr_vm.h
+++ b/drivers/gpu/drm/imagination/pvr_vm.h
@@ -38,10 +38,50 @@ struct pvr_vm_context *pvr_vm_create_context(struct pvr_device *pvr_dev,
 int pvr_vm_map(struct pvr_vm_context *vm_ctx,
 	       struct pvr_gem_object *pvr_obj, u64 pvr_obj_offset,
 	       u64 device_addr, u64 size);
-int pvr_vm_unmap_obj(struct pvr_vm_context *vm_ctx,
-		     struct pvr_gem_object *pvr_obj,
-		     u64 device_addr, u64 size);
 int pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device_addr, u64 size);
+
+/**
+ * enum pvr_vm_bind_type - Type of a &struct pvr_vm_bind_desc.
+ * @PVR_VM_BIND_TYPE_MAP: Map @pvr_vm_bind_desc.obj into a device-virtual range.
+ * @PVR_VM_BIND_TYPE_UNMAP: Unmap a device-virtual range.
+ */
+enum pvr_vm_bind_type {
+	PVR_VM_BIND_TYPE_MAP,
+	PVR_VM_BIND_TYPE_UNMAP,
+};
+
+/**
+ * struct pvr_vm_bind_desc - A bind operation, as passed to pvr_vm_bind().
+ */
+struct pvr_vm_bind_desc {
+	/** @type: Map or unmap. */
+	enum pvr_vm_bind_type type;
+
+	/**
+	 * @obj: Object to map. Map only; must be %NULL for an unmap.
+	 * pvr_vm_bind() does not consume the caller's reference.
+	 */
+	struct pvr_gem_object *obj;
+
+	/** @offset: Offset into @obj to begin mapping from. Map only. */
+	u64 offset;
+
+	/**
+	 * @exact: Fail with -%ENOENT unless a single mapping spans exactly
+	 * the range. Unmap only.
+	 */
+	bool exact;
+
+	/** @device_addr: Device-virtual address at the start of the range. */
+	u64 device_addr;
+
+	/** @size: Size of the range. */
+	u64 size;
+};
+
+int pvr_vm_bind(struct pvr_vm_context *vm_ctx,
+		const struct pvr_vm_bind_desc *descs, u32 desc_count);
+
 void pvr_vm_unmap_all(struct pvr_vm_context *vm_ctx);
 
 dma_addr_t pvr_vm_get_page_table_root_addr(struct pvr_vm_context *vm_ctx);

-- 
2.43.0


  parent reply	other threads:[~2026-11-08  5:16 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-08  5:16 [PATCH v2 0/6] drm/imagination: Add async VM_BIND with sparse mappings Gyeyoung Baek
2026-11-08  5:16 ` [PATCH v2 1/6] drm/imagination: Switch the GPUVM to immediate mode Gyeyoung Baek
2026-11-08  5:16 ` [PATCH v2 2/6] drm/imagination: Stop locking reservations around map and unmap Gyeyoung Baek
2026-11-08  5:16 ` Gyeyoung Baek [this message]
2026-11-08  5:16 ` [PATCH v2 4/6] drm/imagination: Add VM_BIND ioctl Gyeyoung Baek
2026-11-08  5:16 ` [PATCH v2 5/6] drm/imagination: Add asynchronous VM_BIND Gyeyoung Baek
2026-11-08  5:16 ` [PATCH v2 6/6] drm/imagination: Add MAP_SPARSE to VM_BIND Gyeyoung Baek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261108-pvr-vm-bind-v2-3-730103d2cc9a@gmail.com \
    --to=gye976@gmail.com \
    --cc=airlied@gmail.com \
    --cc=alessio.belle@imgtec.com \
    --cc=christian.koenig@amd.com \
    --cc=corbet@lwn.net \
    --cc=donald.robson@imgtec.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=imagination@lists.freedesktop.org \
    --cc=linaro-mm-sig@lists.linaro.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=luigi.santivetti@imgtec.com \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mripard@kernel.org \
    --cc=opensource@mtcoster.net \
    --cc=phasta@kernel.org \
    --cc=rdunlap@infradead.org \
    --cc=sarah.walker@imgtec.com \
    --cc=simona@ffwll.ch \
    --cc=skhan@linuxfoundation.org \
    --cc=sumit.semwal@linaro.org \
    --cc=tzimmermann@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®