aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel
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 /arch/x86/kernel
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 'arch/x86/kernel')
-rw-r--r--arch/x86/kernel/process_32.c2
-rw-r--r--arch/x86/kernel/process_64.c2
2 files changed, 2 insertions, 2 deletions
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index f8476dfbb60d..1f5fa1cf16dd 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -166,7 +166,7 @@ void cpu_idle(void)
166 166
167 /* endless idle loop with no priority at all */ 167 /* endless idle loop with no priority at all */
168 while (1) { 168 while (1) {
169 tick_nohz_stop_sched_tick(); 169 tick_nohz_stop_sched_tick(1);
170 while (!need_resched()) { 170 while (!need_resched()) {
171 void (*idle)(void); 171 void (*idle)(void);
172 172
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index e2319f39988b..c0a5c2a687e6 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -148,7 +148,7 @@ void cpu_idle(void)
148 current_thread_info()->status |= TS_POLLING; 148 current_thread_info()->status |= TS_POLLING;
149 /* endless idle loop with no priority at all */ 149 /* endless idle loop with no priority at all */
150 while (1) { 150 while (1) {
151 tick_nohz_stop_sched_tick(); 151 tick_nohz_stop_sched_tick(1);
152 while (!need_resched()) { 152 while (!need_resched()) {
153 void (*idle)(void); 153 void (*idle)(void);
154 154