Commit 8af72338 authored by Danilo Krummrich's avatar Danilo Krummrich

drm/gpuvm: reference count drm_gpuvm structures

Implement reference counting for struct drm_gpuvm.
Acked-by: default avatarChristian König <christian.koenig@amd.com>
Reviewed-by: default avatarThomas Hellström <thomas.hellstrom@linux.intel.com>
Reviewed-by: default avatarBoris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: default avatarDanilo Krummrich <dakr@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20231108001259.15123-10-dakr@redhat.com
parent 266f7618
...@@ -746,6 +746,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, ...@@ -746,6 +746,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
gpuvm->rb.tree = RB_ROOT_CACHED; gpuvm->rb.tree = RB_ROOT_CACHED;
INIT_LIST_HEAD(&gpuvm->rb.list); INIT_LIST_HEAD(&gpuvm->rb.list);
kref_init(&gpuvm->kref);
gpuvm->name = name ? name : "unknown"; gpuvm->name = name ? name : "unknown";
gpuvm->flags = flags; gpuvm->flags = flags;
gpuvm->ops = ops; gpuvm->ops = ops;
...@@ -770,15 +772,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, ...@@ -770,15 +772,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
} }
EXPORT_SYMBOL_GPL(drm_gpuvm_init); EXPORT_SYMBOL_GPL(drm_gpuvm_init);
/** static void
* drm_gpuvm_destroy() - cleanup a &drm_gpuvm drm_gpuvm_fini(struct drm_gpuvm *gpuvm)
* @gpuvm: pointer to the &drm_gpuvm to clean up
*
* Note that it is a bug to call this function on a manager that still
* holds GPU VA mappings.
*/
void
drm_gpuvm_destroy(struct drm_gpuvm *gpuvm)
{ {
gpuvm->name = NULL; gpuvm->name = NULL;
...@@ -790,7 +785,35 @@ drm_gpuvm_destroy(struct drm_gpuvm *gpuvm) ...@@ -790,7 +785,35 @@ drm_gpuvm_destroy(struct drm_gpuvm *gpuvm)
drm_gem_object_put(gpuvm->r_obj); drm_gem_object_put(gpuvm->r_obj);
} }
EXPORT_SYMBOL_GPL(drm_gpuvm_destroy);
static void
drm_gpuvm_free(struct kref *kref)
{
struct drm_gpuvm *gpuvm = container_of(kref, struct drm_gpuvm, kref);
drm_gpuvm_fini(gpuvm);
if (drm_WARN_ON(gpuvm->drm, !gpuvm->ops->vm_free))
return;
gpuvm->ops->vm_free(gpuvm);
}
/**
* drm_gpuvm_put() - drop a struct drm_gpuvm reference
* @gpuvm: the &drm_gpuvm to release the reference of
*
* This releases a reference to @gpuvm.
*
* This function may be called from atomic context.
*/
void
drm_gpuvm_put(struct drm_gpuvm *gpuvm)
{
if (gpuvm)
kref_put(&gpuvm->kref, drm_gpuvm_free);
}
EXPORT_SYMBOL_GPL(drm_gpuvm_put);
static int static int
__drm_gpuva_insert(struct drm_gpuvm *gpuvm, __drm_gpuva_insert(struct drm_gpuvm *gpuvm,
...@@ -839,11 +862,21 @@ drm_gpuva_insert(struct drm_gpuvm *gpuvm, ...@@ -839,11 +862,21 @@ drm_gpuva_insert(struct drm_gpuvm *gpuvm,
{ {
u64 addr = va->va.addr; u64 addr = va->va.addr;
u64 range = va->va.range; u64 range = va->va.range;
int ret;
if (unlikely(!drm_gpuvm_range_valid(gpuvm, addr, range))) if (unlikely(!drm_gpuvm_range_valid(gpuvm, addr, range)))
return -EINVAL; return -EINVAL;
return __drm_gpuva_insert(gpuvm, va); ret = __drm_gpuva_insert(gpuvm, va);
if (likely(!ret))
/* Take a reference of the GPUVM for the successfully inserted
* drm_gpuva. We can't take the reference in
* __drm_gpuva_insert() itself, since we don't want to increse
* the reference count for the GPUVM's kernel_alloc_node.
*/
drm_gpuvm_get(gpuvm);
return ret;
} }
EXPORT_SYMBOL_GPL(drm_gpuva_insert); EXPORT_SYMBOL_GPL(drm_gpuva_insert);
...@@ -876,6 +909,7 @@ drm_gpuva_remove(struct drm_gpuva *va) ...@@ -876,6 +909,7 @@ drm_gpuva_remove(struct drm_gpuva *va)
} }
__drm_gpuva_remove(va); __drm_gpuva_remove(va);
drm_gpuvm_put(va->vm);
} }
EXPORT_SYMBOL_GPL(drm_gpuva_remove); EXPORT_SYMBOL_GPL(drm_gpuva_remove);
......
...@@ -1780,6 +1780,18 @@ nouveau_uvmm_bo_unmap_all(struct nouveau_bo *nvbo) ...@@ -1780,6 +1780,18 @@ nouveau_uvmm_bo_unmap_all(struct nouveau_bo *nvbo)
} }
} }
static void
nouveau_uvmm_free(struct drm_gpuvm *gpuvm)
{
struct nouveau_uvmm *uvmm = uvmm_from_gpuvm(gpuvm);
kfree(uvmm);
}
static const struct drm_gpuvm_ops gpuvm_ops = {
.vm_free = nouveau_uvmm_free,
};
int int
nouveau_uvmm_ioctl_vm_init(struct drm_device *dev, nouveau_uvmm_ioctl_vm_init(struct drm_device *dev,
void *data, void *data,
...@@ -1830,7 +1842,7 @@ nouveau_uvmm_ioctl_vm_init(struct drm_device *dev, ...@@ -1830,7 +1842,7 @@ nouveau_uvmm_ioctl_vm_init(struct drm_device *dev,
NOUVEAU_VA_SPACE_END, NOUVEAU_VA_SPACE_END,
init->kernel_managed_addr, init->kernel_managed_addr,
init->kernel_managed_size, init->kernel_managed_size,
NULL); &gpuvm_ops);
/* GPUVM takes care from here on. */ /* GPUVM takes care from here on. */
drm_gem_object_put(r_obj); drm_gem_object_put(r_obj);
...@@ -1849,8 +1861,7 @@ nouveau_uvmm_ioctl_vm_init(struct drm_device *dev, ...@@ -1849,8 +1861,7 @@ nouveau_uvmm_ioctl_vm_init(struct drm_device *dev,
return 0; return 0;
out_gpuvm_fini: out_gpuvm_fini:
drm_gpuvm_destroy(&uvmm->base); drm_gpuvm_put(&uvmm->base);
kfree(uvmm);
out_unlock: out_unlock:
mutex_unlock(&cli->mutex); mutex_unlock(&cli->mutex);
return ret; return ret;
...@@ -1902,7 +1913,6 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm) ...@@ -1902,7 +1913,6 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm)
mutex_lock(&cli->mutex); mutex_lock(&cli->mutex);
nouveau_vmm_fini(&uvmm->vmm); nouveau_vmm_fini(&uvmm->vmm);
drm_gpuvm_destroy(&uvmm->base); drm_gpuvm_put(&uvmm->base);
kfree(uvmm);
mutex_unlock(&cli->mutex); mutex_unlock(&cli->mutex);
} }
...@@ -247,6 +247,11 @@ struct drm_gpuvm { ...@@ -247,6 +247,11 @@ struct drm_gpuvm {
struct list_head list; struct list_head list;
} rb; } rb;
/**
* @kref: reference count of this object
*/
struct kref kref;
/** /**
* @kernel_alloc_node: * @kernel_alloc_node:
* *
...@@ -273,7 +278,23 @@ void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, ...@@ -273,7 +278,23 @@ void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
u64 start_offset, u64 range, u64 start_offset, u64 range,
u64 reserve_offset, u64 reserve_range, u64 reserve_offset, u64 reserve_range,
const struct drm_gpuvm_ops *ops); const struct drm_gpuvm_ops *ops);
void drm_gpuvm_destroy(struct drm_gpuvm *gpuvm);
/**
* drm_gpuvm_get() - acquire a struct drm_gpuvm reference
* @gpuvm: the &drm_gpuvm to acquire the reference of
*
* This function acquires an additional reference to @gpuvm. It is illegal to
* call this without already holding a reference. No locks required.
*/
static inline struct drm_gpuvm *
drm_gpuvm_get(struct drm_gpuvm *gpuvm)
{
kref_get(&gpuvm->kref);
return gpuvm;
}
void drm_gpuvm_put(struct drm_gpuvm *gpuvm);
bool drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm, u64 addr, u64 range); bool drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm, u64 addr, u64 range);
bool drm_gpuvm_interval_empty(struct drm_gpuvm *gpuvm, u64 addr, u64 range); bool drm_gpuvm_interval_empty(struct drm_gpuvm *gpuvm, u64 addr, u64 range);
...@@ -673,6 +694,14 @@ static inline void drm_gpuva_init_from_op(struct drm_gpuva *va, ...@@ -673,6 +694,14 @@ static inline void drm_gpuva_init_from_op(struct drm_gpuva *va,
* operations to drivers. * operations to drivers.
*/ */
struct drm_gpuvm_ops { struct drm_gpuvm_ops {
/**
* @vm_free: called when the last reference of a struct drm_gpuvm is
* dropped
*
* This callback is mandatory.
*/
void (*vm_free)(struct drm_gpuvm *gpuvm);
/** /**
* @op_alloc: called when the &drm_gpuvm allocates * @op_alloc: called when the &drm_gpuvm allocates
* a struct drm_gpuva_op * a struct drm_gpuva_op
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment