aboutsummaryrefslogtreecommitdiff
path: root/drivers/cpufreq/cpufreq_interactive.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpufreq/cpufreq_interactive.c')
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c132
1 files changed, 45 insertions, 87 deletions
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index b84f709c760e..39e62187cdbc 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -47,9 +47,10 @@ struct cpufreq_interactive_cpuinfo {
spinlock_t target_freq_lock; /*protects target freq */
unsigned int target_freq;
unsigned int floor_freq;
- unsigned int max_freq;
- u64 floor_validate_time;
- u64 hispeed_validate_time;
+ u64 pol_floor_val_time; /* policy floor_validate_time */
+ u64 loc_floor_val_time; /* per-cpu floor_validate_time */
+ u64 pol_hispeed_val_time; /* policy hispeed_validate_time */
+ u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */
struct rw_semaphore enable_sem;
int governor_enabled;
};
@@ -345,6 +346,7 @@ static void cpufreq_interactive_timer(unsigned long data)
unsigned int loadadjfreq;
unsigned int index;
unsigned long flags;
+ u64 max_fvtime;
if (!down_read_trylock(&pcpu->enable_sem))
return;
@@ -367,7 +369,7 @@ static void cpufreq_interactive_timer(unsigned long data)
tunables->boosted = tunables->boost_val || now < tunables->boostpulse_endtime;
if (cpu_load >= tunables->go_hispeed_load || tunables->boosted) {
- if (pcpu->target_freq < tunables->hispeed_freq) {
+ if (pcpu->policy->cur < tunables->hispeed_freq) {
new_freq = tunables->hispeed_freq;
} else {
new_freq = choose_freq(pcpu, loadadjfreq);
@@ -378,14 +380,14 @@ static void cpufreq_interactive_timer(unsigned long data)
} else {
new_freq = choose_freq(pcpu, loadadjfreq);
if (new_freq > tunables->hispeed_freq &&
- pcpu->target_freq < tunables->hispeed_freq)
+ pcpu->policy->cur < tunables->hispeed_freq)
new_freq = tunables->hispeed_freq;
}
- if (pcpu->target_freq >= tunables->hispeed_freq &&
- new_freq > pcpu->target_freq &&
- now - pcpu->hispeed_validate_time <
- freq_to_above_hispeed_delay(tunables, pcpu->target_freq)) {
+ if (pcpu->policy->cur >= tunables->hispeed_freq &&
+ new_freq > pcpu->policy->cur &&
+ now - pcpu->pol_hispeed_val_time <
+ freq_to_above_hispeed_delay(tunables, pcpu->policy->cur)) {
trace_cpufreq_interactive_notyet(
data, cpu_load, pcpu->target_freq,
pcpu->policy->cur, new_freq);
@@ -393,7 +395,7 @@ static void cpufreq_interactive_timer(unsigned long data)
goto rearm;
}
- pcpu->hispeed_validate_time = now;
+ pcpu->loc_hispeed_val_time = now;
if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,
new_freq, CPUFREQ_RELATION_L,
@@ -408,9 +410,10 @@ static void cpufreq_interactive_timer(unsigned long data)
* Do not scale below floor_freq unless we have been at or above the
* floor frequency for the minimum sample time since last validated.
*/
- if (new_freq < pcpu->floor_freq) {
- if (now - pcpu->floor_validate_time <
- tunables->min_sample_time) {
+ max_fvtime = max(pcpu->pol_floor_val_time, pcpu->loc_floor_val_time);
+ if (new_freq < pcpu->floor_freq &&
+ pcpu->target_freq >= pcpu->policy->cur) {
+ if (now - max_fvtime < tunables->min_sample_time) {
trace_cpufreq_interactive_notyet(
data, cpu_load, pcpu->target_freq,
pcpu->policy->cur, new_freq);
@@ -429,7 +432,9 @@ static void cpufreq_interactive_timer(unsigned long data)
if (!tunables->boosted || new_freq > tunables->hispeed_freq) {
pcpu->floor_freq = new_freq;
- pcpu->floor_validate_time = now;
+ if (pcpu->target_freq >= pcpu->policy->cur ||
+ new_freq >= pcpu->policy->cur)
+ pcpu->loc_floor_val_time = now;
}
if (pcpu->target_freq == new_freq &&
@@ -438,7 +443,7 @@ static void cpufreq_interactive_timer(unsigned long data)
data, cpu_load, pcpu->target_freq,
pcpu->policy->cur, new_freq);
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
- goto rearm_if_notmax;
+ goto rearm;
}
trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq,
@@ -451,14 +456,6 @@ static void cpufreq_interactive_timer(unsigned long data)
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
wake_up_process(speedchange_task);
-rearm_if_notmax:
- /*
- * Already set max speed and don't see a need to change that,
- * wait until next idle to re-evaluate, don't need timer.
- */
- if (pcpu->target_freq == pcpu->policy->max)
- goto exit;
-
rearm:
if (!timer_pending(&pcpu->cpu_timer))
cpufreq_interactive_timer_resched(pcpu);
@@ -468,37 +465,6 @@ exit:
return;
}
-static void cpufreq_interactive_idle_start(void)
-{
- struct cpufreq_interactive_cpuinfo *pcpu =
- &per_cpu(cpuinfo, smp_processor_id());
- int pending;
-
- if (!down_read_trylock(&pcpu->enable_sem))
- return;
- if (!pcpu->governor_enabled) {
- up_read(&pcpu->enable_sem);
- return;
- }
-
- pending = timer_pending(&pcpu->cpu_timer);
-
- if (pcpu->target_freq != pcpu->policy->min) {
- /*
- * Entering idle while not at lowest speed. On some
- * platforms this can hold the other CPU(s) at that speed
- * even though the CPU is idle. Set a timer to re-evaluate
- * speed so this idle CPU doesn't hold the other CPUs above
- * min indefinitely. This should probably be a quirk of
- * the CPUFreq driver.
- */
- if (!pending)
- cpufreq_interactive_timer_resched(pcpu);
- }
-
- up_read(&pcpu->enable_sem);
-}
-
static void cpufreq_interactive_idle_end(void)
{
struct cpufreq_interactive_cpuinfo *pcpu =
@@ -553,6 +519,8 @@ static int cpufreq_interactive_speedchange_task(void *data)
for_each_cpu(cpu, &tmp_mask) {
unsigned int j;
unsigned int max_freq = 0;
+ struct cpufreq_interactive_cpuinfo *pjcpu;
+ u64 hvt = ~0ULL, fvt = 0;
pcpu = &per_cpu(cpuinfo, cpu);
if (!down_read_trylock(&pcpu->enable_sem))
@@ -563,17 +531,30 @@ static int cpufreq_interactive_speedchange_task(void *data)
}
for_each_cpu(j, pcpu->policy->cpus) {
- struct cpufreq_interactive_cpuinfo *pjcpu =
- &per_cpu(cpuinfo, j);
+ pjcpu = &per_cpu(cpuinfo, j);
- if (pjcpu->target_freq > max_freq)
+ fvt = max(fvt, pjcpu->loc_floor_val_time);
+ if (pjcpu->target_freq > max_freq) {
max_freq = pjcpu->target_freq;
+ hvt = pjcpu->loc_hispeed_val_time;
+ } else if (pjcpu->target_freq == max_freq) {
+ hvt = min(hvt, pjcpu->loc_hispeed_val_time);
+ }
+ }
+ for_each_cpu(j, pcpu->policy->cpus) {
+ pjcpu = &per_cpu(cpuinfo, j);
+ pjcpu->pol_floor_val_time = fvt;
}
- if (max_freq != pcpu->policy->cur)
+ if (max_freq != pcpu->policy->cur) {
__cpufreq_driver_target(pcpu->policy,
max_freq,
CPUFREQ_RELATION_H);
+ for_each_cpu(j, pcpu->policy->cpus) {
+ pjcpu = &per_cpu(cpuinfo, j);
+ pjcpu->pol_hispeed_val_time = hvt;
+ }
+ }
trace_cpufreq_interactive_setspeed(cpu,
pcpu->target_freq,
pcpu->policy->cur);
@@ -605,7 +586,7 @@ static void cpufreq_interactive_boost(struct cpufreq_interactive_tunables *tunab
if (pcpu->target_freq < tunables->hispeed_freq) {
pcpu->target_freq = tunables->hispeed_freq;
cpumask_set_cpu(i, &speedchange_cpumask);
- pcpu->hispeed_validate_time =
+ pcpu->pol_hispeed_val_time =
ktime_to_us(ktime_get());
anyboost = 1;
}
@@ -1108,14 +1089,8 @@ static int cpufreq_interactive_idle_notifier(struct notifier_block *nb,
unsigned long val,
void *data)
{
- switch (val) {
- case IDLE_START:
- cpufreq_interactive_idle_start();
- break;
- case IDLE_END:
+ if (val == IDLE_END)
cpufreq_interactive_idle_end();
- break;
- }
return 0;
}
@@ -1232,11 +1207,11 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
pcpu->target_freq = policy->cur;
pcpu->freq_table = freq_table;
pcpu->floor_freq = pcpu->target_freq;
- pcpu->floor_validate_time =
+ pcpu->pol_floor_val_time =
ktime_to_us(ktime_get());
- pcpu->hispeed_validate_time =
- pcpu->floor_validate_time;
- pcpu->max_freq = policy->max;
+ 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;
down_write(&pcpu->enable_sem);
del_timer_sync(&pcpu->cpu_timer);
del_timer_sync(&pcpu->cpu_slack_timer);
@@ -1286,23 +1261,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
up_read(&pcpu->enable_sem);
-
- /* Reschedule timer only if policy->max is raised.
- * Delete the timers, else the timer callback may
- * return without re-arm the timer when failed
- * acquire the semaphore. This race may cause timer
- * stopped unexpectedly.
- */
-
- if (policy->max > pcpu->max_freq) {
- down_write(&pcpu->enable_sem);
- del_timer_sync(&pcpu->cpu_timer);
- del_timer_sync(&pcpu->cpu_slack_timer);
- cpufreq_interactive_timer_start(tunables, j);
- up_write(&pcpu->enable_sem);
- }
-
- pcpu->max_freq = policy->max;
}
break;
}