rcu-tasks: Add trc_inspect_reader() checks for exiting critical section
authorPaul E. McKenney <paulmck@kernel.org>
Wed, 28 Jul 2021 18:32:28 +0000 (11:32 -0700)
committerPaul E. McKenney <paulmck@kernel.org>
Wed, 15 Sep 2021 18:37:50 +0000 (11:37 -0700)
Currently, trc_inspect_reader() treats a task exiting its RCU Tasks
Trace read-side critical section the same as being within that critical
section.  However, this can fail because that task might have already
checked its .need_qs field, which means that it might never decrement
the all-important trc_n_readers_need_end counter.  Of course, for that
to happen, the task would need to never again execute an RCU Tasks Trace
read-side critical section, but this really could happen if the system's
last trampoline was removed.  Note that exit from such a critical section
cannot be treated as a quiescent state due to the possibility of nested
critical sections.  This means that if trc_inspect_reader() sees a
negative nesting value, it must set up to try again later.

This commit therefore ignores tasks that are exiting their RCU Tasks
Trace read-side critical sections so that they will be rechecked later.

[ paulmck: Apply feedback from Neeraj Upadhyay and Boqun Feng. ]

Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
kernel/rcu/tasks.h

index c9d8583..8387e70 100644 (file)
@@ -923,7 +923,7 @@ reset_ipi:
 static bool trc_inspect_reader(struct task_struct *t, void *arg)
 {
        int cpu = task_cpu(t);
-       bool in_qs = false;
+       int nesting;
        bool ofl = cpu_is_offline(cpu);
 
        if (task_curr(t)) {
@@ -943,18 +943,18 @@ static bool trc_inspect_reader(struct task_struct *t, void *arg)
                n_heavy_reader_updates++;
                if (ofl)
                        n_heavy_reader_ofl_updates++;
-               in_qs = true;
+               nesting = 0;
        } else {
                // The task is not running, so C-language access is safe.
-               in_qs = likely(!t->trc_reader_nesting);
+               nesting = t->trc_reader_nesting;
        }
 
-       // Mark as checked so that the grace-period kthread will
-       // remove it from the holdout list.
-       t->trc_reader_checked = true;
-
-       if (in_qs)
-               return true;  // Already in quiescent state, done!!!
+       // If not exiting a read-side critical section, mark as checked
+       // so that the grace-period kthread will remove it from the
+       // holdout list.
+       t->trc_reader_checked = nesting >= 0;
+       if (nesting <= 0)
+               return !nesting;  // If in QS, done, otherwise try again later.
 
        // The task is in a read-side critical section, so set up its
        // state so that it will awaken the grace-period kthread upon exit