Use a reset-domain read trylock for queue mapping and teardown. On contention, drop userq_mutex, wait for reset, then retry. Doorbell removal requires that read lock from its caller instead of acquiring it inside the queue mutex. Disabling the work prevents an IRQ from rearming it between the initial cancellation and removal from the doorbell xarray. Apply the same drain to creation's error path after unpublishing the doorbell.
Signed-off-by: Prike Liang <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 60 ++++++++++++++++++----- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 322c95201494..a745c752f459 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -428,9 +428,12 @@ static void amdgpu_userq_detach_doorbell(struct amdgpu_usermode_queue *queue) { struct amdgpu_device *adev = queue->userq_mgr->adev; - down_read(&adev->reset_domain->sem); + /* + * The caller serializes doorbell removal against an in-progress GPU + * reset by holding adev->reset_domain->sem for read. + */ + lockdep_assert_held_read(&adev->reset_domain->sem); xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index); - up_read(&adev->reset_domain->sem); } /** @@ -549,13 +552,32 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que trace_amdgpu_userq_destroy_start(queue); cancel_delayed_work_sync(&uq_mgr->resume_work); - /* Cancel before taking userq_mutex: cancel_delayed_work_sync() waits - * for any running instance, which itself takes userq_mutex. + /* Keep hang recovery available until the last submission completes. */ + amdgpu_userq_wait_for_last_fence(queue); + /* + * Disable IRQ requeueing before draining hang detection. The worker + * may take userq_mutex and trigger recovery, so neither that mutex nor + * the reset semaphore may be held while waiting for it. */ - cancel_delayed_work_sync(&queue->hang_detect_work); - + disable_delayed_work_sync(&queue->hang_detect_work); +retry: mutex_lock(&uq_mgr->userq_mutex); - amdgpu_userq_wait_for_last_fence(queue); + + /* + * Serialize queue teardown (doorbell detach and MES unmap) against an + * in-progress GPU reset. Do not block on the reset semaphore while + * holding userq_mutex: recovery takes the semaphore for write and then + * (transitively) userq_mutex, so blocking here would invert that order + * and deadlock. If the trylock fails, drop userq_mutex, wait for + * recovery to finish, and retry. + */ + if (!down_read_trylock(&adev->reset_domain->sem)) { + mutex_unlock(&uq_mgr->userq_mutex); + + down_read(&adev->reset_domain->sem); + up_read(&adev->reset_domain->sem); + goto retry; + } amdgpu_userq_detach_doorbell(queue); @@ -566,6 +588,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que atomic_dec(&uq_mgr->userq_count[queue->queue_type]); amdgpu_userq_fence_driver_free(queue); queue->fence_drv = NULL; + up_read(&adev->reset_domain->sem); mutex_unlock(&uq_mgr->userq_mutex); /* @@ -761,16 +784,28 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) if (r) goto clean_mqd; +map_retry: amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr); /* 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(). + /* + * Serialize the map against an in-progress GPU reset (MES is + * unresponsive during recovery). Do not block on the reset + * semaphore while holding userq_mutex: recovery takes the + * semaphore for write and then (transitively) userq_mutex, so + * blocking here would invert that order and deadlock. If the + * trylock fails, drop userq_mutex, wait for recovery, and retry. */ - down_read(&adev->reset_domain->sem); + if (!down_read_trylock(&adev->reset_domain->sem)) { + mutex_unlock(&uq_mgr->userq_mutex); + + down_read(&adev->reset_domain->sem); + up_read(&adev->reset_domain->sem); + goto map_retry; + } r = amdgpu_userq_map_helper(queue); up_read(&adev->reset_domain->sem); if (r) { @@ -803,7 +838,10 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) return 0; erase_doorbell: - xa_erase_irq(&adev->userq_doorbell_xa, index); + down_read(&adev->reset_domain->sem); + amdgpu_userq_detach_doorbell(queue); + up_read(&adev->reset_domain->sem); + disable_delayed_work_sync(&queue->hang_detect_work); clean_mqd: uq_funcs->mqd_destroy(queue); clean_doorbell_bo: -- 2.34.1
