From 13238dc0ee4f9ab8dafa2cca7295736191ae2f42 Mon Sep 17 00:00:00 2001 From: Sebastian Brzezinka Date: Wed, 1 Apr 2026 12:10:07 +0200 Subject: [PATCH 1/8] drm/i915/gt: fix refcount underflow in intel_engine_park_heartbeat A use-after-free / refcount underflow is possible when the heartbeat worker and intel_engine_park_heartbeat() race to release the same engine->heartbeat.systole request. The heartbeat worker reads engine->heartbeat.systole and calls i915_request_put() on it when the request is complete, but clears the pointer in a separate, non-atomic step. Concurrently, a request retirement on another CPU can drop the engine wakeref to zero, triggering __engine_park() -> intel_engine_park_heartbeat(). If the heartbeat timer is pending at that point, cancel_delayed_work() returns true and intel_engine_park_heartbeat() reads the stale non-NULL systole pointer and calls i915_request_put() on it again, causing a refcount underflow: ``` <4> [487.221889] Workqueue: i915-unordered engine_retire [i915] <4> [487.222640] RIP: 0010:refcount_warn_saturate+0x68/0xb0 ... <4> [487.222707] Call Trace: <4> [487.222711] <4> [487.222716] intel_engine_park_heartbeat.part.0+0x6f/0x80 [i915] <4> [487.223115] intel_engine_park_heartbeat+0x25/0x40 [i915] <4> [487.223566] __engine_park+0xb9/0x650 [i915] <4> [487.223973] ____intel_wakeref_put_last+0x2e/0xb0 [i915] <4> [487.224408] __intel_wakeref_put_last+0x72/0x90 [i915] <4> [487.224797] intel_context_exit_engine+0x7c/0x80 [i915] <4> [487.225238] intel_context_exit+0xf1/0x1b0 [i915] <4> [487.225695] i915_request_retire.part.0+0x1b9/0x530 [i915] <4> [487.226178] i915_request_retire+0x1c/0x40 [i915] <4> [487.226625] engine_retire+0x122/0x180 [i915] <4> [487.227037] process_one_work+0x239/0x760 <4> [487.227060] worker_thread+0x200/0x3f0 <4> [487.227068] ? __pfx_worker_thread+0x10/0x10 <4> [487.227075] kthread+0x10d/0x150 <4> [487.227083] ? __pfx_kthread+0x10/0x10 <4> [487.227092] ret_from_fork+0x3d4/0x480 <4> [487.227099] ? __pfx_kthread+0x10/0x10 <4> [487.227107] ret_from_fork_asm+0x1a/0x30 <4> [487.227141] ``` Fix this by replacing the non-atomic pointer read + separate clear with xchg() in both racing paths. xchg() is a single indivisible hardware instruction that atomically reads the old pointer and writes NULL. This guarantees only one of the two concurrent callers obtains the non-NULL pointer and performs the put, the other gets NULL and skips it. Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/15880 Fixes: 058179e72e09 ("drm/i915/gt: Replace hangcheck by heartbeats") Cc: # v5.5+ Signed-off-by: Sebastian Brzezinka Reviewed-by: Krzysztof Karas Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/d4c1c14255688dd07cc8044973c4f032a8d1559e.1775038106.git.sebastian.brzezinka@intel.com --- .../gpu/drm/i915/gt/intel_engine_heartbeat.c | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c index 8d4bb95f8424..cad81592c848 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c @@ -145,10 +145,12 @@ static void heartbeat(struct work_struct *wrk) /* Just in case everything has gone horribly wrong, give it a kick */ intel_engine_flush_submission(engine); - rq = engine->heartbeat.systole; - if (rq && i915_request_completed(rq)) { - i915_request_put(rq); - engine->heartbeat.systole = NULL; + rq = xchg(&engine->heartbeat.systole, NULL); + if (rq) { + if (i915_request_completed(rq)) + i915_request_put(rq); + else + engine->heartbeat.systole = rq; } if (!intel_engine_pm_get_if_awake(engine)) @@ -229,8 +231,11 @@ static void heartbeat(struct work_struct *wrk) unlock: mutex_unlock(&ce->timeline->mutex); out: - if (!engine->i915->params.enable_hangcheck || !next_heartbeat(engine)) - i915_request_put(fetch_and_zero(&engine->heartbeat.systole)); + if (!engine->i915->params.enable_hangcheck || !next_heartbeat(engine)) { + rq = xchg(&engine->heartbeat.systole, NULL); + if (rq) + i915_request_put(rq); + } intel_engine_pm_put(engine); } @@ -244,8 +249,13 @@ void intel_engine_unpark_heartbeat(struct intel_engine_cs *engine) void intel_engine_park_heartbeat(struct intel_engine_cs *engine) { - if (cancel_delayed_work(&engine->heartbeat.work)) - i915_request_put(fetch_and_zero(&engine->heartbeat.systole)); + if (cancel_delayed_work(&engine->heartbeat.work)) { + struct i915_request *rq; + + rq = xchg(&engine->heartbeat.systole, NULL); + if (rq) + i915_request_put(rq); + } } void intel_gt_unpark_heartbeats(struct intel_gt *gt) From f6d4afc9ec6a0bc326151b35a7a3369369180079 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Thu, 9 Apr 2026 08:31:11 +0300 Subject: [PATCH 2/8] drm/i915/gem: Drop check for changed VM in EXECBUF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the introduction of d4433c7600f7 ("drm/i915/gem: Use the proto-context to handle create parameters (v5)") it has not been possible for VM to change after context creation so the check will never fail. Sima's analysis: This check was added in f7ce8639f6ff ("drm/i915/gem: Split the context's obj:vma lut into its own mutex") but without any hint in the commit message as to why. In another hunk of that commit there's a hint though in __eb_add_lut: /* user racing with ctx set-vm */ This would mean that this bug was introduced in e0695db7298e ("drm/i915: Create/destroy VM (ppGTT) for use with contexts"), which allowed to change the gem_ctx->vm at runtime, opening up the race that was partially fixed in the earlier referenced commit about a year later. But it cannot be exploited anymore in anything remotely recent because with the introduction of proto-contexts we've made gem_ctx->vm invariant again, exactly to preemptively close all these potential issues. Specifically d4433c7600f7 ("drm/i915/gem: Use the proto-context to handle create parameters (v5)") is the vm specific part of the proto-context work. v3: - Include Sima's analysis and WARN_ON_ONCE v4: - Focus only on latest mainline codebase References: https://lore.kernel.org/all/20260324151741.29338-1-sosohero200@gmail.com/ Reported-by: Ville Syrjälä Cc: Linus Torvalds Cc: Simona Vetter Cc: Tvrtko Ursulin Cc: Andi Shyti Signed-off-by: Joonas Lahtinen Reviewed-by: Tvrtko Ursulin Link: https://patch.msgid.link/20260409053111.8914-1-joonas.lahtinen@linux.intel.com --- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index bd608cea396f..621f07a55c38 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -895,7 +895,7 @@ static struct i915_vma *eb_lookup_vma(struct i915_execbuffer *eb, u32 handle) rcu_read_lock(); vma = radix_tree_lookup(&eb->gem_context->handles_vma, handle); - if (likely(vma && vma->vm == vm)) + if (likely(vma)) vma = i915_vma_tryget(vma); rcu_read_unlock(); if (likely(vma)) From 5ba54393dcd7adf75a9f39f5a933b1538349cad5 Mon Sep 17 00:00:00 2001 From: Sebastian Brzezinka Date: Thu, 16 Apr 2026 13:31:18 +0200 Subject: [PATCH 3/8] drm/i915: skip __i915_request_skip() for already signaled requests After a GPU reset the HWSP is zeroed, so previously completed requests appear incomplete. If such a request is picked up during reset_rewind() and marked guilty, i915_request_set_error_once() returns early (fence already signaled), leaving fence.error without a fatal error code. The subsequent __i915_request_skip() then hits: ``` GEM_BUG_ON(!fatal_error(rq->fence.error)) ``` Fixes a kernel BUG observed on Sandy Bridge (Gen6) during heartbeat-triggered engine resets. ``` kernel BUG at drivers/gpu/drm/i915/i915_request.c:556! RIP: __i915_request_skip+0x15e/0x1d0 [i915] ... __i915_request_reset+0x212/0xa70 [i915] reset_rewind+0xe4/0x280 [i915] intel_gt_reset+0x30d/0x5b0 [i915] heartbeat+0x516/0x530 [i915] ``` Guard __i915_request_skip() with i915_request_signaled(), if the fence is already signaled, the ring content is committed and there is nothing left to skip. Fixes: 36e191f0644b ("drm/i915: Apply i915_request_skip() on submission") Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/13729 Signed-off-by: Sebastian Brzezinka Cc: stable@vger.kernel.org # v5.7+ Reviewed-by: Krzysztof Karas Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/fe76921d35b6ae85aa651822726d0d9815aa5362.1776339012.git.sebastian.brzezinka@intel.com --- drivers/gpu/drm/i915/gt/intel_reset.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c index 4a1675dea1c7..474a4916bdf2 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.c +++ b/drivers/gpu/drm/i915/gt/intel_reset.c @@ -135,7 +135,8 @@ void __i915_request_reset(struct i915_request *rq, bool guilty) rcu_read_lock(); /* protect the GEM context */ if (guilty) { i915_request_set_error_once(rq, -EIO); - __i915_request_skip(rq); + if (!i915_request_signaled(rq)) + __i915_request_skip(rq); banned = mark_guilty(rq); } else { i915_request_set_error_once(rq, -EAGAIN); From e279173213a435309eaccb3dd916eccdf042c889 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Fri, 15 May 2026 15:30:38 +0200 Subject: [PATCH 4/8] drm/i915: Skip deprecated selftest One of workaround test cases is now deprecated on modern platfroms, skip it. Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/12061 Signed-off-by: Janusz Krzysztofik Reviewed-by: Krzysztof Karas Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260515133052.1628281-2-janusz.krzysztofik@linux.intel.com --- drivers/gpu/drm/i915/gt/selftest_workarounds.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/gt/selftest_workarounds.c b/drivers/gpu/drm/i915/gt/selftest_workarounds.c index 81cdecd38346..4e4c420ae67f 100644 --- a/drivers/gpu/drm/i915/gt/selftest_workarounds.c +++ b/drivers/gpu/drm/i915/gt/selftest_workarounds.c @@ -1064,6 +1064,9 @@ static int live_isolated_whitelist(void *arg) enum intel_engine_id id; int i, err = 0; + if (IS_DG2(gt->i915) || IS_METEORLAKE(gt->i915)) + return 0; + /* * Check that a write into a whitelist register works, but * invisible to a second context. From 4462966a93eb185849b7f174f0d0de53476d00a4 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Fri, 8 May 2026 14:23:51 +0200 Subject: [PATCH 5/8] drm/i915: Fix potential UAF in TTM object purge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TLDR: The bo->ttm object might be changed by calling ttm_bo_validate(), move casting it to an i915_tt object later to actually get the right pointer. A user reported hitting the following bug under heavy use on DG2: [26620.095550] Oops: general protection fault, probably for non-canonical address 0xa56b6b6b6b6b6b8b: 0000 1 SMP NOPTI [26620.095556] CPU: 2 UID: 0 PID: 631 Comm: Xorg Not tainted 6.18.8 #1 PREEMPT(lazy) [26620.095558] Hardware name: ASRock B850M Steel Legend WiFi/B850M Steel Legend WiFi, BIOS 3.50 09/18/2025 [26620.095559] RIP: 0010:i915_ttm_purge+0x84/0x100 [i915] [26620.095604] Code: 00 00 00 48 8d 54 24 10 48 89 e6 48 89 fb e8 83 aa ae ff 85 c0 75 6f 48 83 bb a8 01 00 00 00 74 2c 48 8b 45 78 48 85 c0 74 23 <48> 8b 78 20 48 c7 c2 ff ff ff ff 31 f6 e8 7a 73 e3 e0 48 8b 7d 78 [26620.095605] RSP: 0018:ffffc90005fd7430 EFLAGS: 00010282 [26620.095607] RAX: a56b6b6b6b6b6b6b RBX: ffff8881f46c3dc0 RCX: 0000000000000000 [26620.095608] RDX: 0000000000000000 RSI: 0000000000000246 RDI: 00000000ffffffff [26620.095609] RBP: ffff888289610f00 R08: 0000000000000001 R09: ffff88823b022000 [26620.095609] R10: ffff888103029b28 R11: ffff8881fc7f3800 R12: ffff88810b6150d0 [26620.095609] R13: ffff888289610f00 R14: 0000000000000000 R15: ffff8881f46c3dc0 [26620.095610] FS: 00007f1004d86900(0000) GS:ffff88901c858000(0000) knlGS:0000000000000000 [26620.095611] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [26620.095611] CR2: 00007f0fdf489000 CR3: 000000035b0c1000 CR4: 0000000000750ef0 [26620.095612] PKRU: 55555554 [26620.095612] Call Trace: [26620.095615] [26620.095615] i915_ttm_move+0x2b9/0x420 [i915] [26620.095642] ? ttm_tt_init+0x65/0x80 [ttm] [26620.095644] ? i915_ttm_tt_create+0xc6/0x150 [i915] [26620.095667] ttm_bo_handle_move_mem+0xb6/0x160 [ttm] [26620.095669] ttm_bo_evict+0x100/0x150 [ttm] [26620.095671] ? preempt_count_add+0x64/0xa0 [26620.095673] ? _raw_spin_lock+0xe/0x30 [26620.095675] ? _raw_spin_unlock+0xd/0x30 [26620.095675] ? i915_gem_object_evictable+0xb7/0xd0 [i915] [26620.095704] ttm_bo_evict_cb+0x6e/0xd0 [ttm] [26620.095705] ttm_lru_walk_for_evict+0xa6/0x200 [ttm] [26620.095708] ttm_bo_alloc_resource+0x185/0x4f0 [ttm] [26620.095709] ? init_object+0x62/0xd0 [26620.095712] ttm_bo_validate+0x7a/0x180 [ttm] [26620.095713] ? _raw_spin_unlock_irqrestore+0x16/0x30 [26620.095714] __i915_ttm_get_pages+0xb0/0x170 [i915] [26620.095737] i915_ttm_get_pages+0x9f/0x150 [i915] [26620.095759] ? i915_gem_do_execbuffer+0xedc/0x2b40 [i915] [26620.095786] ? alloc_debug_processing+0xd0/0x100 [26620.095787] ? _raw_spin_unlock_irqrestore+0x16/0x30 [26620.095788] ? i915_vma_instance+0xa0/0x4e0 [i915] [26620.095822] __i915_gem_object_get_pages+0x2f/0x40 [i915] [26620.095848] i915_vma_pin_ww+0x706/0x980 [i915] [26620.095875] ? i915_gem_do_execbuffer+0xedc/0x2b40 [i915] [26620.095904] eb_validate_vmas+0x170/0xa00 [i915] [26620.095930] i915_gem_do_execbuffer+0x1201/0x2b40 [i915] [26620.095953] ? alloc_debug_processing+0xd0/0x100 [26620.095954] ? _raw_spin_unlock_irqrestore+0x16/0x30 [26620.095955] ? i915_gem_execbuffer2_ioctl+0xc9/0x240 [i915] [26620.095977] ? __wake_up_sync_key+0x32/0x50 [26620.095979] ? i915_gem_execbuffer2_ioctl+0xc9/0x240 [i915] [26620.096001] ? __slab_alloc.isra.0+0x67/0xc0 [26620.096003] i915_gem_execbuffer2_ioctl+0x11a/0x240 [i915] Results from decode_stacktrace.sh pointed to dereference of a file pointer field of a i915 TTM page vector container associated with an object being purged on eviction. That path is taken when the object is marked as no longer needed. Code analysis revealed a possibility of the i915 TTM page vector container being replaced with a new instance inside a function that purges content of the object, should it be still busy. That function is called, indirectly via a more general function that changes the object's placement and caching policy, before the problematic dereference, but still after a pointer to the container is captured, rendering the pointer no longer valid. Fix the issue by capturing the pointer to the container only after its potential replacement. v2: Move the container_of() inside the if block (Sebastian), - a simplified version of the commit description that explains briefly why the change is necessary (Christian). Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/14882 Fixes: 7ae034590ceae ("drm/i915/ttm: add tt shmem backend") Signed-off-by: Janusz Krzysztofik Cc: stable@vger.kernel.org # v5.17+ Cc: Matthew Auld Cc: Thomas Hellström Cc: Sebastian Brzezinka Cc: Christian König Reviewed-by: Andi Shyti Reviewed-by: Christian König Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260508122612.469227-2-janusz.krzysztofik@linux.intel.com --- drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index de70517b4ef2..df3fcc2b1248 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -419,8 +419,6 @@ void i915_ttm_free_cached_io_rsgt(struct drm_i915_gem_object *obj) int i915_ttm_purge(struct drm_i915_gem_object *obj) { struct ttm_buffer_object *bo = i915_gem_to_ttm(obj); - struct i915_ttm_tt *i915_tt = - container_of(bo->ttm, typeof(*i915_tt), ttm); struct ttm_operation_ctx ctx = { .interruptible = true, .no_wait_gpu = false, @@ -435,16 +433,22 @@ int i915_ttm_purge(struct drm_i915_gem_object *obj) if (ret) return ret; - if (bo->ttm && i915_tt->filp) { - /* - * The below fput(which eventually calls shmem_truncate) might - * be delayed by worker, so when directly called to purge the - * pages(like by the shrinker) we should try to be more - * aggressive and release the pages immediately. - */ - shmem_truncate_range(file_inode(i915_tt->filp), - 0, (loff_t)-1); - fput(fetch_and_zero(&i915_tt->filp)); + if (bo->ttm) { + struct i915_ttm_tt *i915_tt = + container_of(bo->ttm, typeof(*i915_tt), ttm); + + if (i915_tt->filp) { + /* + * The below fput(which eventually calls shmem_truncate) + * might be delayed by worker, so when directly called + * to purge the pages(like by the shrinker) we should + * try to be more aggressive and release the pages + * immediately. + */ + shmem_truncate_range(file_inode(i915_tt->filp), + 0, (loff_t)-1); + fput(fetch_and_zero(&i915_tt->filp)); + } } obj->write_domain = 0; From e8c3344765aa3f5c36ddbc2ec06a25c118f1bcfb Mon Sep 17 00:00:00 2001 From: Krzysztof Karas Date: Fri, 8 May 2026 08:02:13 +0000 Subject: [PATCH 6/8] drm/i915/selftests: Prevent userspace mapping invalidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration testing in i915 assumes current task's address space to allocate new userspace mapping and uses it without registering real user for that address space in mm_struct. On single NUMA node setups PCI probe executes in the same context as userspace process calling the test (i915_selftest from IGT), but when multiple nodes are available, the PCI code puts probe into a kernel workqueue. This switches execution to a kworker, which does not have its own address space in userspace and must borrow such memory from another process, so "current->active_mm" is unknown at the start of the test. It was observed that mm->mm_users would occasionally be 0 or drop to 0 during the test due to short delay between scheduling and executing work in forked process, which reaped userspace mappings, further leading to failures upon reading from userland memory. Prevent this by adding a PID parameter to a trusted task, so its mm_struct may be used if needed. Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/14204 Signed-off-by: Krzysztof Karas Reviewed-by: Michał Grzelak Reviewed-by: Andi Shyti Reviewed-by: Sebastian Brzezinka Reviewed-by: Janusz Krzysztofik Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260508080214.1979686-2-krzysztof.karas@intel.com --- drivers/gpu/drm/i915/i915_selftest.h | 1 + .../gpu/drm/i915/selftests/i915_selftest.c | 74 ++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_selftest.h b/drivers/gpu/drm/i915/i915_selftest.h index 72922028f4ba..e29ca298e7eb 100644 --- a/drivers/gpu/drm/i915/i915_selftest.h +++ b/drivers/gpu/drm/i915/i915_selftest.h @@ -35,6 +35,7 @@ struct i915_selftest { unsigned long timeout_jiffies; unsigned int timeout_ms; unsigned int random_seed; + unsigned int userspace_pid; char *filter; int mock; int live; diff --git a/drivers/gpu/drm/i915/selftests/i915_selftest.c b/drivers/gpu/drm/i915/selftests/i915_selftest.c index 8460f0a70d04..3adcfdab75ae 100644 --- a/drivers/gpu/drm/i915/selftests/i915_selftest.c +++ b/drivers/gpu/drm/i915/selftests/i915_selftest.c @@ -181,13 +181,56 @@ __wait_gsc_huc_load_completed(struct drm_i915_private *i915) pr_warn(DRIVER_NAME "Timed out waiting for huc load via GSC!\n"); } +static struct mm_struct *get_selftest_mm(int u_pid_nr) +{ + struct task_struct *task = NULL; + struct mm_struct *mm = NULL; + struct pid *u_pid = NULL; + + if (u_pid_nr < 1) + return NULL; + + u_pid = find_get_pid(u_pid_nr); + if (!u_pid) { + pr_warn("Could not find PID: %d\n", u_pid_nr); + return NULL; + } + + task = get_pid_task(u_pid, PIDTYPE_PID); + put_pid(u_pid); + if (!task) { + pr_warn("Could not find task for PID: %d\n", u_pid_nr); + return NULL; + } + + if (task->flags & PF_KTHREAD) { + pr_warn("Task not in userspace: %d\n", u_pid_nr); + put_task_struct(task); + return NULL; + } + + mm = get_task_mm(task); + put_task_struct(task); + if (!mm) { + pr_warn("Could not find address space of task with PID: %d\n", u_pid_nr); + return NULL; + } + + return mm; +} + static int __run_selftests(const char *name, struct selftest *st, unsigned int count, void *data) { + struct mm_struct *mm = NULL; + int u_pid_nr = -1; int err = 0; + if (i915_selftest.userspace_pid) + u_pid_nr = i915_selftest.userspace_pid; + while (!i915_selftest.random_seed) i915_selftest.random_seed = get_random_u32(); @@ -201,14 +244,36 @@ static int __run_selftests(const char *name, pr_info(DRIVER_NAME ": Performing %s selftests with st_random_seed=0x%x st_timeout=%u\n", name, i915_selftest.random_seed, i915_selftest.timeout_ms); + /* + * If we are running in a kthread on a multi NUMA system and the user passed + * a valid PID of a userspace task, then we may borrow its address space + * to prepare a safe environment for the mmap selftests. + */ + if (!current->mm && u_pid_nr > 0) { + mm = get_selftest_mm(u_pid_nr); + if (mm) { + kthread_use_mm(mm); + if (unlikely(!current->mm)) { + mmput(mm); + mm = NULL; + pr_warn("Could not set mm as current->mm\n"); + } + } + } + /* Tests are listed in order in i915_*_selftests.h */ for (; count--; st++) { if (!st->enabled) continue; cond_resched(); - if (signal_pending(current)) + if (signal_pending(current)) { + if (mm) { + kthread_unuse_mm(mm); + mmput_async(mm); + } return -EINTR; + } pr_info(DRIVER_NAME ": Running %s\n", st->name); if (data) @@ -226,6 +291,11 @@ static int __run_selftests(const char *name, st->name, err)) err = -1; + if (mm) { + kthread_unuse_mm(mm); + mmput_async(mm); + } + return err; } @@ -507,6 +577,8 @@ void igt_hexdump(const void *buf, size_t len) module_param_named(st_random_seed, i915_selftest.random_seed, uint, 0400); module_param_named(st_timeout, i915_selftest.timeout_ms, uint, 0400); module_param_named(st_filter, i915_selftest.filter, charp, 0400); +module_param_named(st_userspace_pid, i915_selftest.userspace_pid, uint, 0400); +MODULE_PARM_DESC(st_userspace_pid, "For usage in tests that map userspace memory and require address space with controllable lifetime."); module_param_named_unsafe(mock_selftests, i915_selftest.mock, int, 0400); MODULE_PARM_DESC(mock_selftests, "Run selftests before loading, using mock hardware (0:disabled [default], 1:run tests then load driver, -1:run tests then leave dummy module)"); From 82eb94b1fa1317d470420cccd1da75ff7f1811b3 Mon Sep 17 00:00:00 2001 From: Krzysztof Karas Date: Fri, 8 May 2026 08:02:14 +0000 Subject: [PATCH 7/8] drm/i915/selftests: Run vma tests only if current->mm is present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This set of tests require userspace memory to map objects, so run them only if this that memory is available. Signed-off-by: Krzysztof Karas Reviewed-by: Sebastian Brzezinka Reviewed-by: Andi Shyti Reviewed-by: Janusz Krzysztofik Reviewed-by: Michał Grzelak Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260508080214.1979686-3-krzysztof.karas@intel.com --- .../gpu/drm/i915/gem/selftests/i915_gem_mman.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c index 9d454d0b46f2..d01acfb7d93d 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c @@ -1847,11 +1847,12 @@ static int igt_mmap_revoke(void *arg) int i915_gem_mman_live_selftests(struct drm_i915_private *i915) { int ret; - bool unuse_mm = false; static const struct i915_subtest tests[] = { SUBTEST(igt_partial_tiling), SUBTEST(igt_smoke_tiling), SUBTEST(igt_mmap_offset_exhaustion), + }; + static const struct i915_subtest vma_tests[] = { SUBTEST(igt_mmap), SUBTEST(igt_mmap_migrate), SUBTEST(igt_mmap_access), @@ -1859,15 +1860,14 @@ int i915_gem_mman_live_selftests(struct drm_i915_private *i915) SUBTEST(igt_mmap_gpu), }; - if (!current->mm) { - kthread_use_mm(current->active_mm); - unuse_mm = true; - } - ret = i915_live_subtests(tests, i915); + if (ret) + return ret; - if (unuse_mm) - kthread_unuse_mm(current->active_mm); + if (current->mm) + ret = i915_live_subtests(vma_tests, i915); + else + pr_warn("No current->mm to safely borrow userspace memory from. Skipping VMA tests.\n"); return ret; } From 6c064db893f548d57036fce17c6493808396e620 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Mon, 18 May 2026 12:29:41 +0300 Subject: [PATCH 8/8] Revert "i915: don't use a vma that didn't match the context VM" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5401b9adebc9e5f68df58226f51493ef0e6ceb4d. Superseded by a13edf9b92fc ("drm/i915/gem: Drop check for changed VM in EXECBUF"). Else block is now no-op. Link: https://lore.kernel.org/intel-gfx/CAHk-=whfUM8y3PoFfT21+guKWK-mJmAE=8uLzOT+7HGv5NtqSw@mail.gmail.com/ Cc: Linus Torvalds Cc: Ville Syrjälä Cc: Tvrtko Ursulin Signed-off-by: Joonas Lahtinen Reviewed-by: Tvrtko Ursulin Link: https://patch.msgid.link/20260518092941.47247-1-joonas.lahtinen@linux.intel.com --- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index 05997e8bbb29..1f303d4eaa4d 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -898,8 +898,6 @@ static struct i915_vma *eb_lookup_vma(struct i915_execbuffer *eb, u32 handle) vma = radix_tree_lookup(&eb->gem_context->handles_vma, handle); if (likely(vma)) vma = i915_vma_tryget(vma); - else - vma = NULL; rcu_read_unlock(); if (likely(vma)) return vma;