lkml.org 
[lkml]   [2023]   [Sep]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH drm-misc-next v2 5/7] drm/gpuvm: add an abstraction for a VM / BO combination
    Date
    This patch adds an abstraction layer between the drm_gpuva mappings of
    a particular drm_gem_object and this GEM object itself. The abstraction
    represents a combination of a drm_gem_object and drm_gpuvm. The
    drm_gem_object holds a list of drm_gpuvm_bo structures (the structure
    representing this abstraction), while each drm_gpuvm_bo contains list of
    mappings of this GEM object.

    This has multiple advantages:

    1) We can use the drm_gpuvm_bo structure to attach it to various lists
    of the drm_gpuvm. This is useful for tracking external and evicted
    objects per VM, which is introduced in subsequent patches.

    2) Finding mappings of a certain drm_gem_object mapped in a certain
    drm_gpuvm becomes much cheaper.

    3) Drivers can derive and extend the structure to easily represent
    driver specific states of a BO for a certain GPUVM.

    The idea of this abstraction was taken from amdgpu, hence the credit for
    this idea goes to the developers of amdgpu.

    Cc: Christian König <christian.koenig@amd.com>
    Signed-off-by: Danilo Krummrich <dakr@redhat.com>
    ---
    drivers/gpu/drm/drm_gpuva_mgr.c | 210 +++++++++++++++++++++++--
    drivers/gpu/drm/nouveau/nouveau_uvmm.c | 77 ++++++---
    include/drm/drm_gem.h | 48 ++++--
    include/drm/drm_gpuva_mgr.h | 153 +++++++++++++++++-
    4 files changed, 437 insertions(+), 51 deletions(-)

    diff --git a/drivers/gpu/drm/drm_gpuva_mgr.c b/drivers/gpu/drm/drm_gpuva_mgr.c
    index 838277794990..da7a6e1aabe0 100644
    --- a/drivers/gpu/drm/drm_gpuva_mgr.c
    +++ b/drivers/gpu/drm/drm_gpuva_mgr.c
    @@ -723,6 +723,161 @@ drm_gpuvm_destroy(struct drm_gpuvm *gpuvm)
    }
    EXPORT_SYMBOL_GPL(drm_gpuvm_destroy);

    +/**
    + * drm_gpuvm_bo_create() - create a new instance of struct drm_gpuvm_bo
    + * @gpuvm: The &drm_gpuvm the @obj is mapped in.
    + * @obj: The &drm_gem_object being mapped in the @gpuvm.
    + *
    + * If provided by the driver, this function uses the &drm_gpuvm_ops
    + * vm_bo_alloc() callback to allocate.
    + *
    + * Returns: a pointer to the &drm_gpuvm_bo on success, NULL on failure
    + */
    +struct drm_gpuvm_bo *
    +drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm,
    + struct drm_gem_object *obj)
    +{
    + const struct drm_gpuvm_ops *ops = gpuvm->ops;
    + struct drm_gpuvm_bo *vm_bo;
    +
    + if (ops && ops->vm_bo_alloc)
    + vm_bo = ops->vm_bo_alloc();
    + else
    + vm_bo = kzalloc(sizeof(*vm_bo), GFP_KERNEL);
    +
    + if (unlikely(!vm_bo))
    + return NULL;
    +
    + vm_bo->vm = gpuvm;
    + vm_bo->obj = obj;
    +
    + kref_init(&vm_bo->kref);
    + INIT_LIST_HEAD(&vm_bo->list.gpuva);
    + INIT_LIST_HEAD(&vm_bo->list.entry.gem);
    +
    + drm_gem_object_get(obj);
    +
    + return vm_bo;
    +}
    +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_create);
    +
    +void
    +drm_gpuvm_bo_destroy(struct kref *kref)
    +{
    + struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo,
    + kref);
    + const struct drm_gpuvm_ops *ops = vm_bo->vm->ops;
    +
    + drm_gem_object_put(vm_bo->obj);
    +
    + if (ops && ops->vm_bo_free)
    + ops->vm_bo_free(vm_bo);
    + else
    + kfree(vm_bo);
    +}
    +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_destroy);
    +
    +static struct drm_gpuvm_bo *
    +__drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm,
    + struct drm_gem_object *obj)
    +{
    + struct drm_gpuvm_bo *vm_bo;
    +
    + drm_gem_gpuva_assert_lock_held(obj);
    +
    + drm_gem_for_each_gpuva_gem(vm_bo, obj)
    + if (vm_bo->vm == gpuvm)
    + return vm_bo;
    +
    + return NULL;
    +}
    +
    +/**
    + * drm_gpuvm_bo_find() - find the &drm_gpuvm_bo for the given
    + * &drm_gpuvm and &drm_gem_object
    + * @gpuvm: The &drm_gpuvm the @obj is mapped in.
    + * @obj: The &drm_gem_object being mapped in the @gpuvm.
    + *
    + * Find the &drm_gpuvm_bo representing the combination of the given
    + * &drm_gpuvm and &drm_gem_object. If found, increases the reference
    + * count of the &drm_gpuvm_bo accordingly.
    + *
    + * Returns: a pointer to the &drm_gpuvm_bo on success, NULL on failure
    + */
    +struct drm_gpuvm_bo *
    +drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm,
    + struct drm_gem_object *obj)
    +{
    + struct drm_gpuvm_bo *vm_bo = __drm_gpuvm_bo_find(gpuvm, obj);
    +
    + return vm_bo ? drm_gpuvm_bo_get(vm_bo) : NULL;
    +}
    +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_find);
    +
    +/**
    + * drm_gpuvm_bo_obtain() - obtains and instance of the &drm_gpuvm_bo for the
    + * given &drm_gpuvm and &drm_gem_object
    + * @gpuvm: The &drm_gpuvm the @obj is mapped in.
    + * @obj: The &drm_gem_object being mapped in the @gpuvm.
    + *
    + * Find the &drm_gpuvm_bo representing the combination of the given
    + * &drm_gpuvm and &drm_gem_object. If found, increases the reference
    + * count of the &drm_gpuvm_bo accordingly. If not found, allocates a new
    + * &drm_gpuvm_bo.
    + *
    + * Returns: a pointer to the &drm_gpuvm_bo on success, an ERR_PTR on failure
    + */
    +struct drm_gpuvm_bo *
    +drm_gpuvm_bo_obtain(struct drm_gpuvm *gpuvm,
    + struct drm_gem_object *obj)
    +{
    + struct drm_gpuvm_bo *vm_bo;
    +
    + vm_bo = drm_gpuvm_bo_find(gpuvm, obj);
    + if (vm_bo)
    + return vm_bo;
    +
    + vm_bo = drm_gpuvm_bo_create(gpuvm, obj);
    + if (!vm_bo)
    + return ERR_PTR(-ENOMEM);
    +
    + return vm_bo;
    +}
    +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_obtain);
    +
    +/**
    + * drm_gpuvm_bo_obtain_prealloc() - obtains and instance of the &drm_gpuvm_bo
    + * for the given &drm_gpuvm and &drm_gem_object
    + * @gpuvm: The &drm_gpuvm the @obj is mapped in.
    + * @obj: The &drm_gem_object being mapped in the @gpuvm.
    + * @__vm_bo: A pre-allocated struct drm_gpuvm_bo.
    + *
    + * Find the &drm_gpuvm_bo representing the combination of the given
    + * &drm_gpuvm and &drm_gem_object. If found, increases the reference
    + * count of the found &drm_gpuvm_bo accordingly, while the @__vm_bo reference
    + * count is decreased. If not found @__vm_bo is returned without further
    + * increase of the reference count.
    + *
    + * Returns: a pointer to the found &drm_gpuvm_bo or @__vm_bo if no existing
    + * &drm_gpuvm_bo was found
    + */
    +struct drm_gpuvm_bo *
    +drm_gpuvm_bo_obtain_prealloc(struct drm_gpuvm *gpuvm,
    + struct drm_gem_object *obj,
    + struct drm_gpuvm_bo *__vm_bo)
    +{
    + struct drm_gpuvm_bo *vm_bo;
    +
    + vm_bo = drm_gpuvm_bo_find(gpuvm, obj);
    + if (vm_bo) {
    + drm_gpuvm_bo_put(__vm_bo);
    + return vm_bo;
    + }
    +
    + return __vm_bo;
    +}
    +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_obtain_prealloc);
    +
    static int
    __drm_gpuva_insert(struct drm_gpuvm *gpuvm,
    struct drm_gpuva *va)
    @@ -812,15 +967,20 @@ EXPORT_SYMBOL_GPL(drm_gpuva_remove);
    /**
    * drm_gpuva_link() - link a &drm_gpuva
    * @va: the &drm_gpuva to link
    + * @vm_bo: the &drm_gpuvm_bo to add the &drm_gpuva to
    *
    - * This adds the given &va to the GPU VA list of the &drm_gem_object it is
    - * associated with.
    + * This adds the given &va to the GPU VA list of the &drm_gpuvm_bo and the
    + * &drm_gpuvm_bo to the &drm_gem_object it is associated with.
    + *
    + * For every &drm_gpuva entry added to the &drm_gpuvm_bo an additional
    + * reference of the latter is taken.
    *
    * This function expects the caller to protect the GEM's GPUVA list against
    - * concurrent access using the GEMs dma_resv lock.
    + * concurrent access using either the GEMs dma_resv lock or a driver specific
    + * lock set through drm_gem_gpuva_set_lock().
    */
    void
    -drm_gpuva_link(struct drm_gpuva *va)
    +drm_gpuva_link(struct drm_gpuva *va, struct drm_gpuvm_bo *vm_bo)
    {
    struct drm_gem_object *obj = va->gem.obj;

    @@ -829,7 +989,10 @@ drm_gpuva_link(struct drm_gpuva *va)

    drm_gem_gpuva_assert_lock_held(obj);

    - list_add_tail(&va->gem.entry, &obj->gpuva.list);
    + drm_gpuvm_bo_get(vm_bo);
    + list_add_tail(&va->gem.entry, &vm_bo->list.gpuva);
    + if (list_empty(&vm_bo->list.entry.gem))
    + list_add_tail(&vm_bo->list.entry.gem, &obj->gpuva.list);
    }
    EXPORT_SYMBOL_GPL(drm_gpuva_link);

    @@ -840,20 +1003,40 @@ EXPORT_SYMBOL_GPL(drm_gpuva_link);
    * This removes the given &va from the GPU VA list of the &drm_gem_object it is
    * associated with.
    *
    + * This removes the given &va from the GPU VA list of the &drm_gpuvm_bo and
    + * the &drm_gpuvm_bo from the &drm_gem_object it is associated with in case
    + * this call unlinks the last &drm_gpuva from the &drm_gpuvm_bo.
    + *
    + * For every &drm_gpuva entry removed from the &drm_gpuvm_bo a reference of
    + * the latter is dropped.
    + *
    * This function expects the caller to protect the GEM's GPUVA list against
    - * concurrent access using the GEMs dma_resv lock.
    + * concurrent access using either the GEMs dma_resv lock or a driver specific
    + * lock set through drm_gem_gpuva_set_lock().
    */
    void
    drm_gpuva_unlink(struct drm_gpuva *va)
    {
    struct drm_gem_object *obj = va->gem.obj;
    + struct drm_gpuvm_bo *vm_bo;

    if (unlikely(!obj))
    return;

    drm_gem_gpuva_assert_lock_held(obj);

    + vm_bo = __drm_gpuvm_bo_find(va->vm, obj);
    + if (WARN(!vm_bo, "GPUVA doesn't seem to be linked.\n"))
    + return;
    +
    list_del_init(&va->gem.entry);
    +
    + /* This is the last mapping being unlinked for this GEM object, hence
    + * also remove the VM_BO from the GEM's gpuva list.
    + */
    + if (list_empty(&vm_bo->list.gpuva))
    + list_del_init(&vm_bo->list.entry.gem);
    + drm_gpuvm_bo_put(vm_bo);
    }
    EXPORT_SYMBOL_GPL(drm_gpuva_unlink);

    @@ -998,10 +1181,10 @@ drm_gpuva_remap(struct drm_gpuva *prev,
    struct drm_gpuva *next,
    struct drm_gpuva_op_remap *op)
    {
    - struct drm_gpuva *curr = op->unmap->va;
    - struct drm_gpuvm *gpuvm = curr->vm;
    + struct drm_gpuva *va = op->unmap->va;
    + struct drm_gpuvm *gpuvm = va->vm;

    - drm_gpuva_remove(curr);
    + drm_gpuva_remove(va);

    if (op->prev) {
    drm_gpuva_init_from_op(prev, op->prev);
    @@ -1645,7 +1828,7 @@ drm_gpuva_prefetch_ops_create(struct drm_gpuvm *gpuvm,
    EXPORT_SYMBOL_GPL(drm_gpuva_prefetch_ops_create);

    /**
    - * drm_gpuva_gem_unmap_ops_create() - creates the &drm_gpuva_ops to unmap a GEM
    + * drm_gpuvm_bo_unmap_ops_create() - creates the &drm_gpuva_ops to unmap a GEM
    * @gpuvm: the &drm_gpuvm representing the GPU VA space
    * @obj: the &drm_gem_object to unmap
    *
    @@ -1664,11 +1847,12 @@ EXPORT_SYMBOL_GPL(drm_gpuva_prefetch_ops_create);
    * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure
    */
    struct drm_gpuva_ops *
    -drm_gpuva_gem_unmap_ops_create(struct drm_gpuvm *gpuvm,
    +drm_gpuvm_bo_unmap_ops_create(struct drm_gpuvm *gpuvm,
    struct drm_gem_object *obj)
    {
    struct drm_gpuva_ops *ops;
    struct drm_gpuva_op *op;
    + struct drm_gpuvm_bo *vm_bo;
    struct drm_gpuva *va;
    int ret;

    @@ -1680,7 +1864,7 @@ drm_gpuva_gem_unmap_ops_create(struct drm_gpuvm *gpuvm,

    INIT_LIST_HEAD(&ops->list);

    - drm_gem_for_each_gpuva(va, obj) {
    + drm_gem_for_each_gpuva(va, vm_bo, gpuvm, obj) {
    op = gpuva_op_alloc(gpuvm);
    if (!op) {
    ret = -ENOMEM;
    @@ -1698,7 +1882,7 @@ drm_gpuva_gem_unmap_ops_create(struct drm_gpuvm *gpuvm,
    drm_gpuva_ops_free(gpuvm, ops);
    return ERR_PTR(ret);
    }
    -EXPORT_SYMBOL_GPL(drm_gpuva_gem_unmap_ops_create);
    +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_unmap_ops_create);

    /**
    * drm_gpuva_ops_free() - free the given &drm_gpuva_ops
    diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
    index 21d5a06241ae..a93483a4ceb5 100644
    --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
    +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
    @@ -71,6 +71,7 @@ struct bind_job_op {
    u32 handle;
    u64 offset;
    struct drm_gem_object *obj;
    + struct drm_gpuvm_bo *vm_bo;
    } gem;

    struct nouveau_uvma_region *reg;
    @@ -1113,22 +1114,34 @@ bind_validate_region(struct nouveau_job *job)
    }

    static void
    -bind_link_gpuvas(struct drm_gpuva_ops *ops, struct nouveau_uvma_prealloc *new)
    +bind_link_gpuvas(struct bind_job_op *bop)
    {
    + struct nouveau_uvma_prealloc *new = &bop->new;
    + struct drm_gpuvm_bo *vm_bo = bop->gem.vm_bo;
    + struct drm_gpuva_ops *ops = bop->ops;
    struct drm_gpuva_op *op;

    drm_gpuva_for_each_op(op, ops) {
    switch (op->op) {
    case DRM_GPUVA_OP_MAP:
    - drm_gpuva_link(&new->map->va);
    + drm_gpuva_link(&new->map->va, vm_bo);
    break;
    - case DRM_GPUVA_OP_REMAP:
    + case DRM_GPUVA_OP_REMAP: {
    + struct drm_gpuva *va = op->remap.unmap->va;
    + struct drm_gpuvm_bo *vm_bo;
    +
    + vm_bo = drm_gpuvm_bo_find(va->vm, va->gem.obj);
    + BUG_ON(!vm_bo);
    +
    if (op->remap.prev)
    - drm_gpuva_link(&new->prev->va);
    + drm_gpuva_link(&new->prev->va, vm_bo);
    if (op->remap.next)
    - drm_gpuva_link(&new->next->va);
    - drm_gpuva_unlink(op->remap.unmap->va);
    + drm_gpuva_link(&new->next->va, vm_bo);
    + drm_gpuva_unlink(va);
    +
    + drm_gpuvm_bo_put(vm_bo);
    break;
    + }
    case DRM_GPUVA_OP_UNMAP:
    drm_gpuva_unlink(op->unmap.va);
    break;
    @@ -1150,10 +1163,22 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job)

    list_for_each_op(op, &bind_job->ops) {
    if (op->op == OP_MAP) {
    - op->gem.obj = drm_gem_object_lookup(job->file_priv,
    - op->gem.handle);
    - if (!op->gem.obj)
    + struct drm_gem_object *obj;
    +
    + obj = drm_gem_object_lookup(job->file_priv,
    + op->gem.handle);
    + if (!obj)
    return -ENOENT;
    +
    + dma_resv_lock(obj->resv, NULL);
    + op->gem.vm_bo = drm_gpuvm_bo_obtain(&uvmm->base, obj);
    + dma_resv_unlock(obj->resv);
    + if (IS_ERR(op->gem.vm_bo)) {
    + drm_gem_object_put(obj);
    + return PTR_ERR(op->gem.vm_bo);
    + }
    +
    + op->gem.obj = obj;
    }

    ret = bind_validate_op(job, op);
    @@ -1364,7 +1389,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job)
    case OP_UNMAP_SPARSE:
    case OP_MAP:
    case OP_UNMAP:
    - bind_link_gpuvas(op->ops, &op->new);
    + bind_link_gpuvas(op);
    break;
    default:
    break;
    @@ -1511,6 +1536,12 @@ nouveau_uvmm_bind_job_free_work_fn(struct work_struct *work)
    if (!IS_ERR_OR_NULL(op->ops))
    drm_gpuva_ops_free(&uvmm->base, op->ops);

    + if (!IS_ERR_OR_NULL(op->gem.vm_bo)) {
    + dma_resv_lock(obj->resv, NULL);
    + drm_gpuvm_bo_put(op->gem.vm_bo);
    + dma_resv_unlock(obj->resv);
    + }
    +
    if (obj)
    drm_gem_object_put(obj);
    }
    @@ -1776,15 +1807,18 @@ void
    nouveau_uvmm_bo_map_all(struct nouveau_bo *nvbo, struct nouveau_mem *mem)
    {
    struct drm_gem_object *obj = &nvbo->bo.base;
    + struct drm_gpuvm_bo *vm_bo;
    struct drm_gpuva *va;

    dma_resv_assert_held(obj->resv);

    - drm_gem_for_each_gpuva(va, obj) {
    - struct nouveau_uvma *uvma = uvma_from_va(va);
    + drm_gem_for_each_gpuva_gem(vm_bo, obj) {
    + drm_gpuvm_bo_for_each_va(va, vm_bo) {
    + struct nouveau_uvma *uvma = uvma_from_va(va);

    - nouveau_uvma_map(uvma, mem);
    - drm_gpuva_invalidate(va, false);
    + nouveau_uvma_map(uvma, mem);
    + drm_gpuva_invalidate(va, false);
    + }
    }
    }

    @@ -1792,15 +1826,18 @@ void
    nouveau_uvmm_bo_unmap_all(struct nouveau_bo *nvbo)
    {
    struct drm_gem_object *obj = &nvbo->bo.base;
    + struct drm_gpuvm_bo *vm_bo;
    struct drm_gpuva *va;

    dma_resv_assert_held(obj->resv);

    - drm_gem_for_each_gpuva(va, obj) {
    - struct nouveau_uvma *uvma = uvma_from_va(va);
    + drm_gem_for_each_gpuva_gem(vm_bo, obj) {
    + drm_gpuvm_bo_for_each_va(va, vm_bo) {
    + struct nouveau_uvma *uvma = uvma_from_va(va);

    - nouveau_uvma_unmap(uvma);
    - drm_gpuva_invalidate(va, true);
    + nouveau_uvma_unmap(uvma);
    + drm_gpuva_invalidate(va, true);
    + }
    }
    }

    @@ -1847,14 +1884,14 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli,
    kernel_managed_addr, kernel_managed_size,
    NULL, 0, &cli->uvmm.vmm.vmm);
    if (ret)
    - goto out_free_gpuva_mgr;
    + goto out_free_gpuvm;

    cli->uvmm.vmm.cli = cli;
    mutex_unlock(&cli->mutex);

    return 0;

    -out_free_gpuva_mgr:
    +out_free_gpuvm:
    drm_gpuvm_destroy(&uvmm->base);
    out_unlock:
    mutex_unlock(&cli->mutex);
    diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h
    index bc9f6aa2f3fe..7f591e76d74d 100644
    --- a/include/drm/drm_gem.h
    +++ b/include/drm/drm_gem.h
    @@ -571,7 +571,7 @@ int drm_gem_evict(struct drm_gem_object *obj);
    * drm_gem_gpuva_init() - initialize the gpuva list of a GEM object
    * @obj: the &drm_gem_object
    *
    - * This initializes the &drm_gem_object's &drm_gpuva list.
    + * This initializes the &drm_gem_object's &drm_gpuvm_bo list.
    *
    * Calling this function is only necessary for drivers intending to support the
    * &drm_driver_feature DRIVER_GEM_GPUVA.
    @@ -584,28 +584,44 @@ static inline void drm_gem_gpuva_init(struct drm_gem_object *obj)
    }

    /**
    - * drm_gem_for_each_gpuva() - iternator to walk over a list of gpuvas
    - * @entry__: &drm_gpuva structure to assign to in each iteration step
    - * @obj__: the &drm_gem_object the &drm_gpuvas to walk are associated with
    + * drm_gem_for_each_gpuva_gem() - iterator to walk over a list of &drm_gpuvm_bo
    + * @entry__: &drm_gpuvm_bo structure to assign to in each iteration step
    + * @obj__: the &drm_gem_object the &drm_gpuvm_bo to walk are associated with
    *
    - * This iterator walks over all &drm_gpuva structures associated with the
    - * &drm_gpuva_manager.
    + * This iterator walks over all &drm_gpuvm_bo structures associated with the
    + * &drm_gem_object.
    */
    -#define drm_gem_for_each_gpuva(entry__, obj__) \
    - list_for_each_entry(entry__, &(obj__)->gpuva.list, gem.entry)
    +#define drm_gem_for_each_gpuva_gem(entry__, obj__) \
    + list_for_each_entry(entry__, &(obj__)->gpuva.list, list.entry.gem)

    /**
    - * drm_gem_for_each_gpuva_safe() - iternator to safely walk over a list of
    - * gpuvas
    - * @entry__: &drm_gpuva structure to assign to in each iteration step
    - * @next__: &next &drm_gpuva to store the next step
    - * @obj__: the &drm_gem_object the &drm_gpuvas to walk are associated with
    + * drm_gem_for_each_gpuva_gem_safe() - iterator to safely walk over a list of
    + * &drm_gpuvm_bo
    + * @entry__: &drm_gpuvm_bostructure to assign to in each iteration step
    + * @next__: &next &drm_gpuvm_bo to store the next step
    + * @obj__: the &drm_gem_object the &drm_gpuvm_bo to walk are associated with
    *
    - * This iterator walks over all &drm_gpuva structures associated with the
    + * This iterator walks over all &drm_gpuvm_bo structures associated with the
    * &drm_gem_object. It is implemented with list_for_each_entry_safe(), hence
    * it is save against removal of elements.
    */
    -#define drm_gem_for_each_gpuva_safe(entry__, next__, obj__) \
    - list_for_each_entry_safe(entry__, next__, &(obj__)->gpuva.list, gem.entry)
    +#define drm_gem_for_each_gpuva_gem_safe(entry__, next__, obj__) \
    + list_for_each_entry_safe(entry__, next__, &(obj__)->gpuva.list, list.entry.gem)
    +
    +/**
    + * drm_gem_for_each_gpuva() - iterator to walk over a list of &drm_gpuva
    + * @va__: &drm_gpuva structure to assign to in each iteration step
    + * @vm_bo__: the &drm_gpuvm_bo representing the @gpuvm__ and @obj__ combination
    + * @gpuvm__: the &drm_gpuvm the &drm_gpuvas to walk are associated with
    + * @obj__: the &drm_gem_object the &drm_gpuvas to walk are associated with
    + *
    + * This iterator walks over all &drm_gpuva structures associated with the
    + * &drm_gpuvm and &drm_gem_object.
    + */
    +#define drm_gem_for_each_gpuva(va__, vm_bo__, gpuvm__, obj__) \
    + for (vm_bo__ = drm_gpuvm_bo_find(gpuvm__, obj__), \
    + va__ = vm_bo__ ? list_first_entry(&vm_bo__->list.gpuva, typeof(*va__), gem.entry) : NULL; \
    + va__ && !list_entry_is_head(va__, &vm_bo__->list.gpuva, gem.entry); \
    + va__ = list_next_entry(va__, gem.entry))

    #endif /* __DRM_GEM_H__ */
    diff --git a/include/drm/drm_gpuva_mgr.h b/include/drm/drm_gpuva_mgr.h
    index e09e3e3ac5b2..f8f29c1c858d 100644
    --- a/include/drm/drm_gpuva_mgr.h
    +++ b/include/drm/drm_gpuva_mgr.h
    @@ -32,6 +32,7 @@
    #include <drm/drm_gem.h>

    struct drm_gpuvm;
    +struct drm_gpuvm_bo;
    struct drm_gpuvm_ops;

    /**
    @@ -140,7 +141,7 @@ struct drm_gpuva {
    int drm_gpuva_insert(struct drm_gpuvm *gpuvm, struct drm_gpuva *va);
    void drm_gpuva_remove(struct drm_gpuva *va);

    -void drm_gpuva_link(struct drm_gpuva *va);
    +void drm_gpuva_link(struct drm_gpuva *va, struct drm_gpuvm_bo *vm_bo);
    void drm_gpuva_unlink(struct drm_gpuva *va);

    struct drm_gpuva *drm_gpuva_find(struct drm_gpuvm *gpuvm,
    @@ -339,6 +340,130 @@ __drm_gpuva_next(struct drm_gpuva *va)
    #define drm_gpuvm_for_each_va_safe(va__, next__, gpuvm__) \
    list_for_each_entry_safe(va__, next__, &(gpuvm__)->rb.list, rb.entry)

    +/**
    + * struct drm_gpuvm_bo - structure representing a &drm_gpuvm and
    + * &drm_gem_object combination
    + *
    + * This structure is an abstraction representing a &drm_gpuvm and
    + * &drm_gem_object combination. It serves as an indirection to accelerate
    + * iterating all &drm_gpuvas within a &drm_gpuvm backed by the same
    + * &drm_gem_object.
    + *
    + * Furthermore it is used cache evicted GEM objects for a certain GPU-VM to
    + * accelerate validation.
    + *
    + * Typically, drivers want to create an instance of a struct drm_gpuvm_bo once
    + * a GEM object is mapped first in a GPU-VM and release the instance once the
    + * last mapping of the GEM object in this GPU-VM is unmapped.
    + */
    +struct drm_gpuvm_bo {
    +
    + /**
    + * @gpuvm: The &drm_gpuvm the @obj is mapped in.
    + */
    + struct drm_gpuvm *vm;
    +
    + /**
    + * @obj: The &drm_gem_object being mapped in the @gpuvm.
    + */
    + struct drm_gem_object *obj;
    +
    + /**
    + * @kref: The reference count for this &drm_gpuvm_bo.
    + */
    + struct kref kref;
    +
    + /**
    + * @list: Structure containing all &list_heads.
    + */
    + struct {
    + /**
    + * @gpuva: The list of linked &drm_gpuvas.
    + */
    + struct list_head gpuva;
    +
    + /**
    + * @entry: Structure containing all &list_heads serving as
    + * entry.
    + */
    + struct {
    + /**
    + * @gem: List entry to attach to the &drm_gem_objects
    + * gpuva list.
    + */
    + struct list_head gem;
    + } entry;
    + } list;
    +};
    +
    +struct drm_gpuvm_bo *
    +drm_gpuvm_bo_obtain(struct drm_gpuvm *gpuvm,
    + struct drm_gem_object *obj);
    +struct drm_gpuvm_bo *
    +drm_gpuvm_bo_obtain_prealloc(struct drm_gpuvm *gpuvm,
    + struct drm_gem_object *obj,
    + struct drm_gpuvm_bo *__vm_bo);
    +
    +struct drm_gpuvm_bo *
    +drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm,
    + struct drm_gem_object *obj);
    +
    +struct drm_gpuvm_bo *
    +drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm,
    + struct drm_gem_object *obj);
    +void drm_gpuvm_bo_destroy(struct kref *kref);
    +
    +/**
    + * drm_gpuvm_bo_get() - acquire a struct drm_gpuvm_bo reference
    + * @vm_bo: the &drm_gpuvm_bo to acquire the reference of
    + *
    + * This function acquires an additional reference to @vm_bo. It is illegal to
    + * call this without already holding a reference. No locks required.
    + */
    +static inline struct drm_gpuvm_bo *
    +drm_gpuvm_bo_get(struct drm_gpuvm_bo *vm_bo)
    +{
    + kref_get(&vm_bo->kref);
    + return vm_bo;
    +}
    +
    +/**
    + * drm_gpuvm_bo_put() - drop a struct drm_gpuvm_bo reference
    + * @vm_bo: the &drm_gpuvm_bo to release the reference of
    + *
    + * This releases a reference to @vm_bo.
    + */
    +static inline void
    +drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo)
    +{
    + kref_put(&vm_bo->kref, drm_gpuvm_bo_destroy);
    +}
    +
    +/**
    + * drm_gpuvm_bo_for_each_va() - iterator to walk over a list of &drm_gpuva
    + * @va__: &drm_gpuva structure to assign to in each iteration step
    + * @vm_bo__: the &drm_gpuvm_bo the &drm_gpuva to walk are associated with
    + *
    + * This iterator walks over all &drm_gpuva structures associated with the
    + * &drm_gpuvm_bo.
    + */
    +#define drm_gpuvm_bo_for_each_va(va__, vm_bo__) \
    + list_for_each_entry(va__, &(vm_bo)->list.gpuva, gem.entry)
    +
    +/**
    + * drm_gpuvm_bo_for_each_va_safe() - iterator to safely walk over a list of
    + * &drm_gpuva
    + * @va__: &drm_gpuva structure to assign to in each iteration step
    + * @next__: &next &drm_gpuva to store the next step
    + * @vm_bo__: the &drm_gpuvm_bo the &drm_gpuva to walk are associated with
    + *
    + * This iterator walks over all &drm_gpuva structures associated with the
    + * &drm_gpuvm_bo. It is implemented with list_for_each_entry_safe(), hence
    + * it is save against removal of elements.
    + */
    +#define drm_gpuvm_bo_for_each_va_safe(va__, next__, vm_bo__) \
    + list_for_each_entry_safe(va__, next__, &(vm_bo)->list.gpuva, gem.entry)
    +
    /**
    * enum drm_gpuva_op_type - GPU VA operation type
    *
    @@ -608,7 +733,7 @@ drm_gpuva_prefetch_ops_create(struct drm_gpuvm *gpuvm,
    u64 addr, u64 range);

    struct drm_gpuva_ops *
    -drm_gpuva_gem_unmap_ops_create(struct drm_gpuvm *gpuvm,
    +drm_gpuvm_bo_unmap_ops_create(struct drm_gpuvm *gpuvm,
    struct drm_gem_object *obj);

    void drm_gpuva_ops_free(struct drm_gpuvm *gpuvm,
    @@ -653,6 +778,30 @@ struct drm_gpuvm_ops {
    */
    void (*op_free)(struct drm_gpuva_op *op);

    + /**
    + * @vm_bo_alloc: called when the &drm_gpuvm allocates
    + * a struct drm_gpuvm_bo
    + *
    + * Some drivers may want to embed struct drm_gpuvm_bo into driver
    + * specific structures. By implementing this callback drivers can
    + * allocate memory accordingly.
    + *
    + * This callback is optional.
    + */
    + struct drm_gpuvm_bo *(*vm_bo_alloc)(void);
    +
    + /**
    + * @vm_bo_free: called when the &drm_gpuvm frees a
    + * struct drm_gpuvm_bo
    + *
    + * Some drivers may want to embed struct drm_gpuvm_bo into driver
    + * specific structures. By implementing this callback drivers can
    + * free the previously allocated memory accordingly.
    + *
    + * This callback is optional.
    + */
    + void (*vm_bo_free)(struct drm_gpuvm_bo *vm_bo);
    +
    /**
    * @sm_step_map: called from &drm_gpuva_sm_map to finally insert the
    * mapping once all previous steps were completed
    --
    2.41.0
    \
     
     \ /
      Last update: 2023-09-06 23:48    [W:5.488 / U:0.104 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site