[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH RFC v2 15/15] rcu-tasks: Kick running holdouts through the scheduler



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@xxxxxxxxxx>
> Assisted-by: LLM
> Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>

Much better!  But please see below.

                                                        Thanx, Paul

> ---
>  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
> 
> 



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.