Re: [PATCH RFC v2 15/15] rcu-tasks: Kick running holdouts through the scheduler
From: "Paul E. McKenney" <paulmck@kernel.org>
Date: 2026-09-11 18:46:24
Also in:
bpf, linux-arm-kernel, lkml, rcu, xen-devel
On Fri, Sep 11, 2026 at 02:08:53PM +0000, Josef Bacik wrote:
A holdout that is running on a CPU with nothing else runnable is only preempted if the tick acts on rcu_request_urgent_qs_task()'s flag, and there may be no tick. Now that a preemption outside a trampoline is a quiescent state, have check_holdout_task() call resched_cpu() on a running holdout as well, so the scheduler IPIs it and it goes through __schedule() and reports (or, if it is inside a trampoline, does not report) its own state with purely local ordering. Nothing reads a running task's rcu_tramp_nesting remotely. Only under CONFIG_RCU_TASKS_PREEMPT_QS; other architectures are unchanged. Suggested-by: Paul E. McKenney <paulmck@kernel.org> Assisted-by: LLM Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Much better! But please see below. Thanx, Paul
quoted hunk ↗ jump to hunk
--- kernel/rcu/tasks.h | 11 +++++++++++ 1 file changed, 11 insertions(+)diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h index ba432bd922e2..02d2592ab7a3 100644 --- a/kernel/rcu/tasks.h +++ b/kernel/rcu/tasks.h@@ -1038,8 +1038,18 @@ static bool rcu_tasks_preempted_qs(struct task_struct *t) { return task_call_func(t, rcu_tasks_switched_out_clean, NULL); } + +/* Make a running holdout pass through __schedule() soon, tick or no tick. */ +static void rcu_tasks_kick_running(struct task_struct *t) +{ + int cpu = task_cpu(t); + + if (task_curr(t) && cpu_online(cpu)) + resched_cpu(cpu); +} #else static bool rcu_tasks_preempted_qs(struct task_struct *t) { return false; } +static void rcu_tasks_kick_running(struct task_struct *t) { } #endif /* Per-task initial processing. */@@ -1219,6 +1229,7 @@ static void check_holdout_task(struct task_struct *t, return; } rcu_request_urgent_qs_task(t); + rcu_tasks_kick_running(t);
Something I learned the hard way, though Google paid most of the tuition for this lesson: There can be a *lot* of tasks on large systems, as in hundreds of thousands of them. If these tasks are consuming CPU in very short bursts, we could easily repeatedly invoke resched_cpu() on the same CPU, and all invocations other than the last one are redundant. I instead suggest doing something similar to force_qs_rnp(), where a cpumask is accumulated and at the end resched_cpu() is invoked for each CPU with a bit set in that mask. Of course, if a CPU appears twice while traversing the tasks list, then each of those tasks did a context switch, which would have reported a Tasks RCU quiescent state. Unless those tasks happened to have non-zero ->rcu_tramp_nesting at that time. Which suggests that each task have a pair of counters. Or that rcu_tasks_trampoline_exit() should check for zero ->rcu_tramp_nesting, and report a quiescent state at that point. Either way, if a CPU appears only once in the tasks_list traversal, hitting it with resched_cpu() makes sense. Though we might also need to suppress calls to resched_cpu() for grace periods that are (say) less than one second old. Thoughts? Thanx, Paul
if (!needreport)
return;
if (*firstreport) {
--
2.55.0