[PATCH 42/70] cpufreq: interactive: allow arbitrary speed / delay mappings

From: BÃlint Czobor
Date: Tue Oct 27 2015 - 13:50:36 EST


From: Minsung Kim <ms925.kim@xxxxxxxxxxx>

Accept a string of delays and speeds at which to apply the delay before
raising each step above hispeed. For example, "80000 1300000:200000
1500000:40000" means that the delay at or above 1GHz, until 1.3GHz is 80 msecs,
the delay until 1.5GHz is 200 msecs and the delay at or above 1.5GHz is 40
msecs when hispeed_freq is 1GHz.

[toddpoynor@xxxxxxxxxx: add documentation]
Change-Id: Ifeebede8b1acbdd0a53e5c6916bccbf764dc854f
Signed-off-by: Minsung Kim <ms925.kim@xxxxxxxxxxx>
Signed-off-by: BÃlint Czobor <czoborbalint@xxxxxxxxx>
---
Documentation/cpu-freq/governors.txt | 12 ++-
drivers/cpufreq/cpufreq_interactive.c | 185 ++++++++++++++++++++++-----------
2 files changed, 134 insertions(+), 63 deletions(-)

diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index 4d8ae34..ac8a37e 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -260,7 +260,17 @@ Default is 99%.

above_hispeed_delay: When speed is at or above hispeed_freq, wait for
this long before raising speed in response to continued high load.
-Default is 20000 uS.
+The format is a single delay value, optionally followed by pairs of
+CPU speeds and the delay to use at or above those speeds. Colons can
+be used between the speeds and associated delays for readability. For
+example:
+
+ 80000 1300000:200000 1500000:40000
+
+uses delay 80000 uS until CPU speed 1.3 GHz, at which speed delay
+200000 uS is used until speed 1.5 GHz, at which speed (and above)
+delay 40000 uS is used. If speeds are specified these must appear in
+ascending order. Default is 20000 uS.

timer_rate: Sample rate for reevaluating CPU load when the CPU is not
idle. A deferrable timer is used, such that the CPU will not be woken
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 3447e58..620b46c 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -94,7 +94,11 @@ static unsigned long timer_rate = DEFAULT_TIMER_RATE;
* timer interval.
*/
#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE
-static unsigned long above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY;
+static unsigned int default_above_hispeed_delay[] = {
+ DEFAULT_ABOVE_HISPEED_DELAY };
+static spinlock_t above_hispeed_delay_lock;
+static unsigned int *above_hispeed_delay = default_above_hispeed_delay;
+static int nabove_hispeed_delay = ARRAY_SIZE(default_above_hispeed_delay);

/* Non-zero means indefinite speed boost active */
static int boost_val;
@@ -144,6 +148,23 @@ static void cpufreq_interactive_timer_resched(
spin_unlock_irqrestore(&pcpu->load_lock, flags);
}

+static unsigned int freq_to_above_hispeed_delay(unsigned int freq)
+{
+ int i;
+ unsigned int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&above_hispeed_delay_lock, flags);
+
+ for (i = 0; i < nabove_hispeed_delay - 1 &&
+ freq >= above_hispeed_delay[i+1]; i += 2)
+ ;
+
+ ret = above_hispeed_delay[i];
+ spin_unlock_irqrestore(&above_hispeed_delay_lock, flags);
+ return ret;
+}
+
static unsigned int freq_to_targetload(unsigned int freq)
{
int i;
@@ -316,7 +337,8 @@ static void cpufreq_interactive_timer(unsigned long data)

if (pcpu->target_freq >= hispeed_freq &&
new_freq > pcpu->target_freq &&
- now - pcpu->hispeed_validate_time < above_hispeed_delay_val) {
+ now - pcpu->hispeed_validate_time <
+ freq_to_above_hispeed_delay(pcpu->policy->cur)) {
trace_cpufreq_interactive_notyet(
data, cpu_load, pcpu->target_freq,
pcpu->policy->cur, new_freq);
@@ -580,6 +602,56 @@ static struct notifier_block cpufreq_notifier_block = {
.notifier_call = cpufreq_interactive_notifier,
};

+static unsigned int *get_tokenized_data(const char *buf, int *num_tokens)
+{
+ const char *cp;
+ int i;
+ int ntokens = 1;
+ unsigned int *tokenized_data;
+
+ cp = buf;
+ while ((cp = strpbrk(cp + 1, " :")))
+ ntokens++;
+
+ if (!(ntokens & 0x1)) {
+ tokenized_data = ERR_PTR(-EINVAL);
+ goto err;
+ }
+
+ tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL);
+ if (!tokenized_data) {
+ tokenized_data = ERR_PTR(-ENOMEM);
+ goto err;
+ }
+
+ cp = buf;
+ i = 0;
+ while (i < ntokens) {
+ if (sscanf(cp, "%u", &tokenized_data[i++]) != 1) {
+ tokenized_data = ERR_PTR(-EINVAL);
+ goto err_kfree;
+ }
+
+ cp = strpbrk(cp, " :");
+ if (!cp)
+ break;
+ cp++;
+ }
+
+ if (i != ntokens) {
+ tokenized_data = ERR_PTR(-EINVAL);
+ goto err_kfree;
+ }
+
+ *num_tokens = ntokens;
+ return tokenized_data;
+
+err_kfree:
+ kfree(tokenized_data);
+err:
+ return tokenized_data;
+}
+
static ssize_t show_target_loads(
struct kobject *kobj, struct attribute *attr, char *buf)
{
@@ -602,40 +674,13 @@ static ssize_t store_target_loads(
struct kobject *kobj, struct attribute *attr, const char *buf,
size_t count)
{
- int ret;
- const char *cp;
+ int ntokens;
unsigned int *new_target_loads = NULL;
- int ntokens = 1;
- int i;
unsigned long flags;

- 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;
+ new_target_loads = get_tokenized_data(buf, &ntokens);
+ if (IS_ERR(new_target_loads))
+ return PTR_RET(new_target_loads);

spin_lock_irqsave(&target_loads_lock, flags);
if (target_loads != default_target_loads)
@@ -644,18 +689,56 @@ static ssize_t store_target_loads(
ntarget_loads = ntokens;
spin_unlock_irqrestore(&target_loads_lock, flags);
return count;
-
-err_inval:
- ret = -EINVAL;
-err:
- kfree(new_target_loads);
- return ret;
}

static struct global_attr target_loads_attr =
__ATTR(target_loads, S_IRUGO | S_IWUSR,
show_target_loads, store_target_loads);

+static ssize_t show_above_hispeed_delay(
+ struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ int i;
+ ssize_t ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&above_hispeed_delay_lock, flags);
+
+ for (i = 0; i < nabove_hispeed_delay; i++)
+ ret += sprintf(buf + ret, "%u%s", above_hispeed_delay[i],
+ i & 0x1 ? ":" : " ");
+
+ ret += sprintf(buf + ret, "\n");
+ spin_unlock_irqrestore(&above_hispeed_delay_lock, flags);
+ return ret;
+}
+
+static ssize_t store_above_hispeed_delay(
+ struct kobject *kobj, struct attribute *attr, const char *buf,
+ size_t count)
+{
+ int ntokens;
+ unsigned int *new_above_hispeed_delay = NULL;
+ unsigned long flags;
+
+ new_above_hispeed_delay = get_tokenized_data(buf, &ntokens);
+ if (IS_ERR(new_above_hispeed_delay))
+ return PTR_RET(new_above_hispeed_delay);
+
+ spin_lock_irqsave(&above_hispeed_delay_lock, flags);
+ if (above_hispeed_delay != default_above_hispeed_delay)
+ kfree(above_hispeed_delay);
+ above_hispeed_delay = new_above_hispeed_delay;
+ nabove_hispeed_delay = ntokens;
+ spin_unlock_irqrestore(&above_hispeed_delay_lock, flags);
+ return count;
+
+}
+
+static struct global_attr above_hispeed_delay_attr =
+ __ATTR(above_hispeed_delay, S_IRUGO | S_IWUSR,
+ show_above_hispeed_delay, store_above_hispeed_delay);
+
static ssize_t show_hispeed_freq(struct kobject *kobj,
struct attribute *attr, char *buf)
{
@@ -724,28 +807,6 @@ static ssize_t store_min_sample_time(struct kobject *kobj,
static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644,
show_min_sample_time, store_min_sample_time);

-static ssize_t show_above_hispeed_delay(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", above_hispeed_delay_val);
-}
-
-static ssize_t store_above_hispeed_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_hispeed_delay_val = val;
- return count;
-}
-
-define_one_global_rw(above_hispeed_delay);
-
static ssize_t show_timer_rate(struct kobject *kobj,
struct attribute *attr, char *buf)
{
@@ -865,9 +926,9 @@ define_one_global_rw(boostpulse_duration);

static struct attribute *interactive_attributes[] = {
&target_loads_attr.attr,
+ &above_hispeed_delay_attr.attr,
&hispeed_freq_attr.attr,
&go_hispeed_load_attr.attr,
- &above_hispeed_delay.attr,
&min_sample_time_attr.attr,
&timer_rate_attr.attr,
&timer_slack.attr,
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/