* [PATCH 0/5] drm/amdgpu: five independent fixes in the KMS, userq, UVD and CS paths
@ 2026-08-10 16:13 Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 1/5] drm/amdgpu: free prt_va on the open_kms error path Junrui Luo via B4 Relay
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Junrui Luo via B4 Relay @ 2026-08-10 16:13 UTC (permalink / raw)
To: Alex Deucher, Christian König, David Airlie, Simona Vetter,
Sumit Semwal, Junwei Zhang, Nicolai Hähnle, Prike Liang,
Arvind Yadav, Shashank Sharma, Leo Liu, Felix Kuehling
Cc: amd-gfx, dri-devel, linux-kernel, linux-media, linaro-mm-sig,
Junrui Luo, Yuhao Jiang, stable
Five independent fixes; no dependency between them, they can be applied
or dropped individually.
Patch 1 frees fpriv->prt_va on the amdgpu_driver_open_kms() error path.
amdgpu_vm_fini() releases mappings but never a struct amdgpu_bo_va, so a
failure to map the CSA or the seq64 buffer leaks the bo_va and the
dma_fence stub reference it holds. postclose_kms() already gets this
right; only the open() unwind was missing it.
Patch 2 rejects a mapping without a backing BO in
amdgpu_userq_input_va_validate(). A PRT mapping is routed through
fpriv->prt_va, whose base.bo is NULL, yet a queue_va/rptr_va/wptr_va
inside it passes validation and latches userq_va_mapped. The next unmap
of any PRT mapping in that VM then dereferences bo_va->base.bo in
amdgpu_userq_gem_va_unmap_validate().
Patch 3 bounds the retry loop in amdgpu_userq_ensure_ev_fence(). Every
failure ahead of amdgpu_evf_mgr_rearm() leaves the restore worker giving
up with only a drm_file_err(), so the waiting thread reschedules and
flushes forever in TASK_UNINTERRUPTIBLE - unkillable and out of reach of
the OOM killer. The eviction fence sequence number is used as the loop's
progress condition instead.
Patch 4 applies the decode arm's handle ownership test to the UVD destroy
arm. handles[] and filp[] are per-device, and destroy clears every slot
matching the handle from the command stream without checking the owner,
so one render node client can tear down another's UVD session and leave a
stale filp behind.
Patch 5 releases the userptr HMM ranges in amdgpu_cs_parser_fini().
amdgpu_cs_parser_bos() returns with them live and only two sites free
them; every error edge in between leaks a struct amdgpu_hmm_range plus a
kvmalloc_array() of one hmm_pfn per page of the userptr mapping,
allocated GFP_KERNEL and not charged to the caller's memcg. An IB
address with no VM mapping is enough to reach one of those edges, so it
is repeatable at will from an unprivileged fd.
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
Junrui Luo (5):
drm/amdgpu: free prt_va on the open_kms error path
drm/amdgpu: reject PRT mappings as user queue buffer VAs
drm/amdgpu/userq: bound the eviction fence rearm retry loop
drm/amdgpu: enforce UVD handle ownership on destroy
drm/amdgpu: free userptr HMM ranges on the CS error path
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 10 +++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 5 +++++
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 29 +++++++++++++++++++++++--
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 4 ++--
drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 10 ++++++++-
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 14 ++++++++++--
6 files changed, 65 insertions(+), 7 deletions(-)
---
base-commit: c4f76bf5e107bcda6e496f1c4060c55af091fa79
change-id: 20260810-amdgpu-fixes-b6ab1059a034
Best regards,
--
Junrui Luo <moonafterrain@outlook.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/5] drm/amdgpu: free prt_va on the open_kms error path
2026-08-10 16:13 [PATCH 0/5] drm/amdgpu: five independent fixes in the KMS, userq, UVD and CS paths Junrui Luo via B4 Relay
@ 2026-08-10 16:13 ` Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 2/5] drm/amdgpu: reject PRT mappings as user queue buffer VAs Junrui Luo via B4 Relay
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Junrui Luo via B4 Relay @ 2026-08-10 16:13 UTC (permalink / raw)
To: Alex Deucher, Christian König, David Airlie, Simona Vetter,
Sumit Semwal, Junwei Zhang, Nicolai Hähnle, Prike Liang,
Arvind Yadav, Shashank Sharma, Leo Liu, Felix Kuehling
Cc: amd-gfx, dri-devel, linux-kernel, linux-media, linaro-mm-sig,
Junrui Luo, Yuhao Jiang
From: Junrui Luo <moonafterrain@outlook.com>
amdgpu_driver_open_kms() creates fpriv->prt_va with amdgpu_vm_bo_add()
before mapping the CSA and the seq64 buffer. If either mapping fails
the function jumps to error_vm, which only calls amdgpu_vm_fini() and
then frees fpriv. amdgpu_vm_fini() releases the amdgpu_bo_va_mapping
objects reachable from vm->freed and the vm->va rbtree, but it never
frees a struct amdgpu_bo_va, so the bo_va allocated for prt_va and the
dma_fence stub reference it holds are both lost.
The success path does get this right: amdgpu_driver_postclose_kms()
reserves the root PD and calls amdgpu_vm_bo_del(adev, fpriv->prt_va)
before amdgpu_vm_fini(). Only the open() unwind is missing it.
Drop the bo_va on the error path as well, reserving the root PD as
amdgpu_vm_bo_del() requires.
Fixes: b85891bd6d1b ("drm/amdgpu: IOCTL interface for PRT support v4")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Assisted-by: Claude:claude-opus-5
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 242c48e85912..7ef1c1dcc207 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -1553,6 +1553,11 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
pasid = 0;
}
+ if (fpriv->prt_va &&
+ !WARN_ON(amdgpu_bo_reserve(fpriv->vm.root.bo, true))) {
+ amdgpu_vm_bo_del(adev, fpriv->prt_va);
+ amdgpu_bo_unreserve(fpriv->vm.root.bo);
+ }
amdgpu_vm_fini(adev, &fpriv->vm);
error_pasid:
--
2.51.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/5] drm/amdgpu: reject PRT mappings as user queue buffer VAs
2026-08-10 16:13 [PATCH 0/5] drm/amdgpu: five independent fixes in the KMS, userq, UVD and CS paths Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 1/5] drm/amdgpu: free prt_va on the open_kms error path Junrui Luo via B4 Relay
@ 2026-08-10 16:13 ` Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 3/5] drm/amdgpu/userq: bound the eviction fence rearm retry loop Junrui Luo via B4 Relay
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Junrui Luo via B4 Relay @ 2026-08-10 16:13 UTC (permalink / raw)
To: Alex Deucher, Christian König, David Airlie, Simona Vetter,
Sumit Semwal, Junwei Zhang, Nicolai Hähnle, Prike Liang,
Arvind Yadav, Shashank Sharma, Leo Liu, Felix Kuehling
Cc: amd-gfx, dri-devel, linux-kernel, linux-media, linaro-mm-sig,
Junrui Luo, Yuhao Jiang, stable
From: Junrui Luo <moonafterrain@outlook.com>
amdgpu_userq_input_va_validate() resolves a user-supplied queue_va,
rptr_va or wptr_va to a VM mapping and latches userq_va_mapped on
the owning bo_va. It only checks that a mapping exists and that
the requested span is contained in it, never that the mapping has
a backing BO. PRT mappings do not: amdgpu_gem_va_ioctl() routes
every AMDGPU_VM_PAGE_PRT map through fpriv->prt_va, created via
amdgpu_vm_bo_add(adev, vm, NULL), so base.bo stays NULL while
amdgpu_vm_bo_insert_map() still sets mapping->bo_va.
A VA inside such a mapping therefore passes validation and marks
fpriv->prt_va as userq mapped. The flag is never cleared. On the
next unmap of any PRT mapping in that VM, amdgpu_vm_bo_unmap() sees
userq_va_mapped and calls amdgpu_userq_gem_va_unmap_validate(), which
reads bo_va->base.bo->tbo.base.resv before its ip_mask guard, leading
to a NULL pointer dereference.
Fix by rejecting a mapping without a backing BO in the validation
helper, so the invariant amdgpu_userq_gem_va_unmap_validate() relies
on holds by construction. A sparse mapping has no memory behind it and
cannot serve as a ring, rptr or wptr buffer.
Fixes: 2e7ceac0ea41 ("drm/amdgpu: validate userq va for GEM unmap")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 6d3ed55e9ab4..bec107216811 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -259,6 +259,14 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev,
if (!va_map)
return -EINVAL;
+ /*
+ * A PRT mapping has no backing BO and so can't carry the eviction
+ * fence which amdgpu_userq_gem_va_unmap_validate() waits on. Reject it
+ * here, otherwise that helper dereferences a NULL bo on GEM unmap.
+ */
+ if (!va_map->bo_va->base.bo)
+ return -EINVAL;
+
/* Lookup guarantees start_page is mapped; ensure full span is covered. */
if ((end_addr >> AMDGPU_GPU_PAGE_SHIFT) <= va_map->last) {
va_map->bo_va->userq_va_mapped = true;
--
2.51.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 3/5] drm/amdgpu/userq: bound the eviction fence rearm retry loop
2026-08-10 16:13 [PATCH 0/5] drm/amdgpu: five independent fixes in the KMS, userq, UVD and CS paths Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 1/5] drm/amdgpu: free prt_va on the open_kms error path Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 2/5] drm/amdgpu: reject PRT mappings as user queue buffer VAs Junrui Luo via B4 Relay
@ 2026-08-10 16:13 ` Junrui Luo via B4 Relay
2026-08-10 17:28 ` Christian König
2026-08-10 16:13 ` [PATCH 4/5] drm/amdgpu: enforce UVD handle ownership on destroy Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 5/5] drm/amdgpu: free userptr HMM ranges on the CS error path Junrui Luo via B4 Relay
4 siblings, 1 reply; 7+ messages in thread
From: Junrui Luo via B4 Relay @ 2026-08-10 16:13 UTC (permalink / raw)
To: Alex Deucher, Christian König, David Airlie, Simona Vetter,
Sumit Semwal, Junwei Zhang, Nicolai Hähnle, Prike Liang,
Arvind Yadav, Shashank Sharma, Leo Liu, Felix Kuehling
Cc: amd-gfx, dri-devel, linux-kernel, linux-media, linaro-mm-sig,
Junrui Luo, Yuhao Jiang, stable
From: Junrui Luo <moonafterrain@outlook.com>
amdgpu_userq_ensure_ev_fence() loops until the eviction fence is both
present and unsignaled. The only producer of such a fence is
amdgpu_evf_mgr_rearm(), which runs as the very last step of
amdgpu_userq_vm_validate(). Every failure point ahead of it - the
kzalloc() in the rearm itself, amdgpu_hmm_range_alloc(), the
ttm_bo_validate() calls, the GART binding of the wptr BOs - makes
amdgpu_userq_restore_worker() give up with only a drm_file_err().
Nothing propagates that back, so the waiting thread reschedules the
worker and flushes it again, forever.
Both flush_delayed_work() and mutex_lock() sleep in
TASK_UNINTERRUPTIBLE, so the looping task cannot be killed and the OOM
killer cannot reclaim it. An unprivileged render node client
reaches this from both AMDGPU_USERQ and AMDGPU_USERQ_SIGNAL.
The eviction fence sequence number is already bumped by every
successful rearm, so use it as the loop's progress condition: if a
completed flush of the restore worker did not move it then no rearm
happened and retrying cannot help. Return -ENOMEM in that case and
let both callers report it to userspace.
Fixes: a242a3e4b5be ("drm/amdgpu: simplify eviction fence suspend/resume")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 21 +++++++++++++++++++--
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 4 ++--
drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 10 +++++++++-
3 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index bec107216811..208b53ae5bd1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -448,12 +448,16 @@ static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue)
* Ensures that a valid and not yet signaled eviction fence is attached to the
* usermode queue before any queue operations proceed. If it is signalled, then
* rearm a new eviction fence.
+ *
+ * Returns 0 with @uq_mgr->userq_mutex held, or -ENOMEM with the mutex released
+ * when the restore worker could not rearm the fence.
*/
-void
+int
amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_eviction_fence_mgr *evf_mgr)
{
struct dma_fence *ev_fence;
+ int seq, prev_seq = -1;
retry:
/* Flush any pending resume work to create ev_fence */
@@ -463,7 +467,16 @@ amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr,
ev_fence = amdgpu_evf_mgr_get_fence(evf_mgr);
if (dma_fence_is_signaled(ev_fence)) {
dma_fence_put(ev_fence);
+ seq = atomic_read(&evf_mgr->ev_fence_seq);
mutex_unlock(&uq_mgr->userq_mutex);
+ /*
+ * The sequence number is only bumped by a successful rearm, so
+ * if the flush above ran the worker without moving it then the
+ * restore failed and looping again would never terminate.
+ */
+ if (seq == prev_seq)
+ return -ENOMEM;
+ prev_seq = seq;
/*
* Looks like there was no pending resume work,
* add one now to create a valid eviction fence
@@ -472,6 +485,8 @@ amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr,
goto retry;
}
dma_fence_put(ev_fence);
+
+ return 0;
}
@@ -747,7 +762,9 @@ 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);
+ r = amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
+ if (r)
+ goto erase_doorbell;
/* don't map the queue if scheduling is halted */
if (!adev->userq_halt_for_enforce_isolation ||
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
index 6412a7f7b6ef..c35909bf7ceb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
@@ -164,8 +164,8 @@ void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);
void 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);
+int amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
+ struct amdgpu_eviction_fence_mgr *evf_mgr);
u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev);
bool amdgpu_userq_enabled(struct drm_device *dev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
index 7e80442ec3e5..1c287ce59736 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
@@ -523,7 +523,15 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
goto put_queue;
/* We are here means UQ is active, make sure the eviction fence is valid */
- amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
+ r = amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
+ if (r) {
+ /* The fence is not initialized yet, so unwind it by hand */
+ amdgpu_userq_fence_put_fence_drv_array(fence);
+ amdgpu_userq_fence_driver_put(fence->fence_drv);
+ kvfree(fence->fence_drv_array);
+ kfree(fence);
+ goto put_queue;
+ }
/* Create the new fence */
amdgpu_userq_fence_init(queue, fence, wptr);
--
2.51.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 4/5] drm/amdgpu: enforce UVD handle ownership on destroy
2026-08-10 16:13 [PATCH 0/5] drm/amdgpu: five independent fixes in the KMS, userq, UVD and CS paths Junrui Luo via B4 Relay
` (2 preceding siblings ...)
2026-08-10 16:13 ` [PATCH 3/5] drm/amdgpu/userq: bound the eviction fence rearm retry loop Junrui Luo via B4 Relay
@ 2026-08-10 16:13 ` Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 5/5] drm/amdgpu: free userptr HMM ranges on the CS error path Junrui Luo via B4 Relay
4 siblings, 0 replies; 7+ messages in thread
From: Junrui Luo via B4 Relay @ 2026-08-10 16:13 UTC (permalink / raw)
To: Alex Deucher, Christian König, David Airlie, Simona Vetter,
Sumit Semwal, Junwei Zhang, Nicolai Hähnle, Prike Liang,
Arvind Yadav, Shashank Sharma, Leo Liu, Felix Kuehling
Cc: amd-gfx, dri-devel, linux-kernel, linux-media, linaro-mm-sig,
Junrui Luo, Yuhao Jiang, stable
From: Junrui Luo <moonafterrain@outlook.com>
amdgpu_uvd_cs_msg() validates that a decode message references a handle
owned by the submitting client, rejecting a mismatch between
adev->uvd.filp[i] and ctx->parser->filp. The handles[] and filp[] tables
are per-device and shared by every drm_file that opens the render node.
The destroy message performs no such check: it walks the whole table and
clears every slot matching the handle taken from the command stream
buffer. A client can therefore destroy a handle owned by another client,
clearing the victim's slot and tearing down its session in UVD firmware,
so subsequent decode submissions fail with -ENOENT. Since
amdgpu_uvd_free_handles() only reaps slots whose handle is non-zero, the
cleared slot also retains a stale filp pointer until reused.
Apply the decode arm's ownership test to the destroy arm. The kunmap
is hoisted above the loop, matching the create and decode arms, so
the new error return cannot leak the amdgpu_bo_kmap() reference.
Kernel-initiated teardown goes through amdgpu_uvd_send_msg() and never
runs the parser.
Fixes: 5146419e6feb ("drm/amdgpu: make UVD handle checking more strict")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
index e8b0c62f72be..8d3e5435cf52 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
@@ -918,9 +918,19 @@ static int amdgpu_uvd_cs_msg(struct amdgpu_uvd_cs_ctx *ctx,
case 2:
/* it's a destroy msg, free the handle */
- for (i = 0; i < adev->uvd.max_handles; ++i)
- atomic_cmpxchg(&adev->uvd.handles[i], handle, 0);
amdgpu_bo_kunmap(bo);
+
+ for (i = 0; i < adev->uvd.max_handles; ++i) {
+ if (atomic_read(&adev->uvd.handles[i]) != handle)
+ continue;
+
+ if (adev->uvd.filp[i] != ctx->parser->filp) {
+ DRM_ERROR("UVD handle collision detected!\n");
+ return -EINVAL;
+ }
+
+ atomic_cmpxchg(&adev->uvd.handles[i], handle, 0);
+ }
return 0;
default:
--
2.51.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 5/5] drm/amdgpu: free userptr HMM ranges on the CS error path
2026-08-10 16:13 [PATCH 0/5] drm/amdgpu: five independent fixes in the KMS, userq, UVD and CS paths Junrui Luo via B4 Relay
` (3 preceding siblings ...)
2026-08-10 16:13 ` [PATCH 4/5] drm/amdgpu: enforce UVD handle ownership on destroy Junrui Luo via B4 Relay
@ 2026-08-10 16:13 ` Junrui Luo via B4 Relay
4 siblings, 0 replies; 7+ messages in thread
From: Junrui Luo via B4 Relay @ 2026-08-10 16:13 UTC (permalink / raw)
To: Alex Deucher, Christian König, David Airlie, Simona Vetter,
Sumit Semwal, Junwei Zhang, Nicolai Hähnle, Prike Liang,
Arvind Yadav, Shashank Sharma, Leo Liu, Felix Kuehling
Cc: amd-gfx, dri-devel, linux-kernel, linux-media, linaro-mm-sig,
Junrui Luo, Yuhao Jiang, stable
From: Junrui Luo <moonafterrain@outlook.com>
amdgpu_cs_parser_bos() allocates a struct amdgpu_hmm_range for every
userptr entry of the BO list and returns with them live. They are only
released in two places: the out_free_user_pages label in
amdgpu_cs_parser_bos() itself, and the invalidation check loop in
amdgpu_cs_submit().
Every error edge between those two points leaks. A failure in
amdgpu_cs_patch_jobs(), amdgpu_cs_vm_handling() or amdgpu_cs_sync_rings(),
or an early return from amdgpu_cs_submit() before its release loop, jumps
to error_fini and falls into amdgpu_cs_parser_fini(), which never walks
the BO list for userptr ranges. An IB address with no VM mapping is enough
to get there: amdgpu_cs_patch_ibs() returns the -EINVAL that
amdgpu_cs_find_mapping() hands back, so the leak is repeatable at will
from an unprivileged render node fd. Each leaked entry costs a struct
amdgpu_hmm_range plus its hmm_pfns array, a kvmalloc_array() of one entry
per page of the userptr mapping, allocated with plain GFP_KERNEL and so
not charged to the caller's memory cgroup.
Release the ranges in amdgpu_cs_parser_fini(), which every path out of
amdgpu_cs_ioctl() passes through. amdgpu_hmm_range_free() ignores a NULL
range, so the success path, where amdgpu_cs_submit() has already freed and
cleared them, is unaffected.
Fixes: fec8fdb54e8f ("drm/amdgpu: fix userptr HMM range handling v2")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 617f53f135f3..17c4fec21402 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -1416,6 +1416,16 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser)
amdgpu_vm_bo_invalidate(bo, false);
}
}
+
+ /*
+ * Release the ranges still live on the error paths;
+ * amdgpu_cs_submit() already freed and cleared them when it
+ * got far enough to check them for invalidation.
+ */
+ amdgpu_bo_list_for_each_userptr_entry(e, parser->bo_list) {
+ amdgpu_hmm_range_free(e->range);
+ e->range = NULL;
+ }
amdgpu_bo_list_put(parser->bo_list);
}
--
2.51.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 3/5] drm/amdgpu/userq: bound the eviction fence rearm retry loop
2026-08-10 16:13 ` [PATCH 3/5] drm/amdgpu/userq: bound the eviction fence rearm retry loop Junrui Luo via B4 Relay
@ 2026-08-10 17:28 ` Christian König
0 siblings, 0 replies; 7+ messages in thread
From: Christian König @ 2026-08-10 17:28 UTC (permalink / raw)
To: moonafterrain, Alex Deucher, David Airlie, Simona Vetter,
Sumit Semwal, Junwei Zhang, Nicolai Hähnle, Prike Liang,
Arvind Yadav, Shashank Sharma, Leo Liu, Felix Kuehling
Cc: amd-gfx, dri-devel, linux-kernel, linux-media, linaro-mm-sig,
Yuhao Jiang, stable
On 8/10/26 18:13, Junrui Luo via B4 Relay wrote:
> From: Junrui Luo <moonafterrain@outlook.com>
>
> amdgpu_userq_ensure_ev_fence() loops until the eviction fence is both
> present and unsignaled. The only producer of such a fence is
> amdgpu_evf_mgr_rearm(), which runs as the very last step of
> amdgpu_userq_vm_validate(). Every failure point ahead of it - the
> kzalloc() in the rearm itself, amdgpu_hmm_range_alloc(), the
> ttm_bo_validate() calls, the GART binding of the wptr BOs - makes
> amdgpu_userq_restore_worker() give up with only a drm_file_err().
> Nothing propagates that back, so the waiting thread reschedules the
> worker and flushes it again, forever.
>
> Both flush_delayed_work() and mutex_lock() sleep in
> TASK_UNINTERRUPTIBLE, so the looping task cannot be killed and the OOM
> killer cannot reclaim it. An unprivileged render node client
> reaches this from both AMDGPU_USERQ and AMDGPU_USERQ_SIGNAL.
>
> The eviction fence sequence number is already bumped by every
> successful rearm, so use it as the loop's progress condition: if a
> completed flush of the restore worker did not move it then no rearm
> happened and retrying cannot help. Return -ENOMEM in that case and
> let both callers report it to userspace.
>
> Fixes: a242a3e4b5be ("drm/amdgpu: simplify eviction fence suspend/resume")
> Reported-by: Yuhao Jiang <danisjiang@gmail.com>
> Assisted-by: Claude:claude-opus-5
> Cc: stable@vger.kernel.org
> Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
Absolutely clear NAK!
This functions needs to loop forever should the rearm worker fails to re-arm the fence.
The only thing which could potentially get out of that is to kill the process or maybe that the eviction fence is signaled with an error.
Regards,
Christian.
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 21 +++++++++++++++++++--
> drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 4 ++--
> drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 10 +++++++++-
> 3 files changed, 30 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> index bec107216811..208b53ae5bd1 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> @@ -448,12 +448,16 @@ static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue)
> * Ensures that a valid and not yet signaled eviction fence is attached to the
> * usermode queue before any queue operations proceed. If it is signalled, then
> * rearm a new eviction fence.
> + *
> + * Returns 0 with @uq_mgr->userq_mutex held, or -ENOMEM with the mutex released
> + * when the restore worker could not rearm the fence.
> */
> -void
> +int
> amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr,
> struct amdgpu_eviction_fence_mgr *evf_mgr)
> {
> struct dma_fence *ev_fence;
> + int seq, prev_seq = -1;
>
> retry:
> /* Flush any pending resume work to create ev_fence */
> @@ -463,7 +467,16 @@ amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr,
> ev_fence = amdgpu_evf_mgr_get_fence(evf_mgr);
> if (dma_fence_is_signaled(ev_fence)) {
> dma_fence_put(ev_fence);
> + seq = atomic_read(&evf_mgr->ev_fence_seq);
> mutex_unlock(&uq_mgr->userq_mutex);
> + /*
> + * The sequence number is only bumped by a successful rearm, so
> + * if the flush above ran the worker without moving it then the
> + * restore failed and looping again would never terminate.
> + */
> + if (seq == prev_seq)
> + return -ENOMEM;
> + prev_seq = seq;
> /*
> * Looks like there was no pending resume work,
> * add one now to create a valid eviction fence
> @@ -472,6 +485,8 @@ amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr,
> goto retry;
> }
> dma_fence_put(ev_fence);
> +
> + return 0;
> }
>
>
> @@ -747,7 +762,9 @@ 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);
> + r = amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
> + if (r)
> + goto erase_doorbell;
>
> /* don't map the queue if scheduling is halted */
> if (!adev->userq_halt_for_enforce_isolation ||
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> index 6412a7f7b6ef..c35909bf7ceb 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
> @@ -164,8 +164,8 @@ void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);
>
> void 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);
> +int amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
> + struct amdgpu_eviction_fence_mgr *evf_mgr);
>
> u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev);
> bool amdgpu_userq_enabled(struct drm_device *dev);
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
> index 7e80442ec3e5..1c287ce59736 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
> @@ -523,7 +523,15 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
> goto put_queue;
>
> /* We are here means UQ is active, make sure the eviction fence is valid */
> - amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
> + r = amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
> + if (r) {
> + /* The fence is not initialized yet, so unwind it by hand */
> + amdgpu_userq_fence_put_fence_drv_array(fence);
> + amdgpu_userq_fence_driver_put(fence->fence_drv);
> + kvfree(fence->fence_drv_array);
> + kfree(fence);
> + goto put_queue;
> + }
>
> /* Create the new fence */
> amdgpu_userq_fence_init(queue, fence, wptr);
>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-08-10 17:28 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-10 16:13 [PATCH 0/5] drm/amdgpu: five independent fixes in the KMS, userq, UVD and CS paths Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 1/5] drm/amdgpu: free prt_va on the open_kms error path Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 2/5] drm/amdgpu: reject PRT mappings as user queue buffer VAs Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 3/5] drm/amdgpu/userq: bound the eviction fence rearm retry loop Junrui Luo via B4 Relay
2026-08-10 17:28 ` Christian König
2026-08-10 16:13 ` [PATCH 4/5] drm/amdgpu: enforce UVD handle ownership on destroy Junrui Luo via B4 Relay
2026-08-10 16:13 ` [PATCH 5/5] drm/amdgpu: free userptr HMM ranges on the CS error path Junrui Luo via B4 Relay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®