cpufreq: interactive: Reschedule timer if min_freq is reduced
When a CPU is running at policy->min, slack timer will not be scheduled.
If policy->min is reduced later, current implementation doesn't
reschedule slack timer and thus could leave CPU at a higher
frequency indefinitely as long as the CPU is idle. This behavior is
undesirable from power perspective.
Change-Id: I40bfd7c93ad3fd06e3837dc48befdc07f29c78c8
[junjiew@codeaurora.org: Resolved merge conflicts.]
Signed-off-by: Junjie Wu <junjiew@codeaurora.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index bc466ba..f8293ee 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -53,6 +53,7 @@
u64 pol_hispeed_val_time; /* policy hispeed_validate_time */
u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */
u64 max_freq_hyst_start_time;
+ unsigned int min_freq;
struct rw_semaphore enable_sem;
bool reject_notification;
int governor_enabled;
@@ -193,7 +194,8 @@
usecs_to_jiffies(tunables->timer_rate));
}
-static void cpufreq_interactive_timer_resched(unsigned long cpu)
+static void cpufreq_interactive_timer_resched(unsigned long cpu,
+ bool slack_only)
{
struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
struct cpufreq_interactive_tunables *tunables =
@@ -202,16 +204,18 @@
unsigned long flags;
spin_lock_irqsave(&pcpu->load_lock, flags);
- pcpu->time_in_idle =
- get_cpu_idle_time(smp_processor_id(),
+ expires = round_to_nw_start(pcpu->last_evaluated_jiffy, tunables);
+ if (!slack_only) {
+ pcpu->time_in_idle =
+ get_cpu_idle_time(smp_processor_id(),
&pcpu->time_in_idle_timestamp,
tunables->io_is_busy);
- pcpu->cputime_speedadj = 0;
- pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
- expires = round_to_nw_start(pcpu->last_evaluated_jiffy, tunables);
- del_timer(&pcpu->cpu_timer);
- pcpu->cpu_timer.expires = expires;
- add_timer_on(&pcpu->cpu_timer, cpu);
+ pcpu->cputime_speedadj = 0;
+ pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
+ del_timer(&pcpu->cpu_timer);
+ pcpu->cpu_timer.expires = expires;
+ add_timer_on(&pcpu->cpu_timer, cpu);
+ }
if (tunables->timer_slack_val >= 0 &&
pcpu->target_freq > pcpu->policy->min) {
@@ -576,7 +580,7 @@
rearm:
if (!timer_pending(&pcpu->cpu_timer))
- cpufreq_interactive_timer_resched(data);
+ cpufreq_interactive_timer_resched(data, false);
exit:
up_read(&pcpu->enable_sem);
@@ -597,7 +601,7 @@
/* Arm the timer for 1-2 ticks later if not already. */
if (!timer_pending(&pcpu->cpu_timer)) {
- cpufreq_interactive_timer_resched(smp_processor_id());
+ cpufreq_interactive_timer_resched(smp_processor_id(), false);
} else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) {
del_timer(&pcpu->cpu_timer);
del_timer(&pcpu->cpu_slack_timer);
@@ -1613,6 +1617,7 @@
pcpu->loc_floor_val_time = pcpu->pol_floor_val_time;
pcpu->pol_hispeed_val_time = pcpu->pol_floor_val_time;
pcpu->loc_hispeed_val_time = pcpu->pol_floor_val_time;
+ pcpu->min_freq = policy->min;
pcpu->reject_notification = true;
down_write(&pcpu->enable_sem);
del_timer_sync(&pcpu->cpu_timer);
@@ -1666,6 +1671,11 @@
pcpu->target_freq = policy->min;
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
+
+ if (policy->min < pcpu->min_freq)
+ cpufreq_interactive_timer_resched(j, true);
+ pcpu->min_freq = policy->min;
+
up_read(&pcpu->enable_sem);
}
break;