222 lines
5.9 KiB
Diff
222 lines
5.9 KiB
Diff
From 8af72338dd81d1f8667e0240bd28f5fc98b3f20d Mon Sep 17 00:00:00 2001
|
|
From: Danilo Krummrich <dakr@redhat.com>
|
|
Date: Wed, 8 Nov 2023 01:12:39 +0100
|
|
Subject: [PATCH] drm/gpuvm: reference count drm_gpuvm structures
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Implement reference counting for struct drm_gpuvm.
|
|
|
|
Acked-by: Christian König <christian.koenig@amd.com>
|
|
Reviewed-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
|
|
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Signed-off-by: Danilo Krummrich <dakr@redhat.com>
|
|
Link: https://patchwork.freedesktop.org/patch/msgid/20231108001259.15123-10-dakr@redhat.com
|
|
---
|
|
drivers/gpu/drm/drm_gpuvm.c | 56 +++++++++++++++++++++-----
|
|
drivers/gpu/drm/nouveau/nouveau_uvmm.c | 20 ++++++---
|
|
include/drm/drm_gpuvm.h | 31 +++++++++++++-
|
|
3 files changed, 90 insertions(+), 17 deletions(-)
|
|
|
|
--- a/drivers/gpu/drm/drm_gpuvm.c
|
|
+++ b/drivers/gpu/drm/drm_gpuvm.c
|
|
@@ -740,6 +740,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm,
|
|
gpuvm->rb.tree = RB_ROOT_CACHED;
|
|
INIT_LIST_HEAD(&gpuvm->rb.list);
|
|
|
|
+ kref_init(&gpuvm->kref);
|
|
+
|
|
gpuvm->name = name ? name : "unknown";
|
|
gpuvm->flags = flags;
|
|
gpuvm->ops = ops;
|
|
@@ -764,15 +766,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm,
|
|
}
|
|
EXPORT_SYMBOL_GPL(drm_gpuvm_init);
|
|
|
|
-/**
|
|
- * drm_gpuvm_destroy() - cleanup a &drm_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)
|
|
+static void
|
|
+drm_gpuvm_fini(struct drm_gpuvm *gpuvm)
|
|
{
|
|
gpuvm->name = NULL;
|
|
|
|
@@ -784,7 +779,35 @@ drm_gpuvm_destroy(struct drm_gpuvm *gpuv
|
|
|
|
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
|
|
__drm_gpuva_insert(struct drm_gpuvm *gpuvm,
|
|
@@ -833,11 +856,21 @@ drm_gpuva_insert(struct drm_gpuvm *gpuvm
|
|
{
|
|
u64 addr = va->va.addr;
|
|
u64 range = va->va.range;
|
|
+ int ret;
|
|
|
|
if (unlikely(!drm_gpuvm_range_valid(gpuvm, addr, range)))
|
|
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);
|
|
|
|
@@ -870,6 +903,7 @@ drm_gpuva_remove(struct drm_gpuva *va)
|
|
}
|
|
|
|
__drm_gpuva_remove(va);
|
|
+ drm_gpuvm_put(va->vm);
|
|
}
|
|
EXPORT_SYMBOL_GPL(drm_gpuva_remove);
|
|
|
|
--- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
|
|
@@ -1782,6 +1782,18 @@ nouveau_uvmm_bo_unmap_all(struct nouveau
|
|
}
|
|
}
|
|
|
|
+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
|
|
nouveau_uvmm_ioctl_vm_init(struct drm_device *dev,
|
|
void *data,
|
|
@@ -1832,7 +1844,7 @@ nouveau_uvmm_ioctl_vm_init(struct drm_de
|
|
NOUVEAU_VA_SPACE_END,
|
|
init->kernel_managed_addr,
|
|
init->kernel_managed_size,
|
|
- NULL);
|
|
+ &gpuvm_ops);
|
|
/* GPUVM takes care from here on. */
|
|
drm_gem_object_put(r_obj);
|
|
|
|
@@ -1851,8 +1863,7 @@ nouveau_uvmm_ioctl_vm_init(struct drm_de
|
|
return 0;
|
|
|
|
out_gpuvm_fini:
|
|
- drm_gpuvm_destroy(&uvmm->base);
|
|
- kfree(uvmm);
|
|
+ drm_gpuvm_put(&uvmm->base);
|
|
out_unlock:
|
|
mutex_unlock(&cli->mutex);
|
|
return ret;
|
|
@@ -1904,7 +1915,6 @@ nouveau_uvmm_fini(struct nouveau_uvmm *u
|
|
|
|
mutex_lock(&cli->mutex);
|
|
nouveau_vmm_fini(&uvmm->vmm);
|
|
- drm_gpuvm_destroy(&uvmm->base);
|
|
- kfree(uvmm);
|
|
+ drm_gpuvm_put(&uvmm->base);
|
|
mutex_unlock(&cli->mutex);
|
|
}
|
|
--- a/include/drm/drm_gpuvm.h
|
|
+++ b/include/drm/drm_gpuvm.h
|
|
@@ -248,6 +248,11 @@ struct drm_gpuvm {
|
|
} rb;
|
|
|
|
/**
|
|
+ * @kref: reference count of this object
|
|
+ */
|
|
+ struct kref kref;
|
|
+
|
|
+ /**
|
|
* @kernel_alloc_node:
|
|
*
|
|
* &drm_gpuva representing the address space cutout reserved for
|
|
@@ -273,7 +278,23 @@ void drm_gpuvm_init(struct drm_gpuvm *gp
|
|
u64 start_offset, u64 range,
|
|
u64 reserve_offset, u64 reserve_range,
|
|
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_interval_empty(struct drm_gpuvm *gpuvm, u64 addr, u64 range);
|
|
@@ -674,6 +695,14 @@ static inline void drm_gpuva_init_from_o
|
|
*/
|
|
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
|
|
* a struct drm_gpuva_op
|
|
*
|