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

tick/nohz: Only check for RCU deferred wakeup on user/guest entry when needed

Checking for and processing RCU-nocb deferred wakeup upon user/guest
entry is only relevant when nohz_full runs on the local CPU, otherwise
the periodic tick should take care of it.

Make sure we don't needlessly pollute these fast-paths as a -3%
performance regression on a will-it-scale.per_process_ops has been
reported so far.

Fixes: 47b8ff194c1f (entry: Explicitly flush pending rcuog wakeup before last rescheduling point)
Fixes: 4ae7dc97f726 (entry/kvm: Explicitly flush pending rcuog wakeup before last rescheduling point)
Reported-by: kernel test robot <oliver.sang@intel.com>
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/20210527113441.465489-1-frederic@kernel.org

authored by

Frederic Weisbecker and committed by
Peter Zijlstra
f268c373 02da26ad

+13 -3
+2 -1
include/linux/entry-kvm.h
··· 3 3 #define __LINUX_ENTRYKVM_H 4 4 5 5 #include <linux/entry-common.h> 6 + #include <linux/tick.h> 6 7 7 8 /* Transfer to guest mode work */ 8 9 #ifdef CONFIG_KVM_XFER_TO_GUEST_WORK ··· 58 57 static inline void xfer_to_guest_mode_prepare(void) 59 58 { 60 59 lockdep_assert_irqs_disabled(); 61 - rcu_nocb_flush_deferred_wakeup(); 60 + tick_nohz_user_enter_prepare(); 62 61 } 63 62 64 63 /**
+7
include/linux/tick.h
··· 11 11 #include <linux/context_tracking_state.h> 12 12 #include <linux/cpumask.h> 13 13 #include <linux/sched.h> 14 + #include <linux/rcupdate.h> 14 15 15 16 #ifdef CONFIG_GENERIC_CLOCKEVENTS 16 17 extern void __init tick_init(void); ··· 299 298 { 300 299 if (tick_nohz_full_enabled()) 301 300 __tick_nohz_task_switch(); 301 + } 302 + 303 + static inline void tick_nohz_user_enter_prepare(void) 304 + { 305 + if (tick_nohz_full_cpu(smp_processor_id())) 306 + rcu_nocb_flush_deferred_wakeup(); 302 307 } 303 308 304 309 #endif
+3 -2
kernel/entry/common.c
··· 5 5 #include <linux/highmem.h> 6 6 #include <linux/livepatch.h> 7 7 #include <linux/audit.h> 8 + #include <linux/tick.h> 8 9 9 10 #include "common.h" 10 11 ··· 187 186 local_irq_disable_exit_to_user(); 188 187 189 188 /* Check if any of the above work has queued a deferred wakeup */ 190 - rcu_nocb_flush_deferred_wakeup(); 189 + tick_nohz_user_enter_prepare(); 191 190 192 191 ti_work = READ_ONCE(current_thread_info()->flags); 193 192 } ··· 203 202 lockdep_assert_irqs_disabled(); 204 203 205 204 /* Flush pending rcuog wakeup before the last need_resched() check */ 206 - rcu_nocb_flush_deferred_wakeup(); 205 + tick_nohz_user_enter_prepare(); 207 206 208 207 if (unlikely(ti_work & EXIT_TO_USER_MODE_WORK)) 209 208 ti_work = exit_to_user_mode_loop(regs, ti_work);
+1
kernel/time/tick-sched.c
··· 230 230 231 231 #ifdef CONFIG_NO_HZ_FULL 232 232 cpumask_var_t tick_nohz_full_mask; 233 + EXPORT_SYMBOL_GPL(tick_nohz_full_mask); 233 234 bool tick_nohz_full_running; 234 235 EXPORT_SYMBOL_GPL(tick_nohz_full_running); 235 236 static atomic_t tick_dep_mask;