diff options
Diffstat (limited to 'drivers/cpufreq/cpufreq_interactive.c')
| -rw-r--r-- | drivers/cpufreq/cpufreq_interactive.c | 186 |
1 files changed, 172 insertions, 14 deletions
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 17c42cc12dc8..6ea77a0d6b80 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -70,7 +70,10 @@ static unsigned long go_hispeed_load; /* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 -static unsigned long target_load = DEFAULT_TARGET_LOAD; +static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; +static spinlock_t target_loads_lock; +static unsigned int *target_loads = default_target_loads; +static int ntarget_loads = ARRAY_SIZE(default_target_loads); /* * The minimum amount of time to spend at a frequency before we can ramp down. @@ -125,6 +128,110 @@ static void cpufreq_interactive_timer_resched( &pcpu->time_in_idle_timestamp); } +static unsigned int freq_to_targetload(unsigned int freq) +{ + int i; + unsigned int ret; + + spin_lock(&target_loads_lock); + + for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2) + ; + + ret = target_loads[i]; + spin_unlock(&target_loads_lock); + return ret; +} + +/* + * If increasing frequencies never map to a lower target load then + * choose_freq() will find the minimum frequency that does not exceed its + * target load given the current load. + */ + +static unsigned int choose_freq( + struct cpufreq_interactive_cpuinfo *pcpu, unsigned int curload) +{ + unsigned int freq = pcpu->policy->cur; + unsigned int loadadjfreq = freq * curload; + unsigned int prevfreq, freqmin, freqmax; + unsigned int tl; + int index; + + freqmin = 0; + freqmax = UINT_MAX; + + do { + prevfreq = freq; + tl = freq_to_targetload(freq); + + /* + * Find the lowest frequency where the computed load is less + * than or equal to the target load. + */ + + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, loadadjfreq / tl, + CPUFREQ_RELATION_L, &index); + freq = pcpu->freq_table[index].frequency; + + if (freq > prevfreq) { + /* The previous frequency is too low. */ + freqmin = prevfreq; + + if (freq >= freqmax) { + /* + * Find the highest frequency that is less + * than freqmax. + */ + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmax - 1, CPUFREQ_RELATION_H, + &index); + freq = pcpu->freq_table[index].frequency; + + if (freq == freqmin) { + /* + * The first frequency below freqmax + * has already been found to be too + * low. freqmax is the lowest speed + * we found that is fast enough. + */ + freq = freqmax; + break; + } + } + } else if (freq < prevfreq) { + /* The previous frequency is high enough. */ + freqmax = prevfreq; + + if (freq <= freqmin) { + /* + * Find the lowest frequency that is higher + * than freqmin. + */ + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmin + 1, CPUFREQ_RELATION_L, + &index); + freq = pcpu->freq_table[index].frequency; + + /* + * If freqmax is the first frequency above + * freqmin then we have already found that + * this speed is fast enough. + */ + if (freq == freqmax) + break; + } + } + + /* If same frequency chosen as previous then done. */ + } while (freq != prevfreq); + + return freq; +} + static void cpufreq_interactive_timer(unsigned long data) { u64 now; @@ -180,7 +287,7 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->target_freq < hispeed_freq) new_freq = hispeed_freq; else - new_freq = pcpu->policy->cur * cpu_load / target_load; + new_freq = choose_freq(pcpu, cpu_load); if (pcpu->target_freq >= hispeed_freq && new_freq > pcpu->target_freq && @@ -414,29 +521,79 @@ static void cpufreq_interactive_boost(void) wake_up_process(speedchange_task); } -static ssize_t show_target_load( +static ssize_t show_target_loads( struct kobject *kobj, struct attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", target_load); + int i; + ssize_t ret = 0; + + spin_lock(&target_loads_lock); + + for (i = 0; i < ntarget_loads; i++) + ret += sprintf(buf + ret, "%u%s", target_loads[i], + i & 0x1 ? ":" : " "); + + ret += sprintf(buf + ret, "\n"); + spin_unlock(&target_loads_lock); + return ret; } -static ssize_t store_target_load( +static ssize_t store_target_loads( struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { int ret; - unsigned long val; + const char *cp; + unsigned int *new_target_loads = NULL; + int ntokens = 1; + int i; - ret = strict_strtoul(buf, 0, &val); - if (ret < 0) - return ret; - target_load = val; + cp = buf; + while ((cp = strpbrk(cp + 1, " :"))) + ntokens++; + + if (!(ntokens & 0x1)) + goto err_inval; + + new_target_loads = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); + if (!new_target_loads) { + ret = -ENOMEM; + goto err; + } + + cp = buf; + i = 0; + while (i < ntokens) { + if (sscanf(cp, "%u", &new_target_loads[i++]) != 1) + goto err_inval; + + cp = strpbrk(cp, " :"); + if (!cp) + break; + cp++; + } + + if (i != ntokens) + goto err_inval; + + spin_lock(&target_loads_lock); + if (target_loads != default_target_loads) + kfree(target_loads); + target_loads = new_target_loads; + ntarget_loads = ntokens; + spin_unlock(&target_loads_lock); return count; + +err_inval: + ret = -EINVAL; +err: + kfree(new_target_loads); + return ret; } -static struct global_attr target_load_attr = - __ATTR(target_load, S_IRUGO | S_IWUSR, - show_target_load, store_target_load); +static struct global_attr target_loads_attr = + __ATTR(target_loads, S_IRUGO | S_IWUSR, + show_target_loads, store_target_loads); static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) @@ -599,7 +756,7 @@ static struct global_attr boostpulse = __ATTR(boostpulse, 0200, NULL, store_boostpulse); static struct attribute *interactive_attributes[] = { - &target_load_attr.attr, + &target_loads_attr.attr, &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, &above_hispeed_delay.attr, @@ -739,6 +896,7 @@ static int __init cpufreq_interactive_init(void) pcpu->cpu_timer.data = i; } + spin_lock_init(&target_loads_lock); spin_lock_init(&speedchange_cpumask_lock); speedchange_task = kthread_create(cpufreq_interactive_speedchange_task, NULL, |
