Re: Mapping from preempted task to running task on same CPU
From: Paul E. McKenney
Date: Fri Oct 16 2026 - 12:42:38 EST
On Fri, Oct 16, 2026 at 09:25:00AM +0200, Peter Zijlstra wrote:
> On Fri, Oct 16, 2026 at 10:14:27AM +0530, K Prateek Nayak wrote:
>
> > > @@ -372,17 +381,24 @@ 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();
> >
> > ... since you are IRQs disabled, it should be fine to use it here.
> >
> > If you don't want to take the overhead of rq_lock() + barrier,
> > you can perhaps define a cpu_curr_snapshot_rcu() like:
> >
> > struct task_struct *cpu_curr_snapshot_rcu(int cpu)
> > {
> > return rcu_dereference_all(cpu_curr(cpu));
> > }
> >
> > in kernel/sched/core.c and expose it similar to
> > cpu_curr_snapshot().
> >
> > I'll let Peter comment if he hates it or not ;-)
>
> I would much rather do this than expose rq_lock().
Thank you both! Like this?
Thanx, Paul
------------------------------------------------------------------------
commit 1468f6acb32add422b8f4dcebc0d75e7fd4b95aa
Author: Paul E. McKenney <paulmck@xxxxxxxxxx>
Date: Mon Sep 28 13:35:01 2026 -0700
rcu: Print CPU that preempted stalled reader wants to run on
This commit uses the dirty trick of defining an rcu_cpu_curr() within
kernel/sched/core.c in order to allow the RCU CPU stall warning call out
the CPU that a task preempted within an RCU reader would like to run on.
In addition, print out the task (if any) currently running on that CPU,
and add 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) to locate any tasks that might be preventing the preempted RCU
reader task from running.
[ paulmck: Apply feedback from K Prateek Nayak and Peter Zijlstra. ]
Signed-off-by: Paul E. McKenney <paulmck@xxxxxxxxxx>
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 8b3d47a325cca..3606e5a2cec32 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2532,3 +2532,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..33e953bb40b33 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,10 @@ 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);
+ // Interrupt are disabled for RCU read-side critical section.
+ rscrp->rcu_rdr_cpu_task = th = cpu_curr_snapshot_rcu(cpu);
if (rscrp->on_blkd_list) {
rnp = READ_ONCE(t->rcu_blocked_node);
raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
@@ -352,6 +361,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();
@@ -372,17 +382,24 @@ 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++;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 559735f71b5dd..1fa63eee528c2 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -11320,3 +11320,10 @@ void sched_change_end(struct sched_change_ctx *ctx)
p->sched_class->prio_changed(rq, p, ctx->prio);
}
}
+
+// Not for mainline, but hopefully someone has a trick that gets this
+// job done.
+struct task_struct *cpu_curr_snapshot_rcu(int cpu)
+{
+ return rcu_dereference_all(cpu_curr(cpu));
+}