On 9/10/26 05:02, Matthew Brost wrote:
> 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?

Good point, I just though better save than sorry here but that is indeed not 
needed.

Any other comments/reviews on that series? If not I'm going to send it out once 
more to a wider audience.

Thanks,
Christian.

> 
> 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
>>

Reply via email to