* [PATCH 01/10] drm/exec: Add helper to bypass IGNORE_DUPLICATES flag
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
2026-07-06 8:43 ` Christian König
2026-07-03 16:31 ` [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4 Natalie Vock
` (8 subsequent siblings)
9 siblings, 1 reply; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
TTM is about to switch to drm_exec for locking objects
in the LRU list. When we're done processing the object, we want to
unlock it only if the caller doesn't already hold that lock. If
DRM_EXEC_IGNORE_DUPLICATES is set on the exec object (which callers may
require for unrelated reasons), we have no way of knowing whether the
lock is already held.
To remedy this, add a separate helper that forcefully bypasses the
IGNORE_DUPLICATES flag for only a single locking operation.
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/drm_exec.c | 52 ++++++++++++++++++++++++++++++++++------------
include/drm/drm_exec.h | 2 ++
2 files changed, 41 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
index 7988f5e7d56a3..91de6b4d29df8 100644
--- a/drivers/gpu/drm/drm_exec.c
+++ b/drivers/gpu/drm/drm_exec.c
@@ -190,18 +190,9 @@ static int drm_exec_lock_contended(struct drm_exec *exec)
return ret;
}
-/**
- * drm_exec_lock_obj - lock a GEM object for use
- * @exec: the drm_exec object with the state
- * @obj: the GEM object to lock
- *
- * Lock a GEM object for use and grab a reference to it.
- *
- * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
- * already locked (can be suppressed by setting the DRM_EXEC_IGNORE_DUPLICATES
- * flag), -ENOMEM when memory allocation failed and zero for success.
- */
-int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
+static int __drm_exec_lock_obj(struct drm_exec *exec,
+ struct drm_gem_object *obj,
+ bool always_report_duplicates)
{
int ret;
@@ -226,7 +217,7 @@ int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
return -EDEADLK;
}
- if (unlikely(ret == -EALREADY) &&
+ if (unlikely(ret == -EALREADY) && !always_report_duplicates &&
exec->flags & DRM_EXEC_IGNORE_DUPLICATES)
return 0;
@@ -243,8 +234,43 @@ int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
dma_resv_unlock(obj->resv);
return ret;
}
+
+/**
+ * drm_exec_lock_obj - lock a GEM object for use
+ * @exec: the drm_exec object with the state
+ * @obj: the GEM object to lock
+ *
+ * Lock a GEM object for use and grab a reference to it.
+ *
+ * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
+ * already locked (can be suppressed by setting the DRM_EXEC_IGNORE_DUPLICATES
+ * flag), -ENOMEM when memory allocation failed and zero for success.
+ */
+int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
+{
+ return __drm_exec_lock_obj(exec, obj, false);
+}
EXPORT_SYMBOL(drm_exec_lock_obj);
+/**
+ * drm_exec_lock_obj_report_dup - lock a GEM object for use, but always report duplicates
+ * @exec: the drm_exec object with the state
+ * @obj: the GEM object to lock
+ *
+ * Like drm_exec_lock_obj, lock a GEM object for use and grab a reference to it.
+ * Unlike drm_exec_lock_obj, DRM_EXEC_IGNORE_DUPLICATES is ignored and duplicates are
+ * always reported.
+ *
+ * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
+ * already locked, -ENOMEM when memory allocation failed and zero for success.
+ */
+int drm_exec_lock_obj_report_dup(struct drm_exec *exec,
+ struct drm_gem_object *obj)
+{
+ return __drm_exec_lock_obj(exec, obj, false);
+}
+EXPORT_SYMBOL(drm_exec_lock_obj_report_dup);
+
/**
* drm_exec_unlock_obj - unlock a GEM object in this exec context
* @exec: the drm_exec object with the state
diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
index 8725ba92ff916..ff80dd2b72240 100644
--- a/include/drm/drm_exec.h
+++ b/include/drm/drm_exec.h
@@ -176,6 +176,8 @@ void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr);
void drm_exec_fini(struct drm_exec *exec);
bool drm_exec_cleanup(struct drm_exec *exec);
int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj);
+int drm_exec_lock_obj_report_dup(struct drm_exec *exec,
+ struct drm_gem_object *obj);
void drm_exec_unlock_obj(struct drm_exec *exec, struct drm_gem_object *obj);
int drm_exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj,
unsigned int num_fences);
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH 01/10] drm/exec: Add helper to bypass IGNORE_DUPLICATES flag
2026-07-03 16:31 ` [PATCH 01/10] drm/exec: Add helper to bypass IGNORE_DUPLICATES flag Natalie Vock
@ 2026-07-06 8:43 ` Christian König
0 siblings, 0 replies; 26+ messages in thread
From: Christian König @ 2026-07-06 8:43 UTC (permalink / raw)
To: Natalie Vock, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
On 7/3/26 18:31, Natalie Vock wrote:
> TTM is about to switch to drm_exec for locking objects
> in the LRU list. When we're done processing the object, we want to
> unlock it only if the caller doesn't already hold that lock. If
> DRM_EXEC_IGNORE_DUPLICATES is set on the exec object (which callers may
> require for unrelated reasons), we have no way of knowing whether the
> lock is already held.
>
> To remedy this, add a separate helper that forcefully bypasses the
> IGNORE_DUPLICATES flag for only a single locking operation.
>
> Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> ---
> drivers/gpu/drm/drm_exec.c | 52 ++++++++++++++++++++++++++++++++++------------
> include/drm/drm_exec.h | 2 ++
> 2 files changed, 41 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
> index 7988f5e7d56a3..91de6b4d29df8 100644
> --- a/drivers/gpu/drm/drm_exec.c
> +++ b/drivers/gpu/drm/drm_exec.c
> @@ -190,18 +190,9 @@ static int drm_exec_lock_contended(struct drm_exec *exec)
> return ret;
> }
>
> -/**
> - * drm_exec_lock_obj - lock a GEM object for use
> - * @exec: the drm_exec object with the state
> - * @obj: the GEM object to lock
> - *
> - * Lock a GEM object for use and grab a reference to it.
> - *
> - * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
> - * already locked (can be suppressed by setting the DRM_EXEC_IGNORE_DUPLICATES
> - * flag), -ENOMEM when memory allocation failed and zero for success.
> - */
> -int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
> +static int __drm_exec_lock_obj(struct drm_exec *exec,
> + struct drm_gem_object *obj,
> + bool always_report_duplicates)
Rename the new parameter to ignore_duplicates.
> {
> int ret;
>
> @@ -226,7 +217,7 @@ int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
> return -EDEADLK;
> }
>
> - if (unlikely(ret == -EALREADY) &&
> + if (unlikely(ret == -EALREADY) && !always_report_duplicates &&
> exec->flags & DRM_EXEC_IGNORE_DUPLICATES)
> return 0;
>
> @@ -243,8 +234,43 @@ int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
> dma_resv_unlock(obj->resv);
> return ret;
> }
> +
> +/**
> + * drm_exec_lock_obj - lock a GEM object for use
> + * @exec: the drm_exec object with the state
> + * @obj: the GEM object to lock
> + *
> + * Lock a GEM object for use and grab a reference to it.
> + *
> + * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
> + * already locked (can be suppressed by setting the DRM_EXEC_IGNORE_DUPLICATES
> + * flag), -ENOMEM when memory allocation failed and zero for success.
> + */
> +int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
> +{
> + return __drm_exec_lock_obj(exec, obj, false);
And then use "exec->flags & DRM_EXEC_IGNORE_DUPLICATES" here.
> +}
> EXPORT_SYMBOL(drm_exec_lock_obj);
>
> +/**
> + * drm_exec_lock_obj_report_dup - lock a GEM object for use, but always report duplicates
> + * @exec: the drm_exec object with the state
> + * @obj: the GEM object to lock
> + *
> + * Like drm_exec_lock_obj, lock a GEM object for use and grab a reference to it.
> + * Unlike drm_exec_lock_obj, DRM_EXEC_IGNORE_DUPLICATES is ignored and duplicates are
> + * always reported.
> + *
> + * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
> + * already locked, -ENOMEM when memory allocation failed and zero for success.
> + */
> +int drm_exec_lock_obj_report_dup(struct drm_exec *exec,
> + struct drm_gem_object *obj)
> +{
> + return __drm_exec_lock_obj(exec, obj, false);
BTW That here is buggy, it should have been true.
Apart from that looks good to me,
Christian.
> +}
> +EXPORT_SYMBOL(drm_exec_lock_obj_report_dup);
> +
> /**
> * drm_exec_unlock_obj - unlock a GEM object in this exec context
> * @exec: the drm_exec object with the state
> diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
> index 8725ba92ff916..ff80dd2b72240 100644
> --- a/include/drm/drm_exec.h
> +++ b/include/drm/drm_exec.h
> @@ -176,6 +176,8 @@ void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr);
> void drm_exec_fini(struct drm_exec *exec);
> bool drm_exec_cleanup(struct drm_exec *exec);
> int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj);
> +int drm_exec_lock_obj_report_dup(struct drm_exec *exec,
> + struct drm_gem_object *obj);
> void drm_exec_unlock_obj(struct drm_exec *exec, struct drm_gem_object *obj);
> int drm_exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj,
> unsigned int num_fences);
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
2026-07-03 16:31 ` [PATCH 01/10] drm/exec: Add helper to bypass IGNORE_DUPLICATES flag Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
2026-07-06 13:14 ` Thomas Hellström
2026-07-03 16:31 ` [PATCH 03/10] drm/ttm: remove ttm_lru_walk_ops Natalie Vock
` (7 subsequent siblings)
9 siblings, 1 reply; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
From: Christian König <christian.koenig@amd.com>
Instead of keeping a separate reference count for the TTM object also use
the reference count for DRM GEM objects inside TTM.
Apart from avoiding two reference counts for one object this approach has
the clear advantage of being able to use drm_exec inside TTM.
v2: adjust XE assert as well and re-enable disabled test
v3: handle another case in i915
v4: set GEM driver funcs of transfer BOs to point to the TTM free
callback (Natalie)
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++---
drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
drivers/gpu/drm/ttm/ttm_bo.c | 135 +++++++++++------------
drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
drivers/gpu/drm/xe/xe_bo.c | 2 +-
include/drm/ttm/ttm_bo.h | 9 --
8 files changed, 111 insertions(+), 112 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
index df3fcc2b1248e..642296602de69 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct drm_i915_gem_object *obj)
* Don't manipulate the TTM LRUs while in TTM bo destruction.
* We're called through i915_ttm_delete_mem_notify().
*/
- if (!kref_read(&bo->kref))
+ if (!kref_read(&bo->base.refcount))
return;
/*
@@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct drm_i915_gem_object *obj)
*
* TODO: consider maybe also bumping the shrinker list here when we have
* already unpinned it, which should give us something more like an LRU.
- *
- * TODO: There is a small window of opportunity for this function to
- * get called from eviction after we've dropped the last GEM refcount,
- * but before the TTM deleted flag is set on the object. Avoid
- * adjusting the shrinker list in such cases, since the object is
- * not available to the shrinker anyway due to its zero refcount.
- * To fix this properly we should move to a TTM shrinker LRU list for
- * these objects.
*/
- if (kref_get_unless_zero(&obj->base.refcount)) {
- if (shrinkable != obj->mm.ttm_shrinkable) {
- if (shrinkable) {
- if (obj->mm.madv == I915_MADV_WILLNEED)
- __i915_gem_object_make_shrinkable(obj);
- else
- __i915_gem_object_make_purgeable(obj);
- } else {
- i915_gem_object_make_unshrinkable(obj);
- }
-
- obj->mm.ttm_shrinkable = shrinkable;
+ i915_gem_object_get(obj);
+ if (shrinkable != obj->mm.ttm_shrinkable) {
+ if (shrinkable) {
+ if (obj->mm.madv == I915_MADV_WILLNEED)
+ __i915_gem_object_make_shrinkable(obj);
+ else
+ __i915_gem_object_make_purgeable(obj);
+ } else {
+ i915_gem_object_make_unshrinkable(obj);
}
- i915_gem_object_put(obj);
+
+ obj->mm.ttm_shrinkable = shrinkable;
}
+ i915_gem_object_put(obj);
/*
* Put on the correct LRU list depending on the MADV status
diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
index 56ad8ef325840..904cb4da6c9b3 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
@@ -127,7 +127,7 @@ static void ttm_bo_init_reserved_sys_man(struct kunit *test)
dma_resv_unlock(bo->base.resv);
KUNIT_EXPECT_EQ(test, err, 0);
- KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
+ KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
KUNIT_EXPECT_EQ(test, bo->type, bo_type);
KUNIT_EXPECT_EQ(test, bo->page_alignment, PAGE_SIZE);
@@ -176,7 +176,7 @@ static void ttm_bo_init_reserved_mock_man(struct kunit *test)
dma_resv_unlock(bo->base.resv);
KUNIT_EXPECT_EQ(test, err, 0);
- KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
+ KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
KUNIT_EXPECT_EQ(test, bo->type, bo_type);
KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
@@ -969,6 +969,8 @@ static void ttm_bo_validate_allowed_only_evict(struct kunit *test)
ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
}
+extern const struct drm_gem_object_funcs ttm_deleted_object_funcs;
+
static void ttm_bo_validate_deleted_evict(struct kunit *test)
{
struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
@@ -999,7 +1001,7 @@ static void ttm_bo_validate_deleted_evict(struct kunit *test)
KUNIT_EXPECT_EQ(test, ttm_resource_manager_usage(man), big);
dma_resv_unlock(bo_big->base.resv);
- bo_big->deleted = true;
+ bo_big->base.funcs = &ttm_deleted_object_funcs;
bo_small = ttm_bo_kunit_init(test, test->priv, small, NULL);
bo_small->type = bo_type;
diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
@@ -189,8 +189,6 @@ struct ttm_buffer_object *ttm_bo_kunit_init(struct kunit *test,
bo->bdev = devs->ttm_dev;
bo->destroy = dummy_ttm_bo_destroy;
- kref_init(&bo->kref);
-
return bo;
}
EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 3980f376e3ba4..2b470c1746f60 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -246,88 +246,84 @@ static void ttm_bo_delayed_delete(struct work_struct *work)
ttm_bo_put(bo);
}
-static void ttm_bo_release(struct kref *kref)
+/*
+ * All other callbacks should never ever be called on a deleted TTM object.
+ */
+const struct drm_gem_object_funcs ttm_deleted_object_funcs = {
+ .free = ttm_bo_free
+};
+EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
+
+/* Returns true if the BO is about to get deleted */
+static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
+{
+ return bo->base.funcs == &ttm_deleted_object_funcs;
+}
+
+void ttm_bo_fini(struct ttm_buffer_object *bo)
{
- struct ttm_buffer_object *bo =
- container_of(kref, struct ttm_buffer_object, kref);
struct ttm_device *bdev = bo->bdev;
int ret;
WARN_ON_ONCE(bo->pin_count);
WARN_ON_ONCE(bo->bulk_move);
- if (!bo->deleted) {
- ret = ttm_bo_individualize_resv(bo);
- if (ret) {
- /* Last resort, if we fail to allocate memory for the
- * fences block for the BO to become idle
- */
- dma_resv_wait_timeout(bo->base.resv,
- DMA_RESV_USAGE_BOOKKEEP, false,
- 30 * HZ);
- }
+ ret = ttm_bo_individualize_resv(bo);
+ if (ret) {
+ /* Last resort, if we fail to allocate memory for the
+ * fences block for the BO to become idle
+ */
+ dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP,
+ false, 30 * HZ);
+ }
- if (bdev->funcs->release_notify)
- bdev->funcs->release_notify(bo);
+ if (bo->bdev->funcs->release_notify)
+ bo->bdev->funcs->release_notify(bo);
- drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node);
- ttm_mem_io_free(bdev, bo->resource);
+ drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node);
+ ttm_mem_io_free(bdev, bo->resource);
- if (!dma_resv_test_signaled(&bo->base._resv,
- DMA_RESV_USAGE_BOOKKEEP) ||
- (want_init_on_free() && (bo->ttm != NULL)) ||
- bo->type == ttm_bo_type_sg ||
- !dma_resv_trylock(bo->base.resv)) {
- /* The BO is not idle, resurrect it for delayed destroy */
- ttm_bo_flush_all_fences(bo);
- bo->deleted = true;
+ if (!dma_resv_test_signaled(&bo->base._resv, DMA_RESV_USAGE_BOOKKEEP) ||
+ (want_init_on_free() && (bo->ttm != NULL)) ||
+ bo->type == ttm_bo_type_sg ||
+ !dma_resv_trylock(bo->base.resv)) {
+ /* The BO is not idle, resurrect it for delayed destroy */
+ ttm_bo_flush_all_fences(bo);
- spin_lock(&bdev->lru_lock);
-
- /*
- * Make pinned bos immediately available to
- * shrinkers, now that they are queued for
- * destruction.
- *
- * FIXME: QXL is triggering this. Can be removed when the
- * driver is fixed.
- */
- if (bo->pin_count) {
- bo->pin_count = 0;
- ttm_resource_move_to_lru_tail(bo->resource);
- }
+ spin_lock(&bo->bdev->lru_lock);
- kref_init(&bo->kref);
- spin_unlock(&bdev->lru_lock);
+ /*
+ * Make pinned bos immediately available to
+ * shrinkers, now that they are queued for
+ * destruction.
+ *
+ * FIXME: QXL is triggering this. Can be removed when the
+ * driver is fixed.
+ */
+ if (bo->pin_count) {
+ bo->pin_count = 0;
+ ttm_resource_move_to_lru_tail(bo->resource);
+ }
- INIT_WORK(&bo->delayed_delete, ttm_bo_delayed_delete);
+ kref_init(&bo->base.refcount);
+ bo->base.funcs = &ttm_deleted_object_funcs;
+ spin_unlock(&bo->bdev->lru_lock);
- /* Schedule the worker on the closest NUMA node. This
- * improves performance since system memory might be
- * cleared on free and that is best done on a CPU core
- * close to it.
- */
- queue_work_node(bdev->pool.nid, bdev->wq, &bo->delayed_delete);
- return;
- }
+ INIT_WORK(&bo->delayed_delete, ttm_bo_delayed_delete);
+ /* Schedule the worker on the closest NUMA node. This
+ * improves performance since system memory might be
+ * cleared on free and that is best done on a CPU core
+ * close to it.
+ */
+ queue_work_node(bdev->pool.nid, bdev->wq, &bo->delayed_delete);
+ } else {
ttm_bo_cleanup_memtype_use(bo);
dma_resv_unlock(bo->base.resv);
- }
- atomic_dec(&ttm_glob.bo_count);
- bo->destroy(bo);
-}
-
-/* TODO: remove! */
-void ttm_bo_put(struct ttm_buffer_object *bo)
-{
- kref_put(&bo->kref, ttm_bo_release);
-}
-
-void ttm_bo_fini(struct ttm_buffer_object *bo)
-{
- ttm_bo_put(bo);
+ atomic_dec(&ttm_glob.bo_count);
+ bo->destroy(bo);
+ }
}
EXPORT_SYMBOL(ttm_bo_fini);
@@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device *bdev, struct ttm_resource_manager *man
if (!bo->resource || bo->resource->mem_type != mem_type)
goto out_bo_moved;
- if (bo->deleted) {
+ if (ttm_bo_is_zombie(bo)) {
ret = ttm_bo_wait_ctx(bo, ctx);
if (!ret)
ttm_bo_cleanup_memtype_use(bo);
@@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *
if (bo->pin_count || !bo->bdev->funcs->eviction_valuable(bo, evict_walk->place))
return 0;
- if (bo->deleted) {
+ if (ttm_bo_is_zombie(bo)) {
lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
if (!lret)
ttm_bo_cleanup_memtype_use(bo);
@@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
void ttm_bo_pin(struct ttm_buffer_object *bo)
{
dma_resv_assert_held(bo->base.resv);
- WARN_ON_ONCE(!kref_read(&bo->kref));
spin_lock(&bo->bdev->lru_lock);
if (bo->resource)
ttm_resource_del_bulk_move(bo->resource, bo);
@@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
void ttm_bo_unpin(struct ttm_buffer_object *bo)
{
dma_resv_assert_held(bo->base.resv);
- WARN_ON_ONCE(!kref_read(&bo->kref));
if (WARN_ON_ONCE(!bo->pin_count))
return;
@@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo,
{
int ret;
- kref_init(&bo->kref);
bo->bdev = bdev;
bo->type = type;
bo->page_alignment = alignment;
@@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
goto out;
}
- if (bo->deleted) {
- pgoff_t num_pages = tt->num_pages;
+ if (ttm_bo_is_zombie(bo)) {
+ pgoff_t num_pages = bo->ttm->num_pages;
ret = ttm_bo_wait_ctx(bo, ctx);
if (ret)
diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h b/drivers/gpu/drm/ttm/ttm_bo_internal.h
index e0d48eac74b03..ded2a47be0bcb 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
+++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
@@ -27,6 +27,14 @@
#include <drm/ttm/ttm_bo.h>
+static inline void ttm_bo_free(struct drm_gem_object *gobj)
+{
+ struct ttm_buffer_object *bo = container_of(gobj, typeof(*bo), base);
+
+ atomic_dec(&ttm_glob.bo_count);
+ bo->destroy(bo);
+}
+
/**
* ttm_bo_get - reference a struct ttm_buffer_object
*
@@ -34,7 +42,7 @@
*/
static inline void ttm_bo_get(struct ttm_buffer_object *bo)
{
- kref_get(&bo->kref);
+ drm_gem_object_get(&bo->base);
}
/**
@@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct ttm_buffer_object *bo)
static inline __must_check struct ttm_buffer_object *
ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
{
- if (!kref_get_unless_zero(&bo->kref))
+ if (!kref_get_unless_zero(&bo->base.refcount))
return NULL;
return bo;
}
-void ttm_bo_put(struct ttm_buffer_object *bo);
+static inline void ttm_bo_put(struct ttm_buffer_object *bo)
+{
+ drm_gem_object_put(&bo->base);
+}
#endif
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 3e3c201a02226..7ed085adf1c9b 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -41,6 +41,18 @@
#include "ttm_bo_internal.h"
+static void ttm_transfer_object_free(struct drm_gem_object *obj)
+{
+ struct ttm_buffer_object *bo =
+ container_of(obj, struct ttm_buffer_object, base);
+
+ ttm_bo_fini(bo);
+}
+
+const struct drm_gem_object_funcs ttm_transfer_object_funcs = {
+ .free = ttm_transfer_object_free,
+};
+
struct ttm_transfer_obj {
struct ttm_buffer_object base;
struct ttm_buffer_object *bo;
@@ -247,7 +259,8 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
atomic_inc(&ttm_glob.bo_count);
drm_vma_node_reset(&fbo->base.base.vma_node);
- kref_init(&fbo->base.kref);
+ kref_init(&fbo->base.base.refcount);
+ fbo->base.base.funcs = &ttm_transfer_object_funcs;
fbo->base.destroy = &ttm_transfered_destroy;
fbo->base.pin_count = 0;
if (bo->type != ttm_bo_type_sg)
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 85e6d9a0f575b..5843f850339c7 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -1651,7 +1651,7 @@ static bool xe_ttm_bo_lock_in_destructor(struct ttm_buffer_object *ttm_bo)
struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
bool locked;
- xe_assert(xe, !kref_read(&ttm_bo->kref));
+ xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
/*
* We can typically only race with TTM trylocking under the
diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
index 8310bc3d55f90..1eae9eea5ff32 100644
--- a/include/drm/ttm/ttm_bo.h
+++ b/include/drm/ttm/ttm_bo.h
@@ -78,11 +78,8 @@ enum ttm_bo_type {
* @type: The bo type.
* @page_alignment: Page alignment.
* @destroy: Destruction function. If NULL, kfree is used.
- * @kref: Reference count of this buffer object. When this refcount reaches
- * zero, the object is destroyed or put on the delayed delete list.
* @resource: structure describing current placement.
* @ttm: TTM structure holding system pages.
- * @deleted: True if the object is only a zombie and already deleted.
* @bulk_move: The bulk move object.
* @priority: Priority for LRU, BOs with lower priority are evicted first.
* @pin_count: Pin count.
@@ -109,17 +106,11 @@ struct ttm_buffer_object {
uint32_t page_alignment;
void (*destroy) (struct ttm_buffer_object *);
- /*
- * Members not needing protection.
- */
- struct kref kref;
-
/*
* Members protected by the bo::resv::reserved lock.
*/
struct ttm_resource *resource;
struct ttm_tt *ttm;
- bool deleted;
struct ttm_lru_bulk_move *bulk_move;
unsigned priority;
unsigned pin_count;
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-03 16:31 ` [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4 Natalie Vock
@ 2026-07-06 13:14 ` Thomas Hellström
2026-07-06 14:49 ` Christian König
0 siblings, 1 reply; 26+ messages in thread
From: Thomas Hellström @ 2026-07-06 13:14 UTC (permalink / raw)
To: Natalie Vock, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Christian Koenig, Huang Rui,
Matthew Auld, Matthew Brost, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> From: Christian König <christian.koenig@amd.com>
>
> Instead of keeping a separate reference count for the TTM object also
> use
> the reference count for DRM GEM objects inside TTM.
>
> Apart from avoiding two reference counts for one object this approach
> has
> the clear advantage of being able to use drm_exec inside TTM.
>
> v2: adjust XE assert as well and re-enable disabled test
> v3: handle another case in i915
> v4: set GEM driver funcs of transfer BOs to point to the TTM free
> callback (Natalie)
I think the main review issue from the last time this was on the table
was that we shouldn't resurrect the gem refcount. Apart from the risc
of getting barriers wrong, both xe and IIRC i915 partly rely on the gem
refcount never being resurrected and that callbacks for bos with zero
gem refcount means that the gem part of the object is unusable.
For example xe_bo.c:
if (!xe_bo_is_xe_bo(bo) || !xe_bo_get_unless_zero(xe_bo))
return xe_bo_shrink_purge(ctx, bo, scanned);
So IIRC the conclusion was when removing the ttm refcount we shouldn't
attempt to resurrect the gem one. If the get_unless_zero() fails during
evict walk, we simply find something to wait for. See previous
discussion there.
I fully support removing the ttm refcount, but not if it means
resurrecting the gem refcount.
If we want to sidestep that problem, in favour of getting the proposed
locking functionality in and future proof it, I suggest using
https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
And rebase this series on that. This means we can use the ttm refcount
for the transaction refcounting, and also that if we add a dma-buf map
interface with a dma_resv_txn_obj, we could use that to also have
exhaustive eviction that originates from a dma_buf map.
/Thomas
>
> Signed-off-by: Christian König <christian.koenig@amd.com>
> Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> ---
> drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++---
> drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
> drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
> drivers/gpu/drm/ttm/ttm_bo.c | 135 +++++++++++--
> ----------
> drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
> drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
> drivers/gpu/drm/xe/xe_bo.c | 2 +-
> include/drm/ttm/ttm_bo.h | 9 --
> 8 files changed, 111 insertions(+), 112 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> index df3fcc2b1248e..642296602de69 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
> drm_i915_gem_object *obj)
> * Don't manipulate the TTM LRUs while in TTM bo
> destruction.
> * We're called through i915_ttm_delete_mem_notify().
> */
> - if (!kref_read(&bo->kref))
> + if (!kref_read(&bo->base.refcount))
> return;
>
> /*
> @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
> drm_i915_gem_object *obj)
> *
> * TODO: consider maybe also bumping the shrinker list here
> when we have
> * already unpinned it, which should give us something more
> like an LRU.
> - *
> - * TODO: There is a small window of opportunity for this
> function to
> - * get called from eviction after we've dropped the last GEM
> refcount,
> - * but before the TTM deleted flag is set on the object.
> Avoid
> - * adjusting the shrinker list in such cases, since the
> object is
> - * not available to the shrinker anyway due to its zero
> refcount.
> - * To fix this properly we should move to a TTM shrinker LRU
> list for
> - * these objects.
> */
> - if (kref_get_unless_zero(&obj->base.refcount)) {
> - if (shrinkable != obj->mm.ttm_shrinkable) {
> - if (shrinkable) {
> - if (obj->mm.madv ==
> I915_MADV_WILLNEED)
> -
> __i915_gem_object_make_shrinkable(obj);
> - else
> -
> __i915_gem_object_make_purgeable(obj);
> - } else {
> -
> i915_gem_object_make_unshrinkable(obj);
> - }
> -
> - obj->mm.ttm_shrinkable = shrinkable;
> + i915_gem_object_get(obj);
> + if (shrinkable != obj->mm.ttm_shrinkable) {
> + if (shrinkable) {
> + if (obj->mm.madv == I915_MADV_WILLNEED)
> + __i915_gem_object_make_shrinkable(ob
> j);
> + else
> + __i915_gem_object_make_purgeable(obj
> );
> + } else {
> + i915_gem_object_make_unshrinkable(obj);
> }
> - i915_gem_object_put(obj);
> +
> + obj->mm.ttm_shrinkable = shrinkable;
> }
> + i915_gem_object_put(obj);
>
> /*
> * Put on the correct LRU list depending on the MADV status
> diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> index 56ad8ef325840..904cb4da6c9b3 100644
> --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> @@ -127,7 +127,7 @@ static void ttm_bo_init_reserved_sys_man(struct
> kunit *test)
> dma_resv_unlock(bo->base.resv);
>
> KUNIT_EXPECT_EQ(test, err, 0);
> - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
> KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> KUNIT_EXPECT_EQ(test, bo->page_alignment, PAGE_SIZE);
> @@ -176,7 +176,7 @@ static void ttm_bo_init_reserved_mock_man(struct
> kunit *test)
> dma_resv_unlock(bo->base.resv);
>
> KUNIT_EXPECT_EQ(test, err, 0);
> - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
> KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
> @@ -969,6 +969,8 @@ static void
> ttm_bo_validate_allowed_only_evict(struct kunit *test)
> ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
> }
>
> +extern const struct drm_gem_object_funcs ttm_deleted_object_funcs;
> +
> static void ttm_bo_validate_deleted_evict(struct kunit *test)
> {
> struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
> @@ -999,7 +1001,7 @@ static void ttm_bo_validate_deleted_evict(struct
> kunit *test)
> KUNIT_EXPECT_EQ(test, ttm_resource_manager_usage(man), big);
>
> dma_resv_unlock(bo_big->base.resv);
> - bo_big->deleted = true;
> + bo_big->base.funcs = &ttm_deleted_object_funcs;
>
> bo_small = ttm_bo_kunit_init(test, test->priv, small, NULL);
> bo_small->type = bo_type;
> diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
> --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> @@ -189,8 +189,6 @@ struct ttm_buffer_object
> *ttm_bo_kunit_init(struct kunit *test,
> bo->bdev = devs->ttm_dev;
> bo->destroy = dummy_ttm_bo_destroy;
>
> - kref_init(&bo->kref);
> -
> return bo;
> }
> EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> b/drivers/gpu/drm/ttm/ttm_bo.c
> index 3980f376e3ba4..2b470c1746f60 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -246,88 +246,84 @@ static void ttm_bo_delayed_delete(struct
> work_struct *work)
> ttm_bo_put(bo);
> }
>
> -static void ttm_bo_release(struct kref *kref)
> +/*
> + * All other callbacks should never ever be called on a deleted TTM
> object.
> + */
> +const struct drm_gem_object_funcs ttm_deleted_object_funcs = {
> + .free = ttm_bo_free
> +};
> +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
> +
> +/* Returns true if the BO is about to get deleted */
> +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
> +{
> + return bo->base.funcs == &ttm_deleted_object_funcs;
> +}
> +
> +void ttm_bo_fini(struct ttm_buffer_object *bo)
> {
> - struct ttm_buffer_object *bo =
> - container_of(kref, struct ttm_buffer_object, kref);
> struct ttm_device *bdev = bo->bdev;
> int ret;
>
> WARN_ON_ONCE(bo->pin_count);
> WARN_ON_ONCE(bo->bulk_move);
>
> - if (!bo->deleted) {
> - ret = ttm_bo_individualize_resv(bo);
> - if (ret) {
> - /* Last resort, if we fail to allocate
> memory for the
> - * fences block for the BO to become idle
> - */
> - dma_resv_wait_timeout(bo->base.resv,
> -
> DMA_RESV_USAGE_BOOKKEEP, false,
> - 30 * HZ);
> - }
> + ret = ttm_bo_individualize_resv(bo);
> + if (ret) {
> + /* Last resort, if we fail to allocate memory for
> the
> + * fences block for the BO to become idle
> + */
> + dma_resv_wait_timeout(bo->base.resv,
> DMA_RESV_USAGE_BOOKKEEP,
> + false, 30 * HZ);
> + }
>
> - if (bdev->funcs->release_notify)
> - bdev->funcs->release_notify(bo);
> + if (bo->bdev->funcs->release_notify)
> + bo->bdev->funcs->release_notify(bo);
>
> - drm_vma_offset_remove(bdev->vma_manager, &bo-
> >base.vma_node);
> - ttm_mem_io_free(bdev, bo->resource);
> + drm_vma_offset_remove(bdev->vma_manager, &bo-
> >base.vma_node);
> + ttm_mem_io_free(bdev, bo->resource);
>
> - if (!dma_resv_test_signaled(&bo->base._resv,
> - DMA_RESV_USAGE_BOOKKEEP)
> ||
> - (want_init_on_free() && (bo->ttm != NULL)) ||
> - bo->type == ttm_bo_type_sg ||
> - !dma_resv_trylock(bo->base.resv)) {
> - /* The BO is not idle, resurrect it for
> delayed destroy */
> - ttm_bo_flush_all_fences(bo);
> - bo->deleted = true;
> + if (!dma_resv_test_signaled(&bo->base._resv,
> DMA_RESV_USAGE_BOOKKEEP) ||
> + (want_init_on_free() && (bo->ttm != NULL)) ||
> + bo->type == ttm_bo_type_sg ||
> + !dma_resv_trylock(bo->base.resv)) {
> + /* The BO is not idle, resurrect it for delayed
> destroy */
> + ttm_bo_flush_all_fences(bo);
>
> - spin_lock(&bdev->lru_lock);
> -
> - /*
> - * Make pinned bos immediately available to
> - * shrinkers, now that they are queued for
> - * destruction.
> - *
> - * FIXME: QXL is triggering this. Can be
> removed when the
> - * driver is fixed.
> - */
> - if (bo->pin_count) {
> - bo->pin_count = 0;
> - ttm_resource_move_to_lru_tail(bo-
> >resource);
> - }
> + spin_lock(&bo->bdev->lru_lock);
>
> - kref_init(&bo->kref);
> - spin_unlock(&bdev->lru_lock);
> + /*
> + * Make pinned bos immediately available to
> + * shrinkers, now that they are queued for
> + * destruction.
> + *
> + * FIXME: QXL is triggering this. Can be removed
> when the
> + * driver is fixed.
> + */
> + if (bo->pin_count) {
> + bo->pin_count = 0;
> + ttm_resource_move_to_lru_tail(bo->resource);
> + }
>
> - INIT_WORK(&bo->delayed_delete,
> ttm_bo_delayed_delete);
> + kref_init(&bo->base.refcount);
> + bo->base.funcs = &ttm_deleted_object_funcs;
> + spin_unlock(&bo->bdev->lru_lock);
>
> - /* Schedule the worker on the closest NUMA
> node. This
> - * improves performance since system memory
> might be
> - * cleared on free and that is best done on
> a CPU core
> - * close to it.
> - */
> - queue_work_node(bdev->pool.nid, bdev->wq,
> &bo->delayed_delete);
> - return;
> - }
> + INIT_WORK(&bo->delayed_delete,
> ttm_bo_delayed_delete);
>
> + /* Schedule the worker on the closest NUMA node.
> This
> + * improves performance since system memory might be
> + * cleared on free and that is best done on a CPU
> core
> + * close to it.
> + */
> + queue_work_node(bdev->pool.nid, bdev->wq, &bo-
> >delayed_delete);
> + } else {
> ttm_bo_cleanup_memtype_use(bo);
> dma_resv_unlock(bo->base.resv);
> - }
>
> - atomic_dec(&ttm_glob.bo_count);
> - bo->destroy(bo);
> -}
> -
> -/* TODO: remove! */
> -void ttm_bo_put(struct ttm_buffer_object *bo)
> -{
> - kref_put(&bo->kref, ttm_bo_release);
> -}
> -
> -void ttm_bo_fini(struct ttm_buffer_object *bo)
> -{
> - ttm_bo_put(bo);
> + atomic_dec(&ttm_glob.bo_count);
> + bo->destroy(bo);
> + }
> }
> EXPORT_SYMBOL(ttm_bo_fini);
>
> @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device *bdev,
> struct ttm_resource_manager *man
> if (!bo->resource || bo->resource->mem_type != mem_type)
> goto out_bo_moved;
>
> - if (bo->deleted) {
> + if (ttm_bo_is_zombie(bo)) {
> ret = ttm_bo_wait_ctx(bo, ctx);
> if (!ret)
> ttm_bo_cleanup_memtype_use(bo);
> @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk
> *walk, struct ttm_buffer_object *
> if (bo->pin_count || !bo->bdev->funcs->eviction_valuable(bo,
> evict_walk->place))
> return 0;
>
> - if (bo->deleted) {
> + if (ttm_bo_is_zombie(bo)) {
> lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
> if (!lret)
> ttm_bo_cleanup_memtype_use(bo);
> @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct ttm_device
> *bdev,
> void ttm_bo_pin(struct ttm_buffer_object *bo)
> {
> dma_resv_assert_held(bo->base.resv);
> - WARN_ON_ONCE(!kref_read(&bo->kref));
> spin_lock(&bo->bdev->lru_lock);
> if (bo->resource)
> ttm_resource_del_bulk_move(bo->resource, bo);
> @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
> void ttm_bo_unpin(struct ttm_buffer_object *bo)
> {
> dma_resv_assert_held(bo->base.resv);
> - WARN_ON_ONCE(!kref_read(&bo->kref));
> if (WARN_ON_ONCE(!bo->pin_count))
> return;
>
> @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct ttm_device *bdev,
> struct ttm_buffer_object *bo,
> {
> int ret;
>
> - kref_init(&bo->kref);
> bo->bdev = bdev;
> bo->type = type;
> bo->page_alignment = alignment;
> @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk,
> struct ttm_buffer_object *bo)
> goto out;
> }
>
> - if (bo->deleted) {
> - pgoff_t num_pages = tt->num_pages;
> + if (ttm_bo_is_zombie(bo)) {
> + pgoff_t num_pages = bo->ttm->num_pages;
>
> ret = ttm_bo_wait_ctx(bo, ctx);
> if (ret)
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> index e0d48eac74b03..ded2a47be0bcb 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> @@ -27,6 +27,14 @@
>
> #include <drm/ttm/ttm_bo.h>
>
> +static inline void ttm_bo_free(struct drm_gem_object *gobj)
> +{
> + struct ttm_buffer_object *bo = container_of(gobj,
> typeof(*bo), base);
> +
> + atomic_dec(&ttm_glob.bo_count);
> + bo->destroy(bo);
> +}
> +
> /**
> * ttm_bo_get - reference a struct ttm_buffer_object
> *
> @@ -34,7 +42,7 @@
> */
> static inline void ttm_bo_get(struct ttm_buffer_object *bo)
> {
> - kref_get(&bo->kref);
> + drm_gem_object_get(&bo->base);
> }
>
> /**
> @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
> ttm_buffer_object *bo)
> static inline __must_check struct ttm_buffer_object *
> ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
> {
> - if (!kref_get_unless_zero(&bo->kref))
> + if (!kref_get_unless_zero(&bo->base.refcount))
> return NULL;
> return bo;
> }
>
> -void ttm_bo_put(struct ttm_buffer_object *bo);
> +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
> +{
> + drm_gem_object_put(&bo->base);
> +}
>
> #endif
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> b/drivers/gpu/drm/ttm/ttm_bo_util.c
> index 3e3c201a02226..7ed085adf1c9b 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> @@ -41,6 +41,18 @@
>
> #include "ttm_bo_internal.h"
>
> +static void ttm_transfer_object_free(struct drm_gem_object *obj)
> +{
> + struct ttm_buffer_object *bo =
> + container_of(obj, struct ttm_buffer_object, base);
> +
> + ttm_bo_fini(bo);
> +}
> +
> +const struct drm_gem_object_funcs ttm_transfer_object_funcs = {
> + .free = ttm_transfer_object_free,
> +};
> +
> struct ttm_transfer_obj {
> struct ttm_buffer_object base;
> struct ttm_buffer_object *bo;
> @@ -247,7 +259,8 @@ static int ttm_buffer_object_transfer(struct
> ttm_buffer_object *bo,
> atomic_inc(&ttm_glob.bo_count);
> drm_vma_node_reset(&fbo->base.base.vma_node);
>
> - kref_init(&fbo->base.kref);
> + kref_init(&fbo->base.base.refcount);
> + fbo->base.base.funcs = &ttm_transfer_object_funcs;
> fbo->base.destroy = &ttm_transfered_destroy;
> fbo->base.pin_count = 0;
> if (bo->type != ttm_bo_type_sg)
> diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
> index 85e6d9a0f575b..5843f850339c7 100644
> --- a/drivers/gpu/drm/xe/xe_bo.c
> +++ b/drivers/gpu/drm/xe/xe_bo.c
> @@ -1651,7 +1651,7 @@ static bool xe_ttm_bo_lock_in_destructor(struct
> ttm_buffer_object *ttm_bo)
> struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
> bool locked;
>
> - xe_assert(xe, !kref_read(&ttm_bo->kref));
> + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
>
> /*
> * We can typically only race with TTM trylocking under the
> diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
> index 8310bc3d55f90..1eae9eea5ff32 100644
> --- a/include/drm/ttm/ttm_bo.h
> +++ b/include/drm/ttm/ttm_bo.h
> @@ -78,11 +78,8 @@ enum ttm_bo_type {
> * @type: The bo type.
> * @page_alignment: Page alignment.
> * @destroy: Destruction function. If NULL, kfree is used.
> - * @kref: Reference count of this buffer object. When this refcount
> reaches
> - * zero, the object is destroyed or put on the delayed delete list.
> * @resource: structure describing current placement.
> * @ttm: TTM structure holding system pages.
> - * @deleted: True if the object is only a zombie and already
> deleted.
> * @bulk_move: The bulk move object.
> * @priority: Priority for LRU, BOs with lower priority are evicted
> first.
> * @pin_count: Pin count.
> @@ -109,17 +106,11 @@ struct ttm_buffer_object {
> uint32_t page_alignment;
> void (*destroy) (struct ttm_buffer_object *);
>
> - /*
> - * Members not needing protection.
> - */
> - struct kref kref;
> -
> /*
> * Members protected by the bo::resv::reserved lock.
> */
> struct ttm_resource *resource;
> struct ttm_tt *ttm;
> - bool deleted;
> struct ttm_lru_bulk_move *bulk_move;
> unsigned priority;
> unsigned pin_count;
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-06 13:14 ` Thomas Hellström
@ 2026-07-06 14:49 ` Christian König
2026-07-06 17:01 ` Thomas Hellström
0 siblings, 1 reply; 26+ messages in thread
From: Christian König @ 2026-07-06 14:49 UTC (permalink / raw)
To: Thomas Hellström, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Matthew Brost, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
On 7/6/26 15:14, Thomas Hellström wrote:
> On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
>> From: Christian König <christian.koenig@amd.com>
>>
>> Instead of keeping a separate reference count for the TTM object also
>> use
>> the reference count for DRM GEM objects inside TTM.
>>
>> Apart from avoiding two reference counts for one object this approach
>> has
>> the clear advantage of being able to use drm_exec inside TTM.
>>
>> v2: adjust XE assert as well and re-enable disabled test
>> v3: handle another case in i915
>> v4: set GEM driver funcs of transfer BOs to point to the TTM free
>> callback (Natalie)
>
> I think the main review issue from the last time this was on the table
> was that we shouldn't resurrect the gem refcount. Apart from the risc
> of getting barriers wrong, both xe and IIRC i915 partly rely on the gem
> refcount never being resurrected and that callbacks for bos with zero
> gem refcount means that the gem part of the object is unusable.
I've spend quite some time thinking about that and came to the conclusion that this is actually harmless.
The drivers shouldn't be able to see the resurected BO, except if they go over the LRU list manually (which they shouldn't).
>
> For example xe_bo.c:
>
> if (!xe_bo_is_xe_bo(bo) || !xe_bo_get_unless_zero(xe_bo))
> return xe_bo_shrink_purge(ctx, bo, scanned);
>
> So IIRC the conclusion was when removing the ttm refcount we shouldn't
> attempt to resurrect the gem one. If the get_unless_zero() fails during
> evict walk, we simply find something to wait for. See previous
> discussion there.
Yeah, I considered that as well but the problem is we often doesn't have anything to wait on.
>
> I fully support removing the ttm refcount, but not if it means
> resurrecting the gem refcount.
>
> If we want to sidestep that problem, in favour of getting the proposed
> locking functionality in and future proof it, I suggest using
>
> https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
>
> And rebase this series on that. This means we can use the ttm refcount
> for the transaction refcounting, and also that if we add a dma-buf map
> interface with a dma_resv_txn_obj, we could use that to also have
> exhaustive eviction that originates from a dma_buf map.
I don't think that this is a good idea. It just adds another layer of abstraction and doesn't solve the problem in any way possible.
Regards,
Christian.
>
> /Thomas
>
>>
>> Signed-off-by: Christian König <christian.koenig@amd.com>
>> Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
>> ---
>> drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++---
>> drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
>> drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
>> drivers/gpu/drm/ttm/ttm_bo.c | 135 +++++++++++--
>> ----------
>> drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
>> drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
>> drivers/gpu/drm/xe/xe_bo.c | 2 +-
>> include/drm/ttm/ttm_bo.h | 9 --
>> 8 files changed, 111 insertions(+), 112 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>> b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>> index df3fcc2b1248e..642296602de69 100644
>> --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>> @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
>> drm_i915_gem_object *obj)
>> * Don't manipulate the TTM LRUs while in TTM bo
>> destruction.
>> * We're called through i915_ttm_delete_mem_notify().
>> */
>> - if (!kref_read(&bo->kref))
>> + if (!kref_read(&bo->base.refcount))
>> return;
>>
>> /*
>> @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
>> drm_i915_gem_object *obj)
>> *
>> * TODO: consider maybe also bumping the shrinker list here
>> when we have
>> * already unpinned it, which should give us something more
>> like an LRU.
>> - *
>> - * TODO: There is a small window of opportunity for this
>> function to
>> - * get called from eviction after we've dropped the last GEM
>> refcount,
>> - * but before the TTM deleted flag is set on the object.
>> Avoid
>> - * adjusting the shrinker list in such cases, since the
>> object is
>> - * not available to the shrinker anyway due to its zero
>> refcount.
>> - * To fix this properly we should move to a TTM shrinker LRU
>> list for
>> - * these objects.
>> */
>> - if (kref_get_unless_zero(&obj->base.refcount)) {
>> - if (shrinkable != obj->mm.ttm_shrinkable) {
>> - if (shrinkable) {
>> - if (obj->mm.madv ==
>> I915_MADV_WILLNEED)
>> -
>> __i915_gem_object_make_shrinkable(obj);
>> - else
>> -
>> __i915_gem_object_make_purgeable(obj);
>> - } else {
>> -
>> i915_gem_object_make_unshrinkable(obj);
>> - }
>> -
>> - obj->mm.ttm_shrinkable = shrinkable;
>> + i915_gem_object_get(obj);
>> + if (shrinkable != obj->mm.ttm_shrinkable) {
>> + if (shrinkable) {
>> + if (obj->mm.madv == I915_MADV_WILLNEED)
>> + __i915_gem_object_make_shrinkable(ob
>> j);
>> + else
>> + __i915_gem_object_make_purgeable(obj
>> );
>> + } else {
>> + i915_gem_object_make_unshrinkable(obj);
>> }
>> - i915_gem_object_put(obj);
>> +
>> + obj->mm.ttm_shrinkable = shrinkable;
>> }
>> + i915_gem_object_put(obj);
>>
>> /*
>> * Put on the correct LRU list depending on the MADV status
>> diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
>> b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
>> index 56ad8ef325840..904cb4da6c9b3 100644
>> --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
>> +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
>> @@ -127,7 +127,7 @@ static void ttm_bo_init_reserved_sys_man(struct
>> kunit *test)
>> dma_resv_unlock(bo->base.resv);
>>
>> KUNIT_EXPECT_EQ(test, err, 0);
>> - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
>> + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
>> KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
>> KUNIT_EXPECT_EQ(test, bo->type, bo_type);
>> KUNIT_EXPECT_EQ(test, bo->page_alignment, PAGE_SIZE);
>> @@ -176,7 +176,7 @@ static void ttm_bo_init_reserved_mock_man(struct
>> kunit *test)
>> dma_resv_unlock(bo->base.resv);
>>
>> KUNIT_EXPECT_EQ(test, err, 0);
>> - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
>> + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
>> KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
>> KUNIT_EXPECT_EQ(test, bo->type, bo_type);
>> KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
>> @@ -969,6 +969,8 @@ static void
>> ttm_bo_validate_allowed_only_evict(struct kunit *test)
>> ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
>> }
>>
>> +extern const struct drm_gem_object_funcs ttm_deleted_object_funcs;
>> +
>> static void ttm_bo_validate_deleted_evict(struct kunit *test)
>> {
>> struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
>> @@ -999,7 +1001,7 @@ static void ttm_bo_validate_deleted_evict(struct
>> kunit *test)
>> KUNIT_EXPECT_EQ(test, ttm_resource_manager_usage(man), big);
>>
>> dma_resv_unlock(bo_big->base.resv);
>> - bo_big->deleted = true;
>> + bo_big->base.funcs = &ttm_deleted_object_funcs;
>>
>> bo_small = ttm_bo_kunit_init(test, test->priv, small, NULL);
>> bo_small->type = bo_type;
>> diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
>> b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
>> index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
>> --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
>> +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
>> @@ -189,8 +189,6 @@ struct ttm_buffer_object
>> *ttm_bo_kunit_init(struct kunit *test,
>> bo->bdev = devs->ttm_dev;
>> bo->destroy = dummy_ttm_bo_destroy;
>>
>> - kref_init(&bo->kref);
>> -
>> return bo;
>> }
>> EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
>> b/drivers/gpu/drm/ttm/ttm_bo.c
>> index 3980f376e3ba4..2b470c1746f60 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
>> @@ -246,88 +246,84 @@ static void ttm_bo_delayed_delete(struct
>> work_struct *work)
>> ttm_bo_put(bo);
>> }
>>
>> -static void ttm_bo_release(struct kref *kref)
>> +/*
>> + * All other callbacks should never ever be called on a deleted TTM
>> object.
>> + */
>> +const struct drm_gem_object_funcs ttm_deleted_object_funcs = {
>> + .free = ttm_bo_free
>> +};
>> +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
>> +
>> +/* Returns true if the BO is about to get deleted */
>> +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
>> +{
>> + return bo->base.funcs == &ttm_deleted_object_funcs;
>> +}
>> +
>> +void ttm_bo_fini(struct ttm_buffer_object *bo)
>> {
>> - struct ttm_buffer_object *bo =
>> - container_of(kref, struct ttm_buffer_object, kref);
>> struct ttm_device *bdev = bo->bdev;
>> int ret;
>>
>> WARN_ON_ONCE(bo->pin_count);
>> WARN_ON_ONCE(bo->bulk_move);
>>
>> - if (!bo->deleted) {
>> - ret = ttm_bo_individualize_resv(bo);
>> - if (ret) {
>> - /* Last resort, if we fail to allocate
>> memory for the
>> - * fences block for the BO to become idle
>> - */
>> - dma_resv_wait_timeout(bo->base.resv,
>> -
>> DMA_RESV_USAGE_BOOKKEEP, false,
>> - 30 * HZ);
>> - }
>> + ret = ttm_bo_individualize_resv(bo);
>> + if (ret) {
>> + /* Last resort, if we fail to allocate memory for
>> the
>> + * fences block for the BO to become idle
>> + */
>> + dma_resv_wait_timeout(bo->base.resv,
>> DMA_RESV_USAGE_BOOKKEEP,
>> + false, 30 * HZ);
>> + }
>>
>> - if (bdev->funcs->release_notify)
>> - bdev->funcs->release_notify(bo);
>> + if (bo->bdev->funcs->release_notify)
>> + bo->bdev->funcs->release_notify(bo);
>>
>> - drm_vma_offset_remove(bdev->vma_manager, &bo-
>>> base.vma_node);
>> - ttm_mem_io_free(bdev, bo->resource);
>> + drm_vma_offset_remove(bdev->vma_manager, &bo-
>>> base.vma_node);
>> + ttm_mem_io_free(bdev, bo->resource);
>>
>> - if (!dma_resv_test_signaled(&bo->base._resv,
>> - DMA_RESV_USAGE_BOOKKEEP)
>> ||
>> - (want_init_on_free() && (bo->ttm != NULL)) ||
>> - bo->type == ttm_bo_type_sg ||
>> - !dma_resv_trylock(bo->base.resv)) {
>> - /* The BO is not idle, resurrect it for
>> delayed destroy */
>> - ttm_bo_flush_all_fences(bo);
>> - bo->deleted = true;
>> + if (!dma_resv_test_signaled(&bo->base._resv,
>> DMA_RESV_USAGE_BOOKKEEP) ||
>> + (want_init_on_free() && (bo->ttm != NULL)) ||
>> + bo->type == ttm_bo_type_sg ||
>> + !dma_resv_trylock(bo->base.resv)) {
>> + /* The BO is not idle, resurrect it for delayed
>> destroy */
>> + ttm_bo_flush_all_fences(bo);
>>
>> - spin_lock(&bdev->lru_lock);
>> -
>> - /*
>> - * Make pinned bos immediately available to
>> - * shrinkers, now that they are queued for
>> - * destruction.
>> - *
>> - * FIXME: QXL is triggering this. Can be
>> removed when the
>> - * driver is fixed.
>> - */
>> - if (bo->pin_count) {
>> - bo->pin_count = 0;
>> - ttm_resource_move_to_lru_tail(bo-
>>> resource);
>> - }
>> + spin_lock(&bo->bdev->lru_lock);
>>
>> - kref_init(&bo->kref);
>> - spin_unlock(&bdev->lru_lock);
>> + /*
>> + * Make pinned bos immediately available to
>> + * shrinkers, now that they are queued for
>> + * destruction.
>> + *
>> + * FIXME: QXL is triggering this. Can be removed
>> when the
>> + * driver is fixed.
>> + */
>> + if (bo->pin_count) {
>> + bo->pin_count = 0;
>> + ttm_resource_move_to_lru_tail(bo->resource);
>> + }
>>
>> - INIT_WORK(&bo->delayed_delete,
>> ttm_bo_delayed_delete);
>> + kref_init(&bo->base.refcount);
>> + bo->base.funcs = &ttm_deleted_object_funcs;
>> + spin_unlock(&bo->bdev->lru_lock);
>>
>> - /* Schedule the worker on the closest NUMA
>> node. This
>> - * improves performance since system memory
>> might be
>> - * cleared on free and that is best done on
>> a CPU core
>> - * close to it.
>> - */
>> - queue_work_node(bdev->pool.nid, bdev->wq,
>> &bo->delayed_delete);
>> - return;
>> - }
>> + INIT_WORK(&bo->delayed_delete,
>> ttm_bo_delayed_delete);
>>
>> + /* Schedule the worker on the closest NUMA node.
>> This
>> + * improves performance since system memory might be
>> + * cleared on free and that is best done on a CPU
>> core
>> + * close to it.
>> + */
>> + queue_work_node(bdev->pool.nid, bdev->wq, &bo-
>>> delayed_delete);
>> + } else {
>> ttm_bo_cleanup_memtype_use(bo);
>> dma_resv_unlock(bo->base.resv);
>> - }
>>
>> - atomic_dec(&ttm_glob.bo_count);
>> - bo->destroy(bo);
>> -}
>> -
>> -/* TODO: remove! */
>> -void ttm_bo_put(struct ttm_buffer_object *bo)
>> -{
>> - kref_put(&bo->kref, ttm_bo_release);
>> -}
>> -
>> -void ttm_bo_fini(struct ttm_buffer_object *bo)
>> -{
>> - ttm_bo_put(bo);
>> + atomic_dec(&ttm_glob.bo_count);
>> + bo->destroy(bo);
>> + }
>> }
>> EXPORT_SYMBOL(ttm_bo_fini);
>>
>> @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device *bdev,
>> struct ttm_resource_manager *man
>> if (!bo->resource || bo->resource->mem_type != mem_type)
>> goto out_bo_moved;
>>
>> - if (bo->deleted) {
>> + if (ttm_bo_is_zombie(bo)) {
>> ret = ttm_bo_wait_ctx(bo, ctx);
>> if (!ret)
>> ttm_bo_cleanup_memtype_use(bo);
>> @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk
>> *walk, struct ttm_buffer_object *
>> if (bo->pin_count || !bo->bdev->funcs->eviction_valuable(bo,
>> evict_walk->place))
>> return 0;
>>
>> - if (bo->deleted) {
>> + if (ttm_bo_is_zombie(bo)) {
>> lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
>> if (!lret)
>> ttm_bo_cleanup_memtype_use(bo);
>> @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct ttm_device
>> *bdev,
>> void ttm_bo_pin(struct ttm_buffer_object *bo)
>> {
>> dma_resv_assert_held(bo->base.resv);
>> - WARN_ON_ONCE(!kref_read(&bo->kref));
>> spin_lock(&bo->bdev->lru_lock);
>> if (bo->resource)
>> ttm_resource_del_bulk_move(bo->resource, bo);
>> @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
>> void ttm_bo_unpin(struct ttm_buffer_object *bo)
>> {
>> dma_resv_assert_held(bo->base.resv);
>> - WARN_ON_ONCE(!kref_read(&bo->kref));
>> if (WARN_ON_ONCE(!bo->pin_count))
>> return;
>>
>> @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct ttm_device *bdev,
>> struct ttm_buffer_object *bo,
>> {
>> int ret;
>>
>> - kref_init(&bo->kref);
>> bo->bdev = bdev;
>> bo->type = type;
>> bo->page_alignment = alignment;
>> @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk,
>> struct ttm_buffer_object *bo)
>> goto out;
>> }
>>
>> - if (bo->deleted) {
>> - pgoff_t num_pages = tt->num_pages;
>> + if (ttm_bo_is_zombie(bo)) {
>> + pgoff_t num_pages = bo->ttm->num_pages;
>>
>> ret = ttm_bo_wait_ctx(bo, ctx);
>> if (ret)
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
>> b/drivers/gpu/drm/ttm/ttm_bo_internal.h
>> index e0d48eac74b03..ded2a47be0bcb 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
>> +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
>> @@ -27,6 +27,14 @@
>>
>> #include <drm/ttm/ttm_bo.h>
>>
>> +static inline void ttm_bo_free(struct drm_gem_object *gobj)
>> +{
>> + struct ttm_buffer_object *bo = container_of(gobj,
>> typeof(*bo), base);
>> +
>> + atomic_dec(&ttm_glob.bo_count);
>> + bo->destroy(bo);
>> +}
>> +
>> /**
>> * ttm_bo_get - reference a struct ttm_buffer_object
>> *
>> @@ -34,7 +42,7 @@
>> */
>> static inline void ttm_bo_get(struct ttm_buffer_object *bo)
>> {
>> - kref_get(&bo->kref);
>> + drm_gem_object_get(&bo->base);
>> }
>>
>> /**
>> @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
>> ttm_buffer_object *bo)
>> static inline __must_check struct ttm_buffer_object *
>> ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
>> {
>> - if (!kref_get_unless_zero(&bo->kref))
>> + if (!kref_get_unless_zero(&bo->base.refcount))
>> return NULL;
>> return bo;
>> }
>>
>> -void ttm_bo_put(struct ttm_buffer_object *bo);
>> +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
>> +{
>> + drm_gem_object_put(&bo->base);
>> +}
>>
>> #endif
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
>> b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> index 3e3c201a02226..7ed085adf1c9b 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> @@ -41,6 +41,18 @@
>>
>> #include "ttm_bo_internal.h"
>>
>> +static void ttm_transfer_object_free(struct drm_gem_object *obj)
>> +{
>> + struct ttm_buffer_object *bo =
>> + container_of(obj, struct ttm_buffer_object, base);
>> +
>> + ttm_bo_fini(bo);
>> +}
>> +
>> +const struct drm_gem_object_funcs ttm_transfer_object_funcs = {
>> + .free = ttm_transfer_object_free,
>> +};
>> +
>> struct ttm_transfer_obj {
>> struct ttm_buffer_object base;
>> struct ttm_buffer_object *bo;
>> @@ -247,7 +259,8 @@ static int ttm_buffer_object_transfer(struct
>> ttm_buffer_object *bo,
>> atomic_inc(&ttm_glob.bo_count);
>> drm_vma_node_reset(&fbo->base.base.vma_node);
>>
>> - kref_init(&fbo->base.kref);
>> + kref_init(&fbo->base.base.refcount);
>> + fbo->base.base.funcs = &ttm_transfer_object_funcs;
>> fbo->base.destroy = &ttm_transfered_destroy;
>> fbo->base.pin_count = 0;
>> if (bo->type != ttm_bo_type_sg)
>> diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
>> index 85e6d9a0f575b..5843f850339c7 100644
>> --- a/drivers/gpu/drm/xe/xe_bo.c
>> +++ b/drivers/gpu/drm/xe/xe_bo.c
>> @@ -1651,7 +1651,7 @@ static bool xe_ttm_bo_lock_in_destructor(struct
>> ttm_buffer_object *ttm_bo)
>> struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
>> bool locked;
>>
>> - xe_assert(xe, !kref_read(&ttm_bo->kref));
>> + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
>>
>> /*
>> * We can typically only race with TTM trylocking under the
>> diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
>> index 8310bc3d55f90..1eae9eea5ff32 100644
>> --- a/include/drm/ttm/ttm_bo.h
>> +++ b/include/drm/ttm/ttm_bo.h
>> @@ -78,11 +78,8 @@ enum ttm_bo_type {
>> * @type: The bo type.
>> * @page_alignment: Page alignment.
>> * @destroy: Destruction function. If NULL, kfree is used.
>> - * @kref: Reference count of this buffer object. When this refcount
>> reaches
>> - * zero, the object is destroyed or put on the delayed delete list.
>> * @resource: structure describing current placement.
>> * @ttm: TTM structure holding system pages.
>> - * @deleted: True if the object is only a zombie and already
>> deleted.
>> * @bulk_move: The bulk move object.
>> * @priority: Priority for LRU, BOs with lower priority are evicted
>> first.
>> * @pin_count: Pin count.
>> @@ -109,17 +106,11 @@ struct ttm_buffer_object {
>> uint32_t page_alignment;
>> void (*destroy) (struct ttm_buffer_object *);
>>
>> - /*
>> - * Members not needing protection.
>> - */
>> - struct kref kref;
>> -
>> /*
>> * Members protected by the bo::resv::reserved lock.
>> */
>> struct ttm_resource *resource;
>> struct ttm_tt *ttm;
>> - bool deleted;
>> struct ttm_lru_bulk_move *bulk_move;
>> unsigned priority;
>> unsigned pin_count;
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-06 14:49 ` Christian König
@ 2026-07-06 17:01 ` Thomas Hellström
2026-07-06 17:51 ` Matthew Brost
2026-07-06 18:23 ` Christian König
0 siblings, 2 replies; 26+ messages in thread
From: Thomas Hellström @ 2026-07-06 17:01 UTC (permalink / raw)
To: Christian König, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Matthew Brost, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
On Mon, 2026-07-06 at 16:49 +0200, Christian König wrote:
> On 7/6/26 15:14, Thomas Hellström wrote:
> > On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> > > From: Christian König <christian.koenig@amd.com>
> > >
> > > Instead of keeping a separate reference count for the TTM object
> > > also
> > > use
> > > the reference count for DRM GEM objects inside TTM.
> > >
> > > Apart from avoiding two reference counts for one object this
> > > approach
> > > has
> > > the clear advantage of being able to use drm_exec inside TTM.
> > >
> > > v2: adjust XE assert as well and re-enable disabled test
> > > v3: handle another case in i915
> > > v4: set GEM driver funcs of transfer BOs to point to the TTM free
> > > callback (Natalie)
> >
> > I think the main review issue from the last time this was on the
> > table
> > was that we shouldn't resurrect the gem refcount. Apart from the
> > risc
> > of getting barriers wrong, both xe and IIRC i915 partly rely on the
> > gem
> > refcount never being resurrected and that callbacks for bos with
> > zero
> > gem refcount means that the gem part of the object is unusable.
>
> I've spend quite some time thinking about that and came to the
> conclusion that this is actually harmless.
>
> The drivers shouldn't be able to see the resurected BO, except if
> they go over the LRU list manually (which they shouldn't).
The shrinker uses the TTM helpers for this. Basically the check needs
to be ported to use the zombie interface but the present change also
widens the window where we can't evict / shrink at all due to zero
refcounts.
While it might be made harmless, resurrecting a refcount like this is
IMO not something that should leak into the gem refcount. Nobody else
does this in the kernel tree. The bo in reality becomes a zombie once
the gem refcount reaches zero.
>
> >
> > For example xe_bo.c:
> >
> > if (!xe_bo_is_xe_bo(bo) || !xe_bo_get_unless_zero(xe_bo))
> > return xe_bo_shrink_purge(ctx, bo, scanned);
> >
> > So IIRC the conclusion was when removing the ttm refcount we
> > shouldn't
> > attempt to resurrect the gem one. If the get_unless_zero() fails
> > during
> > evict walk, we simply find something to wait for. See previous
> > discussion there.
>
> Yeah, I considered that as well but the problem is we often doesn't
> have anything to wait on.
That's not the conclusion of the previous discussion?
https://lore.kernel.org/dri-devel/20250716160555.20217-2-christian.koenig@amd.com/#r
>
> >
> > I fully support removing the ttm refcount, but not if it means
> > resurrecting the gem refcount.
> >
> > If we want to sidestep that problem, in favour of getting the
> > proposed
> > locking functionality in and future proof it, I suggest using
> >
> > https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
> >
> > And rebase this series on that. This means we can use the ttm
> > refcount
> > for the transaction refcounting, and also that if we add a dma-buf
> > map
> > interface with a dma_resv_txn_obj, we could use that to also have
> > exhaustive eviction that originates from a dma_buf map.
>
> I don't think that this is a good idea. It just adds another layer of
> abstraction and doesn't solve the problem in any way possible.
This comment confuses me. Exactly what problem isn't solved by this,
and which of the stated benefits/use-cases in the cover-letter do you
think aren't worthwhile?
Also for reference: (Section at the end and follow-up messages)
https://lore.kernel.org/all/3716d43462188590743060755b37e3d060f7600f.camel@linux.intel.com/
Thanks,
Thomas
> Regards,
> Christian.
>
> >
> > /Thomas
> >
> > >
> > > Signed-off-by: tChristian König <christian.koenig@amd.com>
> > > Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> > > ---
> > > drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++---
> > > drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
> > > drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
> > > drivers/gpu/drm/ttm/ttm_bo.c | 135
> > > +++++++++++--
> > > ----------
> > > drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
> > > drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
> > > drivers/gpu/drm/xe/xe_bo.c | 2 +-
> > > include/drm/ttm/ttm_bo.h | 9 --
> > > 8 files changed, 111 insertions(+), 112 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > index df3fcc2b1248e..642296602de69 100644
> > > --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
> > > drm_i915_gem_object *obj)
> > > * Don't manipulate the TTM LRUs while in TTM bo
> > > destruction.
> > > * We're called through i915_ttm_delete_mem_notify().
> > > */
> > > - if (!kref_read(&bo->kref))
> > > + if (!kref_read(&bo->base.refcount))
> > > return;
> > >
> > > /*
> > > @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
> > > drm_i915_gem_object *obj)
> > > *
> > > * TODO: consider maybe also bumping the shrinker list
> > > here
> > > when we have
> > > * already unpinned it, which should give us something
> > > more
> > > like an LRU.
> > > - *
> > > - * TODO: There is a small window of opportunity for this
> > > function to
> > > - * get called from eviction after we've dropped the last
> > > GEM
> > > refcount,
> > > - * but before the TTM deleted flag is set on the object.
> > > Avoid
> > > - * adjusting the shrinker list in such cases, since the
> > > object is
> > > - * not available to the shrinker anyway due to its zero
> > > refcount.
> > > - * To fix this properly we should move to a TTM shrinker
> > > LRU
> > > list for
> > > - * these objects.
> > > */
> > > - if (kref_get_unless_zero(&obj->base.refcount)) {
> > > - if (shrinkable != obj->mm.ttm_shrinkable) {
> > > - if (shrinkable) {
> > > - if (obj->mm.madv ==
> > > I915_MADV_WILLNEED)
> > > -
> > > __i915_gem_object_make_s
> > > hrinkable(obj);
> > > - else
> > > -
> > > __i915_gem_object_make_p
> > > urgeable(obj);
> > > - } else {
> > > -
> > > i915_gem_object_make_unshrinkabl
> > > e(obj);
> > > - }
> > > -
> > > - obj->mm.ttm_shrinkable = shrinkable;
> > > + i915_gem_object_get(obj);
> > > + if (shrinkable != obj->mm.ttm_shrinkable) {
> > > + if (shrinkable) {
> > > + if (obj->mm.madv == I915_MADV_WILLNEED)
> > > + __i915_gem_object_make_shrinkabl
> > > e(ob
> > > j);
> > > + else
> > > + __i915_gem_object_make_purgeable
> > > (obj
> > > );
> > > + } else {
> > > + i915_gem_object_make_unshrinkable(obj);
> > > }
> > > - i915_gem_object_put(obj);
> > > +
> > > + obj->mm.ttm_shrinkable = shrinkable;
> > > }
> > > + i915_gem_object_put(obj);
> > >
> > > /*
> > > * Put on the correct LRU list depending on the MADV
> > > status
> > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > index 56ad8ef325840..904cb4da6c9b3 100644
> > > --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > @@ -127,7 +127,7 @@ static void
> > > ttm_bo_init_reserved_sys_man(struct
> > > kunit *test)
> > > dma_resv_unlock(bo->base.resv);
> > >
> > > KUNIT_EXPECT_EQ(test, err, 0);
> > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
> > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > KUNIT_EXPECT_EQ(test, bo->page_alignment, PAGE_SIZE);
> > > @@ -176,7 +176,7 @@ static void
> > > ttm_bo_init_reserved_mock_man(struct
> > > kunit *test)
> > > dma_resv_unlock(bo->base.resv);
> > >
> > > KUNIT_EXPECT_EQ(test, err, 0);
> > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
> > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
> > > @@ -969,6 +969,8 @@ static void
> > > ttm_bo_validate_allowed_only_evict(struct kunit *test)
> > > ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
> > > }
> > >
> > > +extern const struct drm_gem_object_funcs
> > > ttm_deleted_object_funcs;
> > > +
> > > static void ttm_bo_validate_deleted_evict(struct kunit *test)
> > > {
> > > struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
> > > @@ -999,7 +1001,7 @@ static void
> > > ttm_bo_validate_deleted_evict(struct
> > > kunit *test)
> > > KUNIT_EXPECT_EQ(test, ttm_resource_manager_usage(man),
> > > big);
> > >
> > > dma_resv_unlock(bo_big->base.resv);
> > > - bo_big->deleted = true;
> > > + bo_big->base.funcs = &ttm_deleted_object_funcs;
> > >
> > > bo_small = ttm_bo_kunit_init(test, test->priv, small,
> > > NULL);
> > > bo_small->type = bo_type;
> > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
> > > --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > @@ -189,8 +189,6 @@ struct ttm_buffer_object
> > > *ttm_bo_kunit_init(struct kunit *test,
> > > bo->bdev = devs->ttm_dev;
> > > bo->destroy = dummy_ttm_bo_destroy;
> > >
> > > - kref_init(&bo->kref);
> > > -
> > > return bo;
> > > }
> > > EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
> > > diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> > > b/drivers/gpu/drm/ttm/ttm_bo.c
> > > index 3980f376e3ba4..2b470c1746f60 100644
> > > --- a/drivers/gpu/drm/ttm/ttm_bo.c
> > > +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> > > @@ -246,88 +246,84 @@ static void ttm_bo_delayed_delete(struct
> > > work_struct *work)
> > > ttm_bo_put(bo);
> > > }
> > >
> > > -static void ttm_bo_release(struct kref *kref)
> > > +/*
> > > + * All other callbacks should never ever be called on a deleted
> > > TTM
> > > object.
> > > + */
> > > +const struct drm_gem_object_funcs ttm_deleted_object_funcs = {
> > > + .free = ttm_bo_free
> > > +};
> > > +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
> > > +
> > > +/* Returns true if the BO is about to get deleted */
> > > +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
> > > +{
> > > + return bo->base.funcs == &ttm_deleted_object_funcs;
> > > +}
> > > +
> > > +void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > {
> > > - struct ttm_buffer_object *bo =
> > > - container_of(kref, struct ttm_buffer_object, kref);
> > > struct ttm_device *bdev = bo->bdev;
> > > int ret;
> > >
> > > WARN_ON_ONCE(bo->pin_count);
> > > WARN_ON_ONCE(bo->bulk_move);
> > >
> > > - if (!bo->deleted) {
> > > - ret = ttm_bo_individualize_resv(bo);
> > > - if (ret) {
> > > - /* Last resort, if we fail to allocate
> > > memory for the
> > > - * fences block for the BO to become
> > > idle
> > > - */
> > > - dma_resv_wait_timeout(bo->base.resv,
> > > -
> > > DMA_RESV_USAGE_BOOKKEEP, false,
> > > - 30 * HZ);
> > > - }
> > > + ret = ttm_bo_individualize_resv(bo);
> > > + if (ret) {
> > > + /* Last resort, if we fail to allocate memory
> > > for
> > > the
> > > + * fences block for the BO to become idle
> > > + */
> > > + dma_resv_wait_timeout(bo->base.resv,
> > > DMA_RESV_USAGE_BOOKKEEP,
> > > + false, 30 * HZ);
> > > + }
> > >
> > > - if (bdev->funcs->release_notify)
> > > - bdev->funcs->release_notify(bo);
> > > + if (bo->bdev->funcs->release_notify)
> > > + bo->bdev->funcs->release_notify(bo);
> > >
> > > - drm_vma_offset_remove(bdev->vma_manager, &bo-
> > > > base.vma_node);
> > > - ttm_mem_io_free(bdev, bo->resource);
> > > + drm_vma_offset_remove(bdev->vma_manager, &bo-
> > > > base.vma_node);
> > > + ttm_mem_io_free(bdev, bo->resource);
> > >
> > > - if (!dma_resv_test_signaled(&bo->base._resv,
> > > -
> > > DMA_RESV_USAGE_BOOKKEEP)
> > > > >
> > > - (want_init_on_free() && (bo->ttm != NULL))
> > > ||
> > > - bo->type == ttm_bo_type_sg ||
> > > - !dma_resv_trylock(bo->base.resv)) {
> > > - /* The BO is not idle, resurrect it for
> > > delayed destroy */
> > > - ttm_bo_flush_all_fences(bo);
> > > - bo->deleted = true;
> > > + if (!dma_resv_test_signaled(&bo->base._resv,
> > > DMA_RESV_USAGE_BOOKKEEP) ||
> > > + (want_init_on_free() && (bo->ttm != NULL)) ||
> > > + bo->type == ttm_bo_type_sg ||
> > > + !dma_resv_trylock(bo->base.resv)) {
> > > + /* The BO is not idle, resurrect it for delayed
> > > destroy */
> > > + ttm_bo_flush_all_fences(bo);
> > >
> > > - spin_lock(&bdev->lru_lock);
> > > -
> > > - /*
> > > - * Make pinned bos immediately available
> > > to
> > > - * shrinkers, now that they are queued
> > > for
> > > - * destruction.
> > > - *
> > > - * FIXME: QXL is triggering this. Can be
> > > removed when the
> > > - * driver is fixed.
> > > - */
> > > - if (bo->pin_count) {
> > > - bo->pin_count = 0;
> > > -
> > > ttm_resource_move_to_lru_tail(bo-
> > > > resource);
> > > - }
> > > + spin_lock(&bo->bdev->lru_lock);
> > >
> > > - kref_init(&bo->kref);
> > > - spin_unlock(&bdev->lru_lock);
> > > + /*
> > > + * Make pinned bos immediately available to
> > > + * shrinkers, now that they are queued for
> > > + * destruction.
> > > + *
> > > + * FIXME: QXL is triggering this. Can be removed
> > > when the
> > > + * driver is fixed.
> > > + */
> > > + if (bo->pin_count) {
> > > + bo->pin_count = 0;
> > > + ttm_resource_move_to_lru_tail(bo-
> > > >resource);
> > > + }
> > >
> > > - INIT_WORK(&bo->delayed_delete,
> > > ttm_bo_delayed_delete);
> > > + kref_init(&bo->base.refcount);
> > > + bo->base.funcs = &ttm_deleted_object_funcs;
> > > + spin_unlock(&bo->bdev->lru_lock);
> > >
> > > - /* Schedule the worker on the closest
> > > NUMA
> > > node. This
> > > - * improves performance since system
> > > memory
> > > might be
> > > - * cleared on free and that is best done
> > > on
> > > a CPU core
> > > - * close to it.
> > > - */
> > > - queue_work_node(bdev->pool.nid, bdev-
> > > >wq,
> > > &bo->delayed_delete);
> > > - return;
> > > - }
> > > + INIT_WORK(&bo->delayed_delete,
> > > ttm_bo_delayed_delete);
> > >
> > > + /* Schedule the worker on the closest NUMA node.
> > > This
> > > + * improves performance since system memory
> > > might be
> > > + * cleared on free and that is best done on a
> > > CPU
> > > core
> > > + * close to it.
> > > + */
> > > + queue_work_node(bdev->pool.nid, bdev->wq, &bo-
> > > > delayed_delete);
> > > + } else {
> > > ttm_bo_cleanup_memtype_use(bo);
> > > dma_resv_unlock(bo->base.resv);
> > > - }
> > >
> > > - atomic_dec(&ttm_glob.bo_count);
> > > - bo->destroy(bo);
> > > -}
> > > -
> > > -/* TODO: remove! */
> > > -void ttm_bo_put(struct ttm_buffer_object *bo)
> > > -{
> > > - kref_put(&bo->kref, ttm_bo_release);
> > > -}
> > > -
> > > -void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > -{
> > > - ttm_bo_put(bo);
> > > + atomic_dec(&ttm_glob.bo_count);
> > > + bo->destroy(bo);
> > > + }
> > > }
> > > EXPORT_SYMBOL(ttm_bo_fini);
> > >
> > > @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device
> > > *bdev,
> > > struct ttm_resource_manager *man
> > > if (!bo->resource || bo->resource->mem_type != mem_type)
> > > goto out_bo_moved;
> > >
> > > - if (bo->deleted) {
> > > + if (ttm_bo_is_zombie(bo)) {
> > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > if (!ret)
> > > ttm_bo_cleanup_memtype_use(bo);
> > > @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct
> > > ttm_lru_walk
> > > *walk, struct ttm_buffer_object *
> > > if (bo->pin_count || !bo->bdev->funcs-
> > > >eviction_valuable(bo,
> > > evict_walk->place))
> > > return 0;
> > >
> > > - if (bo->deleted) {
> > > + if (ttm_bo_is_zombie(bo)) {
> > > lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
> > > if (!lret)
> > > ttm_bo_cleanup_memtype_use(bo);
> > > @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct
> > > ttm_device
> > > *bdev,
> > > void ttm_bo_pin(struct ttm_buffer_object *bo)
> > > {
> > > dma_resv_assert_held(bo->base.resv);
> > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > spin_lock(&bo->bdev->lru_lock);
> > > if (bo->resource)
> > > ttm_resource_del_bulk_move(bo->resource, bo);
> > > @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
> > > void ttm_bo_unpin(struct ttm_buffer_object *bo)
> > > {
> > > dma_resv_assert_held(bo->base.resv);
> > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > if (WARN_ON_ONCE(!bo->pin_count))
> > > return;
> > >
> > > @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct ttm_device
> > > *bdev,
> > > struct ttm_buffer_object *bo,
> > > {
> > > int ret;
> > >
> > > - kref_init(&bo->kref);
> > > bo->bdev = bdev;
> > > bo->type = type;
> > > bo->page_alignment = alignment;
> > > @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk
> > > *walk,
> > > struct ttm_buffer_object *bo)
> > > goto out;
> > > }
> > >
> > > - if (bo->deleted) {
> > > - pgoff_t num_pages = tt->num_pages;
> > > + if (ttm_bo_is_zombie(bo)) {
> > > + pgoff_t num_pages = bo->ttm->num_pages;
> > >
> > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > if (ret)
> > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > index e0d48eac74b03..ded2a47be0bcb 100644
> > > --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > @@ -27,6 +27,14 @@
> > >
> > > #include <drm/ttm/ttm_bo.h>
> > >
> > > +static inline void ttm_bo_free(struct drm_gem_object *gobj)
> > > +{
> > > + struct ttm_buffer_object *bo = container_of(gobj,
> > > typeof(*bo), base);
> > > +
> > > + atomic_dec(&ttm_glob.bo_count);
> > > + bo->destroy(bo);
> > > +}
> > > +
> > > /**
> > > * ttm_bo_get - reference a struct ttm_buffer_object
> > > *
> > > @@ -34,7 +42,7 @@
> > > */
> > > static inline void ttm_bo_get(struct ttm_buffer_object *bo)
> > > {
> > > - kref_get(&bo->kref);
> > > + drm_gem_object_get(&bo->base);
> > > }
> > >
> > > /**
> > > @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
> > > ttm_buffer_object *bo)
> > > static inline __must_check struct ttm_buffer_object *
> > > ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
> > > {
> > > - if (!kref_get_unless_zero(&bo->kref))
> > > + if (!kref_get_unless_zero(&bo->base.refcount))
> > > return NULL;
> > > return bo;
> > > }
> > >
> > > -void ttm_bo_put(struct ttm_buffer_object *bo);
> > > +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
> > > +{
> > > + drm_gem_object_put(&bo->base);
> > > +}
> > >
> > > #endif
> > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > index 3e3c201a02226..7ed085adf1c9b 100644
> > > --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > @@ -41,6 +41,18 @@
> > >
> > > #include "ttm_bo_internal.h"
> > >
> > > +static void ttm_transfer_object_free(struct drm_gem_object *obj)
> > > +{
> > > + struct ttm_buffer_object *bo =
> > > + container_of(obj, struct ttm_buffer_object,
> > > base);
> > > +
> > > + ttm_bo_fini(bo);
> > > +}
> > > +
> > > +const struct drm_gem_object_funcs ttm_transfer_object_funcs = {
> > > + .free = ttm_transfer_object_free,
> > > +};
> > > +
> > > struct ttm_transfer_obj {
> > > struct ttm_buffer_object base;
> > > struct ttm_buffer_object *bo;
> > > @@ -247,7 +259,8 @@ static int ttm_buffer_object_transfer(struct
> > > ttm_buffer_object *bo,
> > > atomic_inc(&ttm_glob.bo_count);
> > > drm_vma_node_reset(&fbo->base.base.vma_node);
> > >
> > > - kref_init(&fbo->base.kref);
> > > + kref_init(&fbo->base.base.refcount);
> > > + fbo->base.base.funcs = &ttm_transfer_object_funcs;
> > > fbo->base.destroy = &ttm_transfered_destroy;
> > > fbo->base.pin_count = 0;
> > > if (bo->type != ttm_bo_type_sg)
> > > diff --git a/drivers/gpu/drm/xe/xe_bo.c
> > > b/drivers/gpu/drm/xe/xe_bo.c
> > > index 85e6d9a0f575b..5843f850339c7 100644
> > > --- a/drivers/gpu/drm/xe/xe_bo.c
> > > +++ b/drivers/gpu/drm/xe/xe_bo.c
> > > @@ -1651,7 +1651,7 @@ static bool
> > > xe_ttm_bo_lock_in_destructor(struct
> > > ttm_buffer_object *ttm_bo)
> > > struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
> > > bool locked;
> > >
> > > - xe_assert(xe, !kref_read(&ttm_bo->kref));
> > > + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
> > >
> > > /*
> > > * We can typically only race with TTM trylocking under
> > > the
> > > diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
> > > index 8310bc3d55f90..1eae9eea5ff32 100644
> > > --- a/include/drm/ttm/ttm_bo.h
> > > +++ b/include/drm/ttm/ttm_bo.h
> > > @@ -78,11 +78,8 @@ enum ttm_bo_type {
> > > * @type: The bo type.
> > > * @page_alignment: Page alignment.
> > > * @destroy: Destruction function. If NULL, kfree is used.
> > > - * @kref: Reference count of this buffer object. When this
> > > refcount
> > > reaches
> > > - * zero, the object is destroyed or put on the delayed delete
> > > list.
> > > * @resource: structure describing current placement.
> > > * @ttm: TTM structure holding system pages.
> > > - * @deleted: True if the object is only a zombie and already
> > > deleted.
> > > * @bulk_move: The bulk move object.
> > > * @priority: Priority for LRU, BOs with lower priority are
> > > evicted
> > > first.
> > > * @pin_count: Pin count.
> > > @@ -109,17 +106,11 @@ struct ttm_buffer_object {
> > > uint32_t page_alignment;
> > > void (*destroy) (struct ttm_buffer_object *);
> > >
> > > - /*
> > > - * Members not needing protection.
> > > - */
> > > - struct kref kref;
> > > -
> > > /*
> > > * Members protected by the bo::resv::reserved lock.
> > > */
> > > struct ttm_resource *resource;
> > > struct ttm_tt *ttm;
> > > - bool deleted;
> > > struct ttm_lru_bulk_move *bulk_move;
> > > unsigned priority;
> > > unsigned pin_count;
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-06 17:01 ` Thomas Hellström
@ 2026-07-06 17:51 ` Matthew Brost
2026-07-06 17:53 ` Thomas Hellström
2026-07-06 18:03 ` Thomas Hellström
2026-07-06 18:23 ` Christian König
1 sibling, 2 replies; 26+ messages in thread
From: Matthew Brost @ 2026-07-06 17:51 UTC (permalink / raw)
To: Thomas Hellström
Cc: Christian König, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Alex Deucher, dri-devel, linux-kernel,
intel-gfx, intel-xe, amd-gfx
On Mon, Jul 06, 2026 at 07:01:27PM +0200, Thomas Hellström wrote:
> On Mon, 2026-07-06 at 16:49 +0200, Christian König wrote:
> > On 7/6/26 15:14, Thomas Hellström wrote:
> > > On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> > > > From: Christian König <christian.koenig@amd.com>
> > > >
> > > > Instead of keeping a separate reference count for the TTM object
> > > > also
> > > > use
> > > > the reference count for DRM GEM objects inside TTM.
> > > >
> > > > Apart from avoiding two reference counts for one object this
> > > > approach
> > > > has
> > > > the clear advantage of being able to use drm_exec inside TTM.
> > > >
> > > > v2: adjust XE assert as well and re-enable disabled test
> > > > v3: handle another case in i915
> > > > v4: set GEM driver funcs of transfer BOs to point to the TTM free
> > > > callback (Natalie)
> > >
> > > I think the main review issue from the last time this was on the
> > > table
> > > was that we shouldn't resurrect the gem refcount. Apart from the
> > > risc
> > > of getting barriers wrong, both xe and IIRC i915 partly rely on the
> > > gem
> > > refcount never being resurrected and that callbacks for bos with
> > > zero
> > > gem refcount means that the gem part of the object is unusable.
> >
> > I've spend quite some time thinking about that and came to the
> > conclusion that this is actually harmless.
> >
> > The drivers shouldn't be able to see the resurected BO, except if
> > they go over the LRU list manually (which they shouldn't).
>
> The shrinker uses the TTM helpers for this. Basically the check needs
> to be ported to use the zombie interface but the present change also
> widens the window where we can't evict / shrink at all due to zero
> refcounts.
>
> While it might be made harmless, resurrecting a refcount like this is
> IMO not something that should leak into the gem refcount. Nobody else
> does this in the kernel tree. The bo in reality becomes a zombie once
> the gem refcount reaches zero.
When you say resurrecting a refcount - what exactly do you mean by this?
ref -> 0 -> init ref count 1 -> 0 again?
Matt
>
> >
> > >
> > > For example xe_bo.c:
> > >
> > > if (!xe_bo_is_xe_bo(bo) || !xe_bo_get_unless_zero(xe_bo))
> > > return xe_bo_shrink_purge(ctx, bo, scanned);
> > >
> > > So IIRC the conclusion was when removing the ttm refcount we
> > > shouldn't
> > > attempt to resurrect the gem one. If the get_unless_zero() fails
> > > during
> > > evict walk, we simply find something to wait for. See previous
> > > discussion there.
> >
> > Yeah, I considered that as well but the problem is we often doesn't
> > have anything to wait on.
>
> That's not the conclusion of the previous discussion?
>
> https://lore.kernel.org/dri-devel/20250716160555.20217-2-christian.koenig@amd.com/#r
>
> >
> > >
> > > I fully support removing the ttm refcount, but not if it means
> > > resurrecting the gem refcount.
> > >
> > > If we want to sidestep that problem, in favour of getting the
> > > proposed
> > > locking functionality in and future proof it, I suggest using
> > >
> > > https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
> > >
> > > And rebase this series on that. This means we can use the ttm
> > > refcount
> > > for the transaction refcounting, and also that if we add a dma-buf
> > > map
> > > interface with a dma_resv_txn_obj, we could use that to also have
> > > exhaustive eviction that originates from a dma_buf map.
> >
> > I don't think that this is a good idea. It just adds another layer of
> > abstraction and doesn't solve the problem in any way possible.
>
> This comment confuses me. Exactly what problem isn't solved by this,
> and which of the stated benefits/use-cases in the cover-letter do you
> think aren't worthwhile?
>
> Also for reference: (Section at the end and follow-up messages)
> https://lore.kernel.org/all/3716d43462188590743060755b37e3d060f7600f.camel@linux.intel.com/
>
> Thanks,
> Thomas
>
>
> > Regards,
> > Christian.
> >
> > >
> > > /Thomas
> > >
> > > >
> > > > Signed-off-by: tChristian König <christian.koenig@amd.com>
> > > > Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> > > > ---
> > > > drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++---
> > > > drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
> > > > drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
> > > > drivers/gpu/drm/ttm/ttm_bo.c | 135
> > > > +++++++++++--
> > > > ----------
> > > > drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
> > > > drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
> > > > drivers/gpu/drm/xe/xe_bo.c | 2 +-
> > > > include/drm/ttm/ttm_bo.h | 9 --
> > > > 8 files changed, 111 insertions(+), 112 deletions(-)
> > > >
> > > > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > index df3fcc2b1248e..642296602de69 100644
> > > > --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
> > > > drm_i915_gem_object *obj)
> > > > * Don't manipulate the TTM LRUs while in TTM bo
> > > > destruction.
> > > > * We're called through i915_ttm_delete_mem_notify().
> > > > */
> > > > - if (!kref_read(&bo->kref))
> > > > + if (!kref_read(&bo->base.refcount))
> > > > return;
> > > >
> > > > /*
> > > > @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
> > > > drm_i915_gem_object *obj)
> > > > *
> > > > * TODO: consider maybe also bumping the shrinker list
> > > > here
> > > > when we have
> > > > * already unpinned it, which should give us something
> > > > more
> > > > like an LRU.
> > > > - *
> > > > - * TODO: There is a small window of opportunity for this
> > > > function to
> > > > - * get called from eviction after we've dropped the last
> > > > GEM
> > > > refcount,
> > > > - * but before the TTM deleted flag is set on the object.
> > > > Avoid
> > > > - * adjusting the shrinker list in such cases, since the
> > > > object is
> > > > - * not available to the shrinker anyway due to its zero
> > > > refcount.
> > > > - * To fix this properly we should move to a TTM shrinker
> > > > LRU
> > > > list for
> > > > - * these objects.
> > > > */
> > > > - if (kref_get_unless_zero(&obj->base.refcount)) {
> > > > - if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > - if (shrinkable) {
> > > > - if (obj->mm.madv ==
> > > > I915_MADV_WILLNEED)
> > > > -
> > > > __i915_gem_object_make_s
> > > > hrinkable(obj);
> > > > - else
> > > > -
> > > > __i915_gem_object_make_p
> > > > urgeable(obj);
> > > > - } else {
> > > > -
> > > > i915_gem_object_make_unshrinkabl
> > > > e(obj);
> > > > - }
> > > > -
> > > > - obj->mm.ttm_shrinkable = shrinkable;
> > > > + i915_gem_object_get(obj);
> > > > + if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > + if (shrinkable) {
> > > > + if (obj->mm.madv == I915_MADV_WILLNEED)
> > > > + __i915_gem_object_make_shrinkabl
> > > > e(ob
> > > > j);
> > > > + else
> > > > + __i915_gem_object_make_purgeable
> > > > (obj
> > > > );
> > > > + } else {
> > > > + i915_gem_object_make_unshrinkable(obj);
> > > > }
> > > > - i915_gem_object_put(obj);
> > > > +
> > > > + obj->mm.ttm_shrinkable = shrinkable;
> > > > }
> > > > + i915_gem_object_put(obj);
> > > >
> > > > /*
> > > > * Put on the correct LRU list depending on the MADV
> > > > status
> > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > index 56ad8ef325840..904cb4da6c9b3 100644
> > > > --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > @@ -127,7 +127,7 @@ static void
> > > > ttm_bo_init_reserved_sys_man(struct
> > > > kunit *test)
> > > > dma_resv_unlock(bo->base.resv);
> > > >
> > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
> > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > KUNIT_EXPECT_EQ(test, bo->page_alignment, PAGE_SIZE);
> > > > @@ -176,7 +176,7 @@ static void
> > > > ttm_bo_init_reserved_mock_man(struct
> > > > kunit *test)
> > > > dma_resv_unlock(bo->base.resv);
> > > >
> > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
> > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
> > > > @@ -969,6 +969,8 @@ static void
> > > > ttm_bo_validate_allowed_only_evict(struct kunit *test)
> > > > ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
> > > > }
> > > >
> > > > +extern const struct drm_gem_object_funcs
> > > > ttm_deleted_object_funcs;
> > > > +
> > > > static void ttm_bo_validate_deleted_evict(struct kunit *test)
> > > > {
> > > > struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
> > > > @@ -999,7 +1001,7 @@ static void
> > > > ttm_bo_validate_deleted_evict(struct
> > > > kunit *test)
> > > > KUNIT_EXPECT_EQ(test, ttm_resource_manager_usage(man),
> > > > big);
> > > >
> > > > dma_resv_unlock(bo_big->base.resv);
> > > > - bo_big->deleted = true;
> > > > + bo_big->base.funcs = &ttm_deleted_object_funcs;
> > > >
> > > > bo_small = ttm_bo_kunit_init(test, test->priv, small,
> > > > NULL);
> > > > bo_small->type = bo_type;
> > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
> > > > --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > @@ -189,8 +189,6 @@ struct ttm_buffer_object
> > > > *ttm_bo_kunit_init(struct kunit *test,
> > > > bo->bdev = devs->ttm_dev;
> > > > bo->destroy = dummy_ttm_bo_destroy;
> > > >
> > > > - kref_init(&bo->kref);
> > > > -
> > > > return bo;
> > > > }
> > > > EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
> > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > index 3980f376e3ba4..2b470c1746f60 100644
> > > > --- a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > @@ -246,88 +246,84 @@ static void ttm_bo_delayed_delete(struct
> > > > work_struct *work)
> > > > ttm_bo_put(bo);
> > > > }
> > > >
> > > > -static void ttm_bo_release(struct kref *kref)
> > > > +/*
> > > > + * All other callbacks should never ever be called on a deleted
> > > > TTM
> > > > object.
> > > > + */
> > > > +const struct drm_gem_object_funcs ttm_deleted_object_funcs = {
> > > > + .free = ttm_bo_free
> > > > +};
> > > > +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
> > > > +
> > > > +/* Returns true if the BO is about to get deleted */
> > > > +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
> > > > +{
> > > > + return bo->base.funcs == &ttm_deleted_object_funcs;
> > > > +}
> > > > +
> > > > +void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > {
> > > > - struct ttm_buffer_object *bo =
> > > > - container_of(kref, struct ttm_buffer_object, kref);
> > > > struct ttm_device *bdev = bo->bdev;
> > > > int ret;
> > > >
> > > > WARN_ON_ONCE(bo->pin_count);
> > > > WARN_ON_ONCE(bo->bulk_move);
> > > >
> > > > - if (!bo->deleted) {
> > > > - ret = ttm_bo_individualize_resv(bo);
> > > > - if (ret) {
> > > > - /* Last resort, if we fail to allocate
> > > > memory for the
> > > > - * fences block for the BO to become
> > > > idle
> > > > - */
> > > > - dma_resv_wait_timeout(bo->base.resv,
> > > > -
> > > > DMA_RESV_USAGE_BOOKKEEP, false,
> > > > - 30 * HZ);
> > > > - }
> > > > + ret = ttm_bo_individualize_resv(bo);
> > > > + if (ret) {
> > > > + /* Last resort, if we fail to allocate memory
> > > > for
> > > > the
> > > > + * fences block for the BO to become idle
> > > > + */
> > > > + dma_resv_wait_timeout(bo->base.resv,
> > > > DMA_RESV_USAGE_BOOKKEEP,
> > > > + false, 30 * HZ);
> > > > + }
> > > >
> > > > - if (bdev->funcs->release_notify)
> > > > - bdev->funcs->release_notify(bo);
> > > > + if (bo->bdev->funcs->release_notify)
> > > > + bo->bdev->funcs->release_notify(bo);
> > > >
> > > > - drm_vma_offset_remove(bdev->vma_manager, &bo-
> > > > > base.vma_node);
> > > > - ttm_mem_io_free(bdev, bo->resource);
> > > > + drm_vma_offset_remove(bdev->vma_manager, &bo-
> > > > > base.vma_node);
> > > > + ttm_mem_io_free(bdev, bo->resource);
> > > >
> > > > - if (!dma_resv_test_signaled(&bo->base._resv,
> > > > -
> > > > DMA_RESV_USAGE_BOOKKEEP)
> > > > > >
> > > > - (want_init_on_free() && (bo->ttm != NULL))
> > > > ||
> > > > - bo->type == ttm_bo_type_sg ||
> > > > - !dma_resv_trylock(bo->base.resv)) {
> > > > - /* The BO is not idle, resurrect it for
> > > > delayed destroy */
> > > > - ttm_bo_flush_all_fences(bo);
> > > > - bo->deleted = true;
> > > > + if (!dma_resv_test_signaled(&bo->base._resv,
> > > > DMA_RESV_USAGE_BOOKKEEP) ||
> > > > + (want_init_on_free() && (bo->ttm != NULL)) ||
> > > > + bo->type == ttm_bo_type_sg ||
> > > > + !dma_resv_trylock(bo->base.resv)) {
> > > > + /* The BO is not idle, resurrect it for delayed
> > > > destroy */
> > > > + ttm_bo_flush_all_fences(bo);
> > > >
> > > > - spin_lock(&bdev->lru_lock);
> > > > -
> > > > - /*
> > > > - * Make pinned bos immediately available
> > > > to
> > > > - * shrinkers, now that they are queued
> > > > for
> > > > - * destruction.
> > > > - *
> > > > - * FIXME: QXL is triggering this. Can be
> > > > removed when the
> > > > - * driver is fixed.
> > > > - */
> > > > - if (bo->pin_count) {
> > > > - bo->pin_count = 0;
> > > > -
> > > > ttm_resource_move_to_lru_tail(bo-
> > > > > resource);
> > > > - }
> > > > + spin_lock(&bo->bdev->lru_lock);
> > > >
> > > > - kref_init(&bo->kref);
> > > > - spin_unlock(&bdev->lru_lock);
> > > > + /*
> > > > + * Make pinned bos immediately available to
> > > > + * shrinkers, now that they are queued for
> > > > + * destruction.
> > > > + *
> > > > + * FIXME: QXL is triggering this. Can be removed
> > > > when the
> > > > + * driver is fixed.
> > > > + */
> > > > + if (bo->pin_count) {
> > > > + bo->pin_count = 0;
> > > > + ttm_resource_move_to_lru_tail(bo-
> > > > >resource);
> > > > + }
> > > >
> > > > - INIT_WORK(&bo->delayed_delete,
> > > > ttm_bo_delayed_delete);
> > > > + kref_init(&bo->base.refcount);
> > > > + bo->base.funcs = &ttm_deleted_object_funcs;
> > > > + spin_unlock(&bo->bdev->lru_lock);
> > > >
> > > > - /* Schedule the worker on the closest
> > > > NUMA
> > > > node. This
> > > > - * improves performance since system
> > > > memory
> > > > might be
> > > > - * cleared on free and that is best done
> > > > on
> > > > a CPU core
> > > > - * close to it.
> > > > - */
> > > > - queue_work_node(bdev->pool.nid, bdev-
> > > > >wq,
> > > > &bo->delayed_delete);
> > > > - return;
> > > > - }
> > > > + INIT_WORK(&bo->delayed_delete,
> > > > ttm_bo_delayed_delete);
> > > >
> > > > + /* Schedule the worker on the closest NUMA node.
> > > > This
> > > > + * improves performance since system memory
> > > > might be
> > > > + * cleared on free and that is best done on a
> > > > CPU
> > > > core
> > > > + * close to it.
> > > > + */
> > > > + queue_work_node(bdev->pool.nid, bdev->wq, &bo-
> > > > > delayed_delete);
> > > > + } else {
> > > > ttm_bo_cleanup_memtype_use(bo);
> > > > dma_resv_unlock(bo->base.resv);
> > > > - }
> > > >
> > > > - atomic_dec(&ttm_glob.bo_count);
> > > > - bo->destroy(bo);
> > > > -}
> > > > -
> > > > -/* TODO: remove! */
> > > > -void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > -{
> > > > - kref_put(&bo->kref, ttm_bo_release);
> > > > -}
> > > > -
> > > > -void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > -{
> > > > - ttm_bo_put(bo);
> > > > + atomic_dec(&ttm_glob.bo_count);
> > > > + bo->destroy(bo);
> > > > + }
> > > > }
> > > > EXPORT_SYMBOL(ttm_bo_fini);
> > > >
> > > > @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device
> > > > *bdev,
> > > > struct ttm_resource_manager *man
> > > > if (!bo->resource || bo->resource->mem_type != mem_type)
> > > > goto out_bo_moved;
> > > >
> > > > - if (bo->deleted) {
> > > > + if (ttm_bo_is_zombie(bo)) {
> > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > if (!ret)
> > > > ttm_bo_cleanup_memtype_use(bo);
> > > > @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct
> > > > ttm_lru_walk
> > > > *walk, struct ttm_buffer_object *
> > > > if (bo->pin_count || !bo->bdev->funcs-
> > > > >eviction_valuable(bo,
> > > > evict_walk->place))
> > > > return 0;
> > > >
> > > > - if (bo->deleted) {
> > > > + if (ttm_bo_is_zombie(bo)) {
> > > > lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
> > > > if (!lret)
> > > > ttm_bo_cleanup_memtype_use(bo);
> > > > @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct
> > > > ttm_device
> > > > *bdev,
> > > > void ttm_bo_pin(struct ttm_buffer_object *bo)
> > > > {
> > > > dma_resv_assert_held(bo->base.resv);
> > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > spin_lock(&bo->bdev->lru_lock);
> > > > if (bo->resource)
> > > > ttm_resource_del_bulk_move(bo->resource, bo);
> > > > @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
> > > > void ttm_bo_unpin(struct ttm_buffer_object *bo)
> > > > {
> > > > dma_resv_assert_held(bo->base.resv);
> > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > if (WARN_ON_ONCE(!bo->pin_count))
> > > > return;
> > > >
> > > > @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct ttm_device
> > > > *bdev,
> > > > struct ttm_buffer_object *bo,
> > > > {
> > > > int ret;
> > > >
> > > > - kref_init(&bo->kref);
> > > > bo->bdev = bdev;
> > > > bo->type = type;
> > > > bo->page_alignment = alignment;
> > > > @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk
> > > > *walk,
> > > > struct ttm_buffer_object *bo)
> > > > goto out;
> > > > }
> > > >
> > > > - if (bo->deleted) {
> > > > - pgoff_t num_pages = tt->num_pages;
> > > > + if (ttm_bo_is_zombie(bo)) {
> > > > + pgoff_t num_pages = bo->ttm->num_pages;
> > > >
> > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > if (ret)
> > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > index e0d48eac74b03..ded2a47be0bcb 100644
> > > > --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > @@ -27,6 +27,14 @@
> > > >
> > > > #include <drm/ttm/ttm_bo.h>
> > > >
> > > > +static inline void ttm_bo_free(struct drm_gem_object *gobj)
> > > > +{
> > > > + struct ttm_buffer_object *bo = container_of(gobj,
> > > > typeof(*bo), base);
> > > > +
> > > > + atomic_dec(&ttm_glob.bo_count);
> > > > + bo->destroy(bo);
> > > > +}
> > > > +
> > > > /**
> > > > * ttm_bo_get - reference a struct ttm_buffer_object
> > > > *
> > > > @@ -34,7 +42,7 @@
> > > > */
> > > > static inline void ttm_bo_get(struct ttm_buffer_object *bo)
> > > > {
> > > > - kref_get(&bo->kref);
> > > > + drm_gem_object_get(&bo->base);
> > > > }
> > > >
> > > > /**
> > > > @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
> > > > ttm_buffer_object *bo)
> > > > static inline __must_check struct ttm_buffer_object *
> > > > ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
> > > > {
> > > > - if (!kref_get_unless_zero(&bo->kref))
> > > > + if (!kref_get_unless_zero(&bo->base.refcount))
> > > > return NULL;
> > > > return bo;
> > > > }
> > > >
> > > > -void ttm_bo_put(struct ttm_buffer_object *bo);
> > > > +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > +{
> > > > + drm_gem_object_put(&bo->base);
> > > > +}
> > > >
> > > > #endif
> > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > index 3e3c201a02226..7ed085adf1c9b 100644
> > > > --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > @@ -41,6 +41,18 @@
> > > >
> > > > #include "ttm_bo_internal.h"
> > > >
> > > > +static void ttm_transfer_object_free(struct drm_gem_object *obj)
> > > > +{
> > > > + struct ttm_buffer_object *bo =
> > > > + container_of(obj, struct ttm_buffer_object,
> > > > base);
> > > > +
> > > > + ttm_bo_fini(bo);
> > > > +}
> > > > +
> > > > +const struct drm_gem_object_funcs ttm_transfer_object_funcs = {
> > > > + .free = ttm_transfer_object_free,
> > > > +};
> > > > +
> > > > struct ttm_transfer_obj {
> > > > struct ttm_buffer_object base;
> > > > struct ttm_buffer_object *bo;
> > > > @@ -247,7 +259,8 @@ static int ttm_buffer_object_transfer(struct
> > > > ttm_buffer_object *bo,
> > > > atomic_inc(&ttm_glob.bo_count);
> > > > drm_vma_node_reset(&fbo->base.base.vma_node);
> > > >
> > > > - kref_init(&fbo->base.kref);
> > > > + kref_init(&fbo->base.base.refcount);
> > > > + fbo->base.base.funcs = &ttm_transfer_object_funcs;
> > > > fbo->base.destroy = &ttm_transfered_destroy;
> > > > fbo->base.pin_count = 0;
> > > > if (bo->type != ttm_bo_type_sg)
> > > > diff --git a/drivers/gpu/drm/xe/xe_bo.c
> > > > b/drivers/gpu/drm/xe/xe_bo.c
> > > > index 85e6d9a0f575b..5843f850339c7 100644
> > > > --- a/drivers/gpu/drm/xe/xe_bo.c
> > > > +++ b/drivers/gpu/drm/xe/xe_bo.c
> > > > @@ -1651,7 +1651,7 @@ static bool
> > > > xe_ttm_bo_lock_in_destructor(struct
> > > > ttm_buffer_object *ttm_bo)
> > > > struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
> > > > bool locked;
> > > >
> > > > - xe_assert(xe, !kref_read(&ttm_bo->kref));
> > > > + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
> > > >
> > > > /*
> > > > * We can typically only race with TTM trylocking under
> > > > the
> > > > diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
> > > > index 8310bc3d55f90..1eae9eea5ff32 100644
> > > > --- a/include/drm/ttm/ttm_bo.h
> > > > +++ b/include/drm/ttm/ttm_bo.h
> > > > @@ -78,11 +78,8 @@ enum ttm_bo_type {
> > > > * @type: The bo type.
> > > > * @page_alignment: Page alignment.
> > > > * @destroy: Destruction function. If NULL, kfree is used.
> > > > - * @kref: Reference count of this buffer object. When this
> > > > refcount
> > > > reaches
> > > > - * zero, the object is destroyed or put on the delayed delete
> > > > list.
> > > > * @resource: structure describing current placement.
> > > > * @ttm: TTM structure holding system pages.
> > > > - * @deleted: True if the object is only a zombie and already
> > > > deleted.
> > > > * @bulk_move: The bulk move object.
> > > > * @priority: Priority for LRU, BOs with lower priority are
> > > > evicted
> > > > first.
> > > > * @pin_count: Pin count.
> > > > @@ -109,17 +106,11 @@ struct ttm_buffer_object {
> > > > uint32_t page_alignment;
> > > > void (*destroy) (struct ttm_buffer_object *);
> > > >
> > > > - /*
> > > > - * Members not needing protection.
> > > > - */
> > > > - struct kref kref;
> > > > -
> > > > /*
> > > > * Members protected by the bo::resv::reserved lock.
> > > > */
> > > > struct ttm_resource *resource;
> > > > struct ttm_tt *ttm;
> > > > - bool deleted;
> > > > struct ttm_lru_bulk_move *bulk_move;
> > > > unsigned priority;
> > > > unsigned pin_count;
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-06 17:51 ` Matthew Brost
@ 2026-07-06 17:53 ` Thomas Hellström
2026-07-06 18:03 ` Matthew Brost
2026-07-06 18:03 ` Thomas Hellström
1 sibling, 1 reply; 26+ messages in thread
From: Thomas Hellström @ 2026-07-06 17:53 UTC (permalink / raw)
To: Matthew Brost
Cc: Christian König, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Alex Deucher, dri-devel, linux-kernel,
intel-gfx, intel-xe, amd-gfx
On Mon, 2026-07-06 at 10:51 -0700, Matthew Brost wrote:
> On Mon, Jul 06, 2026 at 07:01:27PM +0200, Thomas Hellström wrote:
> > On Mon, 2026-07-06 at 16:49 +0200, Christian König wrote:
> > > On 7/6/26 15:14, Thomas Hellström wrote:
> > > > On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> > > > > From: Christian König <christian.koenig@amd.com>
> > > > >
> > > > > Instead of keeping a separate reference count for the TTM
> > > > > object
> > > > > also
> > > > > use
> > > > > the reference count for DRM GEM objects inside TTM.
> > > > >
> > > > > Apart from avoiding two reference counts for one object this
> > > > > approach
> > > > > has
> > > > > the clear advantage of being able to use drm_exec inside TTM.
> > > > >
> > > > > v2: adjust XE assert as well and re-enable disabled test
> > > > > v3: handle another case in i915
> > > > > v4: set GEM driver funcs of transfer BOs to point to the TTM
> > > > > free
> > > > > callback (Natalie)
> > > >
> > > > I think the main review issue from the last time this was on
> > > > the
> > > > table
> > > > was that we shouldn't resurrect the gem refcount. Apart from
> > > > the
> > > > risc
> > > > of getting barriers wrong, both xe and IIRC i915 partly rely on
> > > > the
> > > > gem
> > > > refcount never being resurrected and that callbacks for bos
> > > > with
> > > > zero
> > > > gem refcount means that the gem part of the object is unusable.
> > >
> > > I've spend quite some time thinking about that and came to the
> > > conclusion that this is actually harmless.
> > >
> > > The drivers shouldn't be able to see the resurected BO, except if
> > > they go over the LRU list manually (which they shouldn't).
> >
> > The shrinker uses the TTM helpers for this. Basically the check
> > needs
> > to be ported to use the zombie interface but the present change
> > also
> > widens the window where we can't evict / shrink at all due to zero
> > refcounts.
> >
> > While it might be made harmless, resurrecting a refcount like this
> > is
> > IMO not something that should leak into the gem refcount. Nobody
> > else
> > does this in the kernel tree. The bo in reality becomes a zombie
> > once
> > the gem refcount reaches zero.
>
> When you say resurrecting a refcount - what exactly do you mean by
> this?
>
> ref -> 0 -> init ref count 1 -> 0 again?
Yes, the destructor re-initializes it to 1 again. Prevously this was
confined to the TTM refcount only and had limited visibility.
/Thomas
>
> Matt
>
> >
> > >
> > > >
> > > > For example xe_bo.c:
> > > >
> > > > if (!xe_bo_is_xe_bo(bo) ||
> > > > !xe_bo_get_unless_zero(xe_bo))
> > > > return xe_bo_shrink_purge(ctx, bo, scanned);
> > > >
> > > > So IIRC the conclusion was when removing the ttm refcount we
> > > > shouldn't
> > > > attempt to resurrect the gem one. If the get_unless_zero()
> > > > fails
> > > > during
> > > > evict walk, we simply find something to wait for. See previous
> > > > discussion there.
> > >
> > > Yeah, I considered that as well but the problem is we often
> > > doesn't
> > > have anything to wait on.
> >
> > That's not the conclusion of the previous discussion?
> >
> > https://lore.kernel.org/dri-devel/20250716160555.20217-2-christian.koenig@amd.com/#r
> >
> > >
> > > >
> > > > I fully support removing the ttm refcount, but not if it means
> > > > resurrecting the gem refcount.
> > > >
> > > > If we want to sidestep that problem, in favour of getting the
> > > > proposed
> > > > locking functionality in and future proof it, I suggest using
> > > >
> > > > https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
> > > >
> > > > And rebase this series on that. This means we can use the ttm
> > > > refcount
> > > > for the transaction refcounting, and also that if we add a dma-
> > > > buf
> > > > map
> > > > interface with a dma_resv_txn_obj, we could use that to also
> > > > have
> > > > exhaustive eviction that originates from a dma_buf map.
> > >
> > > I don't think that this is a good idea. It just adds another
> > > layer of
> > > abstraction and doesn't solve the problem in any way possible.
> >
> > This comment confuses me. Exactly what problem isn't solved by
> > this,
> > and which of the stated benefits/use-cases in the cover-letter do
> > you
> > think aren't worthwhile?
> >
> > Also for reference: (Section at the end and follow-up messages)
> > https://lore.kernel.org/all/3716d43462188590743060755b37e3d060f7600f.camel@linux.intel.com/
> >
> > Thanks,
> > Thomas
> >
> >
> > > Regards,
> > > Christian.
> > >
> > > >
> > > > /Thomas
> > > >
> > > > >
> > > > > Signed-off-by: tChristian König <christian.koenig@amd.com>
> > > > > Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> > > > > ---
> > > > > drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++--
> > > > > -
> > > > > drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
> > > > > drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
> > > > > drivers/gpu/drm/ttm/ttm_bo.c | 135
> > > > > +++++++++++--
> > > > > ----------
> > > > > drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
> > > > > drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
> > > > > drivers/gpu/drm/xe/xe_bo.c | 2 +-
> > > > > include/drm/ttm/ttm_bo.h | 9 --
> > > > > 8 files changed, 111 insertions(+), 112 deletions(-)
> > > > >
> > > > > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > index df3fcc2b1248e..642296602de69 100644
> > > > > --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
> > > > > drm_i915_gem_object *obj)
> > > > > * Don't manipulate the TTM LRUs while in TTM bo
> > > > > destruction.
> > > > > * We're called through
> > > > > i915_ttm_delete_mem_notify().
> > > > > */
> > > > > - if (!kref_read(&bo->kref))
> > > > > + if (!kref_read(&bo->base.refcount))
> > > > > return;
> > > > >
> > > > > /*
> > > > > @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
> > > > > drm_i915_gem_object *obj)
> > > > > *
> > > > > * TODO: consider maybe also bumping the shrinker
> > > > > list
> > > > > here
> > > > > when we have
> > > > > * already unpinned it, which should give us
> > > > > something
> > > > > more
> > > > > like an LRU.
> > > > > - *
> > > > > - * TODO: There is a small window of opportunity for
> > > > > this
> > > > > function to
> > > > > - * get called from eviction after we've dropped the
> > > > > last
> > > > > GEM
> > > > > refcount,
> > > > > - * but before the TTM deleted flag is set on the
> > > > > object.
> > > > > Avoid
> > > > > - * adjusting the shrinker list in such cases, since
> > > > > the
> > > > > object is
> > > > > - * not available to the shrinker anyway due to its
> > > > > zero
> > > > > refcount.
> > > > > - * To fix this properly we should move to a TTM
> > > > > shrinker
> > > > > LRU
> > > > > list for
> > > > > - * these objects.
> > > > > */
> > > > > - if (kref_get_unless_zero(&obj->base.refcount)) {
> > > > > - if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > > - if (shrinkable) {
> > > > > - if (obj->mm.madv ==
> > > > > I915_MADV_WILLNEED)
> > > > > -
> > > > > __i915_gem_object_ma
> > > > > ke_s
> > > > > hrinkable(obj);
> > > > > - else
> > > > > -
> > > > > __i915_gem_object_ma
> > > > > ke_p
> > > > > urgeable(obj);
> > > > > - } else {
> > > > > -
> > > > > i915_gem_object_make_unshrin
> > > > > kabl
> > > > > e(obj);
> > > > > - }
> > > > > -
> > > > > - obj->mm.ttm_shrinkable = shrinkable;
> > > > > + i915_gem_object_get(obj);
> > > > > + if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > > + if (shrinkable) {
> > > > > + if (obj->mm.madv ==
> > > > > I915_MADV_WILLNEED)
> > > > > + __i915_gem_object_make_shrin
> > > > > kabl
> > > > > e(ob
> > > > > j);
> > > > > + else
> > > > > + __i915_gem_object_make_purge
> > > > > able
> > > > > (obj
> > > > > );
> > > > > + } else {
> > > > > + i915_gem_object_make_unshrinkable(ob
> > > > > j);
> > > > > }
> > > > > - i915_gem_object_put(obj);
> > > > > +
> > > > > + obj->mm.ttm_shrinkable = shrinkable;
> > > > > }
> > > > > + i915_gem_object_put(obj);
> > > > >
> > > > > /*
> > > > > * Put on the correct LRU list depending on the MADV
> > > > > status
> > > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > index 56ad8ef325840..904cb4da6c9b3 100644
> > > > > --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > @@ -127,7 +127,7 @@ static void
> > > > > ttm_bo_init_reserved_sys_man(struct
> > > > > kunit *test)
> > > > > dma_resv_unlock(bo->base.resv);
> > > > >
> > > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount),
> > > > > 1);
> > > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > > KUNIT_EXPECT_EQ(test, bo->page_alignment,
> > > > > PAGE_SIZE);
> > > > > @@ -176,7 +176,7 @@ static void
> > > > > ttm_bo_init_reserved_mock_man(struct
> > > > > kunit *test)
> > > > > dma_resv_unlock(bo->base.resv);
> > > > >
> > > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount),
> > > > > 1);
> > > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > > KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
> > > > > @@ -969,6 +969,8 @@ static void
> > > > > ttm_bo_validate_allowed_only_evict(struct kunit *test)
> > > > > ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
> > > > > }
> > > > >
> > > > > +extern const struct drm_gem_object_funcs
> > > > > ttm_deleted_object_funcs;
> > > > > +
> > > > > static void ttm_bo_validate_deleted_evict(struct kunit
> > > > > *test)
> > > > > {
> > > > > struct ttm_operation_ctx ctx_init = { }, ctx_val =
> > > > > { };
> > > > > @@ -999,7 +1001,7 @@ static void
> > > > > ttm_bo_validate_deleted_evict(struct
> > > > > kunit *test)
> > > > > KUNIT_EXPECT_EQ(test,
> > > > > ttm_resource_manager_usage(man),
> > > > > big);
> > > > >
> > > > > dma_resv_unlock(bo_big->base.resv);
> > > > > - bo_big->deleted = true;
> > > > > + bo_big->base.funcs = &ttm_deleted_object_funcs;
> > > > >
> > > > > bo_small = ttm_bo_kunit_init(test, test->priv,
> > > > > small,
> > > > > NULL);
> > > > > bo_small->type = bo_type;
> > > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
> > > > > --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > @@ -189,8 +189,6 @@ struct ttm_buffer_object
> > > > > *ttm_bo_kunit_init(struct kunit *test,
> > > > > bo->bdev = devs->ttm_dev;
> > > > > bo->destroy = dummy_ttm_bo_destroy;
> > > > >
> > > > > - kref_init(&bo->kref);
> > > > > -
> > > > > return bo;
> > > > > }
> > > > > EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
> > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > index 3980f376e3ba4..2b470c1746f60 100644
> > > > > --- a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > @@ -246,88 +246,84 @@ static void
> > > > > ttm_bo_delayed_delete(struct
> > > > > work_struct *work)
> > > > > ttm_bo_put(bo);
> > > > > }
> > > > >
> > > > > -static void ttm_bo_release(struct kref *kref)
> > > > > +/*
> > > > > + * All other callbacks should never ever be called on a
> > > > > deleted
> > > > > TTM
> > > > > object.
> > > > > + */
> > > > > +const struct drm_gem_object_funcs ttm_deleted_object_funcs =
> > > > > {
> > > > > + .free = ttm_bo_free
> > > > > +};
> > > > > +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
> > > > > +
> > > > > +/* Returns true if the BO is about to get deleted */
> > > > > +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
> > > > > +{
> > > > > + return bo->base.funcs == &ttm_deleted_object_funcs;
> > > > > +}
> > > > > +
> > > > > +void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > > {
> > > > > - struct ttm_buffer_object *bo =
> > > > > - container_of(kref, struct ttm_buffer_object,
> > > > > kref);
> > > > > struct ttm_device *bdev = bo->bdev;
> > > > > int ret;
> > > > >
> > > > > WARN_ON_ONCE(bo->pin_count);
> > > > > WARN_ON_ONCE(bo->bulk_move);
> > > > >
> > > > > - if (!bo->deleted) {
> > > > > - ret = ttm_bo_individualize_resv(bo);
> > > > > - if (ret) {
> > > > > - /* Last resort, if we fail to
> > > > > allocate
> > > > > memory for the
> > > > > - * fences block for the BO to become
> > > > > idle
> > > > > - */
> > > > > - dma_resv_wait_timeout(bo->base.resv,
> > > > > -
> > > > > DMA_RESV_USAGE_BOOKKEEP, false,
> > > > > - 30 * HZ);
> > > > > - }
> > > > > + ret = ttm_bo_individualize_resv(bo);
> > > > > + if (ret) {
> > > > > + /* Last resort, if we fail to allocate
> > > > > memory
> > > > > for
> > > > > the
> > > > > + * fences block for the BO to become idle
> > > > > + */
> > > > > + dma_resv_wait_timeout(bo->base.resv,
> > > > > DMA_RESV_USAGE_BOOKKEEP,
> > > > > + false, 30 * HZ);
> > > > > + }
> > > > >
> > > > > - if (bdev->funcs->release_notify)
> > > > > - bdev->funcs->release_notify(bo);
> > > > > + if (bo->bdev->funcs->release_notify)
> > > > > + bo->bdev->funcs->release_notify(bo);
> > > > >
> > > > > - drm_vma_offset_remove(bdev->vma_manager,
> > > > > &bo-
> > > > > > base.vma_node);
> > > > > - ttm_mem_io_free(bdev, bo->resource);
> > > > > + drm_vma_offset_remove(bdev->vma_manager, &bo-
> > > > > > base.vma_node);
> > > > > + ttm_mem_io_free(bdev, bo->resource);
> > > > >
> > > > > - if (!dma_resv_test_signaled(&bo->base._resv,
> > > > > -
> > > > > DMA_RESV_USAGE_BOOKKEEP)
> > > > > > >
> > > > > - (want_init_on_free() && (bo->ttm !=
> > > > > NULL))
> > > > > > >
> > > > > - bo->type == ttm_bo_type_sg ||
> > > > > - !dma_resv_trylock(bo->base.resv)) {
> > > > > - /* The BO is not idle, resurrect it
> > > > > for
> > > > > delayed destroy */
> > > > > - ttm_bo_flush_all_fences(bo);
> > > > > - bo->deleted = true;
> > > > > + if (!dma_resv_test_signaled(&bo->base._resv,
> > > > > DMA_RESV_USAGE_BOOKKEEP) ||
> > > > > + (want_init_on_free() && (bo->ttm != NULL)) ||
> > > > > + bo->type == ttm_bo_type_sg ||
> > > > > + !dma_resv_trylock(bo->base.resv)) {
> > > > > + /* The BO is not idle, resurrect it for
> > > > > delayed
> > > > > destroy */
> > > > > + ttm_bo_flush_all_fences(bo);
> > > > >
> > > > > - spin_lock(&bdev->lru_lock);
> > > > > -
> > > > > - /*
> > > > > - * Make pinned bos immediately
> > > > > available
> > > > > to
> > > > > - * shrinkers, now that they are
> > > > > queued
> > > > > for
> > > > > - * destruction.
> > > > > - *
> > > > > - * FIXME: QXL is triggering this.
> > > > > Can be
> > > > > removed when the
> > > > > - * driver is fixed.
> > > > > - */
> > > > > - if (bo->pin_count) {
> > > > > - bo->pin_count = 0;
> > > > > -
> > > > > ttm_resource_move_to_lru_tai
> > > > > l(bo-
> > > > > > resource);
> > > > > - }
> > > > > + spin_lock(&bo->bdev->lru_lock);
> > > > >
> > > > > - kref_init(&bo->kref);
> > > > > - spin_unlock(&bdev->lru_lock);
> > > > > + /*
> > > > > + * Make pinned bos immediately available to
> > > > > + * shrinkers, now that they are queued for
> > > > > + * destruction.
> > > > > + *
> > > > > + * FIXME: QXL is triggering this. Can be
> > > > > removed
> > > > > when the
> > > > > + * driver is fixed.
> > > > > + */
> > > > > + if (bo->pin_count) {
> > > > > + bo->pin_count = 0;
> > > > > + ttm_resource_move_to_lru_tail(bo-
> > > > > > resource);
> > > > > + }
> > > > >
> > > > > - INIT_WORK(&bo->delayed_delete,
> > > > > ttm_bo_delayed_delete);
> > > > > + kref_init(&bo->base.refcount);
> > > > > + bo->base.funcs = &ttm_deleted_object_funcs;
> > > > > + spin_unlock(&bo->bdev->lru_lock);
> > > > >
> > > > > - /* Schedule the worker on the
> > > > > closest
> > > > > NUMA
> > > > > node. This
> > > > > - * improves performance since system
> > > > > memory
> > > > > might be
> > > > > - * cleared on free and that is best
> > > > > done
> > > > > on
> > > > > a CPU core
> > > > > - * close to it.
> > > > > - */
> > > > > - queue_work_node(bdev->pool.nid,
> > > > > bdev-
> > > > > > wq,
> > > > > &bo->delayed_delete);
> > > > > - return;
> > > > > - }
> > > > > + INIT_WORK(&bo->delayed_delete,
> > > > > ttm_bo_delayed_delete);
> > > > >
> > > > > + /* Schedule the worker on the closest NUMA
> > > > > node.
> > > > > This
> > > > > + * improves performance since system memory
> > > > > might be
> > > > > + * cleared on free and that is best done on
> > > > > a
> > > > > CPU
> > > > > core
> > > > > + * close to it.
> > > > > + */
> > > > > + queue_work_node(bdev->pool.nid, bdev->wq,
> > > > > &bo-
> > > > > > delayed_delete);
> > > > > + } else {
> > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > dma_resv_unlock(bo->base.resv);
> > > > > - }
> > > > >
> > > > > - atomic_dec(&ttm_glob.bo_count);
> > > > > - bo->destroy(bo);
> > > > > -}
> > > > > -
> > > > > -/* TODO: remove! */
> > > > > -void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > > -{
> > > > > - kref_put(&bo->kref, ttm_bo_release);
> > > > > -}
> > > > > -
> > > > > -void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > > -{
> > > > > - ttm_bo_put(bo);
> > > > > + atomic_dec(&ttm_glob.bo_count);
> > > > > + bo->destroy(bo);
> > > > > + }
> > > > > }
> > > > > EXPORT_SYMBOL(ttm_bo_fini);
> > > > >
> > > > > @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device
> > > > > *bdev,
> > > > > struct ttm_resource_manager *man
> > > > > if (!bo->resource || bo->resource->mem_type !=
> > > > > mem_type)
> > > > > goto out_bo_moved;
> > > > >
> > > > > - if (bo->deleted) {
> > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > > if (!ret)
> > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct
> > > > > ttm_lru_walk
> > > > > *walk, struct ttm_buffer_object *
> > > > > if (bo->pin_count || !bo->bdev->funcs-
> > > > > > eviction_valuable(bo,
> > > > > evict_walk->place))
> > > > > return 0;
> > > > >
> > > > > - if (bo->deleted) {
> > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
> > > > > if (!lret)
> > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct
> > > > > ttm_device
> > > > > *bdev,
> > > > > void ttm_bo_pin(struct ttm_buffer_object *bo)
> > > > > {
> > > > > dma_resv_assert_held(bo->base.resv);
> > > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > > spin_lock(&bo->bdev->lru_lock);
> > > > > if (bo->resource)
> > > > > ttm_resource_del_bulk_move(bo->resource,
> > > > > bo);
> > > > > @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
> > > > > void ttm_bo_unpin(struct ttm_buffer_object *bo)
> > > > > {
> > > > > dma_resv_assert_held(bo->base.resv);
> > > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > > if (WARN_ON_ONCE(!bo->pin_count))
> > > > > return;
> > > > >
> > > > > @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct
> > > > > ttm_device
> > > > > *bdev,
> > > > > struct ttm_buffer_object *bo,
> > > > > {
> > > > > int ret;
> > > > >
> > > > > - kref_init(&bo->kref);
> > > > > bo->bdev = bdev;
> > > > > bo->type = type;
> > > > > bo->page_alignment = alignment;
> > > > > @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk
> > > > > *walk,
> > > > > struct ttm_buffer_object *bo)
> > > > > goto out;
> > > > > }
> > > > >
> > > > > - if (bo->deleted) {
> > > > > - pgoff_t num_pages = tt->num_pages;
> > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > + pgoff_t num_pages = bo->ttm->num_pages;
> > > > >
> > > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > > if (ret)
> > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > index e0d48eac74b03..ded2a47be0bcb 100644
> > > > > --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > @@ -27,6 +27,14 @@
> > > > >
> > > > > #include <drm/ttm/ttm_bo.h>
> > > > >
> > > > > +static inline void ttm_bo_free(struct drm_gem_object *gobj)
> > > > > +{
> > > > > + struct ttm_buffer_object *bo = container_of(gobj,
> > > > > typeof(*bo), base);
> > > > > +
> > > > > + atomic_dec(&ttm_glob.bo_count);
> > > > > + bo->destroy(bo);
> > > > > +}
> > > > > +
> > > > > /**
> > > > > * ttm_bo_get - reference a struct ttm_buffer_object
> > > > > *
> > > > > @@ -34,7 +42,7 @@
> > > > > */
> > > > > static inline void ttm_bo_get(struct ttm_buffer_object *bo)
> > > > > {
> > > > > - kref_get(&bo->kref);
> > > > > + drm_gem_object_get(&bo->base);
> > > > > }
> > > > >
> > > > > /**
> > > > > @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
> > > > > ttm_buffer_object *bo)
> > > > > static inline __must_check struct ttm_buffer_object *
> > > > > ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
> > > > > {
> > > > > - if (!kref_get_unless_zero(&bo->kref))
> > > > > + if (!kref_get_unless_zero(&bo->base.refcount))
> > > > > return NULL;
> > > > > return bo;
> > > > > }
> > > > >
> > > > > -void ttm_bo_put(struct ttm_buffer_object *bo);
> > > > > +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > > +{
> > > > > + drm_gem_object_put(&bo->base);
> > > > > +}
> > > > >
> > > > > #endif
> > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > index 3e3c201a02226..7ed085adf1c9b 100644
> > > > > --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > @@ -41,6 +41,18 @@
> > > > >
> > > > > #include "ttm_bo_internal.h"
> > > > >
> > > > > +static void ttm_transfer_object_free(struct drm_gem_object
> > > > > *obj)
> > > > > +{
> > > > > + struct ttm_buffer_object *bo =
> > > > > + container_of(obj, struct ttm_buffer_object,
> > > > > base);
> > > > > +
> > > > > + ttm_bo_fini(bo);
> > > > > +}
> > > > > +
> > > > > +const struct drm_gem_object_funcs ttm_transfer_object_funcs
> > > > > = {
> > > > > + .free = ttm_transfer_object_free,
> > > > > +};
> > > > > +
> > > > > struct ttm_transfer_obj {
> > > > > struct ttm_buffer_object base;
> > > > > struct ttm_buffer_object *bo;
> > > > > @@ -247,7 +259,8 @@ static int
> > > > > ttm_buffer_object_transfer(struct
> > > > > ttm_buffer_object *bo,
> > > > > atomic_inc(&ttm_glob.bo_count);
> > > > > drm_vma_node_reset(&fbo->base.base.vma_node);
> > > > >
> > > > > - kref_init(&fbo->base.kref);
> > > > > + kref_init(&fbo->base.base.refcount);
> > > > > + fbo->base.base.funcs = &ttm_transfer_object_funcs;
> > > > > fbo->base.destroy = &ttm_transfered_destroy;
> > > > > fbo->base.pin_count = 0;
> > > > > if (bo->type != ttm_bo_type_sg)
> > > > > diff --git a/drivers/gpu/drm/xe/xe_bo.c
> > > > > b/drivers/gpu/drm/xe/xe_bo.c
> > > > > index 85e6d9a0f575b..5843f850339c7 100644
> > > > > --- a/drivers/gpu/drm/xe/xe_bo.c
> > > > > +++ b/drivers/gpu/drm/xe/xe_bo.c
> > > > > @@ -1651,7 +1651,7 @@ static bool
> > > > > xe_ttm_bo_lock_in_destructor(struct
> > > > > ttm_buffer_object *ttm_bo)
> > > > > struct xe_device *xe = ttm_to_xe_device(ttm_bo-
> > > > > >bdev);
> > > > > bool locked;
> > > > >
> > > > > - xe_assert(xe, !kref_read(&ttm_bo->kref));
> > > > > + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
> > > > >
> > > > > /*
> > > > > * We can typically only race with TTM trylocking
> > > > > under
> > > > > the
> > > > > diff --git a/include/drm/ttm/ttm_bo.h
> > > > > b/include/drm/ttm/ttm_bo.h
> > > > > index 8310bc3d55f90..1eae9eea5ff32 100644
> > > > > --- a/include/drm/ttm/ttm_bo.h
> > > > > +++ b/include/drm/ttm/ttm_bo.h
> > > > > @@ -78,11 +78,8 @@ enum ttm_bo_type {
> > > > > * @type: The bo type.
> > > > > * @page_alignment: Page alignment.
> > > > > * @destroy: Destruction function. If NULL, kfree is used.
> > > > > - * @kref: Reference count of this buffer object. When this
> > > > > refcount
> > > > > reaches
> > > > > - * zero, the object is destroyed or put on the delayed
> > > > > delete
> > > > > list.
> > > > > * @resource: structure describing current placement.
> > > > > * @ttm: TTM structure holding system pages.
> > > > > - * @deleted: True if the object is only a zombie and already
> > > > > deleted.
> > > > > * @bulk_move: The bulk move object.
> > > > > * @priority: Priority for LRU, BOs with lower priority are
> > > > > evicted
> > > > > first.
> > > > > * @pin_count: Pin count.
> > > > > @@ -109,17 +106,11 @@ struct ttm_buffer_object {
> > > > > uint32_t page_alignment;
> > > > > void (*destroy) (struct ttm_buffer_object *);
> > > > >
> > > > > - /*
> > > > > - * Members not needing protection.
> > > > > - */
> > > > > - struct kref kref;
> > > > > -
> > > > > /*
> > > > > * Members protected by the bo::resv::reserved lock.
> > > > > */
> > > > > struct ttm_resource *resource;
> > > > > struct ttm_tt *ttm;
> > > > > - bool deleted;
> > > > > struct ttm_lru_bulk_move *bulk_move;
> > > > > unsigned priority;
> > > > > unsigned pin_count;
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-06 17:53 ` Thomas Hellström
@ 2026-07-06 18:03 ` Matthew Brost
2026-07-06 18:05 ` Matthew Brost
0 siblings, 1 reply; 26+ messages in thread
From: Matthew Brost @ 2026-07-06 18:03 UTC (permalink / raw)
To: Thomas Hellström
Cc: Christian König, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Alex Deucher, dri-devel, linux-kernel,
intel-gfx, intel-xe, amd-gfx
On Mon, Jul 06, 2026 at 07:53:12PM +0200, Thomas Hellström wrote:
> On Mon, 2026-07-06 at 10:51 -0700, Matthew Brost wrote:
> > On Mon, Jul 06, 2026 at 07:01:27PM +0200, Thomas Hellström wrote:
> > > On Mon, 2026-07-06 at 16:49 +0200, Christian König wrote:
> > > > On 7/6/26 15:14, Thomas Hellström wrote:
> > > > > On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> > > > > > From: Christian König <christian.koenig@amd.com>
> > > > > >
> > > > > > Instead of keeping a separate reference count for the TTM
> > > > > > object
> > > > > > also
> > > > > > use
> > > > > > the reference count for DRM GEM objects inside TTM.
> > > > > >
> > > > > > Apart from avoiding two reference counts for one object this
> > > > > > approach
> > > > > > has
> > > > > > the clear advantage of being able to use drm_exec inside TTM.
> > > > > >
> > > > > > v2: adjust XE assert as well and re-enable disabled test
> > > > > > v3: handle another case in i915
> > > > > > v4: set GEM driver funcs of transfer BOs to point to the TTM
> > > > > > free
> > > > > > callback (Natalie)
> > > > >
> > > > > I think the main review issue from the last time this was on
> > > > > the
> > > > > table
> > > > > was that we shouldn't resurrect the gem refcount. Apart from
> > > > > the
> > > > > risc
> > > > > of getting barriers wrong, both xe and IIRC i915 partly rely on
> > > > > the
> > > > > gem
> > > > > refcount never being resurrected and that callbacks for bos
> > > > > with
> > > > > zero
> > > > > gem refcount means that the gem part of the object is unusable.
> > > >
> > > > I've spend quite some time thinking about that and came to the
> > > > conclusion that this is actually harmless.
> > > >
> > > > The drivers shouldn't be able to see the resurected BO, except if
> > > > they go over the LRU list manually (which they shouldn't).
> > >
> > > The shrinker uses the TTM helpers for this. Basically the check
> > > needs
> > > to be ported to use the zombie interface but the present change
> > > also
> > > widens the window where we can't evict / shrink at all due to zero
> > > refcounts.
> > >
> > > While it might be made harmless, resurrecting a refcount like this
> > > is
> > > IMO not something that should leak into the gem refcount. Nobody
> > > else
> > > does this in the kernel tree. The bo in reality becomes a zombie
> > > once
> > > the gem refcount reaches zero.
> >
> > When you say resurrecting a refcount - what exactly do you mean by
> > this?
> >
> > ref -> 0 -> init ref count 1 -> 0 again?
>
> Yes, the destructor re-initializes it to 1 again. Prevously this was
> confined to the TTM refcount only and had limited visibility.
>
Put a PT BO on a deferred list -> reinit -> sometime later, do an async
put. This is all done using the TTM refcount. Of course, we don't have
to do it this way, though.
In general, I agree that tricks like this are not the most desirable
approach. If I recall correctly, I implemented this in the Xe code
because it followed an existing paradigm. If we want to merge the TTM
and GEM refcounts, I agree that we should likely move away from this
paradigm, since the GEM refcount significantly expands its scope.
Matt
> /Thomas
>
> >
> > Matt
> >
> > >
> > > >
> > > > >
> > > > > For example xe_bo.c:
> > > > >
> > > > > if (!xe_bo_is_xe_bo(bo) ||
> > > > > !xe_bo_get_unless_zero(xe_bo))
> > > > > return xe_bo_shrink_purge(ctx, bo, scanned);
> > > > >
> > > > > So IIRC the conclusion was when removing the ttm refcount we
> > > > > shouldn't
> > > > > attempt to resurrect the gem one. If the get_unless_zero()
> > > > > fails
> > > > > during
> > > > > evict walk, we simply find something to wait for. See previous
> > > > > discussion there.
> > > >
> > > > Yeah, I considered that as well but the problem is we often
> > > > doesn't
> > > > have anything to wait on.
> > >
> > > That's not the conclusion of the previous discussion?
> > >
> > > https://lore.kernel.org/dri-devel/20250716160555.20217-2-christian.koenig@amd.com/#r
> > >
> > > >
> > > > >
> > > > > I fully support removing the ttm refcount, but not if it means
> > > > > resurrecting the gem refcount.
> > > > >
> > > > > If we want to sidestep that problem, in favour of getting the
> > > > > proposed
> > > > > locking functionality in and future proof it, I suggest using
> > > > >
> > > > > https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
> > > > >
> > > > > And rebase this series on that. This means we can use the ttm
> > > > > refcount
> > > > > for the transaction refcounting, and also that if we add a dma-
> > > > > buf
> > > > > map
> > > > > interface with a dma_resv_txn_obj, we could use that to also
> > > > > have
> > > > > exhaustive eviction that originates from a dma_buf map.
> > > >
> > > > I don't think that this is a good idea. It just adds another
> > > > layer of
> > > > abstraction and doesn't solve the problem in any way possible.
> > >
> > > This comment confuses me. Exactly what problem isn't solved by
> > > this,
> > > and which of the stated benefits/use-cases in the cover-letter do
> > > you
> > > think aren't worthwhile?
> > >
> > > Also for reference: (Section at the end and follow-up messages)
> > > https://lore.kernel.org/all/3716d43462188590743060755b37e3d060f7600f.camel@linux.intel.com/
> > >
> > > Thanks,
> > > Thomas
> > >
> > >
> > > > Regards,
> > > > Christian.
> > > >
> > > > >
> > > > > /Thomas
> > > > >
> > > > > >
> > > > > > Signed-off-by: tChristian König <christian.koenig@amd.com>
> > > > > > Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> > > > > > ---
> > > > > > drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++--
> > > > > > -
> > > > > > drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
> > > > > > drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
> > > > > > drivers/gpu/drm/ttm/ttm_bo.c | 135
> > > > > > +++++++++++--
> > > > > > ----------
> > > > > > drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
> > > > > > drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
> > > > > > drivers/gpu/drm/xe/xe_bo.c | 2 +-
> > > > > > include/drm/ttm/ttm_bo.h | 9 --
> > > > > > 8 files changed, 111 insertions(+), 112 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > > b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > > index df3fcc2b1248e..642296602de69 100644
> > > > > > --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > > +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > > @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
> > > > > > drm_i915_gem_object *obj)
> > > > > > * Don't manipulate the TTM LRUs while in TTM bo
> > > > > > destruction.
> > > > > > * We're called through
> > > > > > i915_ttm_delete_mem_notify().
> > > > > > */
> > > > > > - if (!kref_read(&bo->kref))
> > > > > > + if (!kref_read(&bo->base.refcount))
> > > > > > return;
> > > > > >
> > > > > > /*
> > > > > > @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
> > > > > > drm_i915_gem_object *obj)
> > > > > > *
> > > > > > * TODO: consider maybe also bumping the shrinker
> > > > > > list
> > > > > > here
> > > > > > when we have
> > > > > > * already unpinned it, which should give us
> > > > > > something
> > > > > > more
> > > > > > like an LRU.
> > > > > > - *
> > > > > > - * TODO: There is a small window of opportunity for
> > > > > > this
> > > > > > function to
> > > > > > - * get called from eviction after we've dropped the
> > > > > > last
> > > > > > GEM
> > > > > > refcount,
> > > > > > - * but before the TTM deleted flag is set on the
> > > > > > object.
> > > > > > Avoid
> > > > > > - * adjusting the shrinker list in such cases, since
> > > > > > the
> > > > > > object is
> > > > > > - * not available to the shrinker anyway due to its
> > > > > > zero
> > > > > > refcount.
> > > > > > - * To fix this properly we should move to a TTM
> > > > > > shrinker
> > > > > > LRU
> > > > > > list for
> > > > > > - * these objects.
> > > > > > */
> > > > > > - if (kref_get_unless_zero(&obj->base.refcount)) {
> > > > > > - if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > > > - if (shrinkable) {
> > > > > > - if (obj->mm.madv ==
> > > > > > I915_MADV_WILLNEED)
> > > > > > -
> > > > > > __i915_gem_object_ma
> > > > > > ke_s
> > > > > > hrinkable(obj);
> > > > > > - else
> > > > > > -
> > > > > > __i915_gem_object_ma
> > > > > > ke_p
> > > > > > urgeable(obj);
> > > > > > - } else {
> > > > > > -
> > > > > > i915_gem_object_make_unshrin
> > > > > > kabl
> > > > > > e(obj);
> > > > > > - }
> > > > > > -
> > > > > > - obj->mm.ttm_shrinkable = shrinkable;
> > > > > > + i915_gem_object_get(obj);
> > > > > > + if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > > > + if (shrinkable) {
> > > > > > + if (obj->mm.madv ==
> > > > > > I915_MADV_WILLNEED)
> > > > > > + __i915_gem_object_make_shrin
> > > > > > kabl
> > > > > > e(ob
> > > > > > j);
> > > > > > + else
> > > > > > + __i915_gem_object_make_purge
> > > > > > able
> > > > > > (obj
> > > > > > );
> > > > > > + } else {
> > > > > > + i915_gem_object_make_unshrinkable(ob
> > > > > > j);
> > > > > > }
> > > > > > - i915_gem_object_put(obj);
> > > > > > +
> > > > > > + obj->mm.ttm_shrinkable = shrinkable;
> > > > > > }
> > > > > > + i915_gem_object_put(obj);
> > > > > >
> > > > > > /*
> > > > > > * Put on the correct LRU list depending on the MADV
> > > > > > status
> > > > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > > b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > > index 56ad8ef325840..904cb4da6c9b3 100644
> > > > > > --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > > @@ -127,7 +127,7 @@ static void
> > > > > > ttm_bo_init_reserved_sys_man(struct
> > > > > > kunit *test)
> > > > > > dma_resv_unlock(bo->base.resv);
> > > > > >
> > > > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount),
> > > > > > 1);
> > > > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > > > KUNIT_EXPECT_EQ(test, bo->page_alignment,
> > > > > > PAGE_SIZE);
> > > > > > @@ -176,7 +176,7 @@ static void
> > > > > > ttm_bo_init_reserved_mock_man(struct
> > > > > > kunit *test)
> > > > > > dma_resv_unlock(bo->base.resv);
> > > > > >
> > > > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount),
> > > > > > 1);
> > > > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > > > KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
> > > > > > @@ -969,6 +969,8 @@ static void
> > > > > > ttm_bo_validate_allowed_only_evict(struct kunit *test)
> > > > > > ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
> > > > > > }
> > > > > >
> > > > > > +extern const struct drm_gem_object_funcs
> > > > > > ttm_deleted_object_funcs;
> > > > > > +
> > > > > > static void ttm_bo_validate_deleted_evict(struct kunit
> > > > > > *test)
> > > > > > {
> > > > > > struct ttm_operation_ctx ctx_init = { }, ctx_val =
> > > > > > { };
> > > > > > @@ -999,7 +1001,7 @@ static void
> > > > > > ttm_bo_validate_deleted_evict(struct
> > > > > > kunit *test)
> > > > > > KUNIT_EXPECT_EQ(test,
> > > > > > ttm_resource_manager_usage(man),
> > > > > > big);
> > > > > >
> > > > > > dma_resv_unlock(bo_big->base.resv);
> > > > > > - bo_big->deleted = true;
> > > > > > + bo_big->base.funcs = &ttm_deleted_object_funcs;
> > > > > >
> > > > > > bo_small = ttm_bo_kunit_init(test, test->priv,
> > > > > > small,
> > > > > > NULL);
> > > > > > bo_small->type = bo_type;
> > > > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > > b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > > index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
> > > > > > --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > > @@ -189,8 +189,6 @@ struct ttm_buffer_object
> > > > > > *ttm_bo_kunit_init(struct kunit *test,
> > > > > > bo->bdev = devs->ttm_dev;
> > > > > > bo->destroy = dummy_ttm_bo_destroy;
> > > > > >
> > > > > > - kref_init(&bo->kref);
> > > > > > -
> > > > > > return bo;
> > > > > > }
> > > > > > EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
> > > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > > b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > > index 3980f376e3ba4..2b470c1746f60 100644
> > > > > > --- a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > > @@ -246,88 +246,84 @@ static void
> > > > > > ttm_bo_delayed_delete(struct
> > > > > > work_struct *work)
> > > > > > ttm_bo_put(bo);
> > > > > > }
> > > > > >
> > > > > > -static void ttm_bo_release(struct kref *kref)
> > > > > > +/*
> > > > > > + * All other callbacks should never ever be called on a
> > > > > > deleted
> > > > > > TTM
> > > > > > object.
> > > > > > + */
> > > > > > +const struct drm_gem_object_funcs ttm_deleted_object_funcs =
> > > > > > {
> > > > > > + .free = ttm_bo_free
> > > > > > +};
> > > > > > +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
> > > > > > +
> > > > > > +/* Returns true if the BO is about to get deleted */
> > > > > > +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
> > > > > > +{
> > > > > > + return bo->base.funcs == &ttm_deleted_object_funcs;
> > > > > > +}
> > > > > > +
> > > > > > +void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > > > {
> > > > > > - struct ttm_buffer_object *bo =
> > > > > > - container_of(kref, struct ttm_buffer_object,
> > > > > > kref);
> > > > > > struct ttm_device *bdev = bo->bdev;
> > > > > > int ret;
> > > > > >
> > > > > > WARN_ON_ONCE(bo->pin_count);
> > > > > > WARN_ON_ONCE(bo->bulk_move);
> > > > > >
> > > > > > - if (!bo->deleted) {
> > > > > > - ret = ttm_bo_individualize_resv(bo);
> > > > > > - if (ret) {
> > > > > > - /* Last resort, if we fail to
> > > > > > allocate
> > > > > > memory for the
> > > > > > - * fences block for the BO to become
> > > > > > idle
> > > > > > - */
> > > > > > - dma_resv_wait_timeout(bo->base.resv,
> > > > > > -
> > > > > > DMA_RESV_USAGE_BOOKKEEP, false,
> > > > > > - 30 * HZ);
> > > > > > - }
> > > > > > + ret = ttm_bo_individualize_resv(bo);
> > > > > > + if (ret) {
> > > > > > + /* Last resort, if we fail to allocate
> > > > > > memory
> > > > > > for
> > > > > > the
> > > > > > + * fences block for the BO to become idle
> > > > > > + */
> > > > > > + dma_resv_wait_timeout(bo->base.resv,
> > > > > > DMA_RESV_USAGE_BOOKKEEP,
> > > > > > + false, 30 * HZ);
> > > > > > + }
> > > > > >
> > > > > > - if (bdev->funcs->release_notify)
> > > > > > - bdev->funcs->release_notify(bo);
> > > > > > + if (bo->bdev->funcs->release_notify)
> > > > > > + bo->bdev->funcs->release_notify(bo);
> > > > > >
> > > > > > - drm_vma_offset_remove(bdev->vma_manager,
> > > > > > &bo-
> > > > > > > base.vma_node);
> > > > > > - ttm_mem_io_free(bdev, bo->resource);
> > > > > > + drm_vma_offset_remove(bdev->vma_manager, &bo-
> > > > > > > base.vma_node);
> > > > > > + ttm_mem_io_free(bdev, bo->resource);
> > > > > >
> > > > > > - if (!dma_resv_test_signaled(&bo->base._resv,
> > > > > > -
> > > > > > DMA_RESV_USAGE_BOOKKEEP)
> > > > > > > >
> > > > > > - (want_init_on_free() && (bo->ttm !=
> > > > > > NULL))
> > > > > > > >
> > > > > > - bo->type == ttm_bo_type_sg ||
> > > > > > - !dma_resv_trylock(bo->base.resv)) {
> > > > > > - /* The BO is not idle, resurrect it
> > > > > > for
> > > > > > delayed destroy */
> > > > > > - ttm_bo_flush_all_fences(bo);
> > > > > > - bo->deleted = true;
> > > > > > + if (!dma_resv_test_signaled(&bo->base._resv,
> > > > > > DMA_RESV_USAGE_BOOKKEEP) ||
> > > > > > + (want_init_on_free() && (bo->ttm != NULL)) ||
> > > > > > + bo->type == ttm_bo_type_sg ||
> > > > > > + !dma_resv_trylock(bo->base.resv)) {
> > > > > > + /* The BO is not idle, resurrect it for
> > > > > > delayed
> > > > > > destroy */
> > > > > > + ttm_bo_flush_all_fences(bo);
> > > > > >
> > > > > > - spin_lock(&bdev->lru_lock);
> > > > > > -
> > > > > > - /*
> > > > > > - * Make pinned bos immediately
> > > > > > available
> > > > > > to
> > > > > > - * shrinkers, now that they are
> > > > > > queued
> > > > > > for
> > > > > > - * destruction.
> > > > > > - *
> > > > > > - * FIXME: QXL is triggering this.
> > > > > > Can be
> > > > > > removed when the
> > > > > > - * driver is fixed.
> > > > > > - */
> > > > > > - if (bo->pin_count) {
> > > > > > - bo->pin_count = 0;
> > > > > > -
> > > > > > ttm_resource_move_to_lru_tai
> > > > > > l(bo-
> > > > > > > resource);
> > > > > > - }
> > > > > > + spin_lock(&bo->bdev->lru_lock);
> > > > > >
> > > > > > - kref_init(&bo->kref);
> > > > > > - spin_unlock(&bdev->lru_lock);
> > > > > > + /*
> > > > > > + * Make pinned bos immediately available to
> > > > > > + * shrinkers, now that they are queued for
> > > > > > + * destruction.
> > > > > > + *
> > > > > > + * FIXME: QXL is triggering this. Can be
> > > > > > removed
> > > > > > when the
> > > > > > + * driver is fixed.
> > > > > > + */
> > > > > > + if (bo->pin_count) {
> > > > > > + bo->pin_count = 0;
> > > > > > + ttm_resource_move_to_lru_tail(bo-
> > > > > > > resource);
> > > > > > + }
> > > > > >
> > > > > > - INIT_WORK(&bo->delayed_delete,
> > > > > > ttm_bo_delayed_delete);
> > > > > > + kref_init(&bo->base.refcount);
> > > > > > + bo->base.funcs = &ttm_deleted_object_funcs;
> > > > > > + spin_unlock(&bo->bdev->lru_lock);
> > > > > >
> > > > > > - /* Schedule the worker on the
> > > > > > closest
> > > > > > NUMA
> > > > > > node. This
> > > > > > - * improves performance since system
> > > > > > memory
> > > > > > might be
> > > > > > - * cleared on free and that is best
> > > > > > done
> > > > > > on
> > > > > > a CPU core
> > > > > > - * close to it.
> > > > > > - */
> > > > > > - queue_work_node(bdev->pool.nid,
> > > > > > bdev-
> > > > > > > wq,
> > > > > > &bo->delayed_delete);
> > > > > > - return;
> > > > > > - }
> > > > > > + INIT_WORK(&bo->delayed_delete,
> > > > > > ttm_bo_delayed_delete);
> > > > > >
> > > > > > + /* Schedule the worker on the closest NUMA
> > > > > > node.
> > > > > > This
> > > > > > + * improves performance since system memory
> > > > > > might be
> > > > > > + * cleared on free and that is best done on
> > > > > > a
> > > > > > CPU
> > > > > > core
> > > > > > + * close to it.
> > > > > > + */
> > > > > > + queue_work_node(bdev->pool.nid, bdev->wq,
> > > > > > &bo-
> > > > > > > delayed_delete);
> > > > > > + } else {
> > > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > > dma_resv_unlock(bo->base.resv);
> > > > > > - }
> > > > > >
> > > > > > - atomic_dec(&ttm_glob.bo_count);
> > > > > > - bo->destroy(bo);
> > > > > > -}
> > > > > > -
> > > > > > -/* TODO: remove! */
> > > > > > -void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > > > -{
> > > > > > - kref_put(&bo->kref, ttm_bo_release);
> > > > > > -}
> > > > > > -
> > > > > > -void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > > > -{
> > > > > > - ttm_bo_put(bo);
> > > > > > + atomic_dec(&ttm_glob.bo_count);
> > > > > > + bo->destroy(bo);
> > > > > > + }
> > > > > > }
> > > > > > EXPORT_SYMBOL(ttm_bo_fini);
> > > > > >
> > > > > > @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device
> > > > > > *bdev,
> > > > > > struct ttm_resource_manager *man
> > > > > > if (!bo->resource || bo->resource->mem_type !=
> > > > > > mem_type)
> > > > > > goto out_bo_moved;
> > > > > >
> > > > > > - if (bo->deleted) {
> > > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > > > if (!ret)
> > > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > > @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct
> > > > > > ttm_lru_walk
> > > > > > *walk, struct ttm_buffer_object *
> > > > > > if (bo->pin_count || !bo->bdev->funcs-
> > > > > > > eviction_valuable(bo,
> > > > > > evict_walk->place))
> > > > > > return 0;
> > > > > >
> > > > > > - if (bo->deleted) {
> > > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > > lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
> > > > > > if (!lret)
> > > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > > @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct
> > > > > > ttm_device
> > > > > > *bdev,
> > > > > > void ttm_bo_pin(struct ttm_buffer_object *bo)
> > > > > > {
> > > > > > dma_resv_assert_held(bo->base.resv);
> > > > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > > > spin_lock(&bo->bdev->lru_lock);
> > > > > > if (bo->resource)
> > > > > > ttm_resource_del_bulk_move(bo->resource,
> > > > > > bo);
> > > > > > @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
> > > > > > void ttm_bo_unpin(struct ttm_buffer_object *bo)
> > > > > > {
> > > > > > dma_resv_assert_held(bo->base.resv);
> > > > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > > > if (WARN_ON_ONCE(!bo->pin_count))
> > > > > > return;
> > > > > >
> > > > > > @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct
> > > > > > ttm_device
> > > > > > *bdev,
> > > > > > struct ttm_buffer_object *bo,
> > > > > > {
> > > > > > int ret;
> > > > > >
> > > > > > - kref_init(&bo->kref);
> > > > > > bo->bdev = bdev;
> > > > > > bo->type = type;
> > > > > > bo->page_alignment = alignment;
> > > > > > @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk
> > > > > > *walk,
> > > > > > struct ttm_buffer_object *bo)
> > > > > > goto out;
> > > > > > }
> > > > > >
> > > > > > - if (bo->deleted) {
> > > > > > - pgoff_t num_pages = tt->num_pages;
> > > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > > + pgoff_t num_pages = bo->ttm->num_pages;
> > > > > >
> > > > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > > > if (ret)
> > > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > > b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > > index e0d48eac74b03..ded2a47be0bcb 100644
> > > > > > --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > > @@ -27,6 +27,14 @@
> > > > > >
> > > > > > #include <drm/ttm/ttm_bo.h>
> > > > > >
> > > > > > +static inline void ttm_bo_free(struct drm_gem_object *gobj)
> > > > > > +{
> > > > > > + struct ttm_buffer_object *bo = container_of(gobj,
> > > > > > typeof(*bo), base);
> > > > > > +
> > > > > > + atomic_dec(&ttm_glob.bo_count);
> > > > > > + bo->destroy(bo);
> > > > > > +}
> > > > > > +
> > > > > > /**
> > > > > > * ttm_bo_get - reference a struct ttm_buffer_object
> > > > > > *
> > > > > > @@ -34,7 +42,7 @@
> > > > > > */
> > > > > > static inline void ttm_bo_get(struct ttm_buffer_object *bo)
> > > > > > {
> > > > > > - kref_get(&bo->kref);
> > > > > > + drm_gem_object_get(&bo->base);
> > > > > > }
> > > > > >
> > > > > > /**
> > > > > > @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
> > > > > > ttm_buffer_object *bo)
> > > > > > static inline __must_check struct ttm_buffer_object *
> > > > > > ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
> > > > > > {
> > > > > > - if (!kref_get_unless_zero(&bo->kref))
> > > > > > + if (!kref_get_unless_zero(&bo->base.refcount))
> > > > > > return NULL;
> > > > > > return bo;
> > > > > > }
> > > > > >
> > > > > > -void ttm_bo_put(struct ttm_buffer_object *bo);
> > > > > > +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > > > +{
> > > > > > + drm_gem_object_put(&bo->base);
> > > > > > +}
> > > > > >
> > > > > > #endif
> > > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > > b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > > index 3e3c201a02226..7ed085adf1c9b 100644
> > > > > > --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > > @@ -41,6 +41,18 @@
> > > > > >
> > > > > > #include "ttm_bo_internal.h"
> > > > > >
> > > > > > +static void ttm_transfer_object_free(struct drm_gem_object
> > > > > > *obj)
> > > > > > +{
> > > > > > + struct ttm_buffer_object *bo =
> > > > > > + container_of(obj, struct ttm_buffer_object,
> > > > > > base);
> > > > > > +
> > > > > > + ttm_bo_fini(bo);
> > > > > > +}
> > > > > > +
> > > > > > +const struct drm_gem_object_funcs ttm_transfer_object_funcs
> > > > > > = {
> > > > > > + .free = ttm_transfer_object_free,
> > > > > > +};
> > > > > > +
> > > > > > struct ttm_transfer_obj {
> > > > > > struct ttm_buffer_object base;
> > > > > > struct ttm_buffer_object *bo;
> > > > > > @@ -247,7 +259,8 @@ static int
> > > > > > ttm_buffer_object_transfer(struct
> > > > > > ttm_buffer_object *bo,
> > > > > > atomic_inc(&ttm_glob.bo_count);
> > > > > > drm_vma_node_reset(&fbo->base.base.vma_node);
> > > > > >
> > > > > > - kref_init(&fbo->base.kref);
> > > > > > + kref_init(&fbo->base.base.refcount);
> > > > > > + fbo->base.base.funcs = &ttm_transfer_object_funcs;
> > > > > > fbo->base.destroy = &ttm_transfered_destroy;
> > > > > > fbo->base.pin_count = 0;
> > > > > > if (bo->type != ttm_bo_type_sg)
> > > > > > diff --git a/drivers/gpu/drm/xe/xe_bo.c
> > > > > > b/drivers/gpu/drm/xe/xe_bo.c
> > > > > > index 85e6d9a0f575b..5843f850339c7 100644
> > > > > > --- a/drivers/gpu/drm/xe/xe_bo.c
> > > > > > +++ b/drivers/gpu/drm/xe/xe_bo.c
> > > > > > @@ -1651,7 +1651,7 @@ static bool
> > > > > > xe_ttm_bo_lock_in_destructor(struct
> > > > > > ttm_buffer_object *ttm_bo)
> > > > > > struct xe_device *xe = ttm_to_xe_device(ttm_bo-
> > > > > > >bdev);
> > > > > > bool locked;
> > > > > >
> > > > > > - xe_assert(xe, !kref_read(&ttm_bo->kref));
> > > > > > + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
> > > > > >
> > > > > > /*
> > > > > > * We can typically only race with TTM trylocking
> > > > > > under
> > > > > > the
> > > > > > diff --git a/include/drm/ttm/ttm_bo.h
> > > > > > b/include/drm/ttm/ttm_bo.h
> > > > > > index 8310bc3d55f90..1eae9eea5ff32 100644
> > > > > > --- a/include/drm/ttm/ttm_bo.h
> > > > > > +++ b/include/drm/ttm/ttm_bo.h
> > > > > > @@ -78,11 +78,8 @@ enum ttm_bo_type {
> > > > > > * @type: The bo type.
> > > > > > * @page_alignment: Page alignment.
> > > > > > * @destroy: Destruction function. If NULL, kfree is used.
> > > > > > - * @kref: Reference count of this buffer object. When this
> > > > > > refcount
> > > > > > reaches
> > > > > > - * zero, the object is destroyed or put on the delayed
> > > > > > delete
> > > > > > list.
> > > > > > * @resource: structure describing current placement.
> > > > > > * @ttm: TTM structure holding system pages.
> > > > > > - * @deleted: True if the object is only a zombie and already
> > > > > > deleted.
> > > > > > * @bulk_move: The bulk move object.
> > > > > > * @priority: Priority for LRU, BOs with lower priority are
> > > > > > evicted
> > > > > > first.
> > > > > > * @pin_count: Pin count.
> > > > > > @@ -109,17 +106,11 @@ struct ttm_buffer_object {
> > > > > > uint32_t page_alignment;
> > > > > > void (*destroy) (struct ttm_buffer_object *);
> > > > > >
> > > > > > - /*
> > > > > > - * Members not needing protection.
> > > > > > - */
> > > > > > - struct kref kref;
> > > > > > -
> > > > > > /*
> > > > > > * Members protected by the bo::resv::reserved lock.
> > > > > > */
> > > > > > struct ttm_resource *resource;
> > > > > > struct ttm_tt *ttm;
> > > > > > - bool deleted;
> > > > > > struct ttm_lru_bulk_move *bulk_move;
> > > > > > unsigned priority;
> > > > > > unsigned pin_count;
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-06 18:03 ` Matthew Brost
@ 2026-07-06 18:05 ` Matthew Brost
0 siblings, 0 replies; 26+ messages in thread
From: Matthew Brost @ 2026-07-06 18:05 UTC (permalink / raw)
To: Thomas Hellström
Cc: Christian König, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Alex Deucher, dri-devel, linux-kernel,
intel-gfx, intel-xe, amd-gfx
On Mon, Jul 06, 2026 at 11:03:00AM -0700, Matthew Brost wrote:
> On Mon, Jul 06, 2026 at 07:53:12PM +0200, Thomas Hellström wrote:
> > On Mon, 2026-07-06 at 10:51 -0700, Matthew Brost wrote:
> > > On Mon, Jul 06, 2026 at 07:01:27PM +0200, Thomas Hellström wrote:
> > > > On Mon, 2026-07-06 at 16:49 +0200, Christian König wrote:
> > > > > On 7/6/26 15:14, Thomas Hellström wrote:
> > > > > > On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> > > > > > > From: Christian König <christian.koenig@amd.com>
> > > > > > >
> > > > > > > Instead of keeping a separate reference count for the TTM
> > > > > > > object
> > > > > > > also
> > > > > > > use
> > > > > > > the reference count for DRM GEM objects inside TTM.
> > > > > > >
> > > > > > > Apart from avoiding two reference counts for one object this
> > > > > > > approach
> > > > > > > has
> > > > > > > the clear advantage of being able to use drm_exec inside TTM.
> > > > > > >
> > > > > > > v2: adjust XE assert as well and re-enable disabled test
> > > > > > > v3: handle another case in i915
> > > > > > > v4: set GEM driver funcs of transfer BOs to point to the TTM
> > > > > > > free
> > > > > > > callback (Natalie)
> > > > > >
> > > > > > I think the main review issue from the last time this was on
> > > > > > the
> > > > > > table
> > > > > > was that we shouldn't resurrect the gem refcount. Apart from
> > > > > > the
> > > > > > risc
> > > > > > of getting barriers wrong, both xe and IIRC i915 partly rely on
> > > > > > the
> > > > > > gem
> > > > > > refcount never being resurrected and that callbacks for bos
> > > > > > with
> > > > > > zero
> > > > > > gem refcount means that the gem part of the object is unusable.
> > > > >
> > > > > I've spend quite some time thinking about that and came to the
> > > > > conclusion that this is actually harmless.
> > > > >
> > > > > The drivers shouldn't be able to see the resurected BO, except if
> > > > > they go over the LRU list manually (which they shouldn't).
> > > >
> > > > The shrinker uses the TTM helpers for this. Basically the check
> > > > needs
> > > > to be ported to use the zombie interface but the present change
> > > > also
> > > > widens the window where we can't evict / shrink at all due to zero
> > > > refcounts.
> > > >
> > > > While it might be made harmless, resurrecting a refcount like this
> > > > is
> > > > IMO not something that should leak into the gem refcount. Nobody
> > > > else
> > > > does this in the kernel tree. The bo in reality becomes a zombie
> > > > once
> > > > the gem refcount reaches zero.
> > >
> > > When you say resurrecting a refcount - what exactly do you mean by
> > > this?
> > >
> > > ref -> 0 -> init ref count 1 -> 0 again?
> >
> > Yes, the destructor re-initializes it to 1 again. Prevously this was
> > confined to the TTM refcount only and had limited visibility.
> >
Opps, missed pasting in a line here:
Ok, yes, this is also something in the pipeline for Xe related to CPU bindings:
>
> Put a PT BO on a deferred list -> reinit -> sometime later, do an async
> put. This is all done using the TTM refcount. Of course, we don't have
> to do it this way, though.
>
> In general, I agree that tricks like this are not the most desirable
> approach. If I recall correctly, I implemented this in the Xe code
> because it followed an existing paradigm. If we want to merge the TTM
> and GEM refcounts, I agree that we should likely move away from this
> paradigm, since the GEM refcount significantly expands its scope.
>
> Matt
>
> > /Thomas
> >
> > >
> > > Matt
> > >
> > > >
> > > > >
> > > > > >
> > > > > > For example xe_bo.c:
> > > > > >
> > > > > > if (!xe_bo_is_xe_bo(bo) ||
> > > > > > !xe_bo_get_unless_zero(xe_bo))
> > > > > > return xe_bo_shrink_purge(ctx, bo, scanned);
> > > > > >
> > > > > > So IIRC the conclusion was when removing the ttm refcount we
> > > > > > shouldn't
> > > > > > attempt to resurrect the gem one. If the get_unless_zero()
> > > > > > fails
> > > > > > during
> > > > > > evict walk, we simply find something to wait for. See previous
> > > > > > discussion there.
> > > > >
> > > > > Yeah, I considered that as well but the problem is we often
> > > > > doesn't
> > > > > have anything to wait on.
> > > >
> > > > That's not the conclusion of the previous discussion?
> > > >
> > > > https://lore.kernel.org/dri-devel/20250716160555.20217-2-christian.koenig@amd.com/#r
> > > >
> > > > >
> > > > > >
> > > > > > I fully support removing the ttm refcount, but not if it means
> > > > > > resurrecting the gem refcount.
> > > > > >
> > > > > > If we want to sidestep that problem, in favour of getting the
> > > > > > proposed
> > > > > > locking functionality in and future proof it, I suggest using
> > > > > >
> > > > > > https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
> > > > > >
> > > > > > And rebase this series on that. This means we can use the ttm
> > > > > > refcount
> > > > > > for the transaction refcounting, and also that if we add a dma-
> > > > > > buf
> > > > > > map
> > > > > > interface with a dma_resv_txn_obj, we could use that to also
> > > > > > have
> > > > > > exhaustive eviction that originates from a dma_buf map.
> > > > >
> > > > > I don't think that this is a good idea. It just adds another
> > > > > layer of
> > > > > abstraction and doesn't solve the problem in any way possible.
> > > >
> > > > This comment confuses me. Exactly what problem isn't solved by
> > > > this,
> > > > and which of the stated benefits/use-cases in the cover-letter do
> > > > you
> > > > think aren't worthwhile?
> > > >
> > > > Also for reference: (Section at the end and follow-up messages)
> > > > https://lore.kernel.org/all/3716d43462188590743060755b37e3d060f7600f.camel@linux.intel.com/
> > > >
> > > > Thanks,
> > > > Thomas
> > > >
> > > >
> > > > > Regards,
> > > > > Christian.
> > > > >
> > > > > >
> > > > > > /Thomas
> > > > > >
> > > > > > >
> > > > > > > Signed-off-by: tChristian König <christian.koenig@amd.com>
> > > > > > > Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> > > > > > > ---
> > > > > > > drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++--
> > > > > > > -
> > > > > > > drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
> > > > > > > drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
> > > > > > > drivers/gpu/drm/ttm/ttm_bo.c | 135
> > > > > > > +++++++++++--
> > > > > > > ----------
> > > > > > > drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
> > > > > > > drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
> > > > > > > drivers/gpu/drm/xe/xe_bo.c | 2 +-
> > > > > > > include/drm/ttm/ttm_bo.h | 9 --
> > > > > > > 8 files changed, 111 insertions(+), 112 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > > > b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > > > index df3fcc2b1248e..642296602de69 100644
> > > > > > > --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > > > +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > > > @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
> > > > > > > drm_i915_gem_object *obj)
> > > > > > > * Don't manipulate the TTM LRUs while in TTM bo
> > > > > > > destruction.
> > > > > > > * We're called through
> > > > > > > i915_ttm_delete_mem_notify().
> > > > > > > */
> > > > > > > - if (!kref_read(&bo->kref))
> > > > > > > + if (!kref_read(&bo->base.refcount))
> > > > > > > return;
> > > > > > >
> > > > > > > /*
> > > > > > > @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
> > > > > > > drm_i915_gem_object *obj)
> > > > > > > *
> > > > > > > * TODO: consider maybe also bumping the shrinker
> > > > > > > list
> > > > > > > here
> > > > > > > when we have
> > > > > > > * already unpinned it, which should give us
> > > > > > > something
> > > > > > > more
> > > > > > > like an LRU.
> > > > > > > - *
> > > > > > > - * TODO: There is a small window of opportunity for
> > > > > > > this
> > > > > > > function to
> > > > > > > - * get called from eviction after we've dropped the
> > > > > > > last
> > > > > > > GEM
> > > > > > > refcount,
> > > > > > > - * but before the TTM deleted flag is set on the
> > > > > > > object.
> > > > > > > Avoid
> > > > > > > - * adjusting the shrinker list in such cases, since
> > > > > > > the
> > > > > > > object is
> > > > > > > - * not available to the shrinker anyway due to its
> > > > > > > zero
> > > > > > > refcount.
> > > > > > > - * To fix this properly we should move to a TTM
> > > > > > > shrinker
> > > > > > > LRU
> > > > > > > list for
> > > > > > > - * these objects.
> > > > > > > */
> > > > > > > - if (kref_get_unless_zero(&obj->base.refcount)) {
> > > > > > > - if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > > > > - if (shrinkable) {
> > > > > > > - if (obj->mm.madv ==
> > > > > > > I915_MADV_WILLNEED)
> > > > > > > -
> > > > > > > __i915_gem_object_ma
> > > > > > > ke_s
> > > > > > > hrinkable(obj);
> > > > > > > - else
> > > > > > > -
> > > > > > > __i915_gem_object_ma
> > > > > > > ke_p
> > > > > > > urgeable(obj);
> > > > > > > - } else {
> > > > > > > -
> > > > > > > i915_gem_object_make_unshrin
> > > > > > > kabl
> > > > > > > e(obj);
> > > > > > > - }
> > > > > > > -
> > > > > > > - obj->mm.ttm_shrinkable = shrinkable;
> > > > > > > + i915_gem_object_get(obj);
> > > > > > > + if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > > > > + if (shrinkable) {
> > > > > > > + if (obj->mm.madv ==
> > > > > > > I915_MADV_WILLNEED)
> > > > > > > + __i915_gem_object_make_shrin
> > > > > > > kabl
> > > > > > > e(ob
> > > > > > > j);
> > > > > > > + else
> > > > > > > + __i915_gem_object_make_purge
> > > > > > > able
> > > > > > > (obj
> > > > > > > );
> > > > > > > + } else {
> > > > > > > + i915_gem_object_make_unshrinkable(ob
> > > > > > > j);
> > > > > > > }
> > > > > > > - i915_gem_object_put(obj);
> > > > > > > +
> > > > > > > + obj->mm.ttm_shrinkable = shrinkable;
> > > > > > > }
> > > > > > > + i915_gem_object_put(obj);
> > > > > > >
> > > > > > > /*
> > > > > > > * Put on the correct LRU list depending on the MADV
> > > > > > > status
> > > > > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > > > b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > > > index 56ad8ef325840..904cb4da6c9b3 100644
> > > > > > > --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > > > @@ -127,7 +127,7 @@ static void
> > > > > > > ttm_bo_init_reserved_sys_man(struct
> > > > > > > kunit *test)
> > > > > > > dma_resv_unlock(bo->base.resv);
> > > > > > >
> > > > > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount),
> > > > > > > 1);
> > > > > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > > > > KUNIT_EXPECT_EQ(test, bo->page_alignment,
> > > > > > > PAGE_SIZE);
> > > > > > > @@ -176,7 +176,7 @@ static void
> > > > > > > ttm_bo_init_reserved_mock_man(struct
> > > > > > > kunit *test)
> > > > > > > dma_resv_unlock(bo->base.resv);
> > > > > > >
> > > > > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount),
> > > > > > > 1);
> > > > > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > > > > KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
> > > > > > > @@ -969,6 +969,8 @@ static void
> > > > > > > ttm_bo_validate_allowed_only_evict(struct kunit *test)
> > > > > > > ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
> > > > > > > }
> > > > > > >
> > > > > > > +extern const struct drm_gem_object_funcs
> > > > > > > ttm_deleted_object_funcs;
> > > > > > > +
> > > > > > > static void ttm_bo_validate_deleted_evict(struct kunit
> > > > > > > *test)
> > > > > > > {
> > > > > > > struct ttm_operation_ctx ctx_init = { }, ctx_val =
> > > > > > > { };
> > > > > > > @@ -999,7 +1001,7 @@ static void
> > > > > > > ttm_bo_validate_deleted_evict(struct
> > > > > > > kunit *test)
> > > > > > > KUNIT_EXPECT_EQ(test,
> > > > > > > ttm_resource_manager_usage(man),
> > > > > > > big);
> > > > > > >
> > > > > > > dma_resv_unlock(bo_big->base.resv);
> > > > > > > - bo_big->deleted = true;
> > > > > > > + bo_big->base.funcs = &ttm_deleted_object_funcs;
> > > > > > >
> > > > > > > bo_small = ttm_bo_kunit_init(test, test->priv,
> > > > > > > small,
> > > > > > > NULL);
> > > > > > > bo_small->type = bo_type;
> > > > > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > > > b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > > > index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
> > > > > > > --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > > > @@ -189,8 +189,6 @@ struct ttm_buffer_object
> > > > > > > *ttm_bo_kunit_init(struct kunit *test,
> > > > > > > bo->bdev = devs->ttm_dev;
> > > > > > > bo->destroy = dummy_ttm_bo_destroy;
> > > > > > >
> > > > > > > - kref_init(&bo->kref);
> > > > > > > -
> > > > > > > return bo;
> > > > > > > }
> > > > > > > EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
> > > > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > > > b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > > > index 3980f376e3ba4..2b470c1746f60 100644
> > > > > > > --- a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > > > @@ -246,88 +246,84 @@ static void
> > > > > > > ttm_bo_delayed_delete(struct
> > > > > > > work_struct *work)
> > > > > > > ttm_bo_put(bo);
> > > > > > > }
> > > > > > >
> > > > > > > -static void ttm_bo_release(struct kref *kref)
> > > > > > > +/*
> > > > > > > + * All other callbacks should never ever be called on a
> > > > > > > deleted
> > > > > > > TTM
> > > > > > > object.
> > > > > > > + */
> > > > > > > +const struct drm_gem_object_funcs ttm_deleted_object_funcs =
> > > > > > > {
> > > > > > > + .free = ttm_bo_free
> > > > > > > +};
> > > > > > > +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
> > > > > > > +
> > > > > > > +/* Returns true if the BO is about to get deleted */
> > > > > > > +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
> > > > > > > +{
> > > > > > > + return bo->base.funcs == &ttm_deleted_object_funcs;
> > > > > > > +}
> > > > > > > +
> > > > > > > +void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > > > > {
> > > > > > > - struct ttm_buffer_object *bo =
> > > > > > > - container_of(kref, struct ttm_buffer_object,
> > > > > > > kref);
> > > > > > > struct ttm_device *bdev = bo->bdev;
> > > > > > > int ret;
> > > > > > >
> > > > > > > WARN_ON_ONCE(bo->pin_count);
> > > > > > > WARN_ON_ONCE(bo->bulk_move);
> > > > > > >
> > > > > > > - if (!bo->deleted) {
> > > > > > > - ret = ttm_bo_individualize_resv(bo);
> > > > > > > - if (ret) {
> > > > > > > - /* Last resort, if we fail to
> > > > > > > allocate
> > > > > > > memory for the
> > > > > > > - * fences block for the BO to become
> > > > > > > idle
> > > > > > > - */
> > > > > > > - dma_resv_wait_timeout(bo->base.resv,
> > > > > > > -
> > > > > > > DMA_RESV_USAGE_BOOKKEEP, false,
> > > > > > > - 30 * HZ);
> > > > > > > - }
> > > > > > > + ret = ttm_bo_individualize_resv(bo);
> > > > > > > + if (ret) {
> > > > > > > + /* Last resort, if we fail to allocate
> > > > > > > memory
> > > > > > > for
> > > > > > > the
> > > > > > > + * fences block for the BO to become idle
> > > > > > > + */
> > > > > > > + dma_resv_wait_timeout(bo->base.resv,
> > > > > > > DMA_RESV_USAGE_BOOKKEEP,
> > > > > > > + false, 30 * HZ);
> > > > > > > + }
> > > > > > >
> > > > > > > - if (bdev->funcs->release_notify)
> > > > > > > - bdev->funcs->release_notify(bo);
> > > > > > > + if (bo->bdev->funcs->release_notify)
> > > > > > > + bo->bdev->funcs->release_notify(bo);
> > > > > > >
> > > > > > > - drm_vma_offset_remove(bdev->vma_manager,
> > > > > > > &bo-
> > > > > > > > base.vma_node);
> > > > > > > - ttm_mem_io_free(bdev, bo->resource);
> > > > > > > + drm_vma_offset_remove(bdev->vma_manager, &bo-
> > > > > > > > base.vma_node);
> > > > > > > + ttm_mem_io_free(bdev, bo->resource);
> > > > > > >
> > > > > > > - if (!dma_resv_test_signaled(&bo->base._resv,
> > > > > > > -
> > > > > > > DMA_RESV_USAGE_BOOKKEEP)
> > > > > > > > >
> > > > > > > - (want_init_on_free() && (bo->ttm !=
> > > > > > > NULL))
> > > > > > > > >
> > > > > > > - bo->type == ttm_bo_type_sg ||
> > > > > > > - !dma_resv_trylock(bo->base.resv)) {
> > > > > > > - /* The BO is not idle, resurrect it
> > > > > > > for
> > > > > > > delayed destroy */
> > > > > > > - ttm_bo_flush_all_fences(bo);
> > > > > > > - bo->deleted = true;
> > > > > > > + if (!dma_resv_test_signaled(&bo->base._resv,
> > > > > > > DMA_RESV_USAGE_BOOKKEEP) ||
> > > > > > > + (want_init_on_free() && (bo->ttm != NULL)) ||
> > > > > > > + bo->type == ttm_bo_type_sg ||
> > > > > > > + !dma_resv_trylock(bo->base.resv)) {
> > > > > > > + /* The BO is not idle, resurrect it for
> > > > > > > delayed
> > > > > > > destroy */
> > > > > > > + ttm_bo_flush_all_fences(bo);
> > > > > > >
> > > > > > > - spin_lock(&bdev->lru_lock);
> > > > > > > -
> > > > > > > - /*
> > > > > > > - * Make pinned bos immediately
> > > > > > > available
> > > > > > > to
> > > > > > > - * shrinkers, now that they are
> > > > > > > queued
> > > > > > > for
> > > > > > > - * destruction.
> > > > > > > - *
> > > > > > > - * FIXME: QXL is triggering this.
> > > > > > > Can be
> > > > > > > removed when the
> > > > > > > - * driver is fixed.
> > > > > > > - */
> > > > > > > - if (bo->pin_count) {
> > > > > > > - bo->pin_count = 0;
> > > > > > > -
> > > > > > > ttm_resource_move_to_lru_tai
> > > > > > > l(bo-
> > > > > > > > resource);
> > > > > > > - }
> > > > > > > + spin_lock(&bo->bdev->lru_lock);
> > > > > > >
> > > > > > > - kref_init(&bo->kref);
> > > > > > > - spin_unlock(&bdev->lru_lock);
> > > > > > > + /*
> > > > > > > + * Make pinned bos immediately available to
> > > > > > > + * shrinkers, now that they are queued for
> > > > > > > + * destruction.
> > > > > > > + *
> > > > > > > + * FIXME: QXL is triggering this. Can be
> > > > > > > removed
> > > > > > > when the
> > > > > > > + * driver is fixed.
> > > > > > > + */
> > > > > > > + if (bo->pin_count) {
> > > > > > > + bo->pin_count = 0;
> > > > > > > + ttm_resource_move_to_lru_tail(bo-
> > > > > > > > resource);
> > > > > > > + }
> > > > > > >
> > > > > > > - INIT_WORK(&bo->delayed_delete,
> > > > > > > ttm_bo_delayed_delete);
> > > > > > > + kref_init(&bo->base.refcount);
> > > > > > > + bo->base.funcs = &ttm_deleted_object_funcs;
> > > > > > > + spin_unlock(&bo->bdev->lru_lock);
> > > > > > >
> > > > > > > - /* Schedule the worker on the
> > > > > > > closest
> > > > > > > NUMA
> > > > > > > node. This
> > > > > > > - * improves performance since system
> > > > > > > memory
> > > > > > > might be
> > > > > > > - * cleared on free and that is best
> > > > > > > done
> > > > > > > on
> > > > > > > a CPU core
> > > > > > > - * close to it.
> > > > > > > - */
> > > > > > > - queue_work_node(bdev->pool.nid,
> > > > > > > bdev-
> > > > > > > > wq,
> > > > > > > &bo->delayed_delete);
> > > > > > > - return;
> > > > > > > - }
> > > > > > > + INIT_WORK(&bo->delayed_delete,
> > > > > > > ttm_bo_delayed_delete);
> > > > > > >
> > > > > > > + /* Schedule the worker on the closest NUMA
> > > > > > > node.
> > > > > > > This
> > > > > > > + * improves performance since system memory
> > > > > > > might be
> > > > > > > + * cleared on free and that is best done on
> > > > > > > a
> > > > > > > CPU
> > > > > > > core
> > > > > > > + * close to it.
> > > > > > > + */
> > > > > > > + queue_work_node(bdev->pool.nid, bdev->wq,
> > > > > > > &bo-
> > > > > > > > delayed_delete);
> > > > > > > + } else {
> > > > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > > > dma_resv_unlock(bo->base.resv);
> > > > > > > - }
> > > > > > >
> > > > > > > - atomic_dec(&ttm_glob.bo_count);
> > > > > > > - bo->destroy(bo);
> > > > > > > -}
> > > > > > > -
> > > > > > > -/* TODO: remove! */
> > > > > > > -void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > > > > -{
> > > > > > > - kref_put(&bo->kref, ttm_bo_release);
> > > > > > > -}
> > > > > > > -
> > > > > > > -void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > > > > -{
> > > > > > > - ttm_bo_put(bo);
> > > > > > > + atomic_dec(&ttm_glob.bo_count);
> > > > > > > + bo->destroy(bo);
> > > > > > > + }
> > > > > > > }
> > > > > > > EXPORT_SYMBOL(ttm_bo_fini);
> > > > > > >
> > > > > > > @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device
> > > > > > > *bdev,
> > > > > > > struct ttm_resource_manager *man
> > > > > > > if (!bo->resource || bo->resource->mem_type !=
> > > > > > > mem_type)
> > > > > > > goto out_bo_moved;
> > > > > > >
> > > > > > > - if (bo->deleted) {
> > > > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > > > > if (!ret)
> > > > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > > > @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct
> > > > > > > ttm_lru_walk
> > > > > > > *walk, struct ttm_buffer_object *
> > > > > > > if (bo->pin_count || !bo->bdev->funcs-
> > > > > > > > eviction_valuable(bo,
> > > > > > > evict_walk->place))
> > > > > > > return 0;
> > > > > > >
> > > > > > > - if (bo->deleted) {
> > > > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > > > lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
> > > > > > > if (!lret)
> > > > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > > > @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct
> > > > > > > ttm_device
> > > > > > > *bdev,
> > > > > > > void ttm_bo_pin(struct ttm_buffer_object *bo)
> > > > > > > {
> > > > > > > dma_resv_assert_held(bo->base.resv);
> > > > > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > > > > spin_lock(&bo->bdev->lru_lock);
> > > > > > > if (bo->resource)
> > > > > > > ttm_resource_del_bulk_move(bo->resource,
> > > > > > > bo);
> > > > > > > @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
> > > > > > > void ttm_bo_unpin(struct ttm_buffer_object *bo)
> > > > > > > {
> > > > > > > dma_resv_assert_held(bo->base.resv);
> > > > > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > > > > if (WARN_ON_ONCE(!bo->pin_count))
> > > > > > > return;
> > > > > > >
> > > > > > > @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct
> > > > > > > ttm_device
> > > > > > > *bdev,
> > > > > > > struct ttm_buffer_object *bo,
> > > > > > > {
> > > > > > > int ret;
> > > > > > >
> > > > > > > - kref_init(&bo->kref);
> > > > > > > bo->bdev = bdev;
> > > > > > > bo->type = type;
> > > > > > > bo->page_alignment = alignment;
> > > > > > > @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk
> > > > > > > *walk,
> > > > > > > struct ttm_buffer_object *bo)
> > > > > > > goto out;
> > > > > > > }
> > > > > > >
> > > > > > > - if (bo->deleted) {
> > > > > > > - pgoff_t num_pages = tt->num_pages;
> > > > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > > > + pgoff_t num_pages = bo->ttm->num_pages;
> > > > > > >
> > > > > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > > > > if (ret)
> > > > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > > > b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > > > index e0d48eac74b03..ded2a47be0bcb 100644
> > > > > > > --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > > > @@ -27,6 +27,14 @@
> > > > > > >
> > > > > > > #include <drm/ttm/ttm_bo.h>
> > > > > > >
> > > > > > > +static inline void ttm_bo_free(struct drm_gem_object *gobj)
> > > > > > > +{
> > > > > > > + struct ttm_buffer_object *bo = container_of(gobj,
> > > > > > > typeof(*bo), base);
> > > > > > > +
> > > > > > > + atomic_dec(&ttm_glob.bo_count);
> > > > > > > + bo->destroy(bo);
> > > > > > > +}
> > > > > > > +
> > > > > > > /**
> > > > > > > * ttm_bo_get - reference a struct ttm_buffer_object
> > > > > > > *
> > > > > > > @@ -34,7 +42,7 @@
> > > > > > > */
> > > > > > > static inline void ttm_bo_get(struct ttm_buffer_object *bo)
> > > > > > > {
> > > > > > > - kref_get(&bo->kref);
> > > > > > > + drm_gem_object_get(&bo->base);
> > > > > > > }
> > > > > > >
> > > > > > > /**
> > > > > > > @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
> > > > > > > ttm_buffer_object *bo)
> > > > > > > static inline __must_check struct ttm_buffer_object *
> > > > > > > ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
> > > > > > > {
> > > > > > > - if (!kref_get_unless_zero(&bo->kref))
> > > > > > > + if (!kref_get_unless_zero(&bo->base.refcount))
> > > > > > > return NULL;
> > > > > > > return bo;
> > > > > > > }
> > > > > > >
> > > > > > > -void ttm_bo_put(struct ttm_buffer_object *bo);
> > > > > > > +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > > > > +{
> > > > > > > + drm_gem_object_put(&bo->base);
> > > > > > > +}
> > > > > > >
> > > > > > > #endif
> > > > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > > > b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > > > index 3e3c201a02226..7ed085adf1c9b 100644
> > > > > > > --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > > > @@ -41,6 +41,18 @@
> > > > > > >
> > > > > > > #include "ttm_bo_internal.h"
> > > > > > >
> > > > > > > +static void ttm_transfer_object_free(struct drm_gem_object
> > > > > > > *obj)
> > > > > > > +{
> > > > > > > + struct ttm_buffer_object *bo =
> > > > > > > + container_of(obj, struct ttm_buffer_object,
> > > > > > > base);
> > > > > > > +
> > > > > > > + ttm_bo_fini(bo);
> > > > > > > +}
> > > > > > > +
> > > > > > > +const struct drm_gem_object_funcs ttm_transfer_object_funcs
> > > > > > > = {
> > > > > > > + .free = ttm_transfer_object_free,
> > > > > > > +};
> > > > > > > +
> > > > > > > struct ttm_transfer_obj {
> > > > > > > struct ttm_buffer_object base;
> > > > > > > struct ttm_buffer_object *bo;
> > > > > > > @@ -247,7 +259,8 @@ static int
> > > > > > > ttm_buffer_object_transfer(struct
> > > > > > > ttm_buffer_object *bo,
> > > > > > > atomic_inc(&ttm_glob.bo_count);
> > > > > > > drm_vma_node_reset(&fbo->base.base.vma_node);
> > > > > > >
> > > > > > > - kref_init(&fbo->base.kref);
> > > > > > > + kref_init(&fbo->base.base.refcount);
> > > > > > > + fbo->base.base.funcs = &ttm_transfer_object_funcs;
> > > > > > > fbo->base.destroy = &ttm_transfered_destroy;
> > > > > > > fbo->base.pin_count = 0;
> > > > > > > if (bo->type != ttm_bo_type_sg)
> > > > > > > diff --git a/drivers/gpu/drm/xe/xe_bo.c
> > > > > > > b/drivers/gpu/drm/xe/xe_bo.c
> > > > > > > index 85e6d9a0f575b..5843f850339c7 100644
> > > > > > > --- a/drivers/gpu/drm/xe/xe_bo.c
> > > > > > > +++ b/drivers/gpu/drm/xe/xe_bo.c
> > > > > > > @@ -1651,7 +1651,7 @@ static bool
> > > > > > > xe_ttm_bo_lock_in_destructor(struct
> > > > > > > ttm_buffer_object *ttm_bo)
> > > > > > > struct xe_device *xe = ttm_to_xe_device(ttm_bo-
> > > > > > > >bdev);
> > > > > > > bool locked;
> > > > > > >
> > > > > > > - xe_assert(xe, !kref_read(&ttm_bo->kref));
> > > > > > > + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
> > > > > > >
> > > > > > > /*
> > > > > > > * We can typically only race with TTM trylocking
> > > > > > > under
> > > > > > > the
> > > > > > > diff --git a/include/drm/ttm/ttm_bo.h
> > > > > > > b/include/drm/ttm/ttm_bo.h
> > > > > > > index 8310bc3d55f90..1eae9eea5ff32 100644
> > > > > > > --- a/include/drm/ttm/ttm_bo.h
> > > > > > > +++ b/include/drm/ttm/ttm_bo.h
> > > > > > > @@ -78,11 +78,8 @@ enum ttm_bo_type {
> > > > > > > * @type: The bo type.
> > > > > > > * @page_alignment: Page alignment.
> > > > > > > * @destroy: Destruction function. If NULL, kfree is used.
> > > > > > > - * @kref: Reference count of this buffer object. When this
> > > > > > > refcount
> > > > > > > reaches
> > > > > > > - * zero, the object is destroyed or put on the delayed
> > > > > > > delete
> > > > > > > list.
> > > > > > > * @resource: structure describing current placement.
> > > > > > > * @ttm: TTM structure holding system pages.
> > > > > > > - * @deleted: True if the object is only a zombie and already
> > > > > > > deleted.
> > > > > > > * @bulk_move: The bulk move object.
> > > > > > > * @priority: Priority for LRU, BOs with lower priority are
> > > > > > > evicted
> > > > > > > first.
> > > > > > > * @pin_count: Pin count.
> > > > > > > @@ -109,17 +106,11 @@ struct ttm_buffer_object {
> > > > > > > uint32_t page_alignment;
> > > > > > > void (*destroy) (struct ttm_buffer_object *);
> > > > > > >
> > > > > > > - /*
> > > > > > > - * Members not needing protection.
> > > > > > > - */
> > > > > > > - struct kref kref;
> > > > > > > -
> > > > > > > /*
> > > > > > > * Members protected by the bo::resv::reserved lock.
> > > > > > > */
> > > > > > > struct ttm_resource *resource;
> > > > > > > struct ttm_tt *ttm;
> > > > > > > - bool deleted;
> > > > > > > struct ttm_lru_bulk_move *bulk_move;
> > > > > > > unsigned priority;
> > > > > > > unsigned pin_count;
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-06 17:51 ` Matthew Brost
2026-07-06 17:53 ` Thomas Hellström
@ 2026-07-06 18:03 ` Thomas Hellström
1 sibling, 0 replies; 26+ messages in thread
From: Thomas Hellström @ 2026-07-06 18:03 UTC (permalink / raw)
To: Matthew Brost
Cc: Christian König, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Alex Deucher, dri-devel, linux-kernel,
intel-gfx, intel-xe, amd-gfx
On Mon, 2026-07-06 at 10:51 -0700, Matthew Brost wrote:
> On Mon, Jul 06, 2026 at 07:01:27PM +0200, Thomas Hellström wrote:
> > On Mon, 2026-07-06 at 16:49 +0200, Christian König wrote:
> > > On 7/6/26 15:14, Thomas Hellström wrote:
> > > > On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> > > > > From: Christian König <christian.koenig@amd.com>
> > > > >
> > > > > Instead of keeping a separate reference count for the TTM
> > > > > object
> > > > > also
> > > > > use
> > > > > the reference count for DRM GEM objects inside TTM.
> > > > >
> > > > > Apart from avoiding two reference counts for one object this
> > > > > approach
> > > > > has
> > > > > the clear advantage of being able to use drm_exec inside TTM.
> > > > >
> > > > > v2: adjust XE assert as well and re-enable disabled test
> > > > > v3: handle another case in i915
> > > > > v4: set GEM driver funcs of transfer BOs to point to the TTM
> > > > > free
> > > > > callback (Natalie)
> > > >
> > > > I think the main review issue from the last time this was on
> > > > the
> > > > table
> > > > was that we shouldn't resurrect the gem refcount. Apart from
> > > > the
> > > > risc
> > > > of getting barriers wrong, both xe and IIRC i915 partly rely on
> > > > the
> > > > gem
> > > > refcount never being resurrected and that callbacks for bos
> > > > with
> > > > zero
> > > > gem refcount means that the gem part of the object is unusable.
> > >
> > > I've spend quite some time thinking about that and came to the
> > > conclusion that this is actually harmless.
> > >
> > > The drivers shouldn't be able to see the resurected BO, except if
> > > they go over the LRU list manually (which they shouldn't).
> >
> > The shrinker uses the TTM helpers for this. Basically the check
> > needs
> > to be ported to use the zombie interface but the present change
> > also
> > widens the window where we can't evict / shrink at all due to zero
> > refcounts.
> >
> > While it might be made harmless, resurrecting a refcount like this
> > is
> > IMO not something that should leak into the gem refcount. Nobody
> > else
> > does this in the kernel tree. The bo in reality becomes a zombie
> > once
> > the gem refcount reaches zero.
>
> When you say resurrecting a refcount - what exactly do you mean by
> this?
>
> ref -> 0 -> init ref count 1 -> 0 again?
Resending with full CC list.
Yes. The destructor re-initializes the refcount to 1. Previously this
was confined to the TTM refcount with limited visibility.
/Thomas
>
> Matt
>
> >
> > >
> > > >
> > > > For example xe_bo.c:
> > > >
> > > > if (!xe_bo_is_xe_bo(bo) ||
> > > > !xe_bo_get_unless_zero(xe_bo))
> > > > return xe_bo_shrink_purge(ctx, bo, scanned);
> > > >
> > > > So IIRC the conclusion was when removing the ttm refcount we
> > > > shouldn't
> > > > attempt to resurrect the gem one. If the get_unless_zero()
> > > > fails
> > > > during
> > > > evict walk, we simply find something to wait for. See previous
> > > > discussion there.
> > >
> > > Yeah, I considered that as well but the problem is we often
> > > doesn't
> > > have anything to wait on.
> >
> > That's not the conclusion of the previous discussion?
> >
> > https://lore.kernel.org/dri-devel/20250716160555.20217-2-christian.koenig@amd.com/#r
> >
> > >
> > > >
> > > > I fully support removing the ttm refcount, but not if it means
> > > > resurrecting the gem refcount.
> > > >
> > > > If we want to sidestep that problem, in favour of getting the
> > > > proposed
> > > > locking functionality in and future proof it, I suggest using
> > > >
> > > > https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
> > > >
> > > > And rebase this series on that. This means we can use the ttm
> > > > refcount
> > > > for the transaction refcounting, and also that if we add a dma-
> > > > buf
> > > > map
> > > > interface with a dma_resv_txn_obj, we could use that to also
> > > > have
> > > > exhaustive eviction that originates from a dma_buf map.
> > >
> > > I don't think that this is a good idea. It just adds another
> > > layer of
> > > abstraction and doesn't solve the problem in any way possible.
> >
> > This comment confuses me. Exactly what problem isn't solved by
> > this,
> > and which of the stated benefits/use-cases in the cover-letter do
> > you
> > think aren't worthwhile?
> >
> > Also for reference: (Section at the end and follow-up messages)
> > https://lore.kernel.org/all/3716d43462188590743060755b37e3d060f7600f.camel@linux.intel.com/
> >
> > Thanks,
> > Thomas
> >
> >
> > > Regards,
> > > Christian.
> > >
> > > >
> > > > /Thomas
> > > >
> > > > >
> > > > > Signed-off-by: tChristian König <christian.koenig@amd.com>
> > > > > Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> > > > > ---
> > > > > drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++--
> > > > > -
> > > > > drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
> > > > > drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
> > > > > drivers/gpu/drm/ttm/ttm_bo.c | 135
> > > > > +++++++++++--
> > > > > ----------
> > > > > drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
> > > > > drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
> > > > > drivers/gpu/drm/xe/xe_bo.c | 2 +-
> > > > > include/drm/ttm/ttm_bo.h | 9 --
> > > > > 8 files changed, 111 insertions(+), 112 deletions(-)
> > > > >
> > > > > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > index df3fcc2b1248e..642296602de69 100644
> > > > > --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> > > > > @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
> > > > > drm_i915_gem_object *obj)
> > > > > * Don't manipulate the TTM LRUs while in TTM bo
> > > > > destruction.
> > > > > * We're called through
> > > > > i915_ttm_delete_mem_notify().
> > > > > */
> > > > > - if (!kref_read(&bo->kref))
> > > > > + if (!kref_read(&bo->base.refcount))
> > > > > return;
> > > > >
> > > > > /*
> > > > > @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
> > > > > drm_i915_gem_object *obj)
> > > > > *
> > > > > * TODO: consider maybe also bumping the shrinker
> > > > > list
> > > > > here
> > > > > when we have
> > > > > * already unpinned it, which should give us
> > > > > something
> > > > > more
> > > > > like an LRU.
> > > > > - *
> > > > > - * TODO: There is a small window of opportunity for
> > > > > this
> > > > > function to
> > > > > - * get called from eviction after we've dropped the
> > > > > last
> > > > > GEM
> > > > > refcount,
> > > > > - * but before the TTM deleted flag is set on the
> > > > > object.
> > > > > Avoid
> > > > > - * adjusting the shrinker list in such cases, since
> > > > > the
> > > > > object is
> > > > > - * not available to the shrinker anyway due to its
> > > > > zero
> > > > > refcount.
> > > > > - * To fix this properly we should move to a TTM
> > > > > shrinker
> > > > > LRU
> > > > > list for
> > > > > - * these objects.
> > > > > */
> > > > > - if (kref_get_unless_zero(&obj->base.refcount)) {
> > > > > - if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > > - if (shrinkable) {
> > > > > - if (obj->mm.madv ==
> > > > > I915_MADV_WILLNEED)
> > > > > -
> > > > > __i915_gem_object_ma
> > > > > ke_s
> > > > > hrinkable(obj);
> > > > > - else
> > > > > -
> > > > > __i915_gem_object_ma
> > > > > ke_p
> > > > > urgeable(obj);
> > > > > - } else {
> > > > > -
> > > > > i915_gem_object_make_unshrin
> > > > > kabl
> > > > > e(obj);
> > > > > - }
> > > > > -
> > > > > - obj->mm.ttm_shrinkable = shrinkable;
> > > > > + i915_gem_object_get(obj);
> > > > > + if (shrinkable != obj->mm.ttm_shrinkable) {
> > > > > + if (shrinkable) {
> > > > > + if (obj->mm.madv ==
> > > > > I915_MADV_WILLNEED)
> > > > > + __i915_gem_object_make_shrin
> > > > > kabl
> > > > > e(ob
> > > > > j);
> > > > > + else
> > > > > + __i915_gem_object_make_purge
> > > > > able
> > > > > (obj
> > > > > );
> > > > > + } else {
> > > > > + i915_gem_object_make_unshrinkable(ob
> > > > > j);
> > > > > }
> > > > > - i915_gem_object_put(obj);
> > > > > +
> > > > > + obj->mm.ttm_shrinkable = shrinkable;
> > > > > }
> > > > > + i915_gem_object_put(obj);
> > > > >
> > > > > /*
> > > > > * Put on the correct LRU list depending on the MADV
> > > > > status
> > > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > index 56ad8ef325840..904cb4da6c9b3 100644
> > > > > --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> > > > > @@ -127,7 +127,7 @@ static void
> > > > > ttm_bo_init_reserved_sys_man(struct
> > > > > kunit *test)
> > > > > dma_resv_unlock(bo->base.resv);
> > > > >
> > > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount),
> > > > > 1);
> > > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > > KUNIT_EXPECT_EQ(test, bo->page_alignment,
> > > > > PAGE_SIZE);
> > > > > @@ -176,7 +176,7 @@ static void
> > > > > ttm_bo_init_reserved_mock_man(struct
> > > > > kunit *test)
> > > > > dma_resv_unlock(bo->base.resv);
> > > > >
> > > > > KUNIT_EXPECT_EQ(test, err, 0);
> > > > > - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> > > > > + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount),
> > > > > 1);
> > > > > KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> > > > > KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> > > > > KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
> > > > > @@ -969,6 +969,8 @@ static void
> > > > > ttm_bo_validate_allowed_only_evict(struct kunit *test)
> > > > > ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
> > > > > }
> > > > >
> > > > > +extern const struct drm_gem_object_funcs
> > > > > ttm_deleted_object_funcs;
> > > > > +
> > > > > static void ttm_bo_validate_deleted_evict(struct kunit
> > > > > *test)
> > > > > {
> > > > > struct ttm_operation_ctx ctx_init = { }, ctx_val =
> > > > > { };
> > > > > @@ -999,7 +1001,7 @@ static void
> > > > > ttm_bo_validate_deleted_evict(struct
> > > > > kunit *test)
> > > > > KUNIT_EXPECT_EQ(test,
> > > > > ttm_resource_manager_usage(man),
> > > > > big);
> > > > >
> > > > > dma_resv_unlock(bo_big->base.resv);
> > > > > - bo_big->deleted = true;
> > > > > + bo_big->base.funcs = &ttm_deleted_object_funcs;
> > > > >
> > > > > bo_small = ttm_bo_kunit_init(test, test->priv,
> > > > > small,
> > > > > NULL);
> > > > > bo_small->type = bo_type;
> > > > > diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
> > > > > --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> > > > > @@ -189,8 +189,6 @@ struct ttm_buffer_object
> > > > > *ttm_bo_kunit_init(struct kunit *test,
> > > > > bo->bdev = devs->ttm_dev;
> > > > > bo->destroy = dummy_ttm_bo_destroy;
> > > > >
> > > > > - kref_init(&bo->kref);
> > > > > -
> > > > > return bo;
> > > > > }
> > > > > EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
> > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > index 3980f376e3ba4..2b470c1746f60 100644
> > > > > --- a/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> > > > > @@ -246,88 +246,84 @@ static void
> > > > > ttm_bo_delayed_delete(struct
> > > > > work_struct *work)
> > > > > ttm_bo_put(bo);
> > > > > }
> > > > >
> > > > > -static void ttm_bo_release(struct kref *kref)
> > > > > +/*
> > > > > + * All other callbacks should never ever be called on a
> > > > > deleted
> > > > > TTM
> > > > > object.
> > > > > + */
> > > > > +const struct drm_gem_object_funcs ttm_deleted_object_funcs =
> > > > > {
> > > > > + .free = ttm_bo_free
> > > > > +};
> > > > > +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
> > > > > +
> > > > > +/* Returns true if the BO is about to get deleted */
> > > > > +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
> > > > > +{
> > > > > + return bo->base.funcs == &ttm_deleted_object_funcs;
> > > > > +}
> > > > > +
> > > > > +void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > > {
> > > > > - struct ttm_buffer_object *bo =
> > > > > - container_of(kref, struct ttm_buffer_object,
> > > > > kref);
> > > > > struct ttm_device *bdev = bo->bdev;
> > > > > int ret;
> > > > >
> > > > > WARN_ON_ONCE(bo->pin_count);
> > > > > WARN_ON_ONCE(bo->bulk_move);
> > > > >
> > > > > - if (!bo->deleted) {
> > > > > - ret = ttm_bo_individualize_resv(bo);
> > > > > - if (ret) {
> > > > > - /* Last resort, if we fail to
> > > > > allocate
> > > > > memory for the
> > > > > - * fences block for the BO to become
> > > > > idle
> > > > > - */
> > > > > - dma_resv_wait_timeout(bo->base.resv,
> > > > > -
> > > > > DMA_RESV_USAGE_BOOKKEEP, false,
> > > > > - 30 * HZ);
> > > > > - }
> > > > > + ret = ttm_bo_individualize_resv(bo);
> > > > > + if (ret) {
> > > > > + /* Last resort, if we fail to allocate
> > > > > memory
> > > > > for
> > > > > the
> > > > > + * fences block for the BO to become idle
> > > > > + */
> > > > > + dma_resv_wait_timeout(bo->base.resv,
> > > > > DMA_RESV_USAGE_BOOKKEEP,
> > > > > + false, 30 * HZ);
> > > > > + }
> > > > >
> > > > > - if (bdev->funcs->release_notify)
> > > > > - bdev->funcs->release_notify(bo);
> > > > > + if (bo->bdev->funcs->release_notify)
> > > > > + bo->bdev->funcs->release_notify(bo);
> > > > >
> > > > > - drm_vma_offset_remove(bdev->vma_manager,
> > > > > &bo-
> > > > > > base.vma_node);
> > > > > - ttm_mem_io_free(bdev, bo->resource);
> > > > > + drm_vma_offset_remove(bdev->vma_manager, &bo-
> > > > > > base.vma_node);
> > > > > + ttm_mem_io_free(bdev, bo->resource);
> > > > >
> > > > > - if (!dma_resv_test_signaled(&bo->base._resv,
> > > > > -
> > > > > DMA_RESV_USAGE_BOOKKEEP)
> > > > > > >
> > > > > - (want_init_on_free() && (bo->ttm !=
> > > > > NULL))
> > > > > > >
> > > > > - bo->type == ttm_bo_type_sg ||
> > > > > - !dma_resv_trylock(bo->base.resv)) {
> > > > > - /* The BO is not idle, resurrect it
> > > > > for
> > > > > delayed destroy */
> > > > > - ttm_bo_flush_all_fences(bo);
> > > > > - bo->deleted = true;
> > > > > + if (!dma_resv_test_signaled(&bo->base._resv,
> > > > > DMA_RESV_USAGE_BOOKKEEP) ||
> > > > > + (want_init_on_free() && (bo->ttm != NULL)) ||
> > > > > + bo->type == ttm_bo_type_sg ||
> > > > > + !dma_resv_trylock(bo->base.resv)) {
> > > > > + /* The BO is not idle, resurrect it for
> > > > > delayed
> > > > > destroy */
> > > > > + ttm_bo_flush_all_fences(bo);
> > > > >
> > > > > - spin_lock(&bdev->lru_lock);
> > > > > -
> > > > > - /*
> > > > > - * Make pinned bos immediately
> > > > > available
> > > > > to
> > > > > - * shrinkers, now that they are
> > > > > queued
> > > > > for
> > > > > - * destruction.
> > > > > - *
> > > > > - * FIXME: QXL is triggering this.
> > > > > Can be
> > > > > removed when the
> > > > > - * driver is fixed.
> > > > > - */
> > > > > - if (bo->pin_count) {
> > > > > - bo->pin_count = 0;
> > > > > -
> > > > > ttm_resource_move_to_lru_tai
> > > > > l(bo-
> > > > > > resource);
> > > > > - }
> > > > > + spin_lock(&bo->bdev->lru_lock);
> > > > >
> > > > > - kref_init(&bo->kref);
> > > > > - spin_unlock(&bdev->lru_lock);
> > > > > + /*
> > > > > + * Make pinned bos immediately available to
> > > > > + * shrinkers, now that they are queued for
> > > > > + * destruction.
> > > > > + *
> > > > > + * FIXME: QXL is triggering this. Can be
> > > > > removed
> > > > > when the
> > > > > + * driver is fixed.
> > > > > + */
> > > > > + if (bo->pin_count) {
> > > > > + bo->pin_count = 0;
> > > > > + ttm_resource_move_to_lru_tail(bo-
> > > > > > resource);
> > > > > + }
> > > > >
> > > > > - INIT_WORK(&bo->delayed_delete,
> > > > > ttm_bo_delayed_delete);
> > > > > + kref_init(&bo->base.refcount);
> > > > > + bo->base.funcs = &ttm_deleted_object_funcs;
> > > > > + spin_unlock(&bo->bdev->lru_lock);
> > > > >
> > > > > - /* Schedule the worker on the
> > > > > closest
> > > > > NUMA
> > > > > node. This
> > > > > - * improves performance since system
> > > > > memory
> > > > > might be
> > > > > - * cleared on free and that is best
> > > > > done
> > > > > on
> > > > > a CPU core
> > > > > - * close to it.
> > > > > - */
> > > > > - queue_work_node(bdev->pool.nid,
> > > > > bdev-
> > > > > > wq,
> > > > > &bo->delayed_delete);
> > > > > - return;
> > > > > - }
> > > > > + INIT_WORK(&bo->delayed_delete,
> > > > > ttm_bo_delayed_delete);
> > > > >
> > > > > + /* Schedule the worker on the closest NUMA
> > > > > node.
> > > > > This
> > > > > + * improves performance since system memory
> > > > > might be
> > > > > + * cleared on free and that is best done on
> > > > > a
> > > > > CPU
> > > > > core
> > > > > + * close to it.
> > > > > + */
> > > > > + queue_work_node(bdev->pool.nid, bdev->wq,
> > > > > &bo-
> > > > > > delayed_delete);
> > > > > + } else {
> > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > dma_resv_unlock(bo->base.resv);
> > > > > - }
> > > > >
> > > > > - atomic_dec(&ttm_glob.bo_count);
> > > > > - bo->destroy(bo);
> > > > > -}
> > > > > -
> > > > > -/* TODO: remove! */
> > > > > -void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > > -{
> > > > > - kref_put(&bo->kref, ttm_bo_release);
> > > > > -}
> > > > > -
> > > > > -void ttm_bo_fini(struct ttm_buffer_object *bo)
> > > > > -{
> > > > > - ttm_bo_put(bo);
> > > > > + atomic_dec(&ttm_glob.bo_count);
> > > > > + bo->destroy(bo);
> > > > > + }
> > > > > }
> > > > > EXPORT_SYMBOL(ttm_bo_fini);
> > > > >
> > > > > @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device
> > > > > *bdev,
> > > > > struct ttm_resource_manager *man
> > > > > if (!bo->resource || bo->resource->mem_type !=
> > > > > mem_type)
> > > > > goto out_bo_moved;
> > > > >
> > > > > - if (bo->deleted) {
> > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > > if (!ret)
> > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct
> > > > > ttm_lru_walk
> > > > > *walk, struct ttm_buffer_object *
> > > > > if (bo->pin_count || !bo->bdev->funcs-
> > > > > > eviction_valuable(bo,
> > > > > evict_walk->place))
> > > > > return 0;
> > > > >
> > > > > - if (bo->deleted) {
> > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
> > > > > if (!lret)
> > > > > ttm_bo_cleanup_memtype_use(bo);
> > > > > @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct
> > > > > ttm_device
> > > > > *bdev,
> > > > > void ttm_bo_pin(struct ttm_buffer_object *bo)
> > > > > {
> > > > > dma_resv_assert_held(bo->base.resv);
> > > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > > spin_lock(&bo->bdev->lru_lock);
> > > > > if (bo->resource)
> > > > > ttm_resource_del_bulk_move(bo->resource,
> > > > > bo);
> > > > > @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
> > > > > void ttm_bo_unpin(struct ttm_buffer_object *bo)
> > > > > {
> > > > > dma_resv_assert_held(bo->base.resv);
> > > > > - WARN_ON_ONCE(!kref_read(&bo->kref));
> > > > > if (WARN_ON_ONCE(!bo->pin_count))
> > > > > return;
> > > > >
> > > > > @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct
> > > > > ttm_device
> > > > > *bdev,
> > > > > struct ttm_buffer_object *bo,
> > > > > {
> > > > > int ret;
> > > > >
> > > > > - kref_init(&bo->kref);
> > > > > bo->bdev = bdev;
> > > > > bo->type = type;
> > > > > bo->page_alignment = alignment;
> > > > > @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk
> > > > > *walk,
> > > > > struct ttm_buffer_object *bo)
> > > > > goto out;
> > > > > }
> > > > >
> > > > > - if (bo->deleted) {
> > > > > - pgoff_t num_pages = tt->num_pages;
> > > > > + if (ttm_bo_is_zombie(bo)) {
> > > > > + pgoff_t num_pages = bo->ttm->num_pages;
> > > > >
> > > > > ret = ttm_bo_wait_ctx(bo, ctx);
> > > > > if (ret)
> > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > index e0d48eac74b03..ded2a47be0bcb 100644
> > > > > --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> > > > > @@ -27,6 +27,14 @@
> > > > >
> > > > > #include <drm/ttm/ttm_bo.h>
> > > > >
> > > > > +static inline void ttm_bo_free(struct drm_gem_object *gobj)
> > > > > +{
> > > > > + struct ttm_buffer_object *bo = container_of(gobj,
> > > > > typeof(*bo), base);
> > > > > +
> > > > > + atomic_dec(&ttm_glob.bo_count);
> > > > > + bo->destroy(bo);
> > > > > +}
> > > > > +
> > > > > /**
> > > > > * ttm_bo_get - reference a struct ttm_buffer_object
> > > > > *
> > > > > @@ -34,7 +42,7 @@
> > > > > */
> > > > > static inline void ttm_bo_get(struct ttm_buffer_object *bo)
> > > > > {
> > > > > - kref_get(&bo->kref);
> > > > > + drm_gem_object_get(&bo->base);
> > > > > }
> > > > >
> > > > > /**
> > > > > @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
> > > > > ttm_buffer_object *bo)
> > > > > static inline __must_check struct ttm_buffer_object *
> > > > > ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
> > > > > {
> > > > > - if (!kref_get_unless_zero(&bo->kref))
> > > > > + if (!kref_get_unless_zero(&bo->base.refcount))
> > > > > return NULL;
> > > > > return bo;
> > > > > }
> > > > >
> > > > > -void ttm_bo_put(struct ttm_buffer_object *bo);
> > > > > +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
> > > > > +{
> > > > > + drm_gem_object_put(&bo->base);
> > > > > +}
> > > > >
> > > > > #endif
> > > > > diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > index 3e3c201a02226..7ed085adf1c9b 100644
> > > > > --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> > > > > @@ -41,6 +41,18 @@
> > > > >
> > > > > #include "ttm_bo_internal.h"
> > > > >
> > > > > +static void ttm_transfer_object_free(struct drm_gem_object
> > > > > *obj)
> > > > > +{
> > > > > + struct ttm_buffer_object *bo =
> > > > > + container_of(obj, struct ttm_buffer_object,
> > > > > base);
> > > > > +
> > > > > + ttm_bo_fini(bo);
> > > > > +}
> > > > > +
> > > > > +const struct drm_gem_object_funcs ttm_transfer_object_funcs
> > > > > = {
> > > > > + .free = ttm_transfer_object_free,
> > > > > +};
> > > > > +
> > > > > struct ttm_transfer_obj {
> > > > > struct ttm_buffer_object base;
> > > > > struct ttm_buffer_object *bo;
> > > > > @@ -247,7 +259,8 @@ static int
> > > > > ttm_buffer_object_transfer(struct
> > > > > ttm_buffer_object *bo,
> > > > > atomic_inc(&ttm_glob.bo_count);
> > > > > drm_vma_node_reset(&fbo->base.base.vma_node);
> > > > >
> > > > > - kref_init(&fbo->base.kref);
> > > > > + kref_init(&fbo->base.base.refcount);
> > > > > + fbo->base.base.funcs = &ttm_transfer_object_funcs;
> > > > > fbo->base.destroy = &ttm_transfered_destroy;
> > > > > fbo->base.pin_count = 0;
> > > > > if (bo->type != ttm_bo_type_sg)
> > > > > diff --git a/drivers/gpu/drm/xe/xe_bo.c
> > > > > b/drivers/gpu/drm/xe/xe_bo.c
> > > > > index 85e6d9a0f575b..5843f850339c7 100644
> > > > > --- a/drivers/gpu/drm/xe/xe_bo.c
> > > > > +++ b/drivers/gpu/drm/xe/xe_bo.c
> > > > > @@ -1651,7 +1651,7 @@ static bool
> > > > > xe_ttm_bo_lock_in_destructor(struct
> > > > > ttm_buffer_object *ttm_bo)
> > > > > struct xe_device *xe = ttm_to_xe_device(ttm_bo-
> > > > > >bdev);
> > > > > bool locked;
> > > > >
> > > > > - xe_assert(xe, !kref_read(&ttm_bo->kref));
> > > > > + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
> > > > >
> > > > > /*
> > > > > * We can typically only race with TTM trylocking
> > > > > under
> > > > > the
> > > > > diff --git a/include/drm/ttm/ttm_bo.h
> > > > > b/include/drm/ttm/ttm_bo.h
> > > > > index 8310bc3d55f90..1eae9eea5ff32 100644
> > > > > --- a/include/drm/ttm/ttm_bo.h
> > > > > +++ b/include/drm/ttm/ttm_bo.h
> > > > > @@ -78,11 +78,8 @@ enum ttm_bo_type {
> > > > > * @type: The bo type.
> > > > > * @page_alignment: Page alignment.
> > > > > * @destroy: Destruction function. If NULL, kfree is used.
> > > > > - * @kref: Reference count of this buffer object. When this
> > > > > refcount
> > > > > reaches
> > > > > - * zero, the object is destroyed or put on the delayed
> > > > > delete
> > > > > list.
> > > > > * @resource: structure describing current placement.
> > > > > * @ttm: TTM structure holding system pages.
> > > > > - * @deleted: True if the object is only a zombie and already
> > > > > deleted.
> > > > > * @bulk_move: The bulk move object.
> > > > > * @priority: Priority for LRU, BOs with lower priority are
> > > > > evicted
> > > > > first.
> > > > > * @pin_count: Pin count.
> > > > > @@ -109,17 +106,11 @@ struct ttm_buffer_object {
> > > > > uint32_t page_alignment;
> > > > > void (*destroy) (struct ttm_buffer_object *);
> > > > >
> > > > > - /*
> > > > > - * Members not needing protection.
> > > > > - */
> > > > > - struct kref kref;
> > > > > -
> > > > > /*
> > > > > * Members protected by the bo::resv::reserved lock.
> > > > > */
> > > > > struct ttm_resource *resource;
> > > > > struct ttm_tt *ttm;
> > > > > - bool deleted;
> > > > > struct ttm_lru_bulk_move *bulk_move;
> > > > > unsigned priority;
> > > > > unsigned pin_count;
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-06 17:01 ` Thomas Hellström
2026-07-06 17:51 ` Matthew Brost
@ 2026-07-06 18:23 ` Christian König
2026-07-06 22:26 ` Dave Airlie
1 sibling, 1 reply; 26+ messages in thread
From: Christian König @ 2026-07-06 18:23 UTC (permalink / raw)
To: Thomas Hellström, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Matthew Brost, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
On 7/6/26 19:01, Thomas Hellström wrote:
> On Mon, 2026-07-06 at 16:49 +0200, Christian König wrote:
>> On 7/6/26 15:14, Thomas Hellström wrote:
>>> On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
>>>> From: Christian König <christian.koenig@amd.com>
>>>>
>>>> Instead of keeping a separate reference count for the TTM object
>>>> also
>>>> use
>>>> the reference count for DRM GEM objects inside TTM.
>>>>
>>>> Apart from avoiding two reference counts for one object this
>>>> approach
>>>> has
>>>> the clear advantage of being able to use drm_exec inside TTM.
>>>>
>>>> v2: adjust XE assert as well and re-enable disabled test
>>>> v3: handle another case in i915
>>>> v4: set GEM driver funcs of transfer BOs to point to the TTM free
>>>> callback (Natalie)
>>>
>>> I think the main review issue from the last time this was on the
>>> table
>>> was that we shouldn't resurrect the gem refcount. Apart from the
>>> risc
>>> of getting barriers wrong, both xe and IIRC i915 partly rely on the
>>> gem
>>> refcount never being resurrected and that callbacks for bos with
>>> zero
>>> gem refcount means that the gem part of the object is unusable.
>>
>> I've spend quite some time thinking about that and came to the
>> conclusion that this is actually harmless.
>>
>> The drivers shouldn't be able to see the resurected BO, except if
>> they go over the LRU list manually (which they shouldn't).
>
> The shrinker uses the TTM helpers for this. Basically the check needs
> to be ported to use the zombie interface but the present change also
> widens the window where we can't evict / shrink at all due to zero
> refcounts.
>
> While it might be made harmless, resurrecting a refcount like this is
> IMO not something that should leak into the gem refcount. Nobody else
> does this in the kernel tree. The bo in reality becomes a zombie once
> the gem refcount reaches zero.
I don't really like the solution either, but I don't see much other option.
>>> For example xe_bo.c:
>>>
>>> if (!xe_bo_is_xe_bo(bo) || !xe_bo_get_unless_zero(xe_bo))
>>> return xe_bo_shrink_purge(ctx, bo, scanned);
>>>
>>> So IIRC the conclusion was when removing the ttm refcount we
>>> shouldn't
>>> attempt to resurrect the gem one. If the get_unless_zero() fails
>>> during
>>> evict walk, we simply find something to wait for. See previous
>>> discussion there.
>>
>> Yeah, I considered that as well but the problem is we often doesn't
>> have anything to wait on.
>
> That's not the conclusion of the previous discussion?
>
> https://lore.kernel.org/dri-devel/20250716160555.20217-2-christian.koenig@amd.com/#r
Well, I clearly rejected the idea to wait for the delayed delete worker because that can cause deadlocks no matter how we do it.
The only possibility I see is to grab a reference on the next busy fence and wait for that to signal.
But that approach still leaves a window open where the cleanup worker has not yet cleaned up the resource before we try to allocate a new one.
The only two possibilities I see to close that window are:
1. Resurrect the GEM object.
2. Keep a reference to the GEM object while it is on the LRU.
I would really like to do #2 instead, but that requires that we distinct between GEM object referenze and backing store reference.
The GEM object does have some kind of backing store reference with the handle_count field, but that unfortunately doesn't have the right semantic (e.g. only goes from 0->1 when you actually create a handle etc..).
>>> I fully support removing the ttm refcount, but not if it means
>>> resurrecting the gem refcount.
>>>
>>> If we want to sidestep that problem, in favour of getting the
>>> proposed
>>> locking functionality in and future proof it, I suggest using
>>>
>>> https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
>>>
>>> And rebase this series on that. This means we can use the ttm
>>> refcount
>>> for the transaction refcounting, and also that if we add a dma-buf
>>> map
>>> interface with a dma_resv_txn_obj, we could use that to also have
>>> exhaustive eviction that originates from a dma_buf map.
>>
>> I don't think that this is a good idea. It just adds another layer of
>> abstraction and doesn't solve the problem in any way possible.
>
> This comment confuses me. Exactly what problem isn't solved by this,
> and which of the stated benefits/use-cases in the cover-letter do you
> think aren't worthwhile?
Of course could the drm_exec object work with different reference counters, but as far as I can see that just complicates the situation and again creates a TTM specific solutions which I clearly want to avoid.
I would rather go with the resurrection approach instead, that has less potential for problem I think.
Alaternatively we could re-work the whole GEM refcount/GEM handle count/TTM refcount mess, but that has even more potential to break.
Regards,
Christian.
>
> Also for reference: (Section at the end and follow-up messages)
> https://lore.kernel.org/all/3716d43462188590743060755b37e3d060f7600f.camel@linux.intel.com/
>
> Thanks,
> Thomas
>
>
>> Regards,
>> Christian.
>>
>>>
>>> /Thomas
>>>
>>>>
>>>> Signed-off-by: tChristian König <christian.koenig@amd.com>
>>>> Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
>>>> ---
>>>> drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++---
>>>> drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
>>>> drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
>>>> drivers/gpu/drm/ttm/ttm_bo.c | 135
>>>> +++++++++++--
>>>> ----------
>>>> drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
>>>> drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
>>>> drivers/gpu/drm/xe/xe_bo.c | 2 +-
>>>> include/drm/ttm/ttm_bo.h | 9 --
>>>> 8 files changed, 111 insertions(+), 112 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>>>> b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>>>> index df3fcc2b1248e..642296602de69 100644
>>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>>>> @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
>>>> drm_i915_gem_object *obj)
>>>> * Don't manipulate the TTM LRUs while in TTM bo
>>>> destruction.
>>>> * We're called through i915_ttm_delete_mem_notify().
>>>> */
>>>> - if (!kref_read(&bo->kref))
>>>> + if (!kref_read(&bo->base.refcount))
>>>> return;
>>>>
>>>> /*
>>>> @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
>>>> drm_i915_gem_object *obj)
>>>> *
>>>> * TODO: consider maybe also bumping the shrinker list
>>>> here
>>>> when we have
>>>> * already unpinned it, which should give us something
>>>> more
>>>> like an LRU.
>>>> - *
>>>> - * TODO: There is a small window of opportunity for this
>>>> function to
>>>> - * get called from eviction after we've dropped the last
>>>> GEM
>>>> refcount,
>>>> - * but before the TTM deleted flag is set on the object.
>>>> Avoid
>>>> - * adjusting the shrinker list in such cases, since the
>>>> object is
>>>> - * not available to the shrinker anyway due to its zero
>>>> refcount.
>>>> - * To fix this properly we should move to a TTM shrinker
>>>> LRU
>>>> list for
>>>> - * these objects.
>>>> */
>>>> - if (kref_get_unless_zero(&obj->base.refcount)) {
>>>> - if (shrinkable != obj->mm.ttm_shrinkable) {
>>>> - if (shrinkable) {
>>>> - if (obj->mm.madv ==
>>>> I915_MADV_WILLNEED)
>>>> -
>>>> __i915_gem_object_make_s
>>>> hrinkable(obj);
>>>> - else
>>>> -
>>>> __i915_gem_object_make_p
>>>> urgeable(obj);
>>>> - } else {
>>>> -
>>>> i915_gem_object_make_unshrinkabl
>>>> e(obj);
>>>> - }
>>>> -
>>>> - obj->mm.ttm_shrinkable = shrinkable;
>>>> + i915_gem_object_get(obj);
>>>> + if (shrinkable != obj->mm.ttm_shrinkable) {
>>>> + if (shrinkable) {
>>>> + if (obj->mm.madv == I915_MADV_WILLNEED)
>>>> + __i915_gem_object_make_shrinkabl
>>>> e(ob
>>>> j);
>>>> + else
>>>> + __i915_gem_object_make_purgeable
>>>> (obj
>>>> );
>>>> + } else {
>>>> + i915_gem_object_make_unshrinkable(obj);
>>>> }
>>>> - i915_gem_object_put(obj);
>>>> +
>>>> + obj->mm.ttm_shrinkable = shrinkable;
>>>> }
>>>> + i915_gem_object_put(obj);
>>>>
>>>> /*
>>>> * Put on the correct LRU list depending on the MADV
>>>> status
>>>> diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
>>>> b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
>>>> index 56ad8ef325840..904cb4da6c9b3 100644
>>>> --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
>>>> +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
>>>> @@ -127,7 +127,7 @@ static void
>>>> ttm_bo_init_reserved_sys_man(struct
>>>> kunit *test)
>>>> dma_resv_unlock(bo->base.resv);
>>>>
>>>> KUNIT_EXPECT_EQ(test, err, 0);
>>>> - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
>>>> + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
>>>> KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
>>>> KUNIT_EXPECT_EQ(test, bo->type, bo_type);
>>>> KUNIT_EXPECT_EQ(test, bo->page_alignment, PAGE_SIZE);
>>>> @@ -176,7 +176,7 @@ static void
>>>> ttm_bo_init_reserved_mock_man(struct
>>>> kunit *test)
>>>> dma_resv_unlock(bo->base.resv);
>>>>
>>>> KUNIT_EXPECT_EQ(test, err, 0);
>>>> - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
>>>> + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
>>>> KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
>>>> KUNIT_EXPECT_EQ(test, bo->type, bo_type);
>>>> KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
>>>> @@ -969,6 +969,8 @@ static void
>>>> ttm_bo_validate_allowed_only_evict(struct kunit *test)
>>>> ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
>>>> }
>>>>
>>>> +extern const struct drm_gem_object_funcs
>>>> ttm_deleted_object_funcs;
>>>> +
>>>> static void ttm_bo_validate_deleted_evict(struct kunit *test)
>>>> {
>>>> struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
>>>> @@ -999,7 +1001,7 @@ static void
>>>> ttm_bo_validate_deleted_evict(struct
>>>> kunit *test)
>>>> KUNIT_EXPECT_EQ(test, ttm_resource_manager_usage(man),
>>>> big);
>>>>
>>>> dma_resv_unlock(bo_big->base.resv);
>>>> - bo_big->deleted = true;
>>>> + bo_big->base.funcs = &ttm_deleted_object_funcs;
>>>>
>>>> bo_small = ttm_bo_kunit_init(test, test->priv, small,
>>>> NULL);
>>>> bo_small->type = bo_type;
>>>> diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
>>>> b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
>>>> index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
>>>> --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
>>>> +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
>>>> @@ -189,8 +189,6 @@ struct ttm_buffer_object
>>>> *ttm_bo_kunit_init(struct kunit *test,
>>>> bo->bdev = devs->ttm_dev;
>>>> bo->destroy = dummy_ttm_bo_destroy;
>>>>
>>>> - kref_init(&bo->kref);
>>>> -
>>>> return bo;
>>>> }
>>>> EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
>>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
>>>> b/drivers/gpu/drm/ttm/ttm_bo.c
>>>> index 3980f376e3ba4..2b470c1746f60 100644
>>>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
>>>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
>>>> @@ -246,88 +246,84 @@ static void ttm_bo_delayed_delete(struct
>>>> work_struct *work)
>>>> ttm_bo_put(bo);
>>>> }
>>>>
>>>> -static void ttm_bo_release(struct kref *kref)
>>>> +/*
>>>> + * All other callbacks should never ever be called on a deleted
>>>> TTM
>>>> object.
>>>> + */
>>>> +const struct drm_gem_object_funcs ttm_deleted_object_funcs = {
>>>> + .free = ttm_bo_free
>>>> +};
>>>> +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
>>>> +
>>>> +/* Returns true if the BO is about to get deleted */
>>>> +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
>>>> +{
>>>> + return bo->base.funcs == &ttm_deleted_object_funcs;
>>>> +}
>>>> +
>>>> +void ttm_bo_fini(struct ttm_buffer_object *bo)
>>>> {
>>>> - struct ttm_buffer_object *bo =
>>>> - container_of(kref, struct ttm_buffer_object, kref);
>>>> struct ttm_device *bdev = bo->bdev;
>>>> int ret;
>>>>
>>>> WARN_ON_ONCE(bo->pin_count);
>>>> WARN_ON_ONCE(bo->bulk_move);
>>>>
>>>> - if (!bo->deleted) {
>>>> - ret = ttm_bo_individualize_resv(bo);
>>>> - if (ret) {
>>>> - /* Last resort, if we fail to allocate
>>>> memory for the
>>>> - * fences block for the BO to become
>>>> idle
>>>> - */
>>>> - dma_resv_wait_timeout(bo->base.resv,
>>>> -
>>>> DMA_RESV_USAGE_BOOKKEEP, false,
>>>> - 30 * HZ);
>>>> - }
>>>> + ret = ttm_bo_individualize_resv(bo);
>>>> + if (ret) {
>>>> + /* Last resort, if we fail to allocate memory
>>>> for
>>>> the
>>>> + * fences block for the BO to become idle
>>>> + */
>>>> + dma_resv_wait_timeout(bo->base.resv,
>>>> DMA_RESV_USAGE_BOOKKEEP,
>>>> + false, 30 * HZ);
>>>> + }
>>>>
>>>> - if (bdev->funcs->release_notify)
>>>> - bdev->funcs->release_notify(bo);
>>>> + if (bo->bdev->funcs->release_notify)
>>>> + bo->bdev->funcs->release_notify(bo);
>>>>
>>>> - drm_vma_offset_remove(bdev->vma_manager, &bo-
>>>>> base.vma_node);
>>>> - ttm_mem_io_free(bdev, bo->resource);
>>>> + drm_vma_offset_remove(bdev->vma_manager, &bo-
>>>>> base.vma_node);
>>>> + ttm_mem_io_free(bdev, bo->resource);
>>>>
>>>> - if (!dma_resv_test_signaled(&bo->base._resv,
>>>> -
>>>> DMA_RESV_USAGE_BOOKKEEP)
>>>>>>
>>>> - (want_init_on_free() && (bo->ttm != NULL))
>>>> ||
>>>> - bo->type == ttm_bo_type_sg ||
>>>> - !dma_resv_trylock(bo->base.resv)) {
>>>> - /* The BO is not idle, resurrect it for
>>>> delayed destroy */
>>>> - ttm_bo_flush_all_fences(bo);
>>>> - bo->deleted = true;
>>>> + if (!dma_resv_test_signaled(&bo->base._resv,
>>>> DMA_RESV_USAGE_BOOKKEEP) ||
>>>> + (want_init_on_free() && (bo->ttm != NULL)) ||
>>>> + bo->type == ttm_bo_type_sg ||
>>>> + !dma_resv_trylock(bo->base.resv)) {
>>>> + /* The BO is not idle, resurrect it for delayed
>>>> destroy */
>>>> + ttm_bo_flush_all_fences(bo);
>>>>
>>>> - spin_lock(&bdev->lru_lock);
>>>> -
>>>> - /*
>>>> - * Make pinned bos immediately available
>>>> to
>>>> - * shrinkers, now that they are queued
>>>> for
>>>> - * destruction.
>>>> - *
>>>> - * FIXME: QXL is triggering this. Can be
>>>> removed when the
>>>> - * driver is fixed.
>>>> - */
>>>> - if (bo->pin_count) {
>>>> - bo->pin_count = 0;
>>>> -
>>>> ttm_resource_move_to_lru_tail(bo-
>>>>> resource);
>>>> - }
>>>> + spin_lock(&bo->bdev->lru_lock);
>>>>
>>>> - kref_init(&bo->kref);
>>>> - spin_unlock(&bdev->lru_lock);
>>>> + /*
>>>> + * Make pinned bos immediately available to
>>>> + * shrinkers, now that they are queued for
>>>> + * destruction.
>>>> + *
>>>> + * FIXME: QXL is triggering this. Can be removed
>>>> when the
>>>> + * driver is fixed.
>>>> + */
>>>> + if (bo->pin_count) {
>>>> + bo->pin_count = 0;
>>>> + ttm_resource_move_to_lru_tail(bo-
>>>>> resource);
>>>> + }
>>>>
>>>> - INIT_WORK(&bo->delayed_delete,
>>>> ttm_bo_delayed_delete);
>>>> + kref_init(&bo->base.refcount);
>>>> + bo->base.funcs = &ttm_deleted_object_funcs;
>>>> + spin_unlock(&bo->bdev->lru_lock);
>>>>
>>>> - /* Schedule the worker on the closest
>>>> NUMA
>>>> node. This
>>>> - * improves performance since system
>>>> memory
>>>> might be
>>>> - * cleared on free and that is best done
>>>> on
>>>> a CPU core
>>>> - * close to it.
>>>> - */
>>>> - queue_work_node(bdev->pool.nid, bdev-
>>>>> wq,
>>>> &bo->delayed_delete);
>>>> - return;
>>>> - }
>>>> + INIT_WORK(&bo->delayed_delete,
>>>> ttm_bo_delayed_delete);
>>>>
>>>> + /* Schedule the worker on the closest NUMA node.
>>>> This
>>>> + * improves performance since system memory
>>>> might be
>>>> + * cleared on free and that is best done on a
>>>> CPU
>>>> core
>>>> + * close to it.
>>>> + */
>>>> + queue_work_node(bdev->pool.nid, bdev->wq, &bo-
>>>>> delayed_delete);
>>>> + } else {
>>>> ttm_bo_cleanup_memtype_use(bo);
>>>> dma_resv_unlock(bo->base.resv);
>>>> - }
>>>>
>>>> - atomic_dec(&ttm_glob.bo_count);
>>>> - bo->destroy(bo);
>>>> -}
>>>> -
>>>> -/* TODO: remove! */
>>>> -void ttm_bo_put(struct ttm_buffer_object *bo)
>>>> -{
>>>> - kref_put(&bo->kref, ttm_bo_release);
>>>> -}
>>>> -
>>>> -void ttm_bo_fini(struct ttm_buffer_object *bo)
>>>> -{
>>>> - ttm_bo_put(bo);
>>>> + atomic_dec(&ttm_glob.bo_count);
>>>> + bo->destroy(bo);
>>>> + }
>>>> }
>>>> EXPORT_SYMBOL(ttm_bo_fini);
>>>>
>>>> @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device
>>>> *bdev,
>>>> struct ttm_resource_manager *man
>>>> if (!bo->resource || bo->resource->mem_type != mem_type)
>>>> goto out_bo_moved;
>>>>
>>>> - if (bo->deleted) {
>>>> + if (ttm_bo_is_zombie(bo)) {
>>>> ret = ttm_bo_wait_ctx(bo, ctx);
>>>> if (!ret)
>>>> ttm_bo_cleanup_memtype_use(bo);
>>>> @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct
>>>> ttm_lru_walk
>>>> *walk, struct ttm_buffer_object *
>>>> if (bo->pin_count || !bo->bdev->funcs-
>>>>> eviction_valuable(bo,
>>>> evict_walk->place))
>>>> return 0;
>>>>
>>>> - if (bo->deleted) {
>>>> + if (ttm_bo_is_zombie(bo)) {
>>>> lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
>>>> if (!lret)
>>>> ttm_bo_cleanup_memtype_use(bo);
>>>> @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct
>>>> ttm_device
>>>> *bdev,
>>>> void ttm_bo_pin(struct ttm_buffer_object *bo)
>>>> {
>>>> dma_resv_assert_held(bo->base.resv);
>>>> - WARN_ON_ONCE(!kref_read(&bo->kref));
>>>> spin_lock(&bo->bdev->lru_lock);
>>>> if (bo->resource)
>>>> ttm_resource_del_bulk_move(bo->resource, bo);
>>>> @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
>>>> void ttm_bo_unpin(struct ttm_buffer_object *bo)
>>>> {
>>>> dma_resv_assert_held(bo->base.resv);
>>>> - WARN_ON_ONCE(!kref_read(&bo->kref));
>>>> if (WARN_ON_ONCE(!bo->pin_count))
>>>> return;
>>>>
>>>> @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct ttm_device
>>>> *bdev,
>>>> struct ttm_buffer_object *bo,
>>>> {
>>>> int ret;
>>>>
>>>> - kref_init(&bo->kref);
>>>> bo->bdev = bdev;
>>>> bo->type = type;
>>>> bo->page_alignment = alignment;
>>>> @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk
>>>> *walk,
>>>> struct ttm_buffer_object *bo)
>>>> goto out;
>>>> }
>>>>
>>>> - if (bo->deleted) {
>>>> - pgoff_t num_pages = tt->num_pages;
>>>> + if (ttm_bo_is_zombie(bo)) {
>>>> + pgoff_t num_pages = bo->ttm->num_pages;
>>>>
>>>> ret = ttm_bo_wait_ctx(bo, ctx);
>>>> if (ret)
>>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
>>>> b/drivers/gpu/drm/ttm/ttm_bo_internal.h
>>>> index e0d48eac74b03..ded2a47be0bcb 100644
>>>> --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
>>>> +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
>>>> @@ -27,6 +27,14 @@
>>>>
>>>> #include <drm/ttm/ttm_bo.h>
>>>>
>>>> +static inline void ttm_bo_free(struct drm_gem_object *gobj)
>>>> +{
>>>> + struct ttm_buffer_object *bo = container_of(gobj,
>>>> typeof(*bo), base);
>>>> +
>>>> + atomic_dec(&ttm_glob.bo_count);
>>>> + bo->destroy(bo);
>>>> +}
>>>> +
>>>> /**
>>>> * ttm_bo_get - reference a struct ttm_buffer_object
>>>> *
>>>> @@ -34,7 +42,7 @@
>>>> */
>>>> static inline void ttm_bo_get(struct ttm_buffer_object *bo)
>>>> {
>>>> - kref_get(&bo->kref);
>>>> + drm_gem_object_get(&bo->base);
>>>> }
>>>>
>>>> /**
>>>> @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
>>>> ttm_buffer_object *bo)
>>>> static inline __must_check struct ttm_buffer_object *
>>>> ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
>>>> {
>>>> - if (!kref_get_unless_zero(&bo->kref))
>>>> + if (!kref_get_unless_zero(&bo->base.refcount))
>>>> return NULL;
>>>> return bo;
>>>> }
>>>>
>>>> -void ttm_bo_put(struct ttm_buffer_object *bo);
>>>> +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
>>>> +{
>>>> + drm_gem_object_put(&bo->base);
>>>> +}
>>>>
>>>> #endif
>>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
>>>> b/drivers/gpu/drm/ttm/ttm_bo_util.c
>>>> index 3e3c201a02226..7ed085adf1c9b 100644
>>>> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
>>>> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
>>>> @@ -41,6 +41,18 @@
>>>>
>>>> #include "ttm_bo_internal.h"
>>>>
>>>> +static void ttm_transfer_object_free(struct drm_gem_object *obj)
>>>> +{
>>>> + struct ttm_buffer_object *bo =
>>>> + container_of(obj, struct ttm_buffer_object,
>>>> base);
>>>> +
>>>> + ttm_bo_fini(bo);
>>>> +}
>>>> +
>>>> +const struct drm_gem_object_funcs ttm_transfer_object_funcs = {
>>>> + .free = ttm_transfer_object_free,
>>>> +};
>>>> +
>>>> struct ttm_transfer_obj {
>>>> struct ttm_buffer_object base;
>>>> struct ttm_buffer_object *bo;
>>>> @@ -247,7 +259,8 @@ static int ttm_buffer_object_transfer(struct
>>>> ttm_buffer_object *bo,
>>>> atomic_inc(&ttm_glob.bo_count);
>>>> drm_vma_node_reset(&fbo->base.base.vma_node);
>>>>
>>>> - kref_init(&fbo->base.kref);
>>>> + kref_init(&fbo->base.base.refcount);
>>>> + fbo->base.base.funcs = &ttm_transfer_object_funcs;
>>>> fbo->base.destroy = &ttm_transfered_destroy;
>>>> fbo->base.pin_count = 0;
>>>> if (bo->type != ttm_bo_type_sg)
>>>> diff --git a/drivers/gpu/drm/xe/xe_bo.c
>>>> b/drivers/gpu/drm/xe/xe_bo.c
>>>> index 85e6d9a0f575b..5843f850339c7 100644
>>>> --- a/drivers/gpu/drm/xe/xe_bo.c
>>>> +++ b/drivers/gpu/drm/xe/xe_bo.c
>>>> @@ -1651,7 +1651,7 @@ static bool
>>>> xe_ttm_bo_lock_in_destructor(struct
>>>> ttm_buffer_object *ttm_bo)
>>>> struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
>>>> bool locked;
>>>>
>>>> - xe_assert(xe, !kref_read(&ttm_bo->kref));
>>>> + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
>>>>
>>>> /*
>>>> * We can typically only race with TTM trylocking under
>>>> the
>>>> diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
>>>> index 8310bc3d55f90..1eae9eea5ff32 100644
>>>> --- a/include/drm/ttm/ttm_bo.h
>>>> +++ b/include/drm/ttm/ttm_bo.h
>>>> @@ -78,11 +78,8 @@ enum ttm_bo_type {
>>>> * @type: The bo type.
>>>> * @page_alignment: Page alignment.
>>>> * @destroy: Destruction function. If NULL, kfree is used.
>>>> - * @kref: Reference count of this buffer object. When this
>>>> refcount
>>>> reaches
>>>> - * zero, the object is destroyed or put on the delayed delete
>>>> list.
>>>> * @resource: structure describing current placement.
>>>> * @ttm: TTM structure holding system pages.
>>>> - * @deleted: True if the object is only a zombie and already
>>>> deleted.
>>>> * @bulk_move: The bulk move object.
>>>> * @priority: Priority for LRU, BOs with lower priority are
>>>> evicted
>>>> first.
>>>> * @pin_count: Pin count.
>>>> @@ -109,17 +106,11 @@ struct ttm_buffer_object {
>>>> uint32_t page_alignment;
>>>> void (*destroy) (struct ttm_buffer_object *);
>>>>
>>>> - /*
>>>> - * Members not needing protection.
>>>> - */
>>>> - struct kref kref;
>>>> -
>>>> /*
>>>> * Members protected by the bo::resv::reserved lock.
>>>> */
>>>> struct ttm_resource *resource;
>>>> struct ttm_tt *ttm;
>>>> - bool deleted;
>>>> struct ttm_lru_bulk_move *bulk_move;
>>>> unsigned priority;
>>>> unsigned pin_count;
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4
2026-07-06 18:23 ` Christian König
@ 2026-07-06 22:26 ` Dave Airlie
0 siblings, 0 replies; 26+ messages in thread
From: Dave Airlie @ 2026-07-06 22:26 UTC (permalink / raw)
To: Christian König
Cc: Thomas Hellström, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Simona Vetter, Jani Nikula,
Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin, Huang Rui,
Matthew Auld, Matthew Brost, Alex Deucher, dri-devel,
linux-kernel, intel-gfx, intel-xe, amd-gfx, Danilo Krummrich,
Eliot Courtney
Just cc'ing Danilo and Elliott, since I know we are starting to look
at rust and ttm interactions, and these sort of lifetime/refcount fun
might be something we need to solve in a rust compatible way.
On Tue, 7 Jul 2026 at 04:23, Christian König <christian.koenig@amd.com> wrote:
>
> On 7/6/26 19:01, Thomas Hellström wrote:
> > On Mon, 2026-07-06 at 16:49 +0200, Christian König wrote:
> >> On 7/6/26 15:14, Thomas Hellström wrote:
> >>> On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> >>>> From: Christian König <christian.koenig@amd.com>
> >>>>
> >>>> Instead of keeping a separate reference count for the TTM object
> >>>> also
> >>>> use
> >>>> the reference count for DRM GEM objects inside TTM.
> >>>>
> >>>> Apart from avoiding two reference counts for one object this
> >>>> approach
> >>>> has
> >>>> the clear advantage of being able to use drm_exec inside TTM.
> >>>>
> >>>> v2: adjust XE assert as well and re-enable disabled test
> >>>> v3: handle another case in i915
> >>>> v4: set GEM driver funcs of transfer BOs to point to the TTM free
> >>>> callback (Natalie)
> >>>
> >>> I think the main review issue from the last time this was on the
> >>> table
> >>> was that we shouldn't resurrect the gem refcount. Apart from the
> >>> risc
> >>> of getting barriers wrong, both xe and IIRC i915 partly rely on the
> >>> gem
> >>> refcount never being resurrected and that callbacks for bos with
> >>> zero
> >>> gem refcount means that the gem part of the object is unusable.
> >>
> >> I've spend quite some time thinking about that and came to the
> >> conclusion that this is actually harmless.
> >>
> >> The drivers shouldn't be able to see the resurected BO, except if
> >> they go over the LRU list manually (which they shouldn't).
> >
> > The shrinker uses the TTM helpers for this. Basically the check needs
> > to be ported to use the zombie interface but the present change also
> > widens the window where we can't evict / shrink at all due to zero
> > refcounts.
> >
> > While it might be made harmless, resurrecting a refcount like this is
> > IMO not something that should leak into the gem refcount. Nobody else
> > does this in the kernel tree. The bo in reality becomes a zombie once
> > the gem refcount reaches zero.
>
> I don't really like the solution either, but I don't see much other option.
>
> >>> For example xe_bo.c:
> >>>
> >>> if (!xe_bo_is_xe_bo(bo) || !xe_bo_get_unless_zero(xe_bo))
> >>> return xe_bo_shrink_purge(ctx, bo, scanned);
> >>>
> >>> So IIRC the conclusion was when removing the ttm refcount we
> >>> shouldn't
> >>> attempt to resurrect the gem one. If the get_unless_zero() fails
> >>> during
> >>> evict walk, we simply find something to wait for. See previous
> >>> discussion there.
> >>
> >> Yeah, I considered that as well but the problem is we often doesn't
> >> have anything to wait on.
> >
> > That's not the conclusion of the previous discussion?
> >
> > https://lore.kernel.org/dri-devel/20250716160555.20217-2-christian.koenig@amd.com/#r
>
> Well, I clearly rejected the idea to wait for the delayed delete worker because that can cause deadlocks no matter how we do it.
>
> The only possibility I see is to grab a reference on the next busy fence and wait for that to signal.
>
> But that approach still leaves a window open where the cleanup worker has not yet cleaned up the resource before we try to allocate a new one.
>
> The only two possibilities I see to close that window are:
> 1. Resurrect the GEM object.
> 2. Keep a reference to the GEM object while it is on the LRU.
>
> I would really like to do #2 instead, but that requires that we distinct between GEM object referenze and backing store reference.
>
> The GEM object does have some kind of backing store reference with the handle_count field, but that unfortunately doesn't have the right semantic (e.g. only goes from 0->1 when you actually create a handle etc..).
>
> >>> I fully support removing the ttm refcount, but not if it means
> >>> resurrecting the gem refcount.
> >>>
> >>> If we want to sidestep that problem, in favour of getting the
> >>> proposed
> >>> locking functionality in and future proof it, I suggest using
> >>>
> >>> https://lore.kernel.org/all/20260605112700.181040-1-thomas.hellstrom@linux.intel.com/
> >>>
> >>> And rebase this series on that. This means we can use the ttm
> >>> refcount
> >>> for the transaction refcounting, and also that if we add a dma-buf
> >>> map
> >>> interface with a dma_resv_txn_obj, we could use that to also have
> >>> exhaustive eviction that originates from a dma_buf map.
> >>
> >> I don't think that this is a good idea. It just adds another layer of
> >> abstraction and doesn't solve the problem in any way possible.
> >
> > This comment confuses me. Exactly what problem isn't solved by this,
> > and which of the stated benefits/use-cases in the cover-letter do you
> > think aren't worthwhile?
>
> Of course could the drm_exec object work with different reference counters, but as far as I can see that just complicates the situation and again creates a TTM specific solutions which I clearly want to avoid.
>
> I would rather go with the resurrection approach instead, that has less potential for problem I think.
>
> Alaternatively we could re-work the whole GEM refcount/GEM handle count/TTM refcount mess, but that has even more potential to break.
>
> Regards,
> Christian.
>
>
> >
> > Also for reference: (Section at the end and follow-up messages)
> > https://lore.kernel.org/all/3716d43462188590743060755b37e3d060f7600f.camel@linux.intel.com/
> >
> > Thanks,
> > Thomas
> >
> >
> >> Regards,
> >> Christian.
> >>
> >>>
> >>> /Thomas
> >>>
> >>>>
> >>>> Signed-off-by: tChristian König <christian.koenig@amd.com>
> >>>> Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> >>>> ---
> >>>> drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 35 +++---
> >>>> drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 8 +-
> >>>> drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c | 2 -
> >>>> drivers/gpu/drm/ttm/ttm_bo.c | 135
> >>>> +++++++++++--
> >>>> ----------
> >>>> drivers/gpu/drm/ttm/ttm_bo_internal.h | 17 ++-
> >>>> drivers/gpu/drm/ttm/ttm_bo_util.c | 15 ++-
> >>>> drivers/gpu/drm/xe/xe_bo.c | 2 +-
> >>>> include/drm/ttm/ttm_bo.h | 9 --
> >>>> 8 files changed, 111 insertions(+), 112 deletions(-)
> >>>>
> >>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> >>>> b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> >>>> index df3fcc2b1248e..642296602de69 100644
> >>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> >>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> >>>> @@ -939,7 +939,7 @@ void i915_ttm_adjust_lru(struct
> >>>> drm_i915_gem_object *obj)
> >>>> * Don't manipulate the TTM LRUs while in TTM bo
> >>>> destruction.
> >>>> * We're called through i915_ttm_delete_mem_notify().
> >>>> */
> >>>> - if (!kref_read(&bo->kref))
> >>>> + if (!kref_read(&bo->base.refcount))
> >>>> return;
> >>>>
> >>>> /*
> >>>> @@ -957,30 +957,21 @@ void i915_ttm_adjust_lru(struct
> >>>> drm_i915_gem_object *obj)
> >>>> *
> >>>> * TODO: consider maybe also bumping the shrinker list
> >>>> here
> >>>> when we have
> >>>> * already unpinned it, which should give us something
> >>>> more
> >>>> like an LRU.
> >>>> - *
> >>>> - * TODO: There is a small window of opportunity for this
> >>>> function to
> >>>> - * get called from eviction after we've dropped the last
> >>>> GEM
> >>>> refcount,
> >>>> - * but before the TTM deleted flag is set on the object.
> >>>> Avoid
> >>>> - * adjusting the shrinker list in such cases, since the
> >>>> object is
> >>>> - * not available to the shrinker anyway due to its zero
> >>>> refcount.
> >>>> - * To fix this properly we should move to a TTM shrinker
> >>>> LRU
> >>>> list for
> >>>> - * these objects.
> >>>> */
> >>>> - if (kref_get_unless_zero(&obj->base.refcount)) {
> >>>> - if (shrinkable != obj->mm.ttm_shrinkable) {
> >>>> - if (shrinkable) {
> >>>> - if (obj->mm.madv ==
> >>>> I915_MADV_WILLNEED)
> >>>> -
> >>>> __i915_gem_object_make_s
> >>>> hrinkable(obj);
> >>>> - else
> >>>> -
> >>>> __i915_gem_object_make_p
> >>>> urgeable(obj);
> >>>> - } else {
> >>>> -
> >>>> i915_gem_object_make_unshrinkabl
> >>>> e(obj);
> >>>> - }
> >>>> -
> >>>> - obj->mm.ttm_shrinkable = shrinkable;
> >>>> + i915_gem_object_get(obj);
> >>>> + if (shrinkable != obj->mm.ttm_shrinkable) {
> >>>> + if (shrinkable) {
> >>>> + if (obj->mm.madv == I915_MADV_WILLNEED)
> >>>> + __i915_gem_object_make_shrinkabl
> >>>> e(ob
> >>>> j);
> >>>> + else
> >>>> + __i915_gem_object_make_purgeable
> >>>> (obj
> >>>> );
> >>>> + } else {
> >>>> + i915_gem_object_make_unshrinkable(obj);
> >>>> }
> >>>> - i915_gem_object_put(obj);
> >>>> +
> >>>> + obj->mm.ttm_shrinkable = shrinkable;
> >>>> }
> >>>> + i915_gem_object_put(obj);
> >>>>
> >>>> /*
> >>>> * Put on the correct LRU list depending on the MADV
> >>>> status
> >>>> diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> >>>> b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> >>>> index 56ad8ef325840..904cb4da6c9b3 100644
> >>>> --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> >>>> +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> >>>> @@ -127,7 +127,7 @@ static void
> >>>> ttm_bo_init_reserved_sys_man(struct
> >>>> kunit *test)
> >>>> dma_resv_unlock(bo->base.resv);
> >>>>
> >>>> KUNIT_EXPECT_EQ(test, err, 0);
> >>>> - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> >>>> + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
> >>>> KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> >>>> KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> >>>> KUNIT_EXPECT_EQ(test, bo->page_alignment, PAGE_SIZE);
> >>>> @@ -176,7 +176,7 @@ static void
> >>>> ttm_bo_init_reserved_mock_man(struct
> >>>> kunit *test)
> >>>> dma_resv_unlock(bo->base.resv);
> >>>>
> >>>> KUNIT_EXPECT_EQ(test, err, 0);
> >>>> - KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
> >>>> + KUNIT_EXPECT_EQ(test, kref_read(&bo->base.refcount), 1);
> >>>> KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
> >>>> KUNIT_EXPECT_EQ(test, bo->type, bo_type);
> >>>> KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
> >>>> @@ -969,6 +969,8 @@ static void
> >>>> ttm_bo_validate_allowed_only_evict(struct kunit *test)
> >>>> ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
> >>>> }
> >>>>
> >>>> +extern const struct drm_gem_object_funcs
> >>>> ttm_deleted_object_funcs;
> >>>> +
> >>>> static void ttm_bo_validate_deleted_evict(struct kunit *test)
> >>>> {
> >>>> struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
> >>>> @@ -999,7 +1001,7 @@ static void
> >>>> ttm_bo_validate_deleted_evict(struct
> >>>> kunit *test)
> >>>> KUNIT_EXPECT_EQ(test, ttm_resource_manager_usage(man),
> >>>> big);
> >>>>
> >>>> dma_resv_unlock(bo_big->base.resv);
> >>>> - bo_big->deleted = true;
> >>>> + bo_big->base.funcs = &ttm_deleted_object_funcs;
> >>>>
> >>>> bo_small = ttm_bo_kunit_init(test, test->priv, small,
> >>>> NULL);
> >>>> bo_small->type = bo_type;
> >>>> diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> >>>> b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> >>>> index 5cfe8f3f80d75..b7ab19e0e4b2b 100644
> >>>> --- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> >>>> +++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
> >>>> @@ -189,8 +189,6 @@ struct ttm_buffer_object
> >>>> *ttm_bo_kunit_init(struct kunit *test,
> >>>> bo->bdev = devs->ttm_dev;
> >>>> bo->destroy = dummy_ttm_bo_destroy;
> >>>>
> >>>> - kref_init(&bo->kref);
> >>>> -
> >>>> return bo;
> >>>> }
> >>>> EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
> >>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> >>>> b/drivers/gpu/drm/ttm/ttm_bo.c
> >>>> index 3980f376e3ba4..2b470c1746f60 100644
> >>>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> >>>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> >>>> @@ -246,88 +246,84 @@ static void ttm_bo_delayed_delete(struct
> >>>> work_struct *work)
> >>>> ttm_bo_put(bo);
> >>>> }
> >>>>
> >>>> -static void ttm_bo_release(struct kref *kref)
> >>>> +/*
> >>>> + * All other callbacks should never ever be called on a deleted
> >>>> TTM
> >>>> object.
> >>>> + */
> >>>> +const struct drm_gem_object_funcs ttm_deleted_object_funcs = {
> >>>> + .free = ttm_bo_free
> >>>> +};
> >>>> +EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_deleted_object_funcs);
> >>>> +
> >>>> +/* Returns true if the BO is about to get deleted */
> >>>> +static bool ttm_bo_is_zombie(struct ttm_buffer_object *bo)
> >>>> +{
> >>>> + return bo->base.funcs == &ttm_deleted_object_funcs;
> >>>> +}
> >>>> +
> >>>> +void ttm_bo_fini(struct ttm_buffer_object *bo)
> >>>> {
> >>>> - struct ttm_buffer_object *bo =
> >>>> - container_of(kref, struct ttm_buffer_object, kref);
> >>>> struct ttm_device *bdev = bo->bdev;
> >>>> int ret;
> >>>>
> >>>> WARN_ON_ONCE(bo->pin_count);
> >>>> WARN_ON_ONCE(bo->bulk_move);
> >>>>
> >>>> - if (!bo->deleted) {
> >>>> - ret = ttm_bo_individualize_resv(bo);
> >>>> - if (ret) {
> >>>> - /* Last resort, if we fail to allocate
> >>>> memory for the
> >>>> - * fences block for the BO to become
> >>>> idle
> >>>> - */
> >>>> - dma_resv_wait_timeout(bo->base.resv,
> >>>> -
> >>>> DMA_RESV_USAGE_BOOKKEEP, false,
> >>>> - 30 * HZ);
> >>>> - }
> >>>> + ret = ttm_bo_individualize_resv(bo);
> >>>> + if (ret) {
> >>>> + /* Last resort, if we fail to allocate memory
> >>>> for
> >>>> the
> >>>> + * fences block for the BO to become idle
> >>>> + */
> >>>> + dma_resv_wait_timeout(bo->base.resv,
> >>>> DMA_RESV_USAGE_BOOKKEEP,
> >>>> + false, 30 * HZ);
> >>>> + }
> >>>>
> >>>> - if (bdev->funcs->release_notify)
> >>>> - bdev->funcs->release_notify(bo);
> >>>> + if (bo->bdev->funcs->release_notify)
> >>>> + bo->bdev->funcs->release_notify(bo);
> >>>>
> >>>> - drm_vma_offset_remove(bdev->vma_manager, &bo-
> >>>>> base.vma_node);
> >>>> - ttm_mem_io_free(bdev, bo->resource);
> >>>> + drm_vma_offset_remove(bdev->vma_manager, &bo-
> >>>>> base.vma_node);
> >>>> + ttm_mem_io_free(bdev, bo->resource);
> >>>>
> >>>> - if (!dma_resv_test_signaled(&bo->base._resv,
> >>>> -
> >>>> DMA_RESV_USAGE_BOOKKEEP)
> >>>>>>
> >>>> - (want_init_on_free() && (bo->ttm != NULL))
> >>>> ||
> >>>> - bo->type == ttm_bo_type_sg ||
> >>>> - !dma_resv_trylock(bo->base.resv)) {
> >>>> - /* The BO is not idle, resurrect it for
> >>>> delayed destroy */
> >>>> - ttm_bo_flush_all_fences(bo);
> >>>> - bo->deleted = true;
> >>>> + if (!dma_resv_test_signaled(&bo->base._resv,
> >>>> DMA_RESV_USAGE_BOOKKEEP) ||
> >>>> + (want_init_on_free() && (bo->ttm != NULL)) ||
> >>>> + bo->type == ttm_bo_type_sg ||
> >>>> + !dma_resv_trylock(bo->base.resv)) {
> >>>> + /* The BO is not idle, resurrect it for delayed
> >>>> destroy */
> >>>> + ttm_bo_flush_all_fences(bo);
> >>>>
> >>>> - spin_lock(&bdev->lru_lock);
> >>>> -
> >>>> - /*
> >>>> - * Make pinned bos immediately available
> >>>> to
> >>>> - * shrinkers, now that they are queued
> >>>> for
> >>>> - * destruction.
> >>>> - *
> >>>> - * FIXME: QXL is triggering this. Can be
> >>>> removed when the
> >>>> - * driver is fixed.
> >>>> - */
> >>>> - if (bo->pin_count) {
> >>>> - bo->pin_count = 0;
> >>>> -
> >>>> ttm_resource_move_to_lru_tail(bo-
> >>>>> resource);
> >>>> - }
> >>>> + spin_lock(&bo->bdev->lru_lock);
> >>>>
> >>>> - kref_init(&bo->kref);
> >>>> - spin_unlock(&bdev->lru_lock);
> >>>> + /*
> >>>> + * Make pinned bos immediately available to
> >>>> + * shrinkers, now that they are queued for
> >>>> + * destruction.
> >>>> + *
> >>>> + * FIXME: QXL is triggering this. Can be removed
> >>>> when the
> >>>> + * driver is fixed.
> >>>> + */
> >>>> + if (bo->pin_count) {
> >>>> + bo->pin_count = 0;
> >>>> + ttm_resource_move_to_lru_tail(bo-
> >>>>> resource);
> >>>> + }
> >>>>
> >>>> - INIT_WORK(&bo->delayed_delete,
> >>>> ttm_bo_delayed_delete);
> >>>> + kref_init(&bo->base.refcount);
> >>>> + bo->base.funcs = &ttm_deleted_object_funcs;
> >>>> + spin_unlock(&bo->bdev->lru_lock);
> >>>>
> >>>> - /* Schedule the worker on the closest
> >>>> NUMA
> >>>> node. This
> >>>> - * improves performance since system
> >>>> memory
> >>>> might be
> >>>> - * cleared on free and that is best done
> >>>> on
> >>>> a CPU core
> >>>> - * close to it.
> >>>> - */
> >>>> - queue_work_node(bdev->pool.nid, bdev-
> >>>>> wq,
> >>>> &bo->delayed_delete);
> >>>> - return;
> >>>> - }
> >>>> + INIT_WORK(&bo->delayed_delete,
> >>>> ttm_bo_delayed_delete);
> >>>>
> >>>> + /* Schedule the worker on the closest NUMA node.
> >>>> This
> >>>> + * improves performance since system memory
> >>>> might be
> >>>> + * cleared on free and that is best done on a
> >>>> CPU
> >>>> core
> >>>> + * close to it.
> >>>> + */
> >>>> + queue_work_node(bdev->pool.nid, bdev->wq, &bo-
> >>>>> delayed_delete);
> >>>> + } else {
> >>>> ttm_bo_cleanup_memtype_use(bo);
> >>>> dma_resv_unlock(bo->base.resv);
> >>>> - }
> >>>>
> >>>> - atomic_dec(&ttm_glob.bo_count);
> >>>> - bo->destroy(bo);
> >>>> -}
> >>>> -
> >>>> -/* TODO: remove! */
> >>>> -void ttm_bo_put(struct ttm_buffer_object *bo)
> >>>> -{
> >>>> - kref_put(&bo->kref, ttm_bo_release);
> >>>> -}
> >>>> -
> >>>> -void ttm_bo_fini(struct ttm_buffer_object *bo)
> >>>> -{
> >>>> - ttm_bo_put(bo);
> >>>> + atomic_dec(&ttm_glob.bo_count);
> >>>> + bo->destroy(bo);
> >>>> + }
> >>>> }
> >>>> EXPORT_SYMBOL(ttm_bo_fini);
> >>>>
> >>>> @@ -470,7 +466,7 @@ int ttm_bo_evict_first(struct ttm_device
> >>>> *bdev,
> >>>> struct ttm_resource_manager *man
> >>>> if (!bo->resource || bo->resource->mem_type != mem_type)
> >>>> goto out_bo_moved;
> >>>>
> >>>> - if (bo->deleted) {
> >>>> + if (ttm_bo_is_zombie(bo)) {
> >>>> ret = ttm_bo_wait_ctx(bo, ctx);
> >>>> if (!ret)
> >>>> ttm_bo_cleanup_memtype_use(bo);
> >>>> @@ -524,7 +520,7 @@ static s64 ttm_bo_evict_cb(struct
> >>>> ttm_lru_walk
> >>>> *walk, struct ttm_buffer_object *
> >>>> if (bo->pin_count || !bo->bdev->funcs-
> >>>>> eviction_valuable(bo,
> >>>> evict_walk->place))
> >>>> return 0;
> >>>>
> >>>> - if (bo->deleted) {
> >>>> + if (ttm_bo_is_zombie(bo)) {
> >>>> lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
> >>>> if (!lret)
> >>>> ttm_bo_cleanup_memtype_use(bo);
> >>>> @@ -624,7 +620,6 @@ static int ttm_bo_evict_alloc(struct
> >>>> ttm_device
> >>>> *bdev,
> >>>> void ttm_bo_pin(struct ttm_buffer_object *bo)
> >>>> {
> >>>> dma_resv_assert_held(bo->base.resv);
> >>>> - WARN_ON_ONCE(!kref_read(&bo->kref));
> >>>> spin_lock(&bo->bdev->lru_lock);
> >>>> if (bo->resource)
> >>>> ttm_resource_del_bulk_move(bo->resource, bo);
> >>>> @@ -643,7 +638,6 @@ EXPORT_SYMBOL(ttm_bo_pin);
> >>>> void ttm_bo_unpin(struct ttm_buffer_object *bo)
> >>>> {
> >>>> dma_resv_assert_held(bo->base.resv);
> >>>> - WARN_ON_ONCE(!kref_read(&bo->kref));
> >>>> if (WARN_ON_ONCE(!bo->pin_count))
> >>>> return;
> >>>>
> >>>> @@ -934,7 +928,6 @@ int ttm_bo_init_reserved(struct ttm_device
> >>>> *bdev,
> >>>> struct ttm_buffer_object *bo,
> >>>> {
> >>>> int ret;
> >>>>
> >>>> - kref_init(&bo->kref);
> >>>> bo->bdev = bdev;
> >>>> bo->type = type;
> >>>> bo->page_alignment = alignment;
> >>>> @@ -1131,8 +1124,8 @@ ttm_bo_swapout_cb(struct ttm_lru_walk
> >>>> *walk,
> >>>> struct ttm_buffer_object *bo)
> >>>> goto out;
> >>>> }
> >>>>
> >>>> - if (bo->deleted) {
> >>>> - pgoff_t num_pages = tt->num_pages;
> >>>> + if (ttm_bo_is_zombie(bo)) {
> >>>> + pgoff_t num_pages = bo->ttm->num_pages;
> >>>>
> >>>> ret = ttm_bo_wait_ctx(bo, ctx);
> >>>> if (ret)
> >>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> >>>> b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> >>>> index e0d48eac74b03..ded2a47be0bcb 100644
> >>>> --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
> >>>> +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
> >>>> @@ -27,6 +27,14 @@
> >>>>
> >>>> #include <drm/ttm/ttm_bo.h>
> >>>>
> >>>> +static inline void ttm_bo_free(struct drm_gem_object *gobj)
> >>>> +{
> >>>> + struct ttm_buffer_object *bo = container_of(gobj,
> >>>> typeof(*bo), base);
> >>>> +
> >>>> + atomic_dec(&ttm_glob.bo_count);
> >>>> + bo->destroy(bo);
> >>>> +}
> >>>> +
> >>>> /**
> >>>> * ttm_bo_get - reference a struct ttm_buffer_object
> >>>> *
> >>>> @@ -34,7 +42,7 @@
> >>>> */
> >>>> static inline void ttm_bo_get(struct ttm_buffer_object *bo)
> >>>> {
> >>>> - kref_get(&bo->kref);
> >>>> + drm_gem_object_get(&bo->base);
> >>>> }
> >>>>
> >>>> /**
> >>>> @@ -50,11 +58,14 @@ static inline void ttm_bo_get(struct
> >>>> ttm_buffer_object *bo)
> >>>> static inline __must_check struct ttm_buffer_object *
> >>>> ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
> >>>> {
> >>>> - if (!kref_get_unless_zero(&bo->kref))
> >>>> + if (!kref_get_unless_zero(&bo->base.refcount))
> >>>> return NULL;
> >>>> return bo;
> >>>> }
> >>>>
> >>>> -void ttm_bo_put(struct ttm_buffer_object *bo);
> >>>> +static inline void ttm_bo_put(struct ttm_buffer_object *bo)
> >>>> +{
> >>>> + drm_gem_object_put(&bo->base);
> >>>> +}
> >>>>
> >>>> #endif
> >>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> >>>> b/drivers/gpu/drm/ttm/ttm_bo_util.c
> >>>> index 3e3c201a02226..7ed085adf1c9b 100644
> >>>> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> >>>> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> >>>> @@ -41,6 +41,18 @@
> >>>>
> >>>> #include "ttm_bo_internal.h"
> >>>>
> >>>> +static void ttm_transfer_object_free(struct drm_gem_object *obj)
> >>>> +{
> >>>> + struct ttm_buffer_object *bo =
> >>>> + container_of(obj, struct ttm_buffer_object,
> >>>> base);
> >>>> +
> >>>> + ttm_bo_fini(bo);
> >>>> +}
> >>>> +
> >>>> +const struct drm_gem_object_funcs ttm_transfer_object_funcs = {
> >>>> + .free = ttm_transfer_object_free,
> >>>> +};
> >>>> +
> >>>> struct ttm_transfer_obj {
> >>>> struct ttm_buffer_object base;
> >>>> struct ttm_buffer_object *bo;
> >>>> @@ -247,7 +259,8 @@ static int ttm_buffer_object_transfer(struct
> >>>> ttm_buffer_object *bo,
> >>>> atomic_inc(&ttm_glob.bo_count);
> >>>> drm_vma_node_reset(&fbo->base.base.vma_node);
> >>>>
> >>>> - kref_init(&fbo->base.kref);
> >>>> + kref_init(&fbo->base.base.refcount);
> >>>> + fbo->base.base.funcs = &ttm_transfer_object_funcs;
> >>>> fbo->base.destroy = &ttm_transfered_destroy;
> >>>> fbo->base.pin_count = 0;
> >>>> if (bo->type != ttm_bo_type_sg)
> >>>> diff --git a/drivers/gpu/drm/xe/xe_bo.c
> >>>> b/drivers/gpu/drm/xe/xe_bo.c
> >>>> index 85e6d9a0f575b..5843f850339c7 100644
> >>>> --- a/drivers/gpu/drm/xe/xe_bo.c
> >>>> +++ b/drivers/gpu/drm/xe/xe_bo.c
> >>>> @@ -1651,7 +1651,7 @@ static bool
> >>>> xe_ttm_bo_lock_in_destructor(struct
> >>>> ttm_buffer_object *ttm_bo)
> >>>> struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
> >>>> bool locked;
> >>>>
> >>>> - xe_assert(xe, !kref_read(&ttm_bo->kref));
> >>>> + xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
> >>>>
> >>>> /*
> >>>> * We can typically only race with TTM trylocking under
> >>>> the
> >>>> diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
> >>>> index 8310bc3d55f90..1eae9eea5ff32 100644
> >>>> --- a/include/drm/ttm/ttm_bo.h
> >>>> +++ b/include/drm/ttm/ttm_bo.h
> >>>> @@ -78,11 +78,8 @@ enum ttm_bo_type {
> >>>> * @type: The bo type.
> >>>> * @page_alignment: Page alignment.
> >>>> * @destroy: Destruction function. If NULL, kfree is used.
> >>>> - * @kref: Reference count of this buffer object. When this
> >>>> refcount
> >>>> reaches
> >>>> - * zero, the object is destroyed or put on the delayed delete
> >>>> list.
> >>>> * @resource: structure describing current placement.
> >>>> * @ttm: TTM structure holding system pages.
> >>>> - * @deleted: True if the object is only a zombie and already
> >>>> deleted.
> >>>> * @bulk_move: The bulk move object.
> >>>> * @priority: Priority for LRU, BOs with lower priority are
> >>>> evicted
> >>>> first.
> >>>> * @pin_count: Pin count.
> >>>> @@ -109,17 +106,11 @@ struct ttm_buffer_object {
> >>>> uint32_t page_alignment;
> >>>> void (*destroy) (struct ttm_buffer_object *);
> >>>>
> >>>> - /*
> >>>> - * Members not needing protection.
> >>>> - */
> >>>> - struct kref kref;
> >>>> -
> >>>> /*
> >>>> * Members protected by the bo::resv::reserved lock.
> >>>> */
> >>>> struct ttm_resource *resource;
> >>>> struct ttm_tt *ttm;
> >>>> - bool deleted;
> >>>> struct ttm_lru_bulk_move *bulk_move;
> >>>> unsigned priority;
> >>>> unsigned pin_count;
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 03/10] drm/ttm: remove ttm_lru_walk_ops
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
2026-07-03 16:31 ` [PATCH 01/10] drm/exec: Add helper to bypass IGNORE_DUPLICATES flag Natalie Vock
2026-07-03 16:31 ` [PATCH 02/10] drm/ttm: replace TTMs refcount with the DRM refcount v4 Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
2026-07-06 12:34 ` Thomas Hellström
2026-07-03 16:31 ` [PATCH 04/10] drm/ttm: grab BO reference before locking it Natalie Vock
` (6 subsequent siblings)
9 siblings, 1 reply; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
From: Christian König <christian.koenig@amd.com>
It's just another layer of indirection.
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/ttm/ttm_bo.c | 12 ++----------
drivers/gpu/drm/ttm/ttm_bo_util.c | 2 +-
include/drm/ttm/ttm_bo.h | 34 ++++++++++++++--------------------
3 files changed, 17 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 2b470c1746f60..1fb8c53da0362 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -545,10 +545,6 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *
return lret;
}
-static const struct ttm_lru_walk_ops ttm_evict_walk_ops = {
- .process_bo = ttm_bo_evict_cb,
-};
-
static int ttm_bo_evict_alloc(struct ttm_device *bdev,
struct ttm_resource_manager *man,
const struct ttm_place *place,
@@ -560,7 +556,7 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
{
struct ttm_bo_evict_walk evict_walk = {
.walk = {
- .ops = &ttm_evict_walk_ops,
+ .process_bo = ttm_bo_evict_cb,
.arg = {
.ctx = ctx,
.ticket = ticket,
@@ -1187,10 +1183,6 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
return ret;
}
-const struct ttm_lru_walk_ops ttm_swap_ops = {
- .process_bo = ttm_bo_swapout_cb,
-};
-
/**
* ttm_bo_swapout() - Swap out buffer objects on the LRU list to shmem.
* @bdev: The ttm device.
@@ -1209,7 +1201,7 @@ s64 ttm_bo_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
{
struct ttm_bo_swapout_walk swapout_walk = {
.walk = {
- .ops = &ttm_swap_ops,
+ .process_bo = ttm_bo_swapout_cb,
.arg = {
.ctx = ctx,
.trylock_only = true,
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 7ed085adf1c9b..29f068944a972 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -919,7 +919,7 @@ s64 ttm_lru_walk_for_evict(struct ttm_lru_walk *walk, struct ttm_device *bdev,
s64 lret;
ttm_bo_lru_for_each_reserved_guarded(&cursor, man, &walk->arg, bo) {
- lret = walk->ops->process_bo(walk, bo);
+ lret = walk->process_bo(walk, bo);
if (lret == -EBUSY || lret == -EALREADY)
lret = 0;
progress = (lret < 0) ? lret : progress + lret;
diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
index 1eae9eea5ff32..0fcd5082a7080 100644
--- a/include/drm/ttm/ttm_bo.h
+++ b/include/drm/ttm/ttm_bo.h
@@ -189,24 +189,6 @@ struct ttm_operation_ctx {
uint64_t bytes_moved;
};
-struct ttm_lru_walk;
-
-/** struct ttm_lru_walk_ops - Operations for a LRU walk. */
-struct ttm_lru_walk_ops {
- /**
- * process_bo - Process this bo.
- * @walk: struct ttm_lru_walk describing the walk.
- * @bo: A locked and referenced buffer object.
- *
- * Return: Negative error code on error, User-defined positive value
- * (typically, but not always, size of the processed bo) on success.
- * On success, the returned values are summed by the walk and the
- * walk exits when its target is met.
- * 0 also indicates success, -EBUSY means this bo was skipped.
- */
- s64 (*process_bo)(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo);
-};
-
/**
* struct ttm_lru_walk_arg - Common part for the variants of BO LRU walk.
*/
@@ -223,8 +205,20 @@ struct ttm_lru_walk_arg {
* struct ttm_lru_walk - Structure describing a LRU walk.
*/
struct ttm_lru_walk {
- /** @ops: Pointer to the ops structure. */
- const struct ttm_lru_walk_ops *ops;
+ /**
+ * process_bo - Process this bo.
+ * @walk: struct ttm_lru_walk describing the walk.
+ * @bo: A locked and referenced buffer object.
+ *
+ * Return: Negative error code on error, User-defined positive value
+ * (typically, but not always, size of the processed bo) on success.
+ * On success, the returned values are summed by the walk and the
+ * walk exits when its target is met.
+ * 0 also indicates success, -EBUSY means this bo was skipped.
+ */
+ s64 (*process_bo)(struct ttm_lru_walk *walk,
+ struct ttm_buffer_object *bo);
+
/** @arg: Common bo LRU walk arguments. */
struct ttm_lru_walk_arg arg;
};
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH 03/10] drm/ttm: remove ttm_lru_walk_ops
2026-07-03 16:31 ` [PATCH 03/10] drm/ttm: remove ttm_lru_walk_ops Natalie Vock
@ 2026-07-06 12:34 ` Thomas Hellström
2026-07-06 13:05 ` Christian König
2026-07-06 13:08 ` Natalie Vock
0 siblings, 2 replies; 26+ messages in thread
From: Thomas Hellström @ 2026-07-06 12:34 UTC (permalink / raw)
To: Natalie Vock, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, Christian Koenig, Huang Rui,
Matthew Auld, Matthew Brost, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
Hi,
On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> From: Christian König <christian.koenig@amd.com>
>
> It's just another layer of indirection.
>
> Signed-off-by: Christian König <christian.koenig@amd.com>
> Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
Personally I don't have a strong opinion on this, but the reason for
separating out the ops is that adding function pointers in the walk
iterator itself was once pushed back on quite forcefully by Linus when
I tried to do that in mm/pagewalk. Claiming for various reasons the
standard way of doing that in Linux is using a const ops struct that
ends up in unmodifiable memory.
/Thomas
> ---
> drivers/gpu/drm/ttm/ttm_bo.c | 12 ++----------
> drivers/gpu/drm/ttm/ttm_bo_util.c | 2 +-
> include/drm/ttm/ttm_bo.h | 34 ++++++++++++++---------------
> -----
> 3 files changed, 17 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> b/drivers/gpu/drm/ttm/ttm_bo.c
> index 2b470c1746f60..1fb8c53da0362 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -545,10 +545,6 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk
> *walk, struct ttm_buffer_object *
> return lret;
> }
>
> -static const struct ttm_lru_walk_ops ttm_evict_walk_ops = {
> - .process_bo = ttm_bo_evict_cb,
> -};
> -
> static int ttm_bo_evict_alloc(struct ttm_device *bdev,
> struct ttm_resource_manager *man,
> const struct ttm_place *place,
> @@ -560,7 +556,7 @@ static int ttm_bo_evict_alloc(struct ttm_device
> *bdev,
> {
> struct ttm_bo_evict_walk evict_walk = {
> .walk = {
> - .ops = &ttm_evict_walk_ops,
> + .process_bo = ttm_bo_evict_cb,
> .arg = {
> .ctx = ctx,
> .ticket = ticket,
> @@ -1187,10 +1183,6 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk,
> struct ttm_buffer_object *bo)
> return ret;
> }
>
> -const struct ttm_lru_walk_ops ttm_swap_ops = {
> - .process_bo = ttm_bo_swapout_cb,
> -};
> -
> /**
> * ttm_bo_swapout() - Swap out buffer objects on the LRU list to
> shmem.
> * @bdev: The ttm device.
> @@ -1209,7 +1201,7 @@ s64 ttm_bo_swapout(struct ttm_device *bdev,
> struct ttm_operation_ctx *ctx,
> {
> struct ttm_bo_swapout_walk swapout_walk = {
> .walk = {
> - .ops = &ttm_swap_ops,
> + .process_bo = ttm_bo_swapout_cb,
> .arg = {
> .ctx = ctx,
> .trylock_only = true,
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> b/drivers/gpu/drm/ttm/ttm_bo_util.c
> index 7ed085adf1c9b..29f068944a972 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> @@ -919,7 +919,7 @@ s64 ttm_lru_walk_for_evict(struct ttm_lru_walk
> *walk, struct ttm_device *bdev,
> s64 lret;
>
> ttm_bo_lru_for_each_reserved_guarded(&cursor, man, &walk-
> >arg, bo) {
> - lret = walk->ops->process_bo(walk, bo);
> + lret = walk->process_bo(walk, bo);
> if (lret == -EBUSY || lret == -EALREADY)
> lret = 0;
> progress = (lret < 0) ? lret : progress + lret;
> diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
> index 1eae9eea5ff32..0fcd5082a7080 100644
> --- a/include/drm/ttm/ttm_bo.h
> +++ b/include/drm/ttm/ttm_bo.h
> @@ -189,24 +189,6 @@ struct ttm_operation_ctx {
> uint64_t bytes_moved;
> };
>
> -struct ttm_lru_walk;
> -
> -/** struct ttm_lru_walk_ops - Operations for a LRU walk. */
> -struct ttm_lru_walk_ops {
> - /**
> - * process_bo - Process this bo.
> - * @walk: struct ttm_lru_walk describing the walk.
> - * @bo: A locked and referenced buffer object.
> - *
> - * Return: Negative error code on error, User-defined
> positive value
> - * (typically, but not always, size of the processed bo) on
> success.
> - * On success, the returned values are summed by the walk
> and the
> - * walk exits when its target is met.
> - * 0 also indicates success, -EBUSY means this bo was
> skipped.
> - */
> - s64 (*process_bo)(struct ttm_lru_walk *walk, struct
> ttm_buffer_object *bo);
> -};
> -
> /**
> * struct ttm_lru_walk_arg - Common part for the variants of BO LRU
> walk.
> */
> @@ -223,8 +205,20 @@ struct ttm_lru_walk_arg {
> * struct ttm_lru_walk - Structure describing a LRU walk.
> */
> struct ttm_lru_walk {
> - /** @ops: Pointer to the ops structure. */
> - const struct ttm_lru_walk_ops *ops;
> + /**
> + * process_bo - Process this bo.
> + * @walk: struct ttm_lru_walk describing the walk.
> + * @bo: A locked and referenced buffer object.
> + *
> + * Return: Negative error code on error, User-defined
> positive value
> + * (typically, but not always, size of the processed bo) on
> success.
> + * On success, the returned values are summed by the walk
> and the
> + * walk exits when its target is met.
> + * 0 also indicates success, -EBUSY means this bo was
> skipped.
> + */
> + s64 (*process_bo)(struct ttm_lru_walk *walk,
> + struct ttm_buffer_object *bo);
> +
> /** @arg: Common bo LRU walk arguments. */
> struct ttm_lru_walk_arg arg;
> };
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH 03/10] drm/ttm: remove ttm_lru_walk_ops
2026-07-06 12:34 ` Thomas Hellström
@ 2026-07-06 13:05 ` Christian König
2026-07-06 16:31 ` Matthew Brost
2026-07-06 13:08 ` Natalie Vock
1 sibling, 1 reply; 26+ messages in thread
From: Christian König @ 2026-07-06 13:05 UTC (permalink / raw)
To: Thomas Hellström, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Matthew Brost, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
On 7/6/26 14:34, Thomas Hellström wrote:
> Hi,
>
> On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
>> From: Christian König <christian.koenig@amd.com>
>>
>> It's just another layer of indirection.
>>
>> Signed-off-by: Christian König <christian.koenig@amd.com>
>> Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
>
> Personally I don't have a strong opinion on this, but the reason for
> separating out the ops is that adding function pointers in the walk
> iterator itself was once pushed back on quite forcefully by Linus when
> I tried to do that in mm/pagewalk. Claiming for various reasons the
> standard way of doing that in Linux is using a const ops struct that
> ends up in unmodifiable memory.
Ah! I was already wondering why the extra indirection was used.
I'm perfectly fine to keep it. It just looked a bit odd.
Regards,
Christian.
>
> /Thomas
>
>
>> ---
>> drivers/gpu/drm/ttm/ttm_bo.c | 12 ++----------
>> drivers/gpu/drm/ttm/ttm_bo_util.c | 2 +-
>> include/drm/ttm/ttm_bo.h | 34 ++++++++++++++---------------
>> -----
>> 3 files changed, 17 insertions(+), 31 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
>> b/drivers/gpu/drm/ttm/ttm_bo.c
>> index 2b470c1746f60..1fb8c53da0362 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
>> @@ -545,10 +545,6 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk
>> *walk, struct ttm_buffer_object *
>> return lret;
>> }
>>
>> -static const struct ttm_lru_walk_ops ttm_evict_walk_ops = {
>> - .process_bo = ttm_bo_evict_cb,
>> -};
>> -
>> static int ttm_bo_evict_alloc(struct ttm_device *bdev,
>> struct ttm_resource_manager *man,
>> const struct ttm_place *place,
>> @@ -560,7 +556,7 @@ static int ttm_bo_evict_alloc(struct ttm_device
>> *bdev,
>> {
>> struct ttm_bo_evict_walk evict_walk = {
>> .walk = {
>> - .ops = &ttm_evict_walk_ops,
>> + .process_bo = ttm_bo_evict_cb,
>> .arg = {
>> .ctx = ctx,
>> .ticket = ticket,
>> @@ -1187,10 +1183,6 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk,
>> struct ttm_buffer_object *bo)
>> return ret;
>> }
>>
>> -const struct ttm_lru_walk_ops ttm_swap_ops = {
>> - .process_bo = ttm_bo_swapout_cb,
>> -};
>> -
>> /**
>> * ttm_bo_swapout() - Swap out buffer objects on the LRU list to
>> shmem.
>> * @bdev: The ttm device.
>> @@ -1209,7 +1201,7 @@ s64 ttm_bo_swapout(struct ttm_device *bdev,
>> struct ttm_operation_ctx *ctx,
>> {
>> struct ttm_bo_swapout_walk swapout_walk = {
>> .walk = {
>> - .ops = &ttm_swap_ops,
>> + .process_bo = ttm_bo_swapout_cb,
>> .arg = {
>> .ctx = ctx,
>> .trylock_only = true,
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
>> b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> index 7ed085adf1c9b..29f068944a972 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> @@ -919,7 +919,7 @@ s64 ttm_lru_walk_for_evict(struct ttm_lru_walk
>> *walk, struct ttm_device *bdev,
>> s64 lret;
>>
>> ttm_bo_lru_for_each_reserved_guarded(&cursor, man, &walk-
>>> arg, bo) {
>> - lret = walk->ops->process_bo(walk, bo);
>> + lret = walk->process_bo(walk, bo);
>> if (lret == -EBUSY || lret == -EALREADY)
>> lret = 0;
>> progress = (lret < 0) ? lret : progress + lret;
>> diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
>> index 1eae9eea5ff32..0fcd5082a7080 100644
>> --- a/include/drm/ttm/ttm_bo.h
>> +++ b/include/drm/ttm/ttm_bo.h
>> @@ -189,24 +189,6 @@ struct ttm_operation_ctx {
>> uint64_t bytes_moved;
>> };
>>
>> -struct ttm_lru_walk;
>> -
>> -/** struct ttm_lru_walk_ops - Operations for a LRU walk. */
>> -struct ttm_lru_walk_ops {
>> - /**
>> - * process_bo - Process this bo.
>> - * @walk: struct ttm_lru_walk describing the walk.
>> - * @bo: A locked and referenced buffer object.
>> - *
>> - * Return: Negative error code on error, User-defined
>> positive value
>> - * (typically, but not always, size of the processed bo) on
>> success.
>> - * On success, the returned values are summed by the walk
>> and the
>> - * walk exits when its target is met.
>> - * 0 also indicates success, -EBUSY means this bo was
>> skipped.
>> - */
>> - s64 (*process_bo)(struct ttm_lru_walk *walk, struct
>> ttm_buffer_object *bo);
>> -};
>> -
>> /**
>> * struct ttm_lru_walk_arg - Common part for the variants of BO LRU
>> walk.
>> */
>> @@ -223,8 +205,20 @@ struct ttm_lru_walk_arg {
>> * struct ttm_lru_walk - Structure describing a LRU walk.
>> */
>> struct ttm_lru_walk {
>> - /** @ops: Pointer to the ops structure. */
>> - const struct ttm_lru_walk_ops *ops;
>> + /**
>> + * process_bo - Process this bo.
>> + * @walk: struct ttm_lru_walk describing the walk.
>> + * @bo: A locked and referenced buffer object.
>> + *
>> + * Return: Negative error code on error, User-defined
>> positive value
>> + * (typically, but not always, size of the processed bo) on
>> success.
>> + * On success, the returned values are summed by the walk
>> and the
>> + * walk exits when its target is met.
>> + * 0 also indicates success, -EBUSY means this bo was
>> skipped.
>> + */
>> + s64 (*process_bo)(struct ttm_lru_walk *walk,
>> + struct ttm_buffer_object *bo);
>> +
>> /** @arg: Common bo LRU walk arguments. */
>> struct ttm_lru_walk_arg arg;
>> };
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH 03/10] drm/ttm: remove ttm_lru_walk_ops
2026-07-06 13:05 ` Christian König
@ 2026-07-06 16:31 ` Matthew Brost
0 siblings, 0 replies; 26+ messages in thread
From: Matthew Brost @ 2026-07-06 16:31 UTC (permalink / raw)
To: Christian König
Cc: Thomas Hellström, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Huang Rui, Matthew Auld, Alex Deucher, dri-devel, linux-kernel,
intel-gfx, intel-xe, amd-gfx
On Mon, Jul 06, 2026 at 03:05:54PM +0200, Christian König wrote:
> On 7/6/26 14:34, Thomas Hellström wrote:
> > Hi,
> >
> > On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
> >> From: Christian König <christian.koenig@amd.com>
> >>
> >> It's just another layer of indirection.
> >>
> >> Signed-off-by: Christian König <christian.koenig@amd.com>
> >> Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
> >
> > Personally I don't have a strong opinion on this, but the reason for
> > separating out the ops is that adding function pointers in the walk
> > iterator itself was once pushed back on quite forcefully by Linus when
> > I tried to do that in mm/pagewalk. Claiming for various reasons the
> > standard way of doing that in Linux is using a const ops struct that
> > ends up in unmodifiable memory.
+1, I was about to comment on this patch saying iirc I a read Linus
statement once const ops struct was Linux prefered way.
Matt
>
> Ah! I was already wondering why the extra indirection was used.
>
> I'm perfectly fine to keep it. It just looked a bit odd.
>
> Regards,
> Christian.
>
> >
> > /Thomas
> >
> >
> >> ---
> >> drivers/gpu/drm/ttm/ttm_bo.c | 12 ++----------
> >> drivers/gpu/drm/ttm/ttm_bo_util.c | 2 +-
> >> include/drm/ttm/ttm_bo.h | 34 ++++++++++++++---------------
> >> -----
> >> 3 files changed, 17 insertions(+), 31 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
> >> b/drivers/gpu/drm/ttm/ttm_bo.c
> >> index 2b470c1746f60..1fb8c53da0362 100644
> >> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> >> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> >> @@ -545,10 +545,6 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk
> >> *walk, struct ttm_buffer_object *
> >> return lret;
> >> }
> >>
> >> -static const struct ttm_lru_walk_ops ttm_evict_walk_ops = {
> >> - .process_bo = ttm_bo_evict_cb,
> >> -};
> >> -
> >> static int ttm_bo_evict_alloc(struct ttm_device *bdev,
> >> struct ttm_resource_manager *man,
> >> const struct ttm_place *place,
> >> @@ -560,7 +556,7 @@ static int ttm_bo_evict_alloc(struct ttm_device
> >> *bdev,
> >> {
> >> struct ttm_bo_evict_walk evict_walk = {
> >> .walk = {
> >> - .ops = &ttm_evict_walk_ops,
> >> + .process_bo = ttm_bo_evict_cb,
> >> .arg = {
> >> .ctx = ctx,
> >> .ticket = ticket,
> >> @@ -1187,10 +1183,6 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk,
> >> struct ttm_buffer_object *bo)
> >> return ret;
> >> }
> >>
> >> -const struct ttm_lru_walk_ops ttm_swap_ops = {
> >> - .process_bo = ttm_bo_swapout_cb,
> >> -};
> >> -
> >> /**
> >> * ttm_bo_swapout() - Swap out buffer objects on the LRU list to
> >> shmem.
> >> * @bdev: The ttm device.
> >> @@ -1209,7 +1201,7 @@ s64 ttm_bo_swapout(struct ttm_device *bdev,
> >> struct ttm_operation_ctx *ctx,
> >> {
> >> struct ttm_bo_swapout_walk swapout_walk = {
> >> .walk = {
> >> - .ops = &ttm_swap_ops,
> >> + .process_bo = ttm_bo_swapout_cb,
> >> .arg = {
> >> .ctx = ctx,
> >> .trylock_only = true,
> >> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> >> b/drivers/gpu/drm/ttm/ttm_bo_util.c
> >> index 7ed085adf1c9b..29f068944a972 100644
> >> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> >> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> >> @@ -919,7 +919,7 @@ s64 ttm_lru_walk_for_evict(struct ttm_lru_walk
> >> *walk, struct ttm_device *bdev,
> >> s64 lret;
> >>
> >> ttm_bo_lru_for_each_reserved_guarded(&cursor, man, &walk-
> >>> arg, bo) {
> >> - lret = walk->ops->process_bo(walk, bo);
> >> + lret = walk->process_bo(walk, bo);
> >> if (lret == -EBUSY || lret == -EALREADY)
> >> lret = 0;
> >> progress = (lret < 0) ? lret : progress + lret;
> >> diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
> >> index 1eae9eea5ff32..0fcd5082a7080 100644
> >> --- a/include/drm/ttm/ttm_bo.h
> >> +++ b/include/drm/ttm/ttm_bo.h
> >> @@ -189,24 +189,6 @@ struct ttm_operation_ctx {
> >> uint64_t bytes_moved;
> >> };
> >>
> >> -struct ttm_lru_walk;
> >> -
> >> -/** struct ttm_lru_walk_ops - Operations for a LRU walk. */
> >> -struct ttm_lru_walk_ops {
> >> - /**
> >> - * process_bo - Process this bo.
> >> - * @walk: struct ttm_lru_walk describing the walk.
> >> - * @bo: A locked and referenced buffer object.
> >> - *
> >> - * Return: Negative error code on error, User-defined
> >> positive value
> >> - * (typically, but not always, size of the processed bo) on
> >> success.
> >> - * On success, the returned values are summed by the walk
> >> and the
> >> - * walk exits when its target is met.
> >> - * 0 also indicates success, -EBUSY means this bo was
> >> skipped.
> >> - */
> >> - s64 (*process_bo)(struct ttm_lru_walk *walk, struct
> >> ttm_buffer_object *bo);
> >> -};
> >> -
> >> /**
> >> * struct ttm_lru_walk_arg - Common part for the variants of BO LRU
> >> walk.
> >> */
> >> @@ -223,8 +205,20 @@ struct ttm_lru_walk_arg {
> >> * struct ttm_lru_walk - Structure describing a LRU walk.
> >> */
> >> struct ttm_lru_walk {
> >> - /** @ops: Pointer to the ops structure. */
> >> - const struct ttm_lru_walk_ops *ops;
> >> + /**
> >> + * process_bo - Process this bo.
> >> + * @walk: struct ttm_lru_walk describing the walk.
> >> + * @bo: A locked and referenced buffer object.
> >> + *
> >> + * Return: Negative error code on error, User-defined
> >> positive value
> >> + * (typically, but not always, size of the processed bo) on
> >> success.
> >> + * On success, the returned values are summed by the walk
> >> and the
> >> + * walk exits when its target is met.
> >> + * 0 also indicates success, -EBUSY means this bo was
> >> skipped.
> >> + */
> >> + s64 (*process_bo)(struct ttm_lru_walk *walk,
> >> + struct ttm_buffer_object *bo);
> >> +
> >> /** @arg: Common bo LRU walk arguments. */
> >> struct ttm_lru_walk_arg arg;
> >> };
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 03/10] drm/ttm: remove ttm_lru_walk_ops
2026-07-06 12:34 ` Thomas Hellström
2026-07-06 13:05 ` Christian König
@ 2026-07-06 13:08 ` Natalie Vock
1 sibling, 0 replies; 26+ messages in thread
From: Natalie Vock @ 2026-07-06 13:08 UTC (permalink / raw)
To: Thomas Hellström, Natalie Vock, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin,
Christian Koenig, Huang Rui, Matthew Auld, Matthew Brost,
Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
On 7/6/26 14:34, Thomas Hellström wrote:
> Hi,
>
> On Fri, 2026-07-03 at 18:31 +0200, Natalie Vock wrote:
>> From: Christian König <christian.koenig@amd.com>
>>
>> It's just another layer of indirection.
>>
>> Signed-off-by: Christian König <christian.koenig@amd.com>
>> Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
>
> Personally I don't have a strong opinion on this, but the reason for
> separating out the ops is that adding function pointers in the walk
> iterator itself was once pushed back on quite forcefully by Linus when
> I tried to do that in mm/pagewalk. Claiming for various reasons the
> standard way of doing that in Linux is using a const ops struct that
> ends up in unmodifiable memory.
Ack, will drop in next revision.
Best,
Natalie
>
> /Thomas
>
>
>> ---
>> drivers/gpu/drm/ttm/ttm_bo.c | 12 ++----------
>> drivers/gpu/drm/ttm/ttm_bo_util.c | 2 +-
>> include/drm/ttm/ttm_bo.h | 34 ++++++++++++++---------------
>> -----
>> 3 files changed, 17 insertions(+), 31 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c
>> b/drivers/gpu/drm/ttm/ttm_bo.c
>> index 2b470c1746f60..1fb8c53da0362 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
>> @@ -545,10 +545,6 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk
>> *walk, struct ttm_buffer_object *
>> return lret;
>> }
>>
>> -static const struct ttm_lru_walk_ops ttm_evict_walk_ops = {
>> - .process_bo = ttm_bo_evict_cb,
>> -};
>> -
>> static int ttm_bo_evict_alloc(struct ttm_device *bdev,
>> struct ttm_resource_manager *man,
>> const struct ttm_place *place,
>> @@ -560,7 +556,7 @@ static int ttm_bo_evict_alloc(struct ttm_device
>> *bdev,
>> {
>> struct ttm_bo_evict_walk evict_walk = {
>> .walk = {
>> - .ops = &ttm_evict_walk_ops,
>> + .process_bo = ttm_bo_evict_cb,
>> .arg = {
>> .ctx = ctx,
>> .ticket = ticket,
>> @@ -1187,10 +1183,6 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk,
>> struct ttm_buffer_object *bo)
>> return ret;
>> }
>>
>> -const struct ttm_lru_walk_ops ttm_swap_ops = {
>> - .process_bo = ttm_bo_swapout_cb,
>> -};
>> -
>> /**
>> * ttm_bo_swapout() - Swap out buffer objects on the LRU list to
>> shmem.
>> * @bdev: The ttm device.
>> @@ -1209,7 +1201,7 @@ s64 ttm_bo_swapout(struct ttm_device *bdev,
>> struct ttm_operation_ctx *ctx,
>> {
>> struct ttm_bo_swapout_walk swapout_walk = {
>> .walk = {
>> - .ops = &ttm_swap_ops,
>> + .process_bo = ttm_bo_swapout_cb,
>> .arg = {
>> .ctx = ctx,
>> .trylock_only = true,
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
>> b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> index 7ed085adf1c9b..29f068944a972 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> @@ -919,7 +919,7 @@ s64 ttm_lru_walk_for_evict(struct ttm_lru_walk
>> *walk, struct ttm_device *bdev,
>> s64 lret;
>>
>> ttm_bo_lru_for_each_reserved_guarded(&cursor, man, &walk-
>>> arg, bo) {
>> - lret = walk->ops->process_bo(walk, bo);
>> + lret = walk->process_bo(walk, bo);
>> if (lret == -EBUSY || lret == -EALREADY)
>> lret = 0;
>> progress = (lret < 0) ? lret : progress + lret;
>> diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
>> index 1eae9eea5ff32..0fcd5082a7080 100644
>> --- a/include/drm/ttm/ttm_bo.h
>> +++ b/include/drm/ttm/ttm_bo.h
>> @@ -189,24 +189,6 @@ struct ttm_operation_ctx {
>> uint64_t bytes_moved;
>> };
>>
>> -struct ttm_lru_walk;
>> -
>> -/** struct ttm_lru_walk_ops - Operations for a LRU walk. */
>> -struct ttm_lru_walk_ops {
>> - /**
>> - * process_bo - Process this bo.
>> - * @walk: struct ttm_lru_walk describing the walk.
>> - * @bo: A locked and referenced buffer object.
>> - *
>> - * Return: Negative error code on error, User-defined
>> positive value
>> - * (typically, but not always, size of the processed bo) on
>> success.
>> - * On success, the returned values are summed by the walk
>> and the
>> - * walk exits when its target is met.
>> - * 0 also indicates success, -EBUSY means this bo was
>> skipped.
>> - */
>> - s64 (*process_bo)(struct ttm_lru_walk *walk, struct
>> ttm_buffer_object *bo);
>> -};
>> -
>> /**
>> * struct ttm_lru_walk_arg - Common part for the variants of BO LRU
>> walk.
>> */
>> @@ -223,8 +205,20 @@ struct ttm_lru_walk_arg {
>> * struct ttm_lru_walk - Structure describing a LRU walk.
>> */
>> struct ttm_lru_walk {
>> - /** @ops: Pointer to the ops structure. */
>> - const struct ttm_lru_walk_ops *ops;
>> + /**
>> + * process_bo - Process this bo.
>> + * @walk: struct ttm_lru_walk describing the walk.
>> + * @bo: A locked and referenced buffer object.
>> + *
>> + * Return: Negative error code on error, User-defined
>> positive value
>> + * (typically, but not always, size of the processed bo) on
>> success.
>> + * On success, the returned values are summed by the walk
>> and the
>> + * walk exits when its target is met.
>> + * 0 also indicates success, -EBUSY means this bo was
>> skipped.
>> + */
>> + s64 (*process_bo)(struct ttm_lru_walk *walk,
>> + struct ttm_buffer_object *bo);
>> +
>> /** @arg: Common bo LRU walk arguments. */
>> struct ttm_lru_walk_arg arg;
>> };
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 04/10] drm/ttm: grab BO reference before locking it
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
` (2 preceding siblings ...)
2026-07-03 16:31 ` [PATCH 03/10] drm/ttm: remove ttm_lru_walk_ops Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
2026-07-03 16:31 ` [PATCH 05/10] drm/ttm: switch to ttm_bo_lru_for_each_reserved_guarded for swapout Natalie Vock
` (5 subsequent siblings)
9 siblings, 0 replies; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
From: Christian König <christian.koenig@amd.com>
Previously we always grabbed the BO reference after taking the lock, but
that isn't necessary any more.
So avoid doing that and cleanup the handling here.
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/ttm/ttm_bo_util.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 29f068944a972..a53b25e8c2967 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -1010,14 +1010,17 @@ __ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs)
break;
bo = res->bo;
- if (ttm_lru_walk_trylock(curs, bo))
- bo_locked = true;
- else if (!arg->ticket || arg->ctx->no_wait_gpu || arg->trylock_only)
+ if (!ttm_bo_get_unless_zero(bo))
continue;
- if (!ttm_bo_get_unless_zero(bo)) {
- if (curs->needs_unlock)
- dma_resv_unlock(bo->base.resv);
+ if (ttm_lru_walk_trylock(curs, bo)) {
+ bo_locked = true;
+
+ } else if (!arg->ticket || arg->ctx->no_wait_gpu ||
+ arg->trylock_only) {
+ spin_unlock(lru_lock);
+ ttm_bo_put(bo);
+ spin_lock(lru_lock);
continue;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH 05/10] drm/ttm: switch to ttm_bo_lru_for_each_reserved_guarded for swapout
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
` (3 preceding siblings ...)
2026-07-03 16:31 ` [PATCH 04/10] drm/ttm: grab BO reference before locking it Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
2026-07-03 16:31 ` [PATCH 06/10] drm/ttm: move zombie handling into ttm_bo_evict Natalie Vock
` (4 subsequent siblings)
9 siblings, 0 replies; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
From: Christian König <christian.koenig@amd.com>
Instead of the walker wrapper use the underlying foreach. Saves us quite
a bunch of complexity and loc.
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/ttm/ttm_bo.c | 58 +++++++---------------------------------
drivers/gpu/drm/ttm/ttm_device.c | 19 ++++++++++---
include/drm/ttm/ttm_bo.h | 5 ++--
3 files changed, 27 insertions(+), 55 deletions(-)
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 1fb8c53da0362..24c52df169ac8 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1080,25 +1080,18 @@ int ttm_bo_wait_ctx(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx)
EXPORT_SYMBOL(ttm_bo_wait_ctx);
/**
- * struct ttm_bo_swapout_walk - Parameters for the swapout walk
+ * ttm_bo_swapout() - Swap out buffer objects on the LRU list to shmem.
+ * @bo: The buffer to swap out.
+ * @ctx: The ttm_operation_ctx governing the swapout operation.
+ * @gfp_flags: The gfp flags used for shmem page allocations.
+ *
+ * Return: The number of bytes actually swapped out, or negative error code
+ * on error.
*/
-struct ttm_bo_swapout_walk {
- /** @walk: The walk base parameters. */
- struct ttm_lru_walk walk;
- /** @gfp_flags: The gfp flags to use for ttm_tt_swapout() */
- gfp_t gfp_flags;
- /** @hit_low: Whether we should attempt to swap BO's with low watermark threshold */
- /** @evict_low: If we cannot swap a bo when @try_low is false (first pass) */
- bool hit_low, evict_low;
-};
-
-static s64
-ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
+s64 ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx,
+ gfp_t gfp_flags)
{
struct ttm_place place = { .mem_type = bo->resource->mem_type };
- struct ttm_bo_swapout_walk *swapout_walk =
- container_of(walk, typeof(*swapout_walk), walk);
- struct ttm_operation_ctx *ctx = walk->arg.ctx;
struct ttm_device *bdev = bo->bdev;
struct ttm_tt *tt = bo->ttm;
s64 ret;
@@ -1166,7 +1159,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
bdev->funcs->swap_notify(bo);
if (ttm_tt_is_populated(tt)) {
- ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags);
+ ret = ttm_tt_swapout(bdev, tt, gfp_flags);
if (!ret) {
spin_lock(&bdev->lru_lock);
ttm_resource_del_bulk_move_unevictable(bo->resource, bo);
@@ -1183,37 +1176,6 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
return ret;
}
-/**
- * ttm_bo_swapout() - Swap out buffer objects on the LRU list to shmem.
- * @bdev: The ttm device.
- * @ctx: The ttm_operation_ctx governing the swapout operation.
- * @man: The resource manager whose resources / buffer objects are
- * goint to be swapped out.
- * @gfp_flags: The gfp flags used for shmem page allocations.
- * @target: The desired number of pages to swap out.
- *
- * Return: The number of pages actually swapped out, or negative error code
- * on error.
- */
-s64 ttm_bo_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
- struct ttm_resource_manager *man, gfp_t gfp_flags,
- s64 target)
-{
- struct ttm_bo_swapout_walk swapout_walk = {
- .walk = {
- .process_bo = ttm_bo_swapout_cb,
- .arg = {
- .ctx = ctx,
- .trylock_only = true,
- },
- },
- .gfp_flags = gfp_flags,
- };
-
- return ttm_lru_walk_for_evict(&swapout_walk.walk, bdev, man, target);
-}
-EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_bo_swapout);
-
void ttm_bo_tt_destroy(struct ttm_buffer_object *bo)
{
if (bo->ttm == NULL)
diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c
index d3bfb9a696a74..e4188e2ee7ab1 100644
--- a/drivers/gpu/drm/ttm/ttm_device.c
+++ b/drivers/gpu/drm/ttm/ttm_device.c
@@ -171,6 +171,12 @@ int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
gfp_t gfp_flags)
{
struct ttm_resource_manager *man;
+ struct ttm_bo_lru_cursor cursor;
+ struct ttm_buffer_object *bo;
+ struct ttm_lru_walk_arg arg = {
+ .ctx = ctx,
+ .trylock_only = true
+ };
unsigned i;
s64 lret;
@@ -179,10 +185,15 @@ int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
if (!man || !man->use_tt)
continue;
- lret = ttm_bo_swapout(bdev, ctx, man, gfp_flags, 1);
- /* Can be both positive (num_pages) and negative (error) */
- if (lret)
- return lret;
+ ttm_bo_lru_for_each_reserved_guarded(&cursor, man, &arg, bo) {
+ lret = ttm_bo_swapout(bo, ctx, gfp_flags);
+ continue;
+ /* Can be both positive (num_pages) and negative (error) */
+ if (lret && lret != -EBUSY && lret != -EALREADY)
+ return lret;
+ }
+ if (IS_ERR(bo))
+ return PTR_ERR(bo);
}
return 0;
}
diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
index 0fcd5082a7080..bbed63064c9a9 100644
--- a/include/drm/ttm/ttm_bo.h
+++ b/include/drm/ttm/ttm_bo.h
@@ -408,9 +408,8 @@ void *ttm_bo_kmap_try_from_panic(struct ttm_buffer_object *bo, unsigned long pag
int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map);
void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct iosys_map *map);
int ttm_bo_mmap_obj(struct vm_area_struct *vma, struct ttm_buffer_object *bo);
-s64 ttm_bo_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
- struct ttm_resource_manager *man, gfp_t gfp_flags,
- s64 target);
+s64 ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx,
+ gfp_t gfp_flags);
void ttm_bo_pin(struct ttm_buffer_object *bo);
void ttm_bo_unpin(struct ttm_buffer_object *bo);
int ttm_bo_evict_first(struct ttm_device *bdev,
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH 06/10] drm/ttm: move zombie handling into ttm_bo_evict
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
` (4 preceding siblings ...)
2026-07-03 16:31 ` [PATCH 05/10] drm/ttm: switch to ttm_bo_lru_for_each_reserved_guarded for swapout Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
2026-07-03 16:31 ` [PATCH 07/10] drm/ttm: use ttm_bo_lru_for_each_reserved_guarded in evict_all Natalie Vock
` (3 subsequent siblings)
9 siblings, 0 replies; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
From: Christian König <christian.koenig@amd.com>
Both callers do the same thing, so we can trivially unify that.
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/ttm/ttm_bo.c | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 24c52df169ac8..54f01611ec823 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -359,6 +359,13 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo,
struct ttm_place hop;
int ret = 0;
+ if (ttm_bo_is_zombie(bo)) {
+ ret = ttm_bo_wait_ctx(bo, ctx);
+ if (!ret)
+ ttm_bo_cleanup_memtype_use(bo);
+ return ret;
+ }
+
memset(&hop, 0, sizeof(hop));
dma_resv_assert_held(bo->base.resv);
@@ -466,13 +473,7 @@ int ttm_bo_evict_first(struct ttm_device *bdev, struct ttm_resource_manager *man
if (!bo->resource || bo->resource->mem_type != mem_type)
goto out_bo_moved;
- if (ttm_bo_is_zombie(bo)) {
- ret = ttm_bo_wait_ctx(bo, ctx);
- if (!ret)
- ttm_bo_cleanup_memtype_use(bo);
- } else {
- ret = ttm_bo_evict(bo, ctx);
- }
+ ret = ttm_bo_evict(bo, ctx);
out_bo_moved:
dma_resv_unlock(bo->base.resv);
out_no_lock:
@@ -520,14 +521,7 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *
if (bo->pin_count || !bo->bdev->funcs->eviction_valuable(bo, evict_walk->place))
return 0;
- if (ttm_bo_is_zombie(bo)) {
- lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
- if (!lret)
- ttm_bo_cleanup_memtype_use(bo);
- } else {
- lret = ttm_bo_evict(bo, walk->arg.ctx);
- }
-
+ lret = ttm_bo_evict(bo, walk->arg.ctx);
if (lret)
goto out;
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH 07/10] drm/ttm: use ttm_bo_lru_for_each_reserved_guarded in evict_all
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
` (5 preceding siblings ...)
2026-07-03 16:31 ` [PATCH 06/10] drm/ttm: move zombie handling into ttm_bo_evict Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
2026-07-03 16:31 ` [PATCH 08/10] drm/xe: remove workaround for TTM internals Natalie Vock
` (2 subsequent siblings)
9 siblings, 0 replies; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
From: Christian König <christian.koenig@amd.com>
Use the for_each loop to evict all BOs of an resource manager as well.
Greately simplifying the handling and finally allows us to
remove ttm_bo_evict_first().
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/ttm/ttm_bo.c | 51 +-------------------------------------
drivers/gpu/drm/ttm/ttm_resource.c | 22 ++++++++++------
include/drm/ttm/ttm_bo.h | 1 +
3 files changed, 17 insertions(+), 57 deletions(-)
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 54f01611ec823..80933ba6aada9 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -351,8 +351,7 @@ static int ttm_bo_bounce_temp_buffer(struct ttm_buffer_object *bo,
return 0;
}
-static int ttm_bo_evict(struct ttm_buffer_object *bo,
- struct ttm_operation_ctx *ctx)
+int ttm_bo_evict(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx)
{
struct ttm_resource *evict_mem;
struct ttm_placement placement;
@@ -437,54 +436,6 @@ bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
}
EXPORT_SYMBOL(ttm_bo_eviction_valuable);
-/**
- * ttm_bo_evict_first() - Evict the first bo on the manager's LRU list.
- * @bdev: The ttm device.
- * @man: The manager whose bo to evict.
- * @ctx: The TTM operation ctx governing the eviction.
- *
- * Return: 0 if successful or the resource disappeared. Negative error code on error.
- */
-int ttm_bo_evict_first(struct ttm_device *bdev, struct ttm_resource_manager *man,
- struct ttm_operation_ctx *ctx)
-{
- struct ttm_resource_cursor cursor;
- struct ttm_buffer_object *bo;
- struct ttm_resource *res;
- unsigned int mem_type;
- int ret = 0;
-
- spin_lock(&bdev->lru_lock);
- ttm_resource_cursor_init(&cursor, man);
- res = ttm_resource_manager_first(&cursor);
- ttm_resource_cursor_fini(&cursor);
- if (!res) {
- ret = -ENOENT;
- goto out_no_ref;
- }
- bo = res->bo;
- if (!ttm_bo_get_unless_zero(bo))
- goto out_no_ref;
- mem_type = res->mem_type;
- spin_unlock(&bdev->lru_lock);
- ret = ttm_bo_reserve(bo, ctx->interruptible, ctx->no_wait_gpu, NULL);
- if (ret)
- goto out_no_lock;
- if (!bo->resource || bo->resource->mem_type != mem_type)
- goto out_bo_moved;
-
- ret = ttm_bo_evict(bo, ctx);
-out_bo_moved:
- dma_resv_unlock(bo->base.resv);
-out_no_lock:
- ttm_bo_put(bo);
- return ret;
-
-out_no_ref:
- spin_unlock(&bdev->lru_lock);
- return ret;
-}
-
/**
* struct ttm_bo_evict_walk - Parameters for the evict walk.
*/
diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c
index 154d6739256f8..cf3fc3a594388 100644
--- a/drivers/gpu/drm/ttm/ttm_resource.c
+++ b/drivers/gpu/drm/ttm/ttm_resource.c
@@ -561,17 +561,25 @@ EXPORT_SYMBOL(ttm_resource_manager_init);
int ttm_resource_manager_evict_all(struct ttm_device *bdev,
struct ttm_resource_manager *man)
{
- struct ttm_operation_ctx ctx = { };
+ struct ttm_bo_lru_cursor cursor;
+ struct ttm_buffer_object *bo;
+ struct ttm_operation_ctx ctx = {
+ .interruptible = false,
+ .no_wait_gpu = false,
+ };
+ struct ttm_lru_walk_arg arg = {
+ .ctx = &ctx,
+ .trylock_only = true
+ };
struct dma_fence *fence;
int ret, i;
- do {
- ret = ttm_bo_evict_first(bdev, man, &ctx);
+ ttm_bo_lru_for_each_reserved_guarded(&cursor, man, &arg, bo) {
+ ret = ttm_bo_evict(bo, &ctx);
+ if (ret)
+ return ret;
cond_resched();
- } while (!ret);
-
- if (ret && ret != -ENOENT)
- return ret;
+ }
ret = 0;
diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
index bbed63064c9a9..a4060e44d23d0 100644
--- a/include/drm/ttm/ttm_bo.h
+++ b/include/drm/ttm/ttm_bo.h
@@ -389,6 +389,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
void ttm_bo_fini(struct ttm_buffer_object *bo);
void ttm_bo_set_bulk_move(struct ttm_buffer_object *bo,
struct ttm_lru_bulk_move *bulk);
+int ttm_bo_evict(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx);
bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
const struct ttm_place *place);
int ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo,
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH 08/10] drm/xe: remove workaround for TTM internals
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
` (6 preceding siblings ...)
2026-07-03 16:31 ` [PATCH 07/10] drm/ttm: use ttm_bo_lru_for_each_reserved_guarded in evict_all Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
2026-07-03 16:31 ` [PATCH 09/10] drm/ttm: support using drm_exec during eviction v4 Natalie Vock
2026-07-03 16:31 ` [PATCH 10/10] drm/amdgpu: use drm_exec during BO validation Natalie Vock
9 siblings, 0 replies; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
From: Christian König <christian.koenig@amd.com>
This should no longer be necessary, TTM doesn't lock the BO without a
reference any more.
Only compile tested!
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/xe/xe_bo.c | 32 +++++---------------------------
1 file changed, 5 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 5843f850339c7..34eae56716076 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -1642,31 +1642,6 @@ static unsigned long xe_ttm_io_mem_pfn(struct ttm_buffer_object *ttm_bo,
static void __xe_bo_vunmap(struct xe_bo *bo);
-/*
- * TODO: Move this function to TTM so we don't rely on how TTM does its
- * locking, thereby abusing TTM internals.
- */
-static bool xe_ttm_bo_lock_in_destructor(struct ttm_buffer_object *ttm_bo)
-{
- struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
- bool locked;
-
- xe_assert(xe, !kref_read(&ttm_bo->base.refcount));
-
- /*
- * We can typically only race with TTM trylocking under the
- * lru_lock, which will immediately be unlocked again since
- * the ttm_bo refcount is zero at this point. So trylocking *should*
- * always succeed here, as long as we hold the lru lock.
- */
- spin_lock(&ttm_bo->bdev->lru_lock);
- locked = dma_resv_trylock(&ttm_bo->base._resv);
- spin_unlock(&ttm_bo->bdev->lru_lock);
- xe_assert(xe, locked);
-
- return locked;
-}
-
static void xe_ttm_bo_release_notify(struct ttm_buffer_object *ttm_bo)
{
struct dma_resv_iter cursor;
@@ -1680,8 +1655,11 @@ static void xe_ttm_bo_release_notify(struct ttm_buffer_object *ttm_bo)
bo = ttm_to_xe_bo(ttm_bo);
xe_assert(xe_bo_device(bo), !(bo->created && kref_read(&ttm_bo->base.refcount)));
- if (!xe_ttm_bo_lock_in_destructor(ttm_bo))
- return;
+ /*
+ * This should never fail since there are no other references to the BO
+ * any more.
+ */
+ WARN_ON(!dma_resv_trylock(ttm_bo->base.resv));
/*
* Scrub the preempt fences if any. The unbind fence is already
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH 09/10] drm/ttm: support using drm_exec during eviction v4
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
` (7 preceding siblings ...)
2026-07-03 16:31 ` [PATCH 08/10] drm/xe: remove workaround for TTM internals Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
2026-07-03 16:31 ` [PATCH 10/10] drm/amdgpu: use drm_exec during BO validation Natalie Vock
9 siblings, 0 replies; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
From: Christian König <christian.koenig@amd.com>
Allow specifying a drm_exec object in TTMs operation context which is
used to lock objects during eviction.
This allows to handle deadlocks much more gracefully and with that
avoid returning -ENOMEM on heavily contended domains.
v2: rebased on top of Thomas work
v3: rebased again
v4: rebased, fixed locks of already-reserved buffers being dropped
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/ttm/ttm_bo_util.c | 30 ++++++++++++++++++++++--------
include/drm/ttm/ttm_bo.h | 5 +++++
2 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index a53b25e8c2967..96699532817c2 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -38,6 +38,7 @@
#include <drm/ttm/ttm_tt.h>
#include <drm/drm_cache.h>
+#include <drm/drm_exec.h>
#include "ttm_bo_internal.h"
@@ -837,6 +838,8 @@ static bool ttm_lru_walk_trylock(struct ttm_bo_lru_cursor *curs,
struct ttm_operation_ctx *ctx = curs->arg->ctx;
curs->needs_unlock = false;
+ if (ctx->exec)
+ return false;
if (dma_resv_trylock(bo->base.resv)) {
curs->needs_unlock = true;
@@ -857,7 +860,9 @@ static int ttm_lru_walk_ticketlock(struct ttm_bo_lru_cursor *curs,
struct ttm_lru_walk_arg *arg = curs->arg;
int ret;
- if (arg->ctx->interruptible)
+ if (arg->ctx->exec)
+ ret = drm_exec_lock_obj_report_dup(arg->ctx->exec, &bo->base);
+ else if (arg->ctx->interruptible)
ret = dma_resv_lock_interruptible(bo->base.resv, arg->ticket);
else
ret = dma_resv_lock(bo->base.resv, arg->ticket);
@@ -871,7 +876,11 @@ static int ttm_lru_walk_ticketlock(struct ttm_bo_lru_cursor *curs,
* trylocking for this walk.
*/
arg->ticket = NULL;
- } else if (ret == -EDEADLK) {
+
+ } else if (arg->ctx->exec && arg->ctx->allow_res_evict &&
+ ret == -EALREADY) {
+ ret = 0;
+ } else if (!arg->ctx->exec && ret == -EDEADLK) {
/* Caller needs to exit the ww transaction. */
ret = -ENOSPC;
}
@@ -937,12 +946,17 @@ static void ttm_bo_lru_cursor_cleanup_bo(struct ttm_bo_lru_cursor *curs)
{
struct ttm_buffer_object *bo = curs->bo;
- if (bo) {
- if (curs->needs_unlock)
+ if (!bo)
+ return;
+
+ if (curs->needs_unlock) {
+ if (curs->arg->ctx->exec)
+ drm_exec_unlock_obj(curs->arg->ctx->exec, &bo->base);
+ else
dma_resv_unlock(bo->base.resv);
- ttm_bo_put(bo);
- curs->bo = NULL;
}
+ ttm_bo_put(bo);
+ curs->bo = NULL;
}
/**
@@ -1016,8 +1030,8 @@ __ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs)
if (ttm_lru_walk_trylock(curs, bo)) {
bo_locked = true;
- } else if (!arg->ticket || arg->ctx->no_wait_gpu ||
- arg->trylock_only) {
+ } else if ((!arg->ticket || arg->ctx->no_wait_gpu ||
+ arg->trylock_only) && !arg->ctx->exec) {
spin_unlock(lru_lock);
ttm_bo_put(bo);
spin_lock(lru_lock);
diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
index a4060e44d23d0..156444b5e85d8 100644
--- a/include/drm/ttm/ttm_bo.h
+++ b/include/drm/ttm/ttm_bo.h
@@ -187,6 +187,11 @@ struct ttm_operation_ctx {
* @bytes_moved: Statistics on how many bytes have been moved.
*/
uint64_t bytes_moved;
+ /**
+ * @exec: optional drm_exec object to use for locking BOs and
+ * tracking which are locked.
+ */
+ struct drm_exec *exec;
};
/**
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH 10/10] drm/amdgpu: use drm_exec during BO validation
2026-07-03 16:31 [PATCH 00/10] Use drm_exec to lock TTM buffers, respin Natalie Vock
` (8 preceding siblings ...)
2026-07-03 16:31 ` [PATCH 09/10] drm/ttm: support using drm_exec during eviction v4 Natalie Vock
@ 2026-07-03 16:31 ` Natalie Vock
9 siblings, 0 replies; 26+ messages in thread
From: Natalie Vock @ 2026-07-03 16:31 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Thomas Hellström, Alex Deucher
Cc: dri-devel, linux-kernel, intel-gfx, intel-xe, amd-gfx
From: Christian König <christian.koenig@amd.com>
This allows to detect deadlocks happening because of resource
constraints.
Especially submissions which want to use all of GDS doesn't result in
sporadic -ENOMEM any more.
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 80 ++++++++++++++++++----------------
1 file changed, 42 insertions(+), 38 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index c2e6495a28bc5..052d41013f7a0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -793,7 +793,7 @@ static int amdgpu_cs_bo_validate(void *param, struct amdgpu_bo *bo)
struct ttm_operation_ctx ctx = {
.interruptible = true,
.no_wait_gpu = false,
- .resv = bo->tbo.base.resv
+ .exec = &p->exec,
};
uint32_t domain;
int r;
@@ -845,7 +845,8 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
union drm_amdgpu_cs *cs)
{
struct amdgpu_fpriv *fpriv = p->filp->driver_priv;
- struct ttm_operation_ctx ctx = { true, false };
+ struct ttm_operation_ctx ctx = { .interruptible = true,
+ .exec = &p->exec };
struct amdgpu_vm *vm = &fpriv->vm;
struct amdgpu_bo_list_entry *e;
struct drm_gem_object *obj;
@@ -922,47 +923,53 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
if (unlikely(r))
goto out_free_user_pages;
}
- }
-
- amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
- struct mm_struct *usermm;
- usermm = amdgpu_ttm_tt_get_usermm(e->bo->tbo.ttm);
- if (usermm && usermm != current->mm) {
- r = -EPERM;
- goto out_free_user_pages;
- }
+ amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
+ struct mm_struct *usermm;
- if (amdgpu_ttm_tt_is_userptr(e->bo->tbo.ttm) &&
- e->user_invalidated) {
- amdgpu_bo_placement_from_domain(e->bo,
- AMDGPU_GEM_DOMAIN_CPU);
- r = ttm_bo_validate(&e->bo->tbo, &e->bo->placement,
- &ctx);
- if (r)
+ usermm = amdgpu_ttm_tt_get_usermm(e->bo->tbo.ttm);
+ if (usermm && usermm != current->mm) {
+ r = -EPERM;
goto out_free_user_pages;
+ }
- amdgpu_ttm_tt_set_user_pages(e->bo->tbo.ttm,
- e->range);
+ if (amdgpu_ttm_tt_is_userptr(e->bo->tbo.ttm) &&
+ e->user_invalidated) {
+ amdgpu_bo_placement_from_domain(e->bo,
+ AMDGPU_GEM_DOMAIN_CPU);
+ r = ttm_bo_validate(&e->bo->tbo, &e->bo->placement,
+ &ctx);
+ drm_exec_retry_on_contention(&p->exec);
+ if (r)
+ goto out_free_user_pages;
+
+ amdgpu_ttm_tt_set_user_pages(e->bo->tbo.ttm,
+ e->range);
+ }
}
- }
- amdgpu_cs_get_threshold_for_moves(p->adev, &p->bytes_moved_threshold,
- &p->bytes_moved_vis_threshold);
- p->bytes_moved = 0;
- p->bytes_moved_vis = 0;
-
- r = amdgpu_vm_validate(p->adev, &fpriv->vm, NULL,
- amdgpu_cs_bo_validate, p);
- if (r) {
- drm_err(adev_to_drm(p->adev), "amdgpu_vm_validate() failed.\n");
- goto out_free_user_pages;
- }
+ amdgpu_cs_get_threshold_for_moves(p->adev, &p->bytes_moved_threshold,
+ &p->bytes_moved_vis_threshold);
+ p->bytes_moved = 0;
+ p->bytes_moved_vis = 0;
- drm_exec_for_each_locked_object(&p->exec, obj) {
- r = amdgpu_cs_bo_validate(p, gem_to_amdgpu_bo(obj));
- if (unlikely(r))
+ r = amdgpu_vm_validate(p->adev, &fpriv->vm, NULL,
+ amdgpu_cs_bo_validate, p);
+ drm_exec_retry_on_contention(&p->exec);
+ if (r) {
+ drm_err(adev_to_drm(p->adev), "amdgpu_vm_validate() failed.\n");
goto out_free_user_pages;
+ }
+
+ drm_exec_for_each_locked_object(&p->exec, obj) {
+ r = amdgpu_cs_bo_validate(p, gem_to_amdgpu_bo(obj));
+ drm_exec_retry_on_contention(&p->exec);
+ if (unlikely(r))
+ goto out_free_user_pages;
+ }
+
+ amdgpu_cs_report_moved_bytes(p->adev, p->bytes_moved,
+ p->bytes_moved_vis);
}
if (p->uf_bo) {
@@ -973,9 +980,6 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
p->gang_leader->uf_addr += amdgpu_bo_gpu_offset(p->uf_bo);
}
- amdgpu_cs_report_moved_bytes(p->adev, p->bytes_moved,
- p->bytes_moved_vis);
-
for (i = 0; i < p->gang_size; ++i)
amdgpu_job_set_resources(p->jobs[i], p->bo_list->gds_obj,
p->bo_list->gws_obj,
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread