* [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