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

Reply via email to