Commit baf1638c authored by Himal Prasad Ghimiray's avatar Himal Prasad Ghimiray Committed by Matthew Brost
Browse files

drm/gpuvm: Introduce drm_gpuvm_madvise_ops_create



This ops is used to iterate over GPUVA's in the user-provided range
and split the existing sparse VMA's if the start or end of the input
range lies within it. The operations can create up to 2 REMAPS and 2 MAPs.

The primary use case is for drivers to assign attributes to GPU VAs in
the specified range without performing unmaps or merging mappings,
supporting fine-grained control over sparse va's.

Cc: Danilo Krummrich <dakr@kernel.org>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Boris Brezillon <bbrezillon@kernel.org>
Cc: <dri-devel@lists.freedesktop.org>
Signed-off-by: default avatarHimal Prasad <Ghimiray&lt;himal.prasad.ghimiray@intel.com>
Reviewed-by: default avatarMatthew Brost <matthew.brost@intel.com>
Acked-by: default avatarDanilo Krummrich <dakr@kernel.org>
Signed-off-by: default avatarMatthew Brost <matthew.brost@intel.com>
Link: https://lore.kernel.org/r/20250819162058.2777306-4-himal.prasad.ghimiray@intel.com
parent 33093232
Loading
Loading
Loading
Loading
+188 −37
Original line number Diff line number Diff line
@@ -420,6 +420,71 @@
 *	 new: |-----------|-----| (b.bo_offset=m,a.bo_offset=n+2)
 */

/**
 * DOC: Madvise Logic - Splitting and Traversal
 *
 * This logic handles GPU VA range updates by generating remap and map operations
 * without performing unmaps or merging existing mappings.
 *
 * 1) The requested range lies entirely within a single drm_gpuva. The logic splits
 * the existing mapping at the start and end boundaries and inserts a new map.
 *
 * ::
 *              a      start    end     b
 *         pre: |-----------------------|
 *                     drm_gpuva1
 *
 *              a      start    end     b
 *         new: |-----|=========|-------|
 *               remap   map      remap
 *
 * one REMAP and one MAP : Same behaviour as SPLIT and MERGE
 *
 * 2) The requested range spans multiple drm_gpuva regions. The logic traverses
 * across boundaries, remapping the start and end segments, and inserting two
 * map operations to cover the full range.
 *
 * ::           a       start      b              c        end       d
 *         pre: |------------------|--------------|------------------|
 *                    drm_gpuva1      drm_gpuva2         drm_gpuva3
 *
 *              a       start      b              c        end       d
 *         new: |-------|==========|--------------|========|---------|
 *                remap1   map1       drm_gpuva2    map2     remap2
 *
 * two REMAPS and two MAPS
 *
 * 3) Either start or end lies within a drm_gpuva. A single remap and map operation
 * are generated to update the affected portion.
 *
 *
 * ::           a/start            b              c        end       d
 *         pre: |------------------|--------------|------------------|
 *                    drm_gpuva1      drm_gpuva2         drm_gpuva3
 *
 *              a/start            b              c        end       d
 *         new: |------------------|--------------|========|---------|
 *                drm_gpuva1         drm_gpuva2     map1     remap1
 *
 * ::           a       start      b              c/end              d
 *         pre: |------------------|--------------|------------------|
 *                    drm_gpuva1      drm_gpuva2         drm_gpuva3
 *
 *              a       start      b              c/end              d
 *         new: |-------|==========|--------------|------------------|
 *                remap1   map1       drm_gpuva2        drm_gpuva3
 *
 * one REMAP and one MAP
 *
 * 4) Both start and end align with existing drm_gpuva boundaries. No operations
 * are needed as the range is already covered.
 *
 * 5) No existing drm_gpuvas. No operations.
 *
 * Unlike drm_gpuvm_sm_map_ops_create, this logic avoids unmaps and merging,
 * focusing solely on remap and map operations for efficient traversal and update.
 */

/**
 * DOC: Locking
 *
@@ -2063,6 +2128,9 @@ op_map_cb(const struct drm_gpuvm_ops *fn, void *priv,
{
	struct drm_gpuva_op op = {};

	if (!req)
		return 0;

	op.op = DRM_GPUVA_OP_MAP;
	op.map.va.addr = req->map.va.addr;
	op.map.va.range = req->map.va.range;
@@ -2092,10 +2160,13 @@ op_remap_cb(const struct drm_gpuvm_ops *fn, void *priv,

static int
op_unmap_cb(const struct drm_gpuvm_ops *fn, void *priv,
	    struct drm_gpuva *va, bool merge)
	    struct drm_gpuva *va, bool merge, bool madvise)
{
	struct drm_gpuva_op op = {};

	if (madvise)
		return 0;

	op.op = DRM_GPUVA_OP_UNMAP;
	op.unmap.va = va;
	op.unmap.keep = merge;
@@ -2106,11 +2177,12 @@ op_unmap_cb(const struct drm_gpuvm_ops *fn, void *priv,
static int
__drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
		   const struct drm_gpuvm_ops *ops, void *priv,
		   const struct drm_gpuvm_map_req *req)
		   const struct drm_gpuvm_map_req *req,
		   bool madvise)
{
	struct drm_gem_object *req_obj = req->map.gem.obj;
	const struct drm_gpuvm_map_req *op_map = madvise ? NULL : req;
	struct drm_gpuva *va, *next;

	u64 req_offset = req->map.gem.offset;
	u64 req_range = req->map.va.range;
	u64 req_addr = req->map.va.addr;
@@ -2128,19 +2200,22 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
		u64 end = addr + range;
		bool merge = !!va->gem.obj;

		if (madvise && obj)
			continue;

		if (addr == req_addr) {
			merge &= obj == req_obj &&
				 offset == req_offset;

			if (end == req_end) {
				ret = op_unmap_cb(ops, priv, va, merge);
				ret = op_unmap_cb(ops, priv, va, merge, madvise);
				if (ret)
					return ret;
				break;
			}

			if (end < req_end) {
				ret = op_unmap_cb(ops, priv, va, merge);
				ret = op_unmap_cb(ops, priv, va, merge, madvise);
				if (ret)
					return ret;
				continue;
@@ -2161,6 +2236,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
				ret = op_remap_cb(ops, priv, NULL, &n, &u);
				if (ret)
					return ret;

				if (madvise)
					op_map = req;
				break;
			}
		} else if (addr < req_addr) {
@@ -2181,6 +2259,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
				ret = op_remap_cb(ops, priv, &p, NULL, &u);
				if (ret)
					return ret;

				if (madvise)
					op_map = req;
				break;
			}

@@ -2188,6 +2269,18 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
				ret = op_remap_cb(ops, priv, &p, NULL, &u);
				if (ret)
					return ret;

				if (madvise) {
					struct drm_gpuvm_map_req map_req = {
						.map.va.addr =  req_addr,
						.map.va.range = end - req_addr,
					};

					ret = op_map_cb(ops, priv, &map_req);
					if (ret)
						return ret;
				}

				continue;
			}

@@ -2203,6 +2296,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
				ret = op_remap_cb(ops, priv, &p, &n, &u);
				if (ret)
					return ret;

				if (madvise)
					op_map = req;
				break;
			}
		} else if (addr > req_addr) {
@@ -2211,16 +2307,18 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
					   (addr - req_addr);

			if (end == req_end) {
				ret = op_unmap_cb(ops, priv, va, merge);
				ret = op_unmap_cb(ops, priv, va, merge, madvise);
				if (ret)
					return ret;

				break;
			}

			if (end < req_end) {
				ret = op_unmap_cb(ops, priv, va, merge);
				ret = op_unmap_cb(ops, priv, va, merge, madvise);
				if (ret)
					return ret;

				continue;
			}

@@ -2239,12 +2337,20 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
				ret = op_remap_cb(ops, priv, NULL, &n, &u);
				if (ret)
					return ret;

				if (madvise) {
					struct drm_gpuvm_map_req map_req = {
						.map.va.addr =  addr,
						.map.va.range = req_end - addr,
					};

					return op_map_cb(ops, priv, &map_req);
				}
				break;
			}
		}
	}

	return op_map_cb(ops, priv, req);
	return op_map_cb(ops, priv, op_map);
}

static int
@@ -2296,7 +2402,7 @@ __drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,
			if (ret)
				return ret;
		} else {
			ret = op_unmap_cb(ops, priv, va, false);
			ret = op_unmap_cb(ops, priv, va, false, false);
			if (ret)
				return ret;
		}
@@ -2345,7 +2451,7 @@ drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm, void *priv,
		       ops->sm_step_unmap)))
		return -EINVAL;

	return __drm_gpuvm_sm_map(gpuvm, ops, priv, req);
	return __drm_gpuvm_sm_map(gpuvm, ops, priv, req, false);
}
EXPORT_SYMBOL_GPL(drm_gpuvm_sm_map);

@@ -2483,7 +2589,7 @@ drm_gpuvm_sm_map_exec_lock(struct drm_gpuvm *gpuvm,
			return ret;
	}

	return __drm_gpuvm_sm_map(gpuvm, &lock_ops, exec, req);
	return __drm_gpuvm_sm_map(gpuvm, &lock_ops, exec, req, false);

}
EXPORT_SYMBOL_GPL(drm_gpuvm_sm_map_exec_lock);
@@ -2602,6 +2708,38 @@ static const struct drm_gpuvm_ops gpuvm_list_ops = {
	.sm_step_unmap = drm_gpuva_sm_step,
};

static struct drm_gpuva_ops *
__drm_gpuvm_sm_map_ops_create(struct drm_gpuvm *gpuvm,
			      const struct drm_gpuvm_map_req *req,
			      bool madvise)
{
	struct drm_gpuva_ops *ops;
	struct {
		struct drm_gpuvm *vm;
		struct drm_gpuva_ops *ops;
	} args;
	int ret;

	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
	if (unlikely(!ops))
		return ERR_PTR(-ENOMEM);

	INIT_LIST_HEAD(&ops->list);

	args.vm = gpuvm;
	args.ops = ops;

	ret = __drm_gpuvm_sm_map(gpuvm, &gpuvm_list_ops, &args, req, madvise);
	if (ret)
		goto err_free_ops;

	return ops;

err_free_ops:
	drm_gpuva_ops_free(gpuvm, ops);
	return ERR_PTR(ret);
}

/**
 * drm_gpuvm_sm_map_ops_create() - creates the &drm_gpuva_ops to split and merge
 * @gpuvm: the &drm_gpuvm representing the GPU VA space
@@ -2635,34 +2773,47 @@ struct drm_gpuva_ops *
drm_gpuvm_sm_map_ops_create(struct drm_gpuvm *gpuvm,
			    const struct drm_gpuvm_map_req *req)
{
	struct drm_gpuva_ops *ops;
	struct {
		struct drm_gpuvm *vm;
		struct drm_gpuva_ops *ops;
	} args;
	int ret;

	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
	if (unlikely(!ops))
		return ERR_PTR(-ENOMEM);

	INIT_LIST_HEAD(&ops->list);

	args.vm = gpuvm;
	args.ops = ops;

	ret = __drm_gpuvm_sm_map(gpuvm, &gpuvm_list_ops, &args, req);
	if (ret)
		goto err_free_ops;

	return ops;

err_free_ops:
	drm_gpuva_ops_free(gpuvm, ops);
	return ERR_PTR(ret);
	return __drm_gpuvm_sm_map_ops_create(gpuvm, req, false);
}
EXPORT_SYMBOL_GPL(drm_gpuvm_sm_map_ops_create);

/**
 * drm_gpuvm_madvise_ops_create() - creates the &drm_gpuva_ops to split
 * @gpuvm: the &drm_gpuvm representing the GPU VA space
 * @req: map request arguments
 *
 * This function creates a list of operations to perform splitting
 * of existent mapping(s) at start or end, based on the request map.
 *
 * The list can be iterated with &drm_gpuva_for_each_op and must be processed
 * in the given order. It can contain map and remap operations, but it
 * also can be empty if no operation is required, e.g. if the requested mapping
 * already exists is the exact same way.
 *
 * There will be no unmap operations, a maximum of two remap operations and two
 * map operations. The two map operations correspond to: one from start to the
 * end of drm_gpuvaX, and another from the start of drm_gpuvaY to end.
 *
 * Note that before calling this function again with another mapping request it
 * is necessary to update the &drm_gpuvm's view of the GPU VA space. The
 * previously obtained operations must be either processed or abandoned. To
 * update the &drm_gpuvm's view of the GPU VA space drm_gpuva_insert(),
 * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be
 * used.
 *
 * After the caller finished processing the returned &drm_gpuva_ops, they must
 * be freed with &drm_gpuva_ops_free.
 *
 * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure
 */
struct drm_gpuva_ops *
drm_gpuvm_madvise_ops_create(struct drm_gpuvm *gpuvm,
			     const struct drm_gpuvm_map_req *req)
{
	return __drm_gpuvm_sm_map_ops_create(gpuvm, req, true);
}
EXPORT_SYMBOL_GPL(drm_gpuvm_madvise_ops_create);

/**
 * drm_gpuvm_sm_unmap_ops_create() - creates the &drm_gpuva_ops to split on
 * unmap
+3 −0
Original line number Diff line number Diff line
@@ -1062,6 +1062,9 @@ struct drm_gpuvm_map_req {
struct drm_gpuva_ops *
drm_gpuvm_sm_map_ops_create(struct drm_gpuvm *gpuvm,
			    const struct drm_gpuvm_map_req *req);
struct drm_gpuva_ops *
drm_gpuvm_madvise_ops_create(struct drm_gpuvm *gpuvm,
			     const struct drm_gpuvm_map_req *req);

struct drm_gpuva_ops *
drm_gpuvm_sm_unmap_ops_create(struct drm_gpuvm *gpuvm,