AMD General

Regards,
      Prike

> -----Original Message-----
> From: Prosyak, Vitaly <[email protected]>
> Sent: Tuesday, September 29, 2026 6:49 AM
> To: Liang, Prike <[email protected]>; [email protected]
> Cc: Deucher, Alexander <[email protected]>; Koenig, Christian
> <[email protected]>; Prosyak, Vitaly <[email protected]>
> Subject: Re: [PATCH 12/18] drm/amdgpu: don't block wait gpu reset whthin userq
> lock
>
> Reviewed-by: Vitaly Prosyak <[email protected]>
>
> I have tested this patch on nv31. It successfully resolves the lockdep 
> deadlock
> warning triggered during the reverse
>
> ordering of &reset_domain->sem and &userq_mgr->userq_mutex. Please note that
> this patch requires a rebase to apply
>
>  cleanly onto the current target branch. Once rebased, the lock inversion 
> issue is
> gone, and the trylock-and-retry logic
>
> works cleanly to unblock recovery execution path interactions. We can safely 
> enable
> this on the CI.
>
Thank you for the review and verification, I will send out the new version with 
a clean up and rebase.


> On 2026-09-02 08:49, Prike Liang wrote:
> > The offending edge was code that did a blocking
> > down_read(&adev->reset_domain->sem) as following, while holding
> > userq_mutex. Since GPU recovery takes reset_domain
> > ->sem for write and then transitively acquires userq_mutex,
> > the reverse ordering could deadlock.
> >
> > .569196]
> >                other info that might help us debug this:
> >
> > [  307.569516] Chain exists of:
> >                  &adev->firmware.mutex --> &userq_mgr->userq_mutex -->
> > &reset_domain->sem
> >
> > [  307.570011]  Possible unsafe locking scenario:
> >
> > [  307.570250]        CPU0                    CPU1
> > [  307.570438]        ----                    ----
> > [  307.570624]   lock(&reset_domain->sem);
> > [  307.570785]                                lock(&userq_mgr->userq_mutex);
> > [  307.571061]                                lock(&reset_domain->sem);
> > [  307.571320]   lock(&adev->firmware.mutex);
> > [  307.571491]
> >                 *** DEADLOCK ***
> >
> > Signed-off-by: Prike Liang <[email protected]>
> > ---
> >  drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 51
> > ++++++++++++++++++++---
> >  1 file changed, 45 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > index 2534e4a1a530..a7d5ca741a3b 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > @@ -422,9 +422,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);
> >  }
> >
> >  /**
> > @@ -544,11 +547,34 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr
> > *uq_mgr, struct amdgpu_usermode_que
> >
> >     cancel_delayed_work_sync(&uq_mgr->resume_work);
> >
> > +   /*
> > +    * Cancel hang detection before serializing against a GPU reset. Hang
> > +    * detection triggers recovery, which takes reset_domain->sem for write,
> > +    * so it must not be canceled while that semaphore is held for read.
> > +    * A reset IRQ can restart hang detection, so this is repeated on retry.
> > +    */
> > +   cancel_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);
> > -   cancel_delayed_work_sync(&queue->hang_detect_work);
> >
> >  #if defined(CONFIG_DEBUG_FS)
> >     debugfs_remove_recursive(queue->debugfs_queue);
> > @@ -557,6 +583,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);
> >
> >     /*
> > @@ -734,16 +761,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) {

Reply via email to