Linux Trace Kernel
 help / color / mirror / Atom feed
* [PATCHv5 08/13] selftests/bpf: Emit nop,nop10 instructions combo for x86_64 arch
From: Jiri Olsa @ 2026-07-01 11:13 UTC (permalink / raw)
  To: Oleg Nesterov, Peter Zijlstra, Ingo Molnar, Masami Hiramatsu,
	Andrii Nakryiko
  Cc: Jakub Sitnicki, bpf, linux-trace-kernel
In-Reply-To: <20260701111337.53943-1-jolsa@kernel.org>

Syncing latest usdt.h change [1].

Now that we have nop10 optimization support in kernel, let's emit
nop,nop10 for usdt probe. We leave it up to the library to use
desirable nop instruction.

[1] TBD
Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/testing/selftests/bpf/usdt.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/usdt.h b/tools/testing/selftests/bpf/usdt.h
index c71e21df38b3..75687f50f4e2 100644
--- a/tools/testing/selftests/bpf/usdt.h
+++ b/tools/testing/selftests/bpf/usdt.h
@@ -313,7 +313,7 @@ struct usdt_sema { volatile unsigned short active; };
 #if defined(__ia64__) || defined(__s390__) || defined(__s390x__)
 #define USDT_NOP			nop 0
 #elif defined(__x86_64__)
-#define USDT_NOP                       .byte 0x90, 0x0f, 0x1f, 0x44, 0x00, 0x0 /* nop, nop5 */
+#define USDT_NOP                       .byte 0x90, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 /* nop, nop10 */
 #else
 #define USDT_NOP			nop
 #endif
-- 
2.54.0


^ permalink raw reply related

* [PATCHv5 09/13] selftests/bpf: Change uprobe syscall tests to use nop10
From: Jiri Olsa @ 2026-07-01 11:13 UTC (permalink / raw)
  To: Oleg Nesterov, Peter Zijlstra, Ingo Molnar, Masami Hiramatsu,
	Andrii Nakryiko
  Cc: Jakub Sitnicki, bpf, linux-trace-kernel
In-Reply-To: <20260701111337.53943-1-jolsa@kernel.org>

Optimized uprobes are now on top of 10-bytes nop instructions,
reflect that in existing tests.

Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 .../selftests/bpf/benchs/bench_trigger.c      |  2 +-
 .../selftests/bpf/prog_tests/uprobe_syscall.c | 30 +++++++++++--------
 tools/testing/selftests/bpf/prog_tests/usdt.c | 25 +++++++++-------
 tools/testing/selftests/bpf/usdt_2.c          |  2 +-
 4 files changed, 34 insertions(+), 25 deletions(-)

diff --git a/tools/testing/selftests/bpf/benchs/bench_trigger.c b/tools/testing/selftests/bpf/benchs/bench_trigger.c
index 2f22ec61667b..a60b8173cdc4 100644
--- a/tools/testing/selftests/bpf/benchs/bench_trigger.c
+++ b/tools/testing/selftests/bpf/benchs/bench_trigger.c
@@ -398,7 +398,7 @@ static void *uprobe_producer_ret(void *input)
 #ifdef __x86_64__
 __nocf_check __weak void uprobe_target_nop5(void)
 {
-	asm volatile (".byte 0x0f, 0x1f, 0x44, 0x00, 0x00");
+	asm volatile (".byte 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00");
 }
 
 static void *uprobe_producer_nop5(void *input)
diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
index c944136252c6..ba50071ace40 100644
--- a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
+++ b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
@@ -17,7 +17,7 @@
 #include "uprobe_syscall_executed.skel.h"
 #include "bpf/libbpf_internal.h"
 
-#define USDT_NOP .byte 0x0f, 0x1f, 0x44, 0x00, 0x00
+#define USDT_NOP .byte 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
 #include "usdt.h"
 
 #pragma GCC diagnostic ignored "-Wattributes"
@@ -26,7 +26,7 @@ __attribute__((aligned(16)))
 __nocf_check __weak __naked unsigned long uprobe_regs_trigger(void)
 {
 	asm volatile (
-		".byte 0x0f, 0x1f, 0x44, 0x00, 0x00\n" /* nop5 */
+		".byte 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00\n" /* nop10 */
 		"movq $0xdeadbeef, %rax\n"
 		"ret\n"
 	);
@@ -345,9 +345,9 @@ static void test_uretprobe_syscall_call(void)
 __attribute__((aligned(16)))
 __nocf_check __weak __naked void uprobe_test(void)
 {
-	asm volatile ("					\n"
-		".byte 0x0f, 0x1f, 0x44, 0x00, 0x00	\n"
-		"ret					\n"
+	asm volatile (
+		".byte 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00\n" /* nop10 */
+		"ret\n"
 	);
 }
 
@@ -388,14 +388,15 @@ static int find_uprobes_trampoline(void *tramp_addr)
 	return ret;
 }
 
-static unsigned char nop5[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
+static unsigned char nop10[10]  = { 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static unsigned char lea_rsp[5] = { 0x48, 0x8d, 0x64, 0x24, 0x80 };
 
-static void *find_nop5(void *fn)
+static void *find_nop10(void *fn)
 {
 	int i;
 
-	for (i = 0; i < 10; i++) {
-		if (!memcmp(nop5, fn + i, 5))
+	for (i = 0; i < 128; i++) {
+		if (!memcmp(nop10, fn + i, 10))
 			return fn + i;
 	}
 	return NULL;
@@ -420,7 +421,8 @@ static void *check_attach(struct uprobe_syscall_executed *skel, trigger_t trigge
 	ASSERT_EQ(skel->bss->executed, executed, "executed");
 
 	/* .. and check the trampoline is as expected. */
-	call = (struct __arch_relative_insn *) addr;
+	ASSERT_OK(memcmp(addr, lea_rsp, 5), "lea_rsp");
+	call = (struct __arch_relative_insn *)(addr + 5);
 	tramp = (void *) (call + 1) + call->raddr;
 	ASSERT_EQ(call->op, 0xe8, "call");
 	ASSERT_OK(find_uprobes_trampoline(tramp), "uprobes_trampoline");
@@ -430,9 +432,11 @@ static void *check_attach(struct uprobe_syscall_executed *skel, trigger_t trigge
 
 static void check_detach(void *addr, void *tramp)
 {
+	static const unsigned char nop10_prefix[] = { 0x66, 0x2e, 0x0f, 0x1f, 0x84 };
+
 	/* [uprobes_trampoline] stays after detach */
 	ASSERT_OK(find_uprobes_trampoline(tramp), "uprobes_trampoline");
-	ASSERT_OK(memcmp(addr, nop5, 5), "nop5");
+	ASSERT_OK(memcmp(addr, nop10_prefix, 5), "nop10_prefix");
 }
 
 static void check(struct uprobe_syscall_executed *skel, struct bpf_link *link,
@@ -568,8 +572,8 @@ static void test_uprobe_usdt(void)
 	void *addr;
 
 	errno = 0;
-	addr = find_nop5(usdt_test);
-	if (!ASSERT_OK_PTR(addr, "find_nop5"))
+	addr = find_nop10(usdt_test);
+	if (!ASSERT_OK_PTR(addr, "find_nop10"))
 		return;
 
 	skel = uprobe_syscall_executed__open_and_load();
diff --git a/tools/testing/selftests/bpf/prog_tests/usdt.c b/tools/testing/selftests/bpf/prog_tests/usdt.c
index 69759b27794d..fda3a298ccfc 100644
--- a/tools/testing/selftests/bpf/prog_tests/usdt.c
+++ b/tools/testing/selftests/bpf/prog_tests/usdt.c
@@ -252,7 +252,7 @@ extern void usdt_1(void);
 extern void usdt_2(void);
 
 static unsigned char nop1[1] = { 0x90 };
-static unsigned char nop1_nop5_combo[6] = { 0x90, 0x0f, 0x1f, 0x44, 0x00, 0x00 };
+static unsigned char nop1_nop10_combo[11] = { 0x90, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
 
 static void *find_instr(void *fn, unsigned char *instr, size_t cnt)
 {
@@ -271,17 +271,17 @@ static void subtest_optimized_attach(void)
 	__u8 *addr_1, *addr_2;
 
 	/* usdt_1 USDT probe has single nop instruction */
-	addr_1 = find_instr(usdt_1, nop1_nop5_combo, 6);
-	if (!ASSERT_NULL(addr_1, "usdt_1_find_nop1_nop5_combo"))
+	addr_1 = find_instr(usdt_1, nop1_nop10_combo, 11);
+	if (!ASSERT_NULL(addr_1, "usdt_1_find_nop1_nop10_combo"))
 		return;
 
 	addr_1 = find_instr(usdt_1, nop1, 1);
 	if (!ASSERT_OK_PTR(addr_1, "usdt_1_find_nop1"))
 		return;
 
-	/* usdt_2 USDT probe has nop,nop5 instructions combo */
-	addr_2 = find_instr(usdt_2, nop1_nop5_combo, 6);
-	if (!ASSERT_OK_PTR(addr_2, "usdt_2_find_nop1_nop5_combo"))
+	/* usdt_2 USDT probe has nop,nop10 instructions combo */
+	addr_2 = find_instr(usdt_2, nop1_nop10_combo, 11);
+	if (!ASSERT_OK_PTR(addr_2, "usdt_2_find_nop1_nop10_combo"))
 		return;
 
 	skel = test_usdt__open_and_load();
@@ -309,12 +309,12 @@ static void subtest_optimized_attach(void)
 
 	bpf_link__destroy(skel->links.usdt_executed);
 
-	/* we expect the nop5 ip */
+	/* we expect the nop10 ip */
 	skel->bss->expected_ip = (unsigned long) addr_2 + 1;
 
 	/*
 	 * Attach program on top of usdt_2 which is probe defined on top
-	 * of nop1,nop5 combo, so the probe gets optimized on top of nop5.
+	 * of nop1,nop10 combo, so the probe gets optimized on top of nop10.
 	 */
 	skel->links.usdt_executed = bpf_program__attach_usdt(skel->progs.usdt_executed,
 						     0 /*self*/, "/proc/self/exe",
@@ -328,8 +328,13 @@ static void subtest_optimized_attach(void)
 	/* nop stays on addr_2 address */
 	ASSERT_EQ(*addr_2, 0x90, "nop");
 
-	/* call is on addr_2 + 1 address */
-	ASSERT_EQ(*(addr_2 + 1), 0xe8, "call");
+	/*
+	 * lea -0x80(%rsp), %rsp
+	 * call ...
+	 */
+	static unsigned char expected[] = { 0x48, 0x8d, 0x64, 0x24, 0x80, 0xe8 };
+
+	ASSERT_MEMEQ(addr_2 + 1, expected, sizeof(expected), "lea_and_call");
 	ASSERT_EQ(skel->bss->executed, 4, "executed");
 
 cleanup:
diff --git a/tools/testing/selftests/bpf/usdt_2.c b/tools/testing/selftests/bpf/usdt_2.c
index 789883aaca4c..b359b389f6c0 100644
--- a/tools/testing/selftests/bpf/usdt_2.c
+++ b/tools/testing/selftests/bpf/usdt_2.c
@@ -3,7 +3,7 @@
 #if defined(__x86_64__)
 
 /*
- * Include usdt.h with default nop,nop5 instructions combo.
+ * Include usdt.h with default nop,nop10 instructions combo.
  */
 #include "usdt.h"
 
-- 
2.54.0


^ permalink raw reply related

* [PATCHv5 10/13] selftests/bpf: Change uprobe/usdt trigger bench code to use nop10
From: Jiri Olsa @ 2026-07-01 11:13 UTC (permalink / raw)
  To: Oleg Nesterov, Peter Zijlstra, Ingo Molnar, Masami Hiramatsu,
	Andrii Nakryiko
  Cc: Jakub Sitnicki, bpf, linux-trace-kernel
In-Reply-To: <20260701111337.53943-1-jolsa@kernel.org>

Changing uprobe/usdt trigger bench code to use nop10 instead
of nop5. Also changing run_bench_uprobes.sh to use nop10 triggers.

Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/testing/selftests/bpf/bench.c           | 20 +++++------
 .../selftests/bpf/benchs/bench_trigger.c      | 36 +++++++++----------
 .../selftests/bpf/benchs/run_bench_uprobes.sh |  2 +-
 3 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/tools/testing/selftests/bpf/bench.c b/tools/testing/selftests/bpf/bench.c
index 3d9d2cd7764b..c4a3a6b3eb83 100644
--- a/tools/testing/selftests/bpf/bench.c
+++ b/tools/testing/selftests/bpf/bench.c
@@ -539,12 +539,12 @@ extern const struct bench bench_trig_uretprobe_multi_push;
 extern const struct bench bench_trig_uprobe_multi_ret;
 extern const struct bench bench_trig_uretprobe_multi_ret;
 #ifdef __x86_64__
-extern const struct bench bench_trig_uprobe_nop5;
-extern const struct bench bench_trig_uretprobe_nop5;
-extern const struct bench bench_trig_uprobe_multi_nop5;
-extern const struct bench bench_trig_uretprobe_multi_nop5;
+extern const struct bench bench_trig_uprobe_nop10;
+extern const struct bench bench_trig_uretprobe_nop10;
+extern const struct bench bench_trig_uprobe_multi_nop10;
+extern const struct bench bench_trig_uretprobe_multi_nop10;
 extern const struct bench bench_trig_usdt_nop;
-extern const struct bench bench_trig_usdt_nop5;
+extern const struct bench bench_trig_usdt_nop10;
 #endif
 
 extern const struct bench bench_rb_libbpf;
@@ -622,12 +622,12 @@ static const struct bench *benchs[] = {
 	&bench_trig_uprobe_multi_ret,
 	&bench_trig_uretprobe_multi_ret,
 #ifdef __x86_64__
-	&bench_trig_uprobe_nop5,
-	&bench_trig_uretprobe_nop5,
-	&bench_trig_uprobe_multi_nop5,
-	&bench_trig_uretprobe_multi_nop5,
+	&bench_trig_uprobe_nop10,
+	&bench_trig_uretprobe_nop10,
+	&bench_trig_uprobe_multi_nop10,
+	&bench_trig_uretprobe_multi_nop10,
 	&bench_trig_usdt_nop,
-	&bench_trig_usdt_nop5,
+	&bench_trig_usdt_nop10,
 #endif
 	/* ringbuf/perfbuf benchmarks */
 	&bench_rb_libbpf,
diff --git a/tools/testing/selftests/bpf/benchs/bench_trigger.c b/tools/testing/selftests/bpf/benchs/bench_trigger.c
index a60b8173cdc4..61513efc167a 100644
--- a/tools/testing/selftests/bpf/benchs/bench_trigger.c
+++ b/tools/testing/selftests/bpf/benchs/bench_trigger.c
@@ -396,15 +396,15 @@ static void *uprobe_producer_ret(void *input)
 }
 
 #ifdef __x86_64__
-__nocf_check __weak void uprobe_target_nop5(void)
+__nocf_check __weak void uprobe_target_nop10(void)
 {
 	asm volatile (".byte 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00");
 }
 
-static void *uprobe_producer_nop5(void *input)
+static void *uprobe_producer_nop10(void *input)
 {
 	while (true)
-		uprobe_target_nop5();
+		uprobe_target_nop10();
 	return NULL;
 }
 
@@ -418,7 +418,7 @@ static void *uprobe_producer_usdt_nop(void *input)
 	return NULL;
 }
 
-static void *uprobe_producer_usdt_nop5(void *input)
+static void *uprobe_producer_usdt_nop10(void *input)
 {
 	while (true)
 		usdt_2();
@@ -542,24 +542,24 @@ static void uretprobe_multi_ret_setup(void)
 }
 
 #ifdef __x86_64__
-static void uprobe_nop5_setup(void)
+static void uprobe_nop10_setup(void)
 {
-	usetup(false, false /* !use_multi */, &uprobe_target_nop5);
+	usetup(false, false /* !use_multi */, &uprobe_target_nop10);
 }
 
-static void uretprobe_nop5_setup(void)
+static void uretprobe_nop10_setup(void)
 {
-	usetup(true, false /* !use_multi */, &uprobe_target_nop5);
+	usetup(true, false /* !use_multi */, &uprobe_target_nop10);
 }
 
-static void uprobe_multi_nop5_setup(void)
+static void uprobe_multi_nop10_setup(void)
 {
-	usetup(false, true /* use_multi */, &uprobe_target_nop5);
+	usetup(false, true /* use_multi */, &uprobe_target_nop10);
 }
 
-static void uretprobe_multi_nop5_setup(void)
+static void uretprobe_multi_nop10_setup(void)
 {
-	usetup(true, true /* use_multi */, &uprobe_target_nop5);
+	usetup(true, true /* use_multi */, &uprobe_target_nop10);
 }
 
 static void usdt_setup(const char *name)
@@ -598,7 +598,7 @@ static void usdt_nop_setup(void)
 	usdt_setup("usdt_1");
 }
 
-static void usdt_nop5_setup(void)
+static void usdt_nop10_setup(void)
 {
 	usdt_setup("usdt_2");
 }
@@ -665,10 +665,10 @@ BENCH_TRIG_USERMODE(uretprobe_multi_nop, nop, "uretprobe-multi-nop");
 BENCH_TRIG_USERMODE(uretprobe_multi_push, push, "uretprobe-multi-push");
 BENCH_TRIG_USERMODE(uretprobe_multi_ret, ret, "uretprobe-multi-ret");
 #ifdef __x86_64__
-BENCH_TRIG_USERMODE(uprobe_nop5, nop5, "uprobe-nop5");
-BENCH_TRIG_USERMODE(uretprobe_nop5, nop5, "uretprobe-nop5");
-BENCH_TRIG_USERMODE(uprobe_multi_nop5, nop5, "uprobe-multi-nop5");
-BENCH_TRIG_USERMODE(uretprobe_multi_nop5, nop5, "uretprobe-multi-nop5");
+BENCH_TRIG_USERMODE(uprobe_nop10, nop10, "uprobe-nop10");
+BENCH_TRIG_USERMODE(uretprobe_nop10, nop10, "uretprobe-nop10");
+BENCH_TRIG_USERMODE(uprobe_multi_nop10, nop10, "uprobe-multi-nop10");
+BENCH_TRIG_USERMODE(uretprobe_multi_nop10, nop10, "uretprobe-multi-nop10");
 BENCH_TRIG_USERMODE(usdt_nop, usdt_nop, "usdt-nop");
-BENCH_TRIG_USERMODE(usdt_nop5, usdt_nop5, "usdt-nop5");
+BENCH_TRIG_USERMODE(usdt_nop10, usdt_nop10, "usdt-nop10");
 #endif
diff --git a/tools/testing/selftests/bpf/benchs/run_bench_uprobes.sh b/tools/testing/selftests/bpf/benchs/run_bench_uprobes.sh
index 9ec59423b949..e490b337e960 100755
--- a/tools/testing/selftests/bpf/benchs/run_bench_uprobes.sh
+++ b/tools/testing/selftests/bpf/benchs/run_bench_uprobes.sh
@@ -2,7 +2,7 @@
 
 set -eufo pipefail
 
-for i in usermode-count syscall-count {uprobe,uretprobe}-{nop,push,ret,nop5} usdt-nop usdt-nop5
+for i in usermode-count syscall-count {uprobe,uretprobe}-{nop,push,ret,nop10} usdt-nop usdt-nop10
 do
 	summary=$(sudo ./bench -w2 -d5 -a trig-$i | tail -n1 | cut -d'(' -f1 | cut -d' ' -f3-)
 	printf "%-15s: %s\n" $i "$summary"
-- 
2.54.0


^ permalink raw reply related

* [PATCHv5 11/13] selftests/bpf: Add reattach tests for uprobe syscall
From: Jiri Olsa @ 2026-07-01 11:13 UTC (permalink / raw)
  To: Oleg Nesterov, Peter Zijlstra, Ingo Molnar, Masami Hiramatsu,
	Andrii Nakryiko
  Cc: bpf, linux-trace-kernel
In-Reply-To: <20260701111337.53943-1-jolsa@kernel.org>

Adding reattach tests for uprobe syscall tests to make sure
we can re-attach and optimize same uprobe multiple times.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 .../selftests/bpf/prog_tests/uprobe_syscall.c | 130 ++++++++++++++++--
 1 file changed, 120 insertions(+), 10 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
index ba50071ace40..7711018f8acd 100644
--- a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
+++ b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
@@ -404,6 +404,16 @@ static void *find_nop10(void *fn)
 
 typedef void (__attribute__((nocf_check)) *trigger_t)(void);
 
+static void check_attach_notrigger(struct uprobe_syscall_executed *skel,
+				   void *addr, int executed)
+{
+	unsigned char *op = addr;
+
+	/* Make sure bpf program was not executed. */
+	ASSERT_EQ(skel->bss->executed, executed, "executed");
+	ASSERT_EQ(*op, 0xcc, "int3");
+}
+
 static void *check_attach(struct uprobe_syscall_executed *skel, trigger_t trigger,
 			  void *addr, int executed)
 {
@@ -430,23 +440,26 @@ static void *check_attach(struct uprobe_syscall_executed *skel, trigger_t trigge
 	return tramp;
 }
 
-static void check_detach(void *addr, void *tramp)
+static bool check_detach(void *addr, void *tramp)
 {
 	static const unsigned char nop10_prefix[] = { 0x66, 0x2e, 0x0f, 0x1f, 0x84 };
+	bool ok = true;
 
 	/* [uprobes_trampoline] stays after detach */
-	ASSERT_OK(find_uprobes_trampoline(tramp), "uprobes_trampoline");
-	ASSERT_OK(memcmp(addr, nop10_prefix, 5), "nop10_prefix");
+	ok &= ASSERT_OK(find_uprobes_trampoline(tramp), "uprobes_trampoline");
+	ok &= ASSERT_OK(memcmp(addr, nop10_prefix, 5), "nop10_prefix");
+	return ok;
 }
 
-static void check(struct uprobe_syscall_executed *skel, struct bpf_link *link,
-		  trigger_t trigger, void *addr, int executed)
+static void *check(struct uprobe_syscall_executed *skel, struct bpf_link *link,
+		   trigger_t trigger, void *addr, int executed)
 {
 	void *tramp;
 
 	tramp = check_attach(skel, trigger, addr, executed);
 	bpf_link__destroy(link);
 	check_detach(addr, tramp);
+	return tramp;
 }
 
 static void test_uprobe_legacy(void)
@@ -457,6 +470,7 @@ static void test_uprobe_legacy(void)
 	);
 	struct bpf_link *link;
 	unsigned long offset;
+	void *tramp;
 
 	offset = get_uprobe_offset(&uprobe_test);
 	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
@@ -474,7 +488,30 @@ static void test_uprobe_legacy(void)
 	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_opts"))
 		goto cleanup;
 
-	check(skel, link, uprobe_test, uprobe_test, 2);
+	tramp = check(skel, link, uprobe_test, uprobe_test, 2);
+
+	/* reattach and detach without triggering optimization */
+	link = bpf_program__attach_uprobe_opts(skel->progs.test_uprobe,
+					       0, "/proc/self/exe", offset, NULL);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_opts"))
+		goto cleanup;
+
+	check_attach_notrigger(skel, uprobe_test, 2);
+
+	bpf_link__destroy(link);
+	if (!check_detach(uprobe_test, tramp))
+		goto cleanup;
+
+	uprobe_test();
+	ASSERT_EQ(skel->bss->executed, 2, "executed_no_probe");
+
+	/* reattach with triggering optimization */
+	link = bpf_program__attach_uprobe_opts(skel->progs.test_uprobe,
+				0, "/proc/self/exe", offset, NULL);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_opts"))
+		goto cleanup;
+
+	check(skel, link, uprobe_test, uprobe_test, 4);
 
 	/* uretprobe */
 	skel->bss->executed = 0;
@@ -496,6 +533,7 @@ static void test_uprobe_multi(void)
 	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
 	struct bpf_link *link;
 	unsigned long offset;
+	void *tramp;
 
 	offset = get_uprobe_offset(&uprobe_test);
 	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
@@ -516,7 +554,30 @@ static void test_uprobe_multi(void)
 	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
 		goto cleanup;
 
-	check(skel, link, uprobe_test, uprobe_test, 2);
+	tramp = check(skel, link, uprobe_test, uprobe_test, 2);
+
+	/* reattach and detach without triggering optimization */
+	link = bpf_program__attach_uprobe_multi(skel->progs.test_uprobe_multi,
+				0, "/proc/self/exe", NULL, &opts);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
+		goto cleanup;
+
+	check_attach_notrigger(skel, uprobe_test, 2);
+
+	bpf_link__destroy(link);
+	if (!check_detach(uprobe_test, tramp))
+		goto cleanup;
+
+	uprobe_test();
+	ASSERT_EQ(skel->bss->executed, 2, "executed_no_probe");
+
+	/* reattach with triggering optimization */
+	link = bpf_program__attach_uprobe_multi(skel->progs.test_uprobe_multi,
+				0, "/proc/self/exe", NULL, &opts);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
+		goto cleanup;
+
+	check(skel, link, uprobe_test, uprobe_test, 4);
 
 	/* uretprobe.multi */
 	skel->bss->executed = 0;
@@ -540,6 +601,7 @@ static void test_uprobe_session(void)
 	);
 	struct bpf_link *link;
 	unsigned long offset;
+	void *tramp;
 
 	offset = get_uprobe_offset(&uprobe_test);
 	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
@@ -559,7 +621,30 @@ static void test_uprobe_session(void)
 	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
 		goto cleanup;
 
-	check(skel, link, uprobe_test, uprobe_test, 4);
+	tramp = check(skel, link, uprobe_test, uprobe_test, 4);
+
+	/* reattach and detach without triggering optimization */
+	link = bpf_program__attach_uprobe_multi(skel->progs.test_uprobe_session,
+				0, "/proc/self/exe", NULL, &opts);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
+		goto cleanup;
+
+	check_attach_notrigger(skel, uprobe_test, 4);
+
+	bpf_link__destroy(link);
+	if (!check_detach(uprobe_test, tramp))
+		goto cleanup;
+
+	uprobe_test();
+	ASSERT_EQ(skel->bss->executed, 4, "executed_no_probe");
+
+	/* reattach with triggering optimization */
+	link = bpf_program__attach_uprobe_multi(skel->progs.test_uprobe_session,
+				0, "/proc/self/exe", NULL, &opts);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
+		goto cleanup;
+
+	check(skel, link, uprobe_test, uprobe_test, 8);
 
 cleanup:
 	uprobe_syscall_executed__destroy(skel);
@@ -569,7 +654,7 @@ static void test_uprobe_usdt(void)
 {
 	struct uprobe_syscall_executed *skel;
 	struct bpf_link *link;
-	void *addr;
+	void *addr, *tramp;
 
 	errno = 0;
 	addr = find_nop10(usdt_test);
@@ -588,7 +673,32 @@ static void test_uprobe_usdt(void)
 	if (!ASSERT_OK_PTR(link, "bpf_program__attach_usdt"))
 		goto cleanup;
 
-	check(skel, link, usdt_test, addr, 2);
+	tramp = check(skel, link, usdt_test, addr, 2);
+
+	/* reattach and detach without triggering optimization */
+	link = bpf_program__attach_usdt(skel->progs.test_usdt,
+				-1 /* all PIDs */, "/proc/self/exe",
+				"optimized_uprobe", "usdt", NULL);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_usdt"))
+		goto cleanup;
+
+	check_attach_notrigger(skel, addr, 2);
+
+	bpf_link__destroy(link);
+	if (!check_detach(addr, tramp))
+		goto cleanup;
+
+	usdt_test();
+	ASSERT_EQ(skel->bss->executed, 2, "executed_no_probe");
+
+	/* reattach with triggering optimization */
+	link = bpf_program__attach_usdt(skel->progs.test_usdt,
+				-1 /* all PIDs */, "/proc/self/exe",
+				"optimized_uprobe", "usdt", NULL);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_usdt"))
+		goto cleanup;
+
+	check(skel, link, usdt_test, addr, 4);
 
 cleanup:
 	uprobe_syscall_executed__destroy(skel);
-- 
2.54.0


^ permalink raw reply related

* [PATCHv5 12/13] selftests/bpf: Add tests for uprobe nop10 red zone clobbering
From: Jiri Olsa @ 2026-07-01 11:13 UTC (permalink / raw)
  To: Oleg Nesterov, Peter Zijlstra, Ingo Molnar, Masami Hiramatsu,
	Andrii Nakryiko
  Cc: Jakub Sitnicki, bpf, linux-trace-kernel
In-Reply-To: <20260701111337.53943-1-jolsa@kernel.org>

From: Andrii Nakryiko <andrii@kernel.org>

The uprobe nop5 optimization used to replace a 5-byte NOP with a 5-byte
CALL to a trampoline. The CALL pushes a return address onto the stack at
[rsp-8], clobbering whatever was stored there.

On x86-64, the red zone is the 128 bytes below rsp that user code may use
for temporary storage without adjusting rsp. Compilers can place USDT
argument operands there, generating specs like "8@-8(%rbp)" when rbp ==
rsp. With the CALL-based optimization, the return address overwrites that
argument before the BPF-side USDT argument fetch runs.

Add two tests for this case. The uprobe_syscall subtest stores known values
at -8(%rsp), -16(%rsp), and -24(%rsp), executes an optimized nop10 uprobe,
and verifies the red-zone data is still intact. The USDT subtest triggers a
probe in a function where the compiler places three USDT operands in the
red zone and verifies that all 10 optimized invocations deliver the expected
argument values to BPF.

On an unfixed kernel, the first hit goes through the INT3 path and later
hits use the optimized CALL path, so the red-zone checks fail after
optimization.

Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
[ updates to use nop10 ]
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 .../selftests/bpf/prog_tests/uprobe_syscall.c | 77 +++++++++++++++++++
 tools/testing/selftests/bpf/prog_tests/usdt.c | 49 ++++++++++++
 tools/testing/selftests/bpf/progs/test_usdt.c | 25 ++++++
 tools/testing/selftests/bpf/usdt_2.c          | 13 ++++
 4 files changed, 164 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
index 7711018f8acd..ff07e5df9a65 100644
--- a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
+++ b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
@@ -357,6 +357,50 @@ __nocf_check __weak void usdt_test(void)
 	USDT(optimized_uprobe, usdt);
 }
 
+/*
+ * Assembly-level red zone clobbering test. Stores known values in the
+ * red zone (below RSP), executes a nop10 (uprobe site), and checks that
+ * the values survived. Returns 0 if intact, 1 if clobbered.
+ *
+ * The nop5 optimization used CALL (which pushes a return address to
+ * [rsp-8]), the value at -8(%rsp) was overwritten. The nop10 optimization
+ * should escape that by moving stackpointer below the redzone before
+ * doing the CALL.
+ *
+ * Align the code at 64 bytes, to make sure nop10 is not on page boundary.
+ */
+__attribute__((aligned(64)))
+__nocf_check __weak __naked unsigned long uprobe_red_zone_test(void)
+{
+	asm volatile (
+		"movabs $0x1111111111111111, %%rax\n"
+		"movq   %%rax, -8(%%rsp)\n"
+		"movabs $0x2222222222222222, %%rax\n"
+		"movq   %%rax, -16(%%rsp)\n"
+		"movabs $0x3333333333333333, %%rax\n"
+		"movq   %%rax, -24(%%rsp)\n"
+
+		".byte 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00\n" /* nop10: uprobe site */
+
+		"movabs $0x1111111111111111, %%rax\n"
+		"cmpq   %%rax, -8(%%rsp)\n"
+		"jne    1f\n"
+		"movabs $0x2222222222222222, %%rax\n"
+		"cmpq   %%rax, -16(%%rsp)\n"
+		"jne    1f\n"
+		"movabs $0x3333333333333333, %%rax\n"
+		"cmpq   %%rax, -24(%%rsp)\n"
+		"jne    1f\n"
+
+		"xorl   %%eax, %%eax\n"
+		"retq\n"
+		"1:\n"
+		"movl   $1, %%eax\n"
+		"retq\n"
+		::: "rax", "memory"
+	);
+}
+
 static int find_uprobes_trampoline(void *tramp_addr)
 {
 	void *start, *end;
@@ -871,6 +915,37 @@ static void test_uprobe_race(void)
 #define __NR_uprobe 336
 #endif
 
+static void test_uprobe_red_zone(void)
+{
+	struct uprobe_syscall_executed *skel;
+	struct bpf_link *link;
+	void *nop10_addr;
+	size_t offset;
+	int i;
+
+	nop10_addr = find_nop10(uprobe_red_zone_test);
+	if (!ASSERT_NEQ(nop10_addr, NULL, "find_nop10"))
+		return;
+
+	skel = uprobe_syscall_executed__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "open_and_load"))
+		return;
+
+	offset = get_uprobe_offset(nop10_addr);
+	link = bpf_program__attach_uprobe_opts(skel->progs.test_uprobe,
+			0, "/proc/self/exe", offset, NULL);
+	if (!ASSERT_OK_PTR(link, "attach_uprobe"))
+		goto cleanup;
+
+	for (i = 0; i < 10; i++)
+		ASSERT_EQ(uprobe_red_zone_test(), 0, "red_zone_intact");
+
+	bpf_link__destroy(link);
+
+cleanup:
+	uprobe_syscall_executed__destroy(skel);
+}
+
 static void test_uprobe_error(void)
 {
 	long err = syscall(__NR_uprobe);
@@ -897,6 +972,8 @@ static void __test_uprobe_syscall(void)
 		test_uprobe_usdt();
 	if (test__start_subtest("uprobe_race"))
 		test_uprobe_race();
+	if (test__start_subtest("uprobe_red_zone"))
+		test_uprobe_red_zone();
 	if (test__start_subtest("uprobe_error"))
 		test_uprobe_error();
 	if (test__start_subtest("uprobe_regs_equal"))
diff --git a/tools/testing/selftests/bpf/prog_tests/usdt.c b/tools/testing/selftests/bpf/prog_tests/usdt.c
index fda3a298ccfc..8004c9568ffa 100644
--- a/tools/testing/selftests/bpf/prog_tests/usdt.c
+++ b/tools/testing/selftests/bpf/prog_tests/usdt.c
@@ -250,6 +250,7 @@ static void subtest_basic_usdt(bool optimized)
 #ifdef __x86_64__
 extern void usdt_1(void);
 extern void usdt_2(void);
+extern void usdt_red_zone_trigger(void);
 
 static unsigned char nop1[1] = { 0x90 };
 static unsigned char nop1_nop10_combo[11] = { 0x90, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
@@ -340,6 +341,52 @@ static void subtest_optimized_attach(void)
 cleanup:
 	test_usdt__destroy(skel);
 }
+
+/*
+ * Test that USDT arguments survive nop10 optimization in a function where
+ * the compiler places operands in the red zone.
+ *
+ * Signal handlers are prone to having the compiler place USDT argument
+ * operands in the red zone (below rsp).
+ *
+ * The nop5 optimization used CALL (which pushes a return address to
+ * [rsp-8]), the value at -8(%rsp) was overwritten. The nop10 optimization
+ * should escape that by moving stackpointer below the redzone before
+ * doing the CALL.
+ */
+static void subtest_optimized_red_zone(void)
+{
+	struct test_usdt *skel;
+	int i;
+
+	skel = test_usdt__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "open_and_load"))
+		return;
+
+	skel->bss->expected_arg[0] = 0xDEADBEEF;
+	skel->bss->expected_arg[1] = 0xCAFEBABE;
+	skel->bss->expected_arg[2] = 0xFEEDFACE;
+	skel->bss->expected_pid = getpid();
+
+	skel->links.usdt_check_arg = bpf_program__attach_usdt(
+		skel->progs.usdt_check_arg, 0, "/proc/self/exe",
+		"optimized_attach", "usdt_red_zone", NULL);
+	if (!ASSERT_OK_PTR(skel->links.usdt_check_arg, "attach_usdt_red_zone"))
+		goto cleanup;
+
+	for (i = 0; i < 10; i++)
+		usdt_red_zone_trigger();
+
+	ASSERT_EQ(skel->bss->arg_total, 10, "arg_total");
+	ASSERT_EQ(skel->bss->arg_bad, 0, "arg_bad");
+	ASSERT_EQ(skel->bss->arg_last[0], 0xDEADBEEF, "arg_last_1");
+	ASSERT_EQ(skel->bss->arg_last[1], 0xCAFEBABE, "arg_last_2");
+	ASSERT_EQ(skel->bss->arg_last[2], 0xFEEDFACE, "arg_last_3");
+
+cleanup:
+	test_usdt__destroy(skel);
+}
+
 #endif
 
 unsigned short test_usdt_100_semaphore SEC(".probes");
@@ -613,6 +660,8 @@ void test_usdt(void)
 		subtest_basic_usdt(true);
 	if (test__start_subtest("optimized_attach"))
 		subtest_optimized_attach();
+	if (test__start_subtest("optimized_red_zone"))
+		subtest_optimized_red_zone();
 #endif
 	if (test__start_subtest("multispec"))
 		subtest_multispec_usdt();
diff --git a/tools/testing/selftests/bpf/progs/test_usdt.c b/tools/testing/selftests/bpf/progs/test_usdt.c
index f00cb52874e0..0ee78fb050a1 100644
--- a/tools/testing/selftests/bpf/progs/test_usdt.c
+++ b/tools/testing/selftests/bpf/progs/test_usdt.c
@@ -149,5 +149,30 @@ int usdt_executed(struct pt_regs *ctx)
 		executed++;
 	return 0;
 }
+
+int arg_total;
+int arg_bad;
+long arg_last[3];
+long expected_arg[3];
+int expected_pid;
+
+SEC("usdt")
+int BPF_USDT(usdt_check_arg, long arg1, long arg2, long arg3)
+{
+	if (expected_pid != (bpf_get_current_pid_tgid() >> 32))
+		return 0;
+
+	__sync_fetch_and_add(&arg_total, 1);
+	arg_last[0] = arg1;
+	arg_last[1] = arg2;
+	arg_last[2] = arg3;
+
+	if (arg1 != expected_arg[0] ||
+	    arg2 != expected_arg[1] ||
+	    arg3 != expected_arg[2])
+		__sync_fetch_and_add(&arg_bad, 1);
+
+	return 0;
+}
 #endif
 char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/usdt_2.c b/tools/testing/selftests/bpf/usdt_2.c
index b359b389f6c0..5e38f8605b02 100644
--- a/tools/testing/selftests/bpf/usdt_2.c
+++ b/tools/testing/selftests/bpf/usdt_2.c
@@ -13,4 +13,17 @@ void usdt_2(void)
 	USDT(optimized_attach, usdt_2);
 }
 
+static volatile unsigned long usdt_red_zone_arg1 = 0xDEADBEEF;
+static volatile unsigned long usdt_red_zone_arg2 = 0xCAFEBABE;
+static volatile unsigned long usdt_red_zone_arg3 = 0xFEEDFACE;
+
+void __attribute__((noinline)) usdt_red_zone_trigger(void)
+{
+	unsigned long a1 = usdt_red_zone_arg1;
+	unsigned long a2 = usdt_red_zone_arg2;
+	unsigned long a3 = usdt_red_zone_arg3;
+
+	USDT(optimized_attach, usdt_red_zone, a1, a2, a3);
+}
+
 #endif
-- 
2.54.0


^ permalink raw reply related

* [PATCHv5 13/13] selftests/bpf: Add tests for forked/cloned optimized uprobes
From: Jiri Olsa @ 2026-07-01 11:13 UTC (permalink / raw)
  To: Oleg Nesterov, Peter Zijlstra, Ingo Molnar, Masami Hiramatsu,
	Andrii Nakryiko
  Cc: Jakub Sitnicki, bpf, linux-trace-kernel
In-Reply-To: <20260701111337.53943-1-jolsa@kernel.org>

Adding tests for forked/cloned optimized uprobes and make
sure the child can properly execute optimized probe for
both fork (dups mm) and clone with CLONE_VM.

Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 .../selftests/bpf/prog_tests/uprobe_syscall.c | 89 +++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
index ff07e5df9a65..eb067f029a9f 100644
--- a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
+++ b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
@@ -4,6 +4,8 @@
 
 #ifdef __x86_64__
 
+#define _GNU_SOURCE
+#include <sched.h>
 #include <unistd.h>
 #include <asm/ptrace.h>
 #include <linux/compiler.h>
@@ -13,6 +15,7 @@
 #include <sys/syscall.h>
 #include <sys/prctl.h>
 #include <asm/prctl.h>
+#include <stdnoreturn.h>
 #include "uprobe_syscall.skel.h"
 #include "uprobe_syscall_executed.skel.h"
 #include "bpf/libbpf_internal.h"
@@ -954,6 +957,88 @@ static void test_uprobe_error(void)
 	ASSERT_EQ(errno, EPROTO, "errno");
 }
 
+__attribute__((aligned(16)))
+__nocf_check __weak __naked void uprobe_fork_test(void)
+{
+	asm volatile (
+		".byte 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00\n" /* nop10 */
+		"ret\n"
+	);
+}
+
+static noreturn int child_func(void *arg)
+{
+	struct uprobe_syscall_executed *skel = arg;
+
+	/* Make sure the child's probe is still there and optimized.. */
+	if (memcmp(uprobe_fork_test, lea_rsp, sizeof(lea_rsp)))
+		_exit(1);
+
+	skel->bss->pid = getpid();
+
+	/* .. and it executes properly. */
+	uprobe_fork_test();
+
+	if (skel->bss->executed != 3)
+		_exit(2);
+
+	_exit(0);
+}
+
+static void test_uprobe_fork_optimized(bool clone_vm)
+{
+	struct uprobe_syscall_executed *skel = NULL;
+	struct bpf_link *link = NULL;
+	unsigned long offset;
+	int pid, status, err;
+	char stack[65535];
+
+	offset = get_uprobe_offset(&uprobe_fork_test);
+	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
+		return;
+
+	skel = uprobe_syscall_executed__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "open_and_load"))
+		goto cleanup;
+
+	link = bpf_program__attach_uprobe_opts(skel->progs.test_uprobe,
+				-1, "/proc/self/exe", offset, NULL);
+	if (!ASSERT_OK_PTR(link, "attach_uprobe"))
+		goto cleanup;
+
+	skel->bss->pid = getpid();
+
+	/* Trigger optimization of uprobe in uprobe_fork_test.  */
+	uprobe_fork_test();
+	uprobe_fork_test();
+
+	/* Make sure it got optimied. */
+	if (!ASSERT_OK(memcmp(uprobe_fork_test, lea_rsp, sizeof(lea_rsp)), "optimized"))
+		goto cleanup;
+
+	if (clone_vm) {
+		pid = clone(child_func, stack + sizeof(stack), CLONE_VM|SIGCHLD, skel);
+		if (!ASSERT_GT(pid, 0, "clone"))
+			goto cleanup;
+	} else {
+		pid = fork();
+		if (!ASSERT_GE(pid, 0, "fork"))
+			goto cleanup;
+		if (pid == 0)
+			child_func(skel);
+	}
+
+	/* Wait for the child and verify it exited properly with 0. */
+	err = waitpid(pid, &status, 0);
+	if (ASSERT_EQ(err, pid, "waitpid")) {
+		ASSERT_EQ(WIFEXITED(status), 1, "child_exited");
+		ASSERT_EQ(WEXITSTATUS(status), 0, "child_exit_code");
+	}
+
+cleanup:
+	uprobe_syscall_executed__destroy(skel);
+}
+
 static void __test_uprobe_syscall(void)
 {
 	if (test__start_subtest("uretprobe_regs_equal"))
@@ -974,6 +1059,10 @@ static void __test_uprobe_syscall(void)
 		test_uprobe_race();
 	if (test__start_subtest("uprobe_red_zone"))
 		test_uprobe_red_zone();
+	if (test__start_subtest("uprobe_optimized_fork"))
+		test_uprobe_fork_optimized(false);
+	if (test__start_subtest("uprobe_optimized_clone_vm"))
+		test_uprobe_fork_optimized(true);
 	if (test__start_subtest("uprobe_error"))
 		test_uprobe_error();
 	if (test__start_subtest("uprobe_regs_equal"))
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v8 25/46] KVM: guest_memfd: Enable INIT_SHARED on guest_memfd for x86 Coco VMs
From: Xiaoyao Li @ 2026-07-01 11:18 UTC (permalink / raw)
  To: ackerleytng, aik, andrew.jones, binbin.wu, brauner, chao.p.peng,
	david, jmattson, jthoughton, michael.roth, oupton, pankaj.gupta,
	qperret, rick.p.edgecombe, rientjes, shivankg, steven.price,
	tabba, willy, wyihan, yan.y.zhao, forkloop, pratyush,
	suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
	Sean Christopherson, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Vishal Annapurve, Andrew Morton, Chris Li,
	Kairui Song, Kemeng Shi, Nhat Pham, Barry Song, Axel Rasmussen,
	Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt,
	Kiryl Shutsemau, Baoquan He, Jason Gunthorpe, Vlastimil Babka
  Cc: kvm, linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
	linux-mm, linux-coco
In-Reply-To: <20260618-gmem-inplace-conversion-v8-25-9d2959357853@google.com>

On 6/19/2026 8:32 AM, Ackerley Tng via B4 Relay wrote:
> From: Sean Christopherson <seanjc@google.com>
> 
> Now that guest_memfd supports tracking private vs. shared within gmem
> itself, allow userspace to specify INIT_SHARED on a guest_memfd instance
> for x86 Confidential Computing (CoCo) VMs, so long as in-place conversion
> is enabled, i.e. when it's actually possible for a guest_memfd instance to
> contain shared memory.
> 
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> Reviewed-by: Fuad Tabba <tabba@google.com>
> Signed-off-by: Ackerley Tng <ackerleytng@google.com>

Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>

> ---
>   arch/x86/kvm/x86.c | 13 +++++++------
>   1 file changed, 7 insertions(+), 6 deletions(-)
> 
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 2fde594e86d72..57a543dadb851 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -14116,14 +14116,15 @@ bool kvm_arch_no_poll(struct kvm_vcpu *vcpu)
>   }
>   
>   #ifdef CONFIG_KVM_GUEST_MEMFD
> -/*
> - * KVM doesn't yet support initializing guest_memfd memory as shared for VMs
> - * with private memory (the private vs. shared tracking needs to be moved into
> - * guest_memfd).
> - */
>   bool kvm_arch_supports_gmem_init_shared(struct kvm *kvm)
>   {
> -	return !kvm_arch_has_private_mem(kvm);
> +	/*
> +	 * INIT_SHARED is supported if in-place conversion is enabled, or if
> +	 * the VM doesn't support private memory.  If the VM has private memory
> +	 * and in-place conversion is disabled, then guest_memfd can _only_ be
> +	 * used for private memory.
> +	 */
> +	return gmem_in_place_conversion || !kvm_arch_has_private_mem(kvm);
>   }
>   
>   #ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
> 


^ permalink raw reply

* Re: [PATCHv5 13/13] selftests/bpf: Add tests for forked/cloned optimized uprobes
From: bot+bpf-ci @ 2026-07-01 11:57 UTC (permalink / raw)
  To: jolsa, oleg, peterz, mingo, mhiramat, andrii
  Cc: jakub, bpf, linux-trace-kernel, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, clm, ihor.solodrai
In-Reply-To: <20260701111337.53943-14-jolsa@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 928 bytes --]

> diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
> index ff07e5df9a65..eb067f029a9f 100644
> --- a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c
> +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c

[ ... ]

> @@ -954,6 +957,88 @@ static void test_uprobe_error(void)
> +static void test_uprobe_fork_optimized(bool clone_vm)
> +{

[ ... ]

> +	/* Make sure it got optimied. */
> +	if (!ASSERT_OK(memcmp(uprobe_fork_test, lea_rsp, sizeof(lea_rsp)), "optimized"))

This isn't a bug, but should "optimied" in this comment read "optimized",
to match the spelling used elsewhere in the file?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/28514315910

^ permalink raw reply

* Re: [PATCHv5 12/13] selftests/bpf: Add tests for uprobe nop10 red zone clobbering
From: bot+bpf-ci @ 2026-07-01 11:57 UTC (permalink / raw)
  To: jolsa, oleg, peterz, mingo, mhiramat, andrii
  Cc: jakub, bpf, linux-trace-kernel, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, clm, ihor.solodrai
In-Reply-To: <20260701111337.53943-13-jolsa@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 1725 bytes --]

> diff --git a/tools/testing/selftests/bpf/prog_tests/usdt.c b/tools/testing/selftests/bpf/prog_tests/usdt.c
> index fda3a298ccfc..8004c9568ffa 100644
> --- a/tools/testing/selftests/bpf/prog_tests/usdt.c
> +++ b/tools/testing/selftests/bpf/prog_tests/usdt.c

[ ... ]

> @@ -340,6 +341,52 @@ static void subtest_optimized_attach(void)
>  cleanup:
>  	test_usdt__destroy(skel);
>  }
> +
> +/*
> + * Test that USDT arguments survive nop10 optimization in a function where
> + * the compiler places operands in the red zone.
> + *
> + * Signal handlers are prone to having the compiler place USDT argument
> + * operands in the red zone (below rsp).
> + *
> + * The nop5 optimization used CALL (which pushes a return address to
> + * [rsp-8]), the value at -8(%rsp) was overwritten. The nop10 optimization
> + * should escape that by moving stackpointer below the redzone before
> + * doing the CALL.
> + */
> +static void subtest_optimized_red_zone(void)

This isn't a bug, but the four-line "nop5 optimization used CALL ..."
explanation here reads nearly verbatim to the comment added above
uprobe_red_zone_test() in prog_tests/uprobe_syscall.c.

Since both tests probe the same mechanism, would a single copy (or a
shorter note pointing at the other) be preferable, so the two don't drift
apart?

One small wording note: "moving stackpointer below the redzone" uses
"stackpointer" and "redzone", while the rest of the change and the commit
message use "stack pointer" and "red zone".


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/28514315910

^ permalink raw reply

* Re: [PATCHv5 02/13] uprobes/x86: Remove struct uprobe_trampoline object
From: bot+bpf-ci @ 2026-07-01 11:57 UTC (permalink / raw)
  To: jolsa, oleg, peterz, mingo, mhiramat, andrii
  Cc: bpf, linux-trace-kernel, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai
In-Reply-To: <20260701111337.53943-3-jolsa@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 1630 bytes --]

> diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
> index 2be6707e3320..d2933cf77cd3 100644
> --- a/arch/x86/kernel/uprobes.c
> +++ b/arch/x86/kernel/uprobes.c

[ ... ]

> @@ -1111,21 +1051,19 @@ int set_orig_insn(struct arch_uprobe *auprobe, struct vm_area_struct *vma,
>  static int __arch_uprobe_optimize(struct arch_uprobe *auprobe, struct mm_struct *mm,
>  				  unsigned long vaddr)
>  {
> -	struct uprobe_trampoline *tramp;
> -	struct vm_area_struct *vma;
> -	bool new = false;
> -	int err = 0;
> +	struct pt_regs *regs = task_pt_regs(current);
> +	struct vm_area_struct *vma, *tramp;
> +	int ret;
>
> +	if (!user_64bit_mode(regs))
> +		return -EINVAL;
>  	vma = find_vma(mm, vaddr);
>  	if (!vma)
>  		return -EINVAL;
> -	tramp = get_uprobe_trampoline(vaddr, &new);
> -	if (!tramp)
> -		return -EINVAL;
> -	err = swbp_optimize(auprobe, vma, vaddr, tramp->vaddr);
> -	if (WARN_ON_ONCE(err) && new)
> -		destroy_uprobe_trampoline(tramp);
> -	return err;
> +	tramp = get_uprobe_trampoline(mm, vaddr);
> +	if (IS_ERR(tramp))
> +		return PTR_ERR(tramp);
> +	return WARN_ON_ONCE(swbp_optimize(auprobe, vma, vaddr, tramp->vm_start));
>  }

The variable ret is declared but never used in the refactored
__arch_uprobe_optimize(). The old code used err which was assigned and
returned, but the new code returns directly. Should ret be removed to
avoid a compiler warning?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/28514315910

^ permalink raw reply

* Re: [PATCHv5 05/13] uprobes/x86: Move optimized uprobe from nop5 to nop10
From: bot+bpf-ci @ 2026-07-01 11:57 UTC (permalink / raw)
  To: jolsa, oleg, peterz, mingo, mhiramat, andrii
  Cc: bpf, linux-trace-kernel, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai
In-Reply-To: <20260701111337.53943-6-jolsa@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 2132 bytes --]

> Note as explained in [2] we need to use following nop10:
>        PF1   PF2   ESC   NOPL  MOD   SIB   DISP32
> NOP10: 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 -- cs nopw 0x00000000(%rax,%rax,1)
>
> which means we need to allow 0x2e prefix which maps to INAT_PFX_CS
> attribute in is_prefix_bad function.

This isn't a bug, but could the changelog be updated to describe the
approach actually taken? The patch bypasses uprobe_init_insn() (and
therefore is_prefix_bad()) entirely when can_optimize() is true for the
nop10. The is_prefix_bad() function itself remains unchanged and still
returns true for the CS prefix.

> Unoptimize path (int3_update_unoptimize):
>
>   1) Initial optimized state:
>       48 8d 64 24 80 e8 d0 d1 d2 d3
>      Same as 3) above.
>
>   2) Trap new entries before restoring the NOP bytes:
>       [cc] 8d 64 24 80 e8 d0 d1 d2 d3
>
>      From offset 0 this traps. A thread that had already executed the
>      LEA can still reach the intact CALL at offset 5.
>
>   3) Restore bytes 1..4 of the original NOP while keeping byte 0 trapped
>      and byte 5 as CALL.
>       cc [2e 0f 1f 84] e8 d0 d1 d2 d3
>
>      From offset 0 this still traps. Offset 5 is still the CALL for any
>      thread that was already past the first LEA byte.
>
>   4) Publish the first byte of the original NOP:
>       [66] 2e 0f 1f 84 e8 d0 d1 d2 d3
>
>      From offset 0 this is the restored 10-byte NOP; the CALL opcode and
>      displacement are now only NOP operands.  Offset 5 still decodes as
>      CALL for a thread that was already there.
>
>      Tthere is only a single target uprobe-trampoline for the given nop10
>      instruction address, so the CALL instruction will not be changed across
>      unoptimization/optimization cycles.

This isn't a bug, but there's a typo: "Tthere is only a single target"
should be "There is only a single target".


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/28514315910

^ permalink raw reply

* Re: [PATCH v3 2/9] rv: add generic uprobe infrastructure for RV monitors
From: Gabriele Monaco @ 2026-07-01 11:57 UTC (permalink / raw)
  To: Wen Yang; +Cc: linux-trace-kernel, linux-kernel
In-Reply-To: <d1e84072-7968-4a64-9497-09db83b38682@linux.dev>

On Wed, 2026-07-01 at 02:44 +0800, Wen Yang wrote:
> Thank you for the patient explanation.
> You are right, and v4 implements the embedded approach.
> struct rv_uprobe directly embeds struct uprobe_consumer:
> 
>    struct rv_uprobe {
>        struct uprobe_consumer  uc;
>        struct uprobe          *uprobe;
>        struct inode           *inode;
>        void                   *priv;
>        int (*handler)(...);
>        int (*ret_handler)(...);
>    };

Alright, great. Why are you still using double function pointers? Do you really
need rv_uprobe->handler instead of just using rv_uprobe->uc.handler ? That also
simplifies one function call down the road.

Thanks,
Gabriele

> rv_uprobe_free() is gone — no allocation means no explicit free.  After
> rv_uprobe_unregister() (or rv_uprobe_unregister_nosync() + 
> rv_uprobe_sync()), the caller frees the containing struct directly.  In 
> tlob:
> 
>    rv_uprobe_unregister_nosync(&b->start_probe);
>    rv_uprobe_unregister_nosync(&b->stop_probe);
>    rv_uprobe_sync();
>    kfree(b);   /* frees both embedded consumers */
> 
> This is the pattern from your sketch.
> 
> Regarding my earlier reply that argued for the separate allocation: I 
> was wrong. The key barrier is synchronize_rcu_tasks_trace() (called 
> first in uprobe_unregister_sync()), which waits for all 
> rcu_read_lock_trace() readers including handler_chain().  After it 
> returns, no cons_node.next read is in flight and embedding is safe.
> 
> We appreciate your thorough review. All of your comments have been 
> addressed in v4.
> We'll run local tests for one or two days, and then it will be sent out 
> shortly.
> 
> --
> Best wishes,
> Wen
> 


^ permalink raw reply

* Re: [PATCH 2/2] tracing: Keep pid and comm[] in the same structure
From: David Laight @ 2026-07-01 12:04 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
	linux-trace-kernel, Michal Koutný
In-Reply-To: <20260701063822.3af87520@gandalf.local.home>

On Wed, 1 Jul 2026 06:38:22 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Wed, 1 Jul 2026 11:04:07 +0100
> David Laight <david.laight.linux@gmail.com> wrote:
> 
> > 
> > I thought it was just used to do a pid->string lookup when you run 'cat trace'.
> > But then I found the code that lets userspace read the table....
> > I guess the latter is used by the userspace code that reads the raw trace buffer.  
> 
> Yes, trace-cmd uses it.
...
> > The userspace code could probably be given comm[] for all the running
> > processes and those that exited while tracing_on() set.
> > (I didn't see anything that would clear the table when the trace buffer
> > was cleared.)  
> 
> Well, that would break trace-cmd. As reading the raw buffers clears the
> trace, and trace-cmd reads the saved_cmdlines file *after* it reads the
> trace, as during the trace it gets populated.

So you'd need to clear it when tracing is enabled after the buffer is cleared.
Just a matter of getting the timing right.

If trace-cmd is currently given all 6000 entries (if you've run tracing for
long enough), then giving it all the active processes isn't going to be any
more of a problem.
So you could just save comm[] in the process exit path when the trace buffer
is non-empty, or better those started before tracing was last stopped.
You'd need to give trace-cmd the active ones first and delete the entry
from the cache because the pid might have been reused.

All just needs some coding, testing, and fixing of corner cases.

-- David 

> 
> -- Steve


^ permalink raw reply

* [RFC 0/2] arm64: kprobes: Fix single-step fault and reentry handling
From: Pu Hu @ 2026-07-01 12:14 UTC (permalink / raw)
  To: catalin.marinas@arm.com, will@kernel.org, naveen@kernel.org,
	davem@davemloft.net, mhiramat@kernel.org,
	yang@os.amperecomputing.com, Hongyan Xia, Jiazi Li,
	ada.coupriediaz@arm.com, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
  Cc: Pu Hu

From: hupu <hupu@transsion.com>

This series fixes two arm64 kprobes issues observed when running
simpleperf with preemptirq tracepoints and dwarf callchains while a
kprobe is active on a frequently executed kernel function.

The crash happens in the kprobe debug exception path. While a kprobe is
preparing or executing its XOL single-step instruction, perf/trace code
can run in the same window. That code may either take a fault of its own
or hit another kprobe.

Patch 1 makes kprobe_fault_handler() handle a fault in
KPROBE_HIT_SS/KPROBE_REENTER only when the faulting PC points at the
current kprobe's XOL instruction. Otherwise the fault is left to the
normal fault handling path.

Patch 2 allows a kprobe hit in KPROBE_HIT_SS to be handled as a
recoverable one-level reentry. Only a hit while already in
KPROBE_REENTER remains unrecoverable.

This follows the same logic as the existing x86 fixes:
  6381c24cd6d5 ("kprobes/x86: Fix page-fault handling logic")
  6a5022a56ac3 ("kprobes/x86: Allow to handle reentered kprobe on single-stepping")

Reproducer:

  simpleperf record -p <pid> -f 10000 \
    -e preemptirq:preempt_disable \
    -e preemptirq:preempt_enable \
    --duration 9 --call-graph dwarf \
    -o /data/local/tmp/perf.data

Before this series, the crash reproduced frequently. With both patches
applied, it was no longer reproduced in our testing.

hupu (2):
  arm64: kprobes: Do not handle non-XOL faults as kprobe faults
  arm64: kprobes: Allow reentering kprobes while single-stepping

 arch/arm64/kernel/probes/kprobes.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

-- 
2.43.0


^ permalink raw reply

* [RFC 1/2] arm64: kprobes: Do not handle non-XOL faults as kprobe faults
From: Pu Hu @ 2026-07-01 12:14 UTC (permalink / raw)
  To: catalin.marinas@arm.com, will@kernel.org, naveen@kernel.org,
	davem@davemloft.net, mhiramat@kernel.org,
	yang@os.amperecomputing.com, Hongyan Xia, Jiazi Li,
	ada.coupriediaz@arm.com, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
  Cc: Pu Hu
In-Reply-To: <20260701121448.3926-1-hupu@transsion.com>

From: hupu <hupu@transsion.com>

kprobe_fault_handler() handles faults taken while kprobes is in
KPROBE_HIT_SS or KPROBE_REENTER state as faults caused by the
single-stepped instruction.

That assumption is not always true. While a kprobe is preparing or
executing the out-of-line single-step instruction, other code may run
in that window. For example, perf or trace code can be invoked from the
debug exception path and may take a fault of its own. In that case the
fault did not happen on the kprobe XOL instruction, but the kprobe fault
handler may still try to recover it as a kprobe single-step fault.

This can corrupt the exception recovery flow and leave the real fault to
be handled with a wrong PC. A typical reproducer is running simpleperf
with preemptirq tracepoints and dwarf callchains while a kprobe is
installed on a frequently executed kernel function.

Fix this by handling faults in KPROBE_HIT_SS/KPROBE_REENTER only when
the faulting PC points at the current kprobe's XOL instruction. Faults
from any other PC are left to the normal fault handling path.

This follows the same idea as the x86 fix in commit 6381c24cd6d5
("kprobes/x86: Fix page-fault handling logic").

Signed-off-by: hupu <hupu@transsion.com>
Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
---
 arch/arm64/kernel/probes/kprobes.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index 43a0361a8bf0..e4d2852ce2fb 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -285,6 +285,20 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
 	switch (kcb->kprobe_status) {
 	case KPROBE_HIT_SS:
 	case KPROBE_REENTER:
+		/*
+		 * A fault taken while a kprobe is single-stepping is not
+		 * necessarily caused by the instruction in the XOL slot. For
+		 * example, tracing or perf code running in this window may take
+		 * an unrelated fault.
+		 *
+		 * Handle the fault here only when the faulting PC is the XOL
+		 * instruction of the current kprobe. Otherwise let the normal
+		 * fault handling path deal with it.
+		 */
+		if (cur->ainsn.xol_insn &&
+			instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
+			break;
+
 		/*
 		 * We are here because the instruction being single
 		 * stepped caused a page fault. We reset the current
-- 
2.43.0


^ permalink raw reply related

* [RFC 2/2] arm64: kprobes: Allow reentering kprobes while single-stepping
From: Pu Hu @ 2026-07-01 12:15 UTC (permalink / raw)
  To: catalin.marinas@arm.com, will@kernel.org, naveen@kernel.org,
	davem@davemloft.net, mhiramat@kernel.org,
	yang@os.amperecomputing.com, Hongyan Xia, Jiazi Li,
	ada.coupriediaz@arm.com, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
  Cc: Pu Hu
In-Reply-To: <20260701121448.3926-1-hupu@transsion.com>

From: hupu <hupu@transsion.com>

A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This
can happen when tracing or perf code runs from the debug exception path
while the first kprobe is preparing or executing its out-of-line
single-step instruction.

Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable,
the same as a hit in KPROBE_REENTER. This is too strict. A hit in
KPROBE_HIT_SS is still a one-level reentry and can be handled by saving
the current kprobe state and setting up single-step for the new probe,
just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.

The truly unrecoverable case is hitting another kprobe while already in
KPROBE_REENTER, because the reentry save area has already been consumed.

Move KPROBE_HIT_SS to the recoverable reentry cases and leave
KPROBE_REENTER as the unrecoverable nested reentry case.

This mirrors the x86 fix in commit 6a5022a56ac3
("kprobes/x86: Allow to handle reentered kprobe on single-stepping").

Signed-off-by: hupu <hupu@transsion.com>
Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
---
 arch/arm64/kernel/probes/kprobes.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index e4d2852ce2fb..764b2228cca0 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -240,10 +240,16 @@ static int __kprobes reenter_kprobe(struct kprobe *p,
 	switch (kcb->kprobe_status) {
 	case KPROBE_HIT_SSDONE:
 	case KPROBE_HIT_ACTIVE:
+	case KPROBE_HIT_SS:
+		/*
+		 * A probe can be hit while another kprobe is preparing or
+		 * executing its XOL single-step instruction. This is still a
+		 * recoverable one-level reentry, so handle it in the same way as
+		 * reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
+		 */
 		kprobes_inc_nmissed_count(p);
 		setup_singlestep(p, regs, kcb, 1);
 		break;
-	case KPROBE_HIT_SS:
 	case KPROBE_REENTER:
 		pr_warn("Failed to recover from reentered kprobes.\n");
 		dump_kprobe(p);
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH 2/2] tracing: Keep pid and comm[] in the same structure
From: Steven Rostedt @ 2026-07-01 12:23 UTC (permalink / raw)
  To: David Laight
  Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
	linux-trace-kernel, Michal Koutný
In-Reply-To: <20260701130404.3887c0e5@pumpkin>

On Wed, 1 Jul 2026 13:04:04 +0100
David Laight <david.laight.linux@gmail.com> wrote:


> > Well, that would break trace-cmd. As reading the raw buffers clears the
> > trace, and trace-cmd reads the saved_cmdlines file *after* it reads the
> > trace, as during the trace it gets populated.  
> 
> So you'd need to clear it when tracing is enabled after the buffer is cleared.
> Just a matter of getting the timing right.

Why? Note, saved_cmdlines is for *all* instances. You can have multiple
instances tracing different things and they still all use the one
saved_cmdlines file. It's not tied to any specific buffer. It's a cache. It
gets populated at the next schedule switch after an event occurs.

> 
> If trace-cmd is currently given all 6000 entries (if you've run tracing for
> long enough), then giving it all the active processes isn't going to be any
> more of a problem.

I'm much more concerned about losing processes that were traced! A lot of
tracing happens to tasks while running hackbench. Hackbench isn't being
traced, but if we record it, it will blow away all the cache and we lose
the task names we care about.

> So you could just save comm[] in the process exit path when the trace buffer
> is non-empty, or better those started before tracing was last stopped.

Again, which tracer? You can have multiple instances.

> You'd need to give trace-cmd the active ones first and delete the entry
> from the cache because the pid might have been reused.
> 
> All just needs some coding, testing, and fixing of corner cases.

What exactly are you trying to solve? This hasn't been perfect, but it's
been good enough since 2009.

-- Steve

^ permalink raw reply

* Re: [PATCH v2 1/4] rv/rtapp/sleep: Make the error more informative for user
From: Gabriele Monaco @ 2026-07-01 12:26 UTC (permalink / raw)
  To: Nam Cao; +Cc: Steven Rostedt, linux-trace-kernel, linux-doc, linux-kernel
In-Reply-To: <d97b4b5c476e5792b6875ec9bbf8dc214f999516.1781852967.git.namcao@linutronix.de>

On Fri, 2026-06-19 at 09:21 +0200, Nam Cao wrote:
> -static void handle_sched_wakeup(void *data, struct task_struct *task)
> +static void handle_sched_exit(void *data, bool is_switch)
>  {
> -	ltl_atom_pulse(task, LTL_WAKE, true);
> +	ltl_atom_pulse(current, LTL_SCHEDULE_IN, true);
>  }

Well, this triggers also in a spurious schedule: current -> current, can
that be a problem? Otherwise you may either use sched_switch over next
or pulse only if is_switch.

It probably isn't a big deal though, since there's no SLEEP prior.. But
we may want to avoid the noise.

Looks good overall.

Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>

Thanks,
Gabriele


^ permalink raw reply

* Re: [PATCH v2 2/4] rv/rtapp/sleep: Update nanosleep rule
From: Gabriele Monaco @ 2026-07-01 12:29 UTC (permalink / raw)
  To: Nam Cao; +Cc: Steven Rostedt, linux-trace-kernel, linux-doc, linux-kernel
In-Reply-To: <c7ceb5c6263ee8f43a2676acae669cf486b0d903.1781852967.git.namcao@linutronix.de>

On Fri, 2026-06-19 at 09:21 +0200, Nam Cao wrote:
> CLOCK_REALTIME is the only clock that often is misused in real-time
> applications. The other clocks either are safe for real-time uses
> (CLOCK_TAI, CLOCK_MONOTONIC, CLOCK_BOOTTIME) or are unlikely to be misused
> (CLOCK_AUX, CLOCK_PROCESS_CPUTIME_ID).
> 
> Update the monitor to only warn about CLOCK_REALTIME.
> 
> While at it, update the out-of-sync documentation.
> 
> Signed-off-by: Nam Cao <namcao@linutronix.de>

Looks good, thanks.

Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>

> ---
>  Documentation/trace/rv/monitor_rtapp.rst  | 17 +++++---
>  kernel/trace/rv/monitors/sleep/sleep.c    | 12 ++----
>  kernel/trace/rv/monitors/sleep/sleep.h    | 52 +++++++++++------------
>  tools/verification/models/rtapp/sleep.ltl |  2 +-
>  4 files changed, 39 insertions(+), 44 deletions(-)
> 
> diff --git a/Documentation/trace/rv/monitor_rtapp.rst
> b/Documentation/trace/rv/monitor_rtapp.rst
> index 01656bf7080a..570be67a8f3b 100644
> --- a/Documentation/trace/rv/monitor_rtapp.rst
> +++ b/Documentation/trace/rv/monitor_rtapp.rst
> @@ -51,12 +51,13 @@ The `sleep` monitor reports real-time threads sleeping in
> a manner that may
>  cause undesirable latency. Real-time applications should only put a real-time
>  thread to sleep for one of the following reasons:
>  
> -  - Cyclic work: real-time thread sleeps waiting for the next cycle. For this
> -    case, only the `clock_nanosleep` syscall should be used with
> `TIMER_ABSTIME`
> -    (to avoid time drift) and `CLOCK_MONOTONIC` (to avoid the clock being
> -    changed). No other method is safe for real-time. For example, threads
> -    waiting for timerfd can be woken by softirq which provides no real-time
> -    guarantee.
> +  - Cyclic work: real-time thread sleeps waiting for the next
> +    cycle. For this case, only the `clock_nanosleep` syscall should be
> +    used with `TIMER_ABSTIME` (to avoid time drift). Additionally,
> +    `CLOCK_REALTIME` should not be used (to avoid the clock being
> +    changed). No other method is safe for real-time. For example,
> +    threads waiting for timerfd can be woken by softirq which provides
> +    no real-time guarantee.
>    - Real-time thread waiting for something to happen (e.g. another thread
>      releasing shared resources, or a completion signal from another thread).
> In
>      this case, only futexes (FUTEX_LOCK_PI, FUTEX_LOCK_PI2 or one of
> @@ -99,14 +100,16 @@ The monitor's specification is::
>  
>    RT_VALID_SLEEP_REASON = FUTEX_WAIT
>                         or RT_FRIENDLY_NANOSLEEP
> +                       or EPOLL_WAIT
>  
>    RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP
>                        and NANOSLEEP_TIMER_ABSTIME
> -                      and NANOSLEEP_CLOCK_MONOTONIC
> +                      and not NANOSLEEP_CLOCK_REALTIME
>  
>    RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
>                    or WOKEN_BY_HARDIRQ
>                    or WOKEN_BY_NMI
> +                  or ABORT_SLEEP
>                    or KTHREAD_SHOULD_STOP
>  
>    ALLOWLIST = BLOCK_ON_RT_MUTEX
> diff --git a/kernel/trace/rv/monitors/sleep/sleep.c
> b/kernel/trace/rv/monitors/sleep/sleep.c
> index d6b677fab8f8..638be7d8747f 100644
> --- a/kernel/trace/rv/monitors/sleep/sleep.c
> +++ b/kernel/trace/rv/monitors/sleep/sleep.c
> @@ -44,8 +44,7 @@ static void ltl_atoms_init(struct task_struct *task, struct
> ltl_monitor *mon, bo
>  
>  	if (task_creation) {
>  		ltl_atom_set(mon, LTL_KTHREAD_SHOULD_STOP, false);
> -		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, false);
> -		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
> +		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
>  		ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
>  		ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
>  		ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
> @@ -60,8 +59,7 @@ static void ltl_atoms_init(struct task_struct *task, struct
> ltl_monitor *mon, bo
>  		/* kernel tasks do not do syscall */
>  		ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
>  		ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
> -		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, false);
> -		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
> +		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
>  		ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
>  		ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
>  		ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
> @@ -136,8 +134,7 @@ static void handle_sys_enter(void *data, struct pt_regs
> *regs, long id)
>  	case __NR_clock_nanosleep_time64:
>  #endif
>  		syscall_get_arguments(current, regs, args);
> -		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, args[0] ==
> CLOCK_MONOTONIC);
> -		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, args[0] ==
> CLOCK_TAI);
> +		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, args[0] ==
> CLOCK_REALTIME);
>  		ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, args[1] ==
> TIMER_ABSTIME);
>  		ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, true);
>  		break;
> @@ -178,8 +175,7 @@ static void handle_sys_exit(void *data, struct pt_regs
> *regs, long ret)
>  
>  	ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
>  	ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
> -	ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, false);
> -	ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
> +	ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
>  	ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
>  	ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
>  	ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, false);
> diff --git a/kernel/trace/rv/monitors/sleep/sleep.h
> b/kernel/trace/rv/monitors/sleep/sleep.h
> index 403dc2852c52..2fe2ec7edae8 100644
> --- a/kernel/trace/rv/monitors/sleep/sleep.h
> +++ b/kernel/trace/rv/monitors/sleep/sleep.h
> @@ -20,8 +20,7 @@ enum ltl_atom {
>  	LTL_FUTEX_WAIT,
>  	LTL_KERNEL_THREAD,
>  	LTL_KTHREAD_SHOULD_STOP,
> -	LTL_NANOSLEEP_CLOCK_MONOTONIC,
> -	LTL_NANOSLEEP_CLOCK_TAI,
> +	LTL_NANOSLEEP_CLOCK_REALTIME,
>  	LTL_NANOSLEEP_TIMER_ABSTIME,
>  	LTL_RT,
>  	LTL_SCHEDULE_IN,
> @@ -46,8 +45,7 @@ static const char *ltl_atom_str(enum ltl_atom atom)
>  		"fu_wa",
>  		"ker_th",
>  		"kth_sh_st",
> -		"na_cl_mo",
> -		"na_cl_ta",
> +		"na_cl_re",
>  		"na_ti_ab",
>  		"rt",
>  		"sch_in",
> @@ -87,8 +85,7 @@ static void ltl_start(struct task_struct *task, struct
> ltl_monitor *mon)
>  	bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
>  	bool rt = test_bit(LTL_RT, mon->atoms);
>  	bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME,
> mon->atoms);
> -	bool nanosleep_clock_tai = test_bit(LTL_NANOSLEEP_CLOCK_TAI, mon-
> >atoms);
> -	bool nanosleep_clock_monotonic =
> test_bit(LTL_NANOSLEEP_CLOCK_MONOTONIC, mon->atoms);
> +	bool nanosleep_clock_realtime =
> test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
>  	bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon-
> >atoms);
>  	bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
>  	bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
> @@ -97,17 +94,17 @@ static void ltl_start(struct task_struct *task, struct
> ltl_monitor *mon)
>  	bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
>  	bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
>  	bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
> -	bool val42 = task_is_rcu || task_is_migration;
> -	bool val43 = futex_lock_pi || val42;
> -	bool val5 = block_on_rt_mutex || val43;
> -	bool val34 = abort_sleep || kthread_should_stop;
> -	bool val35 = woken_by_nmi || val34;
> -	bool val36 = woken_by_hardirq || val35;
> -	bool val14 = woken_by_equal_or_higher_prio || val36;
> +	bool val41 = task_is_rcu || task_is_migration;
> +	bool val42 = futex_lock_pi || val41;
> +	bool val5 = block_on_rt_mutex || val42;
> +	bool val33 = abort_sleep || kthread_should_stop;
> +	bool val34 = woken_by_nmi || val33;
> +	bool val35 = woken_by_hardirq || val34;
> +	bool val14 = woken_by_equal_or_higher_prio || val35;
>  	bool val13 = !schedule_in;
> -	bool val26 = nanosleep_clock_monotonic || nanosleep_clock_tai;
> -	bool val27 = nanosleep_timer_abstime && val26;
> -	bool val18 = clock_nanosleep && val27;
> +	bool val25 = !nanosleep_clock_realtime;
> +	bool val26 = nanosleep_timer_abstime && val25;
> +	bool val18 = clock_nanosleep && val26;
>  	bool val20 = val18 || epoll_wait;
>  	bool val9 = futex_wait || val20;
>  	bool val11 = val9 || kernel_thread;
> @@ -138,8 +135,7 @@ ltl_possible_next_states(struct ltl_monitor *mon, unsigned
> int state, unsigned l
>  	bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
>  	bool rt = test_bit(LTL_RT, mon->atoms);
>  	bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME,
> mon->atoms);
> -	bool nanosleep_clock_tai = test_bit(LTL_NANOSLEEP_CLOCK_TAI, mon-
> >atoms);
> -	bool nanosleep_clock_monotonic =
> test_bit(LTL_NANOSLEEP_CLOCK_MONOTONIC, mon->atoms);
> +	bool nanosleep_clock_realtime =
> test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
>  	bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon-
> >atoms);
>  	bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
>  	bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
> @@ -148,17 +144,17 @@ ltl_possible_next_states(struct ltl_monitor *mon,
> unsigned int state, unsigned l
>  	bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
>  	bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
>  	bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
> -	bool val42 = task_is_rcu || task_is_migration;
> -	bool val43 = futex_lock_pi || val42;
> -	bool val5 = block_on_rt_mutex || val43;
> -	bool val34 = abort_sleep || kthread_should_stop;
> -	bool val35 = woken_by_nmi || val34;
> -	bool val36 = woken_by_hardirq || val35;
> -	bool val14 = woken_by_equal_or_higher_prio || val36;
> +	bool val41 = task_is_rcu || task_is_migration;
> +	bool val42 = futex_lock_pi || val41;
> +	bool val5 = block_on_rt_mutex || val42;
> +	bool val33 = abort_sleep || kthread_should_stop;
> +	bool val34 = woken_by_nmi || val33;
> +	bool val35 = woken_by_hardirq || val34;
> +	bool val14 = woken_by_equal_or_higher_prio || val35;
>  	bool val13 = !schedule_in;
> -	bool val26 = nanosleep_clock_monotonic || nanosleep_clock_tai;
> -	bool val27 = nanosleep_timer_abstime && val26;
> -	bool val18 = clock_nanosleep && val27;
> +	bool val25 = !nanosleep_clock_realtime;
> +	bool val26 = nanosleep_timer_abstime && val25;
> +	bool val18 = clock_nanosleep && val26;
>  	bool val20 = val18 || epoll_wait;
>  	bool val9 = futex_wait || val20;
>  	bool val11 = val9 || kernel_thread;
> diff --git a/tools/verification/models/rtapp/sleep.ltl
> b/tools/verification/models/rtapp/sleep.ltl
> index 464c84b9df87..5923e58d7810 100644
> --- a/tools/verification/models/rtapp/sleep.ltl
> +++ b/tools/verification/models/rtapp/sleep.ltl
> @@ -9,7 +9,7 @@ RT_VALID_SLEEP_REASON = FUTEX_WAIT
>  
>  RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP
>                      and NANOSLEEP_TIMER_ABSTIME
> -                    and (NANOSLEEP_CLOCK_MONOTONIC or NANOSLEEP_CLOCK_TAI)
> +                    and not NANOSLEEP_CLOCK_REALTIME
>  
>  RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
>                  or WOKEN_BY_HARDIRQ


^ permalink raw reply

* Re: [RFC 0/2] arm64: kprobes: Fix single-step fault and reentry handling
From: Pu Hu @ 2026-07-01 12:30 UTC (permalink / raw)
  To: catalin.marinas@arm.com, will@kernel.org, naveen@kernel.org,
	davem@davemloft.net, mhiramat@kernel.org,
	yang@os.amperecomputing.com, Hongyan Xia, Jiazi Li,
	ada.coupriediaz@arm.com, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
In-Reply-To: <20260701121448.3926-1-hupu@transsion.com>

Dear Maintainers,

I would like to provide some additional background for this patchset.

We observed a high-probability crash on an Android device running a
6.1.145-based kernel when recording preemptirq tracepoints for a user
space process with dwarf callchains enabled.

The command used to reproduce the issue is:

   simpleperf record -p <PID> -f 10000 \
     -e preemptirq:preempt_disable \
     -e preemptirq:preempt_enable \
     --duration 9 --call-graph dwarf \
     -o /data/local/tmp/perf.data

Here <PID> is the PID of a user space process, for example a foreground
application UI thread or RenderThread.

One important observation is that the crash does not reproduce if
"--call-graph dwarf" is removed.

The crash log shows a data abort on a user virtual address while the PC
is at a probed kernel instruction:

   [  297.177775] Unable to handle kernel paging request at virtual 
address 0000007ff042e000
   [  297.177792] Mem abort info:
   [  297.177795]   ESR = 0x0000000096000007
   [  297.177799]   EC = 0x25: DABT (current EL), IL = 32 bits
   [  297.177803]   SET = 0, FnV = 0
   [  297.177806]   EA = 0, S1PTW = 0
   [  297.177808]   FSC = 0x07: level 3 translation fault
   [  297.177811] Data abort info:
   [  297.177814]   ISV = 0, ISS = 0x00000007
   [  297.177817]   CM = 0, WnR = 0
   [  297.177820] user pgtable: 4k pages, 39-bit VAs, pgdp=000000098c9f2000
   [  297.177825] [0000007ff042e000] pgd=08000009aaaea003, 
p4d=08000009aaaea003, pud=08000009aaaea003, pmd=08000000abca0003, 
pte=0000000000000000
   [  297.177835] Internal error: Oops: 0000000096000007 [#1] PREEMPT SMP
   [  297.178070] Skip md ftrace buffer dump for: 0x2800d70
   ...
   [  297.178485] CPU: 6 PID: 10214 Comm: id.article.news Tainted: P S 
    W  O       6.1.145-android14-11-maybe-dirty-qki-consolidate #1
   [  297.178489] Hardware name: Qualcomm Technologies, Inc. Volcano 
QRD,x6878 (DT)
   [  297.178491] pstate: 22400005 (nzCv daif +PAN -UAO +TCO -DIT -SSBS 
BTYPE=--)
   [  297.178493] pc : folio_wait_bit_common+0x0/0x408
   [  297.178499] lr : perf_output_sample+0x57c/0xacc
   [  297.178502] sp : ffffffc0366c2f90
   [  297.178503] x29: ffffffc0366c2fb0 x28: 0000000000001000 x27: 
0000007ff042d5f8
   [  297.178507] x26: 00000000000035e7 x25: 0000000000000000 x24: 
ffffff892cec3000
   [  297.178510] x23: 0000000000001000 x22: 0000000000009370 x21: 
ffffffc0366c3140
   [  297.178512] x20: ffffff888aa1a180 x19: ffffffc0366c3020 x18: 
ffffffe01103b340
   [  297.178515] x17: 00000000ad6b63b6 x16: 00000000ad6b63b6 x15: 
0000007ff042d5f8
   [  297.178518] x14: 0000000000000000 x13: 003436737365636f x12: 
72705f7070612f6e
   [  297.178520] x11: 69622f6d65747379 x10: 732f0030333d7972 x9 : 
616d6972705f6c6f
   [  297.178523] x8 : 6f705f706173755f x7 : 54454b434f535f44 x6 : 
ffffff892cec39d8
   [  297.178526] x5 : ffffff892cec4000 x4 : 0000000000000008 x3 : 
6e6f6973736e6172
   [  297.178528] x2 : 00000000000005b8 x1 : 0000007ff042e000 x0 : 
ffffff892cec3000
   [  297.178531] Call trace:
   [  297.178532]  folio_wait_bit_common+0x0/0x408
   [  297.178535]  perf_event_output_forward+0x90/0xdc
   [  297.178537]  __perf_event_overflow+0x128/0x1e8
   [  297.178540]  perf_swevent_event+0x94/0x1a0
   [  297.178543]  perf_tp_event+0x140/0x270
   [  297.178545]  perf_trace_run_bpf_submit+0x84/0xe0
   [  297.178547]  perf_trace_preemptirq_template+0xe8/0x124
   [  297.178553]  trace_preempt_on+0xec/0x150
   [  297.178555]  preempt_count_sub+0xa8/0x12c
   [  297.178562]  do_debug_exception+0xd0/0x148
   [  297.178568]  el1_dbg+0x64/0x80
   [  297.178575]  el1h_64_sync_handler+0x3c/0x90
   [  297.178577]  el1h_64_sync+0x68/0x6c
   [  297.178579]  folio_wait_bit_common+0x0/0x408
   [  297.178582]  __get_node_page+0xdc/0x49c
   [  297.178587]  f2fs_get_dnode_of_data+0x404/0x950
   [  297.178589]  f2fs_map_blocks+0x1e0/0xdf8
   [  297.178591]  f2fs_mpage_readpages+0x1f0/0x8d0
   [  297.178594]  f2fs_readahead+0x84/0x10c
   [  297.178596]  read_pages+0xb8/0x434
   [  297.178603]  page_cache_ra_unbounded+0x9c/0x2f0
   [  297.178605]  page_cache_ra_order+0x2b0/0x348
   [  297.178608]  do_sync_mmap_readahead+0xd0/0x228
   [  297.178612]  filemap_fault+0x158/0x46c
   [  297.178615]  f2fs_filemap_fault+0x28/0x114
   [  297.178617]  handle_mm_fault+0x4f8/0x1468
   [  297.178620]  do_page_fault+0x208/0x4b8
   [  297.178622]  do_translation_fault+0x38/0x54
   [  297.178624]  do_mem_abort+0x58/0x118
   [  297.178626]  el0_da+0x48/0xb8
   [  297.178629]  el0t_64_sync_handler+0x98/0xb4
   [  297.178632]  el0t_64_sync+0x1a4/0x1a8
   [  297.178634] Code: 94000004 a8c17bfd d50323bf d65f03c0 (d4200080)
   [  297.178639] ---[ end trace 0000000000000000 ]---

The instruction d4200080 is the kprobe BRK instruction. The stack also
shows that the fault happens while handling a kprobe debug exception, 
and the perf/trace path is entered from that window.

 From the fulldump analysis, the issue appears to be related to the arm64
kprobe single-step/reentry handling. While a kprobe is preparing or
executing its XOL single-step instruction, perf/trace code may run in 
the same window. With dwarf callchains enabled, this path may also 
access user memory and take a data abort. In addition, another kprobe 
may be hit while the first kprobe is still in KPROBE_HIT_SS state.

This matches the type of issue that was fixed on x86 by the following
commits:

   6381c24cd6d5 ("kprobes/x86: Fix page-fault handling logic")
   6a5022a56ac3 ("kprobes/x86: Allow to handle reentered kprobe on 
single-stepping")

This patchset applies the same idea to arm64:

   - Patch 1 makes the arm64 kprobe fault handler handle a fault in
     KPROBE_HIT_SS/KPROBE_REENTER only when the faulting PC is the current
     kprobe's XOL instruction. Otherwise, the fault is left to the normal
     fault handling path.

   - Patch 2 allows a kprobe hit in KPROBE_HIT_SS to be handled as a
     recoverable one-level reentry. The unrecoverable case remains a hit
     while already in KPROBE_REENTER.

With both patches applied, we have kept the same stress test running for
three days and the crash is no longer reproduced.

I still have the full dmesg and fulldump from the crash device. Please
let me know if any additional information would be useful.

Thanks,
hupu


^ permalink raw reply

* Re: [PATCH v2 1/4] rv/rtapp/sleep: Make the error more informative for user
From: Gabriele Monaco @ 2026-07-01 12:34 UTC (permalink / raw)
  To: Nam Cao; +Cc: Steven Rostedt, linux-trace-kernel, linux-doc, linux-kernel
In-Reply-To: <9bc739eec41b6616e9f81cbe50759cefb1c3ad9c.camel@redhat.com>

On Wed, 2026-07-01 at 14:26 +0200, Gabriele Monaco wrote:
> On Fri, 2026-06-19 at 09:21 +0200, Nam Cao wrote:
> > -static void handle_sched_wakeup(void *data, struct task_struct *task)
> > +static void handle_sched_exit(void *data, bool is_switch)
> >  {
> > -	ltl_atom_pulse(task, LTL_WAKE, true);
> > +	ltl_atom_pulse(current, LTL_SCHEDULE_IN, true);
> >  }
> 
> Well, this triggers also in a spurious schedule: current -> current, can
> that be a problem? Otherwise you may either use sched_switch over next

sched_switch would obviously be in the wrong context.. Ignore that.

> or pulse only if is_switch.
> 
> It probably isn't a big deal though, since there's no SLEEP prior.. But
> we may want to avoid the noise.
> 
> Looks good overall.
> 
> Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
> 
> Thanks,
> Gabriele


^ permalink raw reply

* [PATCH 3/6] selftests/ftrace: fix spelling error in poll test comment
From: Wang Yan @ 2026-07-01 12:35 UTC (permalink / raw)
  To: Shuah Khan, Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
	linux-kernel, linux-trace-kernel, linux-kselftest
  Cc: Wang Yan
In-Reply-To: <20260701123520.271580-1-wangyan01@kylinos.cn>

Fix typo "happned" -> "happened" in ftrace poll test comment.

Signed-off-by: Wang Yan <wangyan01@kylinos.cn>
---
 tools/testing/selftests/ftrace/poll.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/ftrace/poll.c b/tools/testing/selftests/ftrace/poll.c
index 53258f7515e7..4da86a20dc85 100644
--- a/tools/testing/selftests/ftrace/poll.c
+++ b/tools/testing/selftests/ftrace/poll.c
@@ -66,7 +66,7 @@ int main(int argc, char *argv[])
 	}
 	close(pfd.fd);
 
-	/* If timeout happned (ret == 0), exit code is 1 */
+	/* If timeout happened (ret == 0), exit code is 1 */
 	if (ret == 0)
 		return 1;
 
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH v3] ufs: core: add hba parameter to trace events
From: Steven Rostedt @ 2026-07-01 12:57 UTC (permalink / raw)
  To: Peter Wang (王信友)
  Cc: linux-trace-kernel@vger.kernel.org,
	CC Chou (周志杰), jejb@linux.ibm.com,
	bvanassche@acm.org, linux-scsi@vger.kernel.org,
	linux-mediatek@lists.infradead.org,
	Chaotian Jing (井朝天),
	Eddie Huang ( 黃智傑),
	Qilin Tan ( 谭麒麟),
	Lin Gui ( 桂林),
	Yi-fan Peng ( 彭羿凡), alim.akhtar@samsung.com,
	Jiajie Hao ( 郝加节),
	Naomi Chu ( 朱詠田),
	Alice Chao ( 趙珮均),
	Ed Tsai ( 蔡宗軒), wsd_upstream,
	avri.altman@wdc.com, martin.petersen@oracle.com,
	Chun-Hung Wu ( 巫駿宏),
	Tun-yu Yu ( 游敦聿)
In-Reply-To: <e4c090a5b8402fe3db137d986f9a6639de73cc67.camel@mediatek.com>

On Wed, 1 Jul 2026 06:11:37 +0000
Peter Wang (王信友) <peter.wang@mediatek.com> wrote:
 
> However, I am curious: if the HBA is removed, implying that the 
> storage would become unusable, might the system encounter an 
> I/O hang or shutdown, potentially preventing its detection? 
> Perhaps it's a theoretical issue that would not manifest 
> in a real-world situation?

Note, it doesn't necessarily mean that the device itself was removed. The
issue is that a pointer to an allocated descriptor is saved in the ring buffer.

Maybe once the device is created it will never go way. But what happens if
for some reason the descriptor is freed and reallocated? Now the old
descriptor pointer is still in the ring buffer.

What in the logic guarantees that the pointer will never be freed?

And lets say there is an issue and the hba is freed and you debug this by
dumping the trace buffer via ftrace_dump_on_oops. Now the dump itself may
crash and you don't have a way to debug what happened.

One other point that causes issues here. It makes user space tracing
useless. Try tracing this with "trace-cmd record". These events will not be
able to be parsed.

-- Steve


^ permalink raw reply

* Re: [PATCH v2 3/4] rv/rtapp/sleep: Stop monitoring kernel threads
From: Gabriele Monaco @ 2026-07-01 13:02 UTC (permalink / raw)
  To: Nam Cao
  Cc: Steven Rostedt, linux-trace-kernel, linux-doc, linux-kernel,
	Sebastian Andrzej Siewior
In-Reply-To: <eec2ca5224bcdacc45b8e1eb2f0e68109e1cae7a.1781852967.git.namcao@linutronix.de>

On Fri, 2026-06-19 at 09:21 +0200, Nam Cao wrote:
> The rtapp/sleep monitor's primary purpose is detecting common mistakes
> with user-space real-time design. Monitoring real-time issues with
> kernel threads is a bonus.
> 
> However, accomodating kernel threads complicates the monitor due to
> the edge cases which is seen by the monitor as lower-priority task
> waking higher-priority task:
> 
>   - kthread_stop() wakes up the task in order to stop it.
> 
>   - The rcu thread and migration thread can be woken by any task.
> 
>   - The ktimerd thread is woken near the end of irq_exit_rcu(), where
>     the preempt counter is "broken" and falsely says this is task
>     context. This requires the monitor to use the hardirq_context flag
>     instead of the preempt counter.
> 
> Beside complicating the monitor, the final case also requires enabling
> CONFIG_TRACE_IRQFLAGS (so that "hardirq_context" can be used). This
> adds overhead to the kernel even when the monitor is not active. This
> may be an obstacle to enabling this monitor in distros' kernels.

Very good especially for this!

> Furthermore, kernel threads usually are started before the monitor is
> enabled. Consequently, the threads' states (i.o.w. the monitor's
> atomic propositions for the threads) are not fully known to the
> monitor. As a result, the kernel threads mostly cannot be monitored.
> 
> Overall, the downsides of accomodating kernel threads outweights the
> benefits. Thus, exclude kernel threads to simplify the monitor.
> 
> Signed-off-by: Nam Cao <namcao@linutronix.de>

Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>

Thanks,
Gabriele

> ---
> Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> ---
>  Documentation/trace/rv/monitor_rtapp.rst  |  22 ++---
>  kernel/trace/rv/monitors/sleep/Kconfig    |   1 -
>  kernel/trace/rv/monitors/sleep/sleep.c    |  39 +-------
>  kernel/trace/rv/monitors/sleep/sleep.h    | 104 +++++++++-------------
>  tools/verification/models/rtapp/sleep.ltl |   7 +-
>  5 files changed, 54 insertions(+), 119 deletions(-)
> 
> diff --git a/Documentation/trace/rv/monitor_rtapp.rst
> b/Documentation/trace/rv/monitor_rtapp.rst
> index 570be67a8f3b..502d3ea412eb 100644
> --- a/Documentation/trace/rv/monitor_rtapp.rst
> +++ b/Documentation/trace/rv/monitor_rtapp.rst
> @@ -93,9 +93,9 @@ assessment.
>  
>  The monitor's specification is::
>  
> -  RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST))
> +  RULE = always ((RT and SLEEP and USER_THREAD) imply (RT_FRIENDLY_SLEEP or
> ALLOWLIST))
>  
> -  RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD)
> +  RT_FRIENDLY_SLEEP = RT_VALID_SLEEP_REASON
>                    and ((not SCHEDULE_IN) until RT_FRIENDLY_WAKE)
>  
>    RT_VALID_SLEEP_REASON = FUTEX_WAIT
> @@ -110,23 +110,13 @@ The monitor's specification is::
>                    or WOKEN_BY_HARDIRQ
>                    or WOKEN_BY_NMI
>                    or ABORT_SLEEP
> -                  or KTHREAD_SHOULD_STOP
>  
>    ALLOWLIST = BLOCK_ON_RT_MUTEX
>             or FUTEX_LOCK_PI
> -           or TASK_IS_RCU
> -           or TASK_IS_MIGRATION
> -
> -Beside the scenarios described above, this specification also handle some
> -special cases:
> -
> -  - `KERNEL_THREAD`: kernel tasks do not have any pattern that can be
> recognized
> -    as valid real-time sleeping reasons. Therefore sleeping reason is not
> -    checked for kernel tasks.
> -  - `KTHREAD_SHOULD_STOP`: a non-real-time thread may stop a real-time kernel
> -    thread by waking it and waiting for it to exit (`kthread_stop()`). This
> -    wakeup is safe for real-time.
> -  - `ALLOWLIST`: to handle known false positives with the kernel.
> +
> +Beside the scenarios described above, this specification also defines an
> allow list
> +to handle some special cases:
> +
>    - `BLOCK_ON_RT_MUTEX` is included in the allowlist due to its
> implementation.
>      In the release path of rt_mutex, a boosted task is de-boosted before
> waking
>      the rt_mutex's waiter. Consequently, the monitor may see a real-time-
> unsafe
> diff --git a/kernel/trace/rv/monitors/sleep/Kconfig
> b/kernel/trace/rv/monitors/sleep/Kconfig
> index 6b7a122e7b47..d6ec3e9a91b6 100644
> --- a/kernel/trace/rv/monitors/sleep/Kconfig
> +++ b/kernel/trace/rv/monitors/sleep/Kconfig
> @@ -5,7 +5,6 @@ config RV_MON_SLEEP
>  	select RV_LTL_MONITOR
>  	depends on HAVE_SYSCALL_TRACEPOINTS
>  	depends on RV_MON_RTAPP
> -	select TRACE_IRQFLAGS
>  	default y
>  	select LTL_MON_EVENTS_ID
>  	bool "sleep monitor"
> diff --git a/kernel/trace/rv/monitors/sleep/sleep.c
> b/kernel/trace/rv/monitors/sleep/sleep.c
> index 638be7d8747f..aa5a984853b5 100644
> --- a/kernel/trace/rv/monitors/sleep/sleep.c
> +++ b/kernel/trace/rv/monitors/sleep/sleep.c
> @@ -43,7 +43,6 @@ static void ltl_atoms_init(struct task_struct *task, struct
> ltl_monitor *mon, bo
>  	ltl_atom_set(mon, LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO, false);
>  
>  	if (task_creation) {
> -		ltl_atom_set(mon, LTL_KTHREAD_SHOULD_STOP, false);
>  		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
>  		ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
>  		ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
> @@ -53,33 +52,7 @@ static void ltl_atoms_init(struct task_struct *task, struct
> ltl_monitor *mon, bo
>  		ltl_atom_set(mon, LTL_BLOCK_ON_RT_MUTEX, false);
>  	}
>  
> -	if (task->flags & PF_KTHREAD) {
> -		ltl_atom_set(mon, LTL_KERNEL_THREAD, true);
> -
> -		/* kernel tasks do not do syscall */
> -		ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
> -		ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
> -		ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
> -		ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
> -		ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
> -		ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
> -
> -		if (strstarts(task->comm, "migration/"))
> -			ltl_atom_set(mon, LTL_TASK_IS_MIGRATION, true);
> -		else
> -			ltl_atom_set(mon, LTL_TASK_IS_MIGRATION, false);
> -
> -		if (strstarts(task->comm, "rcu"))
> -			ltl_atom_set(mon, LTL_TASK_IS_RCU, true);
> -		else
> -			ltl_atom_set(mon, LTL_TASK_IS_RCU, false);
> -	} else {
> -		ltl_atom_set(mon, LTL_KTHREAD_SHOULD_STOP, false);
> -		ltl_atom_set(mon, LTL_KERNEL_THREAD, false);
> -		ltl_atom_set(mon, LTL_TASK_IS_RCU, false);
> -		ltl_atom_set(mon, LTL_TASK_IS_MIGRATION, false);
> -	}
> -
> +	ltl_atom_set(mon, LTL_USER_THREAD, !(task->flags & PF_KTHREAD));
>  }
>  
>  static void handle_sched_set_state(void *data, struct task_struct *task, int
> state)
> @@ -97,7 +70,7 @@ static void handle_sched_exit(void *data, bool is_switch)
>  
>  static void handle_sched_waking(void *data, struct task_struct *task)
>  {
> -	if (this_cpu_read(hardirq_context)) {
> +	if (in_hardirq()) {
>  		ltl_atom_pulse(task, LTL_WOKEN_BY_HARDIRQ, true);
>  	} else if (in_task()) {
>  		if (current->prio <= task->prio)
> @@ -181,12 +154,6 @@ static void handle_sys_exit(void *data, struct pt_regs
> *regs, long ret)
>  	ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, false);
>  }
>  
> -static void handle_kthread_stop(void *data, struct task_struct *task)
> -{
> -	/* FIXME: this could race with other tracepoint handlers */
> -	ltl_atom_update(task, LTL_KTHREAD_SHOULD_STOP, true);
> -}
> -
>  static int enable_sleep(void)
>  {
>  	int retval;
> @@ -200,7 +167,6 @@ static int enable_sleep(void)
>  	rv_attach_trace_probe("rtapp_sleep", sched_set_state_tp,
> handle_sched_set_state);
>  	rv_attach_trace_probe("rtapp_sleep", contention_begin,
> handle_contention_begin);
>  	rv_attach_trace_probe("rtapp_sleep", contention_end,
> handle_contention_end);
> -	rv_attach_trace_probe("rtapp_sleep", sched_kthread_stop,
> handle_kthread_stop);
>  	rv_attach_trace_probe("rtapp_sleep", sys_enter, handle_sys_enter);
>  	rv_attach_trace_probe("rtapp_sleep", sys_exit, handle_sys_exit);
>  	return 0;
> @@ -213,7 +179,6 @@ static void disable_sleep(void)
>  	rv_detach_trace_probe("rtapp_sleep", sched_set_state_tp,
> handle_sched_set_state);
>  	rv_detach_trace_probe("rtapp_sleep", contention_begin,
> handle_contention_begin);
>  	rv_detach_trace_probe("rtapp_sleep", contention_end,
> handle_contention_end);
> -	rv_detach_trace_probe("rtapp_sleep", sched_kthread_stop,
> handle_kthread_stop);
>  	rv_detach_trace_probe("rtapp_sleep", sys_enter, handle_sys_enter);
>  	rv_detach_trace_probe("rtapp_sleep", sys_exit, handle_sys_exit);
>  
> diff --git a/kernel/trace/rv/monitors/sleep/sleep.h
> b/kernel/trace/rv/monitors/sleep/sleep.h
> index 2fe2ec7edae8..44e593f41e6a 100644
> --- a/kernel/trace/rv/monitors/sleep/sleep.h
> +++ b/kernel/trace/rv/monitors/sleep/sleep.h
> @@ -18,15 +18,12 @@ enum ltl_atom {
>  	LTL_EPOLL_WAIT,
>  	LTL_FUTEX_LOCK_PI,
>  	LTL_FUTEX_WAIT,
> -	LTL_KERNEL_THREAD,
> -	LTL_KTHREAD_SHOULD_STOP,
>  	LTL_NANOSLEEP_CLOCK_REALTIME,
>  	LTL_NANOSLEEP_TIMER_ABSTIME,
>  	LTL_RT,
>  	LTL_SCHEDULE_IN,
>  	LTL_SLEEP,
> -	LTL_TASK_IS_MIGRATION,
> -	LTL_TASK_IS_RCU,
> +	LTL_USER_THREAD,
>  	LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
>  	LTL_WOKEN_BY_HARDIRQ,
>  	LTL_WOKEN_BY_NMI,
> @@ -43,15 +40,12 @@ static const char *ltl_atom_str(enum ltl_atom atom)
>  		"ep_wa",
>  		"fu_lo_pi",
>  		"fu_wa",
> -		"ker_th",
> -		"kth_sh_st",
>  		"na_cl_re",
>  		"na_ti_ab",
>  		"rt",
>  		"sch_in",
>  		"sle",
> -		"ta_mi",
> -		"ta_rc",
> +		"us_th",
>  		"wo_eq_hi_pr",
>  		"wo_ha",
>  		"wo_nm",
> @@ -79,46 +73,41 @@ static void ltl_start(struct task_struct *task, struct
> ltl_monitor *mon)
>  	bool woken_by_hardirq = test_bit(LTL_WOKEN_BY_HARDIRQ, mon->atoms);
>  	bool woken_by_equal_or_higher_prio =
> test_bit(LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
>  	     mon->atoms);
> -	bool task_is_rcu = test_bit(LTL_TASK_IS_RCU, mon->atoms);
> -	bool task_is_migration = test_bit(LTL_TASK_IS_MIGRATION, mon->atoms);
> +	bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
>  	bool sleep = test_bit(LTL_SLEEP, mon->atoms);
>  	bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
>  	bool rt = test_bit(LTL_RT, mon->atoms);
>  	bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME,
> mon->atoms);
>  	bool nanosleep_clock_realtime =
> test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
> -	bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon-
> >atoms);
> -	bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
>  	bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
>  	bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
>  	bool epoll_wait = test_bit(LTL_EPOLL_WAIT, mon->atoms);
>  	bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
>  	bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
>  	bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
> -	bool val41 = task_is_rcu || task_is_migration;
> -	bool val42 = futex_lock_pi || val41;
> -	bool val5 = block_on_rt_mutex || val42;
> -	bool val33 = abort_sleep || kthread_should_stop;
> -	bool val34 = woken_by_nmi || val33;
> -	bool val35 = woken_by_hardirq || val34;
> -	bool val14 = woken_by_equal_or_higher_prio || val35;
> +	bool val7 = block_on_rt_mutex || futex_lock_pi;
> +	bool val32 = woken_by_nmi || abort_sleep;
> +	bool val33 = woken_by_hardirq || val32;
> +	bool val14 = woken_by_equal_or_higher_prio || val33;
>  	bool val13 = !schedule_in;
>  	bool val25 = !nanosleep_clock_realtime;
>  	bool val26 = nanosleep_timer_abstime && val25;
>  	bool val18 = clock_nanosleep && val26;
>  	bool val20 = val18 || epoll_wait;
> -	bool val9 = futex_wait || val20;
> -	bool val11 = val9 || kernel_thread;
> +	bool val11 = futex_wait || val20;
> +	bool val3 = !user_thread;
>  	bool val2 = !sleep;
> +	bool val4 = val2 || val3;
>  	bool val1 = !rt;
> -	bool val3 = val1 || val2;
> +	bool val5 = val1 || val4;
>  
> -	if (val3)
> +	if (val5)
>  		__set_bit(S0, mon->states);
>  	if (val11 && val13)
>  		__set_bit(S1, mon->states);
>  	if (val11 && val14)
>  		__set_bit(S4, mon->states);
> -	if (val5)
> +	if (val7)
>  		__set_bit(S5, mon->states);
>  }
>  
> @@ -129,130 +118,125 @@ ltl_possible_next_states(struct ltl_monitor *mon,
> unsigned int state, unsigned l
>  	bool woken_by_hardirq = test_bit(LTL_WOKEN_BY_HARDIRQ, mon->atoms);
>  	bool woken_by_equal_or_higher_prio =
> test_bit(LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
>  	     mon->atoms);
> -	bool task_is_rcu = test_bit(LTL_TASK_IS_RCU, mon->atoms);
> -	bool task_is_migration = test_bit(LTL_TASK_IS_MIGRATION, mon->atoms);
> +	bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
>  	bool sleep = test_bit(LTL_SLEEP, mon->atoms);
>  	bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
>  	bool rt = test_bit(LTL_RT, mon->atoms);
>  	bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME,
> mon->atoms);
>  	bool nanosleep_clock_realtime =
> test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
> -	bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon-
> >atoms);
> -	bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
>  	bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
>  	bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
>  	bool epoll_wait = test_bit(LTL_EPOLL_WAIT, mon->atoms);
>  	bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
>  	bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
>  	bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
> -	bool val41 = task_is_rcu || task_is_migration;
> -	bool val42 = futex_lock_pi || val41;
> -	bool val5 = block_on_rt_mutex || val42;
> -	bool val33 = abort_sleep || kthread_should_stop;
> -	bool val34 = woken_by_nmi || val33;
> -	bool val35 = woken_by_hardirq || val34;
> -	bool val14 = woken_by_equal_or_higher_prio || val35;
> +	bool val7 = block_on_rt_mutex || futex_lock_pi;
> +	bool val32 = woken_by_nmi || abort_sleep;
> +	bool val33 = woken_by_hardirq || val32;
> +	bool val14 = woken_by_equal_or_higher_prio || val33;
>  	bool val13 = !schedule_in;
>  	bool val25 = !nanosleep_clock_realtime;
>  	bool val26 = nanosleep_timer_abstime && val25;
>  	bool val18 = clock_nanosleep && val26;
>  	bool val20 = val18 || epoll_wait;
> -	bool val9 = futex_wait || val20;
> -	bool val11 = val9 || kernel_thread;
> +	bool val11 = futex_wait || val20;
> +	bool val3 = !user_thread;
>  	bool val2 = !sleep;
> +	bool val4 = val2 || val3;
>  	bool val1 = !rt;
> -	bool val3 = val1 || val2;
> +	bool val5 = val1 || val4;
>  
>  	switch (state) {
>  	case S0:
> -		if (val3)
> +		if (val5)
>  			__set_bit(S0, next);
>  		if (val11 && val13)
>  			__set_bit(S1, next);
>  		if (val11 && val14)
>  			__set_bit(S4, next);
> -		if (val5)
> +		if (val7)
>  			__set_bit(S5, next);
>  		break;
>  	case S1:
>  		if (val11 && val13)
>  			__set_bit(S1, next);
> -		if (val13 && val3)
> +		if (val13 && val5)
>  			__set_bit(S2, next);
> -		if (val14 && val3)
> +		if (val14 && val5)
>  			__set_bit(S3, next);
>  		if (val11 && val14)
>  			__set_bit(S4, next);
> -		if (val13 && val5)
> +		if (val13 && val7)
>  			__set_bit(S6, next);
> -		if (val14 && val5)
> +		if (val14 && val7)
>  			__set_bit(S7, next);
>  		break;
>  	case S2:
>  		if (val11 && val13)
>  			__set_bit(S1, next);
> -		if (val13 && val3)
> +		if (val13 && val5)
>  			__set_bit(S2, next);
> -		if (val14 && val3)
> +		if (val14 && val5)
>  			__set_bit(S3, next);
>  		if (val11 && val14)
>  			__set_bit(S4, next);
> -		if (val13 && val5)
> +		if (val13 && val7)
>  			__set_bit(S6, next);
> -		if (val14 && val5)
> +		if (val14 && val7)
>  			__set_bit(S7, next);
>  		break;
>  	case S3:
> -		if (val3)
> +		if (val5)
>  			__set_bit(S0, next);
>  		if (val11 && val13)
>  			__set_bit(S1, next);
>  		if (val11 && val14)
>  			__set_bit(S4, next);
> -		if (val5)
> +		if (val7)
>  			__set_bit(S5, next);
>  		break;
>  	case S4:
> -		if (val3)
> +		if (val5)
>  			__set_bit(S0, next);
>  		if (val11 && val13)
>  			__set_bit(S1, next);
>  		if (val11 && val14)
>  			__set_bit(S4, next);
> -		if (val5)
> +		if (val7)
>  			__set_bit(S5, next);
>  		break;
>  	case S5:
> -		if (val3)
> +		if (val5)
>  			__set_bit(S0, next);
>  		if (val11 && val13)
>  			__set_bit(S1, next);
>  		if (val11 && val14)
>  			__set_bit(S4, next);
> -		if (val5)
> +		if (val7)
>  			__set_bit(S5, next);
>  		break;
>  	case S6:
>  		if (val11 && val13)
>  			__set_bit(S1, next);
> -		if (val13 && val3)
> +		if (val13 && val5)
>  			__set_bit(S2, next);
> -		if (val14 && val3)
> +		if (val14 && val5)
>  			__set_bit(S3, next);
>  		if (val11 && val14)
>  			__set_bit(S4, next);
> -		if (val13 && val5)
> +		if (val13 && val7)
>  			__set_bit(S6, next);
> -		if (val14 && val5)
> +		if (val14 && val7)
>  			__set_bit(S7, next);
>  		break;
>  	case S7:
> -		if (val3)
> +		if (val5)
>  			__set_bit(S0, next);
>  		if (val11 && val13)
>  			__set_bit(S1, next);
>  		if (val11 && val14)
>  			__set_bit(S4, next);
> -		if (val5)
> +		if (val7)
>  			__set_bit(S5, next);
>  		break;
>  	}
> diff --git a/tools/verification/models/rtapp/sleep.ltl
> b/tools/verification/models/rtapp/sleep.ltl
> index 5923e58d7810..4d78fdd204c0 100644
> --- a/tools/verification/models/rtapp/sleep.ltl
> +++ b/tools/verification/models/rtapp/sleep.ltl
> @@ -1,6 +1,6 @@
> -RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST))
> +RULE = always ((RT and SLEEP and USER_THREAD) imply (RT_FRIENDLY_SLEEP or
> ALLOWLIST))
>  
> -RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD)
> +RT_FRIENDLY_SLEEP = RT_VALID_SLEEP_REASON
>                  and ((not SCHEDULE_IN) until RT_FRIENDLY_WAKE)
>  
>  RT_VALID_SLEEP_REASON = FUTEX_WAIT
> @@ -15,9 +15,6 @@ RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
>                  or WOKEN_BY_HARDIRQ
>                  or WOKEN_BY_NMI
>                  or ABORT_SLEEP
> -                or KTHREAD_SHOULD_STOP
>  
>  ALLOWLIST = BLOCK_ON_RT_MUTEX
>           or FUTEX_LOCK_PI
> -         or TASK_IS_RCU
> -         or TASK_IS_MIGRATION


^ permalink raw reply

* Re: [RFC PATCH v2 4/4] rtla/osnoise: Leverage IPI event filters when tracing a subset of CPUs
From: Steven Rostedt @ 2026-07-01 13:11 UTC (permalink / raw)
  To: Tomas Glozar
  Cc: Valentin Schneider, linux-kernel, linux-trace-kernel,
	Masami Hiramatsu, Mathieu Desnoyers, Costa Shulyupin,
	Crystal Wood, John Kacur, Ivan Pravdin, Jonathan Corbet
In-Reply-To: <CAP4=nvT5H73ptaBaZ2fWQ+SjJ-c3eEGZbrSefMV6fxqL-va2XA@mail.gmail.com>

On Wed, 1 Jul 2026 08:45:40 +0200
Tomas Glozar <tglozar@redhat.com> wrote:

> I double-checked that and you are correct that the docstring says so,
> but it's an error in the docstring. According to the manpage, it
> returns the number of bytes written (i.e. positive on success, not
> zero) [1]:

Note, the man page is considered the source of "truth".

> 
> "RETURN VALUE
> ...
> tracefs_event_file_write() and tracefs_event_file_append() returns
> *the number of bytes written to the system/event file* or negative on
> error."
> 
> The code agrees as well: in tracefs_event_file_write() there's the
> wrong docstring (likely copied from another function) [2]:
> 
> /*
>  * tracefs_event_file_write - write to an event file
>  * ...
>  * Return 0 on success, and -1 on error.

Ug, that's a bug and needs to be fixed.

Thanks for catching this. I need to spend some time to catch up on the user
side of tracing. There's a few new bugzillas and patches I need to apply.

-- Steve


>  */
> int tracefs_event_file_write(struct tracefs_instance *instance,
>     const char *system, const char *event,
>     const char *file, const char *str)
> {
>         ....
>         ret = tracefs_instance_file_write(instance, path, str);
>         free(path);
>         return ret;
> }


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox