rcu/nocb: Fix shrinker race against callback enqueuer
authorFrederic Weisbecker <frederic@kernel.org>
Wed, 29 Mar 2023 16:02:01 +0000 (18:02 +0200)
committerPaul E. McKenney <paulmck@kernel.org>
Wed, 10 May 2023 00:26:58 +0000 (17:26 -0700)
commit7625926086765123251f765d91fc3a70617d334d
tree058a08e98dfd20475d655e183ebd6feae0ef1b46
parent5c83cedbaaad6dfe290e50658a204556ac5ac683
rcu/nocb: Fix shrinker race against callback enqueuer

The shrinker resets the lazy callbacks counter in order to trigger the
pending lazy queue flush though the rcuog kthread. The counter reset is
protected by the ->nocb_lock against concurrent accesses...except
for one of them. Here is a list of existing synchronized readers/writer:

1) The first lazy enqueuer (incrementing ->lazy_len to 1) does so under
   ->nocb_lock and ->nocb_bypass_lock.

2) The further lazy enqueuers (incrementing ->lazy_len above 1) do so
   under ->nocb_bypass_lock _only_.

3) The lazy flush checks and resets to 0 under ->nocb_lock and
->nocb_bypass_lock.

The shrinker protects its ->lazy_len reset against cases 1) and 3) but
not against 2). As such, setting ->lazy_len to 0 under the ->nocb_lock
may be cancelled right away by an overwrite from an enqueuer, leading
rcuog to ignore the flush.

To avoid that, use the proper bypass flush API which takes care of all
those details.

Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
kernel/rcu/tree_nocb.h