AMD General You're right, that paragraph is wrong - I'll rewrite it for v2.
In the capture from the report, svm_range_validate_and_map() returns 0 on every fault that reaches it: 1154 of 1154 in the sampled window, 98726 cumulative against a single -EAGAIN. The mapping is re-installed on each pass and the fault is serviced. What the patch is actually for is what is left. Only 2009 of 98728 migrations entered from the fault path reached svm_migrate_copy_to_vram(); the rest unmap the window via MMU_NOTIFY_MIGRATE, collect nothing, and rebuild the mapping they just tore down. Declining the repeat takes zero-collect migrations from 176 to about 18 per 2000 attempts with no measurable change in wall time or pages moved - measured on gfx950, where the reported gfx942 stall does not itself reproduce. v2 will say that instead. The diff is unchanged. Thanks, William -----Original Message----- From: Kuehling, Felix <[email protected]> Sent: Thursday, August 6, 2026 10:55 AM To: Palacek, William <[email protected]>; [email protected] Cc: Yang, Philip <[email protected]>; Kasiviswanathan, Harish <[email protected]>; Liu, Alysa <[email protected]> Subject: Re: [PATCH] drm/amdkfd: don't repeat a SVM migration that collected no pages On 2026-08-06 10:31, William Palacek wrote: > svm_range_restore_pages() migrates the faulting granule to best_loc > whenever prange->actual_loc or best_loc is set, without checking > whether that window already lives there. > > When it does, MIGRATE_VMA_SELECT_SYSTEM has nothing to collect and > migrate_vma_setup() returns cpages == 0. By then it has already issued > MMU_NOTIFY_MIGRATE, which svm_range_cpu_invalidate_pagetables() turns > into svm_range_unmap_from_gpus() for that window, so the mapping the > fault needed is torn down to service a migration that moves nothing > and the fault is re-armed rather than resolved. Under XNACK on an > oversubscribed range this becomes a loop: svm_range_restore_pages() > and > svm_migrate_to_vram() are entered thousands of times per second, > svm_migrate_copy_to_vram() is never reached, and the per process > migration counters stop advancing while the GPU stays busy. Why doesn't it get to svm_range_validate_and_map and resolve the fault? Regards, Felix > > The notification cannot be filtered by pgmap owner, as it is what > invalidates the PTEs for pages that really do move. Avoid starting a > migration that cannot move anything instead. Per window residency is > not cheaply available beforehand, so record the window of a migration > that collected nothing and do not repeat it there until a short > backoff expires. > > Fixes: a546a2768440 ("drm/amdkfd: Use partial migrations/mapping for > GPU/CPU page faults in SVM") > Signed-off-by: William Palacek <[email protected]> > --- > drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 23 +++++++++++++++++++++++ > drivers/gpu/drm/amd/amdkfd/kfd_svm.h | 4 ++++ > 2 files changed, 27 insertions(+) > > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > index fa4054d51f60..b946ae95ff12 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > @@ -50,6 +50,11 @@ > * page table is updated. > */ > #define AMDGPU_SVM_RANGE_RETRY_FAULT_PENDING (2UL * NSEC_PER_MSEC) > + > +/* Long enough that a retry fault storm cannot re-arm a migration > +which > + * has already been found to have nothing to collect. > + */ > +#define AMDGPU_SVM_RANGE_NOOP_MIGRATE_BACKOFF (100UL * NSEC_PER_MSEC) > #if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) > #define dynamic_svm_range_dump(svms) \ > _dynamic_func_call_no_desc("svm_range_dump", svm_range_debug_dump, > svms) @@ -3221,8 +3226,25 @@ svm_range_restore_pages(struct amdgpu_device > *adev, unsigned int pasid, > last = min_t(unsigned long, ALIGN(addr + 1, size) - 1, prange->last); > if (prange->actual_loc != 0 || best_loc != 0) { > if (best_loc) { > + uint64_t vram_pages = prange->vram_pages; > + > + /* migrate_vma_setup() unmaps the window before it knows > + * whether it can collect anything, so a migration that > + * collects nothing re-arms this fault instead of > + * resolving it. > + */ > + if (start == prange->noop_migrate_start && > + ktime_before(timestamp, > + > ktime_add_ns(prange->noop_migrate_timestamp, > + > AMDGPU_SVM_RANGE_NOOP_MIGRATE_BACKOFF))) > + goto skip_migrate; > + > r = svm_migrate_to_vram(prange, best_loc, start, last, > mm, KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU); > + if (!r && prange->vram_pages == vram_pages) { > + prange->noop_migrate_start = start; > + prange->noop_migrate_timestamp = > ktime_get_boottime(); > + } > if (r) { > pr_debug("svm_migrate_to_vram failed (%d) at > %llx, falling back to system memory\n", > r, addr); > @@ -3248,6 +3270,7 @@ svm_range_restore_pages(struct amdgpu_device *adev, > unsigned int pasid, > } > } > > +skip_migrate: > r = svm_range_validate_and_map(mm, start, last, prange, gpuidx, false, > false, false); > if (r) > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > index c7d7adae4476..508a086195ac 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > @@ -93,6 +93,8 @@ struct svm_work_list_item { > * @granularity:migration granularity, log2 num pages > * @invalid: not 0 means cpu page table is invalidated > * @validate_timestamp: system timestamp when range is validated > + * @noop_migrate_start: start of the last migration that collected no > + pages > + * @noop_migrate_timestamp: system timestamp of that migration > * @notifier: register mmu interval notifier > * @work_item: deferred work item information > * @deferred_list: list header used to add range to deferred list @@ > -131,6 +133,8 @@ struct svm_range { > uint8_t granularity; > atomic_t invalid; > ktime_t validate_timestamp; > + unsigned long noop_migrate_start; > + ktime_t noop_migrate_timestamp; > struct mmu_interval_notifier notifier; > struct svm_work_list_item work_item; > struct list_head deferred_list;
