On Thu, Sep 03, 2026 at 03:28:05PM +0200, Christian König wrote:
> Remove the embedded dma_resv (_resv) from struct drm_gem_object and use
> dynamically allocated reservations instead. This change aligns with the
> new reference counted dma_resv infrastructure and reduces the size of
> drm_gem_object.
>
> In drm_gem_private_object_init(), when no external resv is provided,
> allocate a new dma_resv using dma_resv_alloc() instead of initializing
> the embedded _resv field. This allows proper error handling if the
> allocation fails, returning -ENOMEM to the caller.
>
> The corresponding cleanup in drm_gem_private_object_fini() is simplified
> to only put the resv pointer, removing the redundant put on _resv.
>
> For TTM buffer objects, add an individual_resv field to track the
> dynamically allocated reservation object for proper cleanup during
> destruction. This ensures we can distinguish between shared and
> individual reservations when freeing resources.
>
> This change builds on the previous work to make drm_gem_private_object_init()
> return an error code, now utilizing that capability to handle allocation
> failures properly.
>
> Signed-off-by: Christian König <[email protected]>
> Assisted-by: Claude:Sonnet 4
> ---
> .../gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 12 ++---
> drivers/gpu/drm/drm_gem.c | 9 ++--
> drivers/gpu/drm/i915/gem/i915_gem_object.c | 1 -
> drivers/gpu/drm/nouveau/nouveau_bo.c | 3 --
> .../gpu/drm/ttm/tests/ttm_bo_validate_test.c | 2 +-
> drivers/gpu/drm/ttm/ttm_bo.c | 26 ++++++-----
> drivers/gpu/drm/ttm/ttm_bo_util.c | 44 +++++++++++++------
> drivers/gpu/drm/xe/xe_bo.c | 8 ++--
> include/drm/drm_gem.h | 9 ----
> include/drm/ttm/ttm_bo.h | 2 +
> 11 files changed, 65 insertions(+), 53 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
> index 20831dbebc31..a168083edab2 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
> @@ -397,7 +397,7 @@ static int amdgpu_amdkfd_remove_eviction_fence(struct
> amdgpu_bo *bo,
> */
> void amdgpu_amdkfd_remove_all_eviction_fences(struct amdgpu_bo *bo)
> {
> - struct dma_resv *resv = &bo->tbo.base._resv;
> + struct dma_resv *resv = bo->tbo.individual_resv;
> struct dma_fence *fence, *stub;
> struct dma_resv_iter cursor;
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
> index 6c5182d54f7d..026821f6da7c 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
> @@ -1319,7 +1319,7 @@ void amdgpu_bo_release_notify(struct ttm_buffer_object
> *bo)
> * So when this locking here fails something is wrong with the reference
> * counting.
> */
> - if (WARN_ON_ONCE(!dma_resv_trylock(&bo->base._resv)))
> + if (WARN_ON_ONCE(!dma_resv_trylock(bo->individual_resv)))
> return;
>
> amdgpu_amdkfd_remove_all_eviction_fences(abo);
> @@ -1329,22 +1329,22 @@ void amdgpu_bo_release_notify(struct
> ttm_buffer_object *bo)
> adev->in_suspend || drm_dev_is_unplugged(adev_to_drm(adev)))
> goto out;
>
> - r = dma_resv_reserve_fences(&bo->base._resv, 1);
> + r = dma_resv_reserve_fences(bo->individual_resv, 1);
> if (r)
> goto out;
>
> r = amdgpu_ttm_clear_buffer(amdgpu_ttm_next_clear_entity(adev),
> - abo, &bo->base._resv, &fence,
> - false,
> AMDGPU_KERNEL_JOB_ID_CLEAR_ON_RELEASE);
> + abo, bo->individual_resv, &fence, false,
> + AMDGPU_KERNEL_JOB_ID_CLEAR_ON_RELEASE);
> if (WARN_ON(r))
> goto out;
>
> amdgpu_vram_mgr_set_cleared(bo->resource);
> - dma_resv_add_fence(&bo->base._resv, fence, DMA_RESV_USAGE_KERNEL);
> + dma_resv_add_fence(bo->individual_resv, fence, DMA_RESV_USAGE_KERNEL);
> dma_fence_put(fence);
>
> out:
> - dma_resv_unlock(&bo->base._resv);
> + dma_resv_unlock(bo->individual_resv);
> }
>
> /**
> diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
> index d2ce18bbddea..480391df556d 100644
> --- a/drivers/gpu/drm/drm_gem.c
> +++ b/drivers/gpu/drm/drm_gem.c
> @@ -235,9 +235,11 @@ int drm_gem_private_object_init(struct drm_device *dev,
> obj->handle_count = 0;
> obj->size = size;
> mutex_init(&obj->gpuva.lock);
> - dma_resv_init(&obj->_resv);
> - if (!obj->resv)
> - obj->resv = dma_resv_get(&obj->_resv);
> + if (!obj->resv) {
> + obj->resv = dma_resv_alloc();
> + if (!obj->resv)
> + return -ENOMEM;
> + }
>
> drm_gem_gpuva_init(obj);
>
> @@ -259,7 +261,6 @@ void drm_gem_private_object_fini(struct drm_gem_object
> *obj)
> WARN_ON(obj->dma_buf);
>
> dma_resv_put(obj->resv);
> - dma_resv_put(&obj->_resv);
> mutex_destroy(&obj->gpuva.lock);
> }
> EXPORT_SYMBOL(drm_gem_private_object_fini);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c
> b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> index 3f37b0cbb70f..1c73f15d0315 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> @@ -90,7 +90,6 @@ struct drm_i915_gem_object *i915_gem_object_alloc(void)
> void i915_gem_object_free(struct drm_i915_gem_object *obj)
> {
> dma_resv_put(obj->base.resv);
> - dma_resv_put(&obj->base._resv);
> return kmem_cache_free(slab_objects, obj);
> }
>
> diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c
> b/drivers/gpu/drm/nouveau/nouveau_bo.c
> index 67c9d32f4f27..65e1f0c2f984 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_bo.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
> @@ -159,8 +159,6 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
> drm_gem_object_put(nvbo->r_obj);
>
> drm_gem_object_release(&bo->base);
> - } else {
> - dma_resv_put(&bo->base._resv);
> }
>
> kfree(nvbo);
> @@ -385,7 +383,6 @@ nouveau_bo_new(struct nouveau_cli *cli, u64 size, int
> align,
> return PTR_ERR(nvbo);
>
> nvbo->bo.base.size = size;
> - dma_resv_init(&nvbo->bo.base._resv);
> drm_vma_node_reset(&nvbo->bo.base.vma_node);
>
> /* This must be called before ttm_bo_init_reserved(). Subsequent
> 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 e0ecadccccf8..4559352e7115 100644
> --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> @@ -640,7 +640,7 @@ static void
> ttm_bo_validate_no_placement_not_signaled(struct kunit *test)
> KUNIT_EXPECT_EQ(test, ctx.bytes_moved, 0);
>
> if (bo->type != ttm_bo_type_sg)
> - KUNIT_ASSERT_PTR_EQ(test, bo->base.resv, &bo->base._resv);
> + KUNIT_ASSERT_PTR_EQ(test, bo->base.resv, &bo->individual_resv);
>
> /* Make sure we have an idle object at this point */
> dma_resv_wait_timeout(bo->base.resv, usage, false,
> MAX_SCHEDULE_TIMEOUT);
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
> index 999c24251fb7..ecb5f55a0dd1 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -193,13 +193,13 @@ static int ttm_bo_individualize_resv(struct
> ttm_buffer_object *bo)
> {
> int r;
>
> - if (bo->base.resv == &bo->base._resv)
> + if (bo->base.resv == bo->individual_resv)
> return 0;
>
> - BUG_ON(!dma_resv_trylock(&bo->base._resv));
> + BUG_ON(!dma_resv_trylock(bo->individual_resv));
>
> - r = dma_resv_copy_fences(&bo->base._resv, bo->base.resv);
> - dma_resv_unlock(&bo->base._resv);
> + r = dma_resv_copy_fences(bo->individual_resv, bo->base.resv);
> + dma_resv_unlock(bo->individual_resv);
> if (r)
> return r;
>
> @@ -209,7 +209,7 @@ static int ttm_bo_individualize_resv(struct
> ttm_buffer_object *bo)
> * the resv object while holding the lru_lock.
> */
> spin_lock(&bo->bdev->lru_lock);
> - drm_gem_object_set_resv(&bo->base, &bo->base._resv);
> + drm_gem_object_set_resv(&bo->base, bo->individual_resv);
> spin_unlock(&bo->bdev->lru_lock);
> }
>
> @@ -218,7 +218,7 @@ static int ttm_bo_individualize_resv(struct
> ttm_buffer_object *bo)
>
> static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo)
> {
> - struct dma_resv *resv = &bo->base._resv;
> + struct dma_resv *resv = bo->individual_resv;
> struct dma_resv_iter cursor;
> struct dma_fence *fence;
>
> @@ -238,8 +238,8 @@ static void ttm_bo_delayed_delete(struct work_struct
> *work)
>
> bo = container_of(work, typeof(*bo), delayed_delete);
>
> - dma_resv_wait_timeout(&bo->base._resv, DMA_RESV_USAGE_BOOKKEEP, false,
> - MAX_SCHEDULE_TIMEOUT);
> + dma_resv_wait_timeout(bo->individual_resv, DMA_RESV_USAGE_BOOKKEEP,
> + false, MAX_SCHEDULE_TIMEOUT);
> dma_resv_lock(bo->base.resv, NULL);
> ttm_bo_cleanup_memtype_use(bo);
> dma_resv_unlock(bo->base.resv);
> @@ -273,7 +273,7 @@ static void ttm_bo_release(struct kref *kref)
> 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,
> + if (!dma_resv_test_signaled(bo->individual_resv,
> DMA_RESV_USAGE_BOOKKEEP) ||
> (want_init_on_free() && (bo->ttm != NULL)) ||
> bo->type == ttm_bo_type_sg ||
> @@ -316,6 +316,8 @@ static void ttm_bo_release(struct kref *kref)
> }
>
> atomic_dec(&ttm_glob.bo_count);
> + dma_resv_put(bo->individual_resv);
> + bo->individual_resv = NULL;
individual_resv is set to NULL.
> bo->destroy(bo);
vfunc ->destroy() called and this the only call site.
> }
>
> @@ -1197,7 +1199,11 @@ int ttm_bo_init_reserved(struct ttm_device *bdev,
> struct ttm_buffer_object *bo,
> bo->pin_count = 0;
> bo->sg = sg;
> bo->bulk_move = NULL;
> - drm_gem_object_set_resv(&bo->base, resv ?: &bo->base._resv);
> +
> + /* Save the original resv object before overwriting it */
> + bo->individual_resv = dma_resv_get(bo->base.resv);
> + if (resv)
> + drm_gem_object_set_resv(&bo->base, resv);
> atomic_inc(&ttm_glob.bo_count);
>
> /*
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
> b/drivers/gpu/drm/ttm/ttm_bo_util.c
> index e080ce60933e..e39448a5015e 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> @@ -208,7 +208,7 @@ static void ttm_transfered_destroy(struct
> ttm_buffer_object *bo)
This is the vfunc ->destroy() for transfer destroys.
>
> fbo = container_of(bo, struct ttm_transfer_obj, base);
> dma_resv_put(fbo->base.base.resv);
> - dma_resv_put(&fbo->base.base._resv);
> + dma_resv_put(fbo->base.individual_resv);
fbo->base.individual_resv should always be NULL per above comments.
Not functionally incorrect as dma_resv_put has a NULL check, but this
confused me in my analysis of the refcounting.
Can we either delete this or WARN_ON_ONCE(fbo->base.individual_resv) to
future proof this?
Otherwise Xe, TTM, and DRM changes LGTM.
Matt
> ttm_bo_put(fbo->bo);
> kfree(fbo);
> }
> @@ -238,12 +238,23 @@ static int ttm_buffer_object_transfer(struct
> ttm_buffer_object *bo,
> if (!fbo)
> return -ENOMEM;
>
> - fbo->base = *bo;
>
> /**
> * Fix up members that we shouldn't copy directly:
> * TODO: Explicit member copy would probably be better here.
> */
> + fbo->base = *bo;
> +
> + fbo->base.individual_resv = dma_resv_alloc();
> + if (!fbo->base.individual_resv) {
> + ret = -ENOMEM;
> + goto error_free;
> + }
> +
> + if (bo->type != ttm_bo_type_sg)
> + fbo->base.base.resv = dma_resv_get(fbo->base.individual_resv);
> + else
> + dma_resv_get(fbo->base.base.resv);
>
> atomic_inc(&ttm_glob.bo_count);
> drm_vma_node_reset(&fbo->base.base.vma_node);
> @@ -251,19 +262,16 @@ static int ttm_buffer_object_transfer(struct
> ttm_buffer_object *bo,
> kref_init(&fbo->base.kref);
> fbo->base.destroy = &ttm_transfered_destroy;
> fbo->base.pin_count = 0;
> - if (bo->type != ttm_bo_type_sg)
> - fbo->base.base.resv = &fbo->base.base._resv;
>
> - dma_resv_init(&fbo->base.base._resv);
> fbo->base.base.dev = NULL;
> - ret = dma_resv_trylock(&fbo->base.base._resv);
> + ret = dma_resv_trylock(fbo->base.individual_resv);
> WARN_ON(!ret);
>
> - ret = dma_resv_reserve_fences(&fbo->base.base._resv,
> TTM_NUM_MOVE_FENCES);
> + ret = dma_resv_reserve_fences(fbo->base.individual_resv,
> + TTM_NUM_MOVE_FENCES);
> if (ret) {
> - dma_resv_unlock(&fbo->base.base._resv);
> - kfree(fbo);
> - return ret;
> + dma_resv_unlock(fbo->base.individual_resv);
> + goto error_unref;
> }
>
> if (fbo->base.resource) {
> @@ -281,6 +289,14 @@ static int ttm_buffer_object_transfer(struct
> ttm_buffer_object *bo,
>
> *new_obj = &fbo->base;
> return 0;
> +
> +error_unref:
> + dma_resv_put(fbo->base.individual_resv);
> + dma_resv_put(fbo->base.base.resv);
> +
> +error_free:
> + kfree(fbo);
> + return ret;
> }
>
> /**
> @@ -618,7 +634,7 @@ static int ttm_bo_move_to_ghost(struct ttm_buffer_object
> *bo,
> if (ret)
> return ret;
>
> - dma_resv_add_fence(&ghost_obj->base._resv, fence,
> + dma_resv_add_fence(ghost_obj->individual_resv, fence,
> DMA_RESV_USAGE_KERNEL);
>
> /**
> @@ -632,7 +648,7 @@ static int ttm_bo_move_to_ghost(struct ttm_buffer_object
> *bo,
> else
> bo->ttm = NULL;
>
> - dma_resv_unlock(&ghost_obj->base._resv);
> + dma_resv_unlock(ghost_obj->individual_resv);
> ttm_bo_put(ghost_obj);
> return 0;
> }
> @@ -802,14 +818,14 @@ int ttm_bo_pipeline_gutting(struct ttm_buffer_object
> *bo)
> if (ret)
> goto error_destroy_tt;
>
> - ret = dma_resv_copy_fences(&ghost->base._resv, bo->base.resv);
> + ret = dma_resv_copy_fences(ghost->individual_resv, bo->base.resv);
> /* Last resort, wait for the BO to be idle when we are OOM */
> if (ret) {
> dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP,
> false, MAX_SCHEDULE_TIMEOUT);
> }
>
> - dma_resv_unlock(&ghost->base._resv);
> + dma_resv_unlock(ghost->individual_resv);
> ttm_bo_put(ghost);
> bo->ttm = ttm;
> return 0;
> diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
> index 6df7ef07ec0b..422fe4bddf00 100644
> --- a/drivers/gpu/drm/xe/xe_bo.c
> +++ b/drivers/gpu/drm/xe/xe_bo.c
> @@ -1660,7 +1660,7 @@ static bool xe_ttm_bo_lock_in_destructor(struct
> ttm_buffer_object *ttm_bo)
> * 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);
> + locked = dma_resv_trylock(ttm_bo->individual_resv);
> spin_unlock(&ttm_bo->bdev->lru_lock);
> xe_assert(xe, locked);
>
> @@ -1689,14 +1689,14 @@ static void xe_ttm_bo_release_notify(struct
> ttm_buffer_object *ttm_bo)
> * TODO: Don't do this for external bos once we scrub them after
> * unbind.
> */
> - dma_resv_for_each_fence(&cursor, &ttm_bo->base._resv,
> + dma_resv_for_each_fence(&cursor, ttm_bo->individual_resv,
> DMA_RESV_USAGE_BOOKKEEP, fence) {
> if (xe_fence_is_xe_preempt(fence) &&
> !dma_fence_is_signaled(fence)) {
> if (!replacement)
> replacement = dma_fence_get_stub();
>
> - dma_resv_replace_fences(&ttm_bo->base._resv,
> + dma_resv_replace_fences(ttm_bo->individual_resv,
> fence->context,
> replacement,
> DMA_RESV_USAGE_BOOKKEEP);
> @@ -1704,7 +1704,7 @@ static void xe_ttm_bo_release_notify(struct
> ttm_buffer_object *ttm_bo)
> }
> dma_fence_put(replacement);
>
> - dma_resv_unlock(&ttm_bo->base._resv);
> + dma_resv_unlock(ttm_bo->individual_resv);
> }
>
> static void xe_ttm_bo_delete_mem_notify(struct ttm_buffer_object *ttm_bo)
> diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h
> index f889232e8525..8aed680bfca2 100644
> --- a/include/drm/drm_gem.h
> +++ b/include/drm/drm_gem.h
> @@ -385,15 +385,6 @@ struct drm_gem_object {
> */
> struct dma_resv *resv;
>
> - /**
> - * @_resv:
> - *
> - * A reservation object for this GEM object.
> - *
> - * This is unused for imported GEM objects.
> - */
> - struct dma_resv _resv;
> -
> /**
> * @gpuva: Fields used by GPUVM to manage mappings pointing to this GEM
> object.
> *
> diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
> index 32791c4db2a9..f132caa9647f 100644
> --- a/include/drm/ttm/ttm_bo.h
> +++ b/include/drm/ttm/ttm_bo.h
> @@ -77,6 +77,7 @@ enum ttm_bo_type {
> * @bdev: Pointer to the buffer object device structure.
> * @type: The bo type.
> * @page_alignment: Page alignment.
> + * @individual_resv: Individual resv for destruction
> * @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.
> @@ -107,6 +108,7 @@ struct ttm_buffer_object {
> struct ttm_device *bdev;
> enum ttm_bo_type type;
> uint32_t page_alignment;
> + struct dma_resv *individual_resv;
> void (*destroy) (struct ttm_buffer_object *);
>
> /*
> --
> 2.43.0
>