drm/amdgpu/gfx11: add priv-fault user-queue recovery worker

A gfx user-queue priv/bad-op fault is raised by the ME and carries only
the HW slot, not the faulting queue's doorbell. Add a per-IP worker that
drains adev->gfx.userq_priv_fault_slots, reads the doorbell back from each
HQD via soc21_grbm_select (regCP_RB_DOORBELL_CONTROL), looks up the user
queue and kicks its per-queue reset.

The shared amdgpu_gfx_handle_priv_fault() helper schedules this worker for
a gfx user-queue fault; wiring the helper up is done in a later patch.

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)

v3:
 - rebase amd-staging-drm-next. adapt to the commit 9243cf4777 ("drm/amd/amdgpu: remove duplicated code in gfx_v11 and gfx_v12"); no functional change

Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Suggested-by: Mario Sopena-Novales <Mario.Novales@amd.com>
Signed-off-by: Jesse Zhang <Jesse.Zhang@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Jesse Zhang
2026-07-31 12:36:25 +08:00
committed by Alex Deucher
parent 9ad81600b9
commit 2b1e74c07d

View File

@@ -362,6 +362,7 @@ static void gfx_v11_0_ring_invalidate_tlbs(struct amdgpu_ring *ring,
bool all_hub, uint8_t dst_sel);
static void gfx_v11_0_set_safe_mode(struct amdgpu_device *adev, int xcc_id);
static void gfx_v11_0_unset_safe_mode(struct amdgpu_device *adev, int xcc_id);
static void gfx_v11_0_userq_priv_fault_work(struct work_struct *work);
static void gfx_v11_0_update_perf_clk(struct amdgpu_device *adev,
bool enable);
@@ -1932,6 +1933,8 @@ static int gfx_v11_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_v11_0_userq_priv_fault_work);
return 0;
}
@@ -1969,6 +1972,8 @@ static int gfx_v11_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++)
@@ -6711,6 +6716,40 @@ static int gfx_v11_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_v11_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);
soc21_grbm_select(adev, 0, pipe, queue, 0);
db_ctrl = RREG32_SOC15(GC, 0, regCP_RB_DOORBELL_CONTROL);
soc21_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_v11_0_handle_priv_fault(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{