aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTodd Poynor2012-11-14 13:41:21 -0600
committerArve Hjønnevåg2013-02-19 19:55:58 -0600
commit44b906fe720b18798dc64f33eb9d0abc97b4f635 (patch)
treeca7440dab8d87eca93378b807a83624235c306da
parent43a2ba1e21ccc31a68e9e1446b0514bc20078601 (diff)
downloadkernel-common-44b906fe720b18798dc64f33eb9d0abc97b4f635.tar.gz
kernel-common-44b906fe720b18798dc64f33eb9d0abc97b4f635.tar.xz
kernel-common-44b906fe720b18798dc64f33eb9d0abc97b4f635.zip
cpufreq: interactive: allow arbitrary speed / target load mappings
Accept a string of target loads and speeds at which to apply the target loads, per the documentation update in this patch. For example, "85 1000000:90 1700000:99" targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until 1.7GHz and above, at which load 99% is targeted. Attempt to avoid oscillations by evaluating the current speed weighted by current load against each new choice of speed, choosing a higher speed if the current load requires a higher speed. Change-Id: Ie3300206047c84eca5a26b0b63ea512e5207550e Signed-off-by: Todd Poynor <toddpoynor@google.com>
-rw-r--r--Documentation/cpu-freq/governors.txt17
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c186
2 files changed, 189 insertions, 14 deletions
diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index 6aed1ce3673..712f24d0309 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -218,6 +218,23 @@ short-term load since idle exit to determine the cpu speed to ramp to.
218 218
219The tuneable values for this governor are: 219The tuneable values for this governor are:
220 220
221target_loads: CPU load values used to adjust speed to influence the
222current CPU load toward that value. In general, the lower the target
223load, the more often the governor will raise CPU speeds to bring load
224below the target. The format is a single target load, optionally
225followed by pairs of CPU speeds and CPU loads to target at or above
226those speeds. Colons can be used between the speeds and associated
227target loads for readability. For example:
228
229 85 1000000:90 1700000:99
230
231targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until
2321.7GHz and above, at which load 99% is targeted. If speeds are
233specified these must appear in ascending order. Higher target load
234values are typically specified for higher speeds, that is, target load
235values also usually appear in an ascending order. The default is
236target load 90% for all speeds.
237
221min_sample_time: The minimum amount of time to spend at the current 238min_sample_time: The minimum amount of time to spend at the current
222frequency before ramping down. This is to ensure that the governor has 239frequency before ramping down. This is to ensure that the governor has
223seen enough historic cpu load data to determine the appropriate 240seen enough historic cpu load data to determine the appropriate
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index c82d9fee284..ada7da51b78 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -69,7 +69,10 @@ static unsigned long go_hispeed_load;
69 69
70/* Target load. Lower values result in higher CPU speeds. */ 70/* Target load. Lower values result in higher CPU speeds. */
71#define DEFAULT_TARGET_LOAD 90 71#define DEFAULT_TARGET_LOAD 90
72static unsigned long target_load = DEFAULT_TARGET_LOAD; 72static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD};
73static spinlock_t target_loads_lock;
74static unsigned int *target_loads = default_target_loads;
75static int ntarget_loads = ARRAY_SIZE(default_target_loads);
73 76
74/* 77/*
75 * The minimum amount of time to spend at a frequency before we can ramp down. 78 * The minimum amount of time to spend at a frequency before we can ramp down.
@@ -124,6 +127,110 @@ static void cpufreq_interactive_timer_resched(
124 &pcpu->time_in_idle_timestamp); 127 &pcpu->time_in_idle_timestamp);
125} 128}
126 129
130static unsigned int freq_to_targetload(unsigned int freq)
131{
132 int i;
133 unsigned int ret;
134
135 spin_lock(&target_loads_lock);
136
137 for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2)
138 ;
139
140 ret = target_loads[i];
141 spin_unlock(&target_loads_lock);
142 return ret;
143}
144
145/*
146 * If increasing frequencies never map to a lower target load then
147 * choose_freq() will find the minimum frequency that does not exceed its
148 * target load given the current load.
149 */
150
151static unsigned int choose_freq(
152 struct cpufreq_interactive_cpuinfo *pcpu, unsigned int curload)
153{
154 unsigned int freq = pcpu->policy->cur;
155 unsigned int loadadjfreq = freq * curload;
156 unsigned int prevfreq, freqmin, freqmax;
157 unsigned int tl;
158 int index;
159
160 freqmin = 0;
161 freqmax = UINT_MAX;
162
163 do {
164 prevfreq = freq;
165 tl = freq_to_targetload(freq);
166
167 /*
168 * Find the lowest frequency where the computed load is less
169 * than or equal to the target load.
170 */
171
172 cpufreq_frequency_table_target(
173 pcpu->policy, pcpu->freq_table, loadadjfreq / tl,
174 CPUFREQ_RELATION_L, &index);
175 freq = pcpu->freq_table[index].frequency;
176
177 if (freq > prevfreq) {
178 /* The previous frequency is too low. */
179 freqmin = prevfreq;
180
181 if (freq >= freqmax) {
182 /*
183 * Find the highest frequency that is less
184 * than freqmax.
185 */
186 cpufreq_frequency_table_target(
187 pcpu->policy, pcpu->freq_table,
188 freqmax - 1, CPUFREQ_RELATION_H,
189 &index);
190 freq = pcpu->freq_table[index].frequency;
191
192 if (freq == freqmin) {
193 /*
194 * The first frequency below freqmax
195 * has already been found to be too
196 * low. freqmax is the lowest speed
197 * we found that is fast enough.
198 */
199 freq = freqmax;
200 break;
201 }
202 }
203 } else if (freq < prevfreq) {
204 /* The previous frequency is high enough. */
205 freqmax = prevfreq;
206
207 if (freq <= freqmin) {
208 /*
209 * Find the lowest frequency that is higher
210 * than freqmin.
211 */
212 cpufreq_frequency_table_target(
213 pcpu->policy, pcpu->freq_table,
214 freqmin + 1, CPUFREQ_RELATION_L,
215 &index);
216 freq = pcpu->freq_table[index].frequency;
217
218 /*
219 * If freqmax is the first frequency above
220 * freqmin then we have already found that
221 * this speed is fast enough.
222 */
223 if (freq == freqmax)
224 break;
225 }
226 }
227
228 /* If same frequency chosen as previous then done. */
229 } while (freq != prevfreq);
230
231 return freq;
232}
233
127static void cpufreq_interactive_timer(unsigned long data) 234static void cpufreq_interactive_timer(unsigned long data)
128{ 235{
129 u64 now; 236 u64 now;
@@ -179,7 +286,7 @@ static void cpufreq_interactive_timer(unsigned long data)
179 pcpu->target_freq < hispeed_freq) 286 pcpu->target_freq < hispeed_freq)
180 new_freq = hispeed_freq; 287 new_freq = hispeed_freq;
181 else 288 else
182 new_freq = pcpu->policy->cur * cpu_load / target_load; 289 new_freq = choose_freq(pcpu, cpu_load);
183 290
184 if (pcpu->target_freq >= hispeed_freq && 291 if (pcpu->target_freq >= hispeed_freq &&
185 new_freq > pcpu->target_freq && 292 new_freq > pcpu->target_freq &&
@@ -413,29 +520,79 @@ static void cpufreq_interactive_boost(void)
413 wake_up_process(speedchange_task); 520 wake_up_process(speedchange_task);
414} 521}
415 522
416static ssize_t show_target_load( 523static ssize_t show_target_loads(
417 struct kobject *kobj, struct attribute *attr, char *buf) 524 struct kobject *kobj, struct attribute *attr, char *buf)
418{ 525{
419 return sprintf(buf, "%lu\n", target_load); 526 int i;
527 ssize_t ret = 0;
528
529 spin_lock(&target_loads_lock);
530
531 for (i = 0; i < ntarget_loads; i++)
532 ret += sprintf(buf + ret, "%u%s", target_loads[i],
533 i & 0x1 ? ":" : " ");
534
535 ret += sprintf(buf + ret, "\n");
536 spin_unlock(&target_loads_lock);
537 return ret;
420} 538}
421 539
422static ssize_t store_target_load( 540static ssize_t store_target_loads(
423 struct kobject *kobj, struct attribute *attr, const char *buf, 541 struct kobject *kobj, struct attribute *attr, const char *buf,
424 size_t count) 542 size_t count)
425{ 543{
426 int ret; 544 int ret;
427 unsigned long val; 545 const char *cp;
546 unsigned int *new_target_loads = NULL;
547 int ntokens = 1;
548 int i;
428 549
429 ret = strict_strtoul(buf, 0, &val); 550 cp = buf;
430 if (ret < 0) 551 while ((cp = strpbrk(cp + 1, " :")))
431 return ret; 552 ntokens++;
432 target_load = val; 553
554 if (!(ntokens & 0x1))
555 goto err_inval;
556
557 new_target_loads = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL);
558 if (!new_target_loads) {
559 ret = -ENOMEM;
560 goto err;
561 }
562
563 cp = buf;
564 i = 0;
565 while (i < ntokens) {
566 if (sscanf(cp, "%u", &new_target_loads[i++]) != 1)
567 goto err_inval;
568
569 cp = strpbrk(cp, " :");
570 if (!cp)
571 break;
572 cp++;
573 }
574
575 if (i != ntokens)
576 goto err_inval;
577
578 spin_lock(&target_loads_lock);
579 if (target_loads != default_target_loads)
580 kfree(target_loads);
581 target_loads = new_target_loads;
582 ntarget_loads = ntokens;
583 spin_unlock(&target_loads_lock);
433 return count; 584 return count;
585
586err_inval:
587 ret = -EINVAL;
588err:
589 kfree(new_target_loads);
590 return ret;
434} 591}
435 592
436static struct global_attr target_load_attr = 593static struct global_attr target_loads_attr =
437 __ATTR(target_load, S_IRUGO | S_IWUSR, 594 __ATTR(target_loads, S_IRUGO | S_IWUSR,
438 show_target_load, store_target_load); 595 show_target_loads, store_target_loads);
439 596
440static ssize_t show_hispeed_freq(struct kobject *kobj, 597static ssize_t show_hispeed_freq(struct kobject *kobj,
441 struct attribute *attr, char *buf) 598 struct attribute *attr, char *buf)
@@ -598,7 +755,7 @@ static struct global_attr boostpulse =
598 __ATTR(boostpulse, 0200, NULL, store_boostpulse); 755 __ATTR(boostpulse, 0200, NULL, store_boostpulse);
599 756
600static struct attribute *interactive_attributes[] = { 757static struct attribute *interactive_attributes[] = {
601 &target_load_attr.attr, 758 &target_loads_attr.attr,
602 &hispeed_freq_attr.attr, 759 &hispeed_freq_attr.attr,
603 &go_hispeed_load_attr.attr, 760 &go_hispeed_load_attr.attr,
604 &above_hispeed_delay.attr, 761 &above_hispeed_delay.attr,
@@ -738,6 +895,7 @@ static int __init cpufreq_interactive_init(void)
738 pcpu->cpu_timer.data = i; 895 pcpu->cpu_timer.data = i;
739 } 896 }
740 897
898 spin_lock_init(&target_loads_lock);
741 spin_lock_init(&speedchange_cpumask_lock); 899 spin_lock_init(&speedchange_cpumask_lock);
742 speedchange_task = 900 speedchange_task =
743 kthread_create(cpufreq_interactive_speedchange_task, NULL, 901 kthread_create(cpufreq_interactive_speedchange_task, NULL,