Linux Trace Kernel
 help / color / mirror / Atom feed
From: Pu Hu <hupu@transsion.com>
To: "mhiramat@kernel.org" <mhiramat@kernel.org>
Cc: "ada.coupriediaz@arm.com" <ada.coupriediaz@arm.com>,
	"catalin.marinas@arm.com" <catalin.marinas@arm.com>,
	"davem@davemloft.net" <davem@davemloft.net>,
	Hongyan Xia <hongyan.xia@transsion.com>,
	Pu Hu <hupu@transsion.com>, Jiazi Li <jiazi.li@transsion.com>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	"linux-trace-kernel@vger.kernel.org"
	<linux-trace-kernel@vger.kernel.org>,
	"naveen@kernel.org" <naveen@kernel.org>,
	"will@kernel.org" <will@kernel.org>,
	"yang@os.amperecomputing.com" <yang@os.amperecomputing.com>
Subject: [RFC 3/3] selftests/kprobes: Add kprobe stress test for page fault handling
Date: Mon, 6 Jul 2026 08:36:50 +0000	[thread overview]
Message-ID: <20260706083636.159883-4-hupu@transsion.com> (raw)
In-Reply-To: <20260706083636.159883-1-hupu@transsion.com>

From: Pu Hu <hupu@transsion.com>

Add a selftest that stresses kprobe handling around page fault paths.
This reproducer consists of:
- A kprobe module that probes folio_wait_bit_common()
- A userspace program that repeatedly triggers file-backed page faults

This is primarily intended to reproduce arm64 kprobe stability issues
related to XOL slot execution and fault handling during single-stepping.

Signed-off-by: Pu Hu <hupu@transsion.com>
---
 tools/testing/selftests/kprobe/.gitignore     |   2 +
 tools/testing/selftests/kprobe/Makefile       |  75 +++++++++++++
 tools/testing/selftests/kprobe/fault_stress.c | 105 ++++++++++++++++++
 .../selftests/kprobe/kprobe_folio_stress.c    |  70 ++++++++++++
 4 files changed, 252 insertions(+)
 create mode 100644 tools/testing/selftests/kprobe/.gitignore
 create mode 100644 tools/testing/selftests/kprobe/Makefile
 create mode 100644 tools/testing/selftests/kprobe/fault_stress.c
 create mode 100644 tools/testing/selftests/kprobe/kprobe_folio_stress.c

diff --git a/tools/testing/selftests/kprobe/.gitignore b/tools/testing/selftests/kprobe/.gitignore
new file mode 100644
index 000000000000..5fdb3ee02cf1
--- /dev/null
+++ b/tools/testing/selftests/kprobe/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+fault_stress
diff --git a/tools/testing/selftests/kprobe/Makefile b/tools/testing/selftests/kprobe/Makefile
new file mode 100644
index 000000000000..39f20de5370c
--- /dev/null
+++ b/tools/testing/selftests/kprobe/Makefile
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for kprobe fault stress test
+#
+
+ifneq ($(KERNELRELEASE),)
+
+# This part is used by kernel Kbuild when building the external module:
+#
+#   make -C $(KDIR) M=$(CURDIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+#
+obj-m += kprobe_folio_stress.o
+
+ccflags-y += -g -O0 -fno-omit-frame-pointer
+
+else
+
+# This part is used when running make directly in this directory.
+#
+#   make KDIR=$BUILD_DIR ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE all
+#
+
+PWD				:= $(shell pwd)
+ARCH			?= arm64
+CROSS_COMPILE	?= aarch64-dumpstack-linux-gnu-
+KDIR			?= /lib/modules/$(shell uname -r)/build
+DEST_PATH		?= $(PWD)/out
+Q				?= @
+
+# Kernel module
+KP_MOD			:= kprobe_folio_stress
+KP_KO			:= $(KP_MOD).ko
+
+# Userspace test program
+UNIT_TEST		:= fault_stress
+UNIT_TEST_SRC	:= fault_stress.c
+
+# Userspace CFLAGS.
+# These options make userspace stacks easier to unwind.
+USER_CFLAGS		:= -static -g -O0 -fno-omit-frame-pointer -fasynchronous-unwind-tables
+USER_LIBS		:= -lm -lpthread
+
+.PHONY: all modules user install clean
+
+all: modules user
+
+modules:
+	$(Q)$(MAKE) -C $(KDIR) \
+		M=$(PWD) \
+		ARCH=$(ARCH) \
+		CROSS_COMPILE=$(CROSS_COMPILE) \
+		modules
+
+user:
+	$(Q)$(CROSS_COMPILE)gcc \
+		$(USER_CFLAGS) \
+		$(UNIT_TEST_SRC) \
+		-o $(UNIT_TEST) \
+		$(USER_LIBS)
+
+install: all
+	$(Q)mkdir -p $(DEST_PATH)
+	$(Q)cp -f $(KP_KO) $(DEST_PATH)/
+	$(Q)cp -f $(UNIT_TEST) $(DEST_PATH)/
+
+clean:
+	$(Q)$(MAKE) -C $(KDIR) \
+		M=$(PWD) \
+		ARCH=$(ARCH) \
+		CROSS_COMPILE=$(CROSS_COMPILE) \
+		clean
+	$(Q)rm -f $(UNIT_TEST)
+	$(Q)rm -rf $(DEST_PATH)
+
+endif
diff --git a/tools/testing/selftests/kprobe/fault_stress.c b/tools/testing/selftests/kprobe/fault_stress.c
new file mode 100644
index 000000000000..160e172dfa59
--- /dev/null
+++ b/tools/testing/selftests/kprobe/fault_stress.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * fault_stress.c - Userspace program to trigger file-backed page faults
+ *
+ * This program creates a file and repeatedly maps/unmaps it while
+ * accessing memory, triggering file-backed page faults. It's designed
+ * to work with kprobe-folio-stress.c to stress kprobe handling.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define FILE_SIZE   (256UL * 1024 * 1024)
+#define NR_THREADS  8
+
+static void deep_call(int n)
+{
+	volatile char buf[4096];
+
+	memset((void *)buf, n, sizeof(buf));
+
+	if (n > 0)
+		deep_call(n - 1);
+	else
+		sched_yield();
+}
+
+static void *worker(void *arg)
+{
+	const char *path = arg;
+	int fd;
+	char *map;
+	unsigned long i;
+	volatile unsigned long sum = 0;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0) {
+		perror("open");
+		return NULL;
+	}
+
+	map = mmap(NULL, FILE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (map == MAP_FAILED) {
+		perror("mmap");
+		close(fd);
+		return NULL;
+	}
+
+	for (;;) {
+		/*
+		 * Drop the pages backing this mapping from the current
+		 * process. Subsequent accesses are more likely to trigger
+		 * file-backed page faults again.
+		 */
+		madvise(map, FILE_SIZE, MADV_DONTNEED);
+
+		for (i = 0; i < FILE_SIZE; i += 4096 * 17) {
+			sum += map[i];
+			deep_call(64);
+		}
+	}
+
+	munmap(map, FILE_SIZE);
+	close(fd);
+	return NULL;
+}
+
+int main(void)
+{
+	pthread_t th[NR_THREADS];
+	const char *path = "/tmp/fault_stress_file";
+	int fd;
+	int i;
+
+	fd = open(path, O_CREAT | O_RDWR, 0644);
+	if (fd < 0) {
+		perror("open file");
+		return 1;
+	}
+
+	if (ftruncate(fd, FILE_SIZE) < 0) {
+		perror("ftruncate");
+		close(fd);
+		return 1;
+	}
+
+	close(fd);
+
+	for (i = 0; i < NR_THREADS; i++)
+		pthread_create(&th[i], NULL, worker, (void *)path);
+
+	for (i = 0; i < NR_THREADS; i++)
+		pthread_join(th[i], NULL);
+
+	return 0;
+}
diff --git a/tools/testing/selftests/kprobe/kprobe_folio_stress.c b/tools/testing/selftests/kprobe/kprobe_folio_stress.c
new file mode 100644
index 000000000000..181a89f4c495
--- /dev/null
+++ b/tools/testing/selftests/kprobe/kprobe_folio_stress.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * kprobe_folio_stress.c - kprobe stress test for page fault handling
+ *
+ * This module installs a kprobe on folio_wait_bit_common() and is
+ * intended to be used together with fault_stress.c to stress kprobe
+ * handling around fault paths.
+ *
+ * Primary use case: reproduce arm64 kprobe stability issues related to
+ * XOL slot execution and fault handling during single-stepping.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/sched.h>
+#include <linux/atomic.h>
+#include <linux/ratelimit.h>
+#include <linux/kallsyms.h>
+
+
+static atomic64_t kp_hit_count = ATOMIC64_INIT(0);
+
+static int folio_wait_bit_common_handler(
+			struct kprobe *p, struct pt_regs *regs)
+{
+	unsigned long hit;
+
+	hit = atomic64_inc_return(&kp_hit_count);
+
+	pr_info("kp_folio: hit=%lu comm=%s tgid=%d tid=%d\n",
+			    hit, current->comm, current->tgid, current->pid);
+
+	return 0;
+}
+
+static struct kprobe kp_folio_probe = {
+	.symbol_name = "folio_wait_bit_common",
+	.pre_handler = folio_wait_bit_common_handler,
+};
+
+static int __init kprobe_folio_init(void)
+{
+	int ret;
+
+	ret = register_kprobe(&kp_folio_probe);
+	if (ret < 0) {
+		pr_err("kp_folio: register_kprobe failed, ret=%d\n", ret);
+		return ret;
+	}
+
+	pr_info("kp_folio: kprobe registered at %pS, addr=%px\n",
+		kp_folio_probe.addr, kp_folio_probe.addr);
+
+	return 0;
+}
+
+static void __exit kprobe_folio_exit(void)
+{
+	unregister_kprobe(&kp_folio_probe);
+	pr_info("kp_folio: kprobe unregistered, total hits=%lld\n",
+		atomic64_read(&kp_hit_count));
+}
+
+module_init(kprobe_folio_init);
+module_exit(kprobe_folio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pu Hu <hupu@transsion.com>");
+MODULE_DESCRIPTION("kprobe stress test for folio_wait_bit_common");
-- 
2.43.0


      parent reply	other threads:[~2026-07-06  8:37 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-01 12:14 [RFC 0/2] arm64: kprobes: Fix single-step fault and reentry handling Pu Hu
2026-07-01 12:14 ` [RFC 1/2] arm64: kprobes: Do not handle non-XOL faults as kprobe faults Pu Hu
2026-07-01 12:15 ` [RFC 2/2] arm64: kprobes: Allow reentering kprobes while single-stepping Pu Hu
2026-07-01 12:30 ` [RFC 0/2] arm64: kprobes: Fix single-step fault and reentry handling Pu Hu
2026-07-01 13:43 ` Masami Hiramatsu
2026-07-01 13:56   ` Pu Hu
2026-07-02 10:07     ` Pu Hu
2026-07-02 10:09       ` Pu Hu
2026-07-04 14:47         ` Masami Hiramatsu
2026-07-06  8:36           ` [RFC 0/3] " Pu Hu
2026-07-06  8:36             ` [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults Pu Hu
2026-07-06  8:36             ` [RFC 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping Pu Hu
2026-07-06  8:36             ` Pu Hu [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260706083636.159883-4-hupu@transsion.com \
    --to=hupu@transsion.com \
    --cc=ada.coupriediaz@arm.com \
    --cc=catalin.marinas@arm.com \
    --cc=davem@davemloft.net \
    --cc=hongyan.xia@transsion.com \
    --cc=jiazi.li@transsion.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=mhiramat@kernel.org \
    --cc=naveen@kernel.org \
    --cc=will@kernel.org \
    --cc=yang@os.amperecomputing.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox