[PATCH v3 3/4] drm/amdgpu/gfx12: recover gfx user queues on priv-fault
Jesse Zhang <[email protected]> Thu, 30 Jul 2026 10:58:28 +0800
| Newsgroups | org.freedesktop.lists.amd-gfx |
|---|---|
| Message-ID | <[email protected]> |
Mirror the gfx11 priv-fault user-queue recovery for GFX12, reading the doorbell back from the HQD via soc24_grbm_select. v2: - gate on adev->gfx.disable_uq instead of !adev->enable_mes (Alex) - document why both the doorbell (compute) and HW-slot (gfx) reset paths are needed (Alex) Suggested-by: Mario Sopena-Novales <[email protected]> Signed-off-by: Jesse Zhang <[email protected]> --- drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c | 64 +++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index b3887c5262a0..8fe991787974 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -286,6 +286,7 @@ static void gfx_v12_0_ring_invalidate_tlbs(struct amdgpu_ring *ring, bool all_hub, uint8_t dst_sel); static void gfx_v12_0_set_safe_mode(struct amdgpu_device *adev, int xcc_id); static void gfx_v12_0_unset_safe_mode(struct amdgpu_device *adev, int xcc_id); +static void gfx_v12_0_userq_priv_fault_work(struct work_struct *work); static void gfx_v12_0_update_perf_clk(struct amdgpu_device *adev, bool enable); @@ -1608,6 +1609,8 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block) mutex_init(&adev->gfx.mec.reset_mutex); + INIT_WORK(&adev->gfx.userq_priv_fault_work, gfx_v12_0_userq_priv_fault_work); + return 0; } @@ -1645,6 +1648,8 @@ static int gfx_v12_0_sw_fini(struct amdgpu_ip_block *ip_block) int i; struct amdgpu_device *adev = ip_block->adev; + cancel_work_sync(&adev->gfx.userq_priv_fault_work); + for (i = 0; i < adev->gfx.num_gfx_rings; i++) amdgpu_ring_fini(&adev->gfx.gfx_ring[i]); for (i = 0; i < adev->gfx.num_compute_rings; i++) @@ -5038,24 +5043,56 @@ static int gfx_v12_0_set_priv_inst_fault_state(struct amdgpu_device *adev, return 0; } +/* + * A gfx exception IV carries no doorbell. For each faulted slot, read the + * doorbell back from the HQD (left in place by the fatal fault), look up the + * user queue and kick its per-queue reset. + */ +static void gfx_v12_0_userq_priv_fault_work(struct work_struct *work) +{ + struct amdgpu_device *adev = + container_of(work, struct amdgpu_device, gfx.userq_priv_fault_work); + unsigned long slots = xchg(&adev->gfx.userq_priv_fault_slots, 0); + unsigned int id; + + for_each_set_bit(id, &slots, BITS_PER_LONG) { + u8 pipe = id & 0x3; + u8 queue = (id >> 2) & 0x7; + struct amdgpu_usermode_queue *q; + u32 db_ctrl, doorbell; + + amdgpu_gfx_off_ctrl(adev, false); + mutex_lock(&adev->srbm_mutex); + soc24_grbm_select(adev, 0, pipe, queue, 0); + db_ctrl = RREG32_SOC15(GC, 0, regCP_RB_DOORBELL_CONTROL); + soc24_grbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + amdgpu_gfx_off_ctrl(adev, true); + + doorbell = (db_ctrl & CP_RB_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >> + CP_RB_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + q = xa_load(&adev->userq_doorbell_xa, doorbell); + if (q) + amdgpu_userq_start_hang_detect_work(q); + } +} + static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) { u32 doorbell_offset = entry->src_data[0] & AMDGPU_CTXID0_DOORBELL_ID_MASK; + u8 me_id = (entry->ring_id & 0x0c) >> 2; + u8 pipe_id = (entry->ring_id & 0x03) >> 0; + u8 queue_id = (entry->ring_id & 0x70) >> 4; /* * Try KQ first by ring_id; UQ as fallback. KCQ and UQ never share * a HW slot (compute_hqd_mask contract). */ if (!adev->gfx.disable_kq) { - u8 me_id, pipe_id, queue_id; struct amdgpu_ring *ring; int i; - me_id = (entry->ring_id & 0x0c) >> 2; - pipe_id = (entry->ring_id & 0x03) >> 0; - queue_id = (entry->ring_id & 0x70) >> 4; - switch (me_id) { case 0: for (i = 0; i < adev->gfx.num_gfx_rings; i++) { @@ -5084,10 +5121,23 @@ static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev, } } - /* No KQ matched: HW slot is a MES-scheduled user queue. */ - if (adev->enable_mes && doorbell_offset) + /* No KQ matched: the faulting slot belongs to a user queue. */ + if (adev->gfx.disable_uq) + return; + + /* + * A compute user-queue fault IV carries the doorbell offset, so reset the + * queue directly from it. A gfx user-queue fault is raised by the ME and + * carries only the HW slot (no doorbell); record the slot and let the + * worker read the doorbell back from the HQD. + */ + if (doorbell_offset) { amdgpu_userq_process_reset_irq(adev, entry->pasid, doorbell_offset); + } else { + set_bit(pipe_id | (queue_id << 2), &adev->gfx.userq_priv_fault_slots); + schedule_work(&adev->gfx.userq_priv_fault_work); + } } static int gfx_v12_0_priv_reg_irq(struct amdgpu_device *adev, -- 2.49.0