aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2008-07-18 11:27:28 -0400
committerThomas Gleixner <tglx@linutronix.de>2008-07-18 12:10:28 -0400
commitb8f8c3cf0a4ac0632ec3f0e15e9dc0c29de917af (patch)
tree183825db00f4e9252603a51a1be6f8874a963dbc /include/linux
parent857f3fd7a496ddf4329345af65a4a2b16dd25fe8 (diff)
nohz: prevent tick stop outside of the idle loop
Jack Ren and Eric Miao tracked down the following long standing problem in the NOHZ code: scheduler switch to idle task enable interrupts Window starts here ----> interrupt happens (does not set NEED_RESCHED) irq_exit() stops the tick ----> interrupt happens (does set NEED_RESCHED) return from schedule() cpu_idle(): preempt_disable(); Window ends here The interrupts can happen at any point inside the race window. The first interrupt stops the tick, the second one causes the scheduler to rerun and switch away from idle again and we end up with the tick disabled. The fact that it needs two interrupts where the first one does not set NEED_RESCHED and the second one does made the bug obscure and extremly hard to reproduce and analyse. Kudos to Jack and Eric. Solution: Limit the NOHZ functionality to the idle loop to make sure that we can not run into such a situation ever again. cpu_idle() { preempt_disable(); while(1) { tick_nohz_stop_sched_tick(1); <- tell NOHZ code that we are in the idle loop while (!need_resched()) halt(); tick_nohz_restart_sched_tick(); <- disables NOHZ mode preempt_enable_no_resched(); schedule(); preempt_disable(); } } In hindsight we should have done this forever, but ... /me grabs a large brown paperbag. Debugged-by: Jack Ren <jack.ren@marvell.com>, Debugged-by: eric miao <eric.y.miao@gmail.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/tick.h5
1 files changed, 3 insertions, 2 deletions
diff --git a/include/linux/tick.h b/include/linux/tick.h
index a881c652f7e9..d3c02695dc5d 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -49,6 +49,7 @@ struct tick_sched {
49 unsigned long check_clocks; 49 unsigned long check_clocks;
50 enum tick_nohz_mode nohz_mode; 50 enum tick_nohz_mode nohz_mode;
51 ktime_t idle_tick; 51 ktime_t idle_tick;
52 int inidle;
52 int tick_stopped; 53 int tick_stopped;
53 unsigned long idle_jiffies; 54 unsigned long idle_jiffies;
54 unsigned long idle_calls; 55 unsigned long idle_calls;
@@ -105,14 +106,14 @@ static inline int tick_check_oneshot_change(int allow_nohz) { return 0; }
105#endif /* !CONFIG_GENERIC_CLOCKEVENTS */ 106#endif /* !CONFIG_GENERIC_CLOCKEVENTS */
106 107
107# ifdef CONFIG_NO_HZ 108# ifdef CONFIG_NO_HZ
108extern void tick_nohz_stop_sched_tick(void); 109extern void tick_nohz_stop_sched_tick(int inidle);
109extern void tick_nohz_restart_sched_tick(void); 110extern void tick_nohz_restart_sched_tick(void);
110extern void tick_nohz_update_jiffies(void); 111extern void tick_nohz_update_jiffies(void);
111extern ktime_t tick_nohz_get_sleep_length(void); 112extern ktime_t tick_nohz_get_sleep_length(void);
112extern void tick_nohz_stop_idle(int cpu); 113extern void tick_nohz_stop_idle(int cpu);
113extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time); 114extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time);
114# else 115# else
115static inline void tick_nohz_stop_sched_tick(void) { } 116static inline void tick_nohz_stop_sched_tick(int inidle) { }
116static inline void tick_nohz_restart_sched_tick(void) { } 117static inline void tick_nohz_restart_sched_tick(void) { }
117static inline void tick_nohz_update_jiffies(void) { } 118static inline void tick_nohz_update_jiffies(void) { }
118static inline ktime_t tick_nohz_get_sleep_length(void) 119static inline ktime_t tick_nohz_get_sleep_length(void)