sched/cpufreq_sched: Consolidated update
Contains: sched/cpufreq_sched: use shorter throttle for raising OPP Avoid cases where a brief drop in load causes a change to a low OPP for the full throttle period. Use a shorter throttle period for raising OPP than for lowering OPP. sched-freq: Fix handling of max/min frequency This reverts commit 9726142608f5b3bf5df4280243c9d324e692a510. Change-Id: Ia78095354f7ad9492f00deb509a2b45112361eda sched/cpufreq: Increasing throttle_down_nsec to 50ms Change-Id: I2d8969cf2a64fa719b9dd86f43f9dd14b1ff84fe sched-freq: make throttle times tunable Change-Id: I127879645367425b273441d7f0306bb15d5633cb Signed-off-by: Srinath Sridharan <srinathsr@google.com> Signed-off-by: Todd Kjos <tkjos@google.com> Signed-off-by: Juri Lelli <juri.lelli@arm.com> [jstultz: Fwdported to 4.4] Signed-off-by: John Stultz <john.stultz@linaro.org>
This commit is contained in:
parent
765c2ab363
commit
d753e92e19
2 changed files with 160 additions and 17 deletions
|
@ -105,7 +105,7 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
|
||||||
|
|
||||||
config CPU_FREQ_DEFAULT_GOV_SCHED
|
config CPU_FREQ_DEFAULT_GOV_SCHED
|
||||||
bool "sched"
|
bool "sched"
|
||||||
select CPU_FREQ_GOV_SCHED
|
select CPU_FREQ_GOV_INTERACTIVE
|
||||||
help
|
help
|
||||||
Use the CPUfreq governor 'sched' as default. This scales
|
Use the CPUfreq governor 'sched' as default. This scales
|
||||||
cpu frequency using CPU utilization estimates from the
|
cpu frequency using CPU utilization estimates from the
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
|
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
|
|
||||||
#define THROTTLE_NSEC 50000000 /* 50ms default */
|
#define THROTTLE_DOWN_NSEC 50000000 /* 50ms default */
|
||||||
|
#define THROTTLE_UP_NSEC 500000 /* 500us default */
|
||||||
|
|
||||||
struct static_key __read_mostly __sched_freq = STATIC_KEY_INIT_FALSE;
|
struct static_key __read_mostly __sched_freq = STATIC_KEY_INIT_FALSE;
|
||||||
static bool __read_mostly cpufreq_driver_slow;
|
static bool __read_mostly cpufreq_driver_slow;
|
||||||
|
@ -33,8 +34,10 @@ DEFINE_PER_CPU(struct sched_capacity_reqs, cpu_sched_capacity_reqs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gov_data - per-policy data internal to the governor
|
* gov_data - per-policy data internal to the governor
|
||||||
* @throttle: next throttling period expiry. Derived from throttle_nsec
|
* @up_throttle: next throttling period expiry if increasing OPP
|
||||||
* @throttle_nsec: throttle period length in nanoseconds
|
* @down_throttle: next throttling period expiry if decreasing OPP
|
||||||
|
* @up_throttle_nsec: throttle period length in nanoseconds if increasing OPP
|
||||||
|
* @down_throttle_nsec: throttle period length in nanoseconds if decreasing OPP
|
||||||
* @task: worker thread for dvfs transition that may block/sleep
|
* @task: worker thread for dvfs transition that may block/sleep
|
||||||
* @irq_work: callback used to wake up worker thread
|
* @irq_work: callback used to wake up worker thread
|
||||||
* @requested_freq: last frequency requested by the sched governor
|
* @requested_freq: last frequency requested by the sched governor
|
||||||
|
@ -48,11 +51,14 @@ DEFINE_PER_CPU(struct sched_capacity_reqs, cpu_sched_capacity_reqs);
|
||||||
* call down_write(policy->rwsem).
|
* call down_write(policy->rwsem).
|
||||||
*/
|
*/
|
||||||
struct gov_data {
|
struct gov_data {
|
||||||
ktime_t throttle;
|
ktime_t up_throttle;
|
||||||
unsigned int throttle_nsec;
|
ktime_t down_throttle;
|
||||||
|
unsigned int up_throttle_nsec;
|
||||||
|
unsigned int down_throttle_nsec;
|
||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
struct irq_work irq_work;
|
struct irq_work irq_work;
|
||||||
unsigned int requested_freq;
|
unsigned int requested_freq;
|
||||||
|
int max;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void cpufreq_sched_try_driver_target(struct cpufreq_policy *policy,
|
static void cpufreq_sched_try_driver_target(struct cpufreq_policy *policy,
|
||||||
|
@ -66,25 +72,29 @@ static void cpufreq_sched_try_driver_target(struct cpufreq_policy *policy,
|
||||||
|
|
||||||
__cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
|
__cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
|
||||||
|
|
||||||
gd->throttle = ktime_add_ns(ktime_get(), gd->throttle_nsec);
|
gd->up_throttle = ktime_add_ns(ktime_get(), gd->up_throttle_nsec);
|
||||||
|
gd->down_throttle = ktime_add_ns(ktime_get(), gd->down_throttle_nsec);
|
||||||
up_write(&policy->rwsem);
|
up_write(&policy->rwsem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool finish_last_request(struct gov_data *gd)
|
static bool finish_last_request(struct gov_data *gd, unsigned int cur_freq)
|
||||||
{
|
{
|
||||||
ktime_t now = ktime_get();
|
ktime_t now = ktime_get();
|
||||||
|
|
||||||
if (ktime_after(now, gd->throttle))
|
ktime_t throttle = gd->requested_freq < cur_freq ?
|
||||||
|
gd->down_throttle : gd->up_throttle;
|
||||||
|
|
||||||
|
if (ktime_after(now, throttle))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int usec_left = ktime_to_ns(ktime_sub(gd->throttle, now));
|
int usec_left = ktime_to_ns(ktime_sub(throttle, now));
|
||||||
|
|
||||||
usec_left /= NSEC_PER_USEC;
|
usec_left /= NSEC_PER_USEC;
|
||||||
trace_cpufreq_sched_throttled(usec_left);
|
trace_cpufreq_sched_throttled(usec_left);
|
||||||
usleep_range(usec_left, usec_left + 100);
|
usleep_range(usec_left, usec_left + 100);
|
||||||
now = ktime_get();
|
now = ktime_get();
|
||||||
if (ktime_after(now, gd->throttle))
|
if (ktime_after(now, throttle))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +138,7 @@ static int cpufreq_sched_thread(void *data)
|
||||||
* if the frequency thread sleeps while waiting to be
|
* if the frequency thread sleeps while waiting to be
|
||||||
* unthrottled, start over to check for a newer request
|
* unthrottled, start over to check for a newer request
|
||||||
*/
|
*/
|
||||||
if (finish_last_request(gd))
|
if (finish_last_request(gd, policy->cur))
|
||||||
continue;
|
continue;
|
||||||
last_request = new_request;
|
last_request = new_request;
|
||||||
cpufreq_sched_try_driver_target(policy, new_request);
|
cpufreq_sched_try_driver_target(policy, new_request);
|
||||||
|
@ -183,16 +193,21 @@ static void update_fdomain_capacity_request(int cpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert the new maximum capacity request into a cpu frequency */
|
/* Convert the new maximum capacity request into a cpu frequency */
|
||||||
freq_new = capacity * policy->max >> SCHED_CAPACITY_SHIFT;
|
freq_new = capacity * gd->max >> SCHED_CAPACITY_SHIFT;
|
||||||
if (cpufreq_frequency_table_target(policy, policy->freq_table,
|
if (cpufreq_frequency_table_target(policy, policy->freq_table,
|
||||||
freq_new, CPUFREQ_RELATION_L,
|
freq_new, CPUFREQ_RELATION_L,
|
||||||
&index_new))
|
&index_new))
|
||||||
goto out;
|
goto out;
|
||||||
freq_new = policy->freq_table[index_new].frequency;
|
freq_new = policy->freq_table[index_new].frequency;
|
||||||
|
|
||||||
|
if (freq_new > policy->max)
|
||||||
|
freq_new = policy->max;
|
||||||
|
|
||||||
|
if (freq_new < policy->min)
|
||||||
|
freq_new = policy->min;
|
||||||
|
|
||||||
trace_cpufreq_sched_request_opp(cpu, capacity, freq_new,
|
trace_cpufreq_sched_request_opp(cpu, capacity, freq_new,
|
||||||
gd->requested_freq);
|
gd->requested_freq);
|
||||||
|
|
||||||
if (freq_new == gd->requested_freq)
|
if (freq_new == gd->requested_freq)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -246,10 +261,17 @@ static inline void clear_sched_freq(void)
|
||||||
static_key_slow_dec(&__sched_freq);
|
static_key_slow_dec(&__sched_freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct attribute_group sched_attr_group_gov_pol;
|
||||||
|
static struct attribute_group *get_sysfs_attr(void)
|
||||||
|
{
|
||||||
|
return &sched_attr_group_gov_pol;
|
||||||
|
}
|
||||||
|
|
||||||
static int cpufreq_sched_policy_init(struct cpufreq_policy *policy)
|
static int cpufreq_sched_policy_init(struct cpufreq_policy *policy)
|
||||||
{
|
{
|
||||||
struct gov_data *gd;
|
struct gov_data *gd;
|
||||||
int cpu;
|
int cpu;
|
||||||
|
int rc;
|
||||||
|
|
||||||
for_each_cpu(cpu, policy->cpus)
|
for_each_cpu(cpu, policy->cpus)
|
||||||
memset(&per_cpu(cpu_sched_capacity_reqs, cpu), 0,
|
memset(&per_cpu(cpu_sched_capacity_reqs, cpu), 0,
|
||||||
|
@ -259,11 +281,20 @@ static int cpufreq_sched_policy_init(struct cpufreq_policy *policy)
|
||||||
if (!gd)
|
if (!gd)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
gd->throttle_nsec = policy->cpuinfo.transition_latency ?
|
gd->up_throttle_nsec = policy->cpuinfo.transition_latency ?
|
||||||
policy->cpuinfo.transition_latency :
|
policy->cpuinfo.transition_latency :
|
||||||
THROTTLE_NSEC;
|
THROTTLE_UP_NSEC;
|
||||||
|
gd->down_throttle_nsec = THROTTLE_DOWN_NSEC;
|
||||||
pr_debug("%s: throttle threshold = %u [ns]\n",
|
pr_debug("%s: throttle threshold = %u [ns]\n",
|
||||||
__func__, gd->throttle_nsec);
|
__func__, gd->up_throttle_nsec);
|
||||||
|
|
||||||
|
gd->max = policy->max;
|
||||||
|
|
||||||
|
rc = sysfs_create_group(get_governor_parent_kobj(policy), get_sysfs_attr());
|
||||||
|
if (rc) {
|
||||||
|
pr_err("%s: couldn't create sysfs attributes: %d\n", __func__, rc);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
if (cpufreq_driver_is_slow()) {
|
if (cpufreq_driver_is_slow()) {
|
||||||
cpufreq_driver_slow = true;
|
cpufreq_driver_slow = true;
|
||||||
|
@ -301,6 +332,8 @@ static int cpufreq_sched_policy_exit(struct cpufreq_policy *policy)
|
||||||
put_task_struct(gd->task);
|
put_task_struct(gd->task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sysfs_remove_group(get_governor_parent_kobj(policy), get_sysfs_attr());
|
||||||
|
|
||||||
policy->governor_data = NULL;
|
policy->governor_data = NULL;
|
||||||
|
|
||||||
kfree(gd);
|
kfree(gd);
|
||||||
|
@ -317,6 +350,32 @@ static int cpufreq_sched_start(struct cpufreq_policy *policy)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cpufreq_sched_limits(struct cpufreq_policy *policy)
|
||||||
|
{
|
||||||
|
struct gov_data *gd;
|
||||||
|
|
||||||
|
pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz\n",
|
||||||
|
policy->cpu, policy->min, policy->max,
|
||||||
|
policy->cur);
|
||||||
|
|
||||||
|
if (!down_write_trylock(&policy->rwsem))
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
* Need to keep track of highest max frequency for
|
||||||
|
* capacity calculations
|
||||||
|
*/
|
||||||
|
gd = policy->governor_data;
|
||||||
|
if (gd->max < policy->max)
|
||||||
|
gd->max = policy->max;
|
||||||
|
|
||||||
|
if (policy->max < policy->cur)
|
||||||
|
__cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H);
|
||||||
|
else if (policy->min > policy->cur)
|
||||||
|
__cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L);
|
||||||
|
|
||||||
|
up_write(&policy->rwsem);
|
||||||
|
}
|
||||||
|
|
||||||
static int cpufreq_sched_stop(struct cpufreq_policy *policy)
|
static int cpufreq_sched_stop(struct cpufreq_policy *policy)
|
||||||
{
|
{
|
||||||
int cpu;
|
int cpu;
|
||||||
|
@ -340,11 +399,95 @@ static int cpufreq_sched_setup(struct cpufreq_policy *policy,
|
||||||
case CPUFREQ_GOV_STOP:
|
case CPUFREQ_GOV_STOP:
|
||||||
return cpufreq_sched_stop(policy);
|
return cpufreq_sched_stop(policy);
|
||||||
case CPUFREQ_GOV_LIMITS:
|
case CPUFREQ_GOV_LIMITS:
|
||||||
|
cpufreq_sched_limits(policy);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tunables */
|
||||||
|
static ssize_t show_up_throttle_nsec(struct gov_data *gd, char *buf)
|
||||||
|
{
|
||||||
|
return sprintf(buf, "%u\n", gd->up_throttle_nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t store_up_throttle_nsec(struct gov_data *gd,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
long unsigned int val;
|
||||||
|
|
||||||
|
ret = kstrtoul(buf, 0, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
gd->up_throttle_nsec = val;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t show_down_throttle_nsec(struct gov_data *gd, char *buf)
|
||||||
|
{
|
||||||
|
return sprintf(buf, "%u\n", gd->down_throttle_nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t store_down_throttle_nsec(struct gov_data *gd,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
long unsigned int val;
|
||||||
|
|
||||||
|
ret = kstrtoul(buf, 0, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
gd->down_throttle_nsec = val;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create show/store routines
|
||||||
|
* - sys: One governor instance for complete SYSTEM
|
||||||
|
* - pol: One governor instance per struct cpufreq_policy
|
||||||
|
*/
|
||||||
|
#define show_gov_pol_sys(file_name) \
|
||||||
|
static ssize_t show_##file_name##_gov_pol \
|
||||||
|
(struct cpufreq_policy *policy, char *buf) \
|
||||||
|
{ \
|
||||||
|
return show_##file_name(policy->governor_data, buf); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define store_gov_pol_sys(file_name) \
|
||||||
|
static ssize_t store_##file_name##_gov_pol \
|
||||||
|
(struct cpufreq_policy *policy, const char *buf, size_t count) \
|
||||||
|
{ \
|
||||||
|
return store_##file_name(policy->governor_data, buf, count); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define gov_pol_attr_rw(_name) \
|
||||||
|
static struct freq_attr _name##_gov_pol = \
|
||||||
|
__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol)
|
||||||
|
|
||||||
|
#define show_store_gov_pol_sys(file_name) \
|
||||||
|
show_gov_pol_sys(file_name); \
|
||||||
|
store_gov_pol_sys(file_name)
|
||||||
|
#define tunable_handlers(file_name) \
|
||||||
|
show_gov_pol_sys(file_name); \
|
||||||
|
store_gov_pol_sys(file_name); \
|
||||||
|
gov_pol_attr_rw(file_name)
|
||||||
|
|
||||||
|
tunable_handlers(down_throttle_nsec);
|
||||||
|
tunable_handlers(up_throttle_nsec);
|
||||||
|
|
||||||
|
/* Per policy governor instance */
|
||||||
|
static struct attribute *sched_attributes_gov_pol[] = {
|
||||||
|
&up_throttle_nsec_gov_pol.attr,
|
||||||
|
&down_throttle_nsec_gov_pol.attr,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct attribute_group sched_attr_group_gov_pol = {
|
||||||
|
.attrs = sched_attributes_gov_pol,
|
||||||
|
.name = "sched",
|
||||||
|
};
|
||||||
|
|
||||||
#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SCHED
|
#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SCHED
|
||||||
static
|
static
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue