On 2026-11-09 16:35, Han, Qiang wrote:
AMD General

cc @Zhao, Huasha @Gu, Zhenyu

-----Original Message-----
From: Yang, Philip <[email protected]>
Sent: Monday, November 9, 2026 1:15 PM
To: [email protected]; Kuehling, Felix <[email protected]>; Gandham, 
Shashidhar <[email protected]>; Han, Qiang <[email protected]>
Cc: Han, Qiang <[email protected]>; Yang, Philip <[email protected]>
Subject: [PATCH] drm/amdkfd: wait for the queue quiesce in concurrent SVM 
evictions

From: Qiang Han <[email protected]>

With XNACK off, svm_range_evict() leaves the GPU page tables in place and 
relies on the process's queues being stopped before the MMU notifier returns.

Only the first eviction (evicted_ranges == 1) calls kgd2kfd_quiesce_mm(). The 
quiesce preempts every GPU the process has opened and takes milliseconds. Any 
other range invalidated in the meantime sees evicted_ranges > 1 and returns at 
once. The core mm then frees, migrates or reuses the page while the queues still 
run, and an in-flight SDMA or blit copy accesses it through the old GPU PTE. The 
result is silent corruption of the copy, or of whatever process now owns the page.

Hold a new svms->evict_lock across the first evictor's quiesce, so that concurrent 
evictions return only after the queues have stopped. This is the scheme 
amdgpu_amdkfd_evict_userptr() uses with process_info->notifier_lock. evict_lock nests 
inside prange->lock and is taken nowhere else. Under it, the quiesce path takes the 
dqm lock, which is already used in MMU notifiers (memalloc_noreclaim).

One way to reach this: a pageable hipMemcpy() larger than 1 MiB. ROCclr pins 
the buffer with hsa_amd_memory_lock(), libhsakmt registers it as a KFD SVM 
range, and host memory pressure (reclaim, compaction, allocator purge) 
invalidates the range.

Fixes: 8a7c184a16d7 ("drm/amdkfd: svm range eviction and restore")
Signed-off-by: Qing Hang <[email protected]>
Signed-off-by: Philip Yang <[email protected]>

Reviewed-by: Felix Kuehling <[email protected]>


---
  drivers/gpu/drm/amd/amdkfd/kfd_priv.h |  2 ++  
drivers/gpu/drm/amd/amdkfd/kfd_svm.c  | 35 ++++++++++++++++++---------
  2 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h 
b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
index 158fa70a1a8e..581fb7ceba40 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
@@ -909,6 +909,8 @@ struct svm_range_list {
         struct list_head                deferred_range_list;
         struct list_head                criu_svm_metadata_list;
         spinlock_t                      deferred_list_lock;
+       /* serializes XNACK-off evictions with the queue quiesce */
+       struct mutex                    evict_lock;
         atomic_t                        evicted_ranges;
         atomic_t                        drain_pagefaults;
         struct delayed_work             restore_work;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c 
b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index 3688ae7ba35e..8b84333a85b7 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -2141,21 +2141,30 @@ svm_range_evict(struct svm_range *prange, struct 
mm_struct *mm,
                 if (prange->start <= last && prange->last >= start)
                         atomic_inc(&prange->invalid);

+               /* The MMU notifier must not return before the GPU has stopped
+                * accessing the invalidated pages. Only the first eviction 
stops
+                * the queues, which takes milliseconds (HWS preemption on every
+                * GPU of the process). Serialize all evictions of this process 
so
+                * that a concurrent invalidation of another range waits until
+                * that quiesce has completed instead of returning while queues
+                * still run on stale PTEs.
+                */
+               mutex_lock(&svms->evict_lock);
                 evicted_ranges = atomic_inc_return(&svms->evicted_ranges);
-               if (evicted_ranges != 1)
-                       return r;
-
-               pr_debug("evicting svms 0x%p range [0x%lx 0x%lx]\n",
-                        prange->svms, prange->start, prange->last);
+               if (evicted_ranges == 1) {
+                       pr_debug("evicting svms 0x%p range [0x%lx 0x%lx]\n",
+                                prange->svms, prange->start, prange->last);

-               /* First eviction, stop the queues */
-               r = kgd2kfd_quiesce_mm(mm, KFD_QUEUE_EVICTION_TRIGGER_SVM);
-               if (r)
-                       pr_debug("failed to quiesce KFD\n");
+                       /* First eviction, stop the queues */
+                       r = kgd2kfd_quiesce_mm(mm, 
KFD_QUEUE_EVICTION_TRIGGER_SVM);
+                       if (r)
+                               pr_debug("failed to quiesce KFD\n");

-               pr_debug("schedule to restore svm %p ranges\n", svms);
-               queue_delayed_work(system_freezable_wq, &svms->restore_work,
-                       msecs_to_jiffies(AMDGPU_SVM_RANGE_RESTORE_DELAY_MS));
+                       pr_debug("schedule to restore svm %p ranges\n", svms);
+                       queue_delayed_work(system_freezable_wq, 
&svms->restore_work,
+                                          
msecs_to_jiffies(AMDGPU_SVM_RANGE_RESTORE_DELAY_MS));
+               }
+               mutex_unlock(&svms->evict_lock);
         } else {
                 unsigned long s, l;
                 uint32_t trigger;
@@ -3437,6 +3446,7 @@ void svm_range_list_fini(struct kfd_process *p)
                 svm_range_free(prange, true);
         }

+       mutex_destroy(&p->svms.evict_lock);
         mutex_destroy(&p->svms.lock);

         pr_debug("process pid %d svms 0x%p done\n", @@ -3450,6 +3460,7 @@ int 
svm_range_list_init(struct kfd_process *p)

         svms->objects = RB_ROOT_CACHED;
         mutex_init(&svms->lock);
+       mutex_init(&svms->evict_lock);
         INIT_LIST_HEAD(&svms->list);
         atomic_set(&svms->evicted_ranges, 0);
         atomic_set(&svms->drain_pagefaults, 0);
--
2.50.1

Reply via email to