Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rcu/nocb: Prepare for fine-grained deferred wakeup

Tuning the deferred wakeup level must be done from a safe wakeup
point. Currently those sites are:

* ->nocb_timer
* user/idle/guest entry
* CPU down
* softirq/rcuc

All of these sites perform the wake up for both RCU_NOCB_WAKE and
RCU_NOCB_WAKE_FORCE.

In order to merge ->nocb_timer and ->nocb_bypass_timer together, we plan
to add a new RCU_NOCB_WAKE_BYPASS that really should be deferred until
a timer fires so that we don't wake up the NOCB-gp kthread too early.

To prepare for that, this commit specifies the per-callsite wakeup
level/limit.

Cc: Josh Triplett <josh@joshtriplett.org>
Cc: Lai Jiangshan <jiangshanlai@gmail.com>
Cc: Joel Fernandes <joel@joelfernandes.org>
Cc: Neeraj Upadhyay <neeraju@codeaurora.org>
Cc: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
[ paulmck: Fix non-NOCB rcu_nocb_need_deferred_wakeup() definition. ]
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>

authored by

Frederic Weisbecker and committed by
Paul E. McKenney
87090516 f9fc166b

+11 -10
+1 -1
kernel/rcu/tree.c
··· 3911 3911 check_cpu_stall(rdp); 3912 3912 3913 3913 /* Does this CPU need a deferred NOCB wakeup? */ 3914 - if (rcu_nocb_need_deferred_wakeup(rdp)) 3914 + if (rcu_nocb_need_deferred_wakeup(rdp, RCU_NOCB_WAKE)) 3915 3915 return 1; 3916 3916 3917 3917 /* Is this a nohz_full CPU in userspace or idle? (Ignore RCU if so.) */
+1 -1
kernel/rcu/tree.h
··· 433 433 bool *was_alldone, unsigned long flags); 434 434 static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_empty, 435 435 unsigned long flags); 436 - static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp); 436 + static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp, int level); 437 437 static bool do_nocb_deferred_wakeup(struct rcu_data *rdp); 438 438 static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp); 439 439 static void rcu_spawn_cpu_nocb_kthread(int cpu);
+9 -8
kernel/rcu/tree_plugin.h
··· 2352 2352 } 2353 2353 2354 2354 /* Is a deferred wakeup of rcu_nocb_kthread() required? */ 2355 - static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp) 2355 + static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp, int level) 2356 2356 { 2357 - return READ_ONCE(rdp->nocb_defer_wakeup) > RCU_NOCB_WAKE_NOT; 2357 + return READ_ONCE(rdp->nocb_defer_wakeup) >= level; 2358 2358 } 2359 2359 2360 2360 /* Do a deferred wakeup of rcu_nocb_kthread(). */ 2361 - static bool do_nocb_deferred_wakeup_common(struct rcu_data *rdp) 2361 + static bool do_nocb_deferred_wakeup_common(struct rcu_data *rdp, 2362 + int level) 2362 2363 { 2363 2364 unsigned long flags; 2364 2365 int ndw; ··· 2368 2367 2369 2368 raw_spin_lock_irqsave(&rdp_gp->nocb_gp_lock, flags); 2370 2369 2371 - if (!rcu_nocb_need_deferred_wakeup(rdp_gp)) { 2370 + if (!rcu_nocb_need_deferred_wakeup(rdp_gp, level)) { 2372 2371 raw_spin_unlock_irqrestore(&rdp_gp->nocb_gp_lock, flags); 2373 2372 return false; 2374 2373 } ··· 2385 2384 { 2386 2385 struct rcu_data *rdp = from_timer(rdp, t, nocb_timer); 2387 2386 2388 - do_nocb_deferred_wakeup_common(rdp); 2387 + do_nocb_deferred_wakeup_common(rdp, RCU_NOCB_WAKE); 2389 2388 } 2390 2389 2391 2390 /* ··· 2398 2397 if (!rdp->nocb_gp_rdp) 2399 2398 return false; 2400 2399 2401 - if (rcu_nocb_need_deferred_wakeup(rdp->nocb_gp_rdp)) 2402 - return do_nocb_deferred_wakeup_common(rdp); 2400 + if (rcu_nocb_need_deferred_wakeup(rdp->nocb_gp_rdp, RCU_NOCB_WAKE)) 2401 + return do_nocb_deferred_wakeup_common(rdp, RCU_NOCB_WAKE); 2403 2402 return false; 2404 2403 } 2405 2404 ··· 2940 2939 { 2941 2940 } 2942 2941 2943 - static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp) 2942 + static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp, int level) 2944 2943 { 2945 2944 return false; 2946 2945 }