amdgpu_userq_create() maps the queue under reset_domain->sem, but
amdgpu_userq_ensure_ev_fence() returns with userq_mutex held, so the
semaphore nests inside userq_mutex. That is the inverse of the order
used by GPU recovery and lockdep reports a circular dependency:

  Chain exists of:
    &dev->clientlist_mutex --> &userq_mgr->userq_mutex --> &reset_domain->sem

userq_mutex has to stay held from amdgpu_userq_ensure_ev_fence() until
the queue is mapped to keep the eviction fence valid, so only trylock
reset_domain->sem under it. If a reset is in progress, drop userq_mutex,
wait for the reset to finish and retry. Hold the semaphore until the
queue is mapped or its doorbell is erased again on failure.

Fixes: 44fd81554cc0 ("drm/amdgpu/userq: serialize queue map against GPU reset")
Signed-off-by: Prike Liang <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index a34ea8ecd700..817bd395d2f1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -748,18 +748,21 @@ amdgpu_userq_create(struct drm_file *filp, union 
drm_amdgpu_userq *args)
        if (r)
                goto clean_mqd;
 
-       amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
+       for (;;) {
+               amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, 
&fpriv->evf_mgr);
+               /* reset_domain->sem nests outside userq_mutex, so only trylock 
here */
+               if (down_read_trylock(&adev->reset_domain->sem))
+                       break;
+               mutex_unlock(&uq_mgr->userq_mutex);
+               down_read(&adev->reset_domain->sem);
+               up_read(&adev->reset_domain->sem);
+       }
 
        /* don't map the queue if scheduling is halted */
        if (!adev->userq_halt_for_enforce_isolation ||
            ((queue->queue_type != AMDGPU_HW_IP_GFX) &&
             (queue->queue_type != AMDGPU_HW_IP_COMPUTE))) {
-               /* Serialize the map against an in-progress GPU reset (MES is
-                * unresponsive during recovery), matching 
amdgpu_userq_detach_doorbell().
-                */
-               down_read(&adev->reset_domain->sem);
                r = amdgpu_userq_map_helper(queue);
-               up_read(&adev->reset_domain->sem);
                if (r) {
                        drm_file_err(uq_mgr->file, "Failed to map Queue\n");
                        trace_amdgpu_userq_create_end(queue, r);
@@ -770,6 +773,7 @@ amdgpu_userq_create(struct drm_file *filp, union 
drm_amdgpu_userq *args)
 
        atomic_inc(&uq_mgr->userq_count[queue->queue_type]);
        mutex_unlock(&uq_mgr->userq_mutex);
+       up_read(&adev->reset_domain->sem);
 
        r = xa_alloc(&uq_mgr->userq_xa, &qid, queue,
                     XA_LIMIT(1, AMDGPU_MAX_USERQ_COUNT),
@@ -791,6 +795,7 @@ amdgpu_userq_create(struct drm_file *filp, union 
drm_amdgpu_userq *args)
 
 erase_doorbell:
        xa_erase_irq(&adev->userq_doorbell_xa, index);
+       up_read(&adev->reset_domain->sem);
 clean_mqd:
        uq_funcs->mqd_destroy(queue);
 clean_doorbell_bo:
-- 
2.34.1

Reply via email to