From: Vitaly Prosyak <[email protected]>

The eviction-fence worker runs inside a DMA-fence signalling section.
Userq eviction can block on reset_domain->sem while GPU recovery holds
that semaphore and invalidates mappings. Lockdep reports the resulting
dependency cycle through dma_fence_map.

[  +0.000295] amdgpu 0000:03:00.0: GPU reset begin!. Source:  5

[  +0.000233] ======================================================
[  +0.000001] WARNING: possible circular locking dependency detected
[  +0.000000] 7.2.0+ #97 Tainted: G        W
[  +0.000001] ------------------------------------------------------
[  +0.000001] kworker/u128:1/238 is trying to acquire lock:
[  +0.000001] ffff897b4fcc2eb8 (&mapping->i_mmap_rwsem){++++}-{4:4}, at: 
unmap_mapping_pages+0x6a/0xd0
[  +0.000005]
              but task is already holding lock:
[  +0.000001] ffff897b0fd60ec0 (&reset_domain->sem){++++}-{4:4}, at: 
amdgpu_device_lock_reset_domain+0x1c/0x30 [amdgpu]
[  +0.000294]
              which lock already depends on the new lock.

[  +0.000000]
              the existing dependency chain (in reverse order) is:
[  +0.000001]
              -> #3 (&reset_domain->sem){++++}-{4:4}:
[  +0.000002]        down_read+0x30/0x180
[  +0.000004]        amdgpu_userq_unmap_helper+0x46/0x290 [amdgpu]
[  +0.000212]        amdgpu_userq_evict+0x4e/0xf0 [amdgpu]
[  +0.000198]        amdgpu_eviction_fence_suspend_worker+0x51/0xf0 [amdgpu]
[  +0.000176]        process_one_work+0x22e/0x760
[  +0.000003]        worker_thread+0x1db/0x3d0
[  +0.000001]        kthread+0xfe/0x140
[  +0.000001]        ret_from_fork+0x3bd/0x470
[  +0.000002]        ret_from_fork_asm+0x1a/0x30
[  +0.000002]
              -> #2 (dma_fence_map){++++}-{0:0}:
[  +0.000003]        __dma_fence_might_wait+0x42/0xf0
[  +0.000002]        dma_resv_lockdep+0x26d/0x3b0
[  +0.000002]        do_one_initcall+0x74/0x3e0
[  +0.000002]        kernel_init_freeable+0x3f8/0x6d0
[  +0.000002]        kernel_init+0x1b/0x200
[  +0.000002]        ret_from_fork+0x3bd/0x470
[  +0.000001]        ret_from_fork_asm+0x1a/0x30
[  +0.000002]
              -> #1 (mmu_notifier_invalidate_range_start){+.+.}-{0:0}:
[  +0.000002]        __split_huge_pmd+0x86/0x190
[  +0.000002]        vma_adjust_trans_huge+0x12b/0x1c0
[  +0.000002]        __split_vma+0x236/0x370
[  +0.000002]        vms_gather_munmap_vmas+0xe8/0x360
[  +0.000001]        __mmap_region+0x2ab/0x1060
[  +0.000001]        mmap_region+0xc4/0x180
[  +0.000001]        do_mmap+0x48b/0x660
[  +0.000002]        vm_mmap_pgoff+0x129/0x230
[  +0.000002]        ksys_mmap_pgoff+0x1a2/0x270
[  +0.000001]        __x64_sys_mmap+0x33/0x70
[  +0.000002]        x64_sys_call+0x2194/0x26e0
[  +0.000001]        do_syscall_64+0x103/0x6d0
[  +0.000001]        entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  +0.000002]
              -> #0 (&mapping->i_mmap_rwsem){++++}-{4:4}:
[  +0.000002]        __lock_acquire+0x15aa/0x2740
[  +0.000001]        lock_acquire+0xc4/0x2f0
[  +0.000002]        down_read+0x30/0x180
[  +0.000001]        unmap_mapping_pages+0x6a/0xd0
[  +0.000001]        unmap_mapping_range+0x25/0x40
[  +0.000002]        amdgpu_device_gpu_recover+0x17a/0x400 [amdgpu]
[  +0.000168]        amdgpu_debugfs_reset_work+0x68/0x90 [amdgpu]
[  +0.000167]        process_one_work+0x22e/0x760
[  +0.000002]        worker_thread+0x1db/0x3d0
[  +0.000001]        kthread+0xfe/0x140
[  +0.000001]        ret_from_fork+0x3bd/0x470
[  +0.000001]        ret_from_fork_asm+0x1a/0x30
[  +0.000002]
              other info that might help us debug this:

[  +0.000000] Chain exists of:
                &mapping->i_mmap_rwsem --> dma_fence_map --> &reset_domain->sem

[  +0.000003]  Possible unsafe locking scenario:

[  +0.000000]        CPU0                    CPU1
[  +0.000001]        ----                    ----
[  +0.000001]   lock(&reset_domain->sem);
[  +0.000001]                                lock(dma_fence_map);
[  +0.000001]                                lock(&reset_domain->sem);
[  +0.000001]   rlock(&mapping->i_mmap_rwsem);
[  +0.000001]
               *** DEADLOCK ***

[  +0.000000] 3 locks held by kworker/u128:1/238:
[  +0.000001]  #0: ffff897b50d5f940 
((wq_completion)amdgpu-reset-dev){+.+.}-{0:0}, at: process_one_work+0x4a8/0x760
[  +0.000003]  #1: ffffce9fc0a13e30 
((work_completion)(&adev->reset_work)){+.+.}-{0:0}, at: 
process_one_work+0x1ee/0x760
[  +0.000003]  #2: ffff897b0fd60ec0 (&reset_domain->sem){++++}-{4:4}, at: 
amdgpu_device_lock_reset_domain+0x1c/0x30 [amdgpu]
[  +0.000201]
              stack backtrace:
[  +0.000001] CPU: 8 UID: 0 PID: 238 Comm: kworker/u128:1 Tainted: G        W   
        7.2.0+ #97 PREEMPT(full)
[  +0.000002] Tainted: [W]=WARN
[  +0.000000] Hardware name: ASUS System Product Name/ROG MAXIMUS Z790 HERO, 
BIOS 2703 10/17/2024
[  +0.000001] Workqueue: amdgpu-reset-dev amdgpu_debugfs_reset_work [amdgpu]
[  +0.000165] Call Trace:
[  +0.000000]  <TASK>
[  +0.000001]  dump_stack_lvl+0xa5/0x110
[  +0.000002]  dump_stack+0x10/0x20
[  +0.000001]  print_circular_bug+0x285/0x360
[  +0.000001]  check_noncircular+0x14c/0x170
[  +0.000002]  __lock_acquire+0x15aa/0x2740
[  +0.000001]  ? add_lock_to_list+0x35/0x130
[  +0.000002]  lock_acquire+0xc4/0x2f0
[  +0.000001]  ? unmap_mapping_pages+0x6a/0xd0
[  +0.000002]  ? lock_acquire+0xc4/0x2f0
[  +0.000001]  ? amdgpu_device_lock_reset_domain+0x1c/0x30 [amdgpu]
[  +0.000196]  down_read+0x30/0x180
[  +0.000001]  ? unmap_mapping_pages+0x6a/0xd0
[  +0.000002]  unmap_mapping_pages+0x6a/0xd0
[  +0.000002]  unmap_mapping_range+0x25/0x40
[  +0.000001]  amdgpu_device_gpu_recover+0x17a/0x400 [amdgpu]
[  +0.000163]  amdgpu_debugfs_reset_work+0x68/0x90 [amdgpu]
[  +0.000169]  process_one_work+0x22e/0x760
[  +0.000003]  worker_thread+0x1db/0x3d0
[  +0.000000]  ? __pfx_worker_thread+0x10/0x10
[  +0.000001]  kthread+0xfe/0x140
[  +0.000001]  ? __pfx_kthread+0x10/0x10
[  +0.000001]  ret_from_fork+0x3bd/0x470
[  +0.000001]  ? __pfx_kthread+0x10/0x10
[  +0.000001]  ret_from_fork_asm+0x1a/0x30
[  +0.000002]  </TASK>

Use a try-read-lock for eviction unmaps. If reset owns the semaphore,
leave the eviction fence unsignaled and retry the worker after a short
delay. Keep blocking read-locking for other userq unmap paths, which
must remain serialized with reset.

Convert suspend_work to delayed work so retries can be scheduled and
flushed during teardown.

Fixes: aebf196c550e ("drm/amdgpu/userq: wait for GPU reset before mapping or 
unmapping queues")

Cc: Christian König <[email protected]>
Cc: Alex Deucher <[email protected]>
Cc: Prike Liang <[email protected]>
Signed-off-by: Vitaly Prosyak <[email protected]>
---
 .../drm/amd/amdgpu/amdgpu_eviction_fence.c    | 22 ++++++++++----
 .../drm/amd/amdgpu/amdgpu_eviction_fence.h    |  2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c     | 30 ++++++++++++-------
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h     |  2 +-
 4 files changed, 38 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
index a0802012de49..fe45fa164e79 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
@@ -44,7 +44,7 @@ static bool amdgpu_eviction_fence_enable_signaling(struct 
dma_fence *f)
 {
        struct amdgpu_eviction_fence *ev_fence = to_ev_fence(f);
 
-       schedule_work(&ev_fence->evf_mgr->suspend_work);
+       schedule_delayed_work(&ev_fence->evf_mgr->suspend_work, 0);
        return true;
 }
 
@@ -58,13 +58,15 @@ static void
 amdgpu_eviction_fence_suspend_worker(struct work_struct *work)
 {
        struct amdgpu_eviction_fence_mgr *evf_mgr =
-               container_of(work, struct amdgpu_eviction_fence_mgr,
+               container_of(to_delayed_work(work),
+                            struct amdgpu_eviction_fence_mgr,
                             suspend_work);
        struct amdgpu_fpriv *fpriv =
                container_of(evf_mgr, struct amdgpu_fpriv, evf_mgr);
        struct amdgpu_userq_mgr *uq_mgr = &fpriv->userq_mgr;
        struct dma_fence *ev_fence;
        bool cookie;
+       int ret;
 
        mutex_lock(&uq_mgr->userq_mutex);
 
@@ -79,7 +81,14 @@ amdgpu_eviction_fence_suspend_worker(struct work_struct 
*work)
        cookie = dma_fence_begin_signalling();
 
        ev_fence = amdgpu_evf_mgr_get_fence(evf_mgr);
-       amdgpu_userq_evict(uq_mgr);
+       ret = amdgpu_userq_evict(uq_mgr);
+       if (ret == -EAGAIN) {
+               dma_fence_end_signalling(cookie);
+               dma_fence_put(ev_fence);
+               schedule_delayed_work(&evf_mgr->suspend_work, 1);
+               mutex_unlock(&uq_mgr->userq_mutex);
+               return;
+       }
 
        /*
         * Signaling the eviction fence must be done while holding the
@@ -167,14 +176,15 @@ void amdgpu_evf_mgr_init(struct amdgpu_eviction_fence_mgr 
*evf_mgr)
        evf_mgr->ev_fence_ctx = dma_fence_context_alloc(1);
        evf_mgr->ev_fence = dma_fence_get_stub();
 
-       INIT_WORK(&evf_mgr->suspend_work, amdgpu_eviction_fence_suspend_worker);
+       INIT_DELAYED_WORK(&evf_mgr->suspend_work,
+                 amdgpu_eviction_fence_suspend_worker);
 }
 
 void amdgpu_evf_mgr_shutdown(struct amdgpu_eviction_fence_mgr *evf_mgr)
 {
        evf_mgr->shutdown = true;
        /* Make sure that the shutdown is visible to the suspend work */
-       flush_work(&evf_mgr->suspend_work);
+       flush_delayed_work(&evf_mgr->suspend_work);
 }
 
 void amdgpu_evf_mgr_flush_suspend(struct amdgpu_eviction_fence_mgr *evf_mgr)
@@ -182,7 +192,7 @@ void amdgpu_evf_mgr_flush_suspend(struct 
amdgpu_eviction_fence_mgr *evf_mgr)
        dma_fence_wait(rcu_dereference_protected(evf_mgr->ev_fence, true),
                       false);
        /* Make sure that we are done with the last suspend work */
-       flush_work(&evf_mgr->suspend_work);
+       flush_delayed_work(&evf_mgr->suspend_work);
 }
 
 void amdgpu_evf_mgr_fini(struct amdgpu_eviction_fence_mgr *evf_mgr)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.h
index 2a750add4e7b..d3c3d0769864 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.h
@@ -43,7 +43,7 @@ struct amdgpu_eviction_fence_mgr {
         * Only signaled while holding the userq mutex.
         */
        struct dma_fence __rcu  *ev_fence;
-       struct work_struct      suspend_work;
+       struct delayed_work     suspend_work;
        bool                    shutdown;
 };
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index ccf0ff490ac2..d652a3a7a2da 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -351,16 +351,22 @@ static int amdgpu_userq_restore_helper(struct 
amdgpu_usermode_queue *queue)
        return r;
 }
 
-static int amdgpu_userq_unmap_helper(struct amdgpu_usermode_queue *queue)
+static int amdgpu_userq_unmap_helper(struct amdgpu_usermode_queue *queue,
+                                    bool trylock)
 {
        struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr;
        struct amdgpu_device *adev = uq_mgr->adev;
        const struct amdgpu_userq_funcs *userq_funcs =
                adev->userq_funcs[queue->queue_type];
-       int r;
+       int r = 0;
 
        /* The reset changes the queue state and the MES is unusable meanwhile 
*/
-       guard(rwsem_read)(&adev->reset_domain->sem);
+       if (trylock) {
+               if (!down_read_trylock(&adev->reset_domain->sem))
+                       return -EAGAIN;
+       } else {
+               down_read(&adev->reset_domain->sem);
+       }
        if ((queue->state == AMDGPU_USERQ_STATE_MAPPED) ||
            (queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) {
                trace_amdgpu_userq_state_start(queue);
@@ -369,14 +375,16 @@ static int amdgpu_userq_unmap_helper(struct 
amdgpu_usermode_queue *queue)
                if (r) {
                        trace_amdgpu_userq_state_changed(queue, 
AMDGPU_USERQ_STATE_HUNG);
                        queue->state = AMDGPU_USERQ_STATE_HUNG;
-                       return r;
+                       goto unlock;
                } else {
                        trace_amdgpu_userq_state_changed(queue, 
AMDGPU_USERQ_STATE_UNMAPPED);
                        queue->state = AMDGPU_USERQ_STATE_UNMAPPED;
                }
        }
 
-       return 0;
+unlock:
+       up_read(&adev->reset_domain->sem);
+       return r;
 }
 
 static int amdgpu_userq_map_helper(struct amdgpu_usermode_queue *queue)
@@ -563,7 +571,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, 
struct amdgpu_usermode_que
 #if defined(CONFIG_DEBUG_FS)
        debugfs_remove_recursive(queue->debugfs_queue);
 #endif
-       r = amdgpu_userq_unmap_helper(queue);
+       r = amdgpu_userq_unmap_helper(queue, false);
        atomic_dec(&uq_mgr->userq_count[queue->queue_type]);
        amdgpu_userq_fence_driver_free(queue);
        queue->fence_drv = NULL;
@@ -1296,7 +1304,9 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
 
        /* Try to unmap all the queues in this process ctx */
        xa_for_each(&uq_mgr->userq_xa, queue_id, queue) {
-               r = amdgpu_userq_unmap_helper(queue);
+               r = amdgpu_userq_unmap_helper(queue, true);
+               if (r == -EAGAIN)
+                       return r;
                if (r)
                        ret = r;
        }
@@ -1327,10 +1337,10 @@ amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr 
*uq_mgr)
        }
 }
 
-void
+int
 amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr)
 {
-       amdgpu_userq_evict_all(uq_mgr);
+       return amdgpu_userq_evict_all(uq_mgr);
 }
 
 int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file 
*file_priv,
@@ -1422,7 +1432,7 @@ int amdgpu_userq_suspend(struct amdgpu_device *adev)
                if (adev->in_s0ix)
                        r = amdgpu_userq_preempt_helper(queue);
                else
-                       r = amdgpu_userq_unmap_helper(queue);
+                       r = amdgpu_userq_unmap_helper(queue, false);
                if (r)
                        return r;
        }
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
index 488dc21d7c81..4dcf6151de6a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
@@ -163,7 +163,7 @@ void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr 
*userq_mgr);
 void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);
 
 void amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr);
-void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
+int amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
 
 void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
                                  struct amdgpu_eviction_fence_mgr *evf_mgr);
-- 
2.55.0

Reply via email to