On 2026-10-10 12:27, William Palacek wrote:
A GPU reset issued while processes are using the device can Oops the
host in munmap() (free_swap_cache) after SDMA write faults from a user
queue whose process has already exited.
amdgpu_device_gpu_recover() takes reset_domain->sem for write before
amdgpu_amdkfd_pre_reset(), so the down_read_trylock() in
unmap_queues_cpsch() and execute_queues_cpsch() fails with -EIO and no
unmap is sent. stop_cpsch() still clears sched_running, so user queues
keep running while their memory is freed.
Make amdgpu_amdkfd_pre_reset() take the reset device list and call it
once from gpu_recover() before the reset domain is locked. PCI error
handling keeps calling it under the lock, as before. Return -EIO from
unmap_queues_cpsch() when the HWS is already known to be hung, instead
of waiting out the preemption timeout again.
Signed-off-by: William Palacek <[email protected]>
Reviewed-by: Felix Kuehling <[email protected]>
---
v3:
- Make amdgpu_amdkfd_pre_reset() take the device list instead of adding a
similarly named wrapper (Felix)
- Drop kfd_dqm_can_reach_hw(); pci_channel_state is only valid after
amdgpu_pci_error_detected() (Felix)
- Return -EIO from unmap_queues_cpsch() when the HWS is hung (Felix)
- PCI error handling keeps calling pre_reset under the reset lock
v2:
- Drop the SRC_USER gate and the helper; run for every reset source
- Cover amdgpu_pci_error_detected() as well as gpu_recover()
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c | 13 ++++++-------
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h | 4 ++--
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 6 ++++--
.../gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 2 ++
4 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
index e023cc734ebc..fcd857fb7d09 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
@@ -314,15 +314,14 @@ int amdgpu_amdkfd_resume_process(struct amdgpu_device
*adev)
return r;
}
-int amdgpu_amdkfd_pre_reset(struct amdgpu_device *adev,
- struct amdgpu_reset_context *reset_context)
+void amdgpu_amdkfd_pre_reset(struct list_head *device_list,
+ struct amdgpu_reset_context *reset_context)
{
- int r = 0;
-
- if (adev->kfd.dev)
- r = kgd2kfd_pre_reset(adev->kfd.dev, reset_context);
+ struct amdgpu_device *adev;
- return r;
+ list_for_each_entry(adev, device_list, reset_list)
+ if (adev->kfd.dev)
+ kgd2kfd_pre_reset(adev->kfd.dev, reset_context);
}
int amdgpu_amdkfd_post_reset(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
index e3da382ceb3c..7419d3614c47 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
@@ -180,8 +180,8 @@ bool amdgpu_amdkfd_have_atomics_support(struct
amdgpu_device *adev);
bool amdgpu_amdkfd_is_kfd_vmid(struct amdgpu_device *adev, u32 vmid);
-int amdgpu_amdkfd_pre_reset(struct amdgpu_device *adev,
- struct amdgpu_reset_context *reset_context);
+void amdgpu_amdkfd_pre_reset(struct list_head *device_list,
+ struct amdgpu_reset_context *reset_context);
int amdgpu_amdkfd_post_reset(struct amdgpu_device *adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index e0014611f039..7e928d09db34 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -5534,8 +5534,6 @@ static void amdgpu_device_halt_activities(struct
amdgpu_device *adev,
cancel_delayed_work_sync(&tmp_adev->delayed_init_work);
- amdgpu_amdkfd_pre_reset(tmp_adev, reset_context);
-
/*
* Mark these ASICs to be reset as untracked first
* And add them back after reset completed
@@ -5784,6 +5782,9 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
/* Cannot be called after locking reset domain */
amdgpu_ras_pre_reset(adev, &device_list);
+ /* Dequeueing takes reset_domain->sem for read */
+ amdgpu_amdkfd_pre_reset(&device_list, reset_context);
+
/* We need to lock reset domain only once both for XGMI and single
device */
amdgpu_device_recovery_get_reset_lock(adev, &device_list);
@@ -6233,6 +6234,7 @@ pci_ers_result_t amdgpu_pci_error_detected(struct pci_dev *pdev, pci_channel_sta
amdgpu_device_recovery_prepare(adev, &device_list, hive);
amdgpu_device_recovery_get_reset_lock(adev, &device_list);
+ amdgpu_amdkfd_pre_reset(&device_list, &reset_context);
amdgpu_device_halt_activities(adev, NULL, &reset_context,
&device_list,
hive, false);
if (hive)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 5e57576fba21..c2b069832dbe 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -2832,6 +2832,8 @@ static int unmap_queues_cpsch(struct device_queue_manager
*dqm,
if (!dqm->sched_running)
return 0;
+ if (dqm->is_hws_hang)
+ return -EIO;
if (!dqm->active_runlist)
return 0;
if (!down_read_trylock(&dqm->dev->adev->reset_domain->sem))