Latest big LITTLE systems for ARM SoC's have following kind of ranges of freqs: LITTLE CPU ranges: x MHz -> y MHz big CPU ranges: y + delta -> z MHz
For these, we use hispeed_freq as y MHz, to avoid a switch of clusters from LITTLE to big when load burst comes. Similar to non big.LITTLE systems, we also need to avoid highest OPPs for load bursts and so need another "speed breaker".
Hence, we introduce hispeed2_freq here. This would normally be set to z - delta MHz. For, non big.LITTLE systems if we don't set hispeed2_freq, it would be set as policy->max frequency. And so, behavior would remain same as existing code.
Based on Initial work from Sudeep, hence his SOB.
Signed-off-by: Sudeep KarkadaNagesha sudeep.karkadanagesha@arm.com Signed-off-by: Viresh Kumar viresh.kumar@linaro.org --- drivers/cpufreq/cpufreq_interactive.c | 119 +++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 8 deletions(-)
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 4dc7f04..0c5bb2c 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -68,6 +68,13 @@ static unsigned int hispeed_freq; #define DEFAULT_GO_HISPEED_LOAD 85 static unsigned long go_hispeed_load;
+/* Hi speed2 to bump to from Hi speed when load bursts over hispeed freq */ +static unsigned int hispeed2_freq; + +/* Go to hi speed2 when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED2_LOAD 95 +static unsigned long go_hispeed2_load; + /* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; @@ -88,12 +95,15 @@ static unsigned long min_sample_time; static unsigned long timer_rate;
/* - * Wait this long before raising speed above hispeed, by default a single - * timer interval. + * Wait this long before raising speed above hispeed/hispeed2, by default a + * single timer interval. */ #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE static unsigned long above_hispeed_delay_val;
+#define DEFAULT_ABOVE_HISPEED2_DELAY DEFAULT_TIMER_RATE +static unsigned long above_hispeed2_delay_val; + /* Non-zero means indefinite speed boost active */ static int boost_val; /* Duration of a boot pulse in usecs */ @@ -272,7 +282,7 @@ static void cpufreq_interactive_timer(unsigned long data) int cpu_load; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, data); - unsigned int new_freq; + unsigned int new_freq = 0, min_freq = 0; unsigned int loadadjfreq; unsigned int index; unsigned long flags; @@ -301,13 +311,22 @@ static void cpufreq_interactive_timer(unsigned long data) if (pcpu->target_freq < hispeed_freq) { new_freq = hispeed_freq; } else { - new_freq = choose_freq(pcpu, loadadjfreq); - - if (new_freq < hispeed_freq) - new_freq = hispeed_freq; + if (cpu_load >= go_hispeed2_load) { + if (pcpu->target_freq < hispeed2_freq) + new_freq = hispeed2_freq; + else + min_freq = hispeed2_freq; + } else { + min_freq = hispeed_freq; + } } - } else { + } + + if (!new_freq) { new_freq = choose_freq(pcpu, loadadjfreq); + + if (new_freq < min_freq) + new_freq = min_freq; }
if (pcpu->target_freq >= hispeed_freq && @@ -319,6 +338,15 @@ static void cpufreq_interactive_timer(unsigned long data) goto rearm; }
+ if (pcpu->target_freq >= hispeed2_freq && + new_freq > pcpu->target_freq && + now - pcpu->hispeed_validate_time < above_hispeed2_delay_val) { + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + goto rearm; + } + pcpu->hispeed_validate_time = now;
if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, @@ -688,6 +716,51 @@ static ssize_t store_go_hispeed_load(struct kobject *kobj, static struct global_attr go_hispeed_load_attr = __ATTR(go_hispeed_load, 0644, show_go_hispeed_load, store_go_hispeed_load);
+static ssize_t show_hispeed2_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", hispeed2_freq); +} + +static ssize_t store_hispeed2_freq(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + long unsigned int val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + hispeed2_freq = val; + return count; +} + +static struct global_attr hispeed2_freq_attr = __ATTR(hispeed2_freq, 0644, + show_hispeed2_freq, store_hispeed2_freq); + +static ssize_t show_go_hispeed2_load(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", go_hispeed2_load); +} + +static ssize_t store_go_hispeed2_load(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + go_hispeed2_load = val; + return count; +} + +static struct global_attr go_hispeed2_load_attr = __ATTR(go_hispeed2_load, 0644, + show_go_hispeed2_load, store_go_hispeed2_load); + static ssize_t show_min_sample_time(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -732,6 +805,28 @@ static ssize_t store_above_hispeed_delay(struct kobject *kobj,
define_one_global_rw(above_hispeed_delay);
+static ssize_t show_above_hispeed2_delay(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", above_hispeed2_delay_val); +} + +static ssize_t store_above_hispeed2_delay(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + above_hispeed2_delay_val = val; + return count; +} + +define_one_global_rw(above_hispeed2_delay); + static ssize_t show_timer_rate(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -854,6 +949,9 @@ static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, &above_hispeed_delay.attr, + &hispeed2_freq_attr.attr, + &go_hispeed2_load_attr.attr, + &above_hispeed2_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, &timer_slack.attr, @@ -906,6 +1004,9 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (!hispeed_freq) hispeed_freq = policy->max;
+ if (!hispeed2_freq || hispeed2_freq > policy->max) + hispeed2_freq = policy->max; + for_each_cpu(j, policy->cpus) { unsigned long expires;
@@ -994,6 +1095,8 @@ static int __init cpufreq_interactive_init(void) go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY; + go_hispeed2_load = DEFAULT_GO_HISPEED2_LOAD; + above_hispeed2_delay_val = DEFAULT_ABOVE_HISPEED2_DELAY; timer_rate = DEFAULT_TIMER_RATE;
/* Initalize per-cpu timers */
Thanks Viresh, I'm out of the office right now but will look at this more closely early next year.
There's discussions starting among various folks about how to handle big.LITTLE and what role cpufreq governors vs. platform code play a part in that, so all this will probably be something a little longer-term as it shakes out. I've also recently added a target_loads attribute that can pair CPU frequencies with target loads (at speed 200M try for target load 85%, at speed 1.5G try for load 98%...), which may also need adjustment if the governors manage the little CPUs. If the little CPUs have CPU numbers exposed to userspace then maybe these attributes become per-CPU group instead of global?
Anyhow, lots to think about for big.LITTLE.
Thanks -- Todd
On Thu, Dec 20, 2012 at 8:39 PM, Viresh Kumar viresh.kumar@linaro.orgwrote:
Latest big LITTLE systems for ARM SoC's have following kind of ranges of freqs: LITTLE CPU ranges: x MHz -> y MHz big CPU ranges: y + delta -> z MHz
For these, we use hispeed_freq as y MHz, to avoid a switch of clusters from LITTLE to big when load burst comes. Similar to non big.LITTLE systems, we also need to avoid highest OPPs for load bursts and so need another "speed breaker".
Hence, we introduce hispeed2_freq here. This would normally be set to z - delta MHz. For, non big.LITTLE systems if we don't set hispeed2_freq, it would be set as policy->max frequency. And so, behavior would remain same as existing code.
Based on Initial work from Sudeep, hence his SOB.
Signed-off-by: Sudeep KarkadaNagesha sudeep.karkadanagesha@arm.com Signed-off-by: Viresh Kumar viresh.kumar@linaro.org
drivers/cpufreq/cpufreq_interactive.c | 119 +++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 8 deletions(-)
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 4dc7f04..0c5bb2c 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -68,6 +68,13 @@ static unsigned int hispeed_freq; #define DEFAULT_GO_HISPEED_LOAD 85 static unsigned long go_hispeed_load;
+/* Hi speed2 to bump to from Hi speed when load bursts over hispeed freq */ +static unsigned int hispeed2_freq;
+/* Go to hi speed2 when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED2_LOAD 95 +static unsigned long go_hispeed2_load;
/* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; @@ -88,12 +95,15 @@ static unsigned long min_sample_time; static unsigned long timer_rate;
/*
- Wait this long before raising speed above hispeed, by default a single
- timer interval.
- Wait this long before raising speed above hispeed/hispeed2, by default
a
*/
- single timer interval.
#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE static unsigned long above_hispeed_delay_val;
+#define DEFAULT_ABOVE_HISPEED2_DELAY DEFAULT_TIMER_RATE +static unsigned long above_hispeed2_delay_val;
/* Non-zero means indefinite speed boost active */ static int boost_val; /* Duration of a boot pulse in usecs */ @@ -272,7 +282,7 @@ static void cpufreq_interactive_timer(unsigned long data) int cpu_load; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, data);
unsigned int new_freq;
unsigned int new_freq = 0, min_freq = 0; unsigned int loadadjfreq; unsigned int index; unsigned long flags;
@@ -301,13 +311,22 @@ static void cpufreq_interactive_timer(unsigned long data) if (pcpu->target_freq < hispeed_freq) { new_freq = hispeed_freq; } else {
new_freq = choose_freq(pcpu, loadadjfreq);
if (new_freq < hispeed_freq)
new_freq = hispeed_freq;
if (cpu_load >= go_hispeed2_load) {
if (pcpu->target_freq < hispeed2_freq)
new_freq = hispeed2_freq;
else
min_freq = hispeed2_freq;
} else {
min_freq = hispeed_freq;
} }
} else {
}
if (!new_freq) { new_freq = choose_freq(pcpu, loadadjfreq);
if (new_freq < min_freq)
new_freq = min_freq; } if (pcpu->target_freq >= hispeed_freq &&
@@ -319,6 +338,15 @@ static void cpufreq_interactive_timer(unsigned long data) goto rearm; }
if (pcpu->target_freq >= hispeed2_freq &&
new_freq > pcpu->target_freq &&
now - pcpu->hispeed_validate_time < above_hispeed2_delay_val) {
trace_cpufreq_interactive_notyet(
data, cpu_load, pcpu->target_freq,
pcpu->policy->cur, new_freq);
goto rearm;
}
pcpu->hispeed_validate_time = now; if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,
@@ -688,6 +716,51 @@ static ssize_t store_go_hispeed_load(struct kobject *kobj, static struct global_attr go_hispeed_load_attr = __ATTR(go_hispeed_load, 0644, show_go_hispeed_load, store_go_hispeed_load);
+static ssize_t show_hispeed2_freq(struct kobject *kobj,
struct attribute *attr, char *buf)
+{
return sprintf(buf, "%u\n", hispeed2_freq);
+}
+static ssize_t store_hispeed2_freq(struct kobject *kobj,
struct attribute *attr, const char *buf,
size_t count)
+{
int ret;
long unsigned int val;
ret = strict_strtoul(buf, 0, &val);
if (ret < 0)
return ret;
hispeed2_freq = val;
return count;
+}
+static struct global_attr hispeed2_freq_attr = __ATTR(hispeed2_freq, 0644,
show_hispeed2_freq, store_hispeed2_freq);
+static ssize_t show_go_hispeed2_load(struct kobject *kobj,
struct attribute *attr, char *buf)
+{
return sprintf(buf, "%lu\n", go_hispeed2_load);
+}
+static ssize_t store_go_hispeed2_load(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t
count) +{
int ret;
unsigned long val;
ret = strict_strtoul(buf, 0, &val);
if (ret < 0)
return ret;
go_hispeed2_load = val;
return count;
+}
+static struct global_attr go_hispeed2_load_attr = __ATTR(go_hispeed2_load, 0644,
show_go_hispeed2_load, store_go_hispeed2_load);
static ssize_t show_min_sample_time(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -732,6 +805,28 @@ static ssize_t store_above_hispeed_delay(struct kobject *kobj,
define_one_global_rw(above_hispeed_delay);
+static ssize_t show_above_hispeed2_delay(struct kobject *kobj,
struct attribute *attr, char *buf)
+{
return sprintf(buf, "%lu\n", above_hispeed2_delay_val);
+}
+static ssize_t store_above_hispeed2_delay(struct kobject *kobj,
struct attribute *attr,
const char *buf, size_t count)
+{
int ret;
unsigned long val;
ret = strict_strtoul(buf, 0, &val);
if (ret < 0)
return ret;
above_hispeed2_delay_val = val;
return count;
+}
+define_one_global_rw(above_hispeed2_delay);
static ssize_t show_timer_rate(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -854,6 +949,9 @@ static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, &above_hispeed_delay.attr,
&hispeed2_freq_attr.attr,
&go_hispeed2_load_attr.attr,
&above_hispeed2_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, &timer_slack.attr,
@@ -906,6 +1004,9 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (!hispeed_freq) hispeed_freq = policy->max;
if (!hispeed2_freq || hispeed2_freq > policy->max)
hispeed2_freq = policy->max;
for_each_cpu(j, policy->cpus) { unsigned long expires;
@@ -994,6 +1095,8 @@ static int __init cpufreq_interactive_init(void) go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY;
go_hispeed2_load = DEFAULT_GO_HISPEED2_LOAD;
above_hispeed2_delay_val = DEFAULT_ABOVE_HISPEED2_DELAY; timer_rate = DEFAULT_TIMER_RATE; /* Initalize per-cpu timers */
-- 1.7.12.rc2.18.g61b472e
On 29 December 2012 01:11, Todd Poynor toddpoynor@google.com wrote:
Thanks Viresh, I'm out of the office right now but will look at this more closely early next year.
Happy New Year!!
There's discussions starting among various folks about how to handle big.LITTLE and what role cpufreq governors vs. platform code play a part in that, so all this will probably be something a little longer-term as it shakes out. I've also recently added a target_loads attribute that can pair CPU frequencies with target loads (at speed 200M try for target load 85%, at speed 1.5G try for load 98%...), which may also need adjustment if the governors manage the little CPUs. If the little CPUs have CPU numbers exposed to userspace then maybe these attributes become per-CPU group instead of global?
Anyhow, lots to think about for big.LITTLE.
Currently, Linaro is looking for two solutions to big.LITTLE: - Make scheduler aware of big.LITTLE and so, cpufreq isn't required to do anything special (Long term solution, work in progress) - Hide big.LITTLE from scheduler and perform big-LITTLE switching using cpufreq. This is a short term solution and Linaro has released its first version already (Private to members) and this patch came out of the work on that release.
-- viresh