From: Puranjay Mohan <puranjay@kernel.org>
To: "Lai Jiangshan" <jiangshanlai@gmail.com>,
"Paul E. McKenney" <paulmck@kernel.org>,
"Josh Triplett" <josh@joshtriplett.org>,
"Onur Özkan" <work@onurozkan.dev>,
"Frederic Weisbecker" <frederic@kernel.org>,
"Neeraj Upadhyay" <neeraj.upadhyay@kernel.org>,
"Joel Fernandes" <joelagnelf@nvidia.com>,
"Boqun Feng" <boqun@kernel.org>,
"Uladzislau Rezki" <urezki@gmail.com>,
"Davidlohr Bueso" <dave@stgolabs.net>,
"Andrii Nakryiko" <andrii@kernel.org>,
"Eduard Zingerman" <eddyz87@gmail.com>,
"Alexei Starovoitov" <ast@kernel.org>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Kumar Kartikeya Dwivedi" <memxor@gmail.com>
Cc: Puranjay Mohan <puranjay@kernel.org>,
Steven Rostedt <rostedt@goodmis.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
Zqiang <qiang.zhang@linux.dev>,
Martin KaFai Lau <martin.lau@linux.dev>,
Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jiri Olsa <jolsa@kernel.org>,
Emil Tsalapatis <emil@etsalapatis.com>,
Matt Fleming <mfleming@cloudflare.com>,
"Harry Yoo (Oracle)" <harry@kernel.org>,
linux-kernel@vger.kernel.org, rcu@vger.kernel.org,
bpf@vger.kernel.org, linux-rt-devel@lists.linux.dev
Subject: [PATCH v3 6/6] selftests/bpf: Add a call_srcu() re-entry reproducer
Date: Wed, 5 Aug 2026 05:23:43 -0700 [thread overview]
Message-ID: <20260805122346.269445-7-puranjay@kernel.org> (raw)
In-Reply-To: <20260805122346.269445-1-puranjay@kernel.org>
Re-enter call_srcu() from a BPF program to exercise its any-context
safety (and thus call_rcu_tasks_trace(), which is call_srcu() on
rcu_tasks_trace_srcu_struct).
An fentry program on rcu_segcblist_enqueue() fires mid-enqueue: that
function is reached from srcu_gp_start_if_needed() with the srcu_data
->lock held. The program does a task-storage delete, whose only deferred
work is call_rcu_tasks_trace(), re-entering the enqueue on the same CPU.
The triggering thread is pinned to one CPU and matched by TID, so the
program fires only for the test's own delete.
Without the fix the nested call re-takes the same sdp lock and
self-deadlocks; with it the nested __call_srcu() sees interrupts disabled
and defers via irq_work, so the delete returns and the test passes. Since
it can hang an unfixed kernel, run it only against a kernel carrying the
fix.
Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../selftests/bpf/prog_tests/rcu_reentry.c | 95 +++++++++++++++++++
.../testing/selftests/bpf/progs/rcu_reentry.c | 51 ++++++++++
2 files changed, 146 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
create mode 100644 tools/testing/selftests/bpf/progs/rcu_reentry.c
diff --git a/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c b/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
new file mode 100644
index 0000000000000..fa813d1d492b6
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Exercise re-entry into call_srcu() from BPF; see progs/rcu_reentry.c.
+ *
+ * On a kernel without the call_srcu() any-context fix the nested call
+ * self-deadlocks on the srcu_data lock, so this hangs rather than fails.
+ */
+#define _GNU_SOURCE
+#include <sched.h>
+#include <sys/syscall.h>
+#include <test_progs.h>
+#include "rcu_reentry.skel.h"
+
+static int sys_pidfd_open(pid_t pid, unsigned int flags)
+{
+ return syscall(__NR_pidfd_open, pid, flags);
+}
+
+/* Tiny RCU builds have no rcu_segcblist_enqueue() to attach to. */
+static bool have_attach_target(void)
+{
+ char buf[256];
+ bool found = false;
+ FILE *f;
+
+ f = fopen("/proc/kallsyms", "r");
+ if (!f)
+ return true; /* cannot tell; let the attach decide */
+ while (fgets(buf, sizeof(buf), f)) {
+ if (strstr(buf, " rcu_segcblist_enqueue\n")) {
+ found = true;
+ break;
+ }
+ }
+ fclose(f);
+ return found;
+}
+
+void test_rcu_reentry(void)
+{
+ struct rcu_reentry *skel;
+ int err, pidfd = -1, map_fd;
+ cpu_set_t set, old_set;
+ bool affinity_saved;
+ __u64 val = 1;
+
+ if (!have_attach_target()) {
+ test__skip();
+ return;
+ }
+
+ skel = rcu_reentry__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+ return;
+
+ err = rcu_reentry__attach(skel);
+ if (!ASSERT_OK(err, "skel_attach"))
+ goto out;
+
+ /* Keep the re-entry on a single CPU. */
+ affinity_saved = !sched_getaffinity(0, sizeof(old_set), &old_set);
+ CPU_ZERO(&set);
+ CPU_SET(0, &set);
+ if (!ASSERT_OK(sched_setaffinity(0, sizeof(set), &set), "setaffinity"))
+ goto out;
+
+ pidfd = sys_pidfd_open(getpid(), 0);
+ if (!ASSERT_GE(pidfd, 0, "pidfd_open"))
+ goto restore;
+ map_fd = bpf_map__fd(skel->maps.task_stg);
+ err = bpf_map_update_elem(map_fd, &pidfd, &val, BPF_NOEXIST);
+ 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);
+ if (!ASSERT_OK(err, "boot_delete"))
+ goto restore;
+
+ /* Only Tree SRCU enqueues via rcu_segcblist_enqueue(); skip elsewhere. */
+ if (!skel->bss->hits) {
+ test__skip();
+ goto restore;
+ }
+ ASSERT_EQ(skel->bss->get_errs, 0, "nested_storage_get");
+ ASSERT_EQ(skel->bss->del_errs, 0, "nested_storage_delete");
+restore:
+ if (affinity_saved)
+ sched_setaffinity(0, sizeof(old_set), &old_set);
+out:
+ if (pidfd >= 0)
+ close(pidfd);
+ rcu_reentry__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/rcu_reentry.c b/tools/testing/selftests/bpf/progs/rcu_reentry.c
new file mode 100644
index 0000000000000..47a36f704cf3e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/rcu_reentry.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Re-enter call_srcu() from a BPF program. fentry on rcu_segcblist_enqueue()
+ * fires inside call_srcu()'s enqueue (reached from srcu_gp_start_if_needed()
+ * with the srcu_data ->lock held); the handler then calls call_rcu_tasks_trace()
+ * -- itself call_srcu() on rcu_tasks_trace_srcu_struct -- re-entering the same
+ * srcu_data on the same CPU.
+ */
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+struct {
+ __uint(type, BPF_MAP_TYPE_TASK_STORAGE);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+ __type(key, int);
+ __type(value, __u64);
+} task_stg SEC(".maps");
+
+int target_pid;
+int hits;
+int get_errs;
+int del_errs;
+int done;
+
+SEC("fentry/rcu_segcblist_enqueue")
+int BPF_PROG(reenter)
+{
+ struct task_struct *cur;
+
+ if (done || !target_pid)
+ return 0;
+
+ cur = bpf_get_current_task_btf();
+ if (cur->pid != target_pid)
+ return 0;
+
+ /* Issue the nested call exactly once, so the test is deterministic. */
+ done = 1;
+ __sync_fetch_and_add(&hits, 1);
+
+ /* Re-enter via a task-storage delete, which calls call_rcu_tasks_trace(). */
+ if (!bpf_task_storage_get(&task_stg, cur, 0, BPF_LOCAL_STORAGE_GET_F_CREATE))
+ __sync_fetch_and_add(&get_errs, 1);
+ else if (bpf_task_storage_delete(&task_stg, cur))
+ __sync_fetch_and_add(&del_errs, 1);
+
+ return 0;
+}
--
2.53.0-Meta
prev parent reply other threads:[~2026-08-05 12:24 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-05 12:23 [PATCH v3 0/6] rcu,srcu: Make call_rcu()/call_srcu() safe from any context Puranjay Mohan
2026-08-05 12:23 ` [PATCH v3 1/6] rcu: Make call_rcu() safe to call " Puranjay Mohan
2026-08-05 12:36 ` sashiko-bot
2026-08-05 16:49 ` Paul E. McKenney
2026-08-05 12:23 ` [PATCH v3 2/6] rcu: Make Tiny " Puranjay Mohan
2026-08-05 12:37 ` sashiko-bot
2026-08-05 12:23 ` [PATCH v3 3/6] srcu: Make call_srcu() " Puranjay Mohan
2026-08-05 12:35 ` sashiko-bot
2026-08-06 14:04 ` Zqiang
2026-08-06 14:08 ` Puranjay Mohan
2026-08-05 12:23 ` [PATCH v3 4/6] srcu: Make Tiny " Puranjay Mohan
2026-08-05 12:23 ` [PATCH v3 5/6] rcutorture: Exercise ->call() from NMI context Puranjay Mohan
2026-08-05 12:23 ` Puranjay Mohan [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=20260805122346.269445-7-puranjay@kernel.org \
--to=puranjay@kernel.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=boqun@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=dave@stgolabs.net \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=frederic@kernel.org \
--cc=harry@kernel.org \
--cc=jiangshanlai@gmail.com \
--cc=joelagnelf@nvidia.com \
--cc=jolsa@kernel.org \
--cc=josh@joshtriplett.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=martin.lau@linux.dev \
--cc=mathieu.desnoyers@efficios.com \
--cc=memxor@gmail.com \
--cc=mfleming@cloudflare.com \
--cc=neeraj.upadhyay@kernel.org \
--cc=paulmck@kernel.org \
--cc=qiang.zhang@linux.dev \
--cc=rcu@vger.kernel.org \
--cc=rostedt@goodmis.org \
--cc=song@kernel.org \
--cc=urezki@gmail.com \
--cc=work@onurozkan.dev \
--cc=yonghong.song@linux.dev \
/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