AMD-GFX Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] drm/amdgpu/sdma: add detect_hung_queue callback
@ 2026-09-03  9:42 Jesse Zhang
  2026-09-03  9:42 ` [PATCH 2/4] drm/amdgpu/sdma6: implement detect_hung_queue Jesse Zhang
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Jesse Zhang @ 2026-09-03  9:42 UTC (permalink / raw)
  To: amd-gfx; +Cc: Alexander.Deucher, Christian Koenig, Jesse Zhang

Add an optional per-IP callback that maps a user queue's doorbell index
to its HW slot (instance, queue_id). It is needed to reset a hung SDMA
user queue over MMIO, since struct amdgpu_usermode_queue does not store
its HW slot.

Signed-off-by: Jesse Zhang <Jesse.Zhang@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h
index 671cfbb67b7a..659836c2c377 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h
@@ -59,6 +59,8 @@ struct amdgpu_sdma_funcs {
 	int (*stop_kernel_queue)(struct amdgpu_ring *ring);
 	int (*start_kernel_queue)(struct amdgpu_ring *ring);
 	int (*soft_reset_kernel_queue)(struct amdgpu_device *adev, u32 instance_id);
+	bool (*detect_hung_queue)(struct amdgpu_device *adev, u32 doorbell_index,
+				  u32 *instance_id, u32 *queue_id);
 };
 
 struct amdgpu_sdma_instance {
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/4] drm/amdgpu/sdma6: implement detect_hung_queue
  2026-09-03  9:42 [PATCH 1/4] drm/amdgpu/sdma: add detect_hung_queue callback Jesse Zhang
@ 2026-09-03  9:42 ` Jesse Zhang
  2026-09-03  9:42 ` [PATCH 3/4] drm/amdgpu/sdma7: " Jesse Zhang
  2026-09-03  9:42 ` [PATCH 4/4] drm/amdgpu/userq: reset a hung SDMA user queue over MMIO Jesse Zhang
  2 siblings, 0 replies; 5+ messages in thread
From: Jesse Zhang @ 2026-09-03  9:42 UTC (permalink / raw)
  To: amd-gfx; +Cc: Alexander.Deucher, Christian Koenig, Jesse Zhang

Match each SDMA queue's DOORBELL_OFFSET register against the given
doorbell to recover its (instance, queue_id) HW slot. The per-queue
register stride is derived from the named QUEUE0/QUEUE1 registers.

Signed-off-by: Jesse Zhang <Jesse.Zhang@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c | 33 ++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
index 303fd7d1b7c8..0b64eb5b32e9 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
@@ -1556,6 +1556,34 @@ static int sdma_v6_0_ring_preempt_ib(struct amdgpu_ring *ring)
 	return r;
 }
 
+/* dword stride between adjacent per-queue register banks */
+#define SDMA_V6_0_QUEUE_REG_STRIDE \
+	(regSDMA0_QUEUE1_RB_CNTL - regSDMA0_QUEUE0_RB_CNTL)
+
+/* find the HW slot (instance, queue_id) whose doorbell matches doorbell_index */
+static bool sdma_v6_0_detect_hung_queue(struct amdgpu_device *adev,
+					u32 doorbell_index,
+					u32 *instance_id, u32 *queue_id)
+{
+	u32 i, q, reg, dboff;
+
+	for (i = 0; i < adev->sdma.num_instances; i++) {
+		for (q = 0; q < 8; q++) {
+			reg = sdma_v6_0_get_reg_offset(adev, i,
+				regSDMA0_QUEUE0_DOORBELL_OFFSET +
+				q * SDMA_V6_0_QUEUE_REG_STRIDE);
+			dboff = (RREG32(reg) &
+				 SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET_MASK) >> 2;
+			if (dboff == doorbell_index) {
+				*instance_id = i;
+				*queue_id = q;
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
 static int sdma_v6_0_reset_queue(struct amdgpu_ring *ring,
 				 unsigned int vmid,
 				 struct amdgpu_fence *timedout_fence)
@@ -1762,6 +1790,10 @@ static const struct amdgpu_ring_funcs sdma_v6_0_ring_funcs = {
 	.reset = sdma_v6_0_reset_queue,
 };
 
+static const struct amdgpu_sdma_funcs sdma_v6_0_sdma_funcs = {
+	.detect_hung_queue = sdma_v6_0_detect_hung_queue,
+};
+
 static void sdma_v6_0_set_ring_funcs(struct amdgpu_device *adev)
 {
 	int i;
@@ -1769,6 +1801,7 @@ static void sdma_v6_0_set_ring_funcs(struct amdgpu_device *adev)
 	for (i = 0; i < adev->sdma.num_instances; i++) {
 		adev->sdma.instance[i].ring.funcs = &sdma_v6_0_ring_funcs;
 		adev->sdma.instance[i].ring.me = i;
+		adev->sdma.instance[i].funcs = &sdma_v6_0_sdma_funcs;
 	}
 }
 
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/4] drm/amdgpu/sdma7: implement detect_hung_queue
  2026-09-03  9:42 [PATCH 1/4] drm/amdgpu/sdma: add detect_hung_queue callback Jesse Zhang
  2026-09-03  9:42 ` [PATCH 2/4] drm/amdgpu/sdma6: implement detect_hung_queue Jesse Zhang
@ 2026-09-03  9:42 ` Jesse Zhang
  2026-09-03  9:42 ` [PATCH 4/4] drm/amdgpu/userq: reset a hung SDMA user queue over MMIO Jesse Zhang
  2 siblings, 0 replies; 5+ messages in thread
From: Jesse Zhang @ 2026-09-03  9:42 UTC (permalink / raw)
  To: amd-gfx; +Cc: Alexander.Deucher, Christian Koenig, Jesse Zhang

Match each SDMA queue's DOORBELL_OFFSET register against the given
doorbell to recover its (instance, queue_id) HW slot. The per-queue
register stride is derived from the named QUEUE0/QUEUE1 registers.

Signed-off-by: Jesse Zhang <Jesse.Zhang@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c | 33 ++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
index d5552f206e4d..c55c941b82cf 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
@@ -784,6 +784,34 @@ static int sdma_v7_0_soft_reset(struct amdgpu_ip_block *ip_block)
 	return sdma_v7_0_start(adev);
 }
 
+/* dword stride between adjacent per-queue register banks */
+#define SDMA_V7_0_QUEUE_REG_STRIDE \
+	(regSDMA0_QUEUE1_RB_CNTL - regSDMA0_QUEUE0_RB_CNTL)
+
+/* find the HW slot (instance, queue_id) whose doorbell matches doorbell_index */
+static bool sdma_v7_0_detect_hung_queue(struct amdgpu_device *adev,
+					u32 doorbell_index,
+					u32 *instance_id, u32 *queue_id)
+{
+	u32 i, q, reg, dboff;
+
+	for (i = 0; i < adev->sdma.num_instances; i++) {
+		for (q = 0; q < 8; q++) {
+			reg = sdma_v7_0_get_reg_offset(adev, i,
+				regSDMA0_QUEUE0_DOORBELL_OFFSET +
+				q * SDMA_V7_0_QUEUE_REG_STRIDE);
+			dboff = (RREG32(reg) &
+				 SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET_MASK) >> 2;
+			if (dboff == doorbell_index) {
+				*instance_id = i;
+				*queue_id = q;
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
 static int sdma_v7_0_reset_queue(struct amdgpu_ring *ring,
 				 unsigned int vmid,
 				 struct amdgpu_fence *timedout_fence)
@@ -1694,6 +1722,10 @@ static const struct amdgpu_ring_funcs sdma_v7_0_ring_funcs = {
 	.reset = sdma_v7_0_reset_queue,
 };
 
+static const struct amdgpu_sdma_funcs sdma_v7_0_sdma_funcs = {
+	.detect_hung_queue = sdma_v7_0_detect_hung_queue,
+};
+
 static void sdma_v7_0_set_ring_funcs(struct amdgpu_device *adev)
 {
 	int i;
@@ -1701,6 +1733,7 @@ static void sdma_v7_0_set_ring_funcs(struct amdgpu_device *adev)
 	for (i = 0; i < adev->sdma.num_instances; i++) {
 		adev->sdma.instance[i].ring.funcs = &sdma_v7_0_ring_funcs;
 		adev->sdma.instance[i].ring.me = i;
+		adev->sdma.instance[i].funcs = &sdma_v7_0_sdma_funcs;
 	}
 }
 
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 4/4] drm/amdgpu/userq: reset a hung SDMA user queue over MMIO
  2026-09-03  9:42 [PATCH 1/4] drm/amdgpu/sdma: add detect_hung_queue callback Jesse Zhang
  2026-09-03  9:42 ` [PATCH 2/4] drm/amdgpu/sdma6: implement detect_hung_queue Jesse Zhang
  2026-09-03  9:42 ` [PATCH 3/4] drm/amdgpu/sdma7: " Jesse Zhang
@ 2026-09-03  9:42 ` Jesse Zhang
  2026-09-03 18:25   ` Alex Deucher
  2 siblings, 1 reply; 5+ messages in thread
From: Jesse Zhang @ 2026-09-03  9:42 UTC (permalink / raw)
  To: amd-gfx; +Cc: Alexander.Deucher, Christian Koenig, Jesse Zhang

A hung SDMA user queue wedges MES, so the MES packet reset times out and
falls back to a full MODE1 reset - once per in-flight job, a reset storm.

The queue is still on its HW slot at the first hang-detect, so use
detect_hung_queue to recover its slot from the doorbell and reset it over
MMIO, which does not need MES. Mark it HUNG (not UNMAPPED) so the restore
worker does not re-map and re-run the guilty job, and short-circuit the
per-fence hang-detect re-entry once the queue is already reset.

Signed-off-by: Jesse Zhang <Jesse.Zhang@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 48 ++++++++++++++++++----
 1 file changed, 39 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
index 46ebc002548d..2ce94cd04fd5 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
@@ -213,26 +213,56 @@ int mes_userq_reset(struct amdgpu_usermode_queue *queue)
 	struct mes_reset_queue_input queue_input;
 	int r;
 
-	/* XXX: add a FW version check for SDMA per queue reset */
+	/* already reset by an earlier job's hang-detect; just signal and bail */
+	if (queue->state == AMDGPU_USERQ_STATE_HUNG) {
+		amdgpu_userq_fence_driver_force_completion(queue);
+		return 0;
+	}
+
 	memset(&queue_input, 0x0, sizeof(struct mes_reset_queue_input));
 	queue_input.doorbell_offset = queue->doorbell_index;
 	queue_input.queue_type = queue->queue_type;
 
+	/*
+	 * The MES packet reset fails once the hung queue wedges MES. For SDMA the
+	 * queue is still on its HW slot, so reset it over MMIO instead: recover
+	 * the (instance, queue_id) slot from the doorbell.
+	 */
+	if (queue->queue_type == AMDGPU_HW_IP_DMA &&
+	    adev->sdma.instance[0].funcs &&
+	    adev->sdma.instance[0].funcs->detect_hung_queue) {
+		u32 instance, hw_queue_id;
+
+		if (adev->sdma.instance[0].funcs->detect_hung_queue(adev,
+				queue->doorbell_index, &instance, &hw_queue_id)) {
+			queue_input.use_mmio = true;
+			queue_input.me_id = instance;
+			queue_input.queue_id = hw_queue_id;
+		} else {
+			dev_warn(adev->dev,
+				 "SDMA userq (doorbell %llu) not on any HW slot; falling back to MES reset\n",
+				 queue->doorbell_index);
+		}
+	}
+
+	/* HUNG, not UNMAPPED: the guilty job is still in the ring, so the
+	 * restore worker must not re-map and re-run it.
+	 */
+	queue->state = AMDGPU_USERQ_STATE_HUNG;
+
 	amdgpu_mes_lock(&adev->mes);
 	r = adev->mes.funcs->reset_hw_queue(&adev->mes, &queue_input);
 	amdgpu_mes_unlock(&adev->mes);
 	if (r)
 		return r;
 
-	/* mes_userq_unmap() does not update queue->state; mark it UNMAPPED so the
-	 * destroy path does not issue a second REMOVE_QUEUE for the removed queue.
-	 */
+	/* drop the queue from MES and force-complete its fences */
 	r = mes_userq_unmap(queue);
-	if (!r) {
-		trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_UNMAPPED);
-		queue->state = AMDGPU_USERQ_STATE_UNMAPPED;
-	}
-	return r;
+	if (r)
+		return r;
+
+	amdgpu_userq_fence_driver_force_completion(queue);
+	return 0;
 }
 
 int mes_userq_reset_queue(struct amdgpu_device *adev,
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 4/4] drm/amdgpu/userq: reset a hung SDMA user queue over MMIO
  2026-09-03  9:42 ` [PATCH 4/4] drm/amdgpu/userq: reset a hung SDMA user queue over MMIO Jesse Zhang
@ 2026-09-03 18:25   ` Alex Deucher
  0 siblings, 0 replies; 5+ messages in thread
From: Alex Deucher @ 2026-09-03 18:25 UTC (permalink / raw)
  To: Jesse Zhang; +Cc: amd-gfx, Alexander.Deucher, Christian Koenig

On Thu, Sep 3, 2026 at 6:20 AM Jesse Zhang <Jesse.Zhang@amd.com> wrote:
>
> A hung SDMA user queue wedges MES, so the MES packet reset times out and
> falls back to a full MODE1 reset - once per in-flight job, a reset storm.
>
> The queue is still on its HW slot at the first hang-detect, so use
> detect_hung_queue to recover its slot from the doorbell and reset it over
> MMIO, which does not need MES. Mark it HUNG (not UNMAPPED) so the restore
> worker does not re-map and re-run the guilty job, and short-circuit the
> per-fence hang-detect re-entry once the queue is already reset.

I thought newer versions of MES firmware handled this.  Maybe we need
a firmware version check?  For now, the series is:
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>

>
> Signed-off-by: Jesse Zhang <Jesse.Zhang@amd.com>
> ---
>  drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 48 ++++++++++++++++++----
>  1 file changed, 39 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
> index 46ebc002548d..2ce94cd04fd5 100644
> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
> @@ -213,26 +213,56 @@ int mes_userq_reset(struct amdgpu_usermode_queue *queue)
>         struct mes_reset_queue_input queue_input;
>         int r;
>
> -       /* XXX: add a FW version check for SDMA per queue reset */
> +       /* already reset by an earlier job's hang-detect; just signal and bail */
> +       if (queue->state == AMDGPU_USERQ_STATE_HUNG) {
> +               amdgpu_userq_fence_driver_force_completion(queue);
> +               return 0;
> +       }
> +
>         memset(&queue_input, 0x0, sizeof(struct mes_reset_queue_input));
>         queue_input.doorbell_offset = queue->doorbell_index;
>         queue_input.queue_type = queue->queue_type;
>
> +       /*
> +        * The MES packet reset fails once the hung queue wedges MES. For SDMA the
> +        * queue is still on its HW slot, so reset it over MMIO instead: recover
> +        * the (instance, queue_id) slot from the doorbell.
> +        */
> +       if (queue->queue_type == AMDGPU_HW_IP_DMA &&
> +           adev->sdma.instance[0].funcs &&
> +           adev->sdma.instance[0].funcs->detect_hung_queue) {
> +               u32 instance, hw_queue_id;
> +
> +               if (adev->sdma.instance[0].funcs->detect_hung_queue(adev,
> +                               queue->doorbell_index, &instance, &hw_queue_id)) {
> +                       queue_input.use_mmio = true;
> +                       queue_input.me_id = instance;
> +                       queue_input.queue_id = hw_queue_id;
> +               } else {
> +                       dev_warn(adev->dev,
> +                                "SDMA userq (doorbell %llu) not on any HW slot; falling back to MES reset\n",
> +                                queue->doorbell_index);
> +               }
> +       }
> +
> +       /* HUNG, not UNMAPPED: the guilty job is still in the ring, so the
> +        * restore worker must not re-map and re-run it.
> +        */
> +       queue->state = AMDGPU_USERQ_STATE_HUNG;
> +
>         amdgpu_mes_lock(&adev->mes);
>         r = adev->mes.funcs->reset_hw_queue(&adev->mes, &queue_input);
>         amdgpu_mes_unlock(&adev->mes);
>         if (r)
>                 return r;
>
> -       /* mes_userq_unmap() does not update queue->state; mark it UNMAPPED so the
> -        * destroy path does not issue a second REMOVE_QUEUE for the removed queue.
> -        */
> +       /* drop the queue from MES and force-complete its fences */
>         r = mes_userq_unmap(queue);
> -       if (!r) {
> -               trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_UNMAPPED);
> -               queue->state = AMDGPU_USERQ_STATE_UNMAPPED;
> -       }
> -       return r;
> +       if (r)
> +               return r;
> +
> +       amdgpu_userq_fence_driver_force_completion(queue);
> +       return 0;
>  }
>
>  int mes_userq_reset_queue(struct amdgpu_device *adev,
> --
> 2.49.0
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-03 18:26 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03  9:42 [PATCH 1/4] drm/amdgpu/sdma: add detect_hung_queue callback Jesse Zhang
2026-09-03  9:42 ` [PATCH 2/4] drm/amdgpu/sdma6: implement detect_hung_queue Jesse Zhang
2026-09-03  9:42 ` [PATCH 3/4] drm/amdgpu/sdma7: " Jesse Zhang
2026-09-03  9:42 ` [PATCH 4/4] drm/amdgpu/userq: reset a hung SDMA user queue over MMIO Jesse Zhang
2026-09-03 18:25   ` Alex Deucher

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox