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]> --- 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)) -- 2.43.0
