[PATCH 4/6] drm/ttm: rework BO delayed delete.

Pan, Xinhui Xinhui.Pan at amd.com
Tue Feb 11 05:43:30 UTC 2020


comments inline

> 2020年2月11日 13:26,Pan, Xinhui <Xinhui.Pan at amd.com> 写道:
> 
> comments inline.
> [xh]
> 
> 
>> 2020年2月10日 23:09,Christian König <ckoenig.leichtzumerken at gmail.com> 写道:
>> 
>> This patch reworks the whole delayed deletion of BOs which aren't idle.
>> 
>> Instead of having two counters for the BO structure we resurrect the BO
>> when we find that a deleted BO is not idle yet.
>> 
>> This has many advantages, especially that we don't need to
>> increment/decrement the BOs reference counter any more when it
>> moves on the LRUs.
>> 
>> Signed-off-by: Christian König <christian.koenig at amd.com>
>> ---
>> drivers/gpu/drm/ttm/ttm_bo.c      | 217 +++++++++++++-----------------
>> drivers/gpu/drm/ttm/ttm_bo_util.c |   1 -
>> include/drm/ttm/ttm_bo_api.h      |  11 +-
>> 3 files changed, 97 insertions(+), 132 deletions(-)
>> 
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
>> index e12fc2c2d165..d0624685f5d2 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
>> @@ -145,26 +145,6 @@ static inline uint32_t ttm_bo_type_flags(unsigned type)
>> 	return 1 << (type);
>> }
>> 
>> -static void ttm_bo_release_list(struct kref *list_kref)
>> -{
>> -	struct ttm_buffer_object *bo =
>> -	    container_of(list_kref, struct ttm_buffer_object, list_kref);
>> -	size_t acc_size = bo->acc_size;
>> -
>> -	BUG_ON(kref_read(&bo->list_kref));
>> -	BUG_ON(kref_read(&bo->kref));
>> -	BUG_ON(bo->mem.mm_node != NULL);
>> -	BUG_ON(!list_empty(&bo->lru));
>> -	BUG_ON(!list_empty(&bo->ddestroy));
>> -	ttm_tt_destroy(bo->ttm);
>> -	atomic_dec(&ttm_bo_glob.bo_count);
>> -	dma_fence_put(bo->moving);
>> -	if (!ttm_bo_uses_embedded_gem_object(bo))
>> -		dma_resv_fini(&bo->base._resv);
>> -	bo->destroy(bo);
>> -	ttm_mem_global_free(&ttm_mem_glob, acc_size);
>> -}
>> -
>> static void ttm_bo_add_mem_to_lru(struct ttm_buffer_object *bo,
>> 				  struct ttm_mem_reg *mem)
>> {
>> @@ -181,21 +161,14 @@ static void ttm_bo_add_mem_to_lru(struct ttm_buffer_object *bo,
>> 
>> 	man = &bdev->man[mem->mem_type];
>> 	list_add_tail(&bo->lru, &man->lru[bo->priority]);
>> -	kref_get(&bo->list_kref);
>> 
>> 	if (!(man->flags & TTM_MEMTYPE_FLAG_FIXED) && bo->ttm &&
>> 	    !(bo->ttm->page_flags & (TTM_PAGE_FLAG_SG |
>> 				     TTM_PAGE_FLAG_SWAPPED))) {
>> 		list_add_tail(&bo->swap, &ttm_bo_glob.swap_lru[bo->priority]);
>> -		kref_get(&bo->list_kref);
>> 	}
>> }
>> 
>> -static void ttm_bo_ref_bug(struct kref *list_kref)
>> -{
>> -	BUG();
>> -}
>> -
>> static void ttm_bo_del_from_lru(struct ttm_buffer_object *bo)
>> {
>> 	struct ttm_bo_device *bdev = bo->bdev;
>> @@ -203,12 +176,10 @@ static void ttm_bo_del_from_lru(struct ttm_buffer_object *bo)
>> 
>> 	if (!list_empty(&bo->swap)) {
>> 		list_del_init(&bo->swap);
>> -		kref_put(&bo->list_kref, ttm_bo_ref_bug);
>> 		notify = true;
>> 	}
>> 	if (!list_empty(&bo->lru)) {
>> 		list_del_init(&bo->lru);
>> -		kref_put(&bo->list_kref, ttm_bo_ref_bug);
>> 		notify = true;
>> 	}
>> 
>> @@ -421,8 +392,7 @@ static int ttm_bo_individualize_resv(struct ttm_buffer_object *bo)
>> 	BUG_ON(!dma_resv_trylock(&bo->base._resv));
>> 
>> 	r = dma_resv_copy_fences(&bo->base._resv, bo->base.resv);
>> -	if (r)
>> -		dma_resv_unlock(&bo->base._resv);
>> +	dma_resv_unlock(&bo->base._resv);
>> 
>> 	return r;
>> }
>> @@ -449,68 +419,10 @@ static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo)
>> 	rcu_read_unlock();
>> }
>> 
>> -static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)
>> -{
>> -	struct ttm_bo_device *bdev = bo->bdev;
>> -	int ret;
>> -
>> -	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_rcu(bo->base.resv, true, false,
>> -						    30 * HZ);
>> -		spin_lock(&ttm_bo_glob.lru_lock);
>> -		goto error;
>> -	}
>> -
>> -	spin_lock(&ttm_bo_glob.lru_lock);
>> -	ret = dma_resv_trylock(bo->base.resv) ? 0 : -EBUSY;
>> -	if (!ret) {
>> -		if (dma_resv_test_signaled_rcu(&bo->base._resv, true)) {
>> -			ttm_bo_del_from_lru(bo);
>> -			spin_unlock(&ttm_bo_glob.lru_lock);
>> -			if (bo->base.resv != &bo->base._resv)
>> -				dma_resv_unlock(&bo->base._resv);
>> -
>> -			ttm_bo_cleanup_memtype_use(bo);
>> -			dma_resv_unlock(bo->base.resv);
>> -			return;
>> -		}
>> -
>> -		ttm_bo_flush_all_fences(bo);
>> -
>> -		/*
>> -		 * Make NO_EVICT bos immediately available to
>> -		 * shrinkers, now that they are queued for
>> -		 * destruction.
>> -		 */
>> -		if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) {
>> -			bo->mem.placement &= ~TTM_PL_FLAG_NO_EVICT;
>> -			ttm_bo_move_to_lru_tail(bo, NULL);
>> -		}
>> -
>> -		dma_resv_unlock(bo->base.resv);
>> -	}
>> -	if (bo->base.resv != &bo->base._resv) {
>> -		ttm_bo_flush_all_fences(bo);
>> -		dma_resv_unlock(&bo->base._resv);
>> -	}
>> -
>> -error:
>> -	kref_get(&bo->list_kref);
>> -	list_add_tail(&bo->ddestroy, &bdev->ddestroy);
>> -	spin_unlock(&ttm_bo_glob.lru_lock);
>> -
>> -	schedule_delayed_work(&bdev->wq,
>> -			      ((HZ / 100) < 1) ? 1 : HZ / 100);
>> -}
>> -
>> /**
>> * function ttm_bo_cleanup_refs
>> - * If bo idle, remove from delayed- and lru lists, and unref.
>> - * If not idle, do nothing.
>> + * If bo idle, remove from lru lists, and unref.
>> + * If not idle, block if possible.
>> *
>> * Must be called with lru_lock and reservation held, this function
>> * will drop the lru lock and optionally the reservation lock before returning.
>> @@ -572,14 +484,14 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo,
>> 
>> 	ttm_bo_del_from_lru(bo);
>> 	list_del_init(&bo->ddestroy);
>> -	kref_put(&bo->list_kref, ttm_bo_ref_bug);
>> -
>> 	spin_unlock(&ttm_bo_glob.lru_lock);
>> 	ttm_bo_cleanup_memtype_use(bo);
>> 
>> 	if (unlock_resv)
>> 		dma_resv_unlock(bo->base.resv);
>> 
>> +	ttm_bo_put(bo);
>> +
>> 	return 0;
>> }
>> 
>> @@ -601,8 +513,9 @@ static bool ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all)
>> 
>> 		bo = list_first_entry(&bdev->ddestroy, struct ttm_buffer_object,
>> 				      ddestroy);
>> -		kref_get(&bo->list_kref);
>> 		list_move_tail(&bo->ddestroy, &removed);
>> +		if (!ttm_bo_get_unless_zero(bo))
>> +			continue;
>> 
>> 		if (remove_all || bo->base.resv != &bo->base._resv) {
>> 			spin_unlock(&glob->lru_lock);
>> @@ -617,7 +530,7 @@ static bool ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all)
>> 			spin_unlock(&glob->lru_lock);
>> 		}
>> 
>> -		kref_put(&bo->list_kref, ttm_bo_release_list);
>> +		ttm_bo_put(bo);
>> 		spin_lock(&glob->lru_lock);
>> 	}
>> 	list_splice_tail(&removed, &bdev->ddestroy);
>> @@ -643,16 +556,68 @@ static void ttm_bo_release(struct kref *kref)
>> 	    container_of(kref, struct ttm_buffer_object, kref);
>> 	struct ttm_bo_device *bdev = bo->bdev;
>> 	struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type];
>> +	size_t acc_size = bo->acc_size;
>> +	int ret;
>> 
>> -	if (bo->bdev->driver->release_notify)
>> -		bo->bdev->driver->release_notify(bo);
>> +	if (!bo->deleted) {
>> +		if (bo->bdev->driver->release_notify)
>> +			bo->bdev->driver->release_notify(bo);
>> 
>> -	drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node);
>> -	ttm_mem_io_lock(man, false);
>> -	ttm_mem_io_free_vm(bo);
>> -	ttm_mem_io_unlock(man);
>> -	ttm_bo_cleanup_refs_or_queue(bo);
>> -	kref_put(&bo->list_kref, ttm_bo_release_list);
>> +		drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node);
>> +		ttm_mem_io_lock(man, false);
>> +		ttm_mem_io_free_vm(bo);
>> +		ttm_mem_io_unlock(man);
>> +
>> +		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_rcu(bo->base.resv, true, false,
>> +						  30 * HZ);
>> +		}
>> +	}
>> +
>> +	if (!dma_resv_test_signaled_rcu(bo->base.resv, true)) {
>> +		/* The BO is not idle, resurrect it for delayed destroy */
>> +		ttm_bo_flush_all_fences(bo);
>> +		bo->deleted = true;
>> +
>> +		/*
>> +		 * Make NO_EVICT bos immediately available to
>> +		 * shrinkers, now that they are queued for
>> +		 * destruction.
>> +		 */
>> +		if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) {
>> +			bo->mem.placement &= ~TTM_PL_FLAG_NO_EVICT;
>> +			ttm_bo_move_to_lru_tail(bo, NULL);
> 
> [xh] this should be under lru lock.
> 
>> +		}
>> +
>> +		spin_lock(&ttm_bo_glob.lru_lock);
>> +		kref_init(&bo->kref);
>> +		list_add_tail(&bo->ddestroy, &bdev->ddestroy);
>> +		spin_unlock(&ttm_bo_glob.lru_lock);
>> +
>> +		schedule_delayed_work(&bdev->wq,
>> +				      ((HZ / 100) < 1) ? 1 : HZ / 100);
>> +		return;
>> +	}
>> +
>> +	spin_lock(&ttm_bo_glob.lru_lock);
>> +	ttm_bo_del_from_lru(bo);
>> +	list_del(&bo->ddestroy);
>> +	spin_unlock(&ttm_bo_glob.lru_lock);
>> +
>> +	ttm_bo_cleanup_memtype_use(bo);
>> +
>> +	BUG_ON(bo->mem.mm_node != NULL);
>> +	ttm_tt_destroy(bo->ttm);
> 
> [xh] already destroy it in ttm_bo_cleanup_memtype_use.
> 
> 
>> +	atomic_dec(&ttm_bo_glob.bo_count);
>> +	dma_fence_put(bo->moving);
>> +	if (!ttm_bo_uses_embedded_gem_object(bo))
>> +		dma_resv_fini(&bo->base._resv);
>> +	bo->destroy(bo);
>> +	ttm_mem_global_free(&ttm_mem_glob, acc_size);
>> }
>> 
>> void ttm_bo_put(struct ttm_buffer_object *bo)
>> @@ -755,8 +720,7 @@ static bool ttm_bo_evict_swapout_allowable(struct ttm_buffer_object *bo,
>> 
>> 	if (bo->base.resv == ctx->resv) {
>> 		dma_resv_assert_held(bo->base.resv);
>> -		if (ctx->flags & TTM_OPT_FLAG_ALLOW_RES_EVICT
>> -		    || !list_empty(&bo->ddestroy))
>> +		if (ctx->flags & TTM_OPT_FLAG_ALLOW_RES_EVICT || bo->deleted)
>> 			ret = true;
>> 		*locked = false;
>> 		if (busy)
>> @@ -837,6 +801,11 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
>> 					dma_resv_unlock(bo->base.resv);
>> 				continue;
>> 			}
>> +			if (!ttm_bo_get_unless_zero(bo)) {
>> +				if (locked)
>> +					dma_resv_unlock(bo->base.resv);
>> +				continue;
>> +			}
>> 			break;
>> 		}
>> 
>> @@ -848,21 +817,19 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
>> 	}
>> 
>> 	if (!bo) {
>> -		if (busy_bo)
>> -			kref_get(&busy_bo->list_kref);
>> +		if (busy_bo && !ttm_bo_get_unless_zero(busy_bo))
>> +			busy_bo = NULL;
>> 		spin_unlock(&ttm_bo_glob.lru_lock);
>> 		ret = ttm_mem_evict_wait_busy(busy_bo, ctx, ticket);
>> 		if (busy_bo)
>> -			kref_put(&busy_bo->list_kref, ttm_bo_release_list);
>> +			ttm_bo_put(busy_bo);
>> 		return ret;
>> 	}
>> 
>> -	kref_get(&bo->list_kref);
>> -
>> -	if (!list_empty(&bo->ddestroy)) {
>> +	if (bo->deleted) {
>> 		ret = ttm_bo_cleanup_refs(bo, ctx->interruptible,
>> 					  ctx->no_wait_gpu, locked);
>> -		kref_put(&bo->list_kref, ttm_bo_release_list);
>> +		ttm_bo_put(bo);
>> 		return ret;
>> 	}
>> 
>> @@ -872,7 +839,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
>> 	if (locked)
>> 		ttm_bo_unreserve(bo);
>> 
>> -	kref_put(&bo->list_kref, ttm_bo_release_list);
>> +	ttm_bo_put(bo);
>> 	return ret;
>> }
>> 
>> @@ -1284,7 +1251,6 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev,
>> 	bo->destroy = destroy ? destroy : ttm_bo_default_destroy;
>> 
>> 	kref_init(&bo->kref);
>> -	kref_init(&bo->list_kref);
>> 	INIT_LIST_HEAD(&bo->lru);
>> 	INIT_LIST_HEAD(&bo->ddestroy);
>> 	INIT_LIST_HEAD(&bo->swap);
>> @@ -1804,11 +1770,18 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx)
>> 	spin_lock(&glob->lru_lock);
>> 	for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) {
>> 		list_for_each_entry(bo, &glob->swap_lru[i], swap) {
>> -			if (ttm_bo_evict_swapout_allowable(bo, ctx, &locked,
>> -							   NULL)) {
>> -				ret = 0;
>> -				break;
>> +			if (!ttm_bo_evict_swapout_allowable(bo, ctx, &locked,
>> +							    NULL))
>> +				continue;
>> +
>> +			if (!ttm_bo_get_unless_zero(bo)) {
>> +				if (locked)
>> +					dma_resv_unlock(bo->base.resv);
>> +				continue;
>> 			}
> 
> [xh] As you get the bo. when you put it?
> 
> You only put one bo just before return. BUT you get the bos in the for loop. Am I missing somethings?
> 
> thanks
> xinhui
> 
> 
[xh] Never mind.
I missed the break;

thanks
xinhui

>> +
>> +			ret = 0;
>> +			break;
>> 		}
>> 		if (!ret)
>> 			break;
>> @@ -1819,11 +1792,9 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx)
>> 		return ret;
>> 	}
>> 
>> -	kref_get(&bo->list_kref);
>> -
>> -	if (!list_empty(&bo->ddestroy)) {
>> +	if (bo->deleted) {
>> 		ret = ttm_bo_cleanup_refs(bo, false, false, locked);
>> -		kref_put(&bo->list_kref, ttm_bo_release_list);
>> +		ttm_bo_put(bo);
>> 		return ret;
>> 	}
>> 
>> @@ -1877,7 +1848,7 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx)
>> 	 */
>> 	if (locked)
>> 		dma_resv_unlock(bo->base.resv);
>> -	kref_put(&bo->list_kref, ttm_bo_release_list);
>> +	ttm_bo_put(bo);
>> 	return ret;
>> }
>> EXPORT_SYMBOL(ttm_bo_swapout);
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> index 86d152472f38..c8e359ded1df 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> @@ -507,7 +507,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
>> 	fbo->base.moving = NULL;
>> 	drm_vma_node_reset(&fbo->base.base.vma_node);
>> 
>> -	kref_init(&fbo->base.list_kref);
>> 	kref_init(&fbo->base.kref);
>> 	fbo->base.destroy = &ttm_transfered_destroy;
>> 	fbo->base.acc_size = 0;
>> diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
>> index 66ca49db9633..b9bc1b00142e 100644
>> --- a/include/drm/ttm/ttm_bo_api.h
>> +++ b/include/drm/ttm/ttm_bo_api.h
>> @@ -135,18 +135,14 @@ struct ttm_tt;
>> * @num_pages: Actual number of pages.
>> * @acc_size: Accounted size for this object.
>> * @kref: Reference count of this buffer object. When this refcount reaches
>> - * zero, the object is put on the delayed delete list.
>> - * @list_kref: List reference count of this buffer object. This member is
>> - * used to avoid destruction while the buffer object is still on a list.
>> - * Lru lists may keep one refcount, the delayed delete list, and kref != 0
>> - * keeps one refcount. When this refcount reaches zero,
>> - * the object is destroyed.
>> + * zero, the object is destroyed or put on the delayed delete list.
>> * @mem: structure describing current placement.
>> * @persistent_swap_storage: Usually the swap storage is deleted for buffers
>> * pinned in physical memory. If this behaviour is not desired, this member
>> * holds a pointer to a persistent shmem object.
>> * @ttm: TTM structure holding system pages.
>> * @evicted: Whether the object was evicted without user-space knowing.
>> + * @deleted: True if the object is only a zombie and already deleted.
>> * @lru: List head for the lru list.
>> * @ddestroy: List head for the delayed destroy list.
>> * @swap: List head for swap LRU list.
>> @@ -183,9 +179,7 @@ struct ttm_buffer_object {
>> 	/**
>> 	* Members not needing protection.
>> 	*/
>> -
>> 	struct kref kref;
>> -	struct kref list_kref;
>> 
>> 	/**
>> 	 * Members protected by the bo::resv::reserved lock.
>> @@ -195,6 +189,7 @@ struct ttm_buffer_object {
>> 	struct file *persistent_swap_storage;
>> 	struct ttm_tt *ttm;
>> 	bool evicted;
>> +	bool deleted;
>> 
>> 	/**
>> 	 * Members protected by the bdev::lru_lock.
>> -- 
>> 2.17.1
>> 
> 



More information about the dri-devel mailing list