summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2018-03-15 18:05:50 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2018-04-05 13:01:14 -0400
commit2aaf709a518d26563b80fd7a42379d7aa7ffed4a (patch)
treef58fc91024e9511ffed664ba51dc2f246e09ad1e
parent0e7767687fdabfc58d5046e7488632bf2ecd4d0c (diff)
sched: idle: Do not stop the tick upfront in the idle loop
Push the decision whether or not to stop the tick somewhat deeper into the idle loop. Stopping the tick upfront leads to unpleasant outcomes in case the idle governor doesn't agree with the nohz code on the duration of the upcoming idle period. Specifically, if the tick has been stopped and the idle governor predicts short idle, the situation is bad regardless of whether or not the prediction is accurate. If it is accurate, the tick has been stopped unnecessarily which means excessive overhead. If it is not accurate, the CPU is likely to spend too much time in the (shallow, because short idle has been predicted) idle state selected by the governor [1]. As the first step towards addressing this problem, change the code to make the tick stopping decision inside of the loop in do_idle(). In particular, do not stop the tick in the cpu_idle_poll() code path. Also don't do that in tick_nohz_irq_exit() which doesn't really have enough information on whether or not to stop the tick. Link: https://marc.info/?l=linux-pm&m=150116085925208&w=2 # [1] Link: https://tu-dresden.de/zih/forschung/ressourcen/dateien/projekte/haec/powernightmares.pdf Suggested-by: Frederic Weisbecker <frederic@kernel.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Frederic Weisbecker <frederic@kernel.org> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
-rw-r--r--include/linux/tick.h2
-rw-r--r--kernel/sched/idle.c9
-rw-r--r--kernel/time/tick-sched.c26
3 files changed, 26 insertions, 11 deletions
diff --git a/include/linux/tick.h b/include/linux/tick.h
index 1d253df9ea3c..fccebfba167e 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -116,6 +116,7 @@ extern bool tick_nohz_enabled;
116extern bool tick_nohz_tick_stopped(void); 116extern bool tick_nohz_tick_stopped(void);
117extern bool tick_nohz_tick_stopped_cpu(int cpu); 117extern bool tick_nohz_tick_stopped_cpu(int cpu);
118extern void tick_nohz_idle_stop_tick(void); 118extern void tick_nohz_idle_stop_tick(void);
119extern void tick_nohz_idle_restart_tick(void);
119extern void tick_nohz_idle_enter(void); 120extern void tick_nohz_idle_enter(void);
120extern void tick_nohz_idle_exit(void); 121extern void tick_nohz_idle_exit(void);
121extern void tick_nohz_irq_exit(void); 122extern void tick_nohz_irq_exit(void);
@@ -137,6 +138,7 @@ static inline void tick_nohz_idle_stop_tick_protected(void)
137static inline int tick_nohz_tick_stopped(void) { return 0; } 138static inline int tick_nohz_tick_stopped(void) { return 0; }
138static inline int tick_nohz_tick_stopped_cpu(int cpu) { return 0; } 139static inline int tick_nohz_tick_stopped_cpu(int cpu) { return 0; }
139static inline void tick_nohz_idle_stop_tick(void) { } 140static inline void tick_nohz_idle_stop_tick(void) { }
141static inline void tick_nohz_idle_restart_tick(void) { }
140static inline void tick_nohz_idle_enter(void) { } 142static inline void tick_nohz_idle_enter(void) { }
141static inline void tick_nohz_idle_exit(void) { } 143static inline void tick_nohz_idle_exit(void) { }
142 144
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index c0bc111878e6..3777e83c0b5a 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -216,13 +216,13 @@ static void do_idle(void)
216 216
217 __current_set_polling(); 217 __current_set_polling();
218 tick_nohz_idle_enter(); 218 tick_nohz_idle_enter();
219 tick_nohz_idle_stop_tick_protected();
220 219
221 while (!need_resched()) { 220 while (!need_resched()) {
222 check_pgt_cache(); 221 check_pgt_cache();
223 rmb(); 222 rmb();
224 223
225 if (cpu_is_offline(cpu)) { 224 if (cpu_is_offline(cpu)) {
225 tick_nohz_idle_stop_tick_protected();
226 cpuhp_report_idle_dead(); 226 cpuhp_report_idle_dead();
227 arch_cpu_idle_dead(); 227 arch_cpu_idle_dead();
228 } 228 }
@@ -236,10 +236,13 @@ static void do_idle(void)
236 * broadcast device expired for us, we don't want to go deep 236 * broadcast device expired for us, we don't want to go deep
237 * idle as we know that the IPI is going to arrive right away. 237 * idle as we know that the IPI is going to arrive right away.
238 */ 238 */
239 if (cpu_idle_force_poll || tick_check_broadcast_expired()) 239 if (cpu_idle_force_poll || tick_check_broadcast_expired()) {
240 tick_nohz_idle_restart_tick();
240 cpu_idle_poll(); 241 cpu_idle_poll();
241 else 242 } else {
243 tick_nohz_idle_stop_tick();
242 cpuidle_idle_call(); 244 cpuidle_idle_call();
245 }
243 arch_cpu_idle_exit(); 246 arch_cpu_idle_exit();
244 } 247 }
245 248
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 678349aec483..f5d37788ea85 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -960,12 +960,10 @@ void tick_nohz_irq_exit(void)
960{ 960{
961 struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched); 961 struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
962 962
963 if (ts->inidle) { 963 if (ts->inidle)
964 tick_nohz_start_idle(ts); 964 tick_nohz_start_idle(ts);
965 __tick_nohz_idle_stop_tick(ts); 965 else
966 } else {
967 tick_nohz_full_update_tick(ts); 966 tick_nohz_full_update_tick(ts);
968 }
969} 967}
970 968
971/** 969/**
@@ -1026,6 +1024,20 @@ static void tick_nohz_account_idle_ticks(struct tick_sched *ts)
1026#endif 1024#endif
1027} 1025}
1028 1026
1027static void __tick_nohz_idle_restart_tick(struct tick_sched *ts, ktime_t now)
1028{
1029 tick_nohz_restart_sched_tick(ts, now);
1030 tick_nohz_account_idle_ticks(ts);
1031}
1032
1033void tick_nohz_idle_restart_tick(void)
1034{
1035 struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
1036
1037 if (ts->tick_stopped)
1038 __tick_nohz_idle_restart_tick(ts, ktime_get());
1039}
1040
1029/** 1041/**
1030 * tick_nohz_idle_exit - restart the idle tick from the idle task 1042 * tick_nohz_idle_exit - restart the idle tick from the idle task
1031 * 1043 *
@@ -1050,10 +1062,8 @@ void tick_nohz_idle_exit(void)
1050 if (ts->idle_active) 1062 if (ts->idle_active)
1051 tick_nohz_stop_idle(ts, now); 1063 tick_nohz_stop_idle(ts, now);
1052 1064
1053 if (ts->tick_stopped) { 1065 if (ts->tick_stopped)
1054 tick_nohz_restart_sched_tick(ts, now); 1066 __tick_nohz_idle_restart_tick(ts, now);
1055 tick_nohz_account_idle_ticks(ts);
1056 }
1057 1067
1058 local_irq_enable(); 1068 local_irq_enable();
1059} 1069}