summaryrefslogtreecommitdiffstats
path: root/kernel/rcu/tree_nocb.h
diff options
context:
space:
mode:
authorFrederic Weisbecker <frederic@kernel.org>2023-03-29 18:02:01 +0200
committerPaul E. McKenney <paulmck@kernel.org>2023-05-09 17:26:58 -0700
commit7625926086765123251f765d91fc3a70617d334d (patch)
tree058a08e98dfd20475d655e183ebd6feae0ef1b46 /kernel/rcu/tree_nocb.h
parent5c83cedbaaad6dfe290e50658a204556ac5ac683 (diff)
downloadlinux-7625926086765123251f765d91fc3a70617d334d.tar.gz
linux-7625926086765123251f765d91fc3a70617d334d.tar.bz2
linux-7625926086765123251f765d91fc3a70617d334d.zip
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>
Diffstat (limited to 'kernel/rcu/tree_nocb.h')
-rw-r--r--kernel/rcu/tree_nocb.h2
1 files changed, 1 insertions, 1 deletions
diff --git a/kernel/rcu/tree_nocb.h b/kernel/rcu/tree_nocb.h
index 1a86883902ce..c321fce2af8e 100644
--- a/kernel/rcu/tree_nocb.h
+++ b/kernel/rcu/tree_nocb.h
@@ -1364,7 +1364,7 @@ lazy_rcu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
continue;
rcu_nocb_lock_irqsave(rdp, flags);
- WRITE_ONCE(rdp->lazy_len, 0);
+ WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, jiffies, false));
rcu_nocb_unlock_irqrestore(rdp, flags);
wake_nocb_gp(rdp, false);
sc->nr_to_scan -= _count;