blob: 1516f2d5b94ff3e975dd8f5b29c4fd25278c3c41 [file] [log] [blame]
/*
* Energy efficient cpu selection
*
* Copyright (C) 2018 Samsung Electronics Co., Ltd
* Park Bumgyu <bumgyu.park@samsung.com>
*/
#include <linux/cpufreq.h>
#include <trace/events/ems.h>
#include "../sched.h"
#include "ems.h"
/*
* The compute capacity, power consumption at this compute capacity and
* frequency of state. The cap and power are used to find the energy
* efficiency cpu, and the frequency is used to create the capacity table.
*/
struct energy_state {
unsigned long frequency;
unsigned long cap;
unsigned long power;
/* for sse */
unsigned long cap_s;
unsigned long power_s;
};
/*
* Each cpu can have its own mips, coefficient and energy table. Generally,
* cpus in the same frequency domain have the same mips, coefficient and
* energy table.
*/
struct energy_table {
unsigned int mips;
unsigned int coefficient;
unsigned int mips_s;
unsigned int coefficient_s;
struct energy_state *states;
unsigned int nr_states;
};
DEFINE_PER_CPU(struct energy_table, energy_table);
/* check the status of energy table */
bool energy_initialized;
void set_energy_table_status(bool status)
{
energy_initialized = status;
}
bool get_energy_table_status(void)
{
return energy_initialized;
}
inline unsigned int get_cpu_mips(unsigned int cpu, int sse)
{
return sse ? per_cpu(energy_table, cpu).mips_s :
per_cpu(energy_table, cpu).mips;
}
unsigned int get_cpu_max_capacity(unsigned int cpu, int sse)
{
struct energy_table *table = &per_cpu(energy_table, cpu);
/* If energy table wasn't initialized, return 0 as capacity */
if (!table->states)
return 0;
return sse ? table->states[table->nr_states - 1].cap_s :
table->states[table->nr_states - 1].cap;
}
unsigned long get_freq_cap(unsigned int cpu, unsigned long freq, int sse)
{
struct energy_table *table = &per_cpu(energy_table, cpu);
struct energy_state *state = NULL;
int i;
for (i = 0; i < table->nr_states; i++) {
if (table->states[i].frequency >= freq) {
state = &table->states[i];
break;
}
}
if (!state)
return 0;
return sse ? state->cap_s : state->cap;
}
extern unsigned long __ml_cpu_util(int cpu, int sse);
unsigned int calculate_energy(struct task_struct *p, int target_cpu)
{
unsigned long util[NR_CPUS] = {0, };
unsigned int total_energy = 0;
int cpu;
if (!get_energy_table_status())
return UINT_MAX;
/*
* 0. Calculate utilization of the entire active cpu when task
* is assigned to target cpu.
*/
for_each_cpu(cpu, cpu_active_mask) {
if (unlikely(cpu == target_cpu))
util[cpu] = ml_task_attached_cpu_util(cpu, p);
else
util[cpu] = ml_cpu_util_wake(cpu, p);
}
for_each_cpu(cpu, cpu_active_mask) {
struct energy_table *table;
unsigned long max_util = 0, util_sum = 0, util_sum_s = 0;
unsigned long capacity = SCHED_CAPACITY_SCALE;
unsigned long capacity_s = SCHED_CAPACITY_SCALE;
int i, cap_idx;
/* Compute coregroup energy with only one cpu per coregroup */
if (cpu != cpumask_first(cpu_coregroup_mask(cpu)))
continue;
/*
* 1. The cpu in the coregroup has same capacity and the
* capacity depends on the cpu that has the biggest
* utilization. Find biggest utilization in the coregroup
* to know what capacity the cpu will have.
*/
for_each_cpu(i, cpu_coregroup_mask(cpu))
if (util[i] > max_util)
max_util = util[i];
/*
* 2. Find the capacity according to biggest utilization in
* coregroup.
*/
table = &per_cpu(energy_table, cpu);
cap_idx = table->nr_states - 1;
for (i = 0; i < table->nr_states; i++) {
if (table->states[i].cap >= max_util) {
capacity = table->states[i].cap;
capacity_s = table->states[i].cap_s;
cap_idx = i;
break;
}
}
/*
* 3. Get the utilization sum of coregroup. Since cpu
* utilization of CFS reflects the performance of cpu,
* normalize the utilization to calculate the amount of
* cpu usuage that excludes cpu performance.
*/
for_each_cpu(i, cpu_coregroup_mask(cpu)) {
unsigned long uss_util = __ml_cpu_util(i, 0);
unsigned long sse_util = __ml_cpu_util(i, 1);
if (i == task_cpu(p)) {
if (p->sse)
sse_util -= min_t(unsigned long, sse_util, ml_task_util_est(p));
else
uss_util -= min_t(unsigned long, uss_util, ml_task_util_est(p));
}
if (i == target_cpu) {
if (p->sse)
sse_util += ml_task_util_est(p);
else
uss_util += ml_task_util_est(p);
}
/* task utilization exceeds capacity of cpu */
if (util[i] >= capacity) {
util_sum += ml_cpu_util_ratio(cpu, 0);
util_sum_s += ml_cpu_util_ratio(cpu, 1);
continue;
}
/* normalize cpu utilization */
util_sum += (uss_util << SCHED_CAPACITY_SHIFT) / capacity;
util_sum_s += (sse_util << SCHED_CAPACITY_SHIFT) / capacity_s;
}
/*
* 4. compute active energy
*/
total_energy += util_sum * table->states[cap_idx].power;
total_energy += util_sum_s * table->states[cap_idx].power_s;
}
return total_energy;
}
static int find_min_util_cpu(const struct cpumask *mask, struct task_struct *p)
{
unsigned long min_util = ULONG_MAX;
int min_util_cpu = -1;
int cpu;
/* Find energy efficient cpu in each coregroup. */
for_each_cpu_and(cpu, mask, cpu_active_mask) {
unsigned long capacity_orig = capacity_orig_of(cpu);
unsigned long util = ml_task_attached_cpu_util(cpu, p);
/* Skip over-capacity cpu */
if (util >= capacity_orig)
continue;
/*
* Choose min util cpu within coregroup as candidates.
* Choosing a min util cpu is most likely to handle
* wake-up task without increasing the frequecncy.
*/
if (util < min_util) {
min_util = util;
min_util_cpu = cpu;
}
}
return min_util_cpu;
}
struct eco_env {
struct task_struct *p;
int prev_cpu;
};
static int select_eco_cpu(struct eco_env *eenv)
{
unsigned int best_energy = UINT_MAX;
unsigned int prev_energy;
int eco_cpu = eenv->prev_cpu;
int cpu, best_cpu = -1;
/*
* It is meaningless to find an energy cpu when the energy table is
* not created or has not been created yet.
*/
if (!per_cpu(energy_table, eenv->prev_cpu).nr_states)
return eenv->prev_cpu;
for_each_cpu(cpu, cpu_active_mask) {
struct cpumask mask;
int energy_cpu;
if (cpu != cpumask_first(cpu_coregroup_mask(cpu)))
continue;
cpumask_and(&mask, cpu_coregroup_mask(cpu), tsk_cpus_allowed(eenv->p));
/*
* Checking prev cpu is meaningless, because the energy of prev cpu
* will be compared to best cpu at last
*/
cpumask_clear_cpu(eenv->prev_cpu, &mask);
if (cpumask_empty(&mask))
continue;
/*
* Select the best target, which is expected to consume the
* lowest energy among the min util cpu for each coregroup.
*/
energy_cpu = find_min_util_cpu(&mask, eenv->p);
if (cpu_selected(energy_cpu)) {
unsigned int energy = calculate_energy(eenv->p, energy_cpu);
if (energy < best_energy) {
best_energy = energy;
best_cpu = energy_cpu;
}
}
}
if (!cpu_selected(best_cpu))
return -1;
/*
* Compare prev cpu to best cpu to determine whether keeping the task
* on PREV CPU and sending the task to BEST CPU is beneficial for
* energy.
* An energy saving is considered meaningful if it reduces the energy
* consumption of PREV CPU candidate by at least ~1.56%.
*/
prev_energy = calculate_energy(eenv->p, eenv->prev_cpu);
if (prev_energy - (prev_energy >> 6) > best_energy)
eco_cpu = best_cpu;
trace_ems_select_eco_cpu(eenv->p, eco_cpu, eenv->prev_cpu, best_cpu,
prev_energy, best_energy);
return eco_cpu;
}
int select_energy_cpu(struct task_struct *p, int prev_cpu, int sd_flag, int sync)
{
struct sched_domain *sd = NULL;
int cpu = smp_processor_id();
struct eco_env eenv = {
.p = p,
.prev_cpu = prev_cpu,
};
if (!sched_feat(ENERGY_AWARE))
return -1;
/*
* Energy-aware wakeup placement on overutilized cpu is hard to get
* energy gain.
*/
rcu_read_lock();
sd = rcu_dereference_sched(cpu_rq(prev_cpu)->sd);
if (!sd || sd->shared->overutilized) {
rcu_read_unlock();
return -1;
}
rcu_read_unlock();
/*
* We cannot do energy-aware wakeup placement sensibly for tasks
* with 0 utilization, so let them be placed according to the normal
* strategy.
*/
if (!task_util(p))
return -1;
if (sysctl_sched_sync_hint_enable && sync)
if (cpumask_test_cpu(cpu, &p->cpus_allowed))
return cpu;
/*
* Find eco-friendly target.
* After selecting the best cpu according to strategy,
* we choose a cpu that is energy efficient compared to prev cpu.
*/
return select_eco_cpu(&eenv);
}
static int c_weight[NR_CPUS];
static int e_weight[NR_CPUS];
static int tiny_task_level = 10;
unsigned int calculate_efficiency(struct task_struct *p, int target_cpu)
{
unsigned long util[NR_CPUS] = {0, };
unsigned int energy, eff;
unsigned int cap_idx;
struct energy_table *table;
unsigned long capacity = SCHED_CAPACITY_SCALE;
unsigned long capacity_s = SCHED_CAPACITY_SCALE;
unsigned long max_util = 0, uss_util, sse_util;
unsigned long uss_ratio, sse_ratio;
int cpu, i;
if (!get_energy_table_status())
return UINT_MAX;
/*
* 0. Calculate utilization of the entire active cpu when task
* is assigned to target cpu.
*/
for_each_cpu(cpu, cpu_coregroup_mask(target_cpu)) {
if (unlikely(cpu == target_cpu))
util[cpu] = ml_task_attached_cpu_util(cpu, p);
else
util[cpu] = ml_cpu_util_wake(cpu, p);
/*
* 1. The cpu in the coregroup has same capacity and the
* capacity depends on the cpu that has the biggest
* utilization. Find biggest utilization in the coregroup
* to know what capacity the cpu will have.
*/
if (util[cpu] > max_util)
max_util = util[cpu];
}
/*
* 2. Find the capacity according to biggest utilization in
* coregroup.
*/
table = &per_cpu(energy_table, target_cpu);
cap_idx = table->nr_states - 1;
for (i = 0; i < table->nr_states; i++) {
if (table->states[i].cap >= max_util) {
capacity = table->states[i].cap;
capacity_s = table->states[i].cap_s;
cap_idx = i;
break;
}
}
if (util[target_cpu] >= capacity) {
uss_util = ml_cpu_util_ratio(target_cpu, 0);
sse_util = ml_cpu_util_ratio(target_cpu, 1);
goto cal_eff;
}
uss_util = __ml_cpu_util(target_cpu, 0);
sse_util = __ml_cpu_util(target_cpu, 1);
if (target_cpu != task_cpu(p)) {
if (p->sse)
sse_util += ml_task_util(p);
else
uss_util += ml_task_util(p);
}
if (sched_feat(UTIL_EST)) {
unsigned int uss_util_est = __ml_cpu_util_est(target_cpu, 0);
unsigned int sse_util_est = __ml_cpu_util_est(target_cpu, 1);
if (target_cpu != task_cpu(p)) {
if (p->sse)
sse_util += ml_task_util_est(p);
else
uss_util += ml_task_util_est(p);
}
uss_util = max_t(unsigned long, uss_util, uss_util_est);
sse_util = max_t(unsigned long, sse_util, sse_util_est);
}
cal_eff:
uss_ratio = (uss_util << SCHED_CAPACITY_SHIFT) / capacity;
sse_ratio = (sse_util << SCHED_CAPACITY_SHIFT) / capacity_s;
energy = uss_ratio * table->states[cap_idx].power;
energy += sse_ratio * table->states[cap_idx].power_s;
if (p->sse)
capacity = capacity_s;
eff = ((energy * e_weight[target_cpu]) << 10) / (capacity * c_weight[target_cpu]);
return eff;
}
static int select_eff_cpu(struct eco_env *eenv)
{
unsigned int best_energy = UINT_MAX;
int cpu, best_cpu = -1;
/*
* It is meaningless to find an energy cpu when the energy table is
* not created or has not been created yet.
*/
if (!per_cpu(energy_table, eenv->prev_cpu).nr_states)
return find_min_util_cpu(cpu_coregroup_mask(4), eenv->p);
if (ml_task_util_est(eenv->p) <= tiny_task_level)
return find_min_util_cpu(cpu_coregroup_mask(4), eenv->p);
for_each_cpu(cpu, cpu_active_mask) {
unsigned int energy;
if (!cpumask_test_cpu(cpu, tsk_cpus_allowed(eenv->p)))
continue;
energy = calculate_efficiency(eenv->p, cpu);
if (energy < best_energy) {
best_energy = energy;
best_cpu = cpu;
}
}
return best_cpu;
}
int select_best_cpu(struct task_struct *p, int prev_cpu, int sd_flag, int sync)
{
int cpu = smp_processor_id();
struct eco_env eenv = {
.p = p,
.prev_cpu = prev_cpu,
};
if (!sched_feat(ENERGY_AWARE))
return -1;
/*
* We cannot do energy-aware wakeup placement sensibly for tasks
* with 0 utilization, so let them be placed according to the normal
* strategy.
*/
if (sysctl_sched_sync_hint_enable && sync)
if (cpumask_test_cpu(cpu, &p->cpus_allowed))
return cpu;
/*
* Find eco-friendly target.
* After selecting the best cpu according to strategy,
* we choose a cpu that is energy efficient compared to prev cpu.
*/
return select_eff_cpu(&eenv);
}
#ifdef CONFIG_SIMPLIFIED_ENERGY_MODEL
static void
fill_power_table(struct energy_table *table, int table_size,
unsigned long *f_table, unsigned int *v_table,
int max_f, int min_f)
{
int i, index = 0;
int c = table->coefficient, c_s = table->coefficient_s, v;
unsigned long f, power, power_s;
/* energy table and frequency table are inverted */
for (i = table_size - 1; i >= 0; i--) {
if (f_table[i] > max_f || f_table[i] < min_f)
continue;
f = f_table[i] / 1000; /* KHz -> MHz */
v = v_table[i] / 1000; /* uV -> mV */
/*
* power = coefficent * frequency * voltage^2
*/
power = c * f * v * v;
power_s = c_s * f * v * v;
/*
* Generally, frequency is more than treble figures in MHz and
* voltage is also more then treble figures in mV, so the
* calculated power is larger than 10^9. For convenience of
* calculation, divide the value by 10^9.
*/
do_div(power, 1000000000);
do_div(power_s, 1000000000);
table->states[index].power = power;
table->states[index].power_s = power_s;
/* save frequency to energy table */
table->states[index].frequency = f_table[i];
index++;
}
}
static void
fill_cap_table(struct energy_table *table, int max_mips, unsigned long max_mips_freq)
{
int i, m = table->mips, m_s = table->mips_s;
unsigned long f;
for (i = 0; i < table->nr_states; i++) {
f = table->states[i].frequency;
/*
* capacity = freq/max_freq * mips/max_mips * 1024
*/
table->states[i].cap = f * m * 1024 / max_mips_freq / max_mips;
table->states[i].cap_s = f * m_s * 1024 / max_mips_freq / max_mips;
}
}
static void show_energy_table(struct energy_table *table, int cpu)
{
int i;
pr_info("[Energy Table: cpu%d]\n", cpu);
for (i = 0; i < table->nr_states; i++) {
pr_info("[%2d] cap=%4lu power=%4lu | cap(S)=%4lu power(S)=%4lu\n",
i, table->states[i].cap, table->states[i].power,
table->states[i].cap_s, table->states[i].power_s);
}
}
static DEFINE_PER_CPU(unsigned long, cpu_capacity) = SCHED_CAPACITY_SCALE;
static DEFINE_PER_CPU(unsigned long, cpu_capacity_s) = SCHED_CAPACITY_SCALE;
static DEFINE_PER_CPU(unsigned long, cpufreq_capacity) = SCHED_CAPACITY_SCALE;
static DEFINE_PER_CPU(unsigned long, cpufreq_capacity_s) = SCHED_CAPACITY_SCALE;
static DEFINE_PER_CPU(unsigned long, qos_capacity) = SCHED_CAPACITY_SCALE;
static DEFINE_PER_CPU(unsigned long, qos_capacity_s) = SCHED_CAPACITY_SCALE;
unsigned long capacity_orig_of_sse(int cpu, int sse)
{
return sse ? per_cpu(cpu_capacity_s, cpu) : per_cpu(cpu_capacity, cpu);
}
static DEFINE_PER_CPU(unsigned long, cpu_capacity_ratio) = SCHED_CAPACITY_SCALE;
static DEFINE_PER_CPU(unsigned long, cpu_capacity_ratio_s) = SCHED_CAPACITY_SCALE;
unsigned long capacity_ratio(int cpu, int sse)
{
return sse ? per_cpu(cpu_capacity_ratio_s, cpu) : per_cpu(cpu_capacity_ratio, cpu);
}
static inline void update_capacity_ratio(int cpu)
{
per_cpu(cpu_capacity_ratio, cpu) = (per_cpu(cpu_capacity, cpu) << SCHED_CAPACITY_SHIFT)
/ per_cpu(cpu_capacity_s, cpu);
per_cpu(cpu_capacity_ratio_s, cpu) = (per_cpu(cpu_capacity_s, cpu) << SCHED_CAPACITY_SHIFT)
/ per_cpu(cpu_capacity, cpu);
}
/*
* Store the original capacity to update the cpu capacity according to the
* max frequency of cpufreq.
*/
static DEFINE_PER_CPU(unsigned long, cpu_orig_scale) = SCHED_CAPACITY_SCALE;
static DEFINE_PER_CPU(unsigned long, cpu_orig_scale_s) = SCHED_CAPACITY_SCALE;
#define calculate_scale(orig, max) ((orig * max) >> SCHED_CAPACITY_SHIFT)
static inline void update_capacity(int cpu)
{
unsigned long freq_cap, qos_cap;
freq_cap = per_cpu(cpufreq_capacity, cpu);
qos_cap = per_cpu(qos_capacity, cpu);
per_cpu(cpu_capacity, cpu) = min(freq_cap, qos_cap);
topology_set_cpu_scale(cpu, per_cpu(cpu_capacity, cpu));
freq_cap = per_cpu(cpufreq_capacity_s, cpu);
qos_cap = per_cpu(qos_capacity_s, cpu);
per_cpu(cpu_capacity_s, cpu) = min(freq_cap, qos_cap);
update_capacity_ratio(cpu);
}
static inline void update_cpufreq_capacity(int cpu, unsigned long max_scale)
{
unsigned long scale;
scale = calculate_scale(per_cpu(cpu_orig_scale, cpu), max_scale);
per_cpu(cpufreq_capacity, cpu) = scale;
/*
* If system does not consider sse, cpu_orig_scale_s is 0. In this
* case, cpufreq_capacity_s has same value as cpufreq_capacity.
*/
if (per_cpu(cpu_orig_scale_s, cpu))
scale = calculate_scale(per_cpu(cpu_orig_scale_s, cpu), max_scale);
per_cpu(cpufreq_capacity_s, cpu) = scale;
update_capacity(cpu);
}
static int sched_cpufreq_policy_callback(struct notifier_block *nb,
unsigned long event, void *data)
{
struct cpufreq_policy *policy = data;
unsigned long max_scale;
int cpu;
if (event != CPUFREQ_NOTIFY)
return NOTIFY_DONE;
/*
* When policy->max is pressed, the performance of the cpu is constrained.
* In the constrained state, the cpu capacity also changes, and the
* overutil condition changes accordingly, so the cpu scale is updated
* whenever policy is changed.
*/
max_scale = (policy->max << SCHED_CAPACITY_SHIFT);
max_scale /= policy->cpuinfo.max_freq;
for_each_cpu(cpu, policy->related_cpus)
update_cpufreq_capacity(cpu, max_scale);
return NOTIFY_OK;
}
static struct notifier_block sched_cpufreq_policy_notifier = {
.notifier_call = sched_cpufreq_policy_callback,
};
void update_qos_capacity(int cpu, unsigned long freq, unsigned long max)
{
unsigned long max_scale;
int i;
max_scale = (freq << SCHED_CAPACITY_SHIFT);
max_scale /= max;
for_each_cpu(i, cpu_coregroup_mask(cpu)) {
unsigned long scale;
scale = calculate_scale(per_cpu(cpu_orig_scale, i), max_scale);
per_cpu(qos_capacity, i) = scale;
if (per_cpu(cpu_orig_scale_s, i))
scale = calculate_scale(per_cpu(cpu_orig_scale_s, i), max_scale);
per_cpu(qos_capacity_s, i) = scale;
update_capacity(i);
}
}
/*
* Whenever frequency domain is registered, and energy table corresponding to
* the domain is created. Because cpu in the same frequency domain has the same
* energy table. Capacity is calculated based on the max frequency of the fastest
* cpu, so once the frequency domain of the faster cpu is regsitered, capacity
* is recomputed.
*/
void init_sched_energy_table(struct cpumask *cpus, int table_size,
unsigned long *f_table, unsigned int *v_table,
int max_f, int min_f)
{
struct energy_table *table;
int cpu, i, mips, valid_table_size = 0;
int max_mips = 0;
unsigned long max_mips_freq = 0;
int last_state;
cpumask_and(cpus, cpus, cpu_possible_mask);
if (cpumask_empty(cpus))
return;
mips = per_cpu(energy_table, cpumask_any(cpus)).mips;
for_each_cpu(cpu, cpus) {
/*
* All cpus in a frequency domain must have the same capacity
* because cpu and frequency domain always are same.
* Verifying domain is enough to check the mips so it does not
* need to check mips_s.
*/
if (mips != per_cpu(energy_table, cpu).mips) {
pr_warn("cpu%d has different cpacity!!\n", cpu);
return;
}
}
/* get size of valid frequency table to allocate energy table */
for (i = 0; i < table_size; i++) {
if (f_table[i] > max_f || f_table[i] < min_f)
continue;
valid_table_size++;
}
/* there is no valid row in the table, energy table is not created */
if (!valid_table_size)
return;
/* allocate memory for energy table and fill power table */
for_each_cpu(cpu, cpus) {
table = &per_cpu(energy_table, cpu);
table->states = kcalloc(valid_table_size,
sizeof(struct energy_state), GFP_KERNEL);
if (unlikely(!table->states))
return;
table->nr_states = valid_table_size;
fill_power_table(table, table_size, f_table, v_table, max_f, min_f);
}
/*
* Find fastest cpu among the cpu to which the energy table is allocated.
* The mips and max frequency of fastest cpu are needed to calculate
* capacity.
*/
for_each_possible_cpu(cpu) {
table = &per_cpu(energy_table, cpu);
if (!table->states)
continue;
mips = max(table->mips, table->mips_s);
if (mips > max_mips) {
max_mips = mips;
last_state = table->nr_states - 1;
max_mips_freq = table->states[last_state].frequency;
}
}
/*
* Calculate and fill capacity table.
* Recalculate the capacity whenever frequency domain changes because
* the fastest cpu may have changed and the capacity needs to be
* recalculated.
*/
for_each_possible_cpu(cpu) {
struct sched_domain *sd;
table = &per_cpu(energy_table, cpu);
if (!table->states)
continue;
fill_cap_table(table, max_mips, max_mips_freq);
show_energy_table(table, cpu);
last_state = table->nr_states - 1;
per_cpu(cpu_orig_scale_s, cpu) = table->states[last_state].cap_s;
per_cpu(cpu_orig_scale, cpu) = table->states[last_state].cap;
topology_set_cpu_scale(cpu, table->states[last_state].cap);
rcu_read_lock();
for_each_domain(cpu, sd)
update_group_capacity(sd, cpu);
rcu_read_unlock();
}
topology_update();
}
static ssize_t show_energy_weight(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int cpu, len = 0;
for_each_possible_cpu(cpu) {
len += sprintf(buf + len, "[cpu%d] perf:%d energy:%d\n",
cpu, c_weight[cpu], e_weight[cpu]);
}
return len;
}
static ssize_t store_energy_weight(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf,
size_t count)
{
int cpu, cw, ew, i;
if (sscanf(buf, "%d %d %d", &cpu, &cw, &ew) != 3)
return -EINVAL;
/* Check cpu is possible */
if (!cpumask_test_cpu(cpu, cpu_possible_mask))
return -EINVAL;
/* Check ratio isn't outrage */
if (cw < 0 || ew < 0)
return -EINVAL;
for_each_cpu(i, cpu_coregroup_mask(cpu)) {
c_weight[i] = cw;
e_weight[i] = ew;
}
return count;
}
static struct kobj_attribute energy_attr =
__ATTR(energy_weight, 0644, show_energy_weight, store_energy_weight);
static int __init init_energy_weight(void)
{
int ret;
ret = sysfs_create_file(ems_kobj, &energy_attr.attr);
if (ret)
pr_err("%s: faile to create sysfs file\n", __func__);
return 0;
}
late_initcall(init_energy_weight);
static int __init init_sched_energy_data(void)
{
struct device_node *cpu_node, *cpu_phandle;
int cpu;
for_each_possible_cpu(cpu) {
struct energy_table *table;
cpu_node = of_get_cpu_node(cpu, NULL);
if (!cpu_node) {
pr_warn("CPU device node missing for CPU %d\n", cpu);
return -ENODATA;
}
cpu_phandle = of_parse_phandle(cpu_node, "sched-energy-data", 0);
if (!cpu_phandle) {
pr_warn("CPU device node has no sched-energy-data\n");
return -ENODATA;
}
table = &per_cpu(energy_table, cpu);
if (of_property_read_u32(cpu_phandle, "capacity-mips", &table->mips)) {
pr_warn("No capacity-mips data\n");
return -ENODATA;
}
if (of_property_read_u32(cpu_phandle, "power-coefficient", &table->coefficient)) {
pr_warn("No power-coefficient data\n");
return -ENODATA;
}
/* Data for sse is OPTIONAL */
of_property_read_u32(cpu_phandle, "capacity-mips-s", &table->mips_s);
of_property_read_u32(cpu_phandle, "power-coefficient-s", &table->coefficient_s);
of_property_read_u32(cpu_phandle, "capacity-weight", &c_weight[cpu]);
of_property_read_u32(cpu_phandle, "energy-weight", &e_weight[cpu]);
of_node_put(cpu_phandle);
of_node_put(cpu_node);
pr_info("cpu%d mips=%d coefficient=%d mips_s=%d coefficient_s=%d\n",
cpu, table->mips, table->coefficient, table->mips_s, table->coefficient_s);
}
cpufreq_register_notifier(&sched_cpufreq_policy_notifier, CPUFREQ_POLICY_NOTIFIER);
return 0;
}
core_initcall(init_sched_energy_data);
#endif /* CONFIG_SIMPLIFIED_ENERGY_MODEL */