> 2020年2月13日 18:01,Koenig, Christian <[email protected]> 写道:
>
> Am 13.02.20 um 05:11 schrieb Pan, Xinhui:
>>
>>
>>> 2020年2月12日 19:53,Christian König <[email protected]> 写道:
>>>
>>> Am 12.02.20 um 07:23 schrieb Pan, Xinhui:
>>>>> 2020年2月11日 23:43,Christian König <[email protected]> 写道:
>>>>>
>>>>> When non-imported BOs are resurrected for delayed delete we replace
>>>>> the dma_resv object to allow for easy reclaiming of the resources.
>>>>>
>>>>> v2: move that to ttm_bo_individualize_resv
>>>>> v3: add a comment to explain what's going on
>>>>>
>>>>> Signed-off-by: Christian König <[email protected]>
>>>>> Reviewed-by: xinhui pan <[email protected]>
>>>>> ---
>>>>> drivers/gpu/drm/ttm/ttm_bo.c | 14 +++++++++++++-
>>>>> 1 file changed, 13 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
>>>>> index bfc42a9e4fb4..8174603d390f 100644
>>>>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
>>>>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
>>>>> @@ -393,6 +393,18 @@ static int ttm_bo_individualize_resv(struct
>>>>> ttm_buffer_object *bo)
>>>>>
>>>>> r = dma_resv_copy_fences(&bo->base._resv, bo->base.resv);
>>>>> dma_resv_unlock(&bo->base._resv);
>>>>> + if (r)
>>>>> + return r;
>>>>> +
>>>>> + if (bo->type != ttm_bo_type_sg) {
>>>>> + /* This works because the BO is about to be destroyed and nobody
>>>>> + * reference it any more. The only tricky case is the trylock on
>>>>> + * the resv object while holding the lru_lock.
>>>>> + */
>>>>> + spin_lock(&ttm_bo_glob.lru_lock);
>>>>> + bo->base.resv = &bo->base._resv;
>>>>> + spin_unlock(&ttm_bo_glob.lru_lock);
>>>>> + }
>>>>>
>>>> how about something like that.
>>>> the basic idea is to do the bo cleanup work in bo release first and avoid
>>>> any race with evict.
>>>> As in bo dieing progress, evict also just do bo cleanup work.
>>>>
>>>> If bo is busy, neither bo_release nor evict can do cleanupwork on it.
>>>> For the bo release case, we just add bo back to lru list.
>>>> So we can clean it up both in workqueue and shrinker as the past way did.
>>>>
>>>> @@ -405,8 +405,9 @@ static int ttm_bo_individualize_resv(struct
>>>> ttm_buffer_object *bo)
>>>> if (bo->type != ttm_bo_type_sg) {
>>>> spin_lock(&ttm_bo_glob.lru_lock);
>>>> - bo->base.resv = &bo->base._resv;
>>>> + ttm_bo_del_from_lru(bo);
>>>> spin_unlock(&ttm_bo_glob.lru_lock);
>>>> + bo->base.resv = &bo->base._resv;
>>>> }
>>>> return r;
>>>> @@ -606,10 +607,9 @@ static void ttm_bo_release(struct kref *kref)
>>>> * shrinkers, now that they are queued for
>>>> * destruction.
>>>> */
>>>> - if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) {
>>>> + 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);
>>>> - }
>>>> + ttm_bo_add_mem_to_lru(bo, &bo->mem);
>>>> kref_init(&bo->kref);
>>>> list_add_tail(&bo->ddestroy, &bdev->ddestroy);
>>> Yeah, thought about that as well. But this has the major drawback that the
>>> deleted BO moves to the end of the LRU, which is something we don't want.
>> well, as the bo is busy, looks like it needs time to being idle. putting it
>> to tail seems fair.
>
> No, see BOs should move to the tail of the LRU whenever they are used.
> Freeing up a BO is basically the opposite of using it.
>
> So what would happen on the next memory contention is that the MM would evict
> BOs which are still used and only after come to the delete BO which could
> have been removed long ago.
>
>>> I think the real solution to this problem is to go a completely different
>>> way and remove the delayed delete feature from TTM altogether. Instead this
>>> should be part of some DRM domain handler component.
>>>
>> yes, completely agree. As long as we can shrink bos when OOM, the workqueue
>> is not necessary, The workqueue does not help in a heavy workload case.
>>
>> Pls see my patches below, I remove the workqueue, and what’s more, we can
>> clearup the bo without lru lock hold.
>> That would reduce the lock contention. I run kfdtest and got a good
>> performance result.
>
> No, that's an approach we had before as well and it also doesn't work that
> well.
>
> See the problem is that we can only remove the BO from the LRU after it has
> released the memory it references. Otherwise we run into the issue that some
> threads can't wait for the memory to be freed any more and run into an OOM
> situation.
>
ok, we can keep the workqueue at it is.
Now I come up with another idea that evict and swap can touch the destroy list
first, then lru list.
Looks like putting a dieing bo in lru list is useless.
thanks
xinhui
> Regards,
> Christian.
>
>>
>>
>>> In other words it should not matter if a BO is evicted, moved or freed.
>>> Whenever a piece of memory becomes available again we keep around a fence
>>> which marks the end of using this piece of memory.
>>>
>>> When then somebody asks for new memory we work through the LRU and test if
>>> using a certain piece of memory makes sense or not. If we find that a BO
>>> needs to be evicted for this we return a reference to the BO in question to
>>> the upper level handling.
>>>
>>> If we find that we can do the allocation but only with recently freed up
>>> memory we gather the fences and say you can only use the newly allocated
>>> memory after waiting for those.
>>>
>>> HEY! Wait a second! Did I just outlined what a potential replacement to TTM
>>> would look like?
>> yes, that is a good picture. Looks like we could do more work hen. :)
>>
>> thanks
>> xinhui
>>
>>
>> --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
>> index e795d5b5f8af..ac826a09b4ec 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
>> @@ -405,6 +405,9 @@ static int ttm_bo_individualize_resv(struct
>> ttm_buffer_object *bo)
>> if (bo->type != ttm_bo_type_sg) {
>> spin_lock(&ttm_bo_glob.lru_lock);
>> + /* it is very likely to release bo successfully.
>> + * if not, just adding it back.
>> + */
>> ttm_bo_del_from_lru(bo);
>> spin_unlock(&ttm_bo_glob.lru_lock);
>> bo->base.resv = &bo->base._resv;
>> @@ -466,18 +469,20 @@ static int ttm_bo_cleanup_refs(struct
>> ttm_buffer_object *bo,
>> if (unlock_resv)
>> dma_resv_unlock(bo->base.resv);
>> - spin_unlock(&ttm_bo_glob.lru_lock);
>> lret = dma_resv_wait_timeout_rcu(resv, true, interruptible,
>> 30 * HZ);
>> if (lret < 0)
>> - return lret;
>> - else if (lret == 0)
>> - return -EBUSY;
>> + goto busy;
>> + else if (lret == 0) {
>> + ret = -EBUSY;
>> + goto busy;
>> + }
>> - spin_lock(&ttm_bo_glob.lru_lock);
>> if (unlock_resv && !dma_resv_trylock(bo->base.resv)) {
>> + /* no race should be on it now */
>> + BUG();
>> /*
>> * We raced, and lost, someone else holds the
>> reservation now,
>> * and is probably busy in ttm_bo_cleanup_memtype_use.
>> @@ -486,20 +491,18 @@ static int ttm_bo_cleanup_refs(struct
>> ttm_buffer_object *bo,
>> * delayed destruction would succeed, so just return
>> success
>> * here.
>> */
>> - spin_unlock(&ttm_bo_glob.lru_lock);
>> return 0;
>> }
>> ret = 0;
>> }
>> - if (ret || unlikely(list_empty(&bo->ddestroy))) {
>> + if (ret) {
>> if (unlock_resv)
>> dma_resv_unlock(bo->base.resv);
>> - spin_unlock(&ttm_bo_glob.lru_lock);
>> - return ret;
>> + goto busy;
>> }
>> - ttm_bo_del_from_lru(bo);
>> + spin_lock(&ttm_bo_glob.lru_lock);
>> list_del_init(&bo->ddestroy);
>> spin_unlock(&ttm_bo_glob.lru_lock);
>> ttm_bo_cleanup_memtype_use(bo);
>> @@ -510,11 +513,20 @@ static int ttm_bo_cleanup_refs(struct
>> ttm_buffer_object *bo,
>> ttm_bo_put(bo);
>> return 0;
>> +
>> +busy:
>> + spin_lock(&ttm_bo_glob.lru_lock);
>> + ttm_bo_add_mem_to_lru(bo, &bo->mem);
>> + spin_unlock(&ttm_bo_glob.lru_lock);
>> +
>> + return ret;
>> }
>> /**
>> * Traverse the delayed list, and call ttm_bo_cleanup_refs on all
>> * encountered buffers.
>> + *
>> + * only called bo device release
>> */
>> static bool ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool
>> remove_all)
>> {
>> @@ -533,17 +545,17 @@ static bool ttm_bo_delayed_delete(struct ttm_bo_device
>> *bdev, bool remove_all)
>> list_move_tail(&bo->ddestroy, &removed);
>> if (!ttm_bo_get_unless_zero(bo))
>> continue;
>> + ttm_bo_del_from_lru(bo);
>> + spin_unlock(&glob->lru_lock);
>> if (remove_all || bo->base.resv != &bo->base._resv) {
>> - spin_unlock(&glob->lru_lock);
>> dma_resv_lock(bo->base.resv, NULL);
>> -
>> - spin_lock(&glob->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_lock(&glob->lru_lock);
>> + ttm_bo_add_mem_to_lru(bo, &bo->mem);
>> spin_unlock(&glob->lru_lock);
>> }
>> @@ -559,12 +571,8 @@ static bool ttm_bo_delayed_delete(struct ttm_bo_device
>> *bdev, bool remove_all)
>> static void ttm_bo_delayed_workqueue(struct work_struct *work)
>> {
>> - struct ttm_bo_device *bdev =
>> - container_of(work, struct ttm_bo_device, wq.work);
>> -
>> - if (!ttm_bo_delayed_delete(bdev, false))
>> - schedule_delayed_work(&bdev->wq,
>> - ((HZ / 100) < 1) ? 1 : HZ / 100);
>> + WARN(true, "do not schedule it");
>> + return;
>> }
>> static void ttm_bo_release(struct kref *kref)
>> @@ -595,6 +603,7 @@ static void ttm_bo_release(struct kref *kref)
>> ttm_mem_io_unlock(man);
>> }
>> + /* if bo is busy, spend a little more time to add bo to lru and
>> ddestroy list*/
>> 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);
>> @@ -615,8 +624,6 @@ static void ttm_bo_release(struct kref *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;
>> }
>> @@ -842,6 +849,9 @@ static int ttm_mem_evict_first(struct ttm_bo_device
>> *bdev,
>> return ret;
>> }
>> + ttm_bo_del_from_lru(bo);
>> + spin_unlock(&ttm_bo_glob.lru_lock);
>> +
>> if (bo->deleted) {
>> ret = ttm_bo_cleanup_refs(bo, ctx->interruptible,
>> ctx->no_wait_gpu, locked);
>> @@ -849,8 +859,6 @@ static int ttm_mem_evict_first(struct ttm_bo_device
>> *bdev,
>> return ret;
>> }
>> - spin_unlock(&ttm_bo_glob.lru_lock);
>> -
>> ret = ttm_bo_evict(bo, ctx);
>> if (locked)
>> ttm_bo_unreserve(bo);
>> @@ -1809,14 +1817,15 @@ int ttm_bo_swapout(struct ttm_bo_global *glob,
>> struct ttm_operation_ctx *ctx)
>> return ret;
>> }
>> + ttm_bo_del_from_lru(bo);
>> + spin_unlock(&glob->lru_lock);
>> +
>> if (bo->deleted) {
>> ret = ttm_bo_cleanup_refs(bo, false, false, locked);
>> ttm_bo_put(bo);
>> return ret;
>> }
>> - ttm_bo_del_from_lru(bo);
>> - spin_unlock(&glob->lru_lock);
>> /**
>> * Move to system cached
>
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel