From: "Paul E. McKenney" <paulmck@kernel.org>
To: rcu@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, kernel-team@meta.com,
rostedt@goodmis.org, "Paul E. McKenney" <paulmck@kernel.org>,
K Prateek Nayak <kprateek.nayak@amd.com>
Subject: [PATCH 7/8] rcu: Print CPU that preempted stalled reader wants to run on
Date: Mon, 9 Nov 2026 14:19:38 -0800 [thread overview]
Message-ID: <20261109221939.2070171-7-paulmck@kernel.org> (raw)
In-Reply-To: <db8c185c-5523-48e5-abee-020503b4875f@paulmck-laptop>
This commit defines a cpu_curr_snapshot_rcu() function that invokes
rcu_dereference_all(cpu_curr()) to determine which task is currently
running on the specified CPU. The RCU CPU stall warning then uses this
new function in order to print the task that has preempted the stalled
task (the stalled task being the one that is in an RCU read-side critical
section, thus preventing the current RCU grace period from completing).
This modified stall-warning message also includes the CPU, along with
an exclamation mark ("!") after the CPU number if that CPU is currently
offline.
This is a potential step towards allowing people (or LLMs, for that
matter) locate any tasks that might be preventing the preempted RCU
reader task from running.
Adding a get_task_struct() for the currently running task would be a
bug because we are not in a position to prevent that task from exiting.
Yes, yes, because interrupts are disabled throughout, the corresponding
task_struct structure cannot be freed, but following sequence of events
is all too possible:
o The check_slow_task() function places a pointer to the task_struct
structure in its local variable "th".
o The corresponding task exits, reducing its task_struct structure's
->usage to zero. The task_struct structure is therefore passed
to call_rcu().
o The check_slow_task() function invokes get_task_struct(th),
setting th->usage to one.
o The rcu_print_task_stall() function invokes put_task_struct(),
again reducing ->usage to zero, and again passing the task
struct to call_rcu(). This results in either double-call_rcu()
or use-after-free, depending on when the RCU grace period for
the first call_rcu() completes. Both outcomes are bad.
This commit therefore relies solely on disabled interrupts acting as
an implicit RCU read-side critical section that prevents all of the
task_struct structures from being freed, thus avoiding the need for
get_task_struct().
The original get_task_struct() calls in rcu_print_task_stall() on the
tasks on the ->blkd_tasks list are safe because those tasks cannot exit
without first acquiring the rcu_node ->lock that this code already holds
(because these tasks must remove themselves from the ->blkd_tasks list).
But it is better to be consistent and rely on RCU in both cases.
These pre-existing get_task_struct() and put_task_struct() calls are
therefore removed.
[ paulmck: Apply feedback from K Prateek Nayak and Peter Zijlstra. ]
[ paulmck: Apply feedback from Steven Rostedt. ]
Link: https://lore.kernel.org/all/a7a21bf3-a7d5-45bb-be4f-0119fe53fa20@paulmck-laptop/
Link: https://lore.kernel.org/all/e8b6b372-d16b-4939-aa7a-755b7149e0fb@paulmck-laptop/
Link: https://lore.kernel.org/all/22d0d56c-6cef-47d7-a923-1d888e224b58@paulmck-laptop/
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Reviewed-by: Steven Rostedt <rostedt@goodmis.org>
Reviewed-by: K Prateek Nayak <kprateek.nayak@amd.com>
Tested-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
include/linux/sched.h | 2 ++
kernel/rcu/tree_stall.h | 33 +++++++++++++++++++++++++++------
kernel/sched/core.c | 13 +++++++++++++
3 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 1c93da75226e3..ba7c7bb13b6e9 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2527,3 +2527,5 @@ extern void migrate_enable(void);
DEFINE_LOCK_GUARD_0(migrate, migrate_disable(), migrate_enable())
#endif
+
+struct task_struct *cpu_curr_snapshot_rcu(int cpu);
diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h
index 803a56af3259f..08699652f51c2 100644
--- a/kernel/rcu/tree_stall.h
+++ b/kernel/rcu/tree_stall.h
@@ -309,6 +309,9 @@ struct rcu_stall_chk_rdr {
bool on_blkd_list;
bool rcu_rdr_running;
int rcu_rdr_boosted;
+ int rcu_rdr_cpu;
+ int rcu_rdr_cpu_offline;
+ struct task_struct *rcu_rdr_cpu_task;
};
/*
@@ -317,8 +320,10 @@ struct rcu_stall_chk_rdr {
*/
static int check_slow_task(struct task_struct *t, void *arg)
{
+ int cpu;
struct rcu_node *rnp;
struct rcu_stall_chk_rdr *rscrp = arg;
+ struct task_struct *th;
if (task_curr(t))
return -EBUSY; // It is running, so decline to inspect it.
@@ -327,6 +332,13 @@ static int check_slow_task(struct task_struct *t, void *arg)
rscrp->on_blkd_list = !list_empty(&t->rcu_node_entry);
rscrp->rcu_rdr_running = task_curr(t);
rscrp->rcu_rdr_boosted = 0;
+ rscrp->rcu_rdr_cpu = cpu = task_cpu(t);
+ rscrp->rcu_rdr_cpu_offline = cpu_is_offline(cpu);
+ lockdep_assert_irqs_disabled();
+ rscrp->rcu_rdr_cpu_task = th = cpu_curr_snapshot_rcu(cpu);
+ // There is no need for get_task_struct(t) because we are in
+ // an implicit RCU read-side critical section due to interrupts
+ // being disabled.
if (rscrp->on_blkd_list) {
rnp = READ_ONCE(t->rcu_blocked_node);
raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
@@ -352,6 +364,7 @@ static int rcu_print_task_stall(struct rcu_node *rnp, unsigned long flags)
int ndetected = 0;
struct rcu_stall_chk_rdr rscr;
struct task_struct *t;
+ struct task_struct *tc;
struct task_struct *ts[8];
lockdep_assert_irqs_disabled();
@@ -364,7 +377,9 @@ static int rcu_print_task_stall(struct rcu_node *rnp, unsigned long flags)
t = list_entry(rnp->gp_tasks->prev,
struct task_struct, rcu_node_entry);
list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) {
- get_task_struct(t);
+ // There is no need for get_task_struct(t) because we
+ // are in an implicit RCU read-side critical section due
+ // to interrupts being disabled.
ts[i++] = t;
if (i >= ARRAY_SIZE(ts))
break;
@@ -372,19 +387,25 @@ static int rcu_print_task_stall(struct rcu_node *rnp, unsigned long flags)
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
while (i) {
t = ts[--i];
- if (task_call_func(t, check_slow_task, &rscr))
+ if (task_call_func(t, check_slow_task, &rscr)) {
pr_cont(" P%d", t->pid);
- else
- pr_cont(" P%d/%d:%c%c%c%c%c%c",
+ } else {
+ tc = rscr.rcu_rdr_cpu_task;
+ pr_cont(" P%d/%d:%c%c%c%c%c%c CPU %d%s %s (%d)%s",
t->pid, rscr.nesting,
".b"[rscr.rs.b.blocked],
".q"[rscr.rs.b.need_qs],
".e"[rscr.rs.b.exp_hint],
".l"[rscr.on_blkd_list],
".R"[rscr.rcu_rdr_running],
- ".B?"[rscr.rcu_rdr_boosted]);
+ ".B?"[rscr.rcu_rdr_boosted],
+ rscr.rcu_rdr_cpu,
+ rscr.rcu_rdr_cpu_offline ? "!" : "",
+ tc ? tc->comm : "???",
+ tc ? tc->pid : -1,
+ tc ? (is_idle_task(tc) ? " (idle!)" : "") : " (no task)");
+ }
lockdep_assert_irqs_disabled();
- put_task_struct(t);
ndetected++;
}
pr_cont("\n");
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index b5b205b1a6e22..98b45b2557e92 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4605,6 +4605,19 @@ struct task_struct *cpu_curr_snapshot(int cpu)
return t;
}
+/**
+ * cpu_curr_snapshot_rcu - Return the task currently running on CPU
+ * @cpu: CPU to query
+ *
+ * The caller must be within an RCU read-side critical section.
+ *
+ * Return: Pointer to task_struct of task running on @cpu.
+ */
+struct task_struct *cpu_curr_snapshot_rcu(int cpu)
+{
+ return rcu_dereference_all(cpu_curr(cpu));
+}
+
/**
* wake_up_process - Wake up a specific process
* @p: The process to be woken up.
--
2.40.1
next prev parent reply other threads:[~2026-11-09 22:19 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 22:19 [PATCH 0/8] Miscellaneous RCU updates for v7.5 Paul E. McKenney
2026-11-09 22:19 ` [PATCH 1/8] rcutorture: Enable RCU_BOOST where supported Paul E. McKenney
2026-11-09 22:19 ` [PATCH 2/8] rcutorture: Disable RCU priority boost testing on non-TREE03 scenarios Paul E. McKenney
2026-11-09 22:19 ` [PATCH 3/8] Documentation/litmus-tests: Add SRCU fastpath anchor-before-scan test Paul E. McKenney
2026-11-09 22:19 ` [PATCH 4/8] Documentation/litmus-tests: Add SRCU fastpath scan-before-anchor test Paul E. McKenney
2026-11-09 22:19 ` [PATCH 5/8] rcuscale: Add srcua scale type Paul E. McKenney
2026-11-09 22:19 ` [PATCH 6/8] rcuscale: Consolidate per-writer arrays into struct writer_state Paul E. McKenney
2026-11-09 22:19 ` Paul E. McKenney [this message]
2026-11-09 22:19 ` [PATCH 8/8] doc: Document additional RCU task-stall dump information Paul E. McKenney
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=20261109221939.2070171-7-paulmck@kernel.org \
--to=paulmck@kernel.org \
--cc=kernel-team@meta.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=rcu@vger.kernel.org \
--cc=rostedt@goodmis.org \
/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
all inboxes | Powered by JetHome®