rcu: Make idle entry report expedited quiescent states
authorPaul E. McKenney <paulmck@kernel.org>
Wed, 29 Sep 2021 18:09:34 +0000 (11:09 -0700)
committerPaul E. McKenney <paulmck@kernel.org>
Wed, 8 Dec 2021 00:22:22 +0000 (16:22 -0800)
In non-preemptible kernels, an unfortunately timed expedited grace period
can result in the rcu_exp_handler() IPI handler setting the rcu_data
structure's cpu_no_qs.b.exp field just as the target CPU enters idle.
There are situations in which this field will not be checked until after
that CPU exits idle.  The resulting grace-period latency does not qualify
as "expedited".

This commit therefore checks this field upon non-preemptible idle entry in
the rcu_preempt_deferred_qs() function.  It also qualifies the rcu_core()
preempt_count() check with IS_ENABLED(CONFIG_PREEMPT_COUNT) to prevent
false-positive quiescent states from count-free kernels.

Reported-by: Neeraj Upadhyay <neeraju@codeaurora.org>
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
kernel/rcu/tree.c
kernel/rcu/tree_plugin.h

index ef8d36f580fc358614dfbcd95264c454fda09feb..a8d1fe35f48204150d00fbbdb90b583122421a5c 100644 (file)
@@ -2714,7 +2714,7 @@ static __latent_entropy void rcu_core(void)
        WARN_ON_ONCE(!rdp->beenonline);
 
        /* Report any deferred quiescent states if preemption enabled. */
-       if (!(preempt_count() & PREEMPT_MASK)) {
+       if (IS_ENABLED(CONFIG_PREEMPT_COUNT) && (!(preempt_count() & PREEMPT_MASK))) {
                rcu_preempt_deferred_qs(current);
        } else if (rcu_preempt_need_deferred_qs(current)) {
                set_tsk_need_resched(current);
index e1a9fb96e0b917135670d21701513d05b4804388..8fb1612021a09e7e6c964f73b5fbeb780c7dd1a9 100644 (file)
@@ -927,7 +927,18 @@ static bool rcu_preempt_need_deferred_qs(struct task_struct *t)
 {
        return false;
 }
-static void rcu_preempt_deferred_qs(struct task_struct *t) { }
+
+// Except that we do need to respond to a request by an expedited grace
+// period for a quiescent state from this CPU.  Note that requests from
+// tasks are handled when removing the task from the blocked-tasks list
+// below.
+static void rcu_preempt_deferred_qs(struct task_struct *t)
+{
+       struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
+
+       if (rdp->cpu_no_qs.b.exp)
+               rcu_report_exp_rdp(rdp);
+}
 
 /*
  * Because there is no preemptible RCU, there can be no readers blocked,