summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Zhang <Jesse.Zhang@amd.com>2026-09-17 10:50:25 +0800
committerAlex Deucher <alexander.deucher@amd.com>2026-10-01 11:21:44 -0400
commitaa1a19e18e5307f22b71c2399b3ea2ebea1350ef (patch)
treeff706af7ef8b179d4e1ac7d3dcbe185b8dfb218b
parentac3477ac78238c6c03fb97765262ef2f291f7235 (diff)
downloadlinux-next-aa1a19e18e5307f22b71c2399b3ea2ebea1350ef.tar.gz
linux-next-aa1a19e18e5307f22b71c2399b3ea2ebea1350ef.zip
drm/amdgpu/gfx: add a shared gfx pipe reset helper
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 <Jesse.Zhang@amd.com> Reviewed-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c114
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h16
2 files changed, 129 insertions, 1 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
index b91bb5166ea8..5241f12de407 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
@@ -2099,6 +2099,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 +2377,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 +3209,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)
{