diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2026-10-27 19:03:05 +0000 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-10-28 04:07:03 +0000 |
| commit | 3c0ca9f9a98e6b8bbaea13c5e39613237d50fe47 (patch) | |
| tree | b40b60dbb7d0f12a33615eab19727446e5af45e5 /tools/testing | |
| parent | 2ad395a3847113289e443aafd51f2a751bc32ea2 (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.c | 19 |
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; |
