summaryrefslogtreecommitdiff
path: root/tools/testing
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-10-27 19:03:05 +0000
committerAlexei Starovoitov <ast@kernel.org>2026-10-28 04:07:03 +0000
commit3c0ca9f9a98e6b8bbaea13c5e39613237d50fe47 (patch)
treeb40b60dbb7d0f12a33615eab19727446e5af45e5 /tools/testing
parent2ad395a3847113289e443aafd51f2a751bc32ea2 (diff)
selftests/bpf: Avoid a page fault in rcu_reentry while the handler is armed
The test arms a handler at rcu_segcblist_enqueue() for its thread. Then it deletes an element of a task storage map. The delete gets to rcu_segcblist_enqueue(), and the handler does a nested delete there. The handler fires at the first rcu_segcblist_enqueue() in the thread. A page fault can be the first: skel->bss->target_pid = tid the handler is armed the next insn is in a page that is not in memory asm_exc_page_fault filemap_fault v9fs_issue_read p9_req_put irq_work_run rcu_defer_drain rcu_segcblist_enqueue the handler deletes the element bpf_map_delete_elem() -ENOENT test_rcu_reentry:FAIL:boot_delete unexpected error: -2 (errno 2) The result depends on the layout of test_progs. In the binary that failed, test_rcu_reentry() crosses the end of a page immediately after the store. This binary fails each time when it is on 9p. A copy of the same file on ext4 passes. Move the store and the delete to a function. Call it one time without a pid and with a bad fd. This call brings the text and .bss into memory, and it does not arm the handler. Fixes: 0464f587c61a ("selftests/bpf: Add a call_srcu() re-entry reproducer") Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261027190314.1440199-11-alexei.starovoitov@gmail.com
Diffstat (limited to 'tools/testing')
-rw-r--r--tools/testing/selftests/bpf/prog_tests/rcu_reentry.c19
1 files changed, 15 insertions, 4 deletions
diff --git a/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c b/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
index de23a14b3d40..10b4ea913921 100644
--- a/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
+++ b/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
@@ -23,10 +23,17 @@ static bool have_tree_srcu(void)
return kallsyms_find("srcu_expedite_current", &addr) == 0;
}
+/* Arm the handler for this thread, then trigger call_rcu_tasks_trace(). */
+static noinline int arm_and_delete(struct rcu_reentry *skel, int pid, int map_fd, int *pidfd)
+{
+ skel->bss->target_pid = pid;
+ return bpf_map_delete_elem(map_fd, pidfd);
+}
+
void test_rcu_reentry(void)
{
struct rcu_reentry *skel;
- int err, pidfd = -1, map_fd;
+ int err, pidfd = -1, bad_fd = -1, map_fd;
cpu_set_t set, old_set;
bool affinity_saved;
__u64 val = 1;
@@ -63,9 +70,13 @@ void test_rcu_reentry(void)
if (!ASSERT_OK(err, "boot_create"))
goto restore;
- /* Arm the handler for this thread, then trigger call_rcu_tasks_trace(). */
- skel->bss->target_pid = syscall(__NR_gettid);
- err = bpf_map_delete_elem(map_fd, &pidfd);
+ /*
+ * The handler fires at the first rcu_segcblist_enqueue() in this thread, and it deletes
+ * the element. A page fault can get there too, when the file system reads the page.
+ * Go through the same code first, without a pid, to bring its text and .bss in.
+ */
+ arm_and_delete(skel, 0, map_fd, &bad_fd);
+ err = arm_and_delete(skel, syscall(__NR_gettid), map_fd, &pidfd);
if (!ASSERT_OK(err, "boot_delete"))
goto restore;