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