Serialize GFX recovery with mec.reset_mutex and attempt MES suspend
before locating a user HQD. Back up collateral kernel rings before reset,
then resume MES before testing and replaying rebuilt rings.

Allow reset to proceed if suspend fails. Always attempt resume, report
reset or resume errors, and leave schedulers stopped on failure.

Signed-off-by: Jesse Zhang <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 115 +++++++++++++++++++++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h |  16 ++++
 2 files changed, 129 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
index b91bb5166ea8..2da839228911 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
@@ -1704,7 +1704,6 @@ static ssize_t 
compute_partition_mem_alloc_mode_show(struct device *dev,
                          amdgpu_gfx_compute_mem_alloc_mode_desc(mode));
 }
 

 static ssize_t compute_partition_mem_alloc_mode_store(struct device *dev,
                                                struct device_attribute *addr,
                                                const char *buf, size_t count)
@@ -2099,6 +2098,84 @@ static int amdgpu_gfx_mes_reset_queue_start(struct 
amdgpu_ring *ring,
        return 0;
 }
 
+/*
+ * Reset one GFX queue and preserve collateral kernel rings on its pipe.
+ * Exactly one of @ring / @queue is non-NULL.
+ * This helper takes the reset mutex; callers must not already hold it.
+ */
+int amdgpu_gfx_reset_mes_gfx(struct amdgpu_device *adev,
+                            struct amdgpu_ring *ring,
+                            struct amdgpu_fence *guilty_fence,
+                            struct amdgpu_usermode_queue *queue,
+                            unsigned int vmid, bool use_mmio)
+{
+       struct mes_reset_queue_input queue_input = { 0 };
+       struct amdgpu_gfx_pipe_reset_ctx ctx;
+       u32 me = U32_MAX, pipe = U32_MAX;
+       int i, r, resume_r;
+
+       mutex_lock(&adev->gfx.mec.reset_mutex);
+
+       /*
+        * Suspend before detecting the HQD to avoid resolving a slot MES 
remaps.
+        * A hung queue can prevent suspend; still try reset and always resume.
+        */
+       amdgpu_mes_suspend(adev, 0);
+
+       if (queue) {
+               u32 hw_queue_id;
+
+               queue_input.queue_type = queue->queue_type;
+               queue_input.doorbell_offset = queue->doorbell_index;
+               if (adev->gfx.funcs->detect_hung_queue(adev,
+                               queue->doorbell_index, &me, &pipe,
+                               &hw_queue_id)) {
+                       queue_input.use_mmio = true;
+                       queue_input.me_id = me;
+                       queue_input.pipe_id = pipe;
+                       queue_input.queue_id = hw_queue_id;
+               }
+       } else {
+               me = ring->me;
+               pipe = ring->pipe;
+       }
+
+       amdgpu_gfx_pipe_reset_prepare(adev, ring, me, pipe, &ctx);
+
+       if (ring)
+               r = amdgpu_gfx_mes_reset_queue_start(ring, vmid, guilty_fence,
+                                                    use_mmio);
+       else
+               r = mes_userq_reset_hw(queue, &queue_input);
+
+       resume_r = amdgpu_mes_resume(adev, 0);
+       if (!r)
+               r = resume_r;
+
+       /* MES must run before ring tests or replay. */
+       if (!r && ring)
+               r = amdgpu_ring_reset_helper_end(ring, guilty_fence);
+
+       for (i = 0; !r && i < adev->gfx.num_gfx_rings; i++) {
+               struct amdgpu_ring *cring = &adev->gfx.gfx_ring[i];
+
+               /* MQD rebuild clears wptr; replay only rings rebuilt by pipe 
reset. */
+               if ((ctx.replay_mask & BIT(i)) && !cring->wptr)
+                       r = amdgpu_ring_reset_helper_end(cring, NULL);
+       }
+
+       /* Leave stopped schedulers to GPU recovery if any part failed. */
+       if (!r) {
+               for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
+                       if (ctx.sched_mask & BIT(i))
+                               
drm_sched_wqueue_start(&adev->gfx.gfx_ring[i].sched);
+               }
+       }
+
+       mutex_unlock(&adev->gfx.mec.reset_mutex);
+       return r;
+}
+
 int amdgpu_gfx_mes_reset_queue(struct amdgpu_ring *ring,
                               unsigned int vmid,
                               struct amdgpu_fence *timedout_fence,
@@ -2299,6 +2376,41 @@ static void amdgpu_gfx_reset_stop_compute_scheds(struct 
amdgpu_device *adev,
        }
 }
 
+/* Caller holds reset_mutex and attempted MES suspend before HQD lookup. */
+void amdgpu_gfx_pipe_reset_prepare(struct amdgpu_device *adev,
+                                  struct amdgpu_ring *guilty_ring,
+                                  u32 me, u32 pipe,
+                                  struct amdgpu_gfx_pipe_reset_ctx *ctx)
+{
+       struct amdgpu_ring *ring;
+       int i;
+
+       lockdep_assert_held(&adev->gfx.mec.reset_mutex);
+       ctx->sched_mask = 0;
+       ctx->replay_mask = 0;
+
+       for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
+               ring = &adev->gfx.gfx_ring[i];
+               /* An unresolved userq slot requires protecting every GFX pipe. 
*/
+               if (ring == guilty_ring ||
+                   (me != U32_MAX && ring->me != me) ||
+                   (pipe != U32_MAX && ring->pipe != pipe) ||
+                   !amdgpu_ring_sched_ready(ring))
+                       continue;
+
+               if (!drm_sched_is_stopped(&ring->sched)) {
+                       drm_sched_wqueue_stop(&ring->sched);
+                       ctx->sched_mask |= BIT(i);
+               }
+
+               if (amdgpu_fence_count_emitted(ring)) {
+                       amdgpu_ring_reset_helper_begin(ring, NULL);
+                       if (ring->ring_backup_entries_to_copy)
+                               ctx->replay_mask |= BIT(i);
+               }
+       }
+}
+
 /*
  * Match the MES-reported hung doorbell against a compute ring and run
  * the reset. On hit, the matched ring and its guilty fence are returned
@@ -3096,4 +3208,3 @@ int amdgpu_gfx_ring_preempt_ib(struct amdgpu_ring *ring)
 
        return 0;
 }
-
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
index c948fd2130a0..855651f0c930 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
@@ -700,6 +700,22 @@ int amdgpu_gfx_mes_reset_queue(struct amdgpu_ring *ring,
                               struct amdgpu_fence *timedout_fence,
                               bool use_mmio);
 void amdgpu_gfx_mqd_reset_restore(struct amdgpu_ring *ring);
+struct amdgpu_gfx_pipe_reset_ctx {
+       /* Schedulers stopped by this reset. */
+       unsigned long sched_mask;
+       /* Rings with backed-up commands. */
+       unsigned long replay_mask;
+};
+
+void amdgpu_gfx_pipe_reset_prepare(struct amdgpu_device *adev,
+                                  struct amdgpu_ring *guilty_ring,
+                                  u32 me, u32 pipe,
+                                  struct amdgpu_gfx_pipe_reset_ctx *ctx);
+int amdgpu_gfx_reset_mes_gfx(struct amdgpu_device *adev,
+                            struct amdgpu_ring *ring,
+                            struct amdgpu_fence *guilty_fence,
+                            struct amdgpu_usermode_queue *queue,
+                            unsigned int vmid, bool use_mmio);
 
 static inline const char *amdgpu_gfx_compute_mode_desc(int mode)
 {
-- 
2.49.0

Reply via email to