An individual BO can leave the VM idle list while drm_exec backs off on
contention during userq restore. The retry prelocks that BO and retains
it in exec->objects, but the restarted VM-list walk can skip its
drm_exec_prepare_obj() call.

amdgpu_evf_mgr_rearm() still attaches an eviction fence to every locked
object. With CONFIG_DEBUG_MUTEXES, the preceding unlock resets
max_fences to num_fences. If ttm_bo_validate() returns without moving
the BO and no existing fence can be replaced, dma_resv_add_fence() hits
its capacity BUG.

Signed-off-by: Prike Liang <[email protected]>
---
 .../drm/amd/amdgpu/amdgpu_eviction_fence.c    |  7 +++--
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c     | 26 ++++++++++++++++++-
 2 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
index f0cc49213c01..8b2e25770bdf 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
@@ -159,11 +159,10 @@ int amdgpu_evf_mgr_rearm(struct amdgpu_eviction_fence_mgr 
*evf_mgr,
        dma_fence_put(evf_mgr->ev_fence);
        evf_mgr->ev_fence = &ev_fence->base;
 
-       /* And add it to all existing BOs */
+       /* The caller has validated and reserved space on every locked BO. */
        drm_exec_for_each_locked_object(exec, index, obj) {
-               struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
-
-               amdgpu_evf_mgr_attach_fence(evf_mgr, bo);
+               dma_resv_add_fence(obj->resv, &ev_fence->base,
+                                  DMA_RESV_USAGE_BOOKKEEP);
        }
        return 0;
 }
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index e6ce9d12bdb4..0f509e67ca1a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -1105,6 +1105,7 @@ amdgpu_userq_vm_validate_and_restore_queue(struct 
amdgpu_userq_mgr *uq_mgr)
        struct amdgpu_bo_va *bo_va;
        struct amdgpu_usermode_queue *queue;
        struct amdgpu_bo *bo;
+       struct drm_gem_object *obj;
        struct drm_exec exec;
        struct xarray xa;
        int ret;
@@ -1184,7 +1185,21 @@ amdgpu_userq_vm_validate_and_restore_queue(struct 
amdgpu_userq_mgr *uq_mgr)
                invalidated = false;
        }
 
-       ret = amdgpu_vm_handle_moved(adev, vm, NULL);
+       /*
+        * A contended BO can change VM lists while drm_exec drops the locks.
+        * Prepare every finally locked object before updating PTEs or 
publishing
+        * the replacement eviction fence, including prelocked retry objects.
+        */
+       drm_exec_for_each_locked_object(&exec, tmp_key, obj) {
+               ret = dma_resv_reserve_fences(obj->resv, TTM_NUM_MOVE_FENCES + 
1);
+               if (ret)
+                       goto unlock_all;
+               ret = amdgpu_userq_validate_vm(NULL, gem_to_amdgpu_bo(obj));
+               if (ret)
+                       goto unlock_all;
+       }
+
+       ret = amdgpu_vm_handle_moved(adev, vm, &exec.ticket);
        if (ret)
                goto unlock_all;
 
@@ -1255,6 +1270,8 @@ amdgpu_userq_vm_validate_and_restore_queue(struct 
amdgpu_userq_mgr *uq_mgr)
         */
        list_for_each_entry(bo_va, &vm->always_valid.idle, base.vm_status)
                dma_fence_wait(bo_va->last_pt_update, false);
+       list_for_each_entry(bo_va, &vm->individual.idle, base.vm_status)
+               dma_fence_wait(bo_va->last_pt_update, false);
        /*
         * The PRT bo_va is kept off the state lists, so its PTE update fence
         * lands in prt_va->last_pt_update rather than vm->last_update; wait on
@@ -1281,6 +1298,13 @@ amdgpu_userq_vm_validate_and_restore_queue(struct 
amdgpu_userq_mgr *uq_mgr)
                queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(bo);
        }
 
+       /* Page-table updates may have consumed the earlier reservation slots. 
*/
+       drm_exec_for_each_locked_object(&exec, tmp_key, obj) {
+               ret = dma_resv_reserve_fences(obj->resv, 1);
+               if (ret)
+                       goto unlock_all;
+       }
+
        /* Back off without publishing a fence if recovery owns the hardware. */
        if (!down_read_trylock(&adev->reset_domain->sem)) {
                ret = -EAGAIN;
-- 
2.34.1

Reply via email to