When a job times out the kernel tries to reset the GPU to get
the system back to a normal state.
Sometimes it's useful to disable this process - for instance to
be able to inspect the hardware state at the time of the hang.
Since leaving fence unsignalled might affect the system's
stability, this commit enables the drm wedge framework for amdgpu.

When a hang condition is detected, the GPU isn't reset but all
the pending fences are signalled and the hung GPU cannot receive
new work. For KFD it's implemented by making
kfd_process_device_data_by_id return NULL if the requested GPU
is wedged.

When the user is done, it's possible to trigger a GPU
reset.

The auto-recovery is still enabled by default, and to use this
feature one has to either:
* boot with amdgpu.gpu_recovery=0: not recommended as it disables
  recovery for all GPUs
* write 0 to /sys/kernel/debug/dri/X/amdgpu_gpu_recover to disable
  auto-recovery for one GPU only

Runtime power management is disabled when the device is wedged
(because we can't submit any work to the GPU).

Assisted-by: Claude:Sonnet 5
Signed-off-by: Pierre-Eric Pelloux-Prayer <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu.h        | 13 +++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |  4 +++
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c    |  5 ++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c    |  3 ++
 drivers/gpu/drm/amd/amdgpu/amdgpu_job.c    | 34 ++++++++++++++++++++--
 drivers/gpu/drm/amd/amdkfd/kfd_process.c   |  6 +++-
 6 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 21b33dc34edf..039a92f2eb0f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -964,6 +964,13 @@ struct amdgpu_device {
         */
        bool gpu_recovery_allowed;
 
+       /* Set to 1 when the device has been declared wedged following a
+        * hang: it no longer accepts new work and pending fences have been
+        * force-signalled. Reset to 0 once the device is reset back to a
+        * working state.
+        */
+       atomic_t wedge_status;
+
        /* KFD
         * Must be last --ends in a flexible-array member.
         */
@@ -1349,4 +1356,10 @@ void amdgpu_device_set_uid(struct amdgpu_uid *uid_info,
                           uint64_t uid);
 uint64_t amdgpu_device_get_uid(struct amdgpu_uid *uid_info,
                               enum amdgpu_uid_type type, uint8_t inst);
+
+static inline bool amdgpu_device_is_wedged(struct amdgpu_device *adev)
+{
+       return atomic_read(&adev->wedge_status);
+}
+
 #endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 5578d5f64937..949de13d7997 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -4035,6 +4035,8 @@ int amdgpu_device_init(struct amdgpu_device *adev,
        }
 
        adev->gpu_recovery_allowed = true;
+       if (atomic_xchg(&adev->wedge_status, 0))
+               pm_runtime_put_autosuspend(adev->dev);
 
 fence_driver_init:
        /* Fence driver */
@@ -5745,6 +5747,8 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
                dev_info(adev->dev, "GPU reset end with ret = %d\n", r);
 
        atomic_set(&adev->reset_domain->reset_res, r);
+       if (atomic_xchg(&adev->wedge_status, 0))
+               pm_runtime_put_autosuspend(adev->dev);
 
        if (!r) {
                struct amdgpu_task_info *ti = NULL;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 0ab380ca7e64..399e935df7b2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -3006,10 +3006,15 @@ long amdgpu_drm_ioctl(struct file *filp,
                      unsigned int cmd, unsigned long arg)
 {
        struct drm_file *file_priv = filp->private_data;
+       struct amdgpu_device *adev;
        struct drm_device *dev;
        long ret;
 
        dev = file_priv->minor->dev;
+       adev = drm_to_adev(dev);
+       if (amdgpu_device_is_wedged(adev))
+               return -ENODEV;
+
        ret = pm_runtime_get_sync(dev->dev);
        if (ret < 0)
                goto out;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
index 3dc8faa091d7..f43d85ba4b78 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
@@ -716,6 +716,9 @@ void amdgpu_gmc_flush_gpu_tlb(struct amdgpu_device *adev, 
uint32_t vmid,
        struct amdgpu_job *job;
        int r;
 
+       if (amdgpu_device_is_wedged(adev))
+               return;
+
        ring = to_amdgpu_ring(adev->mman.buffer_funcs_scheds[0]);
 
        if (!hub->sdma_invalidation_workaround || vmid ||
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index 43511e0419a1..41d083646da5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -24,6 +24,7 @@
 #include <linux/kthread.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
+#include <linux/pm_runtime.h>
 
 #include <drm/drm_drv.h>
 
@@ -185,9 +186,35 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct 
drm_sched_job *s_job)
                if (r)
                        dev_err(adev->dev, "GPU Recovery Failed: %d\n", r);
        } else {
-               drm_sched_suspend_timeout(&ring->sched);
-               if (amdgpu_sriov_vf(adev))
+               if (amdgpu_sriov_vf(adev)) {
+                       drm_sched_suspend_timeout(&ring->sched);
                        adev->virt.tdr_debug = true;
+               } else {
+                       struct drm_gpu_scheduler *sched;
+                       struct amdgpu_fence *guilty_fence;
+
+                       /* Declare the device as wedged if it's not already. */
+                       if (!atomic_xchg(&adev->wedge_status, 1)) {
+                               pm_runtime_get_sync(adev->dev);
+
+                               pci_clear_master(adev->pdev);
+
+                               drm_dev_wedged_event(&adev->ddev, 
DRM_WEDGE_RECOVERY_REBIND |
+                                               DRM_WEDGE_RECOVERY_BUS_RESET, 
NULL);
+                       }
+
+                       guilty_fence = to_amdgpu_job(s_job)->hw_fence;
+
+                       sched = &ring->sched;
+
+                       /* Prevent anybody else from touching the ring buffer. 
*/
+                       drm_sched_wqueue_stop(sched);
+
+                       amdgpu_fence_driver_force_completion(ring, 
&guilty_fence->base);
+
+                       /* Start the scheduler again */
+                       drm_sched_wqueue_start(sched);
+               }
        }
 
 exit:
@@ -453,7 +480,8 @@ static struct dma_fence *amdgpu_job_run(struct 
drm_sched_job *sched_job)
 
        /* Skip job if VRAM is lost and never resubmit gangs */
        if (job->generation != amdgpu_vm_generation(adev, job->vm) ||
-           (job->job_run_counter && job->gang_submit))
+           (job->job_run_counter && job->gang_submit) ||
+           amdgpu_device_is_wedged(adev))
                dma_fence_set_error(finished, -ECANCELED);
 
        if (finished->error < 0) {
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c 
b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index 0a7c1900da95..544dc960833b 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -2433,8 +2433,12 @@ struct kfd_process_device 
*kfd_process_device_data_by_id(struct kfd_process *p,
                for (i = 0; i < p->n_pdds; i++) {
                        struct kfd_process_device *pdd = p->pdds[i];
 
-                       if (pdd->user_gpu_id == gpu_id)
+                       if (pdd->user_gpu_id == gpu_id) {
+                               if (amdgpu_device_is_wedged(pdd->dev->adev))
+                                       return NULL;
+
                                return pdd;
+                       }
                }
        }
        return NULL;
-- 
2.43.0

Reply via email to