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. Call amdgpu_amdkfd_pre_reset() before the lock, for gpu_recover and PCI error handling. Skip the dequeue when the HWS is hung or the PCI link is down, so recovery does not wait out the preemption timeout. Signed-off-by: William Palacek <[email protected]> --- Notes: v2: - Drop the SRC_USER gate and the helper; run for every reset source - Cover amdgpu_pci_error_detected() as well as gpu_recover() - Skip the dequeue when the HWS is hung or the PCI link is down drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 16 ++++++++++++++-- .../drm/amd/amdkfd/kfd_device_queue_manager.c | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index e0014611f039..8ae6228dbf31 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -5479,6 +5479,16 @@ static void amdgpu_device_recovery_prepare(struct amdgpu_device *adev, } } +/* Dequeueing needs reset_domain->sem for read, so run before it is write-locked */ +static void amdgpu_device_kfd_pre_reset(struct amdgpu_reset_context *reset_context, + struct list_head *device_list) +{ + struct amdgpu_device *tmp_adev; + + list_for_each_entry(tmp_adev, device_list, reset_list) + amdgpu_amdkfd_pre_reset(tmp_adev, reset_context); +} + static void amdgpu_device_recovery_get_reset_lock(struct amdgpu_device *adev, struct list_head *device_list) { @@ -5534,8 +5544,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 +5792,9 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, /* Cannot be called after locking reset domain */ amdgpu_ras_pre_reset(adev, &device_list); + /* Cannot be called after locking reset domain either */ + amdgpu_device_kfd_pre_reset(reset_context, &device_list); + /* We need to lock reset domain only once both for XGMI and single device */ amdgpu_device_recovery_get_reset_lock(adev, &device_list); @@ -6232,6 +6243,7 @@ pci_ers_result_t amdgpu_pci_error_detected(struct pci_dev *pdev, pci_channel_sta INIT_LIST_HEAD(&device_list); amdgpu_device_recovery_prepare(adev, &device_list, hive); + amdgpu_device_kfd_pre_reset(&reset_context, &device_list); amdgpu_device_recovery_get_reset_lock(adev, &device_list); amdgpu_device_halt_activities(adev, NULL, &reset_context, &device_list, hive, false); 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..988fce11a4fb 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -81,6 +81,16 @@ static void set_queue_as_reset(struct device_queue_manager *dqm, struct queue *q struct qcm_process_device *qpd, bool verbose); static int reset_queues_mes(struct device_queue_manager *dqm, struct queue *q); +/* + * A dead HWS or an offline PCI link cannot service a dequeue request. Skip it + * rather than wait out queue_preemption_timeout_ms per node. + */ +static inline bool kfd_dqm_can_reach_hw(struct device_queue_manager *dqm) +{ + return !dqm->is_hws_hang && + dqm->dev->adev->pci_channel_state == pci_channel_io_normal; +} + static inline enum KFD_MQD_TYPE get_mqd_type_from_queue_type(enum kfd_queue_type type) { @@ -314,6 +324,8 @@ static int remove_queue_mes_on_reset_option(struct device_queue_manager *dqm, st if (!dqm->sched_running || dqm->sched_halt) return 0; + if (!kfd_dqm_can_reach_hw(dqm)) + return 0; if (!down_read_trylock(&adev->reset_domain->sem)) return -EIO; @@ -2832,6 +2844,8 @@ static int unmap_queues_cpsch(struct device_queue_manager *dqm, if (!dqm->sched_running) return 0; + if (!kfd_dqm_can_reach_hw(dqm)) + return 0; if (!dqm->active_runlist) return 0; if (!down_read_trylock(&dqm->dev->adev->reset_domain->sem)) -- 2.43.0
