[PATCH 3/9] drm/ttm: use per BO cleanup workers

Felix Kuehling felix.kuehling at amd.com
Tue Nov 29 21:14:10 UTC 2022


On 2022-11-25 05:21, Christian König wrote:
> Instead of a single worker going over the list of delete BOs in regular
> intervals use a per BO worker which blocks for the resv object and
> locking of the BO.
>
> This not only simplifies the handling massively, but also results in
> much better response time when cleaning up buffers.
>
> Signed-off-by: Christian König <christian.koenig at amd.com>

Just thinking out loud: If I understand it correctly, this can cause a 
lot of sleeping worker threads when 
AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE is used and many BOs are freed at 
the same time. This happens e.g. when a KFD process terminates or 
crashes. I guess with a concurrency-managed workqueue this isn't going 
to be excessive. And since it's on a per device workqueue, it doesn't 
stall work items on the system work queue or from other devices.

I'm trying to understand why you set WQ_MEM_RECLAIM. This work queue is 
not about freeing ttm_resources but about freeing the BOs. But it 
affects freeing of ghost_objs that are holding the ttm_resources being 
freed.

If those assumptions all make sense, patches 1-3 are

Reviewed-by: Felix Kuehling <Felix.Kuehling at amd.com>


> ---
>   drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |   2 +-
>   drivers/gpu/drm/i915/i915_gem.c            |   2 +-
>   drivers/gpu/drm/i915/intel_region_ttm.c    |   2 +-
>   drivers/gpu/drm/ttm/ttm_bo.c               | 112 ++++++++-------------
>   drivers/gpu/drm/ttm/ttm_bo_util.c          |   1 -
>   drivers/gpu/drm/ttm/ttm_device.c           |  24 ++---
>   include/drm/ttm/ttm_bo_api.h               |  18 +---
>   include/drm/ttm/ttm_device.h               |   7 +-
>   8 files changed, 57 insertions(+), 111 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> index 2b1db37e25c1..74ccbd566777 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> @@ -3984,7 +3984,7 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev)
>   	amdgpu_fence_driver_hw_fini(adev);
>   
>   	if (adev->mman.initialized)
> -		flush_delayed_work(&adev->mman.bdev.wq);
> +		drain_workqueue(adev->mman.bdev.wq);
>   
>   	if (adev->pm_sysfs_en)
>   		amdgpu_pm_sysfs_fini(adev);
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 8468ca9885fd..c38306f156d6 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -1099,7 +1099,7 @@ void i915_gem_drain_freed_objects(struct drm_i915_private *i915)
>   {
>   	while (atomic_read(&i915->mm.free_count)) {
>   		flush_work(&i915->mm.free_work);
> -		flush_delayed_work(&i915->bdev.wq);
> +		drain_workqueue(i915->bdev.wq);
>   		rcu_barrier();
>   	}
>   }
> diff --git a/drivers/gpu/drm/i915/intel_region_ttm.c b/drivers/gpu/drm/i915/intel_region_ttm.c
> index cf89d0c2a2d9..657bbc16a48a 100644
> --- a/drivers/gpu/drm/i915/intel_region_ttm.c
> +++ b/drivers/gpu/drm/i915/intel_region_ttm.c
> @@ -132,7 +132,7 @@ int intel_region_ttm_fini(struct intel_memory_region *mem)
>   			break;
>   
>   		msleep(20);
> -		flush_delayed_work(&mem->i915->bdev.wq);
> +		drain_workqueue(mem->i915->bdev.wq);
>   	}
>   
>   	/* If we leaked objects, Don't free the region causing use after free */
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
> index b77262a623e0..4749b65bedc4 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -280,14 +280,13 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo,
>   		ret = 0;
>   	}
>   
> -	if (ret || unlikely(list_empty(&bo->ddestroy))) {
> +	if (ret) {
>   		if (unlock_resv)
>   			dma_resv_unlock(bo->base.resv);
>   		spin_unlock(&bo->bdev->lru_lock);
>   		return ret;
>   	}
>   
> -	list_del_init(&bo->ddestroy);
>   	spin_unlock(&bo->bdev->lru_lock);
>   	ttm_bo_cleanup_memtype_use(bo);
>   
> @@ -300,47 +299,21 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo,
>   }
>   
>   /*
> - * Traverse the delayed list, and call ttm_bo_cleanup_refs on all
> - * encountered buffers.
> + * Block for the dma_resv object to become idle, lock the buffer and clean up
> + * the resource and tt object.
>    */
> -bool ttm_bo_delayed_delete(struct ttm_device *bdev, bool remove_all)
> +static void ttm_bo_delayed_delete(struct work_struct *work)
>   {
> -	struct list_head removed;
> -	bool empty;
> -
> -	INIT_LIST_HEAD(&removed);
> -
> -	spin_lock(&bdev->lru_lock);
> -	while (!list_empty(&bdev->ddestroy)) {
> -		struct ttm_buffer_object *bo;
> -
> -		bo = list_first_entry(&bdev->ddestroy, struct ttm_buffer_object,
> -				      ddestroy);
> -		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(&bdev->lru_lock);
> -			dma_resv_lock(bo->base.resv, NULL);
> -
> -			spin_lock(&bdev->lru_lock);
> -			ttm_bo_cleanup_refs(bo, false, !remove_all, true);
> -
> -		} else if (dma_resv_trylock(bo->base.resv)) {
> -			ttm_bo_cleanup_refs(bo, false, !remove_all, true);
> -		} else {
> -			spin_unlock(&bdev->lru_lock);
> -		}
> +	struct ttm_buffer_object *bo;
>   
> -		ttm_bo_put(bo);
> -		spin_lock(&bdev->lru_lock);
> -	}
> -	list_splice_tail(&removed, &bdev->ddestroy);
> -	empty = list_empty(&bdev->ddestroy);
> -	spin_unlock(&bdev->lru_lock);
> +	bo = container_of(work, typeof(*bo), delayed_delete);
>   
> -	return empty;
> +	dma_resv_wait_timeout(bo->base.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);
> +	ttm_bo_put(bo);
>   }
>   
>   static void ttm_bo_release(struct kref *kref)
> @@ -369,44 +342,40 @@ 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, DMA_RESV_USAGE_BOOKKEEP) ||
> -	    !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;
>   
> -		spin_lock(&bo->bdev->lru_lock);
> +		if (!dma_resv_test_signaled(bo->base.resv,
> +					    DMA_RESV_USAGE_BOOKKEEP) ||
> +		    !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;
>   
> -		/*
> -		 * 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);
> -		list_add_tail(&bo->ddestroy, &bdev->ddestroy);
> -		spin_unlock(&bo->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);
> +			}
>   
> -		schedule_delayed_work(&bdev->wq,
> -				      ((HZ / 100) < 1) ? 1 : HZ / 100);
> -		return;
> -	}
> +			kref_init(&bo->kref);
> +			spin_unlock(&bo->bdev->lru_lock);
>   
> -	spin_lock(&bo->bdev->lru_lock);
> -	list_del(&bo->ddestroy);
> -	spin_unlock(&bo->bdev->lru_lock);
> +			INIT_WORK(&bo->delayed_delete, ttm_bo_delayed_delete);
> +			queue_work(bdev->wq, &bo->delayed_delete);
> +			return;
> +		}
>   
> -	ttm_bo_cleanup_memtype_use(bo);
> -	dma_resv_unlock(bo->base.resv);
> +		ttm_bo_cleanup_memtype_use(bo);
> +		dma_resv_unlock(bo->base.resv);
> +	}
>   
>   	atomic_dec(&ttm_glob.bo_count);
>   	bo->destroy(bo);
> @@ -946,7 +915,6 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo,
>   	int ret;
>   
>   	kref_init(&bo->kref);
> -	INIT_LIST_HEAD(&bo->ddestroy);
>   	bo->bdev = bdev;
>   	bo->type = type;
>   	bo->page_alignment = alignment;
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
> index ba3aa0a0fc43..ae4b7922ee1a 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> @@ -230,7 +230,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
>   	 */
>   
>   	atomic_inc(&ttm_glob.bo_count);
> -	INIT_LIST_HEAD(&fbo->base.ddestroy);
>   	drm_vma_node_reset(&fbo->base.base.vma_node);
>   
>   	kref_init(&fbo->base.kref);
> diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c
> index e7147e304637..e9bedca4dfdc 100644
> --- a/drivers/gpu/drm/ttm/ttm_device.c
> +++ b/drivers/gpu/drm/ttm/ttm_device.c
> @@ -175,16 +175,6 @@ int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
>   }
>   EXPORT_SYMBOL(ttm_device_swapout);
>   
> -static void ttm_device_delayed_workqueue(struct work_struct *work)
> -{
> -	struct ttm_device *bdev =
> -		container_of(work, struct ttm_device, wq.work);
> -
> -	if (!ttm_bo_delayed_delete(bdev, false))
> -		schedule_delayed_work(&bdev->wq,
> -				      ((HZ / 100) < 1) ? 1 : HZ / 100);
> -}
> -
>   /**
>    * ttm_device_init
>    *
> @@ -215,15 +205,19 @@ int ttm_device_init(struct ttm_device *bdev, struct ttm_device_funcs *funcs,
>   	if (ret)
>   		return ret;
>   
> +	bdev->wq = alloc_workqueue("ttm", WQ_MEM_RECLAIM | WQ_HIGHPRI, 16);
> +	if (!bdev->wq) {
> +		ttm_global_release();
> +		return -ENOMEM;
> +	}
> +
>   	bdev->funcs = funcs;
>   
>   	ttm_sys_man_init(bdev);
>   	ttm_pool_init(&bdev->pool, dev, use_dma_alloc, use_dma32);
>   
>   	bdev->vma_manager = vma_manager;
> -	INIT_DELAYED_WORK(&bdev->wq, ttm_device_delayed_workqueue);
>   	spin_lock_init(&bdev->lru_lock);
> -	INIT_LIST_HEAD(&bdev->ddestroy);
>   	INIT_LIST_HEAD(&bdev->pinned);
>   	bdev->dev_mapping = mapping;
>   	mutex_lock(&ttm_global_mutex);
> @@ -247,10 +241,8 @@ void ttm_device_fini(struct ttm_device *bdev)
>   	list_del(&bdev->device_list);
>   	mutex_unlock(&ttm_global_mutex);
>   
> -	cancel_delayed_work_sync(&bdev->wq);
> -
> -	if (ttm_bo_delayed_delete(bdev, true))
> -		pr_debug("Delayed destroy list was clean\n");
> +	drain_workqueue(bdev->wq);
> +	destroy_workqueue(bdev->wq);
>   
>   	spin_lock(&bdev->lru_lock);
>   	for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i)
> diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
> index 7758347c461c..69e62bbb01e3 100644
> --- a/include/drm/ttm/ttm_bo_api.h
> +++ b/include/drm/ttm/ttm_bo_api.h
> @@ -92,7 +92,6 @@ struct ttm_tt;
>    * @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.
> - * @ddestroy: List head for the delayed destroy list.
>    * @swap: List head for swap LRU list.
>    * @offset: The current GPU offset, which can have different meanings
>    * depending on the memory type. For SYSTEM type memory, it should be 0.
> @@ -135,19 +134,14 @@ struct ttm_buffer_object {
>   	struct ttm_tt *ttm;
>   	bool deleted;
>   	struct ttm_lru_bulk_move *bulk_move;
> +	unsigned priority;
> +	unsigned pin_count;
>   
>   	/**
> -	 * Members protected by the bdev::lru_lock.
> -	 */
> -
> -	struct list_head ddestroy;
> -
> -	/**
> -	 * Members protected by a bo reservation.
> +	 * @delayed_delete: Work item used when we can't delete the BO
> +	 * immediately
>   	 */
> -
> -	unsigned priority;
> -	unsigned pin_count;
> +	struct work_struct delayed_delete;
>   
>   	/**
>   	 * Special members that are protected by the reserve lock
> @@ -448,8 +442,6 @@ void ttm_bo_vm_close(struct vm_area_struct *vma);
>   
>   int ttm_bo_vm_access(struct vm_area_struct *vma, unsigned long addr,
>   		     void *buf, int len, int write);
> -bool ttm_bo_delayed_delete(struct ttm_device *bdev, bool remove_all);
> -
>   vm_fault_t ttm_bo_vm_dummy_page(struct vm_fault *vmf, pgprot_t prot);
>   
>   #endif
> diff --git a/include/drm/ttm/ttm_device.h b/include/drm/ttm/ttm_device.h
> index 95b3c04b1ab9..4f3e81eac6f3 100644
> --- a/include/drm/ttm/ttm_device.h
> +++ b/include/drm/ttm/ttm_device.h
> @@ -251,11 +251,6 @@ struct ttm_device {
>   	 */
>   	spinlock_t lru_lock;
>   
> -	/**
> -	 * @ddestroy: Destroyed but not yet cleaned up buffer objects.
> -	 */
> -	struct list_head ddestroy;
> -
>   	/**
>   	 * @pinned: Buffer objects which are pinned and so not on any LRU list.
>   	 */
> @@ -270,7 +265,7 @@ struct ttm_device {
>   	/**
>   	 * @wq: Work queue structure for the delayed delete workqueue.
>   	 */
> -	struct delayed_work wq;
> +	struct workqueue_struct *wq;
>   };
>   
>   int ttm_global_swapout(struct ttm_operation_ctx *ctx, gfp_t gfp_flags);


More information about the dri-devel mailing list