aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-03-21 19:08:50 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-03-22 21:12:14 -0400
commitb7eaf1aab9f8bd2e49fceed77ebc66c1b5800718 (patch)
tree317ca435926987aae530eadcd7b9cc4c12ab1893
parentcba1dfb57b94c234728b689d9b00d4267fa1a879 (diff)
cpufreq: schedutil: Avoid reducing frequency of busy CPUs prematurely
The way the schedutil governor uses the PELT metric causes it to underestimate the CPU utilization in some cases. That can be easily demonstrated by running kernel compilation on a Sandy Bridge Intel processor, running turbostat in parallel with it and looking at the values written to the MSR_IA32_PERF_CTL register. Namely, the expected result would be that when all CPUs were 100% busy, all of them would be requested to run in the maximum P-state, but observation shows that this clearly isn't the case. The CPUs run in the maximum P-state for a while and then are requested to run slower and go back to the maximum P-state after a while again. That causes the actual frequency of the processor to visibly oscillate below the sustainable maximum in a jittery fashion which clearly is not desirable. That has been attributed to CPU utilization metric updates on task migration that cause the total utilization value for the CPU to be reduced by the utilization of the migrated task. If that happens, the schedutil governor may see a CPU utilization reduction and will attempt to reduce the CPU frequency accordingly right away. That may be premature, though, for example if the system is generally busy and there are other runnable tasks waiting to be run on that CPU already. This is unlikely to be an issue on systems where cpufreq policies are shared between multiple CPUs, because in those cases the policy utilization is computed as the maximum of the CPU utilization values over the whole policy and if that turns out to be low, reducing the frequency for the policy most likely is a good idea anyway. On systems with one CPU per policy, however, it may affect performance adversely and even lead to increased energy consumption in some cases. On those systems it may be addressed by taking another utilization metric into consideration, like whether or not the CPU whose frequency is about to be reduced has been idle recently, because if that's not the case, the CPU is likely to be busy in the near future and its frequency should not be reduced. To that end, use the counter of idle calls in the timekeeping code. Namely, make the schedutil governor look at that counter for the current CPU every time before its frequency is about to be reduced. If the counter has not changed since the previous iteration of the governor computations for that CPU, the CPU has been busy for all that time and its frequency should not be decreased, so if the new frequency would be lower than the one set previously, the governor will skip the frequency update. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Joel Fernandes <joelaf@google.com>
-rw-r--r--include/linux/tick.h1
-rw-r--r--kernel/sched/cpufreq_schedutil.c27
-rw-r--r--kernel/time/tick-sched.c12
3 files changed, 40 insertions, 0 deletions
diff --git a/include/linux/tick.h b/include/linux/tick.h
index a04fea19676f..fe01e68bf520 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -117,6 +117,7 @@ extern void tick_nohz_idle_enter(void);
117extern void tick_nohz_idle_exit(void); 117extern void tick_nohz_idle_exit(void);
118extern void tick_nohz_irq_exit(void); 118extern void tick_nohz_irq_exit(void);
119extern ktime_t tick_nohz_get_sleep_length(void); 119extern ktime_t tick_nohz_get_sleep_length(void);
120extern unsigned long tick_nohz_get_idle_calls(void);
120extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time); 121extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time);
121extern u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time); 122extern u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time);
122#else /* !CONFIG_NO_HZ_COMMON */ 123#else /* !CONFIG_NO_HZ_COMMON */
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index f5ffe241812e..c1ffb5dc8af6 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -61,6 +61,11 @@ struct sugov_cpu {
61 unsigned long util; 61 unsigned long util;
62 unsigned long max; 62 unsigned long max;
63 unsigned int flags; 63 unsigned int flags;
64
65 /* The field below is for single-CPU policies only. */
66#ifdef CONFIG_NO_HZ_COMMON
67 unsigned long saved_idle_calls;
68#endif
64}; 69};
65 70
66static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu); 71static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu);
@@ -192,6 +197,19 @@ static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, unsigned long *util,
192 sg_cpu->iowait_boost >>= 1; 197 sg_cpu->iowait_boost >>= 1;
193} 198}
194 199
200#ifdef CONFIG_NO_HZ_COMMON
201static bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu)
202{
203 unsigned long idle_calls = tick_nohz_get_idle_calls();
204 bool ret = idle_calls == sg_cpu->saved_idle_calls;
205
206 sg_cpu->saved_idle_calls = idle_calls;
207 return ret;
208}
209#else
210static inline bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu) { return false; }
211#endif /* CONFIG_NO_HZ_COMMON */
212
195static void sugov_update_single(struct update_util_data *hook, u64 time, 213static void sugov_update_single(struct update_util_data *hook, u64 time,
196 unsigned int flags) 214 unsigned int flags)
197{ 215{
@@ -200,6 +218,7 @@ static void sugov_update_single(struct update_util_data *hook, u64 time,
200 struct cpufreq_policy *policy = sg_policy->policy; 218 struct cpufreq_policy *policy = sg_policy->policy;
201 unsigned long util, max; 219 unsigned long util, max;
202 unsigned int next_f; 220 unsigned int next_f;
221 bool busy;
203 222
204 sugov_set_iowait_boost(sg_cpu, time, flags); 223 sugov_set_iowait_boost(sg_cpu, time, flags);
205 sg_cpu->last_update = time; 224 sg_cpu->last_update = time;
@@ -207,12 +226,20 @@ static void sugov_update_single(struct update_util_data *hook, u64 time,
207 if (!sugov_should_update_freq(sg_policy, time)) 226 if (!sugov_should_update_freq(sg_policy, time))
208 return; 227 return;
209 228
229 busy = sugov_cpu_is_busy(sg_cpu);
230
210 if (flags & SCHED_CPUFREQ_RT_DL) { 231 if (flags & SCHED_CPUFREQ_RT_DL) {
211 next_f = policy->cpuinfo.max_freq; 232 next_f = policy->cpuinfo.max_freq;
212 } else { 233 } else {
213 sugov_get_util(&util, &max); 234 sugov_get_util(&util, &max);
214 sugov_iowait_boost(sg_cpu, &util, &max); 235 sugov_iowait_boost(sg_cpu, &util, &max);
215 next_f = get_next_freq(sg_policy, util, max); 236 next_f = get_next_freq(sg_policy, util, max);
237 /*
238 * Do not reduce the frequency if the CPU has not been idle
239 * recently, as the reduction is likely to be premature then.
240 */
241 if (busy && next_f < sg_policy->next_freq)
242 next_f = sg_policy->next_freq;
216 } 243 }
217 sugov_update_commit(sg_policy, time, next_f); 244 sugov_update_commit(sg_policy, time, next_f);
218} 245}
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 7fe53be86077..64c97fc130c4 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -993,6 +993,18 @@ ktime_t tick_nohz_get_sleep_length(void)
993 return ts->sleep_length; 993 return ts->sleep_length;
994} 994}
995 995
996/**
997 * tick_nohz_get_idle_calls - return the current idle calls counter value
998 *
999 * Called from the schedutil frequency scaling governor in scheduler context.
1000 */
1001unsigned long tick_nohz_get_idle_calls(void)
1002{
1003 struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
1004
1005 return ts->idle_calls;
1006}
1007
996static void tick_nohz_account_idle_ticks(struct tick_sched *ts) 1008static void tick_nohz_account_idle_ticks(struct tick_sched *ts)
997{ 1009{
998#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE 1010#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE