[PATCH drm-misc-next v7 4/7] drm/gpuvm: add an abstraction for a VM / BO combination

Danilo Krummrich dakr at redhat.com
Wed Nov 1 17:21:09 UTC 2023


On 11/1/23 17:38, Thomas Hellström wrote:
> On Tue, 2023-10-31 at 18:38 +0100, Danilo Krummrich wrote:
>> On 10/31/23 11:32, Thomas Hellström wrote:
>>> On Mon, 2023-10-23 at 22:16 +0200, Danilo Krummrich wrote:
>>>> Add 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 at amd.com>
>>>> Signed-off-by: Danilo Krummrich <dakr at redhat.com>
>>>> ---
>>>>    drivers/gpu/drm/drm_gpuvm.c            | 335
>>>> +++++++++++++++++++++--
>>>> --
>>>>    drivers/gpu/drm/nouveau/nouveau_uvmm.c |  64 +++--
>>>>    include/drm/drm_gem.h                  |  32 +--
>>>>    include/drm/drm_gpuvm.h                | 188 +++++++++++++-
>>>>    4 files changed, 533 insertions(+), 86 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/drm_gpuvm.c
>>>> b/drivers/gpu/drm/drm_gpuvm.c
>>>> index c03332883432..7f4f5919f84c 100644
>>>> --- a/drivers/gpu/drm/drm_gpuvm.c
>>>> +++ b/drivers/gpu/drm/drm_gpuvm.c
>>>> @@ -70,6 +70,18 @@
>>>>     * &drm_gem_object, such as the &drm_gem_object containing the
>>>> root
>>>> page table,
>>>>     * but it can also be a 'dummy' object, which can be allocated
>>>> with
>>>>     * drm_gpuvm_resv_object_alloc().
>>>> + *
>>>> + * In order to connect a struct drm_gpuva its backing
>>>> &drm_gem_object each
>>>> + * &drm_gem_object maintains a list of &drm_gpuvm_bo structures,
>>>> and
>>>> each
>>>> + * &drm_gpuvm_bo contains a list of &drm_gpuva structures.
>>>> + *
>>>> + * A &drm_gpuvm_bo is an abstraction that represents a
>>>> combination
>>>> of a
>>>> + * &drm_gpuvm and a &drm_gem_object. Every such combination
>>>> should
>>>> be unique.
>>>> + * This is ensured by the API through drm_gpuvm_bo_obtain() and
>>>> + * drm_gpuvm_bo_obtain_prealloc() which first look into the
>>>> corresponding
>>>> + * &drm_gem_object list of &drm_gpuvm_bos for an existing
>>>> instance
>>>> of this
>>>> + * particular combination. If not existent a new instance is
>>>> created
>>>> and linked
>>>> + * to the &drm_gem_object.
>>>>     */
>>>>    
>>>>    /**
>>>> @@ -395,21 +407,28 @@
>>>>    /**
>>>>     * DOC: Locking
>>>>     *
>>>> - * Generally, the GPU VA manager does not take care of locking
>>>> itself, it is
>>>> - * the drivers responsibility to take care about locking.
>>>> Drivers
>>>> might want to
>>>> - * protect the following operations: inserting, removing and
>>>> iterating
>>>> - * &drm_gpuva objects as well as generating all kinds of
>>>> operations,
>>>> such as
>>>> - * split / merge or prefetch.
>>>> - *
>>>> - * The GPU VA manager also does not take care of the locking of
>>>> the
>>>> backing
>>>> - * &drm_gem_object buffers GPU VA lists by itself; drivers are
>>>> responsible to
>>>> - * enforce mutual exclusion using either the GEMs dma_resv lock
>>>> or
>>>> alternatively
>>>> - * a driver specific external lock. For the latter see also
>>>> - * drm_gem_gpuva_set_lock().
>>>> - *
>>>> - * However, the GPU VA manager contains lockdep checks to ensure
>>>> callers of its
>>>> - * API hold the corresponding lock whenever the &drm_gem_objects
>>>> GPU
>>>> VA list is
>>>> - * accessed by functions such as drm_gpuva_link() or
>>>> drm_gpuva_unlink().
>>>> + * In terms of managing &drm_gpuva entries DRM GPUVM does not
>>>> take
>>>> care of
>>>> + * locking itself, it is the drivers responsibility to take care
>>>> about locking.
>>>> + * Drivers might want to protect the following operations:
>>>> inserting, removing
>>>> + * and iterating &drm_gpuva objects as well as generating all
>>>> kinds
>>>> of
>>>> + * operations, such as split / merge or prefetch.
>>>> + *
>>>> + * DRM GPUVM also does not take care of the locking of the
>>>> backing
>>>> + * &drm_gem_object buffers GPU VA lists and &drm_gpuvm_bo
>>>> abstractions by
>>>> + * itself; drivers are responsible to enforce mutual exclusion
>>>> using
>>>> either the
>>>> + * GEMs dma_resv lock or alternatively a driver specific
>>>> external
>>>> lock. For the
>>>> + * latter see also drm_gem_gpuva_set_lock().
>>>> + *
>>>> + * However, DRM GPUVM contains lockdep checks to ensure callers
>>>> of
>>>> its API hold
>>>> + * the corresponding lock whenever the &drm_gem_objects GPU VA
>>>> list
>>>> is accessed
>>>> + * by functions such as drm_gpuva_link() or drm_gpuva_unlink(),
>>>> but
>>>> also
>>>> + * drm_gpuvm_bo_obtain() and drm_gpuvm_bo_put().
>>>> + *
>>>> + * The latter is required since on creation and destruction of a
>>>> &drm_gpuvm_bo
>>>> + * the &drm_gpuvm_bo is attached / removed from the
>>>> &drm_gem_objects
>>>> gpuva list.
>>>> + * Subsequent calls to drm_gpuvm_bo_obtain() for the same
>>>> &drm_gpuvm
>>>> and
>>>> + * &drm_gem_object must be able to observe previous creations
>>>> and
>>>> destructions
>>>> + * of &drm_gpuvm_bos in order to keep instances unique.
>>>>     */
>>>>    
>>>>    /**
>>>> @@ -439,6 +458,7 @@
>>>>     *     {
>>>>     *             struct drm_gpuva_ops *ops;
>>>>     *             struct drm_gpuva_op *op
>>>> + *             struct drm_gpuvm_bo *vm_bo;
>>>>     *
>>>>     *             driver_lock_va_space();
>>>>     *             ops = drm_gpuvm_sm_map_ops_create(gpuvm, addr,
>>>> range,
>>>> @@ -446,6 +466,10 @@
>>>>     *             if (IS_ERR(ops))
>>>>     *                     return PTR_ERR(ops);
>>>>     *
>>>> + *             vm_bo = drm_gpuvm_bo_obtain(gpuvm, obj);
>>>> + *             if (IS_ERR(vm_bo))
>>>> + *                     return PTR_ERR(vm_bo);
>>>> + *
>>>>     *             drm_gpuva_for_each_op(op, ops) {
>>>>     *                     struct drm_gpuva *va;
>>>>     *
>>>> @@ -458,7 +482,7 @@
>>>>     *
>>>>     *                             driver_vm_map();
>>>>     *                             drm_gpuva_map(gpuvm, va, &op-
>>>>> map);
>>>> - *                             drm_gpuva_link(va);
>>>> + *                             drm_gpuva_link(va, vm_bo);
>>>>     *
>>>>     *                             break;
>>>>     *                     case DRM_GPUVA_OP_REMAP: {
>>>> @@ -485,11 +509,11 @@
>>>>     *                             driver_vm_remap();
>>>>     *                             drm_gpuva_remap(prev, next, &op-
>>>>> remap);
>>>>     *
>>>> - *                             drm_gpuva_unlink(va);
>>>>     *                             if (prev)
>>>> - *                                     drm_gpuva_link(prev);
>>>> + *                                     drm_gpuva_link(prev, va-
>>>>> vm_bo);
>>>>     *                             if (next)
>>>> - *                                     drm_gpuva_link(next);
>>>> + *                                     drm_gpuva_link(next, va-
>>>>> vm_bo);
>>>> + *                             drm_gpuva_unlink(va);
>>>>     *
>>>>     *                             break;
>>>>     *                     }
>>>> @@ -505,6 +529,7 @@
>>>>     *                             break;
>>>>     *                     }
>>>>     *             }
>>>> + *             drm_gpuvm_bo_put(vm_bo);
>>>>     *             driver_unlock_va_space();
>>>>     *
>>>>     *             return 0;
>>>> @@ -514,6 +539,7 @@
>>>>     *
>>>>     *     struct driver_context {
>>>>     *             struct drm_gpuvm *gpuvm;
>>>> + *             struct drm_gpuvm_bo *vm_bo;
>>>>     *             struct drm_gpuva *new_va;
>>>>     *             struct drm_gpuva *prev_va;
>>>>     *             struct drm_gpuva *next_va;
>>>> @@ -534,6 +560,7 @@
>>>>     *                               struct drm_gem_object *obj,
>>>> u64
>>>> offset)
>>>>     *     {
>>>>     *             struct driver_context ctx;
>>>> + *             struct drm_gpuvm_bo *vm_bo;
>>>>     *             struct drm_gpuva_ops *ops;
>>>>     *             struct drm_gpuva_op *op;
>>>>     *             int ret = 0;
>>>> @@ -543,16 +570,23 @@
>>>>     *             ctx.new_va = kzalloc(sizeof(*ctx.new_va),
>>>> GFP_KERNEL);
>>>>     *             ctx.prev_va = kzalloc(sizeof(*ctx.prev_va),
>>>> GFP_KERNEL);
>>>>     *             ctx.next_va = kzalloc(sizeof(*ctx.next_va),
>>>> GFP_KERNEL);
>>>> - *             if (!ctx.new_va || !ctx.prev_va || !ctx.next_va)
>>>> {
>>>> + *             ctx.vm_bo = drm_gpuvm_bo_create(gpuvm, obj);
>>>> + *             if (!ctx.new_va || !ctx.prev_va || !ctx.next_va
>>>> ||
>>>> !vm_bo) {
>>>>     *                     ret = -ENOMEM;
>>>>     *                     goto out;
>>>>     *             }
>>>>     *
>>>> + *             // Typically protected with a driver specific GEM
>>>> gpuva lock
>>>> + *             // used in the fence signaling path for
>>>> drm_gpuva_link() and
>>>> + *             // drm_gpuva_unlink(), hence pre-allocate.
>>>> + *             ctx.vm_bo =
>>>> drm_gpuvm_bo_obtain_prealloc(ctx.vm_bo);
>>>> + *
>>>>     *             driver_lock_va_space();
>>>>     *             ret = drm_gpuvm_sm_map(gpuvm, &ctx, addr, range,
>>>> obj,
>>>> offset);
>>>>     *             driver_unlock_va_space();
>>>>     *
>>>>     *     out:
>>>> + *             drm_gpuvm_bo_put(ctx.vm_bo);
>>>>     *             kfree(ctx.new_va);
>>>>     *             kfree(ctx.prev_va);
>>>>     *             kfree(ctx.next_va);
>>>> @@ -565,7 +599,7 @@
>>>>     *
>>>>     *             drm_gpuva_map(ctx->vm, ctx->new_va, &op->map);
>>>>     *
>>>> - *             drm_gpuva_link(ctx->new_va);
>>>> + *             drm_gpuva_link(ctx->new_va, ctx->vm_bo);
>>>>     *
>>>>     *             // prevent the new GPUVA from being freed in
>>>>     *             // driver_mapping_create()
>>>> @@ -577,22 +611,23 @@
>>>>     *     int driver_gpuva_remap(struct drm_gpuva_op *op, void
>>>> *__ctx)
>>>>     *     {
>>>>     *             struct driver_context *ctx = __ctx;
>>>> + *             struct drm_gpuva *va = op->remap.unmap->va;
>>>>     *
>>>>     *             drm_gpuva_remap(ctx->prev_va, ctx->next_va, &op-
>>>>> remap);
>>>>     *
>>>> - *             drm_gpuva_unlink(op->remap.unmap->va);
>>>> - *             kfree(op->remap.unmap->va);
>>>> - *
>>>>     *             if (op->remap.prev) {
>>>> - *                     drm_gpuva_link(ctx->prev_va);
>>>> + *                     drm_gpuva_link(ctx->prev_va, va->vm_bo);
>>>>     *                     ctx->prev_va = NULL;
>>>>     *             }
>>>>     *
>>>>     *             if (op->remap.next) {
>>>> - *                     drm_gpuva_link(ctx->next_va);
>>>> + *                     drm_gpuva_link(ctx->next_va, va->vm_bo);
>>>>     *                     ctx->next_va = NULL;
>>>>     *             }
>>>>     *
>>>> + *             drm_gpuva_unlink(va);
>>>> + *             kfree(va);
>>>> + *
>>>>     *             return 0;
>>>>     *     }
>>>>     *
>>>> @@ -774,6 +809,194 @@ 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
>>>
>>> Still needs s/Returns:/Return:/g
>>>
>>>> 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;
>>>> +       drm_gem_object_get(obj);
>>>> +
>>>> +       kref_init(&vm_bo->kref);
>>>> +       INIT_LIST_HEAD(&vm_bo->list.gpuva);
>>>> +       INIT_LIST_HEAD(&vm_bo->list.entry.gem);
>>>> +
>>>> +       return vm_bo;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_create);
>>>> +
>>>> +static void
>>>> +drm_gpuvm_bo_destroy(struct kref *kref)
>>>> +{
>>>> +       struct drm_gpuvm_bo *vm_bo = container_of(kref, struct
>>>> drm_gpuvm_bo,
>>>> +                                                 kref);
>>>> +       struct drm_gpuvm *gpuvm = vm_bo->vm;
>>>> +       const struct drm_gpuvm_ops *ops = gpuvm->ops;
>>>> +       struct drm_gem_object *obj = vm_bo->obj;
>>>> +       bool lock = !drm_gpuvm_resv_protected(gpuvm);
>>>> +
>>>> +       if (!lock)
>>>> +               drm_gpuvm_resv_assert_held(gpuvm);
>>>> +
>>>> +       drm_gem_gpuva_assert_lock_held(obj);
>>>> +       list_del(&vm_bo->list.entry.gem);
>>>> +
>>>> +       if (ops && ops->vm_bo_free)
>>>> +               ops->vm_bo_free(vm_bo);
>>>> +       else
>>>> +               kfree(vm_bo);
>>>> +
>>>> +       drm_gem_object_put(obj);
>>>> +}
>>>> +
>>>> +/**
>>>> + * 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.
>>>> + *
>>>> + * If the reference count drops to zero, the &gpuvm_bo is
>>>> destroyed,
>>>> which
>>>> + * includes removing it from the GEMs gpuva list. Hence, if a
>>>> call
>>>> to this
>>>> + * function can potentially let the reference count to zero the
>>>> caller must
>>>> + * hold the dma-resv or driver specific GEM gpuva lock.
>>>> + */
>>>> +void
>>>> +drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo)
>>>> +{
>>>> +       if (vm_bo)
>>>> +               kref_put(&vm_bo->kref, drm_gpuvm_bo_destroy);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put);
>>>> +
>>>> +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_gpuvm_bo(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.
>>>> + *
>>>> + * A new &drm_gpuvm_bo is added to the GEMs gpuva list.
>>>> + *
>>>> + * 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);
>>>> +
>>>> +       drm_gem_gpuva_assert_lock_held(obj);
>>>> +       list_add_tail(&vm_bo->list.entry.gem, &obj->gpuva.list);
>>>> +
>>>> +       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
>>>> + * @__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.
>>>> + *
>>>> + * A new &drm_gpuvm_bo is added to the GEMs gpuva list.
>>>> + *
>>>> + * 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_bo *__vm_bo)
>>>> +{
>>>> +       struct drm_gpuvm *gpuvm = __vm_bo->vm;
>>>> +       struct drm_gem_object *obj = __vm_bo->obj;
>>>> +       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;
>>>> +       }
>>>> +
>>>> +       drm_gem_gpuva_assert_lock_held(obj);
>>>> +       list_add_tail(&__vm_bo->list.entry.gem, &obj-
>>>>> gpuva.list);
>>>> +
>>>> +       return __vm_bo;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_obtain_prealloc);
>>>> +
>>>>    static int
>>>>    __drm_gpuva_insert(struct drm_gpuvm *gpuvm,
>>>>                      struct drm_gpuva *va)
>>>> @@ -864,24 +1087,33 @@ 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;
>>>> +       struct drm_gpuvm *gpuvm = va->vm;
>>>>    
>>>>           if (unlikely(!obj))
>>>>                   return;
>>>>    
>>>> -       drm_gem_gpuva_assert_lock_held(obj);
>>>> +       drm_WARN_ON(gpuvm->drm, obj != vm_bo->obj);
>>>>    
>>>> -       list_add_tail(&va->gem.entry, &obj->gpuva.list);
>>>> +       va->vm_bo = drm_gpuvm_bo_get(vm_bo);
>>>> +
>>>> +       drm_gem_gpuva_assert_lock_held(obj);
>>>> +       list_add_tail(&va->gem.entry, &vm_bo->list.gpuva);
>>>>    }
>>>>    EXPORT_SYMBOL_GPL(drm_gpuva_link);
>>>>    
>>>> @@ -892,20 +1124,31 @@ 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 = va->vm_bo;
>>>>    
>>>>           if (unlikely(!obj))
>>>>                   return;
>>>>    
>>>>           drm_gem_gpuva_assert_lock_held(obj);
>>>> -
>>>>           list_del_init(&va->gem.entry);
>>>> +
>>>> +       va->vm_bo = NULL;
>>>> +       drm_gpuvm_bo_put(vm_bo);
>>>>    }
>>>>    EXPORT_SYMBOL_GPL(drm_gpuva_unlink);
>>>>    
>>>> @@ -1050,10 +1293,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);
>>>> @@ -1695,9 +1938,8 @@ drm_gpuvm_prefetch_ops_create(struct
>>>> drm_gpuvm
>>>> *gpuvm,
>>>>    EXPORT_SYMBOL_GPL(drm_gpuvm_prefetch_ops_create);
>>>>    
>>>>    /**
>>>> - * drm_gpuvm_gem_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
>>>> + * drm_gpuvm_bo_unmap_ops_create() - creates the &drm_gpuva_ops
>>>> to
>>>> unmap a GEM
>>>> + * @vm_bo: the &drm_gpuvm_bo abstraction
>>>>     *
>>>>     * This function creates a list of operations to perform
>>>> unmapping
>>>> for every
>>>>     * GPUVA attached to a GEM.
>>>> @@ -1714,15 +1956,14 @@
>>>> EXPORT_SYMBOL_GPL(drm_gpuvm_prefetch_ops_create);
>>>>     * Returns: a pointer to the &drm_gpuva_ops on success, an
>>>> ERR_PTR
>>>> on failure
>>>>     */
>>>>    struct drm_gpuva_ops *
>>>> -drm_gpuvm_gem_unmap_ops_create(struct drm_gpuvm *gpuvm,
>>>> -                              struct drm_gem_object *obj)
>>>> +drm_gpuvm_bo_unmap_ops_create(struct drm_gpuvm_bo *vm_bo)
>>>>    {
>>>>           struct drm_gpuva_ops *ops;
>>>>           struct drm_gpuva_op *op;
>>>>           struct drm_gpuva *va;
>>>>           int ret;
>>>>    
>>>> -       drm_gem_gpuva_assert_lock_held(obj);
>>>> +       drm_gem_gpuva_assert_lock_held(vm_bo->obj);
>>>>    
>>>>           ops = kzalloc(sizeof(*ops), GFP_KERNEL);
>>>>           if (!ops)
>>>> @@ -1730,8 +1971,8 @@ drm_gpuvm_gem_unmap_ops_create(struct
>>>> drm_gpuvm
>>>> *gpuvm,
>>>>    
>>>>           INIT_LIST_HEAD(&ops->list);
>>>>    
>>>> -       drm_gem_for_each_gpuva(va, obj) {
>>>> -               op = gpuva_op_alloc(gpuvm);
>>>> +       drm_gpuvm_bo_for_each_va(va, vm_bo) {
>>>> +               op = gpuva_op_alloc(vm_bo->vm);
>>>>                   if (!op) {
>>>>                           ret = -ENOMEM;
>>>>                           goto err_free_ops;
>>>> @@ -1745,10 +1986,10 @@ drm_gpuvm_gem_unmap_ops_create(struct
>>>> drm_gpuvm *gpuvm,
>>>>           return ops;
>>>>    
>>>>    err_free_ops:
>>>> -       drm_gpuva_ops_free(gpuvm, ops);
>>>> +       drm_gpuva_ops_free(vm_bo->vm, ops);
>>>>           return ERR_PTR(ret);
>>>>    }
>>>> -EXPORT_SYMBOL_GPL(drm_gpuvm_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 ed439bf4032f..1e95b0a1b047 100644
>>>> --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
>>>> +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
>>>> @@ -62,6 +62,8 @@ struct bind_job_op {
>>>>           enum vm_bind_op op;
>>>>           u32 flags;
>>>>    
>>>> +       struct drm_gpuvm_bo *vm_bo;
>>>> +
>>>>           struct {
>>>>                   u64 addr;
>>>>                   u64 range;
>>>> @@ -1113,22 +1115,28 @@ 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->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;
>>>> +
>>>>                           if (op->remap.prev)
>>>> -                               drm_gpuva_link(&new->prev->va);
>>>> +                               drm_gpuva_link(&new->prev->va,
>>>> 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,
>>>> va-
>>>>> vm_bo);
>>>> +                       drm_gpuva_unlink(va);
>>>>                           break;
>>>> +               }
>>>>                   case DRM_GPUVA_OP_UNMAP:
>>>>                           drm_gpuva_unlink(op->unmap.va);
>>>>                           break;
>>>> @@ -1150,10 +1158,18 @@ 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 (!(op->gem.obj = obj))
>>>>                                   return -ENOENT;
>>>> +
>>>> +                       dma_resv_lock(obj->resv, NULL);
>>>> +                       op->vm_bo = drm_gpuvm_bo_obtain(&uvmm-
>>>>> base,
>>>> obj);
>>>> +                       dma_resv_unlock(obj->resv);
>>>> +                       if (IS_ERR(op->vm_bo))
>>>> +                               return PTR_ERR(op->vm_bo);
>>>>                   }
>>>>    
>>>>                   ret = bind_validate_op(job, op);
>>>> @@ -1364,7 +1380,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 +1527,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->vm_bo)) {
>>>> +                       dma_resv_lock(obj->resv, NULL);
>>>> +                       drm_gpuvm_bo_put(op->vm_bo);
>>>> +                       dma_resv_unlock(obj->resv);
>>>> +               }
>>>> +
>>>>                   if (obj)
>>>>                           drm_gem_object_put(obj);
>>>>           }
>>>> @@ -1776,15 +1798,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_gpuvm_bo(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 +1817,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_gpuvm_bo(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);
>>>> +               }
>>>>           }
>>>>    }
>>>>    
>>>> diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h
>>>> index 16364487fde9..369505447acd 100644
>>>> --- a/include/drm/drm_gem.h
>>>> +++ b/include/drm/drm_gem.h
>>>> @@ -580,7 +580,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.
>>>> @@ -593,28 +593,28 @@ 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_gpuvm_bo() - 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_gpuvm_bo(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_gpuvm_bo_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_gpuvm_bo_safe(entry__, next__, obj__) \
>>>> +       list_for_each_entry_safe(entry__, next__, &(obj__)-
>>>>> gpuva.list, list.entry.gem)
>>>>    
>>>>    #endif /* __DRM_GEM_H__ */
>>>> diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h
>>>> index 47cbacb244b9..466fdd76c71a 100644
>>>> --- a/include/drm/drm_gpuvm.h
>>>> +++ b/include/drm/drm_gpuvm.h
>>>> @@ -25,6 +25,7 @@
>>>>     * OTHER DEALINGS IN THE SOFTWARE.
>>>>     */
>>>>    
>>>> +#include <linux/dma-resv.h>
>>>>    #include <linux/list.h>
>>>>    #include <linux/rbtree.h>
>>>>    #include <linux/types.h>
>>>> @@ -33,6 +34,7 @@
>>>>    #include <drm/drm_gem.h>
>>>>    
>>>>    struct drm_gpuvm;
>>>> +struct drm_gpuvm_bo;
>>>>    struct drm_gpuvm_ops;
>>>>    
>>>>    /**
>>>> @@ -73,6 +75,12 @@ struct drm_gpuva {
>>>>            */
>>>>           struct drm_gpuvm *vm;
>>>>    
>>>> +       /**
>>>> +        * @vm_bo: the &drm_gpuvm_bo abstraction for the mapped
>>>> +        * &drm_gem_object
>>>> +        */
>>>> +       struct drm_gpuvm_bo *vm_bo;
>>>> +
>>>>           /**
>>>>            * @flags: the &drm_gpuva_flags for this mapping
>>>>            */
>>>> @@ -108,7 +116,7 @@ struct drm_gpuva {
>>>>                   struct drm_gem_object *obj;
>>>>    
>>>>                   /**
>>>> -                * @entry: the &list_head to attach this object
>>>> to a
>>>> &drm_gem_object
>>>> +                * @entry: the &list_head to attach this object
>>>> to a
>>>> &drm_gpuvm_bo
>>>>                    */
>>>>                   struct list_head entry;
>>>>           } gem;
>>>> @@ -141,7 +149,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,
>>>> @@ -188,10 +196,16 @@ static inline bool
>>>> drm_gpuva_invalidated(struct
>>>> drm_gpuva *va)
>>>>     * enum drm_gpuvm_flags - flags for struct drm_gpuvm
>>>>     */
>>>>    enum drm_gpuvm_flags {
>>>> +       /**
>>>> +        * @DRM_GPUVM_RESV_PROTECTED: GPUVM is protected
>>>> externally
>>>> by the
>>>> +        * GPUVM's &dma_resv lock
>>>> +        */
>>>> +       DRM_GPUVM_RESV_PROTECTED = BIT(0),
>>>> +
>>>>           /**
>>>>            * @DRM_GPUVM_USERBITS: user defined bits
>>>>            */
>>>> -       DRM_GPUVM_USERBITS = BIT(0),
>>>> +       DRM_GPUVM_USERBITS = BIT(1),
>>>>    };
>>>>    
>>>>    /**
>>>> @@ -280,6 +294,19 @@ bool drm_gpuvm_interval_empty(struct
>>>> drm_gpuvm
>>>> *gpuvm, u64 addr, u64 range);
>>>>    struct drm_gem_object *
>>>>    drm_gpuvm_resv_object_alloc(struct drm_device *drm);
>>>>    
>>>> +/**
>>>> + * drm_gpuvm_resv_protected() - indicates whether
>>>> &DRM_GPUVM_RESV_PROTECTED is
>>>> + * set
>>>> + * @gpuvm: the &drm_gpuvm
>>>> + *
>>>> + * Returns: true if &DRM_GPUVM_RESV_PROTECTED is set, false
>>>> otherwise.
>>>> + */
>>>> +static inline bool
>>>> +drm_gpuvm_resv_protected(struct drm_gpuvm *gpuvm)
>>>> +{
>>>> +       return gpuvm->flags & DRM_GPUVM_RESV_PROTECTED;
>>>> +}
>>>> +
>>>>    /**
>>>>     * drm_gpuvm_resv() - returns the &drm_gpuvm's &dma_resv
>>>>     * @gpuvm__: the &drm_gpuvm
>>>> @@ -298,6 +325,12 @@ drm_gpuvm_resv_object_alloc(struct
>>>> drm_device
>>>> *drm);
>>>>     */
>>>>    #define drm_gpuvm_resv_obj(gpuvm__) ((gpuvm__)->r_obj)
>>>>    
>>>> +#define drm_gpuvm_resv_held(gpuvm__) \
>>>> +       dma_resv_held(drm_gpuvm_resv(gpuvm__))
>>>> +
>>>> +#define drm_gpuvm_resv_assert_held(gpuvm__) \
>>>> +       dma_resv_assert_held(drm_gpuvm_resv(gpuvm__))
>>>> +
>>>>    #define drm_gpuvm_resv_held(gpuvm__) \
>>>>           dma_resv_held(drm_gpuvm_resv(gpuvm__))
>>>>    
>>>> @@ -382,6 +415,128 @@ __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 {
>>>> +       /**
>>>> +        * @vm: The &drm_gpuvm the @obj is mapped in. This
>>>> pointer is
>>>> not
>>>> +        * reference counted.
>>>> +        *
>>>> +        * A struct drm_gpuvm_bo is not allowed to out-live its
>>>> &drm_gpuvm
>>>> +        * context. Implicitly, this is ensured by the fact that
>>>> the
>>>> driver is
>>>> +        * responsible to ensure the VM doesn't contain mappings
>>>> once
>>>> it's
>>>> +        * freed, since a struct drm_gpuvm_bo should be freed
>>>> once
>>>> the last
>>>> +        * mapping being backed by the corresponding buffer
>>>> object is
>>>> unmapped.
>>>> +        */
>>>
>>>
>>> I don't think the above is completely true. Let's assume in the
>>> !RESV_PROTECTED case that a reference is grabbed on the
>>> drm_gpuvm_bo
>>> during an iteration over a list. Then user-space closes the vm and
>>> all
>>> vmas are unlinked, but this reference remains but the vm pointer
>>> becomes stale. In the RESV_PROTECTED case this is ensured not to
>>> happen
>>> if by the vm->resv being grabbed during unlink, but in the
>>> !RESV_PROTECTED case, the above wording isn't sufficient. The
>>> caller
>>> needs to ensure the vm stays alive using some sort of similar rule
>>> or
>>> use kref_get_unless_zero() on the vm under the spinlock if
>>> dereferenced.
>>
>> The list is part of the GPUVM. Hence, the caller *must* either
>> already hold
>> a reference to the GPUVM or otherwise ensure it's not freed while
>> iterating
>> this list. All the drm_gpuvm_bo structures within this list can't
>> have a
>> pointer to another VM than this one by definition.
>>
>> Anyway, I recognize that this isn't very obvious. Hence, I think we
>> should
>> probably reference count GPUVMs as well. I'd think of the same way we
>> do it
>> with drm_gem_objects. However, I'd prefer to introduce this with a
>> subsequent
>> patch.
> 
> Well, I think we should actually be OK in most cases, and refcounting
> here would probably result in circular dependencies.

Where would you see a circular dependency with reference counted GPUVMs?

Actually, I already started implementing it, because I think it's really
what we should do.

> 
> I think to do this properly one would document that this pointer is not
> refecounted and that dereferencing that pointer requires a strong vm
> reference from elsewhere, or holding the bo resv and verifying that the
> gpuvm_bo is on the gem object's gpuvm_bo list.

Yeah, I think the comment above coveres that. However, I probably even want
to introduce reference counting already in this series, hence this and the
below would just go away.

> 
> We've had a lot of tricky lifetime problems of vms and vmas in the i915
> driver so that's why I think clearly documenting the rules for
> dereferencing is important. In particular if we, in the future provide
> some sort of iteration over the gem object's gpvum_bo list, dropping
> the lock while iterating, that will blow up.
> 
> /Thomas
> 
> 
>>
>>>
>>>> +       struct drm_gpuvm *vm;
>>>> +
>>>> +       /**
>>>> +        * @obj: The &drm_gem_object being mapped in @vm. This is
>>>> a
>>>> reference
>>>> +        * counted pointer.
>>>> +        */
>>>> +       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;
>>>
>>> Still missing doc on how the @gpuva stays alive during iteration
>>> over
>>> the list?
>>
>> Thanks for pointing this out again, I missed that one.
>>
>> - Danilo
>>
>>>
>>>
>>>
>>> 8<-------------------------------------------------------------
>>>
>>> Thanks,
>>> Thomas
>>>
>>
> 



More information about the dri-devel mailing list