Skip to content

Commit a0c55ec

Browse files
PhilipYangAalexdeucher
authored andcommitted
drm/amdkfd: process exit and retry fault race
kfd_process_wq_release drain retry fault to ensure no retry fault comes after removing kfd process from the hash table, otherwise svm page fault handler will fail to recover the fault and dump GPU vm fault log. Refactor deferred list work to get_task_mm and take mmap write lock to handle all ranges, and avoid mm is gone while inserting mmu notifier. Signed-off-by: Philip Yang <[email protected]> Reviewed-by: Felix Kuehling <[email protected]> Signed-off-by: Alex Deucher <[email protected]>
1 parent 514f4a9 commit a0c55ec

File tree

1 file changed

+36
-27
lines changed

1 file changed

+36
-27
lines changed

drivers/gpu/drm/amd/amdkfd/kfd_svm.c

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,43 +1979,42 @@ static void svm_range_deferred_list_work(struct work_struct *work)
19791979
struct svm_range_list *svms;
19801980
struct svm_range *prange;
19811981
struct mm_struct *mm;
1982+
struct kfd_process *p;
19821983

19831984
svms = container_of(work, struct svm_range_list, deferred_list_work);
19841985
pr_debug("enter svms 0x%p\n", svms);
19851986

1987+
p = container_of(svms, struct kfd_process, svms);
1988+
/* Avoid mm is gone when inserting mmu notifier */
1989+
mm = get_task_mm(p->lead_thread);
1990+
if (!mm) {
1991+
pr_debug("svms 0x%p process mm gone\n", svms);
1992+
return;
1993+
}
1994+
retry:
1995+
mmap_write_lock(mm);
1996+
1997+
/* Checking for the need to drain retry faults must be inside
1998+
* mmap write lock to serialize with munmap notifiers.
1999+
*/
2000+
if (unlikely(READ_ONCE(svms->drain_pagefaults))) {
2001+
WRITE_ONCE(svms->drain_pagefaults, false);
2002+
mmap_write_unlock(mm);
2003+
svm_range_drain_retry_fault(svms);
2004+
goto retry;
2005+
}
2006+
19862007
spin_lock(&svms->deferred_list_lock);
19872008
while (!list_empty(&svms->deferred_range_list)) {
19882009
prange = list_first_entry(&svms->deferred_range_list,
19892010
struct svm_range, deferred_list);
2011+
list_del_init(&prange->deferred_list);
19902012
spin_unlock(&svms->deferred_list_lock);
2013+
19912014
pr_debug("prange 0x%p [0x%lx 0x%lx] op %d\n", prange,
19922015
prange->start, prange->last, prange->work_item.op);
19932016

1994-
mm = prange->work_item.mm;
1995-
retry:
1996-
mmap_write_lock(mm);
19972017
mutex_lock(&svms->lock);
1998-
1999-
/* Checking for the need to drain retry faults must be in
2000-
* mmap write lock to serialize with munmap notifiers.
2001-
*
2002-
* Remove from deferred_list must be inside mmap write lock,
2003-
* otherwise, svm_range_list_lock_and_flush_work may hold mmap
2004-
* write lock, and continue because deferred_list is empty, then
2005-
* deferred_list handle is blocked by mmap write lock.
2006-
*/
2007-
spin_lock(&svms->deferred_list_lock);
2008-
if (unlikely(svms->drain_pagefaults)) {
2009-
svms->drain_pagefaults = false;
2010-
spin_unlock(&svms->deferred_list_lock);
2011-
mutex_unlock(&svms->lock);
2012-
mmap_write_unlock(mm);
2013-
svm_range_drain_retry_fault(svms);
2014-
goto retry;
2015-
}
2016-
list_del_init(&prange->deferred_list);
2017-
spin_unlock(&svms->deferred_list_lock);
2018-
20192018
mutex_lock(&prange->migrate_mutex);
20202019
while (!list_empty(&prange->child_list)) {
20212020
struct svm_range *pchild;
@@ -2031,12 +2030,13 @@ static void svm_range_deferred_list_work(struct work_struct *work)
20312030

20322031
svm_range_handle_list_op(svms, prange);
20332032
mutex_unlock(&svms->lock);
2034-
mmap_write_unlock(mm);
20352033

20362034
spin_lock(&svms->deferred_list_lock);
20372035
}
20382036
spin_unlock(&svms->deferred_list_lock);
20392037

2038+
mmap_write_unlock(mm);
2039+
mmput(mm);
20402040
pr_debug("exit svms 0x%p\n", svms);
20412041
}
20422042

@@ -2589,7 +2589,7 @@ svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid,
25892589
p = kfd_lookup_process_by_pasid(pasid);
25902590
if (!p) {
25912591
pr_debug("kfd process not founded pasid 0x%x\n", pasid);
2592-
return -ESRCH;
2592+
return 0;
25932593
}
25942594
if (!p->xnack_enabled) {
25952595
pr_debug("XNACK not enabled for pasid 0x%x\n", pasid);
@@ -2600,10 +2600,12 @@ svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid,
26002600

26012601
pr_debug("restoring svms 0x%p fault address 0x%llx\n", svms, addr);
26022602

2603+
/* p->lead_thread is available as kfd_process_wq_release flush the work
2604+
* before releasing task ref.
2605+
*/
26032606
mm = get_task_mm(p->lead_thread);
26042607
if (!mm) {
26052608
pr_debug("svms 0x%p failed to get mm\n", svms);
2606-
r = -ESRCH;
26072609
goto out;
26082610
}
26092611

@@ -2730,6 +2732,13 @@ void svm_range_list_fini(struct kfd_process *p)
27302732
/* Ensure list work is finished before process is destroyed */
27312733
flush_work(&p->svms.deferred_list_work);
27322734

2735+
/*
2736+
* Ensure no retry fault comes in afterwards, as page fault handler will
2737+
* not find kfd process and take mm lock to recover fault.
2738+
*/
2739+
svm_range_drain_retry_fault(&p->svms);
2740+
2741+
27332742
list_for_each_entry_safe(prange, next, &p->svms.list, list) {
27342743
svm_range_unlink(prange);
27352744
svm_range_remove_notifier(prange);

0 commit comments

Comments
 (0)