Skip to contentSkip to conversation

perf: Clean ups and simplifications

PATCH RESEND Applied Steven Rostedt 5 participants 22 messages Aug 20, 2025 – Jan 27

Subsystems (5)
BPF perf events Tracing Scheduler +1 more subsystems
lore
Share & tools
lore
#0 lore raw

cover perf: Clean ups and simplifications

This is a resend from: https://lore.kernel.org/linux-trace-kernel/[email protected]/

It cleanly applied on top of the latest tip/perf/core:
448f97fba901 ("perf: Convert mmap() refcounts to refcount_t")

The ongoing work to support sframes[1] in the kernel required a few clean ups to the perf kernel side. Instead of having these changes in the ongoing work, there's no reason they cannot be applied now.

These are just clean ups to the perf code to help simplify the updates that will be made later.

[1] https://lore.kernel.org/linux-trace-kernel/[email protected]

Josh Poimboeuf (4):
perf: Remove get_perf_callchain() init_nr argument
perf: Have get_perf_callchain() return NULL if crosstask and user are set
perf: Simplify get_perf_callchain() user logic
perf: Skip user unwind if the task is a kernel thread

Steven Rostedt (1):

      perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

----

 include/linux/perf_event.h |  2 +-
 kernel/bpf/stackmap.c      |  4 ++--
 kernel/events/callchain.c  | 40 +++++++++++++++++++---------------------
 kernel/events/core.c       |  9 +++++----
 4 files changed, 27 insertions(+), 28 deletions(-)
#1 lore raw

PATCH RESEND 1/5 perf: Remove get_perf_callchain() init_nr argument

From: Josh Poimboeuf <[email protected]>

The 'init_nr' argument has double duty: it's used to initialize both the number of contexts and the number of stack entries. That's confusing and the callers always pass zero anyway. Hard code the zero.

---
 include/linux/perf_event.h |  2 +-
 kernel/bpf/stackmap.c      |  4 ++--
 kernel/events/callchain.c  | 12 ++++++------
 kernel/events/core.c       |  2 +-
 4 files changed, 10 insertions(+), 10 deletions(-)
include/linux/perf_event.h +1 -1
index bfbf9ea53f25..fd1d91017b99 100644
@@ -1719,7 +1719,7 @@ DECLARE_PER_CPU(struct perf_callchain_entry, perf_callchain_entry);
 extern void perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs);
 extern void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs);
 extern struct perf_callchain_entry *
-get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
+get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		   u32 max_stack, bool crosstask, bool add_mark);
 extern int get_callchain_buffers(int max_stack);
 extern void put_callchain_buffers(void);
kernel/bpf/stackmap.c +2 -2
index 3615c06b7dfa..ec3a57a5fba1 100644
@@ -314,7 +314,7 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map,
 	if (max_depth > sysctl_perf_event_max_stack)
 		max_depth = sysctl_perf_event_max_stack;
 
-	trace = get_perf_callchain(regs, 0, kernel, user, max_depth,
+	trace = get_perf_callchain(regs, kernel, user, max_depth,
 				   false, false);
 
 	if (unlikely(!trace))
@@ -451,7 +451,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
 	else if (kernel && task)
 		trace = get_callchain_entry_for_task(task, max_depth);
 	else
-		trace = get_perf_callchain(regs, 0, kernel, user, max_depth,
+		trace = get_perf_callchain(regs, kernel, user, max_depth,
 					   crosstask, false);
 
 	if (unlikely(!trace) || trace->nr < skip) {
kernel/events/callchain.c +6 -6
index 6c83ad674d01..b0f5bd228cd8 100644
@@ -217,7 +217,7 @@ static void fixup_uretprobe_trampoline_entries(struct perf_callchain_entry *entr
 }
 
 struct perf_callchain_entry *
-get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
+get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		   u32 max_stack, bool crosstask, bool add_mark)
 {
 	struct perf_callchain_entry *entry;
@@ -228,11 +228,11 @@ get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
 	if (!entry)
 		return NULL;
 
-	ctx.entry     = entry;
-	ctx.max_stack = max_stack;
-	ctx.nr	      = entry->nr = init_nr;
-	ctx.contexts       = 0;
-	ctx.contexts_maxed = false;
+	ctx.entry		= entry;
+	ctx.max_stack		= max_stack;
+	ctx.nr			= entry->nr = 0;
+	ctx.contexts		= 0;
+	ctx.contexts_maxed	= false;
 
 	if (kernel && !user_mode(regs)) {
 		if (add_mark)
kernel/events/core.c +1 -1
index ea357044d780..bade8e0fced7 100644
@@ -8210,7 +8210,7 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
 	if (!kernel && !user)
 		return &__empty_callchain;
 
-	callchain = get_perf_callchain(regs, 0, kernel, user,
+	callchain = get_perf_callchain(regs, kernel, user,
 				       max_stack, crosstask, true);
 	return callchain ?: &__empty_callchain;
 }
Signature · 1 line
2.50.1
merge bot Applied
#2 lore raw

[tip: perf/core] perf: Remove get_perf_callchain() init_nr argument

The following commit has been merged into the perf/core branch of tip:

The following commit has been merged into the perf/core branch of tip:

Commit-ID:     e649bcda25b5ae1a30a182cc450f928a0b282c93
Gitweb:        https://git.kernel.org/tip/e649bcda25b5ae1a30a182cc450f928a0b282c93
Author:        Josh Poimboeuf <[email protected]>
AuthorDate:    Wed, 20 Aug 2025 14:03:39 -04:00
Committer:     Peter Zijlstra <[email protected]>
CommitterDate: Tue, 26 Aug 2025 09:51:12 +02:00

perf: Remove get_perf_callchain() init_nr argument

The 'init_nr' argument has double duty: it's used to initialize both the number of contexts and the number of stack entries. That's confusing and the callers always pass zero anyway. Hard code the zero.

---
 include/linux/perf_event.h |  2 +-
 kernel/bpf/stackmap.c      |  4 ++--
 kernel/events/callchain.c  | 12 ++++++------
 kernel/events/core.c       |  2 +-
 4 files changed, 10 insertions(+), 10 deletions(-)
include/linux/perf_event.h +1 -1
index bfbf9ea..fd1d910 100644
@@ -1719,7 +1719,7 @@ DECLARE_PER_CPU(struct perf_callchain_entry, perf_callchain_entry);
 extern void perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs);
 extern void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs);
 extern struct perf_callchain_entry *
-get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
+get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		   u32 max_stack, bool crosstask, bool add_mark);
 extern int get_callchain_buffers(int max_stack);
 extern void put_callchain_buffers(void);
kernel/bpf/stackmap.c +2 -2
index 3615c06..ec3a57a 100644
@@ -314,7 +314,7 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map,
 	if (max_depth > sysctl_perf_event_max_stack)
 		max_depth = sysctl_perf_event_max_stack;
 
-	trace = get_perf_callchain(regs, 0, kernel, user, max_depth,
+	trace = get_perf_callchain(regs, kernel, user, max_depth,
 				   false, false);
 
 	if (unlikely(!trace))
@@ -451,7 +451,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
 	else if (kernel && task)
 		trace = get_callchain_entry_for_task(task, max_depth);
 	else
-		trace = get_perf_callchain(regs, 0, kernel, user, max_depth,
+		trace = get_perf_callchain(regs, kernel, user, max_depth,
 					   crosstask, false);
 
 	if (unlikely(!trace) || trace->nr < skip) {
kernel/events/callchain.c +6 -6
index 6c83ad6..b0f5bd2 100644
@@ -217,7 +217,7 @@ static void fixup_uretprobe_trampoline_entries(struct perf_callchain_entry *entr
 }
 
 struct perf_callchain_entry *
-get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
+get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		   u32 max_stack, bool crosstask, bool add_mark)
 {
 	struct perf_callchain_entry *entry;
@@ -228,11 +228,11 @@ get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
 	if (!entry)
 		return NULL;
 
-	ctx.entry     = entry;
-	ctx.max_stack = max_stack;
-	ctx.nr	      = entry->nr = init_nr;
-	ctx.contexts       = 0;
-	ctx.contexts_maxed = false;
+	ctx.entry		= entry;
+	ctx.max_stack		= max_stack;
+	ctx.nr			= entry->nr = 0;
+	ctx.contexts		= 0;
+	ctx.contexts_maxed	= false;
 
 	if (kernel && !user_mode(regs)) {
 		if (add_mark)
kernel/events/core.c +1 -1
index ea35704..bade8e0 100644
@@ -8210,7 +8210,7 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
 	if (!kernel && !user)
 		return &__empty_callchain;
 
-	callchain = get_perf_callchain(regs, 0, kernel, user,
+	callchain = get_perf_callchain(regs, kernel, user,
 				       max_stack, crosstask, true);
 	return callchain ?: &__empty_callchain;
 }
#3 lore raw

PATCH RESEND 2/5 perf: Have get_perf_callchain() return NULL if crosstask and user are set

In reply to Steven Rostedt

From: Josh Poimboeuf <[email protected]>

get_perf_callchain() doesn't support cross-task unwinding for user space stacks, have it return NULL if both the crosstask and user arguments are set.

---
 kernel/events/callchain.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
kernel/events/callchain.c +5 -5
index b0f5bd228cd8..cd0e3fc7ed05 100644
@@ -224,6 +224,10 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 	struct perf_callchain_entry_ctx ctx;
 	int rctx, start_entry_idx;
 
+	/* crosstask is not supported for user stacks */
+	if (crosstask && user && !kernel)
+		return NULL;
+
 	entry = get_callchain_entry(&rctx);
 	if (!entry)
 		return NULL;
@@ -240,7 +244,7 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		perf_callchain_kernel(&ctx, regs);
 	}
 
-	if (user) {
+	if (user && !crosstask) {
 		if (!user_mode(regs)) {
 			if  (current->mm)
 				regs = task_pt_regs(current);
@@ -249,9 +253,6 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		}
 
 		if (regs) {
-			if (crosstask)
-				goto exit_put;
-
 			if (add_mark)
 				perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);
 
@@ -261,7 +262,6 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		}
 	}
 
-exit_put:
 	put_callchain_entry(rctx);
 
 	return entry;
Signature · 1 line
2.50.1
merge bot Applied
#4 lore raw

[tip: perf/core] perf: Have get_perf_callchain() return NULL if crosstask and user are set

The following commit has been merged into the perf/core branch of tip:

The following commit has been merged into the perf/core branch of tip:

Commit-ID:     153f9e74dec230f2e070e16fa061bc7adfd2c450
Gitweb:        https://git.kernel.org/tip/153f9e74dec230f2e070e16fa061bc7adfd2c450
Author:        Josh Poimboeuf <[email protected]>
AuthorDate:    Wed, 20 Aug 2025 14:03:40 -04:00
Committer:     Peter Zijlstra <[email protected]>
CommitterDate: Tue, 26 Aug 2025 09:51:12 +02:00

perf: Have get_perf_callchain() return NULL if crosstask and user are set

get_perf_callchain() doesn't support cross-task unwinding for user space stacks, have it return NULL if both the crosstask and user arguments are set.

---
 kernel/events/callchain.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
kernel/events/callchain.c +5 -5
index b0f5bd2..cd0e3fc 100644
@@ -224,6 +224,10 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 	struct perf_callchain_entry_ctx ctx;
 	int rctx, start_entry_idx;
 
+	/* crosstask is not supported for user stacks */
+	if (crosstask && user && !kernel)
+		return NULL;
+
 	entry = get_callchain_entry(&rctx);
 	if (!entry)
 		return NULL;
@@ -240,7 +244,7 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		perf_callchain_kernel(&ctx, regs);
 	}
 
-	if (user) {
+	if (user && !crosstask) {
 		if (!user_mode(regs)) {
 			if  (current->mm)
 				regs = task_pt_regs(current);
@@ -249,9 +253,6 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		}
 
 		if (regs) {
-			if (crosstask)
-				goto exit_put;
-
 			if (add_mark)
 				perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);
 
@@ -261,7 +262,6 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		}
 	}
 
-exit_put:
 	put_callchain_entry(rctx);
 
 	return entry;
#5 lore raw

PATCH RESEND 3/5 perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

In reply to Steven Rostedt

From: Steven Rostedt <[email protected]>

To determine if a task is a kernel thread or not, it is more reliable to use (current->flags & (PF_KTHREAD|PF_USER_WORKERi)) than to rely on current->mm being NULL. That is because some kernel tasks (io_uring helpers) may have a mm field.

---
 kernel/events/callchain.c | 6 +++---
 kernel/events/core.c      | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)
kernel/events/callchain.c +3 -3
index cd0e3fc7ed05..5982d18f169b 100644
@@ -246,10 +246,10 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
 	if (user && !crosstask) {
 		if (!user_mode(regs)) {
-			if  (current->mm)
-				regs = task_pt_regs(current);
-			else
+			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
 				regs = NULL;
+			else
+				regs = task_pt_regs(current);
 		}
 
 		if (regs) {
kernel/events/core.c +2 -2
index bade8e0fced7..f880cec0c980 100644
@@ -7446,7 +7446,7 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
 	if (user_mode(regs)) {
 		regs_user->abi = perf_reg_abi(current);
 		regs_user->regs = regs;
-	} else if (!(current->flags & PF_KTHREAD)) {
+	} else if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
 		perf_get_regs_user(regs_user, regs);
 	} else {
 		regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;
@@ -8086,7 +8086,7 @@ static u64 perf_virt_to_phys(u64 virt)
 		 * Try IRQ-safe get_user_page_fast_only first.
 		 * If failed, leave phys_addr as 0.
 		 */
-		if (current->mm != NULL) {
+		if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
 			struct page *p;
 
 			pagefault_disable();
Signature · 1 line
2.50.1
merge bot Applied
#6 lore raw

[tip: perf/core] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

The following commit has been merged into the perf/core branch of tip:

The following commit has been merged into the perf/core branch of tip:

Commit-ID:     90942f9fac05702065ff82ed0bade0d08168d4ea
Gitweb:        https://git.kernel.org/tip/90942f9fac05702065ff82ed0bade0d08168d4ea
Author:        Steven Rostedt <[email protected]>
AuthorDate:    Wed, 20 Aug 2025 14:03:41 -04:00
Committer:     Peter Zijlstra <[email protected]>
CommitterDate: Tue, 26 Aug 2025 09:51:13 +02:00

perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

To determine if a task is a kernel thread or not, it is more reliable to use (current->flags & (PF_KTHREAD|PF_USER_WORKERi)) than to rely on current->mm being NULL. That is because some kernel tasks (io_uring helpers) may have a mm field.

---
 kernel/events/callchain.c | 6 +++---
 kernel/events/core.c      | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)
kernel/events/callchain.c +3 -3
index cd0e3fc..5982d18 100644
@@ -246,10 +246,10 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
 	if (user && !crosstask) {
 		if (!user_mode(regs)) {
-			if  (current->mm)
-				regs = task_pt_regs(current);
-			else
+			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
 				regs = NULL;
+			else
+				regs = task_pt_regs(current);
 		}
 
 		if (regs) {
kernel/events/core.c +2 -2
index bade8e0..f880cec 100644
@@ -7446,7 +7446,7 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
 	if (user_mode(regs)) {
 		regs_user->abi = perf_reg_abi(current);
 		regs_user->regs = regs;
-	} else if (!(current->flags & PF_KTHREAD)) {
+	} else if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
 		perf_get_regs_user(regs_user, regs);
 	} else {
 		regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;
@@ -8086,7 +8086,7 @@ static u64 perf_virt_to_phys(u64 virt)
 		 * Try IRQ-safe get_user_page_fast_only first.
 		 * If failed, leave phys_addr as 0.
 		 */
-		if (current->mm != NULL) {
+		if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
 			struct page *p;
 
 			pagefault_disable();
#7 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

In reply to Steven Rostedt

Hi Steven,

Quoted 48 lines · Steven Rostedt

From: Steven Rostedt <[email protected]>

To determine if a task is a kernel thread or not, it is more reliable to use (current->flags & (PF_KTHREAD|PF_USER_WORKERi)) than to rely on current->mm being NULL. That is because some kernel tasks (io_uring helpers) may have a mm field.

---
 kernel/events/callchain.c | 6 +++---
 kernel/events/core.c      | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)
kernel/events/callchain.c +3 -3
index cd0e3fc7ed05..5982d18f169b 100644
@@ -246,10 +246,10 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
 	if (user && !crosstask) {
 		if (!user_mode(regs)) {
-			if  (current->mm)
-				regs = task_pt_regs(current);
-			else
+			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
 				regs = NULL;
+			else
+				regs = task_pt_regs(current);
 		}
 
 		if (regs) {
kernel/events/core.c +1 -1
index bade8e0fced7..f880cec0c980 100644
@@ -7446,7 +7446,7 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
 	if (user_mode(regs)) {
 		regs_user->abi = perf_reg_abi(current);
 		regs_user->regs = regs;
-	} else if (!(current->flags & PF_KTHREAD)) {
+	} else if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
 		perf_get_regs_user(regs_user, regs);
 	} else {
 		regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;

kernel/events/core.c static u64 perf_virt_to_phys(u64 virt) · line 8089

Quoted 5 lines
kernel/events/core.c +1 -1
@@ -8086,7 +8086,7 @@ static u64 perf_virt_to_phys(u64 virt)
 		 * Try IRQ-safe get_user_page_fast_only first.
 		 * If failed, leave phys_addr as 0.
 		 */
-		if (current->mm != NULL) {
+		if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {

Subsequent code uses current->mm. This triggers a crash when running a page table stress test. See below for details. I have seen the crash in 6.12.57 and 6.18-rc5.

Guenter

---

[  120.334908] BUG: kernel NULL pointer dereference, address: 0000000000000078
[  120.341901] #PF: supervisor read access in kernel mode
[  120.347055] #PF: error_code(0x0000) - not-present page
[  120.352208] PGD 0 P4D 0
[  120.354750] Oops: Oops: 0000 [#1] SMP NOPTI
[  120.358946] CPU: 36 UID: 0 PID: 14127 Comm: page_table_stre Tainted: G S         O        6.18.0-smp-DEV #2 NONE
[  120.369242] Tainted: [S]=CPU_OUT_OF_SPEC, [O]=OOT_MODULE
[  120.374568] Hardware name: Google LLC Indus/Indus_QC_03, BIOS 30.116.4 08/29/2025
[  120.382075] RIP: 0010:gup_fast_fallback+0x150/0xb60
[  120.386977] Code: d0 c9 8b 48 89 84 24 a0 00 00 00 48 8b 80 30 05 00 00 0f b6 0d 0d 6b 1a 01 49 89 f8 49 d3 e8 41 81 e0 ff 01 00 00 41 c1 e0
 03 <4c> 03 40 78 4c 8d 5b ff 44 89 c8 83 e0 01 48 8d 04 45 05 00 00 00
[  120.405809] RSP: 0018:ffffa32be5f9b7a0 EFLAGS: 00010006
[  120.411051] RAX: 0000000000000000 RBX: 00007f0f57dfd000 RCX: 0000000000000027
[  120.418210] RDX: 0000000000000046 RSI: 0000000000000001 RDI: 00007f0f57dfc000
[  120.425368] RBP: 0000000000000000 R08: 00000000000007f0 R09: 0000000000100002
[  120.432526] R10: ffffa32be5f9b8c8 R11: 0000000000000000 R12: 00007f0f57dfc6c0
[  120.439683] R13: ffff99b44dd7c800 R14: 00000000fffffff2 R15: 00000000000800c3
[  120.446842] FS:  0000000000000000(0000) GS:ffff9a127357b000(0000) knlGS:0000000000000000
[  120.454956] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  120.460721] CR2: 0000000000000078 CR3: 000000512d03e006 CR4: 00000000007706f0
[  120.467879] PKRU: 55555554
[  120.470592] Call Trace:
[  120.473045]  <TASK>
[  120.475152]  perf_prepare_sample+0x77b/0x910
[  120.479445]  perf_event_output+0x35/0x100
[  120.483467]  intel_pmu_drain_pebs_nhm+0x570/0x750
[  120.488198]  intel_pmu_pebs_sched_task+0x74/0x80
[  120.492839]  ? __put_partials+0xd6/0x130
[  120.496775]  ? __mt_destroy+0x3f/0x80
[  120.500451]  ? put_cpu_partial+0x9b/0xc0
[  120.504384]  ? __slab_free+0x249/0x320
[  120.508144]  ? refill_obj_stock+0x120/0x1a0
[  120.512341]  ? __mt_destroy+0x3f/0x80
[  120.516013]  ? kfree+0x2ca/0x390
[  120.519254]  ? update_load_avg+0x1c8/0x7d0
[  120.523364]  ? update_entity_lag+0xf6/0x110
[  120.527560]  intel_pmu_sched_task+0x1d/0x30
[  120.531755]  perf_pmu_sched_task+0xf2/0x1a0
[  120.535952]  __perf_event_task_sched_out+0x3f/0x1f0
[  120.540844]  ? pick_next_task_fair+0x3e/0x2a0
[  120.545214]  __schedule+0xad0/0xb40
[  120.548715]  do_task_dead+0x48/0xa0
[  120.552215]  do_exit+0x734/0x920
[  120.555463]  ? do_exit+0x9/0x920
[  120.558699]  do_group_exit+0x85/0x90
[  120.562284]  __x64_sys_exit_group+0x17/0x20
[  120.566478]  x64_sys_call+0x21f7/0x2200
[  120.570327]  do_syscall_64+0x6f/0x940
[  120.574001]  ? clear_bhb_loop+0x50/0xa0
[  120.577849]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  120.582915] RIP: 0033:0x7f0f5a0d2c48
[  120.586501] Code: Unable to access opcode bytes at 0x7f0f5a0d2c1e.
[  120.592700] RSP: 002b:00007f0f57dfcec8 EFLAGS: 00000207 ORIG_RAX: 00000000000000e7
[  120.600294] RAX: ffffffffffffffda RBX: 00007f0f57dfd700 RCX: 00007f0f5a0d2c48
[  120.607452] RDX: 00007f0f57dfd660 RSI: 0000000000000000 RDI: 0000000000000000
[  120.614607] RBP: 00007f0f57dfcef0 R08: 00007f0f57dfd700 R09: 00007f0f57dfd700
[  120.621765] R10: 00007f0f5a17a6c0 R11: 0000000000000207 R12: 00007f0f57dfd9d0
[  120.628923] R13: 00007ffc64840aa6 R14: 00007f0f57dfdd1c R15: 00007f0f57dfcfc0
[  120.636081]  </TASK>
[  120.638272] Modules linked in: vfat fat i2c_mux_pca954x i2c_mux spidev cdc_acm xhci_pci xhci_hcd gq(O) sha3_generic
[  120.649976] gsmi: Log Shutdown Reason 0x03
[  120.654086] CR2: 0000000000000078
[  120.657409] ---[ end trace 0000000000000000 ]---

Stack decode:

[  120.334908] BUG: kernel NULL pointer dereference, address: 0000000000000078
[  120.341901] #PF: supervisor read access in kernel mode
[  120.347055] #PF: error_code(0x0000) - not-present page
[  120.352208] PGD 0 P4D 0
[  120.354750] Oops: Oops: 0000 [#1] SMP NOPTI
[  120.358946] CPU: 36 UID: 0 PID: 14127 Comm: page_table_stre Tainted: G S         O        6.18.0-smp-DEV #2 NONE
[  120.369242] Tainted: [S]=CPU_OUT_OF_SPEC, [O]=OOT_MODULE
[  120.374568] Hardware name: Google LLC Indus/Indus_QC_03, BIOS 30.116.4 08/29/2025
[  120.382075] RIP: 0010:gup_fast_fallback (./include/linux/pgtable.h:140 mm/gup.c:3795 mm/gup.c:3899 mm/gup.c:3946)
[ 120.386977] Code: d0 c9 8b 48 89 84 24 a0 00 00 00 48 8b 80 30 05 00 00 0f b6 0d 0d 6b 1a 01 49 89 f8 49 d3 e8 41 81 e0 ff 01 00 00 41 c1 e0
03 <4c> 03 40 78 4c 8d 5b ff 44 89 c8 83 e0 01 48 8d 04 45 05 00 00 00
All code
========
   0:   d0 c9                   ror    $1,%cl
   2:   8b 48 89                mov    -0x77(%rax),%ecx
   5:   84 24 a0                test   %ah,(%rax,%riz,4)
   8:   00 00                   add    %al,(%rax)
   a:   00 48 8b                add    %cl,-0x75(%rax)
   d:   80 30 05                xorb   $0x5,(%rax)
  10:   00 00                   add    %al,(%rax)
  12:   0f b6 0d 0d 6b 1a 01    movzbl 0x11a6b0d(%rip),%ecx        # 0x11a6b26
  19:   49 89 f8                mov    %rdi,%r8
  1c:   49 d3 e8                shr    %cl,%r8
  1f:   41 81 e0 ff 01 00 00    and    $0x1ff,%r8d
  26:   41 c1 e0 03             shl    $0x3,%r8d
  2a:*  4c 03 40 78             add    0x78(%rax),%r8           <-- trapping instruction
  2e:   4c 8d 5b ff             lea    -0x1(%rbx),%r11
  32:   44 89 c8                mov    %r9d,%eax
  35:   83 e0 01                and    $0x1,%eax
  38:   48 8d 04 45 05 00 00    lea    0x5(,%rax,2),%rax
  3f:   00

Code starting with the faulting instruction
===========================================

   0:   4c 03 40 78             add    0x78(%rax),%r8
   4:   4c 8d 5b ff             lea    -0x1(%rbx),%r11
   8:   44 89 c8                mov    %r9d,%eax
   b:   83 e0 01                and    $0x1,%eax
   e:   48 8d 04 45 05 00 00    lea    0x5(,%rax,2),%rax
  15:   00
[  120.405809] RSP: 0018:ffffa32be5f9b7a0 EFLAGS: 00010006
[  120.411051] RAX: 0000000000000000 RBX: 00007f0f57dfd000 RCX: 0000000000000027
[  120.418210] RDX: 0000000000000046 RSI: 0000000000000001 RDI: 00007f0f57dfc000
[  120.425368] RBP: 0000000000000000 R08: 00000000000007f0 R09: 0000000000100002
[  120.432526] R10: ffffa32be5f9b8c8 R11: 0000000000000000 R12: 00007f0f57dfc6c0
[  120.439683] R13: ffff99b44dd7c800 R14: 00000000fffffff2 R15: 00000000000800c3
[  120.446842] FS:  0000000000000000(0000) GS:ffff9a127357b000(0000) knlGS:0000000000000000
[  120.454956] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  120.460721] CR2: 0000000000000078 CR3: 000000512d03e006 CR4: 00000000007706f0
[  120.467879] PKRU: 55555554
[  120.470592] Call Trace:
[  120.473045]  <TASK>
[  120.475152] perf_prepare_sample (kernel/events/core.c:7490 kernel/events/core.c:8302)
[  120.479445] perf_event_output (kernel/events/core.c:8389 kernel/events/core.c:8426)
[  120.483467] intel_pmu_drain_pebs_nhm (arch/x86/events/intel/ds.c:? arch/x86/events/intel/ds.c:2182 arch/x86/events/intel/ds.c:2372)
[  120.488198] intel_pmu_pebs_sched_task (arch/x86/events/intel/ds.c:939 arch/x86/events/intel/ds.c:1248)
[  120.492839] ? __put_partials (mm/slub.c:3195)
[  120.496775] ? __mt_destroy (lib/maple_tree.c:? lib/maple_tree.c:6883)
[  120.500451] ? put_cpu_partial (mm/slub.c:3278)
[  120.504384] ? __slab_free (mm/slub.c:4521)
[  120.508144] ? refill_obj_stock (./include/linux/percpu-refcount.h:335 ./include/linux/percpu-refcount.h:351 ./include/linux/memcontrol.h:988
 mm/memcontrol.c:3732)
[  120.512341] ? __mt_destroy (lib/maple_tree.c:? lib/maple_tree.c:6883)
[  120.516013] ? kfree (mm/slab.h:681 mm/slub.c:4649 mm/slub.c:4797)
[  120.519254] ? update_load_avg (kernel/sched/fair.c:5376 kernel/sched/fair.c:5601 kernel/sched/fair.c:5720)
[  120.523364] ? update_entity_lag (kernel/sched/fair.c:?)
[  120.527560] intel_pmu_sched_task (arch/x86/events/intel/core.c:5231)
[  120.531755] perf_pmu_sched_task (kernel/events/core.c:1219 kernel/events/core.c:1231 kernel/events/core.c:3739 kernel/events/core.c:3755)
[  120.535952] __perf_event_task_sched_out (kernel/events/core.c:3776)
[  120.540844] ? pick_next_task_fair (kernel/sched/sched.h:4660 kernel/sched/sched.h:4666 kernel/sched/fair.c:9593 kernel/sched/fair.c:15504)
[  120.545214] __schedule (kernel/sched/core.c:7405 kernel/sched/core.c:8080)
[  120.548715] do_task_dead (??:?)
[  120.552215] do_exit (./include/linux/list.h:364 kernel/exit.c:810 kernel/exit.c:1030)
[  120.555463] ? do_exit (kernel/exit.c:934)
[  120.558699] do_group_exit (kernel/exit.c:1161)
[  120.562284] __x64_sys_exit_group (kernel/exit.c:1172)
[  120.566478] x64_sys_call (arch/x86/entry/syscall_64.c:32)
[  120.570327] do_syscall_64 (arch/x86/entry/common.c:57 arch/x86/entry/common.c:100)
[  120.574001] ? clear_bhb_loop (arch/x86/entry/entry_64.S:1598)
#8 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

Quoted 1 line · On Wed, 12 Nov 2025 19:11:15 -0800 Guenter Roeck <[email protected]> wrote:

Hi Steven,

Hi Guenter,

Somehow this got filed away in my archive without me seeing it.

Quoted 51 lines · Steven Rostedt
Quoted 49 lines

From: Steven Rostedt <[email protected]>

To determine if a task is a kernel thread or not, it is more reliable to use (current->flags & (PF_KTHREAD|PF_USER_WORKERi)) than to rely on current->mm being NULL. That is because some kernel tasks (io_uring helpers) may have a mm field.

---
 kernel/events/callchain.c | 6 +++---
 kernel/events/core.c      | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)
kernel/events/callchain.c +3 -3
index cd0e3fc7ed05..5982d18f169b 100644
@@ -246,10 +246,10 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
 	if (user && !crosstask) {
 		if (!user_mode(regs)) {
-			if  (current->mm)
-				regs = task_pt_regs(current);
-			else
+			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
 				regs = NULL;
+			else
+				regs = task_pt_regs(current);
 		}
 
 		if (regs) {
kernel/events/core.c +1 -1
index bade8e0fced7..f880cec0c980 100644
@@ -7446,7 +7446,7 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
 	if (user_mode(regs)) {
 		regs_user->abi = perf_reg_abi(current);
 		regs_user->regs = regs;
-	} else if (!(current->flags & PF_KTHREAD)) {
+	} else if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
 		perf_get_regs_user(regs_user, regs);
 	} else {
 		regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;
@@ -8086,7 +8086,7 @@ static u64 perf_virt_to_phys(u64 virt)
 		 * Try IRQ-safe get_user_page_fast_only first.
Quoted 8 lines

kernel/events/core.c static u64 perf_virt_to_phys(u64 virt) · line 8089

Quoted 4 lines
kernel/events/core.c +1 -1
 		 * If failed, leave phys_addr as 0.
 		 */
-		if (current->mm != NULL) {
+		if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {  

Subsequent code uses current->mm. This triggers a crash when running a page table stress test. See below for details. I have seen the crash in 6.12.57 and 6.18-rc5.

Hmm, that should not happen. But obvious it is. Can you add this:

kernel/events/callchain.c +2 -0
index 1f6589578703..ff201098e5e5 100644
@@ -248,6 +248,8 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 		if (!user_mode(regs)) {
 			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
 				goto exit_put;
+			if (WARN_ONCE(!current->mm, "Bad flags %x", current->flags))
+				goto exit_put;
 			regs = task_pt_regs(current);
 		}
 

I'd like to see what current->flags are when ->mm is NULL.

Thanks!

-- Steve

#9 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

In reply to Guenter Roeck
Quoted 34 lines · On Wed, 12 Nov 2025 19:11:15 -0800 Guenter Roeck <[email protected]> wrote:
[  120.334908] BUG: kernel NULL pointer dereference, address: 0000000000000078
[  120.341901] #PF: supervisor read access in kernel mode
[  120.347055] #PF: error_code(0x0000) - not-present page
[  120.352208] PGD 0 P4D 0
[  120.354750] Oops: Oops: 0000 [#1] SMP NOPTI
[  120.358946] CPU: 36 UID: 0 PID: 14127 Comm: page_table_stre Tainted: G S         O        6.18.0-smp-DEV #2 NONE
[  120.369242] Tainted: [S]=CPU_OUT_OF_SPEC, [O]=OOT_MODULE
[  120.374568] Hardware name: Google LLC Indus/Indus_QC_03, BIOS 30.116.4 08/29/2025
[  120.382075] RIP: 0010:gup_fast_fallback+0x150/0xb60
[  120.386977] Code: d0 c9 8b 48 89 84 24 a0 00 00 00 48 8b 80 30 05 00 00 0f b6 0d 0d 6b 1a 01 49 89 f8 49 d3 e8 41 81 e0 ff 01 00 00 41 c1 e0
 03 <4c> 03 40 78 4c 8d 5b ff 44 89 c8 83 e0 01 48 8d 04 45 05 00 00 00
[  120.405809] RSP: 0018:ffffa32be5f9b7a0 EFLAGS: 00010006
[  120.411051] RAX: 0000000000000000 RBX: 00007f0f57dfd000 RCX: 0000000000000027
[  120.418210] RDX: 0000000000000046 RSI: 0000000000000001 RDI: 00007f0f57dfc000
[  120.425368] RBP: 0000000000000000 R08: 00000000000007f0 R09: 0000000000100002
[  120.432526] R10: ffffa32be5f9b8c8 R11: 0000000000000000 R12: 00007f0f57dfc6c0
[  120.439683] R13: ffff99b44dd7c800 R14: 00000000fffffff2 R15: 00000000000800c3
[  120.446842] FS:  0000000000000000(0000) GS:ffff9a127357b000(0000) knlGS:0000000000000000
[  120.454956] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  120.460721] CR2: 0000000000000078 CR3: 000000512d03e006 CR4: 00000000007706f0
[  120.467879] PKRU: 55555554
[  120.470592] Call Trace:
[  120.473045]  <TASK>
[  120.475152]  perf_prepare_sample+0x77b/0x910
[  120.479445]  perf_event_output+0x35/0x100
[  120.483467]  intel_pmu_drain_pebs_nhm+0x570/0x750
[  120.488198]  intel_pmu_pebs_sched_task+0x74/0x80
[  120.492839]  ? __put_partials+0xd6/0x130
[  120.496775]  ? __mt_destroy+0x3f/0x80
[  120.500451]  ? put_cpu_partial+0x9b/0xc0
[  120.504384]  ? __slab_free+0x249/0x320
[  120.508144]  ? refill_obj_stock+0x120/0x1a0
[  120.512341]  ? __mt_destroy+0x3f/0x80
[  120.516013]  ? kfree+0x2ca/0x390
Quoted 8 lines
[  120.519254]  ? update_load_avg+0x1c8/0x7d0
[  120.523364]  ? update_entity_lag+0xf6/0x110
[  120.527560]  intel_pmu_sched_task+0x1d/0x30
[  120.531755]  perf_pmu_sched_task+0xf2/0x1a0
[  120.535952]  __perf_event_task_sched_out+0x3f/0x1f0
[  120.540844]  ? pick_next_task_fair+0x3e/0x2a0
[  120.545214]  __schedule+0xad0/0xb40
[  120.548715]  do_task_dead+0x48/0xa0

Ah, this is called at do_task_dead()

I guess we need to also test for !current->mm because the flags set for an exiting task is done when we can still do callchains. Thus, the only way to know if it is safe to do a callchain when a task is exiting is via task->mm and not task->flags :-/

-- Steve

Quoted 21 lines
[  120.552215]  do_exit+0x734/0x920
[  120.555463]  ? do_exit+0x9/0x920
[  120.558699]  do_group_exit+0x85/0x90
[  120.562284]  __x64_sys_exit_group+0x17/0x20
[  120.566478]  x64_sys_call+0x21f7/0x2200
[  120.570327]  do_syscall_64+0x6f/0x940
[  120.574001]  ? clear_bhb_loop+0x50/0xa0
[  120.577849]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  120.582915] RIP: 0033:0x7f0f5a0d2c48
[  120.586501] Code: Unable to access opcode bytes at 0x7f0f5a0d2c1e.
[  120.592700] RSP: 002b:00007f0f57dfcec8 EFLAGS: 00000207 ORIG_RAX: 00000000000000e7
[  120.600294] RAX: ffffffffffffffda RBX: 00007f0f57dfd700 RCX: 00007f0f5a0d2c48
[  120.607452] RDX: 00007f0f57dfd660 RSI: 0000000000000000 RDI: 0000000000000000
[  120.614607] RBP: 00007f0f57dfcef0 R08: 00007f0f57dfd700 R09: 00007f0f57dfd700
[  120.621765] R10: 00007f0f5a17a6c0 R11: 0000000000000207 R12: 00007f0f57dfd9d0
[  120.628923] R13: 00007ffc64840aa6 R14: 00007f0f57dfdd1c R15: 00007f0f57dfcfc0
[  120.636081]  </TASK>
[  120.638272] Modules linked in: vfat fat i2c_mux_pca954x i2c_mux spidev cdc_acm xhci_pci xhci_hcd gq(O) sha3_generic
[  120.649976] gsmi: Log Shutdown Reason 0x03
[  120.654086] CR2: 0000000000000078
[  120.657409] ---[ end trace 0000000000000000 ]---
maintainer
#10 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

Quoted 4 lines · On Mon, 26 Jan 2026 12:05:53 -0500 Steven Rostedt <[email protected]> wrote:

I guess we need to also test for !current->mm because the flags set for an exiting task is done when we can still do callchains. Thus, the only way to know if it is safe to do a callchain when a task is exiting is via task->mm and not task->flags :-/

Can you test this patch?

-- Steve

kernel/events/callchain.c +8 -1
index 1f6589578703..c82d61d73bd8 100644
@@ -246,7 +246,14 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
 	if (user && !crosstask) {
 		if (!user_mode(regs)) {
-			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
+			/*
+			 * Testing current->mm is not enough as some kernel threads
+			 * may have one set. But testing the flags is not enough
+			 * either as this can be called after a user task
+			 * frees its mm just before it exits.
+			 */
+			if (!current->mm ||
+			    (current->flags & (PF_KTHREAD | PF_USER_WORKER)))
 				goto exit_put;
 			regs = task_pt_regs(current);
 		}
#11 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

Quoted 10 lines · Steven Rostedt
Quoted 4 lines · On Mon, 26 Jan 2026 12:05:53 -0500 Steven Rostedt <[email protected]> wrote:

I guess we need to also test for !current->mm because the flags set for an exiting task is done when we can still do callchains. Thus, the only way to know if it is safe to do a callchain when a task is exiting is via task->mm and not task->flags :-/

Can you test this patch?

Sure, though we had dropped the offending patch from the LTS backport, so that will take a bit.

Guenter

Quoted 22 lines

-- Steve

kernel/events/callchain.c +8 -1
index 1f6589578703..c82d61d73bd8 100644
@@ -246,7 +246,14 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
  	if (user && !crosstask) {
  		if (!user_mode(regs)) {
-			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
+			/*
+			 * Testing current->mm is not enough as some kernel threads
+			 * may have one set. But testing the flags is not enough
+			 * either as this can be called after a user task
+			 * frees its mm just before it exits.
+			 */
+			if (!current->mm ||
+			    (current->flags & (PF_KTHREAD | PF_USER_WORKER)))
  				goto exit_put;
  			regs = task_pt_regs(current);
  		}
#12 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

In reply to Steven Rostedt
Quoted 10 lines · Steven Rostedt
Quoted 4 lines · On Mon, 26 Jan 2026 12:05:53 -0500 Steven Rostedt <[email protected]> wrote:

I guess we need to also test for !current->mm because the flags set for an exiting task is done when we can still do callchains. Thus, the only way to know if it is safe to do a callchain when a task is exiting is via task->mm and not task->flags :-/

Can you test this patch?

Still crashing, though not as often and with a slightly different backtrace. I added the backtrace to the bug report @ Google.

Guenter

Quoted 22 lines

-- Steve

kernel/events/callchain.c +8 -1
index 1f6589578703..c82d61d73bd8 100644
@@ -246,7 +246,14 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
  	if (user && !crosstask) {
  		if (!user_mode(regs)) {
-			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
+			/*
+			 * Testing current->mm is not enough as some kernel threads
+			 * may have one set. But testing the flags is not enough
+			 * either as this can be called after a user task
+			 * frees its mm just before it exits.
+			 */
+			if (!current->mm ||
+			    (current->flags & (PF_KTHREAD | PF_USER_WORKER)))
  				goto exit_put;
  			regs = task_pt_regs(current);
  		}
maintainer
#13 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

Quoted 2 lines · On Mon, 26 Jan 2026 17:32:35 -0800 Guenter Roeck <[email protected]> wrote:

Still crashing, though not as often and with a slightly different backtrace. I added the backtrace to the bug report @ Google.

I figured there would be other locations. A while ago I had a patch to wrap the checks in a "is_user_thread()" helper function[1], but Ingo had issues with it. It seems now it's biting us in the butt and let's see if it would help now. I modified it slightly.

[1] https://lore.kernel.org/linux-trace-kernel/[email protected]/

If the below fixes it, I'll resend it, but now as a real bug fix.

-- Steve

include/linux/sched.h +5 -0
index da0133524d08..5f00b5ed0f3b 100644
@@ -1776,6 +1776,11 @@ static __always_inline bool is_percpu_thread(void)
 		(current->nr_cpus_allowed  == 1);
 }
 
+static __always_inline bool is_user_task(struct task_struct *task)
+{
+	return task->mm && !(task->flags & (PF_KTHREAD | PF_USER_WORKER));
+}
+
 /* Per-process atomic flags. */
 #define PFA_NO_NEW_PRIVS		0	/* May not gain new privileges. */
 #define PFA_SPREAD_PAGE			1	/* Spread page cache over cpuset */
kernel/events/callchain.c +1 -1
index 1f6589578703..9d24b6e0c91f 100644
@@ -246,7 +246,7 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
 	if (user && !crosstask) {
 		if (!user_mode(regs)) {
-			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
+			if (!is_user_task(current))
 				goto exit_put;
 			regs = task_pt_regs(current);
 		}
kernel/events/core.c +3 -3
index a0fa488bce84..8cca80094624 100644
@@ -7460,7 +7460,7 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
 	if (user_mode(regs)) {
 		regs_user->abi = perf_reg_abi(current);
 		regs_user->regs = regs;
-	} else if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
+	} else if (is_user_task(current)) {
 		perf_get_regs_user(regs_user, regs);
 	} else {
 		regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;
@@ -8100,7 +8100,7 @@ static u64 perf_virt_to_phys(u64 virt)
 		 * Try IRQ-safe get_user_page_fast_only first.
 		 * If failed, leave phys_addr as 0.
 		 */
-		if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
+		if (is_user_task(current)) {
 			struct page *p;
 
 			pagefault_disable();
@@ -8215,7 +8215,7 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
 {
 	bool kernel = !event->attr.exclude_callchain_kernel;
 	bool user   = !event->attr.exclude_callchain_user &&
-		!(current->flags & (PF_KTHREAD | PF_USER_WORKER));
+		is_user_task(current);
 	/* Disallow cross-task user callchains. */
 	bool crosstask = event->ctx->task && event->ctx->task != current;
 	bool defer_user = IS_ENABLED(CONFIG_UNWIND_USER) && user &&
#14 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

Hi Steven,

Quoted 6 lines · Steven Rostedt
Quoted 2 lines · On Mon, 26 Jan 2026 17:32:35 -0800 Guenter Roeck <[email protected]> wrote:

Still crashing, though not as often and with a slightly different backtrace. I added the backtrace to the bug report @ Google.

Quoted 9 lines

I figured there would be other locations. A while ago I had a patch to wrap the checks in a "is_user_thread()" helper function[1], but Ingo had issues with it. It seems now it's biting us in the butt and let's see if it would help now. I modified it slightly.

[1] https://lore.kernel.org/linux-trace-kernel/[email protected]/

If the below fixes it, I'll resend it, but now as a real bug fix.

Trying. So far I can no longer reproduce the problem with the patch below applied. Obviously that doesn't mean that the problem is fixed, only that I can no longer reproduce it. I'll keep trying with different platforms.

Guenter

Quoted 62 lines

-- Steve

include/linux/sched.h +5 -0
index da0133524d08..5f00b5ed0f3b 100644
@@ -1776,6 +1776,11 @@ static __always_inline bool is_percpu_thread(void)
  		(current->nr_cpus_allowed  == 1);
  }
 
+static __always_inline bool is_user_task(struct task_struct *task)
+{
+	return task->mm && !(task->flags & (PF_KTHREAD | PF_USER_WORKER));
+}
+
  /* Per-process atomic flags. */
  #define PFA_NO_NEW_PRIVS		0	/* May not gain new privileges. */
  #define PFA_SPREAD_PAGE			1	/* Spread page cache over cpuset */
kernel/events/callchain.c +1 -1
index 1f6589578703..9d24b6e0c91f 100644
@@ -246,7 +246,7 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
  	if (user && !crosstask) {
  		if (!user_mode(regs)) {
-			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
+			if (!is_user_task(current))
  				goto exit_put;
  			regs = task_pt_regs(current);
  		}
kernel/events/core.c +3 -3
index a0fa488bce84..8cca80094624 100644
@@ -7460,7 +7460,7 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
  	if (user_mode(regs)) {
  		regs_user->abi = perf_reg_abi(current);
  		regs_user->regs = regs;
-	} else if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
+	} else if (is_user_task(current)) {
  		perf_get_regs_user(regs_user, regs);
  	} else {
  		regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;
@@ -8100,7 +8100,7 @@ static u64 perf_virt_to_phys(u64 virt)
  		 * Try IRQ-safe get_user_page_fast_only first.
  		 * If failed, leave phys_addr as 0.
  		 */
-		if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
+		if (is_user_task(current)) {
  			struct page *p;
 
  			pagefault_disable();
@@ -8215,7 +8215,7 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
  {
  	bool kernel = !event->attr.exclude_callchain_kernel;
  	bool user   = !event->attr.exclude_callchain_user &&
-		!(current->flags & (PF_KTHREAD | PF_USER_WORKER));
+		is_user_task(current);
  	/* Disallow cross-task user callchains. */
  	bool crosstask = event->ctx->task && event->ctx->task != current;
  	bool defer_user = IS_ENABLED(CONFIG_UNWIND_USER) && user &&
maintainer
#15 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

Quoted 3 lines · On Tue, 27 Jan 2026 11:07:05 -0800 Guenter Roeck <[email protected]> wrote:

Trying. So far I can no longer reproduce the problem with the patch below applied. Obviously that doesn't mean that the problem is fixed, only that I can no longer reproduce it. I'll keep trying with different platforms.

Well, the revert of the patch just replaces the flags test with a test for current->mm being NULL. This patch simply makes all those locations test both the flags and for current->mm being NULL.

I can't see how it doesn't fix it.

Anyway, I'll start making this into a legitimate patch.

Thanks,

-- Steve

Tested-by
#16 lore raw

Re: [RESEND][PATCH 3/5] perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

In reply to Steven Rostedt
Quoted 6 lines · Steven Rostedt
Quoted 2 lines · On Mon, 26 Jan 2026 17:32:35 -0800 Guenter Roeck <[email protected]> wrote:

Still crashing, though not as often and with a slightly different backtrace. I added the backtrace to the bug report @ Google.

Quoted 9 lines

I figured there would be other locations. A while ago I had a patch to wrap the checks in a "is_user_thread()" helper function[1], but Ingo had issues with it. It seems now it's biting us in the butt and let's see if it would help now. I modified it slightly.

[1] https://lore.kernel.org/linux-trace-kernel/[email protected]/

If the below fixes it, I'll resend it, but now as a real bug fix.

All attempts to reproduce the problem after applying the patch below failed, so feel free to go ahead and add

to the patch.

Thanks,
Guenter

Quoted 62 lines

-- Steve

include/linux/sched.h +5 -0
index da0133524d08..5f00b5ed0f3b 100644
@@ -1776,6 +1776,11 @@ static __always_inline bool is_percpu_thread(void)
  		(current->nr_cpus_allowed  == 1);
  }
 
+static __always_inline bool is_user_task(struct task_struct *task)
+{
+	return task->mm && !(task->flags & (PF_KTHREAD | PF_USER_WORKER));
+}
+
  /* Per-process atomic flags. */
  #define PFA_NO_NEW_PRIVS		0	/* May not gain new privileges. */
  #define PFA_SPREAD_PAGE			1	/* Spread page cache over cpuset */
kernel/events/callchain.c +1 -1
index 1f6589578703..9d24b6e0c91f 100644
@@ -246,7 +246,7 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 
  	if (user && !crosstask) {
  		if (!user_mode(regs)) {
-			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
+			if (!is_user_task(current))
  				goto exit_put;
  			regs = task_pt_regs(current);
  		}
kernel/events/core.c +3 -3
index a0fa488bce84..8cca80094624 100644
@@ -7460,7 +7460,7 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
  	if (user_mode(regs)) {
  		regs_user->abi = perf_reg_abi(current);
  		regs_user->regs = regs;
-	} else if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
+	} else if (is_user_task(current)) {
  		perf_get_regs_user(regs_user, regs);
  	} else {
  		regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;
@@ -8100,7 +8100,7 @@ static u64 perf_virt_to_phys(u64 virt)
  		 * Try IRQ-safe get_user_page_fast_only first.
  		 * If failed, leave phys_addr as 0.
  		 */
-		if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
+		if (is_user_task(current)) {
  			struct page *p;
 
  			pagefault_disable();
@@ -8215,7 +8215,7 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
  {
  	bool kernel = !event->attr.exclude_callchain_kernel;
  	bool user   = !event->attr.exclude_callchain_user &&
-		!(current->flags & (PF_KTHREAD | PF_USER_WORKER));
+		is_user_task(current);
  	/* Disallow cross-task user callchains. */
  	bool crosstask = event->ctx->task && event->ctx->task != current;
  	bool defer_user = IS_ENABLED(CONFIG_UNWIND_USER) && user &&
#17 lore raw

PATCH RESEND 4/5 perf: Simplify get_perf_callchain() user logic

In reply to Steven Rostedt

From: Josh Poimboeuf <[email protected]>

Simplify the get_perf_callchain() user logic a bit. task_pt_regs() should never be NULL.

---
 kernel/events/callchain.c | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)
kernel/events/callchain.c +8 -10
index 5982d18f169b..808c0d7a31fa 100644
@@ -247,21 +247,19 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 	if (user && !crosstask) {
 		if (!user_mode(regs)) {
 			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
-				regs = NULL;
-			else
-				regs = task_pt_regs(current);
+				goto exit_put;
+			regs = task_pt_regs(current);
 		}
 
-		if (regs) {
-			if (add_mark)
-				perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);
+		if (add_mark)
+			perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);
 
-			start_entry_idx = entry->nr;
-			perf_callchain_user(&ctx, regs);
-			fixup_uretprobe_trampoline_entries(entry, start_entry_idx);
-		}
+		start_entry_idx = entry->nr;
+		perf_callchain_user(&ctx, regs);
+		fixup_uretprobe_trampoline_entries(entry, start_entry_idx);
 	}
 
+exit_put:
 	put_callchain_entry(rctx);
 
 	return entry;
Signature · 1 line
2.50.1
merge bot Applied
#18 lore raw

[tip: perf/core] perf: Simplify get_perf_callchain() user logic

The following commit has been merged into the perf/core branch of tip:

The following commit has been merged into the perf/core branch of tip:

Commit-ID:     d77e3319e31098a6cb97b7ce4e71ba676e327fd7
Gitweb:        https://git.kernel.org/tip/d77e3319e31098a6cb97b7ce4e71ba676e327fd7
Author:        Josh Poimboeuf <[email protected]>
AuthorDate:    Wed, 20 Aug 2025 14:03:42 -04:00
Committer:     Peter Zijlstra <[email protected]>
CommitterDate: Tue, 26 Aug 2025 09:51:13 +02:00

perf: Simplify get_perf_callchain() user logic

Simplify the get_perf_callchain() user logic a bit. task_pt_regs() should never be NULL.

---
 kernel/events/callchain.c | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)
kernel/events/callchain.c +8 -10
index 5982d18..808c0d7 100644
@@ -247,21 +247,19 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
 	if (user && !crosstask) {
 		if (!user_mode(regs)) {
 			if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
-				regs = NULL;
-			else
-				regs = task_pt_regs(current);
+				goto exit_put;
+			regs = task_pt_regs(current);
 		}
 
-		if (regs) {
-			if (add_mark)
-				perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);
+		if (add_mark)
+			perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);
 
-			start_entry_idx = entry->nr;
-			perf_callchain_user(&ctx, regs);
-			fixup_uretprobe_trampoline_entries(entry, start_entry_idx);
-		}
+		start_entry_idx = entry->nr;
+		perf_callchain_user(&ctx, regs);
+		fixup_uretprobe_trampoline_entries(entry, start_entry_idx);
 	}
 
+exit_put:
 	put_callchain_entry(rctx);
 
 	return entry;
#19 lore raw

PATCH RESEND 5/5 perf: Skip user unwind if the task is a kernel thread

In reply to Steven Rostedt

From: Josh Poimboeuf <[email protected]>

If the task is not a user thread, there's no user stack to unwind.

---
 kernel/events/core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/events/core.c +2 -1
index f880cec0c980..28de3baff792 100644
@@ -8198,7 +8198,8 @@ struct perf_callchain_entry *
 perf_callchain(struct perf_event *event, struct pt_regs *regs)
 {
 	bool kernel = !event->attr.exclude_callchain_kernel;
-	bool user   = !event->attr.exclude_callchain_user;
+	bool user   = !event->attr.exclude_callchain_user &&
+		!(current->flags & (PF_KTHREAD | PF_USER_WORKER));
 	/* Disallow cross-task user callchains. */
 	bool crosstask = event->ctx->task && event->ctx->task != current;
 	const u32 max_stack = event->attr.sample_max_stack;
Signature · 1 line
2.50.1
merge bot Applied
#20 lore raw

[tip: perf/core] perf: Skip user unwind if the task is a kernel thread

The following commit has been merged into the perf/core branch of tip:

The following commit has been merged into the perf/core branch of tip:

Commit-ID:     16ed389227651330879e17bd83d43bd234006722
Gitweb:        https://git.kernel.org/tip/16ed389227651330879e17bd83d43bd234006722
Author:        Josh Poimboeuf <[email protected]>
AuthorDate:    Wed, 20 Aug 2025 14:03:43 -04:00
Committer:     Peter Zijlstra <[email protected]>
CommitterDate: Tue, 26 Aug 2025 09:51:13 +02:00

perf: Skip user unwind if the task is a kernel thread

If the task is not a user thread, there's no user stack to unwind.

---
 kernel/events/core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/events/core.c +2 -1
index f880cec..28de3ba 100644
@@ -8198,7 +8198,8 @@ struct perf_callchain_entry *
 perf_callchain(struct perf_event *event, struct pt_regs *regs)
 {
 	bool kernel = !event->attr.exclude_callchain_kernel;
-	bool user   = !event->attr.exclude_callchain_user;
+	bool user   = !event->attr.exclude_callchain_user &&
+		!(current->flags & (PF_KTHREAD | PF_USER_WORKER));
 	/* Disallow cross-task user callchains. */
 	bool crosstask = event->ctx->task && event->ctx->task != current;
 	const u32 max_stack = event->attr.sample_max_stack;
maintainer
#21 lore raw
In reply to Steven Rostedt
Quoted 16 lines · Steven Rostedt

This is a resend from: https://lore.kernel.org/linux-trace-kernel/[email protected]/

It cleanly applied on top of the latest tip/perf/core:
448f97fba901 ("perf: Convert mmap() refcounts to refcount_t")

The ongoing work to support sframes[1] in the kernel required a few clean ups to the perf kernel side. Instead of having these changes in the ongoing work, there's no reason they cannot be applied now.

These are just clean ups to the perf code to help simplify the updates that will be made later.

[1] https://lore.kernel.org/linux-trace-kernel/[email protected]

Quoted 8 lines

Josh Poimboeuf (4):
perf: Remove get_perf_callchain() init_nr argument
perf: Have get_perf_callchain() return NULL if crosstask and user are set
perf: Simplify get_perf_callchain() user logic
perf: Skip user unwind if the task is a kernel thread

Steven Rostedt (1):

      perf: Use current->flags & PF_KTHREAD|PF_USER_WORKER instead of current->mm == NULL

Thanks!