From 2e7f55eb408c3f72ee1957a0d0ad11d8648a6379 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 17 May 2026 09:17:42 -0400 Subject: [PATCH 01/19] drm/amdgpu: fix lock leak on ENOMEM in AMDGPU_GEM_OP_GET_MAPPING_INFO The AMDGPU_GEM_OP_GET_MAPPING_INFO branch of amdgpu_gem_op_ioctl() holds three cleanup-tracked resources before calling kvcalloc(): the drm_gem_object reference from drm_gem_object_lookup(), the drm_exec lock on the looked-up GEM via drm_exec_lock_obj(), and the drm_exec lock on the per-process VM root page directory via amdgpu_vm_lock_pd(). All three are released by the out_exec label that every other error path in this function jumps to. The kvcalloc() failure path returns -ENOMEM directly, skipping out_exec and leaking all three. The leaked per-process VM root PD dma_resv lock is the load-bearing leak: any subsequent operation on the same VM (further GEM ops, command-submission, eviction, TTM shrinker callbacks) blocks on the held lock. DRM_IOCTL_AMDGPU_GEM_OP is DRM_AUTH | DRM_RENDER_ALLOW, so this is an unprivileged-local denial of service against the caller's GPU context, reachable by any process with /dev/dri/renderD* access. Route the failure through out_exec so drm_exec_fini() and drm_gem_object_put() run. Reproduced on stock 7.0.0-10, Ryzen 7 5700U / Radeon Vega (Lucienne): the failing ioctl returns -ENOMEM and a second GET_MAPPING_INFO on the same fd then blocks in drm_exec_lock_obj() on the leaked dma_resv. SIGKILL on the caller does not reap the task; the fd-release path during process exit goes through amdgpu_gem_object_close() -> drm_exec_prepare_obj() on the same lock, leaving the task in D state until the box is rebooted. The patched kernel was not rebuilt and re-tested on this hardware; the fix is mechanical. Tested on a single Lucienne / Vega box only. Ziyi Guo posted an independent INT_MAX-bound check for args->num_entries in the same branch [1]; the two patches are complementary and can land in either order. Fixes: 4d82724f7f2b ("drm/amdgpu: Add mapping info option for GEM_OP ioctl") Link: https://lore.kernel.org/all/20260208000255.4073363-1-n7l8m4@u.northwestern.edu/ # [1] Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Alex Deucher (cherry picked from commit b69d3256d79de15f54c322986ff4da68f1d65b0a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 123d4a09114d..06dd2e8a5b47 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1094,8 +1094,10 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, * be retried. */ vm_entries = kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL); - if (!vm_entries) - return -ENOMEM; + if (!vm_entries) { + r = -ENOMEM; + goto out_exec; + } amdgpu_vm_bo_va_for_each_valid_mapping(bo_va, mapping) { if (num_mappings < args->num_entries) { From a1ba4594232c87c3b8defd6f89a2e40f8b08395d Mon Sep 17 00:00:00 2001 From: Ziyi Guo Date: Sun, 8 Feb 2026 00:02:55 +0000 Subject: [PATCH 02/19] drm/amdgpu: check num_entries in GEM_OP GET_MAPPING_INFO kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL) at amdgpu_gem.c:1050 uses the user-supplied num_entries directly without any upper bounds check. Since num_entries is a __u32 and sizeof(drm_amdgpu_gem_vm_entry) is 32 bytes, a large num_entries produces an allocation exceeding INT_MAX, triggering WARNING in __kvmalloc_node_noprof(), causing a kernel WARNING, TAINT_WARN, and panic on CONFIG_PANIC_ON_WARN=y systems. Add a size bounds check before we invoke the kvzalloc() to reject oversized num_entries early with -EINVAL. Fixes: 4d82724f7f2b ("drm/amdgpu: Add mapping info option for GEM_OP ioctl") Signed-off-by: Ziyi Guo Signed-off-by: Alex Deucher (cherry picked from commit 1fe7bf5457f6efd7be60b17e23163ba54341d73d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 06dd2e8a5b47..fe6d988e7f24 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1093,6 +1093,11 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, * If that number is larger than the size of the array, the ioctl must * be retried. */ + if (args->num_entries > INT_MAX / sizeof(*vm_entries)) { + r = -EINVAL; + goto out_exec; + } + vm_entries = kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL); if (!vm_entries) { r = -ENOMEM; From 4a03d23ce6ad474cb15862563bc9132e16e3e31e Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Tue, 19 May 2026 15:02:00 +0530 Subject: [PATCH 03/19] drm/amdgpu/userq: Fix doorbell object cleanup of queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unpin and unref the door bell obj if queue creation fails before initialization is complete. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 8c7506f7ba945f21e5abe7f8eac0a3acca6b5330) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index f070ea37d918..2301a44a03b1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -787,7 +787,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) r = uq_funcs->mqd_create(queue, &args->in); if (r) { drm_file_err(uq_mgr->file, "Failed to create Queue\n"); - goto clean_mapping; + goto clean_doorbell_bo; } /* Update VM owner at userq submit-time for page-fault attribution. */ @@ -808,7 +808,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) if (r) { drm_file_err(uq_mgr->file, "Failed to map Queue\n"); mutex_unlock(&uq_mgr->userq_mutex); - goto clean_doorbell; + goto erase_doorbell; } } @@ -831,10 +831,15 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) args->out.queue_id = qid; return 0; -clean_doorbell: +erase_doorbell: xa_erase_irq(&adev->userq_doorbell_xa, index); clean_mqd: uq_funcs->mqd_destroy(queue); +clean_doorbell_bo: + amdgpu_bo_reserve(queue->db_obj.obj, true); + amdgpu_bo_unpin(queue->db_obj.obj); + amdgpu_bo_unreserve(queue->db_obj.obj); + amdgpu_bo_unref(&queue->db_obj.obj); clean_mapping: amdgpu_bo_reserve(fpriv->vm.root.bo, true); amdgpu_userq_buffer_vas_list_cleanup(adev, queue); From ba4c0ff47ee098c8e17d25f9dc050e6276bf9979 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Tue, 19 May 2026 15:12:42 +0530 Subject: [PATCH 04/19] drm/amdgpu/userq: Fix the mutex_init cleanup for fence_drv_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mutex fence_drv_lock is destroyed in amdgpu_userq_fence_driver_free also in one of the jump condition mutex_destroy is also called leading to double mutex_destroy. So rearranging the code so amdgpu_userq_fence_driver_free takes care of the clean up along with mutex_destroy. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 384dbef269d101e5b671fc7b942c56734cd1d186) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 2301a44a03b1..d6390cc5a798 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -748,12 +748,12 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) INIT_DELAYED_WORK(&queue->hang_detect_work, amdgpu_userq_hang_detect_work); - mutex_init(&queue->fence_drv_lock); - xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC); r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv); if (r) goto free_queue; + xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC); + mutex_init(&queue->fence_drv_lock); /* Make sure the queue can actually run with those virtual addresses. */ r = amdgpu_bo_reserve(fpriv->vm.root.bo, false); if (r) @@ -844,7 +844,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) amdgpu_bo_reserve(fpriv->vm.root.bo, true); amdgpu_userq_buffer_vas_list_cleanup(adev, queue); amdgpu_bo_unreserve(fpriv->vm.root.bo); - mutex_destroy(&queue->fence_drv_lock); free_fence_drv: amdgpu_userq_fence_driver_free(queue); free_queue: From ec78a85d95e9c37b6ca16d6ed1639fa64d5dd6dc Mon Sep 17 00:00:00 2001 From: Ivan Lipski Date: Thu, 14 May 2026 11:53:50 -0400 Subject: [PATCH 05/19] drm/amd/display: Write REFCLK to 48MHz on DCN21 [Why&How] dccg21_init() calls dccg2_init() which hardcodes 100MHz refclk values for MICROSECOND_TIME_BASE_DIV and MILLISECOND_TIME_BASE_DIV. DCN21 uses 48MHz refclk, so the wrong values corrupt DCCG timing and cause eDP link training failure on cold boot. Write the correct 48MHz values directly instead of calling dccg2_init(). v2: Fixed typo Fixes: e6e2b956fc81 ("drm/amd/display: Add missing DCCG register entries for DCN20-DCN316") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5272 Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5311 Reported-by: Max Chernoff Tested-by: Max Chernoff Signed-off-by: Ivan Lipski Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 08236c3ef284cd2d110e5e3d51fc9615e551f9dc) Cc: stable@vger.kernel.org --- .../drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c index c4d4eea140f3..1f23dfccf07a 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c @@ -105,15 +105,26 @@ static void dccg21_update_dpp_dto(struct dccg *dccg, int dpp_inst, int req_dppcl * dccg2_init() unconditionally overwrites MICROSECOND_TIME_BASE_DIV to * 0x00120264, destroying the marker before it can be read. * - * Guard the call: if the S0i3 marker is present, skip dccg2_init() so the + * Guard the call: if the S0i3 marker is present, skip init so the * WA can function correctly. bios_golden_init() will handle init in that case. + * + * DCN21 uses 48MHz refclk, not 100MHz, so we must explicitly set the correct + * values (48MHz is taken from rn_clk_mgr_construct()). */ static void dccg21_init(struct dccg *dccg) { + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + if (dccg2_is_s0i3_golden_init_wa_done(dccg)) return; - dccg2_init(dccg); + /* 48MHz refclk from rn_clk_mgr_construct() */ + REG_WRITE(MICROSECOND_TIME_BASE_DIV, 0x00120230); + REG_WRITE(MILLISECOND_TIME_BASE_DIV, 0x0010bb80); + REG_WRITE(DISPCLK_FREQ_CHANGE_CNTL, 0x0e01003c); + + if (REG(REFCLK_CNTL)) + REG_WRITE(REFCLK_CNTL, 0); } static const struct dccg_funcs dccg21_funcs = { From e984d61d92e702096058f0f828f4b2b8563b88ce Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Thu, 7 May 2026 15:51:49 -0400 Subject: [PATCH 06/19] drm/amdkfd: fix NULL pointer bug in svm_range_set_attr The process_info could be NULL if user doesn't call kfd_ioctl_acquire_vm before calling kfd_ioctl_svm. Signed-off-by: Eric Huang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 83a26c812e0529eb040d31a76f73e33e637243d4) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 35ec67d9739b..3841943da5ec 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -3732,6 +3732,9 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, svms = &p->svms; + if (!process_info) + return -EINVAL; + mutex_lock(&process_info->lock); svm_range_list_lock_and_flush_work(svms, mm); From d8d9c820405eb1fcbde959de8898ad7d716a2d7b Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 18 May 2026 17:42:15 +0530 Subject: [PATCH 07/19] drm/amdgpu: simplify return value in amdgpu_userq_get_doorbell_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_userq_get_doorbell_index returns a uint64 type index as well as a int type failure values. Simplifying this and using a int type return value and getting the index in input pointer of type uint64 type. Also since it's used at once place making it static would be better. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit e947ec9d0529d5f93dbdb33cd197347f6a7b2922) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 21 +++++++++++---------- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 4 ---- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index d6390cc5a798..34c0d9ee94f2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -536,12 +536,13 @@ void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr, amdgpu_bo_unref(&userq_obj->obj); } -uint64_t +static int amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_db_info *db_info, - struct drm_file *filp) + struct drm_file *filp, + u64 *index) { - uint64_t index; + u64 doorbell_index; struct drm_gem_object *gobj; struct amdgpu_userq_obj *db_obj = db_info->db_obj; int r, db_size; @@ -588,12 +589,13 @@ amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, goto unpin_bo; } - index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj, - db_info->doorbell_offset, db_size); + doorbell_index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj, + db_info->doorbell_offset, db_size); drm_dbg_driver(adev_to_drm(uq_mgr->adev), - "[Usermode queues] doorbell index=%lld\n", index); + "[Usermode queues] doorbell index=%lld\n", doorbell_index); amdgpu_bo_unreserve(db_obj->obj); - return index; + *index = doorbell_index; + return 0; unpin_bo: amdgpu_bo_unpin(db_obj->obj); @@ -776,10 +778,9 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) db_info.doorbell_handle = queue->doorbell_handle; db_info.db_obj = &queue->db_obj; db_info.doorbell_offset = args->in.doorbell_offset; - index = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp); - if (index == (uint64_t)-EINVAL) { + r = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp, &index); + if (r) { drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n"); - r = -EINVAL; goto clean_mapping; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 49b33e2d6932..033b8a0de6b1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -163,10 +163,6 @@ 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); -uint64_t amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_db_info *db_info, - struct drm_file *filp); - u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev); bool amdgpu_userq_enabled(struct drm_device *dev); From cedee93d43f893ce67e39b57c67240965c7c5a69 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 18 May 2026 18:33:00 +0530 Subject: [PATCH 08/19] drm/amdgpu/userq: add amdgpu_bo_unpin when amdgpu_ttm_alloc_gart fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unpin the wptr_obj->obj when amdgpu_ttm_alloc_gart fails. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit d8145c437ccdc2d91c579787290f82788172bea0) --- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 5b4121ddc78c..026940fad524 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -81,7 +81,7 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, ret = amdgpu_ttm_alloc_gart(&wptr_obj->obj->tbo); if (ret) { DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret); - goto fail_map; + goto fail_alloc_gart; } queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(wptr_obj->obj); @@ -89,6 +89,8 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, drm_exec_fini(&exec); return 0; +fail_alloc_gart: + amdgpu_bo_unpin(wptr_obj->obj); fail_map: amdgpu_bo_unref(&wptr_obj->obj); fail_lock: From a00caed2302c604c19a5cab781e34d7ba4fa7558 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 18 May 2026 18:55:25 +0530 Subject: [PATCH 09/19] drm/amdgpu/userq: reserve root bo without interruption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the code to make it an uninterruptible reservation for root bo. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit d409ab4e387d94b2e593d558b54b7bfd315e0e75) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 34c0d9ee94f2..5da107dffcce 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -620,11 +620,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que /* Cancel any pending hang detection work and cleanup */ cancel_delayed_work_sync(&queue->hang_detect_work); - r = amdgpu_bo_reserve(vm->root.bo, false); - if (r) { - drm_file_err(uq_mgr->file, "Failed to reserve root bo during userqueue destroy\n"); - return r; - } + amdgpu_bo_reserve(vm->root.bo, true); amdgpu_userq_buffer_vas_list_cleanup(adev, queue); amdgpu_bo_unreserve(vm->root.bo); From cf4aafdccefccc7f8236fed028d06725246e289e Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 18 May 2026 19:58:08 +0530 Subject: [PATCH 10/19] drm/amdgpu/userq: make sure queue is valid in the hang_detect_work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thread 1: Running amdgpu_userq_destroy which eventually remove the queue from door bell and set userq_mgr = NULL. Thread2: An interrupt might have scheduled the hang_detect_work which still need userq_mgr to be valid but could get an NULL ptrs. To fix that make sure we cancel the hang_detect_work again before setting userq_mgr to NULL. Along with that we also need all the queue va to remain valid till we could be running anything on the queue and hence moving the userq_va post hang_detect handler is cancelled. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 1a66ceb98b137d18d303b9889f0e7d8c4db73943) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 5da107dffcce..8d7dad1d30eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -427,8 +427,6 @@ static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue) xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index); amdgpu_userq_fence_driver_free(queue); queue->fence_drv = NULL; - queue->userq_mgr = NULL; - list_del(&queue->userq_va_list); up_read(&adev->reset_domain->sem); } @@ -619,11 +617,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que /* Cancel any pending hang detection work and cleanup */ cancel_delayed_work_sync(&queue->hang_detect_work); - - amdgpu_bo_reserve(vm->root.bo, true); - amdgpu_userq_buffer_vas_list_cleanup(adev, queue); - amdgpu_bo_unreserve(vm->root.bo); - mutex_lock(&uq_mgr->userq_mutex); amdgpu_userq_wait_for_last_fence(queue); @@ -635,6 +628,13 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que amdgpu_userq_cleanup(queue); mutex_unlock(&uq_mgr->userq_mutex); + cancel_delayed_work_sync(&queue->hang_detect_work); + amdgpu_bo_reserve(vm->root.bo, true); + amdgpu_userq_buffer_vas_list_cleanup(adev, queue); + amdgpu_bo_unreserve(vm->root.bo); + list_del(&queue->userq_va_list); + queue->userq_mgr = NULL; + amdgpu_bo_reserve(queue->db_obj.obj, true); amdgpu_bo_unpin(queue->db_obj.obj); amdgpu_bo_unreserve(queue->db_obj.obj); From 0fb4a8e64a9db74eeda8da7d0b78985392ae483b Mon Sep 17 00:00:00 2001 From: "Stanley.Yang" Date: Mon, 11 May 2026 16:49:19 +0800 Subject: [PATCH 11/19] drm/amdgpu: fix potential overflow in fs_info.debugfs_name Use snprintf() with sizeof(fs_info.debugfs_name) so a long RAS block name plus the "_err_inject" suffix cannot overflow the 32-byte buffer. Signed-off-by: Stanley.Yang Reviewed-by: Tao Zhou Signed-off-by: Alex Deucher (cherry picked from commit 1a58070fda26857a8f6acc0ab05428e60d5c6844) --- drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 6c644cfe6695..fc9f3adf9912 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -2280,7 +2280,8 @@ void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev) list_for_each_entry(obj, &con->head, node) { if (amdgpu_ras_is_supported(adev, obj->head.block) && (obj->attr_inuse == 1)) { - sprintf(fs_info.debugfs_name, "%s_err_inject", + snprintf(fs_info.debugfs_name, sizeof(fs_info.debugfs_name), + "%s_err_inject", get_ras_block_str(&obj->head)); fs_info.head = obj->head; amdgpu_ras_debugfs_create(adev, &fs_info, dir); From 6842b6a4b72da9b2906ffc5ca9d846ace2c54c14 Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 14 May 2026 10:31:20 -0400 Subject: [PATCH 12/19] drm/amdkfd: Check for pdd drm file first in CRIU restore path CRIU restore ioctls are meant to be called by CRIU with no existing drm file. There's an error path for if the drm file unexpectedly exists. It was positioned so it was missing a fput(drm_file). Do that check earlier, as soon as we have the pdd. Signed-off-by: David Francis Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 2bab781dac78916c5cc8de76345a4102449267d7) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 03b266b26738..8785f7810157 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -2300,6 +2300,11 @@ static int criu_restore_devices(struct kfd_process *p, ret = -EINVAL; goto exit; } + + if (pdd->drm_file) { + ret = -EINVAL; + goto exit; + } pdd->user_gpu_id = device_buckets[i].user_gpu_id; drm_file = fget(device_buckets[i].drm_fd); @@ -2310,11 +2315,6 @@ static int criu_restore_devices(struct kfd_process *p, goto exit; } - if (pdd->drm_file) { - ret = -EINVAL; - goto exit; - } - /* create the vm using render nodes for kfd pdd */ if (kfd_process_device_init_vm(pdd, drm_file)) { pr_err("could not init vm for given pdd\n"); From dd4f3ee535b3b0ac027f75dbf9dc5fc88733c765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 19 May 2026 10:41:54 +0200 Subject: [PATCH 13/19] drm/amd/pm/si: Disregard vblank time when no displays are connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When no displays are connected, there is no vblank happening so the power management code shouldn't worry about it. This fixes a regression that caused the memory clock to be stuck at maximum when there were no displays connected to a SI GPU. Fixes: 9003a0746864 ("drm/amd/pm: Treat zero vblank time as too short in si_dpm (v3)") Fixes: 9d73b107a61b ("drm/amd/pm: Use pm_display_cfg in legacy DPM (v2)") Reviewed-by: Alex Deucher Tested-by: Jeremy Klarenbeek Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 6d87e0199f7b83735b56e422d59f170a201897a8) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index 36942467d4ad..c3aff5d0c53d 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -3076,6 +3076,10 @@ static bool si_dpm_vblank_too_short(void *handle) /* we never hit the non-gddr5 limit so disable it */ u32 switch_limit = adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 0; + /* Disregard vblank time when there are no displays connected */ + if (!adev->pm.pm_display_cfg.num_display) + return false; + /* Consider zero vblank time too short and disable MCLK switching. * Note that the vblank time is set to maximum when no displays are attached, * so we'll still enable MCLK switching in that case. From ca8e7a119a2e4045324cffb8f9f58bedcc3dc928 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Wed, 20 May 2026 16:13:09 +0530 Subject: [PATCH 14/19] drm/amdgpu/userq: remove amdgpu_userq_create/destroy_object wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the amdgpu_userq_create/destroy_object wrappers and use directly the kernel bo allocation function which does all the things which are done in wrapper. Signed-off-by: Sunil Khatri Suggested-by: Christian König Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit deb02080ca5d3f015cf71e56067a39ef2f141998) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 67 ---------------------- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 7 --- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 29 +++++++--- 3 files changed, 21 insertions(+), 82 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 8d7dad1d30eb..a93f5c238e55 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -465,74 +465,7 @@ amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr, dma_fence_put(ev_fence); } -int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj, - int size) -{ - struct amdgpu_device *adev = uq_mgr->adev; - struct amdgpu_bo_param bp; - int r; - memset(&bp, 0, sizeof(bp)); - bp.byte_align = PAGE_SIZE; - bp.domain = AMDGPU_GEM_DOMAIN_GTT; - bp.flags = AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS | - AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; - bp.type = ttm_bo_type_kernel; - bp.size = size; - bp.resv = NULL; - bp.bo_ptr_size = sizeof(struct amdgpu_bo); - - r = amdgpu_bo_create(adev, &bp, &userq_obj->obj); - if (r) { - drm_file_err(uq_mgr->file, "Failed to allocate BO for userqueue (%d)", r); - return r; - } - - r = amdgpu_bo_reserve(userq_obj->obj, true); - if (r) { - drm_file_err(uq_mgr->file, "Failed to reserve BO to map (%d)", r); - goto free_obj; - } - - r = amdgpu_bo_pin(userq_obj->obj, AMDGPU_GEM_DOMAIN_GTT); - if (r) - goto unresv; - - r = amdgpu_ttm_alloc_gart(&(userq_obj->obj)->tbo); - if (r) { - drm_file_err(uq_mgr->file, "Failed to alloc GART for userqueue object (%d)", r); - goto unpin_bo; - } - - r = amdgpu_bo_kmap(userq_obj->obj, &userq_obj->cpu_ptr); - if (r) { - drm_file_err(uq_mgr->file, "Failed to map BO for userqueue (%d)", r); - goto unpin_bo; - } - - userq_obj->gpu_addr = amdgpu_bo_gpu_offset(userq_obj->obj); - amdgpu_bo_unreserve(userq_obj->obj); - memset(userq_obj->cpu_ptr, 0, size); - return 0; - -unpin_bo: - amdgpu_bo_unpin(userq_obj->obj); -unresv: - amdgpu_bo_unreserve(userq_obj->obj); -free_obj: - amdgpu_bo_unref(&userq_obj->obj); - - return r; -} - -void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj) -{ - amdgpu_bo_kunmap(userq_obj->obj); - amdgpu_bo_unpin(userq_obj->obj); - amdgpu_bo_unref(&userq_obj->obj); -} static int amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 033b8a0de6b1..76ef5cfab52e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -151,13 +151,6 @@ void amdgpu_userq_mgr_cancel_reset_work(struct amdgpu_device *adev); void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr); void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr); -int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj, - int size); - -void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj); - void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr); void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr, diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 026940fad524..71251370c8b3 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -192,12 +192,16 @@ static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr, * for the same. */ size = AMDGPU_USERQ_PROC_CTX_SZ + AMDGPU_USERQ_GANG_CTX_SZ; - r = amdgpu_userq_create_object(uq_mgr, ctx, size); + r = amdgpu_bo_create_kernel(uq_mgr->adev, size, 0, + AMDGPU_GEM_DOMAIN_GTT, + &ctx->obj, &ctx->gpu_addr, + &ctx->cpu_ptr); if (r) { DRM_ERROR("Failed to allocate ctx space bo for userqueue, err:%d\n", r); return r; } + memset(ctx->cpu_ptr, 0, size); return 0; } @@ -270,13 +274,19 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, return -ENOMEM; } - r = amdgpu_userq_create_object(uq_mgr, &queue->mqd, - AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size)); + r = amdgpu_bo_create_kernel(adev, + AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size), + 0, AMDGPU_GEM_DOMAIN_GTT, + &queue->mqd.obj, &queue->mqd.gpu_addr, + &queue->mqd.cpu_ptr); if (r) { DRM_ERROR("Failed to create MQD object for userqueue\n"); goto free_props; } + memset(queue->mqd.cpu_ptr, 0, + AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size)); + /* Initialize the MQD BO with user given values */ userq_props->wptr_gpu_addr = mqd_user->wptr_va; userq_props->rptr_gpu_addr = mqd_user->rptr_va; @@ -432,10 +442,12 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, return 0; free_ctx: - amdgpu_userq_destroy_object(uq_mgr, &queue->fw_obj); + amdgpu_bo_free_kernel(&queue->fw_obj.obj, &queue->fw_obj.gpu_addr, + &queue->fw_obj.cpu_ptr); free_mqd: - amdgpu_userq_destroy_object(uq_mgr, &queue->mqd); + amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, + &queue->mqd.cpu_ptr); free_props: kfree(userq_props); @@ -445,11 +457,12 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue) { - struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; - amdgpu_userq_destroy_object(uq_mgr, &queue->fw_obj); + amdgpu_bo_free_kernel(&queue->fw_obj.obj, &queue->fw_obj.gpu_addr, + &queue->fw_obj.cpu_ptr); kfree(queue->userq_prop); - amdgpu_userq_destroy_object(uq_mgr, &queue->mqd); + amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, + &queue->mqd.cpu_ptr); } static int mes_userq_preempt(struct amdgpu_usermode_queue *queue) From 93f5534b35a05ef8a0109c1eefa800062fee810a Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 12 May 2026 10:19:52 -0400 Subject: [PATCH 15/19] drm/amdkfd: fix a vulnerability of integer overflow in kfd debugger get_queue_ids() computes array_size = num_queues * sizeof(uint32_t), which could overflow on 32-bit size_t build. using array_size() instead, it saturates to SIZE_MAX on overflow. Signed-off-by: Eric Huang Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 2d57a0475f085c08b49312dfd8edcb461845f285) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index e0a31e11f0ff..0d7296c739ed 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3308,12 +3308,14 @@ static void copy_context_work_handler(struct work_struct *work) static uint32_t *get_queue_ids(uint32_t num_queues, uint32_t *usr_queue_id_array) { - size_t array_size = num_queues * sizeof(uint32_t); - if (!usr_queue_id_array) return NULL; - return memdup_user(usr_queue_id_array, array_size); + if (num_queues > KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) + return ERR_PTR(-EINVAL); + + return memdup_user(usr_queue_id_array, + array_size(num_queues, sizeof(uint32_t))); } int resume_queues(struct kfd_process *p, From 0b8a1600ab50f331aeeba47c777a1b34cba606bf Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Wed, 20 May 2026 16:25:50 +0530 Subject: [PATCH 16/19] drm/amdgpu/userq: move mqd_destroy to later stage to keep core obj valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mqd_destroy cleans up queue core objects like mqd and fw_object which are needed for any pending fence to signal properly. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 4ad65d610096498c8e265615aba42b3c47441bb5) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index a93f5c238e55..28a1849e7dcd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -417,12 +417,10 @@ static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue) { struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; struct amdgpu_device *adev = uq_mgr->adev; - const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type]; /* Wait for mode-1 reset to complete */ down_read(&adev->reset_domain->sem); - uq_funcs->mqd_destroy(queue); /* Use interrupt-safe locking since IRQ handlers may access these XArrays */ xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index); amdgpu_userq_fence_driver_free(queue); @@ -541,15 +539,15 @@ static int amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_queue *queue) { struct amdgpu_device *adev = uq_mgr->adev; - struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr); - struct amdgpu_vm *vm = &fpriv->vm; - + const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type]; + struct amdgpu_vm *vm = queue->vm; int r = 0; cancel_delayed_work_sync(&uq_mgr->resume_work); /* Cancel any pending hang detection work and cleanup */ cancel_delayed_work_sync(&queue->hang_detect_work); + mutex_lock(&uq_mgr->userq_mutex); amdgpu_userq_wait_for_last_fence(queue); @@ -566,6 +564,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que amdgpu_userq_buffer_vas_list_cleanup(adev, queue); amdgpu_bo_unreserve(vm->root.bo); list_del(&queue->userq_va_list); + uq_funcs->mqd_destroy(queue); queue->userq_mgr = NULL; amdgpu_bo_reserve(queue->db_obj.obj, true); From 181307acf8ea597ad63fd574b44d0f98a329a61b Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Wed, 20 May 2026 16:39:49 +0530 Subject: [PATCH 17/19] drm/amdgpu/userq: use array instead of list for userq_vas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use arrays instead of list for userq_vas since we have fixed no of bos. Also, we dont have to worry to free that memory later since this array would be free along with queue only. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit ef7dc711a664b0c548ecfdf13a00436b7446b8e7) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 88 ++++++---------------- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 20 +++-- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 14 ++-- 3 files changed, 45 insertions(+), 77 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 28a1849e7dcd..cf192500800f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -215,33 +215,15 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell) xa_unlock_irqrestore(xa, flags); } -static int amdgpu_userq_buffer_va_list_add(struct amdgpu_usermode_queue *queue, - struct amdgpu_bo_va_mapping *va_map, u64 addr) -{ - struct amdgpu_userq_va_cursor *va_cursor; - struct userq_va_list; - - va_cursor = kzalloc_obj(*va_cursor); - if (!va_cursor) - return -ENOMEM; - - INIT_LIST_HEAD(&va_cursor->list); - va_cursor->gpu_addr = addr; - va_map->bo_va->userq_va_mapped = true; - list_add(&va_cursor->list, &queue->userq_va_list); - - return 0; -} - int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, struct amdgpu_usermode_queue *queue, - u64 addr, u64 expected_size) + u64 addr, u64 expected_size, + u64 *va_out) { struct amdgpu_bo_va_mapping *va_map; struct amdgpu_vm *vm = queue->vm; u64 user_addr; u64 size; - int r = 0; /* Caller must hold vm->root.bo reservation */ dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv); @@ -250,20 +232,18 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, size = expected_size >> AMDGPU_GPU_PAGE_SHIFT; va_map = amdgpu_vm_bo_lookup_mapping(vm, user_addr); - if (!va_map) { - r = -EINVAL; - goto out_err; - } + if (!va_map) + return -EINVAL; + /* Only validate the userq whether resident in the VM mapping range */ if (user_addr >= va_map->start && va_map->last - user_addr + 1 >= size) { - amdgpu_userq_buffer_va_list_add(queue, va_map, user_addr); + va_map->bo_va->userq_va_mapped = true; + *va_out = user_addr; return 0; } - r = -EINVAL; -out_err: - return r; + return -EINVAL; } static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr) @@ -284,14 +264,16 @@ static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr) static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue) { - struct amdgpu_userq_va_cursor *va_cursor, *tmp; - int r = 0; + int i, r = 0; - list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) { - r += amdgpu_userq_buffer_va_mapped(queue->vm, va_cursor->gpu_addr); + for (i = 0; i < ARRAY_SIZE(queue->userq_vas.va_array); i++) { + if (!queue->userq_vas.va_array[i]) + continue; + r += amdgpu_userq_buffer_va_mapped(queue->vm, + queue->userq_vas.va_array[i]); dev_dbg(queue->userq_mgr->adev->dev, "validate the userq mapping:%p va:%llx r:%d\n", - queue, va_cursor->gpu_addr, r); + queue, queue->userq_vas.va_array[i], r); } if (r != 0) @@ -300,24 +282,7 @@ static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue) return false; } -static void amdgpu_userq_buffer_vas_list_cleanup(struct amdgpu_device *adev, - struct amdgpu_usermode_queue *queue) -{ - struct amdgpu_userq_va_cursor *va_cursor, *tmp; - struct amdgpu_bo_va_mapping *mapping; - /* Caller must hold vm->root.bo reservation */ - dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv); - - list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) { - mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, va_cursor->gpu_addr); - if (mapping) - dev_dbg(adev->dev, "delete the userq:%p va:%llx\n", - queue, va_cursor->gpu_addr); - list_del(&va_cursor->list); - kfree(va_cursor); - } -} static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue) { @@ -540,7 +505,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que { struct amdgpu_device *adev = uq_mgr->adev; const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type]; - struct amdgpu_vm *vm = queue->vm; int r = 0; cancel_delayed_work_sync(&uq_mgr->resume_work); @@ -560,10 +524,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que mutex_unlock(&uq_mgr->userq_mutex); cancel_delayed_work_sync(&queue->hang_detect_work); - amdgpu_bo_reserve(vm->root.bo, true); - amdgpu_userq_buffer_vas_list_cleanup(adev, queue); - amdgpu_bo_unreserve(vm->root.bo); - list_del(&queue->userq_va_list); uq_funcs->mqd_destroy(queue); queue->userq_mgr = NULL; @@ -669,7 +629,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) } kref_init(&queue->refcount); - INIT_LIST_HEAD(&queue->userq_va_list); queue->doorbell_handle = args->in.doorbell_handle; queue->queue_type = args->in.ip_type; queue->vm = &fpriv->vm; @@ -690,14 +649,17 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) goto free_fence_drv; if (amdgpu_userq_input_va_validate(adev, queue, args->in.queue_va, - args->in.queue_size) || + args->in.queue_size, + &queue->userq_vas.va.queue_rb) || amdgpu_userq_input_va_validate(adev, queue, args->in.rptr_va, - AMDGPU_GPU_PAGE_SIZE) || + AMDGPU_GPU_PAGE_SIZE, + &queue->userq_vas.va.rptr) || amdgpu_userq_input_va_validate(adev, queue, args->in.wptr_va, - AMDGPU_GPU_PAGE_SIZE)) { + AMDGPU_GPU_PAGE_SIZE, + &queue->userq_vas.va.wptr)) { r = -EINVAL; amdgpu_bo_unreserve(fpriv->vm.root.bo); - goto clean_mapping; + goto free_fence_drv; } amdgpu_bo_unreserve(fpriv->vm.root.bo); @@ -709,7 +671,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) r = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp, &index); if (r) { drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n"); - goto clean_mapping; + goto free_fence_drv; } queue->doorbell_index = index; @@ -769,10 +731,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) amdgpu_bo_unpin(queue->db_obj.obj); amdgpu_bo_unreserve(queue->db_obj.obj); amdgpu_bo_unref(&queue->db_obj.obj); -clean_mapping: - amdgpu_bo_reserve(fpriv->vm.root.bo, true); - amdgpu_userq_buffer_vas_list_cleanup(adev, queue); - amdgpu_bo_unreserve(fpriv->vm.root.bo); free_fence_drv: amdgpu_userq_fence_driver_free(queue); free_queue: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 76ef5cfab52e..28cfc6682333 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -48,11 +48,6 @@ struct amdgpu_userq_obj { struct amdgpu_bo *obj; }; -struct amdgpu_userq_va_cursor { - u64 gpu_addr; - struct list_head list; -}; - struct amdgpu_usermode_queue { int queue_type; enum amdgpu_userq_state state; @@ -93,7 +88,17 @@ struct amdgpu_usermode_queue { struct delayed_work hang_detect_work; struct kref refcount; - struct list_head userq_va_list; + union { + struct { + u64 queue_rb; + u64 wptr; + u64 rptr; + u64 eop; + u64 shadow; + u64 csa; + } va; + u64 va_array[6]; + } userq_vas; }; struct amdgpu_userq_funcs { @@ -174,7 +179,8 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell); int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, struct amdgpu_usermode_queue *queue, - u64 addr, u64 expected_size); + u64 addr, u64 expected_size, u64 *va_out); + void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, struct amdgpu_bo_va_mapping *mapping, uint64_t saddr); diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 71251370c8b3..98aa00eeb2f4 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -318,8 +318,9 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, kfree(compute_mqd); goto free_mqd; } - r = amdgpu_userq_input_va_validate(adev, queue, compute_mqd->eop_va, - 2048); + r = amdgpu_userq_input_va_validate(adev, queue, + compute_mqd->eop_va, 2048, + &queue->userq_vas.va.eop); amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(compute_mqd); @@ -368,7 +369,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, goto free_mqd; } r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->shadow_va, - shadow_info.shadow_size); + shadow_info.shadow_size, + &queue->userq_vas.va.shadow); if (r) { amdgpu_bo_unreserve(queue->vm->root.bo); kfree(mqd_gfx_v11); @@ -376,7 +378,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, } r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->csa_va, - shadow_info.csa_size); + shadow_info.csa_size, + &queue->userq_vas.va.csa); amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(mqd_gfx_v11); @@ -406,7 +409,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, goto free_mqd; } r = amdgpu_userq_input_va_validate(adev, queue, mqd_sdma_v11->csa_va, - 32); + 32, + &queue->userq_vas.va.csa); amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(mqd_sdma_v11); From 962d684b5dc0741dcd93485d41b450de402d5592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 18 Feb 2026 12:53:27 +0100 Subject: [PATCH 18/19] drm/amdgpu: fix amdgpu_hmm_range_get_pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The notifier sequence must only be read once or otherwise we could work with invalid pages. While at it also fix the coding style, e.g. drop the pre-initialized return value and use the common define for 2G range. Signed-off-by: Christian König Reviewed-by: Vitaly Prosyak Tested-by: Vitaly Prosyak Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit c08972f555945cda57b0adb72272a37910153390) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index f72990ac046e..0a9582da3a33 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -51,8 +51,6 @@ #include "amdgpu_amdkfd.h" #include "amdgpu_hmm.h" -#define MAX_WALK_BYTE (2UL << 30) - /** * amdgpu_hmm_invalidate_gfx - callback to notify about mm change * @@ -170,11 +168,13 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, void *owner, struct amdgpu_hmm_range *range) { - unsigned long end; + const u64 max_bytes = SZ_2G; + + struct hmm_range *hmm_range = &range->hmm_range; unsigned long timeout; unsigned long *pfns; - int r = 0; - struct hmm_range *hmm_range = &range->hmm_range; + unsigned long end; + int r; pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); if (unlikely(!pfns)) { @@ -191,8 +191,9 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, end = start + npages * PAGE_SIZE; hmm_range->dev_private_owner = owner; + hmm_range->notifier_seq = mmu_interval_read_begin(notifier); do { - hmm_range->end = min(hmm_range->start + MAX_WALK_BYTE, end); + hmm_range->end = min(hmm_range->start + max_bytes, end); pr_debug("hmm range: start = 0x%lx, end = 0x%lx", hmm_range->start, hmm_range->end); @@ -200,7 +201,6 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT); retry: - hmm_range->notifier_seq = mmu_interval_read_begin(notifier); r = hmm_range_fault(hmm_range); if (unlikely(r)) { if (r == -EBUSY && !time_after(jiffies, timeout)) @@ -210,7 +210,7 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, if (hmm_range->end == end) break; - hmm_range->hmm_pfns += MAX_WALK_BYTE >> PAGE_SHIFT; + hmm_range->hmm_pfns += max_bytes >> PAGE_SHIFT; hmm_range->start = hmm_range->end; } while (hmm_range->end < end); From 1c824497d8acd3187d585d6187cedc1897dcc871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 18 Feb 2026 12:31:29 +0100 Subject: [PATCH 19/19] drm/amdgpu: fix calling VM invalidation in amdgpu_hmm_invalidate_gfx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we don't invalidate page tables on next CS. Signed-off-by: Christian König Reviewed-by: Vitaly Prosyak Tested-by: Vitaly Prosyak Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit b6444d1bcbc34f6f2a31a3aab3059be082f3683e) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index 0a9582da3a33..5bfa5a84b09c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -76,6 +76,7 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, mmu_interval_set_seq(mni, cur_seq); + amdgpu_vm_bo_invalidate(bo, false); r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, false, MAX_SCHEDULE_TIMEOUT); mutex_unlock(&adev->notifier_lock); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index fccd758b6699..c9f88ecce1a7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1631,6 +1631,7 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, { struct amdgpu_bo_va *bo_va; struct dma_resv *resv; + struct amdgpu_bo *bo; bool clear, unlock; int r; @@ -1650,11 +1651,13 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, while (!list_empty(&vm->invalidated)) { bo_va = list_first_entry(&vm->invalidated, struct amdgpu_bo_va, base.vm_status); - resv = bo_va->base.bo->tbo.base.resv; + bo = bo_va->base.bo; + resv = bo->tbo.base.resv; spin_unlock(&vm->status_lock); /* Try to reserve the BO to avoid clearing its ptes */ - if (!adev->debug_vm && dma_resv_trylock(resv)) { + if (!adev->debug_vm && !amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) && + dma_resv_trylock(resv)) { clear = false; unlock = true; /* The caller is already holding the reservation lock */