Skip to content

Commit

Permalink
cpufreq: governor: Implement per policy instances of governors
Browse files Browse the repository at this point in the history
Currently, there can't be multiple instances of single governor_type.
If we have a multi-package system, where we have multiple instances
of struct policy (per package), we can't have multiple instances of
same governor. i.e. We can't have multiple instances of ondemand
governor for multiple packages.

Governors directory in sysfs is created at /sys/devices/system/cpu/cpufreq/
governor-name/. Which again reflects that there can be only one
instance of a governor_type in the system.

This is a bottleneck for multicluster system, where we want different
packages to use same governor type, but with different tunables.

This patch uses the infrastructure provided by earlier patch and
implements init/exit routines for ondemand and conservative
governors.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
Viresh Kumar authored and Rafael J. Wysocki committed Mar 31, 2013
1 parent 7bd353a commit 4d5dcc4
Show file tree
Hide file tree
Showing 6 changed files with 538 additions and 270 deletions.
15 changes: 11 additions & 4 deletions drivers/cpufreq/cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ void disable_cpufreq(void)
static LIST_HEAD(cpufreq_governor_list);
static DEFINE_MUTEX(cpufreq_governor_mutex);

bool have_governor_per_policy(void)
{
return cpufreq_driver->have_governor_per_policy;
}

static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs)
{
struct cpufreq_policy *data;
Expand Down Expand Up @@ -1546,10 +1551,12 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
policy->cpu, event);
ret = policy->governor->governor(policy, event);

if (event == CPUFREQ_GOV_START)
policy->governor->initialized++;
else if (event == CPUFREQ_GOV_STOP)
policy->governor->initialized--;
if (!ret) {
if (event == CPUFREQ_GOV_POLICY_INIT)
policy->governor->initialized++;
else if (event == CPUFREQ_GOV_POLICY_EXIT)
policy->governor->initialized--;
}

/* we keep one module reference alive for
each CPU governed by this CPU */
Expand Down
193 changes: 118 additions & 75 deletions drivers/cpufreq/cpufreq_conservative.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/percpu-defs.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/types.h>

Expand All @@ -31,17 +32,8 @@
#define DEF_SAMPLING_DOWN_FACTOR (1)
#define MAX_SAMPLING_DOWN_FACTOR (10)

static struct dbs_data cs_dbs_data;
static DEFINE_PER_CPU(struct cs_cpu_dbs_info_s, cs_cpu_dbs_info);

static struct cs_dbs_tuners cs_tuners = {
.up_threshold = DEF_FREQUENCY_UP_THRESHOLD,
.down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD,
.sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR,
.ignore_nice = 0,
.freq_step = 5,
};

/*
* Every sampling_rate, we check, if current idle time is less than 20%
* (default), then we try to increase frequency Every sampling_rate *
Expand All @@ -55,24 +47,26 @@ static void cs_check_cpu(int cpu, unsigned int load)
{
struct cs_cpu_dbs_info_s *dbs_info = &per_cpu(cs_cpu_dbs_info, cpu);
struct cpufreq_policy *policy = dbs_info->cdbs.cur_policy;
struct dbs_data *dbs_data = policy->governor_data;
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
unsigned int freq_target;

/*
* break out if we 'cannot' reduce the speed as the user might
* want freq_step to be zero
*/
if (cs_tuners.freq_step == 0)
if (cs_tuners->freq_step == 0)
return;

/* Check for frequency increase */
if (load > cs_tuners.up_threshold) {
if (load > cs_tuners->up_threshold) {
dbs_info->down_skip = 0;

/* if we are already at full speed then break out early */
if (dbs_info->requested_freq == policy->max)
return;

freq_target = (cs_tuners.freq_step * policy->max) / 100;
freq_target = (cs_tuners->freq_step * policy->max) / 100;

/* max freq cannot be less than 100. But who knows.... */
if (unlikely(freq_target == 0))
Expand All @@ -92,8 +86,8 @@ static void cs_check_cpu(int cpu, unsigned int load)
* support the current CPU usage without triggering the up policy. To be
* safe, we focus 10 points under the threshold.
*/
if (load < (cs_tuners.down_threshold - 10)) {
freq_target = (cs_tuners.freq_step * policy->max) / 100;
if (load < (cs_tuners->down_threshold - 10)) {
freq_target = (cs_tuners->freq_step * policy->max) / 100;

dbs_info->requested_freq -= freq_target;
if (dbs_info->requested_freq < policy->min)
Expand All @@ -119,11 +113,13 @@ static void cs_dbs_timer(struct work_struct *work)
unsigned int cpu = dbs_info->cdbs.cur_policy->cpu;
struct cs_cpu_dbs_info_s *core_dbs_info = &per_cpu(cs_cpu_dbs_info,
cpu);
int delay = delay_for_sampling_rate(cs_tuners.sampling_rate);
struct dbs_data *dbs_data = dbs_info->cdbs.cur_policy->governor_data;
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
int delay = delay_for_sampling_rate(cs_tuners->sampling_rate);

mutex_lock(&core_dbs_info->cdbs.timer_mutex);
if (need_load_eval(&core_dbs_info->cdbs, cs_tuners.sampling_rate))
dbs_check_cpu(&cs_dbs_data, cpu);
if (need_load_eval(&core_dbs_info->cdbs, cs_tuners->sampling_rate))
dbs_check_cpu(dbs_data, cpu);

schedule_delayed_work_on(smp_processor_id(), dw, delay);
mutex_unlock(&core_dbs_info->cdbs.timer_mutex);
Expand Down Expand Up @@ -154,74 +150,74 @@ static int dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
}

/************************** sysfs interface ************************/
static ssize_t show_sampling_rate_min(struct kobject *kobj,
struct attribute *attr, char *buf)
{
return sprintf(buf, "%u\n", cs_dbs_data.min_sampling_rate);
}
static struct common_dbs_data cs_dbs_cdata;

static ssize_t store_sampling_down_factor(struct kobject *a,
struct attribute *b,
const char *buf, size_t count)
static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data,
const char *buf, size_t count)
{
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);

if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1)
return -EINVAL;

cs_tuners.sampling_down_factor = input;
cs_tuners->sampling_down_factor = input;
return count;
}

static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
static ssize_t store_sampling_rate(struct dbs_data *dbs_data, const char *buf,
size_t count)
{
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);

if (ret != 1)
return -EINVAL;

cs_tuners.sampling_rate = max(input, cs_dbs_data.min_sampling_rate);
cs_tuners->sampling_rate = max(input, dbs_data->min_sampling_rate);
return count;
}

static ssize_t store_up_threshold(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
static ssize_t store_up_threshold(struct dbs_data *dbs_data, const char *buf,
size_t count)
{
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);

if (ret != 1 || input > 100 || input <= cs_tuners.down_threshold)
if (ret != 1 || input > 100 || input <= cs_tuners->down_threshold)
return -EINVAL;

cs_tuners.up_threshold = input;
cs_tuners->up_threshold = input;
return count;
}

static ssize_t store_down_threshold(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
static ssize_t store_down_threshold(struct dbs_data *dbs_data, const char *buf,
size_t count)
{
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);

/* cannot be lower than 11 otherwise freq will not fall */
if (ret != 1 || input < 11 || input > 100 ||
input >= cs_tuners.up_threshold)
input >= cs_tuners->up_threshold)
return -EINVAL;

cs_tuners.down_threshold = input;
cs_tuners->down_threshold = input;
return count;
}

static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf,
size_t count)
{
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
unsigned int input, j;
int ret;

Expand All @@ -232,27 +228,28 @@ static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b,
if (input > 1)
input = 1;

if (input == cs_tuners.ignore_nice) /* nothing to do */
if (input == cs_tuners->ignore_nice) /* nothing to do */
return count;

cs_tuners.ignore_nice = input;
cs_tuners->ignore_nice = input;

/* we need to re-evaluate prev_cpu_idle */
for_each_online_cpu(j) {
struct cs_cpu_dbs_info_s *dbs_info;
dbs_info = &per_cpu(cs_cpu_dbs_info, j);
dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j,
&dbs_info->cdbs.prev_cpu_wall);
if (cs_tuners.ignore_nice)
if (cs_tuners->ignore_nice)
dbs_info->cdbs.prev_cpu_nice =
kcpustat_cpu(j).cpustat[CPUTIME_NICE];
}
return count;
}

static ssize_t store_freq_step(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
static ssize_t store_freq_step(struct dbs_data *dbs_data, const char *buf,
size_t count)
{
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
unsigned int input;
int ret;
ret = sscanf(buf, "%u", &input);
Expand All @@ -267,43 +264,88 @@ static ssize_t store_freq_step(struct kobject *a, struct attribute *b,
* no need to test here if freq_step is zero as the user might actually
* want this, they would be crazy though :)
*/
cs_tuners.freq_step = input;
cs_tuners->freq_step = input;
return count;
}

show_one(cs, sampling_rate, sampling_rate);
show_one(cs, sampling_down_factor, sampling_down_factor);
show_one(cs, up_threshold, up_threshold);
show_one(cs, down_threshold, down_threshold);
show_one(cs, ignore_nice_load, ignore_nice);
show_one(cs, freq_step, freq_step);

define_one_global_rw(sampling_rate);
define_one_global_rw(sampling_down_factor);
define_one_global_rw(up_threshold);
define_one_global_rw(down_threshold);
define_one_global_rw(ignore_nice_load);
define_one_global_rw(freq_step);
define_one_global_ro(sampling_rate_min);

static struct attribute *dbs_attributes[] = {
&sampling_rate_min.attr,
&sampling_rate.attr,
&sampling_down_factor.attr,
&up_threshold.attr,
&down_threshold.attr,
&ignore_nice_load.attr,
&freq_step.attr,
show_store_one(cs, sampling_rate);
show_store_one(cs, sampling_down_factor);
show_store_one(cs, up_threshold);
show_store_one(cs, down_threshold);
show_store_one(cs, ignore_nice);
show_store_one(cs, freq_step);
declare_show_sampling_rate_min(cs);

gov_sys_pol_attr_rw(sampling_rate);
gov_sys_pol_attr_rw(sampling_down_factor);
gov_sys_pol_attr_rw(up_threshold);
gov_sys_pol_attr_rw(down_threshold);
gov_sys_pol_attr_rw(ignore_nice);
gov_sys_pol_attr_rw(freq_step);
gov_sys_pol_attr_ro(sampling_rate_min);

static struct attribute *dbs_attributes_gov_sys[] = {
&sampling_rate_min_gov_sys.attr,
&sampling_rate_gov_sys.attr,
&sampling_down_factor_gov_sys.attr,
&up_threshold_gov_sys.attr,
&down_threshold_gov_sys.attr,
&ignore_nice_gov_sys.attr,
&freq_step_gov_sys.attr,
NULL
};

static struct attribute_group cs_attr_group = {
.attrs = dbs_attributes,
static struct attribute_group cs_attr_group_gov_sys = {
.attrs = dbs_attributes_gov_sys,
.name = "conservative",
};

static struct attribute *dbs_attributes_gov_pol[] = {
&sampling_rate_min_gov_pol.attr,
&sampling_rate_gov_pol.attr,
&sampling_down_factor_gov_pol.attr,
&up_threshold_gov_pol.attr,
&down_threshold_gov_pol.attr,
&ignore_nice_gov_pol.attr,
&freq_step_gov_pol.attr,
NULL
};

static struct attribute_group cs_attr_group_gov_pol = {
.attrs = dbs_attributes_gov_pol,
.name = "conservative",
};

/************************** sysfs end ************************/

static int cs_init(struct dbs_data *dbs_data)
{
struct cs_dbs_tuners *tuners;

tuners = kzalloc(sizeof(struct cs_dbs_tuners), GFP_KERNEL);
if (!tuners) {
pr_err("%s: kzalloc failed\n", __func__);
return -ENOMEM;
}

tuners->up_threshold = DEF_FREQUENCY_UP_THRESHOLD;
tuners->down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD;
tuners->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR;
tuners->ignore_nice = 0;
tuners->freq_step = 5;

dbs_data->tuners = tuners;
dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO *
jiffies_to_usecs(10);
mutex_init(&dbs_data->mutex);
return 0;
}

static void cs_exit(struct dbs_data *dbs_data)
{
kfree(dbs_data->tuners);
}

define_get_cpu_dbs_routines(cs_cpu_dbs_info);

static struct notifier_block cs_cpufreq_notifier_block = {
Expand All @@ -314,21 +356,23 @@ static struct cs_ops cs_ops = {
.notifier_block = &cs_cpufreq_notifier_block,
};

static struct dbs_data cs_dbs_data = {
static struct common_dbs_data cs_dbs_cdata = {
.governor = GOV_CONSERVATIVE,
.attr_group = &cs_attr_group,
.tuners = &cs_tuners,
.attr_group_gov_sys = &cs_attr_group_gov_sys,
.attr_group_gov_pol = &cs_attr_group_gov_pol,
.get_cpu_cdbs = get_cpu_cdbs,
.get_cpu_dbs_info_s = get_cpu_dbs_info_s,
.gov_dbs_timer = cs_dbs_timer,
.gov_check_cpu = cs_check_cpu,
.gov_ops = &cs_ops,
.init = cs_init,
.exit = cs_exit,
};

static int cs_cpufreq_governor_dbs(struct cpufreq_policy *policy,
unsigned int event)
{
return cpufreq_governor_dbs(&cs_dbs_data, policy, event);
return cpufreq_governor_dbs(policy, &cs_dbs_cdata, event);
}

#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
Expand All @@ -343,7 +387,6 @@ struct cpufreq_governor cpufreq_gov_conservative = {

static int __init cpufreq_gov_dbs_init(void)
{
mutex_init(&cs_dbs_data.mutex);
return cpufreq_register_governor(&cpufreq_gov_conservative);
}

Expand Down
Loading

0 comments on commit 4d5dcc4

Please sign in to comment.