It is assumed that rcu won't be used once we switch to ticklessmode and until we restart the tick. However this is not alwaystrue, as in x86-64 where we dereference the idle notifiers afterthe tick is stopped.

To prepare for fixing this, add two new APIs:tick_nohz_idle_enter_norcu() and tick_nohz_idle_exit_norcu().

If no use of RCU is made in the idle loop betweentick_nohz_enter_idle() and tick_nohz_exit_idle() calls, the archmust instead call the new *_norcu() version such that the arch doesn'tneed to call rcu_idle_enter() and rcu_idle_exit().

Otherwise the arch must call tick_nohz_enter_idle() andtick_nohz_exit_idle() and also call explicitly:

- rcu_idle_enter() after its last use of RCU before the CPU is putto sleep.- rcu_idle_exit() before the first use of RCU after the CPU is wokenup.

# ifdef CONFIG_NO_HZ-extern void tick_nohz_idle_enter(void);+extern void __tick_nohz_idle_enter(void);+static inline void tick_nohz_idle_enter(void)+{+ local_irq_disable();+ __tick_nohz_idle_enter();+ local_irq_enable();+} extern void tick_nohz_idle_exit(void);++/*+ * Call this pair of function if the arch doesn't make any use+ * of RCU in-between. You won't need to call rcu_idle_enter() and+ * rcu_idle_exit().+ * Otherwise you need to call tick_nohz_idle_enter() and tick_nohz_idle_exit()+ * and explicitly tell RCU about the window around the place the CPU enters low+ * power mode where no RCU use is made. This is done by calling rcu_idle_enter()+ * after the last use of RCU before the CPU is put to sleep and by calling+ * rcu_idle_exit() before the first use of RCU after the CPU woke up.+ */+static inline void tick_nohz_idle_enter_norcu(void)+{+ /*+ * Also call rcu_idle_enter() in the irq disabled section even+ * if it disables irq itself.+ * Just an optimization that prevents from an interrupt happening+ * between it and __tick_nohz_idle_enter() to lose time to help+ * completing a grace period while we could be in extended grace+ * period already.+ */+ local_irq_disable();+ __tick_nohz_idle_enter();+ rcu_idle_enter();+ local_irq_enable();+}+static inline void tick_nohz_idle_exit_norcu(void)+{+ rcu_idle_exit();+ tick_nohz_idle_exit();+} extern void tick_nohz_irq_exit(void); extern ktime_t tick_nohz_get_sleep_length(void); extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time); extern u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time); # else-static inline void tick_nohz_idle_enter(void)+static inline void tick_nohz_idle_enter(void) { }+static inline void tick_nohz_idle_exit(void) { }+static inline void tick_nohz_idle_enter_norcu(void) { rcu_idle_enter(); }-static inline void tick_nohz_idle_exit(void)+static inline void tick_nohz_idle_exit_norcu(void) { rcu_idle_exit(); }diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.cindex 52b7ace..360d028 100644--- a/kernel/time/tick-sched.c+++ b/kernel/time/tick-sched.c@@ -424,18 +424,22 @@ out: * * When the next event is more than a tick into the future, stop the idle tick * Called when we start the idle loop.- * This also enters into RCU extended quiescent state so that this CPU doesn't- * need anymore to be part of any global grace period completion. This way- * the tick can be stopped safely as we don't need to report quiescent states.+ *+ * If no use of RCU is made in the idle loop between+ * tick_nohz_idle_enter() and tick_nohz_idle_exit() calls, then+ * tick_nohz_idle_enter_norcu() should be called instead and the arch+ * doesn't need to call rcu_idle_enter() and rcu_idle_exit() explicitly.+ *+ * Otherwise the arch is responsible of calling:+ *+ * - rcu_idle_enter() after its last use of RCU before the CPU is put+ * to sleep.+ * - rcu_idle_exit() before the first use of RCU after the CPU is woken up. */-void tick_nohz_idle_enter(void)+void __tick_nohz_idle_enter(void) { struct tick_sched *ts;