* [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure
@ 2026-10-13 8:24 Prike Liang
2026-10-13 8:24 ` [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU reset Prike Liang
` (8 more replies)
0 siblings, 9 replies; 18+ messages in thread
From: Prike Liang @ 2026-10-13 8:24 UTC (permalink / raw)
To: amd-gfx
Cc: Alexander.Deucher, Christian.Koenig, Lijo.Lazar, Vitaly.Prosyak,
Prike Liang
On userq eviction failure, the suspend worker waits for reset_work
while holding userq_mutex and inside a DMA-fence signaling section.
This makes eviction-fence completion depend on GPU recovery finishing,
although recovery can itself require memory allocation and eviction.
Fixes: c8ed2de0f2ee ("drm/amdgpu: rework userq reset work handling")
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c | 12 +++++++++---
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 +++-------
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 2 +-
3 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
index 082d045edf45..c2d1bc7decba 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
@@ -65,6 +65,7 @@ amdgpu_eviction_fence_suspend_worker(struct work_struct *work)
struct amdgpu_userq_mgr *uq_mgr = &fpriv->userq_mgr;
struct dma_fence *ev_fence;
bool cookie;
+ int r;
mutex_lock(&uq_mgr->userq_mutex);
@@ -79,21 +80,26 @@ amdgpu_eviction_fence_suspend_worker(struct work_struct *work)
cookie = dma_fence_begin_signalling();
ev_fence = amdgpu_evf_mgr_get_fence(evf_mgr);
- amdgpu_userq_evict(uq_mgr);
+ r = amdgpu_userq_evict(uq_mgr);
/*
* Signaling the eviction fence must be done while holding the
* userq_mutex. Otherwise we won't resume the queues before issuing the
* next fence.
*/
- dma_fence_signal(ev_fence);
+ if (!r)
+ dma_fence_signal(ev_fence);
dma_fence_end_signalling(cookie);
dma_fence_put(ev_fence);
- if (!evf_mgr->shutdown)
+ if (!r && !evf_mgr->shutdown)
schedule_delayed_work(&uq_mgr->resume_work, 0);
mutex_unlock(&uq_mgr->userq_mutex);
+
+ if (r)
+ amdgpu_reset_domain_schedule(uq_mgr->adev->reset_domain,
+ &uq_mgr->reset_work);
}
int amdgpu_evf_mgr_attach_fence(struct amdgpu_eviction_fence_mgr *evf_mgr,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 3fe91fdafe55..5fef0cc7703c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -1308,13 +1308,9 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
ret = r;
}
- if (ret) {
+ if (ret)
drm_file_err(uq_mgr->file,
"Couldn't unmap all the queues, eviction failed ret=%d\n", ret);
- amdgpu_reset_domain_schedule(uq_mgr->adev->reset_domain,
- &uq_mgr->reset_work);
- flush_work(&uq_mgr->reset_work);
- }
return ret;
}
@@ -1334,10 +1330,10 @@ amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr)
}
}
-void
+int
amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr)
{
- amdgpu_userq_evict_all(uq_mgr);
+ return amdgpu_userq_evict_all(uq_mgr);
}
int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file *file_priv,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
index 488dc21d7c81..4dcf6151de6a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
@@ -163,7 +163,7 @@ void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr);
void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);
void amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr);
-void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
+int amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
struct amdgpu_eviction_fence_mgr *evf_mgr);
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU reset
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
@ 2026-10-13 8:24 ` Prike Liang
2026-10-13 9:27 ` Christian König
2026-10-13 8:24 ` [PATCH 3/9] drm/amdgpu: skip VMHUB fault handling while the device is inaccessible Prike Liang
` (7 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Prike Liang @ 2026-10-13 8:24 UTC (permalink / raw)
To: amd-gfx
Cc: Alexander.Deucher, Christian.Koenig, Lijo.Lazar, Vitaly.Prosyak,
Prike Liang
GPU recovery already notifies DRM clients. Skip the VKMS mode-config
suspend/resume helpers during that internal reset to avoid duplicate
client notification and atomic-state handling under the reset lock.
Keep ordinary system suspend and resume unchanged.
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
index d6b51b3f216b..a49c7dda4c4e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
@@ -491,6 +491,9 @@ static int amdgpu_vkms_suspend(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int r;
+ if (amdgpu_in_reset(adev))
+ return 0;
+
r = drm_mode_config_helper_suspend(adev_to_drm(adev));
if (r)
return r;
@@ -505,6 +508,9 @@ static int amdgpu_vkms_resume(struct amdgpu_ip_block *ip_block)
r = amdgpu_vkms_hw_init(ip_block);
if (r)
return r;
+ if (amdgpu_in_reset(ip_block->adev))
+ return 0;
+
return drm_mode_config_helper_resume(adev_to_drm(ip_block->adev));
}
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 3/9] drm/amdgpu: skip VMHUB fault handling while the device is inaccessible
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
2026-10-13 8:24 ` [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU reset Prike Liang
@ 2026-10-13 8:24 ` Prike Liang
2026-10-13 8:24 ` [PATCH 4/9] drm/amdgpu/userq: complete hung queue fences after successful removal Prike Liang
` (6 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Prike Liang @ 2026-10-13 8:24 UTC (permalink / raw)
To: amd-gfx
Cc: Alexander.Deucher, Christian.Koenig, Lijo.Lazar, Vitaly.Prosyak,
Prike Liang
Skip GFX11 retry-fault processing and VMHUB status MMIO when reset is
in progress or hardware access has been disabled. Retain a rate-limited
diagnostic containing the fault address and client.
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c
index 3cdc7893587a..07b7ad753fe4 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c
@@ -115,6 +115,13 @@ static int gmc_v11_0_process_interrupt(struct amdgpu_device *adev,
addr = (u64)entry->src_data[0] << 12;
addr |= ((u64)entry->src_data[1] & 0xf) << 44;
+ if (amdgpu_in_reset(adev) || adev->no_hw_access) {
+ dev_err_ratelimited(adev->dev,
+ "[inaccessible device] page fault address 0x%016llx from client %d\n",
+ addr, entry->client_id);
+ return 0;
+ }
+
if (retry_fault) {
cam_index = entry->src_data[2] & 0x3ff;
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 4/9] drm/amdgpu/userq: complete hung queue fences after successful removal
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
2026-10-13 8:24 ` [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU reset Prike Liang
2026-10-13 8:24 ` [PATCH 3/9] drm/amdgpu: skip VMHUB fault handling while the device is inaccessible Prike Liang
@ 2026-10-13 8:24 ` Prike Liang
2026-10-13 8:24 ` [PATCH 5/9] drm/amdgpu: finish GPU recovery callbacks before restarting schedulers Prike Liang
` (5 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Prike Liang @ 2026-10-13 8:24 UTC (permalink / raw)
To: amd-gfx
Cc: Alexander.Deucher, Christian.Koenig, Lijo.Lazar, Vitaly.Prosyak,
Prike Liang
Only mark an additional MES queue as hung and complete its fences after
both hardware reset and removal succeed. A failed operation leaves its
dependencies pending for device recovery; it does not prove DMA stopped.
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
index fe49aa4dc5ff..3b973ea0c24a 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
@@ -282,7 +282,6 @@ int mes_userq_reset_queue(struct amdgpu_device *adev,
if (uq == guilty_uq)
continue;
if (uq->doorbell_index == db) {
- uq->state = AMDGPU_USERQ_STATE_HUNG;
if (use_mmio)
r = amdgpu_mes_reset_queue_mmio(adev, queue_type, 0, 1, pipe, queue, 0);
else
@@ -292,8 +291,10 @@ int mes_userq_reset_queue(struct amdgpu_device *adev,
r = mes_userq_unmap(uq);
if (r)
return r;
+ trace_amdgpu_userq_state_changed(uq, AMDGPU_USERQ_STATE_HUNG);
+ uq->state = AMDGPU_USERQ_STATE_HUNG;
amdgpu_userq_fence_driver_force_completion(uq);
- break;
+ return 0;
}
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 5/9] drm/amdgpu: finish GPU recovery callbacks before restarting schedulers
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
` (2 preceding siblings ...)
2026-10-13 8:24 ` [PATCH 4/9] drm/amdgpu/userq: complete hung queue fences after successful removal Prike Liang
@ 2026-10-13 8:24 ` Prike Liang
2026-10-13 8:24 ` [PATCH 6/9] drm/amdgpu: serialize JPEG and VCN idle work with GPU reset Prike Liang
` (4 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Prike Liang @ 2026-10-13 8:24 UTC (permalink / raw)
To: amd-gfx
Cc: Alexander.Deucher, Christian.Koenig, Lijo.Lazar, Vitaly.Prosyak,
Prike Liang
Run GPU post-reset callbacks, including KFD and display-audio resume,
before restarting DRM scheduler workqueues. IP hardware initialization
already happens inside ASIC recovery; this change orders the remaining
callbacks before new scheduler submissions. Preserve the emergency-stop
and guity job completition path's callbacks without starting schedulers,
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index fa67de6d066c..83d98c9191b0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -5783,8 +5783,10 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
amdgpu_device_halt_activities(adev, job, reset_context, &device_list,
hive, need_emergency_restart);
- if (need_emergency_restart)
- goto skip_sched_resume;
+ if (need_emergency_restart) {
+ amdgpu_device_gpu_resume(adev, &device_list, need_emergency_restart);
+ goto reset_unlock;
+ }
/*
* Must check guilty signal here since after this point all old
* HW fences are force signaled.
@@ -5794,18 +5796,17 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
if (job && (dma_fence_get_status(&job->hw_fence->base) > 0)) {
job_signaled = true;
dev_info(adev->dev, "Guilty job already signaled, skipping HW reset");
- goto skip_hw_reset;
+ goto post_resume;
}
r = amdgpu_device_asic_reset(adev, &device_list, reset_context);
if (r)
goto reset_unlock;
-skip_hw_reset:
+post_resume:
+ amdgpu_device_gpu_resume(adev, &device_list, need_emergency_restart);
r = amdgpu_device_sched_resume(&device_list, reset_context, job_signaled);
if (r)
goto reset_unlock;
-skip_sched_resume:
- amdgpu_device_gpu_resume(adev, &device_list, need_emergency_restart);
reset_unlock:
amdgpu_device_recovery_put_reset_lock(adev, &device_list);
amdgpu_ras_post_reset(adev, &device_list);
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 6/9] drm/amdgpu: serialize JPEG and VCN idle work with GPU reset
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
` (3 preceding siblings ...)
2026-10-13 8:24 ` [PATCH 5/9] drm/amdgpu: finish GPU recovery callbacks before restarting schedulers Prike Liang
@ 2026-10-13 8:24 ` Prike Liang
2026-10-13 9:28 ` Christian König
2026-10-13 8:24 ` [PATCH 7/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in destroy Prike Liang
` (3 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Prike Liang @ 2026-10-13 8:24 UTC (permalink / raw)
To: amd-gfx
Cc: Alexander.Deucher, Christian.Koenig, Lijo.Lazar, Vitaly.Prosyak,
Prike Liang
Take the reset-domain read semaphore in the common JPEG and VCN idle
workers so their hardware accesses wait for GPU recovery to finish.
Keep end-use scheduling, including from recovery ring tests, so idle
engines can power down even if no further jobs arrive.
Do not synchronously cancel these workers from recovery while holding
the reset write semaphore. Use nonblocking cancellation in the common
ring begin-use paths and during reset-time hardware finalization, while
retaining synchronous draining for normal suspend and teardown.
Serialize the idle decision and VCN DPG pause updates with begin-use
under the power-gating mutex. Check the submission count before reading
fences, with acquire ordering against end-use, so a running worker cannot
miss a submission that completes between the two checks.
VCN 1.0 and 2.5 override the common idle worker and retain their existing
worker and cancellation behavior.
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c | 28 +++++++++++++++------
drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c | 32 ++++++++++++++++++------
drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h | 1 +
drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c | 2 +-
drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c | 2 +-
drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c | 2 +-
drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c | 2 +-
drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c | 2 +-
drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c | 3 +--
drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c | 2 +-
drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c | 2 +-
drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c | 2 +-
drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c | 2 +-
drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c | 2 +-
drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c | 2 +-
drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c | 2 +-
drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c | 2 +-
drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c | 2 +-
drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c | 2 +-
drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c | 2 +-
drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c | 2 +-
21 files changed, 65 insertions(+), 33 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c
index 9887a4f99aed..39b53730f59a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c
@@ -97,7 +97,11 @@ int amdgpu_jpeg_sw_fini(struct amdgpu_device *adev)
int amdgpu_jpeg_suspend(struct amdgpu_device *adev)
{
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ /* The idle worker may be waiting for the reset writer to release sem. */
+ if (amdgpu_in_reset(adev))
+ cancel_delayed_work(&adev->jpeg.idle_work);
+ else
+ cancel_delayed_work_sync(&adev->jpeg.idle_work);
return 0;
}
@@ -114,6 +118,12 @@ static void amdgpu_jpeg_idle_work_handler(struct work_struct *work)
unsigned int fences = 0;
unsigned int i, j;
+ down_read(&adev->reset_domain->sem);
+ mutex_lock(&adev->jpeg.jpeg_pg_lock);
+ /* Check submissions before fences, while excluding a new begin_use. */
+ if (atomic_read_acquire(&adev->jpeg.total_submission_cnt))
+ goto reschedule;
+
for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) {
if (adev->jpeg.harvest_config & (1U << i))
continue;
@@ -122,13 +132,17 @@ static void amdgpu_jpeg_idle_work_handler(struct work_struct *work)
fences += amdgpu_fence_count_emitted(&adev->jpeg.inst[i].ring_dec[j]);
}
- if (!fences && !atomic_read(&adev->jpeg.total_submission_cnt)) {
- mutex_lock(&adev->jpeg.jpeg_pg_lock);
+ if (!fences) {
amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_JPEG,
AMD_PG_STATE_GATE);
- mutex_unlock(&adev->jpeg.jpeg_pg_lock);
- } else
- schedule_delayed_work(&adev->jpeg.idle_work, JPEG_IDLE_TIMEOUT);
+ goto unlock;
+ }
+
+reschedule:
+ schedule_delayed_work(&adev->jpeg.idle_work, JPEG_IDLE_TIMEOUT);
+unlock:
+ mutex_unlock(&adev->jpeg.jpeg_pg_lock);
+ up_read(&adev->reset_domain->sem);
}
void amdgpu_jpeg_ring_begin_use(struct amdgpu_ring *ring)
@@ -136,7 +150,7 @@ void amdgpu_jpeg_ring_begin_use(struct amdgpu_ring *ring)
struct amdgpu_device *adev = ring->adev;
if (!atomic_fetch_inc(&adev->jpeg.total_submission_cnt))
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ cancel_delayed_work(&adev->jpeg.idle_work);
mutex_lock(&adev->jpeg.jpeg_pg_lock);
amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_JPEG,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
index 17db7264269e..d37f36d554df 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
@@ -459,6 +459,15 @@ void amdgpu_vcn_put_profile(struct amdgpu_device *adev)
mutex_unlock(&adev->vcn.workload_profile_mutex);
}
+void amdgpu_vcn_cancel_idle_work(struct amdgpu_vcn_inst *vcn_inst)
+{
+ /* The idle worker may be waiting for the reset writer to release sem. */
+ if (amdgpu_in_reset(vcn_inst->adev))
+ cancel_delayed_work(&vcn_inst->idle_work);
+ else
+ cancel_delayed_work_sync(&vcn_inst->idle_work);
+}
+
static void amdgpu_vcn_idle_work_handler(struct work_struct *work)
{
struct amdgpu_vcn_inst *vcn_inst =
@@ -466,10 +475,17 @@ static void amdgpu_vcn_idle_work_handler(struct work_struct *work)
struct amdgpu_device *adev = vcn_inst->adev;
unsigned int fences = 0, fence[AMDGPU_MAX_VCN_INSTANCES] = {0};
unsigned int i = vcn_inst->inst, j;
+ bool idle = false;
if (adev->vcn.harvest_config & (1 << i))
return;
+ down_read(&adev->reset_domain->sem);
+ mutex_lock(&vcn_inst->vcn_pg_lock);
+ /* Check submissions before fences, while excluding a new begin_use. */
+ if (atomic_read_acquire(&vcn_inst->total_submission_cnt))
+ goto unlock;
+
for (j = 0; j < adev->vcn.inst[i].num_enc_rings; ++j)
fence[i] += amdgpu_fence_count_emitted(&vcn_inst->ring_enc[j]);
@@ -490,15 +506,17 @@ static void amdgpu_vcn_idle_work_handler(struct work_struct *work)
fence[i] += amdgpu_fence_count_emitted(&vcn_inst->ring_dec);
fences += fence[i];
- if (!fences && !atomic_read(&vcn_inst->total_submission_cnt)) {
- mutex_lock(&vcn_inst->vcn_pg_lock);
+ idle = !fences;
+ if (idle)
vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_GATE);
- mutex_unlock(&vcn_inst->vcn_pg_lock);
- amdgpu_vcn_put_profile(adev);
- } else {
+unlock:
+ mutex_unlock(&vcn_inst->vcn_pg_lock);
+ if (idle)
+ amdgpu_vcn_put_profile(adev);
+ else
schedule_delayed_work(&vcn_inst->idle_work, VCN_IDLE_TIMEOUT);
- }
+ up_read(&adev->reset_domain->sem);
}
void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring)
@@ -507,7 +525,7 @@ void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring)
struct amdgpu_vcn_inst *vcn_inst = &adev->vcn.inst[ring->me];
if (!atomic_fetch_inc(&vcn_inst->total_submission_cnt))
- cancel_delayed_work_sync(&vcn_inst->idle_work);
+ cancel_delayed_work(&vcn_inst->idle_work);
mutex_lock(&vcn_inst->vcn_pg_lock);
vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_UNGATE);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
index bea95307fd42..a6438950854e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
@@ -526,6 +526,7 @@ int amdgpu_vcn_suspend(struct amdgpu_device *adev, int i);
int amdgpu_vcn_resume(struct amdgpu_device *adev, int i);
void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring);
void amdgpu_vcn_ring_end_use(struct amdgpu_ring *ring);
+void amdgpu_vcn_cancel_idle_work(struct amdgpu_vcn_inst *vcn_inst);
bool amdgpu_vcn_is_disabled_vcn(struct amdgpu_device *adev,
enum vcn_ring_type type, uint32_t vcn_instance);
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c
index 72069b77f6f1..b048fd4b8b59 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c
@@ -177,7 +177,7 @@ static int jpeg_v2_0_hw_fini(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
RREG32_SOC15(JPEG, 0, mmUVD_JRBC_STATUS))
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c
index 2968256d4cd7..f6a9258ae3ca 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c
@@ -239,7 +239,7 @@ static int jpeg_v2_5_hw_fini(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int i;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) {
if (adev->jpeg.harvest_config & (1 << i))
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c
index 564e40899df2..922711aa06a3 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c
@@ -192,7 +192,7 @@ static int jpeg_v3_0_hw_fini(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
RREG32_SOC15(JPEG, 0, mmUVD_JRBC_STATUS))
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c
index b29ca3a49939..fe7ab9cb35bb 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c
@@ -221,7 +221,7 @@ static int jpeg_v4_0_hw_fini(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
if (!amdgpu_sriov_vf(adev)) {
if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
RREG32_SOC15(JPEG, 0, regUVD_JRBC_STATUS))
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
index 0343a64595f2..f4896ad9d1de 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
@@ -455,7 +455,7 @@ static int jpeg_v4_0_3_hw_fini(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int ret = 0;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
if (!amdgpu_sriov_vf(adev)) {
if (adev->jpeg.cur_state != AMD_PG_STATE_GATE)
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c
index 4da3f9c995c1..5a0132407a30 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c
@@ -250,7 +250,7 @@ static int jpeg_v4_0_5_hw_fini(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int i;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) {
if (adev->jpeg.harvest_config & (1 << i))
@@ -883,4 +883,3 @@ const struct amdgpu_ip_block_version jpeg_v4_0_5_ip_block = {
.rev = 5,
.funcs = &jpeg_v4_0_5_ip_funcs,
};
-
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c
index dcdb089b4308..91968ad26f17 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c
@@ -189,7 +189,7 @@ static int jpeg_v5_0_0_hw_fini(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
RREG32_SOC15(JPEG, 0, regUVD_JRBC_STATUS))
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
index f2debf6b6bd0..27116d1e9e64 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
@@ -326,7 +326,7 @@ static int jpeg_v5_0_1_hw_fini(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int ret = 0;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
if (!amdgpu_sriov_vf(adev)) {
if (adev->jpeg.cur_state != AMD_PG_STATE_GATE)
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c
index dd0abce73687..7e068c2d4d6d 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c
@@ -299,7 +299,7 @@ static int jpeg_v5_0_2_hw_fini(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int ret = 0;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
if (!amdgpu_sriov_vf(adev)) {
if (adev->jpeg.cur_state != AMD_PG_STATE_GATE)
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
index 8ddd12740a04..16daeb61114c 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
@@ -172,7 +172,7 @@ static int jpeg_v5_3_0_hw_fini(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ amdgpu_jpeg_suspend(adev);
if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
RREG32_SOC15(JPEG, 0, regUVD_JRBC0_UVD_JRBC_STATUS))
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c
index 7d2b9e4b913a..50378ee5a463 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c
@@ -322,7 +322,7 @@ static int vcn_v2_0_hw_fini(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
struct amdgpu_vcn_inst *vinst = adev->vcn.inst;
- cancel_delayed_work_sync(&vinst->idle_work);
+ amdgpu_vcn_cancel_idle_work(vinst);
if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
(vinst->cur_state != AMD_PG_STATE_GATE &&
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
index 0f4f4d66e2e4..3e85abd1e82c 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
@@ -449,7 +449,7 @@ static int vcn_v3_0_hw_fini(struct amdgpu_ip_block *ip_block)
if (adev->vcn.harvest_config & (1 << i))
continue;
- cancel_delayed_work_sync(&vinst->idle_work);
+ amdgpu_vcn_cancel_idle_work(vinst);
if (!amdgpu_sriov_vf(adev)) {
if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
index 4b7f7bbe18da..5b47a643d725 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
@@ -374,7 +374,7 @@ static int vcn_v4_0_hw_fini(struct amdgpu_ip_block *ip_block)
if (adev->vcn.harvest_config & (1 << i))
continue;
- cancel_delayed_work_sync(&vinst->idle_work);
+ amdgpu_vcn_cancel_idle_work(vinst);
if (!amdgpu_sriov_vf(adev)) {
if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
index 3f39aa83c319..5aa09c3bd79b 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
@@ -417,7 +417,7 @@ static int vcn_v4_0_3_hw_fini(struct amdgpu_ip_block *ip_block)
for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
struct amdgpu_vcn_inst *vinst = &adev->vcn.inst[i];
- cancel_delayed_work_sync(&vinst->idle_work);
+ amdgpu_vcn_cancel_idle_work(vinst);
if (vinst->cur_state != AMD_PG_STATE_GATE)
vinst->set_pg_state(vinst, AMD_PG_STATE_GATE);
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
index f30f80c1b07f..9693950cd16d 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
@@ -327,7 +327,7 @@ static int vcn_v4_0_5_hw_fini(struct amdgpu_ip_block *ip_block)
if (adev->vcn.harvest_config & (1 << i))
continue;
- cancel_delayed_work_sync(&vinst->idle_work);
+ amdgpu_vcn_cancel_idle_work(vinst);
if (!amdgpu_sriov_vf(adev)) {
if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c
index 7b7c3bc9f046..0c189063a6ab 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c
@@ -291,7 +291,7 @@ static int vcn_v5_0_0_hw_fini(struct amdgpu_ip_block *ip_block)
if (adev->vcn.harvest_config & (1 << i))
continue;
- cancel_delayed_work_sync(&vinst->idle_work);
+ amdgpu_vcn_cancel_idle_work(vinst);
if (!amdgpu_sriov_vf(adev)) {
if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
index c9e990ab297d..3fa553fb41d3 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
@@ -364,7 +364,7 @@ static int vcn_v5_0_1_hw_fini(struct amdgpu_ip_block *ip_block)
for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
struct amdgpu_vcn_inst *vinst = &adev->vcn.inst[i];
- cancel_delayed_work_sync(&adev->vcn.inst[i].idle_work);
+ amdgpu_vcn_cancel_idle_work(&adev->vcn.inst[i]);
if (vinst->cur_state != AMD_PG_STATE_GATE)
vinst->set_pg_state(vinst, AMD_PG_STATE_GATE);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c
index ed47ddd5b08c..eb13f792e25d 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c
@@ -279,7 +279,7 @@ static int vcn_v5_0_2_hw_fini(struct amdgpu_ip_block *ip_block)
for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
struct amdgpu_vcn_inst *vinst = &adev->vcn.inst[i];
- cancel_delayed_work_sync(&adev->vcn.inst[i].idle_work);
+ amdgpu_vcn_cancel_idle_work(&adev->vcn.inst[i]);
if (vinst->cur_state != AMD_PG_STATE_GATE)
vinst->set_pg_state(vinst, AMD_PG_STATE_GATE);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 7/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in destroy
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
` (4 preceding siblings ...)
2026-10-13 8:24 ` [PATCH 6/9] drm/amdgpu: serialize JPEG and VCN idle work with GPU reset Prike Liang
@ 2026-10-13 8:24 ` Prike Liang
2026-10-13 9:30 ` Christian König
2026-10-13 8:24 ` [PATCH 8/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in create Prike Liang
` (2 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Prike Liang @ 2026-10-13 8:24 UTC (permalink / raw)
To: amd-gfx
Cc: Alexander.Deucher, Christian.Koenig, Lijo.Lazar, Vitaly.Prosyak,
Prike Liang
amdgpu_userq_destroy() takes reset_domain->sem for read in
amdgpu_userq_detach_doorbell() while holding userq_mutex. GPU recovery
holds reset_domain->sem for write and then takes clientlist_mutex in
drm_client_dev_suspend(), which already depends on userq_mutex through
the fbdev reservation and userq restore paths. Lockdep reports:
Chain exists of:
&dev->clientlist_mutex --> &userq_mgr->userq_mutex --> &reset_domain->sem
Keep waiting for the last fence under userq_mutex only, as the fence of a
hung queue is only force-completed by the reset, then take
reset_domain->sem before re-acquiring userq_mutex for the doorbell
detach, unmap and fence driver teardown. This also keeps the MES unmap
from racing with an in-progress GPU reset.
Fixes: f18719ef4bb7 ("drm/amdgpu: Convert amdgpu userqueue management from IDR to XArray")
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 5fef0cc7703c..a34ea8ecd700 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -415,15 +415,6 @@ static void amdgpu_userq_wait_for_last_fence(struct amdgpu_usermode_queue *queue
dma_fence_wait(f, false);
}
-static void amdgpu_userq_detach_doorbell(struct amdgpu_usermode_queue *queue)
-{
- struct amdgpu_device *adev = queue->userq_mgr->adev;
-
- down_read(&adev->reset_domain->sem);
- xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index);
- up_read(&adev->reset_domain->sem);
-}
-
/**
* amdgpu_userq_ensure_ev_fence - ensure a valid, unsignaled eviction fence exists
* @uq_mgr: the usermode queue manager for this process
@@ -547,8 +538,12 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
mutex_lock(&uq_mgr->userq_mutex);
amdgpu_userq_wait_for_last_fence(queue);
+ mutex_unlock(&uq_mgr->userq_mutex);
- amdgpu_userq_detach_doorbell(queue);
+ /* GPU recovery takes userq_mutex under reset_domain->sem, keep that order */
+ down_read(&adev->reset_domain->sem);
+ mutex_lock(&uq_mgr->userq_mutex);
+ xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index);
#if defined(CONFIG_DEBUG_FS)
debugfs_remove_recursive(queue->debugfs_queue);
@@ -558,6 +553,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
amdgpu_userq_fence_driver_free(queue);
queue->fence_drv = NULL;
mutex_unlock(&uq_mgr->userq_mutex);
+ up_read(&adev->reset_domain->sem);
/*
* A failed unmap means MES could not remove the hung queue and is now
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 8/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in create
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
` (5 preceding siblings ...)
2026-10-13 8:24 ` [PATCH 7/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in destroy Prike Liang
@ 2026-10-13 8:24 ` Prike Liang
2026-10-13 8:24 ` [PATCH 9/9] drm/amdgpu/mes: skip unmap queue notification during GPU reset Prike Liang
2026-10-13 9:26 ` [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Christian König
8 siblings, 0 replies; 18+ messages in thread
From: Prike Liang @ 2026-10-13 8:24 UTC (permalink / raw)
To: amd-gfx
Cc: Alexander.Deucher, Christian.Koenig, Lijo.Lazar, Vitaly.Prosyak,
Prike Liang
amdgpu_userq_create() maps the queue under reset_domain->sem, but
amdgpu_userq_ensure_ev_fence() returns with userq_mutex held, so the
semaphore nests inside userq_mutex. That is the inverse of the order
used by GPU recovery and lockdep reports a circular dependency:
Chain exists of:
&dev->clientlist_mutex --> &userq_mgr->userq_mutex --> &reset_domain->sem
userq_mutex has to stay held from amdgpu_userq_ensure_ev_fence() until
the queue is mapped to keep the eviction fence valid, so only trylock
reset_domain->sem under it. If a reset is in progress, drop userq_mutex,
wait for the reset to finish and retry. Hold the semaphore until the
queue is mapped or its doorbell is erased again on failure.
Fixes: 44fd81554cc0 ("drm/amdgpu/userq: serialize queue map against GPU reset")
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index a34ea8ecd700..817bd395d2f1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -748,18 +748,21 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
if (r)
goto clean_mqd;
- amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
+ for (;;) {
+ amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
+ /* reset_domain->sem nests outside userq_mutex, so only trylock here */
+ if (down_read_trylock(&adev->reset_domain->sem))
+ break;
+ mutex_unlock(&uq_mgr->userq_mutex);
+ down_read(&adev->reset_domain->sem);
+ up_read(&adev->reset_domain->sem);
+ }
/* don't map the queue if scheduling is halted */
if (!adev->userq_halt_for_enforce_isolation ||
((queue->queue_type != AMDGPU_HW_IP_GFX) &&
(queue->queue_type != AMDGPU_HW_IP_COMPUTE))) {
- /* Serialize the map against an in-progress GPU reset (MES is
- * unresponsive during recovery), matching amdgpu_userq_detach_doorbell().
- */
- down_read(&adev->reset_domain->sem);
r = amdgpu_userq_map_helper(queue);
- up_read(&adev->reset_domain->sem);
if (r) {
drm_file_err(uq_mgr->file, "Failed to map Queue\n");
trace_amdgpu_userq_create_end(queue, r);
@@ -770,6 +773,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
atomic_inc(&uq_mgr->userq_count[queue->queue_type]);
mutex_unlock(&uq_mgr->userq_mutex);
+ up_read(&adev->reset_domain->sem);
r = xa_alloc(&uq_mgr->userq_xa, &qid, queue,
XA_LIMIT(1, AMDGPU_MAX_USERQ_COUNT),
@@ -791,6 +795,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
erase_doorbell:
xa_erase_irq(&adev->userq_doorbell_xa, index);
+ up_read(&adev->reset_domain->sem);
clean_mqd:
uq_funcs->mqd_destroy(queue);
clean_doorbell_bo:
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 9/9] drm/amdgpu/mes: skip unmap queue notification during GPU reset
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
` (6 preceding siblings ...)
2026-10-13 8:24 ` [PATCH 8/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in create Prike Liang
@ 2026-10-13 8:24 ` Prike Liang
2026-10-13 9:26 ` [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Christian König
8 siblings, 0 replies; 18+ messages in thread
From: Prike Liang @ 2026-10-13 8:24 UTC (permalink / raw)
To: amd-gfx
Cc: Alexander.Deucher, Christian.Koenig, Lijo.Lazar, Vitaly.Prosyak,
Prike Liang
amdgpu_mes_userq_notify_unmap_work_handler() submits a MES misc op
without holding reset_domain->sem, so it can ring the MES doorbell while
the GPU is being reset:
WARNING: amdgpu_device.c:928 at amdgpu_device_skip_hw_access+0x73/0x90 [amdgpu]
Workqueue: events amdgpu_mes_userq_notify_unmap_work_handler [amdgpu]
Call Trace:
amdgpu_mm_wdoorbell64+0x20/0x70 [amdgpu]
mes_v11_0_ring_set_wptr+0x71/0x80 [amdgpu]
amdgpu_ring_commit+0x5b/0xa0 [amdgpu]
mes_v11_0_submit_pkt_and_poll_completion.constprop.0+0x207/0x520 [amdgpu]
mes_v11_0_misc_op+0x8b/0x240 [amdgpu]
amdgpu_mes_notify_unmap_queue+0xa3/0x110 [amdgpu]
amdgpu_mes_userq_notify_unmap_work_handler+0x24/0x70 [amdgpu]
Only send the notification when reset_domain->sem can be trylocked. The
work re-arms itself while the HW queues are oversubscribed, so the
notification is retried once the reset has finished.
Fixes: a3e112968588 ("drm/amdgpu/userq: create the same workaround of oversubscription timer")
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
index 4cb37cdbf821..09009925611a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
@@ -1192,7 +1192,11 @@ static void amdgpu_mes_userq_notify_unmap_work_handler(struct work_struct *work)
userq_notify_unmap_work.work);
struct amdgpu_device *adev = mes->adev;
- amdgpu_mes_notify_unmap_queue(adev);
+ /* MES is unusable during GPU recovery; the re-arm below retries later */
+ if (down_read_trylock(&adev->reset_domain->sem)) {
+ amdgpu_mes_notify_unmap_queue(adev);
+ up_read(&adev->reset_domain->sem);
+ }
/* Re-arm if still oversubscribed */
if (atomic_read(&mes->userq_hw_queue_count) >
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
` (7 preceding siblings ...)
2026-10-13 8:24 ` [PATCH 9/9] drm/amdgpu/mes: skip unmap queue notification during GPU reset Prike Liang
@ 2026-10-13 9:26 ` Christian König
2026-10-14 9:02 ` Liang, Prike
8 siblings, 1 reply; 18+ messages in thread
From: Christian König @ 2026-10-13 9:26 UTC (permalink / raw)
To: Prike Liang, amd-gfx; +Cc: Alexander.Deucher, Lijo.Lazar, Vitaly.Prosyak
On 10/13/26 10:24, Prike Liang wrote:
> On userq eviction failure, the suspend worker waits for reset_work
> while holding userq_mutex and inside a DMA-fence signaling section.
> This makes eviction-fence completion depend on GPU recovery finishing,
> although recovery can itself require memory allocation and eviction.
Absolutely clear NAK to that!
GPU recovery is to unlock memory allocation and eviction, so it can *NEVER* allocate memory nor depend on eviction to complete.
This patch here is just utterly nonsense. What we need to avoid is to touch the userq mutex and/or any eviction fence from the GPU recovery.
Regards,
Christian.
>
> Fixes: c8ed2de0f2ee ("drm/amdgpu: rework userq reset work handling")
> Signed-off-by: Prike Liang <Prike.Liang@amd.com>
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c | 12 +++++++++---
> drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 +++-------
> drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 2 +-
> 3 files changed, 13 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
> index 082d045edf45..c2d1bc7decba 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
> @@ -65,6 +65,7 @@ amdgpu_eviction_fence_suspend_worker(struct work_struct *work)
> struct amdgpu_userq_mgr *uq_mgr = &fpriv->userq_mgr;
> struct dma_fence *ev_fence;
> bool cookie;
> + int r;
>
> mutex_lock(&uq_mgr->userq_mutex);
>
> @@ -79,21 +80,26 @@ amdgpu_eviction_fence_suspend_worker(struct work_struct *work)
> cookie = dma_fence_begin_signalling();
>
> ev_fence = amdgpu_evf_mgr_get_fence(evf_mgr);
> - amdgpu_userq_evict(uq_mgr);
> + r = amdgpu_userq_evict(uq_mgr);
>
> /*
> * Signaling the eviction fence must be done while holding the
> * userq_mutex. Otherwise we won't resume the queues before issuing the
> * next fence.
> */
> - dma_fence_signal(ev_fence);
> + if (!r)
> + dma_fence_signal(ev_fence);
> dma_fence_end_signalling(cookie);
> dma_fence_put(ev_fence);
>
> - if (!evf_mgr->shutdown)
> + if (!r && !evf_mgr->shutdown)
> schedule_delayed_work(&uq_mgr->resume_work, 0);
>
> mutex_unlock(&uq_mgr->userq_mutex);
> +
> + if (r)
> + amdgpu_reset_domain_schedule(uq_mgr->adev->reset_domain,
> + &uq_mgr->reset_work);
> }
>
> int amdgpu_evf_mgr_attach_fence(struct amdgpu_eviction_fence_mgr *evf_mgr,
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> index 3fe91fdafe55..5fef0cc7703c 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> @@ -1308,13 +1308,9 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
> ret = r;
> }
>
> - if (ret) {
> + if (ret)
> drm_file_err(uq_mgr->file,
> "Couldn't unmap all the queues, eviction failed ret=%d\n", ret);
> - amdgpu_reset_domain_schedule(uq_mgr->adev->reset_domain,
> - &uq_mgr->reset_work);
> - flush_work(&uq_mgr->reset_work);
> - }
> return ret;
> }
>
> @@ -1334,10 +1330,10 @@ amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr)
> }
> }
>
> -void
> +int
> amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr)
> {
> - amdgpu_userq_evict_all(uq_mgr);
> + return amdgpu_userq_evict_all(uq_mgr);
> }
>
> int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file *file_priv,
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> index 488dc21d7c81..4dcf6151de6a 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> @@ -163,7 +163,7 @@ void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr);
> void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);
>
> void amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr);
> -void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
> +int amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
>
> void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
> struct amdgpu_eviction_fence_mgr *evf_mgr);
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU reset
2026-10-13 8:24 ` [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU reset Prike Liang
@ 2026-10-13 9:27 ` Christian König
2026-10-15 3:45 ` Liang, Prike
0 siblings, 1 reply; 18+ messages in thread
From: Christian König @ 2026-10-13 9:27 UTC (permalink / raw)
To: Prike Liang, amd-gfx; +Cc: Alexander.Deucher, Lijo.Lazar, Vitaly.Prosyak
On 10/13/26 10:24, Prike Liang wrote:
> GPU recovery already notifies DRM clients. Skip the VKMS mode-config
> suspend/resume helpers during that internal reset to avoid duplicate
> client notification and atomic-state handling under the reset lock.
> Keep ordinary system suspend and resume unchanged.
Clear NAK to that as well.
The amdgpu_in_reset() function can only be called from inside the GPU reset itself!
It is *NOT* allowed to skip any operation outside the reset depending on this.
Regards,
Christian.
>
> Signed-off-by: Prike Liang <Prike.Liang@amd.com>
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
> index d6b51b3f216b..a49c7dda4c4e 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
> @@ -491,6 +491,9 @@ static int amdgpu_vkms_suspend(struct amdgpu_ip_block *ip_block)
> struct amdgpu_device *adev = ip_block->adev;
> int r;
>
> + if (amdgpu_in_reset(adev))
> + return 0;
> +
> r = drm_mode_config_helper_suspend(adev_to_drm(adev));
> if (r)
> return r;
> @@ -505,6 +508,9 @@ static int amdgpu_vkms_resume(struct amdgpu_ip_block *ip_block)
> r = amdgpu_vkms_hw_init(ip_block);
> if (r)
> return r;
> + if (amdgpu_in_reset(ip_block->adev))
> + return 0;
> +
> return drm_mode_config_helper_resume(adev_to_drm(ip_block->adev));
> }
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 6/9] drm/amdgpu: serialize JPEG and VCN idle work with GPU reset
2026-10-13 8:24 ` [PATCH 6/9] drm/amdgpu: serialize JPEG and VCN idle work with GPU reset Prike Liang
@ 2026-10-13 9:28 ` Christian König
0 siblings, 0 replies; 18+ messages in thread
From: Christian König @ 2026-10-13 9:28 UTC (permalink / raw)
To: Prike Liang, amd-gfx; +Cc: Alexander.Deucher, Lijo.Lazar, Vitaly.Prosyak
On 10/13/26 10:24, Prike Liang wrote:
> Take the reset-domain read semaphore in the common JPEG and VCN idle
> workers so their hardware accesses wait for GPU recovery to finish.
> Keep end-use scheduling, including from recovery ring tests, so idle
> engines can power down even if no further jobs arrive.
>
> Do not synchronously cancel these workers from recovery while holding
> the reset write semaphore. Use nonblocking cancellation in the common
> ring begin-use paths and during reset-time hardware finalization, while
> retaining synchronous draining for normal suspend and teardown.
>
> Serialize the idle decision and VCN DPG pause updates with begin-use
> under the power-gating mutex. Check the submission count before reading
> fences, with acquire ordering against end-use, so a running worker cannot
> miss a submission that completes between the two checks.
>
> VCN 1.0 and 2.5 override the common idle worker and retain their existing
> worker and cancellation behavior.
>
> Signed-off-by: Prike Liang <Prike.Liang@amd.com>
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c | 28 +++++++++++++++------
> drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c | 32 ++++++++++++++++++------
> drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h | 1 +
> drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c | 3 +--
> drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c | 2 +-
> drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c | 2 +-
> 21 files changed, 65 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c
> index 9887a4f99aed..39b53730f59a 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c
> @@ -97,7 +97,11 @@ int amdgpu_jpeg_sw_fini(struct amdgpu_device *adev)
>
> int amdgpu_jpeg_suspend(struct amdgpu_device *adev)
> {
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + /* The idle worker may be waiting for the reset writer to release sem. */
> + if (amdgpu_in_reset(adev))
Again, complete NAK to that!
Christian.
> + cancel_delayed_work(&adev->jpeg.idle_work);
> + else
> + cancel_delayed_work_sync(&adev->jpeg.idle_work);
>
> return 0;
> }
> @@ -114,6 +118,12 @@ static void amdgpu_jpeg_idle_work_handler(struct work_struct *work)
> unsigned int fences = 0;
> unsigned int i, j;
>
> + down_read(&adev->reset_domain->sem);
> + mutex_lock(&adev->jpeg.jpeg_pg_lock);
> + /* Check submissions before fences, while excluding a new begin_use. */
> + if (atomic_read_acquire(&adev->jpeg.total_submission_cnt))
> + goto reschedule;
> +
> for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) {
> if (adev->jpeg.harvest_config & (1U << i))
> continue;
> @@ -122,13 +132,17 @@ static void amdgpu_jpeg_idle_work_handler(struct work_struct *work)
> fences += amdgpu_fence_count_emitted(&adev->jpeg.inst[i].ring_dec[j]);
> }
>
> - if (!fences && !atomic_read(&adev->jpeg.total_submission_cnt)) {
> - mutex_lock(&adev->jpeg.jpeg_pg_lock);
> + if (!fences) {
> amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_JPEG,
> AMD_PG_STATE_GATE);
> - mutex_unlock(&adev->jpeg.jpeg_pg_lock);
> - } else
> - schedule_delayed_work(&adev->jpeg.idle_work, JPEG_IDLE_TIMEOUT);
> + goto unlock;
> + }
> +
> +reschedule:
> + schedule_delayed_work(&adev->jpeg.idle_work, JPEG_IDLE_TIMEOUT);
> +unlock:
> + mutex_unlock(&adev->jpeg.jpeg_pg_lock);
> + up_read(&adev->reset_domain->sem);
> }
>
> void amdgpu_jpeg_ring_begin_use(struct amdgpu_ring *ring)
> @@ -136,7 +150,7 @@ void amdgpu_jpeg_ring_begin_use(struct amdgpu_ring *ring)
> struct amdgpu_device *adev = ring->adev;
>
> if (!atomic_fetch_inc(&adev->jpeg.total_submission_cnt))
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + cancel_delayed_work(&adev->jpeg.idle_work);
>
> mutex_lock(&adev->jpeg.jpeg_pg_lock);
> amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_JPEG,
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
> index 17db7264269e..d37f36d554df 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
> @@ -459,6 +459,15 @@ void amdgpu_vcn_put_profile(struct amdgpu_device *adev)
> mutex_unlock(&adev->vcn.workload_profile_mutex);
> }
>
> +void amdgpu_vcn_cancel_idle_work(struct amdgpu_vcn_inst *vcn_inst)
> +{
> + /* The idle worker may be waiting for the reset writer to release sem. */
> + if (amdgpu_in_reset(vcn_inst->adev))
> + cancel_delayed_work(&vcn_inst->idle_work);
> + else
> + cancel_delayed_work_sync(&vcn_inst->idle_work);
> +}
> +
> static void amdgpu_vcn_idle_work_handler(struct work_struct *work)
> {
> struct amdgpu_vcn_inst *vcn_inst =
> @@ -466,10 +475,17 @@ static void amdgpu_vcn_idle_work_handler(struct work_struct *work)
> struct amdgpu_device *adev = vcn_inst->adev;
> unsigned int fences = 0, fence[AMDGPU_MAX_VCN_INSTANCES] = {0};
> unsigned int i = vcn_inst->inst, j;
> + bool idle = false;
>
> if (adev->vcn.harvest_config & (1 << i))
> return;
>
> + down_read(&adev->reset_domain->sem);
> + mutex_lock(&vcn_inst->vcn_pg_lock);
> + /* Check submissions before fences, while excluding a new begin_use. */
> + if (atomic_read_acquire(&vcn_inst->total_submission_cnt))
> + goto unlock;
> +
> for (j = 0; j < adev->vcn.inst[i].num_enc_rings; ++j)
> fence[i] += amdgpu_fence_count_emitted(&vcn_inst->ring_enc[j]);
>
> @@ -490,15 +506,17 @@ static void amdgpu_vcn_idle_work_handler(struct work_struct *work)
> fence[i] += amdgpu_fence_count_emitted(&vcn_inst->ring_dec);
> fences += fence[i];
>
> - if (!fences && !atomic_read(&vcn_inst->total_submission_cnt)) {
> - mutex_lock(&vcn_inst->vcn_pg_lock);
> + idle = !fences;
> + if (idle)
> vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_GATE);
> - mutex_unlock(&vcn_inst->vcn_pg_lock);
> - amdgpu_vcn_put_profile(adev);
>
> - } else {
> +unlock:
> + mutex_unlock(&vcn_inst->vcn_pg_lock);
> + if (idle)
> + amdgpu_vcn_put_profile(adev);
> + else
> schedule_delayed_work(&vcn_inst->idle_work, VCN_IDLE_TIMEOUT);
> - }
> + up_read(&adev->reset_domain->sem);
> }
>
> void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring)
> @@ -507,7 +525,7 @@ void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring)
> struct amdgpu_vcn_inst *vcn_inst = &adev->vcn.inst[ring->me];
>
> if (!atomic_fetch_inc(&vcn_inst->total_submission_cnt))
> - cancel_delayed_work_sync(&vcn_inst->idle_work);
> + cancel_delayed_work(&vcn_inst->idle_work);
>
> mutex_lock(&vcn_inst->vcn_pg_lock);
> vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_UNGATE);
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
> index bea95307fd42..a6438950854e 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
> @@ -526,6 +526,7 @@ int amdgpu_vcn_suspend(struct amdgpu_device *adev, int i);
> int amdgpu_vcn_resume(struct amdgpu_device *adev, int i);
> void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring);
> void amdgpu_vcn_ring_end_use(struct amdgpu_ring *ring);
> +void amdgpu_vcn_cancel_idle_work(struct amdgpu_vcn_inst *vcn_inst);
>
> bool amdgpu_vcn_is_disabled_vcn(struct amdgpu_device *adev,
> enum vcn_ring_type type, uint32_t vcn_instance);
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c
> index 72069b77f6f1..b048fd4b8b59 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c
> @@ -177,7 +177,7 @@ static int jpeg_v2_0_hw_fini(struct amdgpu_ip_block *ip_block)
> {
> struct amdgpu_device *adev = ip_block->adev;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
>
> if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
> RREG32_SOC15(JPEG, 0, mmUVD_JRBC_STATUS))
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c
> index 2968256d4cd7..f6a9258ae3ca 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c
> @@ -239,7 +239,7 @@ static int jpeg_v2_5_hw_fini(struct amdgpu_ip_block *ip_block)
> struct amdgpu_device *adev = ip_block->adev;
> int i;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
>
> for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) {
> if (adev->jpeg.harvest_config & (1 << i))
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c
> index 564e40899df2..922711aa06a3 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c
> @@ -192,7 +192,7 @@ static int jpeg_v3_0_hw_fini(struct amdgpu_ip_block *ip_block)
> {
> struct amdgpu_device *adev = ip_block->adev;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
>
> if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
> RREG32_SOC15(JPEG, 0, mmUVD_JRBC_STATUS))
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c
> index b29ca3a49939..fe7ab9cb35bb 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c
> @@ -221,7 +221,7 @@ static int jpeg_v4_0_hw_fini(struct amdgpu_ip_block *ip_block)
> {
> struct amdgpu_device *adev = ip_block->adev;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
> if (!amdgpu_sriov_vf(adev)) {
> if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
> RREG32_SOC15(JPEG, 0, regUVD_JRBC_STATUS))
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
> index 0343a64595f2..f4896ad9d1de 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
> @@ -455,7 +455,7 @@ static int jpeg_v4_0_3_hw_fini(struct amdgpu_ip_block *ip_block)
> struct amdgpu_device *adev = ip_block->adev;
> int ret = 0;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
>
> if (!amdgpu_sriov_vf(adev)) {
> if (adev->jpeg.cur_state != AMD_PG_STATE_GATE)
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c
> index 4da3f9c995c1..5a0132407a30 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c
> @@ -250,7 +250,7 @@ static int jpeg_v4_0_5_hw_fini(struct amdgpu_ip_block *ip_block)
> struct amdgpu_device *adev = ip_block->adev;
> int i;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
>
> for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) {
> if (adev->jpeg.harvest_config & (1 << i))
> @@ -883,4 +883,3 @@ const struct amdgpu_ip_block_version jpeg_v4_0_5_ip_block = {
> .rev = 5,
> .funcs = &jpeg_v4_0_5_ip_funcs,
> };
> -
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c
> index dcdb089b4308..91968ad26f17 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c
> @@ -189,7 +189,7 @@ static int jpeg_v5_0_0_hw_fini(struct amdgpu_ip_block *ip_block)
> {
> struct amdgpu_device *adev = ip_block->adev;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
>
> if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
> RREG32_SOC15(JPEG, 0, regUVD_JRBC_STATUS))
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
> index f2debf6b6bd0..27116d1e9e64 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
> @@ -326,7 +326,7 @@ static int jpeg_v5_0_1_hw_fini(struct amdgpu_ip_block *ip_block)
> struct amdgpu_device *adev = ip_block->adev;
> int ret = 0;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
>
> if (!amdgpu_sriov_vf(adev)) {
> if (adev->jpeg.cur_state != AMD_PG_STATE_GATE)
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c
> index dd0abce73687..7e068c2d4d6d 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c
> @@ -299,7 +299,7 @@ static int jpeg_v5_0_2_hw_fini(struct amdgpu_ip_block *ip_block)
> struct amdgpu_device *adev = ip_block->adev;
> int ret = 0;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
>
> if (!amdgpu_sriov_vf(adev)) {
> if (adev->jpeg.cur_state != AMD_PG_STATE_GATE)
> diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
> index 8ddd12740a04..16daeb61114c 100644
> --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
> @@ -172,7 +172,7 @@ static int jpeg_v5_3_0_hw_fini(struct amdgpu_ip_block *ip_block)
> {
> struct amdgpu_device *adev = ip_block->adev;
>
> - cancel_delayed_work_sync(&adev->jpeg.idle_work);
> + amdgpu_jpeg_suspend(adev);
>
> if (adev->jpeg.cur_state != AMD_PG_STATE_GATE &&
> RREG32_SOC15(JPEG, 0, regUVD_JRBC0_UVD_JRBC_STATUS))
> diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c
> index 7d2b9e4b913a..50378ee5a463 100644
> --- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c
> @@ -322,7 +322,7 @@ static int vcn_v2_0_hw_fini(struct amdgpu_ip_block *ip_block)
> struct amdgpu_device *adev = ip_block->adev;
> struct amdgpu_vcn_inst *vinst = adev->vcn.inst;
>
> - cancel_delayed_work_sync(&vinst->idle_work);
> + amdgpu_vcn_cancel_idle_work(vinst);
>
> if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
> (vinst->cur_state != AMD_PG_STATE_GATE &&
> diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
> index 0f4f4d66e2e4..3e85abd1e82c 100644
> --- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
> @@ -449,7 +449,7 @@ static int vcn_v3_0_hw_fini(struct amdgpu_ip_block *ip_block)
> if (adev->vcn.harvest_config & (1 << i))
> continue;
>
> - cancel_delayed_work_sync(&vinst->idle_work);
> + amdgpu_vcn_cancel_idle_work(vinst);
>
> if (!amdgpu_sriov_vf(adev)) {
> if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
> diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
> index 4b7f7bbe18da..5b47a643d725 100644
> --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
> @@ -374,7 +374,7 @@ static int vcn_v4_0_hw_fini(struct amdgpu_ip_block *ip_block)
> if (adev->vcn.harvest_config & (1 << i))
> continue;
>
> - cancel_delayed_work_sync(&vinst->idle_work);
> + amdgpu_vcn_cancel_idle_work(vinst);
>
> if (!amdgpu_sriov_vf(adev)) {
> if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
> diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
> index 3f39aa83c319..5aa09c3bd79b 100644
> --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
> +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
> @@ -417,7 +417,7 @@ static int vcn_v4_0_3_hw_fini(struct amdgpu_ip_block *ip_block)
> for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
> struct amdgpu_vcn_inst *vinst = &adev->vcn.inst[i];
>
> - cancel_delayed_work_sync(&vinst->idle_work);
> + amdgpu_vcn_cancel_idle_work(vinst);
>
> if (vinst->cur_state != AMD_PG_STATE_GATE)
> vinst->set_pg_state(vinst, AMD_PG_STATE_GATE);
> diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
> index f30f80c1b07f..9693950cd16d 100644
> --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
> +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
> @@ -327,7 +327,7 @@ static int vcn_v4_0_5_hw_fini(struct amdgpu_ip_block *ip_block)
> if (adev->vcn.harvest_config & (1 << i))
> continue;
>
> - cancel_delayed_work_sync(&vinst->idle_work);
> + amdgpu_vcn_cancel_idle_work(vinst);
>
> if (!amdgpu_sriov_vf(adev)) {
> if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
> diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c
> index 7b7c3bc9f046..0c189063a6ab 100644
> --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c
> @@ -291,7 +291,7 @@ static int vcn_v5_0_0_hw_fini(struct amdgpu_ip_block *ip_block)
> if (adev->vcn.harvest_config & (1 << i))
> continue;
>
> - cancel_delayed_work_sync(&vinst->idle_work);
> + amdgpu_vcn_cancel_idle_work(vinst);
>
> if (!amdgpu_sriov_vf(adev)) {
> if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) ||
> diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
> index c9e990ab297d..3fa553fb41d3 100644
> --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
> +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
> @@ -364,7 +364,7 @@ static int vcn_v5_0_1_hw_fini(struct amdgpu_ip_block *ip_block)
> for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
> struct amdgpu_vcn_inst *vinst = &adev->vcn.inst[i];
>
> - cancel_delayed_work_sync(&adev->vcn.inst[i].idle_work);
> + amdgpu_vcn_cancel_idle_work(&adev->vcn.inst[i]);
> if (vinst->cur_state != AMD_PG_STATE_GATE)
> vinst->set_pg_state(vinst, AMD_PG_STATE_GATE);
> }
> diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c
> index ed47ddd5b08c..eb13f792e25d 100644
> --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c
> +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c
> @@ -279,7 +279,7 @@ static int vcn_v5_0_2_hw_fini(struct amdgpu_ip_block *ip_block)
> for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
> struct amdgpu_vcn_inst *vinst = &adev->vcn.inst[i];
>
> - cancel_delayed_work_sync(&adev->vcn.inst[i].idle_work);
> + amdgpu_vcn_cancel_idle_work(&adev->vcn.inst[i]);
> if (vinst->cur_state != AMD_PG_STATE_GATE)
> vinst->set_pg_state(vinst, AMD_PG_STATE_GATE);
> }
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 7/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in destroy
2026-10-13 8:24 ` [PATCH 7/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in destroy Prike Liang
@ 2026-10-13 9:30 ` Christian König
2026-10-15 6:38 ` Liang, Prike
0 siblings, 1 reply; 18+ messages in thread
From: Christian König @ 2026-10-13 9:30 UTC (permalink / raw)
To: Prike Liang, amd-gfx; +Cc: Alexander.Deucher, Lijo.Lazar, Vitaly.Prosyak
On 10/13/26 10:24, Prike Liang wrote:
> amdgpu_userq_destroy() takes reset_domain->sem for read in
> amdgpu_userq_detach_doorbell() while holding userq_mutex.
Yeah that is correct behavior.
> GPU recovery
> holds reset_domain->sem for write and then takes clientlist_mutex in
> drm_client_dev_suspend(),
That is completely broken behavior and need to be fixed.
The GPU reset can *NEVER* call drm_client_dev_suspend(), who came up with that nonsense?
Regards,
Christian.
> which already depends on userq_mutex through
> the fbdev reservation and userq restore paths. Lockdep reports:
>
> Chain exists of:
> &dev->clientlist_mutex --> &userq_mgr->userq_mutex --> &reset_domain->sem
>
> Keep waiting for the last fence under userq_mutex only, as the fence of a
> hung queue is only force-completed by the reset, then take
> reset_domain->sem before re-acquiring userq_mutex for the doorbell
> detach, unmap and fence driver teardown. This also keeps the MES unmap
> from racing with an in-progress GPU reset.
>
> Fixes: f18719ef4bb7 ("drm/amdgpu: Convert amdgpu userqueue management from IDR to XArray")
> Signed-off-by: Prike Liang <Prike.Liang@amd.com>
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 16 ++++++----------
> 1 file changed, 6 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> index 5fef0cc7703c..a34ea8ecd700 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> @@ -415,15 +415,6 @@ static void amdgpu_userq_wait_for_last_fence(struct amdgpu_usermode_queue *queue
> dma_fence_wait(f, false);
> }
>
> -static void amdgpu_userq_detach_doorbell(struct amdgpu_usermode_queue *queue)
> -{
> - struct amdgpu_device *adev = queue->userq_mgr->adev;
> -
> - down_read(&adev->reset_domain->sem);
> - xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index);
> - up_read(&adev->reset_domain->sem);
> -}
> -
> /**
> * amdgpu_userq_ensure_ev_fence - ensure a valid, unsignaled eviction fence exists
> * @uq_mgr: the usermode queue manager for this process
> @@ -547,8 +538,12 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
>
> mutex_lock(&uq_mgr->userq_mutex);
> amdgpu_userq_wait_for_last_fence(queue);
> + mutex_unlock(&uq_mgr->userq_mutex);
>
> - amdgpu_userq_detach_doorbell(queue);
> + /* GPU recovery takes userq_mutex under reset_domain->sem, keep that order */
> + down_read(&adev->reset_domain->sem);
> + mutex_lock(&uq_mgr->userq_mutex);
> + xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index);
>
> #if defined(CONFIG_DEBUG_FS)
> debugfs_remove_recursive(queue->debugfs_queue);
> @@ -558,6 +553,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
> amdgpu_userq_fence_driver_free(queue);
> queue->fence_drv = NULL;
> mutex_unlock(&uq_mgr->userq_mutex);
> + up_read(&adev->reset_domain->sem);
>
> /*
> * A failed unmap means MES could not remove the hung queue and is now
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure
2026-10-13 9:26 ` [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Christian König
@ 2026-10-14 9:02 ` Liang, Prike
2026-10-14 10:56 ` Christian König
0 siblings, 1 reply; 18+ messages in thread
From: Liang, Prike @ 2026-10-14 9:02 UTC (permalink / raw)
To: Koenig, Christian, amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander, Lazar, Lijo, Prosyak, Vitaly
AMD General
Regards,
Prike
> -----Original Message-----
> From: Koenig, Christian <Christian.Koenig@amd.com>
> Sent: Tuesday, October 13, 2026 5:26 PM
> To: Liang, Prike <Prike.Liang@amd.com>; amd-gfx@lists.freedesktop.org
> Cc: Deucher, Alexander <Alexander.Deucher@amd.com>; Lazar, Lijo
> <Lijo.Lazar@amd.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> Subject: Re: [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction
> failure
>
> On 10/13/26 10:24, Prike Liang wrote:
> > On userq eviction failure, the suspend worker waits for reset_work
> > while holding userq_mutex and inside a DMA-fence signaling section.
> > This makes eviction-fence completion depend on GPU recovery finishing,
> > although recovery can itself require memory allocation and eviction.
>
> Absolutely clear NAK to that!
>
> GPU recovery is to unlock memory allocation and eviction, so it can *NEVER*
> allocate memory nor depend on eviction to complete.
>
> This patch here is just utterly nonsense. What we need to avoid is to touch the userq
> mutex and/or any eviction fence from the GPU recovery.
You're right, the current lock ordering userq mutex-> reset semaphore in the userq eviction signaling path does not need to change,
as it matches the order declared in amdgpu_lockdep_init(). On further check, the VCN/UVD IB test may perform a memory
allocation with GFP_KERNEL that triggers reclaim during GPU recovery. We may need a follow-up patch to address the resulting potential deadlock.
Regards,
Prike
Regards,
> Christian.
>
> >
> > Fixes: c8ed2de0f2ee ("drm/amdgpu: rework userq reset work handling")
> > Signed-off-by: Prike Liang <Prike.Liang@amd.com>
> > ---
> > drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c | 12 +++++++++---
> > drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 +++-------
> > drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 2 +-
> > 3 files changed, 13 insertions(+), 11 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
> > b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
> > index 082d045edf45..c2d1bc7decba 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
> > @@ -65,6 +65,7 @@ amdgpu_eviction_fence_suspend_worker(struct work_struct
> *work)
> > struct amdgpu_userq_mgr *uq_mgr = &fpriv->userq_mgr;
> > struct dma_fence *ev_fence;
> > bool cookie;
> > + int r;
> >
> > mutex_lock(&uq_mgr->userq_mutex);
> >
> > @@ -79,21 +80,26 @@ amdgpu_eviction_fence_suspend_worker(struct
> work_struct *work)
> > cookie = dma_fence_begin_signalling();
> >
> > ev_fence = amdgpu_evf_mgr_get_fence(evf_mgr);
> > - amdgpu_userq_evict(uq_mgr);
> > + r = amdgpu_userq_evict(uq_mgr);
> >
> > /*
> > * Signaling the eviction fence must be done while holding the
> > * userq_mutex. Otherwise we won't resume the queues before issuing the
> > * next fence.
> > */
> > - dma_fence_signal(ev_fence);
> > + if (!r)
> > + dma_fence_signal(ev_fence);
> > dma_fence_end_signalling(cookie);
> > dma_fence_put(ev_fence);
> >
> > - if (!evf_mgr->shutdown)
> > + if (!r && !evf_mgr->shutdown)
> > schedule_delayed_work(&uq_mgr->resume_work, 0);
> >
> > mutex_unlock(&uq_mgr->userq_mutex);
> > +
> > + if (r)
> > + amdgpu_reset_domain_schedule(uq_mgr->adev->reset_domain,
> > + &uq_mgr->reset_work);
> > }
> >
> > int amdgpu_evf_mgr_attach_fence(struct amdgpu_eviction_fence_mgr
> > *evf_mgr, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > index 3fe91fdafe55..5fef0cc7703c 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > @@ -1308,13 +1308,9 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr
> *uq_mgr)
> > ret = r;
> > }
> >
> > - if (ret) {
> > + if (ret)
> > drm_file_err(uq_mgr->file,
> > "Couldn't unmap all the queues, eviction failed ret=%d\n",
> ret);
> > - amdgpu_reset_domain_schedule(uq_mgr->adev->reset_domain,
> > - &uq_mgr->reset_work);
> > - flush_work(&uq_mgr->reset_work);
> > - }
> > return ret;
> > }
> >
> > @@ -1334,10 +1330,10 @@ amdgpu_userq_wait_for_signal(struct
> amdgpu_userq_mgr *uq_mgr)
> > }
> > }
> >
> > -void
> > +int
> > amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr) {
> > - amdgpu_userq_evict_all(uq_mgr);
> > + return amdgpu_userq_evict_all(uq_mgr);
> > }
> >
> > int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct
> > drm_file *file_priv, diff --git
> > a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> > b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> > index 488dc21d7c81..4dcf6151de6a 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> > @@ -163,7 +163,7 @@ void amdgpu_userq_mgr_cancel_resume(struct
> > amdgpu_userq_mgr *userq_mgr); void amdgpu_userq_mgr_fini(struct
> > amdgpu_userq_mgr *userq_mgr);
> >
> > void amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr);
> > -void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
> > +int amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
> >
> > void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
> > struct amdgpu_eviction_fence_mgr *evf_mgr);
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure
2026-10-14 9:02 ` Liang, Prike
@ 2026-10-14 10:56 ` Christian König
0 siblings, 0 replies; 18+ messages in thread
From: Christian König @ 2026-10-14 10:56 UTC (permalink / raw)
To: Liang, Prike, amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander, Lazar, Lijo, Prosyak, Vitaly
On 10/14/26 11:02, Liang, Prike wrote:
> AMD General
>
> Regards,
> Prike
>
>> -----Original Message-----
>> From: Koenig, Christian <Christian.Koenig@amd.com>
>> Sent: Tuesday, October 13, 2026 5:26 PM
>> To: Liang, Prike <Prike.Liang@amd.com>; amd-gfx@lists.freedesktop.org
>> Cc: Deucher, Alexander <Alexander.Deucher@amd.com>; Lazar, Lijo
>> <Lijo.Lazar@amd.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
>> Subject: Re: [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction
>> failure
>>
>> On 10/13/26 10:24, Prike Liang wrote:
>>> On userq eviction failure, the suspend worker waits for reset_work
>>> while holding userq_mutex and inside a DMA-fence signaling section.
>>> This makes eviction-fence completion depend on GPU recovery finishing,
>>> although recovery can itself require memory allocation and eviction.
>>
>> Absolutely clear NAK to that!
>>
>> GPU recovery is to unlock memory allocation and eviction, so it can *NEVER*
>> allocate memory nor depend on eviction to complete.
>>
>> This patch here is just utterly nonsense. What we need to avoid is to touch the userq
>> mutex and/or any eviction fence from the GPU recovery.
>
> You're right, the current lock ordering userq mutex-> reset semaphore in the userq eviction signaling path does not need to change,
> as it matches the order declared in amdgpu_lockdep_init(). On further check, the VCN/UVD IB test may perform a memory
> allocation with GFP_KERNEL that triggers reclaim during GPU recovery. We may need a follow-up patch to address the resulting potential deadlock.
Yeah that is a really good catch. That needs to use GFP_ATOMIC so that we can use the emergency reserves.
That should probably be ok since the memory is immediately freed up again.
Regards,
Christian.
>
> Regards,
> Prike
>
> Regards,
>> Christian.
>>
>>>
>>> Fixes: c8ed2de0f2ee ("drm/amdgpu: rework userq reset work handling")
>>> Signed-off-by: Prike Liang <Prike.Liang@amd.com>
>>> ---
>>> drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c | 12 +++++++++---
>>> drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 +++-------
>>> drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 2 +-
>>> 3 files changed, 13 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
>>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
>>> index 082d045edf45..c2d1bc7decba 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
>>> @@ -65,6 +65,7 @@ amdgpu_eviction_fence_suspend_worker(struct work_struct
>> *work)
>>> struct amdgpu_userq_mgr *uq_mgr = &fpriv->userq_mgr;
>>> struct dma_fence *ev_fence;
>>> bool cookie;
>>> + int r;
>>>
>>> mutex_lock(&uq_mgr->userq_mutex);
>>>
>>> @@ -79,21 +80,26 @@ amdgpu_eviction_fence_suspend_worker(struct
>> work_struct *work)
>>> cookie = dma_fence_begin_signalling();
>>>
>>> ev_fence = amdgpu_evf_mgr_get_fence(evf_mgr);
>>> - amdgpu_userq_evict(uq_mgr);
>>> + r = amdgpu_userq_evict(uq_mgr);
>>>
>>> /*
>>> * Signaling the eviction fence must be done while holding the
>>> * userq_mutex. Otherwise we won't resume the queues before issuing the
>>> * next fence.
>>> */
>>> - dma_fence_signal(ev_fence);
>>> + if (!r)
>>> + dma_fence_signal(ev_fence);
>>> dma_fence_end_signalling(cookie);
>>> dma_fence_put(ev_fence);
>>>
>>> - if (!evf_mgr->shutdown)
>>> + if (!r && !evf_mgr->shutdown)
>>> schedule_delayed_work(&uq_mgr->resume_work, 0);
>>>
>>> mutex_unlock(&uq_mgr->userq_mutex);
>>> +
>>> + if (r)
>>> + amdgpu_reset_domain_schedule(uq_mgr->adev->reset_domain,
>>> + &uq_mgr->reset_work);
>>> }
>>>
>>> int amdgpu_evf_mgr_attach_fence(struct amdgpu_eviction_fence_mgr
>>> *evf_mgr, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>> index 3fe91fdafe55..5fef0cc7703c 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>> @@ -1308,13 +1308,9 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr
>> *uq_mgr)
>>> ret = r;
>>> }
>>>
>>> - if (ret) {
>>> + if (ret)
>>> drm_file_err(uq_mgr->file,
>>> "Couldn't unmap all the queues, eviction failed ret=%d\n",
>> ret);
>>> - amdgpu_reset_domain_schedule(uq_mgr->adev->reset_domain,
>>> - &uq_mgr->reset_work);
>>> - flush_work(&uq_mgr->reset_work);
>>> - }
>>> return ret;
>>> }
>>>
>>> @@ -1334,10 +1330,10 @@ amdgpu_userq_wait_for_signal(struct
>> amdgpu_userq_mgr *uq_mgr)
>>> }
>>> }
>>>
>>> -void
>>> +int
>>> amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr) {
>>> - amdgpu_userq_evict_all(uq_mgr);
>>> + return amdgpu_userq_evict_all(uq_mgr);
>>> }
>>>
>>> int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct
>>> drm_file *file_priv, diff --git
>>> a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
>>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
>>> index 488dc21d7c81..4dcf6151de6a 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
>>> @@ -163,7 +163,7 @@ void amdgpu_userq_mgr_cancel_resume(struct
>>> amdgpu_userq_mgr *userq_mgr); void amdgpu_userq_mgr_fini(struct
>>> amdgpu_userq_mgr *userq_mgr);
>>>
>>> void amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr);
>>> -void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
>>> +int amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
>>>
>>> void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
>>> struct amdgpu_eviction_fence_mgr *evf_mgr);
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU reset
2026-10-13 9:27 ` Christian König
@ 2026-10-15 3:45 ` Liang, Prike
2026-10-15 8:32 ` Christian König
0 siblings, 1 reply; 18+ messages in thread
From: Liang, Prike @ 2026-10-15 3:45 UTC (permalink / raw)
To: Koenig, Christian, amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander, Lazar, Lijo, Prosyak, Vitaly
AMD General
Regards,
Prike
> -----Original Message-----
> From: Koenig, Christian <Christian.Koenig@amd.com>
> Sent: Tuesday, October 13, 2026 5:28 PM
> To: Liang, Prike <Prike.Liang@amd.com>; amd-gfx@lists.freedesktop.org
> Cc: Deucher, Alexander <Alexander.Deucher@amd.com>; Lazar, Lijo
> <Lijo.Lazar@amd.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> Subject: Re: [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU
> reset
>
> On 10/13/26 10:24, Prike Liang wrote:
> > GPU recovery already notifies DRM clients. Skip the VKMS mode-config
> > suspend/resume helpers during that internal reset to avoid duplicate
> > client notification and atomic-state handling under the reset lock.
> > Keep ordinary system suspend and resume unchanged.
>
> Clear NAK to that as well.
>
> The amdgpu_in_reset() function can only be called from inside the GPU reset itself!
>
> It is *NOT* allowed to skip any operation outside the reset depending on this.
How about moving the drm_mode_config_helper_suspend()/resume() calls for
VKMS into the system suspend/resume path (amdgpu_device_suspend/resume())?
VKMS has no hardware state a reset can lose, so recovery doesn't need them,
and that can drop the amdgpu_in_reset() check entirely.
Regards,
Prike
> Regards,
> Christian.
>
> >
> > Signed-off-by: Prike Liang <Prike.Liang@amd.com>
> > ---
> > drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c | 6 ++++++
> > 1 file changed, 6 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
> > b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
> > index d6b51b3f216b..a49c7dda4c4e 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
> > @@ -491,6 +491,9 @@ static int amdgpu_vkms_suspend(struct
> amdgpu_ip_block *ip_block)
> > struct amdgpu_device *adev = ip_block->adev;
> > int r;
> >
> > + if (amdgpu_in_reset(adev))
> > + return 0;
> > +
> > r = drm_mode_config_helper_suspend(adev_to_drm(adev));
> > if (r)
> > return r;
> > @@ -505,6 +508,9 @@ static int amdgpu_vkms_resume(struct amdgpu_ip_block
> *ip_block)
> > r = amdgpu_vkms_hw_init(ip_block);
> > if (r)
> > return r;
> > + if (amdgpu_in_reset(ip_block->adev))
> > + return 0;
> > +
> > return drm_mode_config_helper_resume(adev_to_drm(ip_block->adev));
> > }
> >
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH 7/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in destroy
2026-10-13 9:30 ` Christian König
@ 2026-10-15 6:38 ` Liang, Prike
0 siblings, 0 replies; 18+ messages in thread
From: Liang, Prike @ 2026-10-15 6:38 UTC (permalink / raw)
To: Koenig, Christian, amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander, Lazar, Lijo, Prosyak, Vitaly
AMD General
Regards,
Prike
> -----Original Message-----
> From: Koenig, Christian <Christian.Koenig@amd.com>
> Sent: Tuesday, October 13, 2026 5:31 PM
> To: Liang, Prike <Prike.Liang@amd.com>; amd-gfx@lists.freedesktop.org
> Cc: Deucher, Alexander <Alexander.Deucher@amd.com>; Lazar, Lijo
> <Lijo.Lazar@amd.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> Subject: Re: [PATCH 7/9] drm/amdgpu/userq: take reset_domain->sem before
> userq_mutex in destroy
>
> On 10/13/26 10:24, Prike Liang wrote:
> > amdgpu_userq_destroy() takes reset_domain->sem for read in
> > amdgpu_userq_detach_doorbell() while holding userq_mutex.
>
> Yeah that is correct behavior.
>
> > GPU recovery
> > holds reset_domain->sem for write and then takes clientlist_mutex in
> > drm_client_dev_suspend(),
>
> That is completely broken behavior and need to be fixed.
>
> The GPU reset can *NEVER* call drm_client_dev_suspend(), who came up with
> that nonsense?
The call to drm_client_dev_suspend() have been in the GPU recovery path for some time, possibly leveraged incorrectly from the system suspend path. I’ll clean it up in the GPU recovery path.
Regards,
Prike
> Regards,
> Christian.
>
> > which already depends on userq_mutex through the fbdev reservation and
> > userq restore paths. Lockdep reports:
> >
> > Chain exists of:
> > &dev->clientlist_mutex --> &userq_mgr->userq_mutex -->
> > &reset_domain->sem
> >
> > Keep waiting for the last fence under userq_mutex only, as the fence
> > of a hung queue is only force-completed by the reset, then take
> > reset_domain->sem before re-acquiring userq_mutex for the doorbell
> > detach, unmap and fence driver teardown. This also keeps the MES unmap
> > from racing with an in-progress GPU reset.
> >
> > Fixes: f18719ef4bb7 ("drm/amdgpu: Convert amdgpu userqueue management
> > from IDR to XArray")
> > Signed-off-by: Prike Liang <Prike.Liang@amd.com>
> > ---
> > drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 16 ++++++----------
> > 1 file changed, 6 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > index 5fef0cc7703c..a34ea8ecd700 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> > @@ -415,15 +415,6 @@ static void amdgpu_userq_wait_for_last_fence(struct
> amdgpu_usermode_queue *queue
> > dma_fence_wait(f, false);
> > }
> >
> > -static void amdgpu_userq_detach_doorbell(struct amdgpu_usermode_queue
> > *queue) -{
> > - struct amdgpu_device *adev = queue->userq_mgr->adev;
> > -
> > - down_read(&adev->reset_domain->sem);
> > - xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index);
> > - up_read(&adev->reset_domain->sem);
> > -}
> > -
> > /**
> > * amdgpu_userq_ensure_ev_fence - ensure a valid, unsignaled eviction fence
> exists
> > * @uq_mgr: the usermode queue manager for this process @@ -547,8
> > +538,12 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr,
> > struct amdgpu_usermode_que
> >
> > mutex_lock(&uq_mgr->userq_mutex);
> > amdgpu_userq_wait_for_last_fence(queue);
> > + mutex_unlock(&uq_mgr->userq_mutex);
> >
> > - amdgpu_userq_detach_doorbell(queue);
> > + /* GPU recovery takes userq_mutex under reset_domain->sem, keep that
> order */
> > + down_read(&adev->reset_domain->sem);
> > + mutex_lock(&uq_mgr->userq_mutex);
> > + xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index);
> >
> > #if defined(CONFIG_DEBUG_FS)
> > debugfs_remove_recursive(queue->debugfs_queue);
> > @@ -558,6 +553,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr
> *uq_mgr, struct amdgpu_usermode_que
> > amdgpu_userq_fence_driver_free(queue);
> > queue->fence_drv = NULL;
> > mutex_unlock(&uq_mgr->userq_mutex);
> > + up_read(&adev->reset_domain->sem);
> >
> > /*
> > * A failed unmap means MES could not remove the hung queue and is
> > now
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU reset
2026-10-15 3:45 ` Liang, Prike
@ 2026-10-15 8:32 ` Christian König
0 siblings, 0 replies; 18+ messages in thread
From: Christian König @ 2026-10-15 8:32 UTC (permalink / raw)
To: Liang, Prike, amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander, Lazar, Lijo, Prosyak, Vitaly
On 10/15/26 05:45, Liang, Prike wrote:
> AMD General
>
> Regards,
> Prike
>
>> -----Original Message-----
>> From: Koenig, Christian <Christian.Koenig@amd.com>
>> Sent: Tuesday, October 13, 2026 5:28 PM
>> To: Liang, Prike <Prike.Liang@amd.com>; amd-gfx@lists.freedesktop.org
>> Cc: Deucher, Alexander <Alexander.Deucher@amd.com>; Lazar, Lijo
>> <Lijo.Lazar@amd.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
>> Subject: Re: [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU
>> reset
>>
>> On 10/13/26 10:24, Prike Liang wrote:
>>> GPU recovery already notifies DRM clients. Skip the VKMS mode-config
>>> suspend/resume helpers during that internal reset to avoid duplicate
>>> client notification and atomic-state handling under the reset lock.
>>> Keep ordinary system suspend and resume unchanged.
>>
>> Clear NAK to that as well.
>>
>> The amdgpu_in_reset() function can only be called from inside the GPU reset itself!
>>
>> It is *NOT* allowed to skip any operation outside the reset depending on this.
>
> How about moving the drm_mode_config_helper_suspend()/resume() calls for
> VKMS into the system suspend/resume path (amdgpu_device_suspend/resume())?
> VKMS has no hardware state a reset can lose, so recovery doesn't need them,
> and that can drop the amdgpu_in_reset() check entirely.
I'm not an expert for the VKMS stuff but that sounds like it makes sense to me.
Regards,
Christian.
>
> Regards,
> Prike
>> Regards,
>> Christian.
>>
>>>
>>> Signed-off-by: Prike Liang <Prike.Liang@amd.com>
>>> ---
>>> drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c | 6 ++++++
>>> 1 file changed, 6 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
>>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
>>> index d6b51b3f216b..a49c7dda4c4e 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c
>>> @@ -491,6 +491,9 @@ static int amdgpu_vkms_suspend(struct
>> amdgpu_ip_block *ip_block)
>>> struct amdgpu_device *adev = ip_block->adev;
>>> int r;
>>>
>>> + if (amdgpu_in_reset(adev))
>>> + return 0;
>>> +
>>> r = drm_mode_config_helper_suspend(adev_to_drm(adev));
>>> if (r)
>>> return r;
>>> @@ -505,6 +508,9 @@ static int amdgpu_vkms_resume(struct amdgpu_ip_block
>> *ip_block)
>>> r = amdgpu_vkms_hw_init(ip_block);
>>> if (r)
>>> return r;
>>> + if (amdgpu_in_reset(ip_block->adev))
>>> + return 0;
>>> +
>>> return drm_mode_config_helper_resume(adev_to_drm(ip_block->adev));
>>> }
>>>
>
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-10-15 8:33 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 8:24 [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Prike Liang
2026-10-13 8:24 ` [PATCH 2/9] drm/amdgpu: skip VKMS mode-config helpers during GPU reset Prike Liang
2026-10-13 9:27 ` Christian König
2026-10-15 3:45 ` Liang, Prike
2026-10-15 8:32 ` Christian König
2026-10-13 8:24 ` [PATCH 3/9] drm/amdgpu: skip VMHUB fault handling while the device is inaccessible Prike Liang
2026-10-13 8:24 ` [PATCH 4/9] drm/amdgpu/userq: complete hung queue fences after successful removal Prike Liang
2026-10-13 8:24 ` [PATCH 5/9] drm/amdgpu: finish GPU recovery callbacks before restarting schedulers Prike Liang
2026-10-13 8:24 ` [PATCH 6/9] drm/amdgpu: serialize JPEG and VCN idle work with GPU reset Prike Liang
2026-10-13 9:28 ` Christian König
2026-10-13 8:24 ` [PATCH 7/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in destroy Prike Liang
2026-10-13 9:30 ` Christian König
2026-10-15 6:38 ` Liang, Prike
2026-10-13 8:24 ` [PATCH 8/9] drm/amdgpu/userq: take reset_domain->sem before userq_mutex in create Prike Liang
2026-10-13 8:24 ` [PATCH 9/9] drm/amdgpu/mes: skip unmap queue notification during GPU reset Prike Liang
2026-10-13 9:26 ` [PATCH 1/9] drm/amdgpu: defer GPU recovery after userq eviction failure Christian König
2026-10-14 9:02 ` Liang, Prike
2026-10-14 10:56 ` Christian König
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox