mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 14:21:47 +00:00
PM / devfreq: passive: Keep cpufreq_policy for possible cpus
The passive governor requires the cpu data to get the next target frequency of devfreq device if depending on cpu. In order to reduce the unnecessary memory data, keep cpufreq_policy data for possible cpus instead of NR_CPU. Tested-by: Chen-Yu Tsai <wenst@chromium.org> Tested-by: Johnson Wang <johnson.wang@mediatek.com> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
This commit is contained in:
parent
05723e7123
commit
26984d9d58
@ -49,6 +49,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* struct devfreq_cpu_data - Hold the per-cpu data
|
* struct devfreq_cpu_data - Hold the per-cpu data
|
||||||
|
* @node: list node
|
||||||
* @dev: reference to cpu device.
|
* @dev: reference to cpu device.
|
||||||
* @first_cpu: the cpumask of the first cpu of a policy.
|
* @first_cpu: the cpumask of the first cpu of a policy.
|
||||||
* @opp_table: reference to cpu opp table.
|
* @opp_table: reference to cpu opp table.
|
||||||
@ -60,6 +61,8 @@
|
|||||||
* This is auto-populated by the governor.
|
* This is auto-populated by the governor.
|
||||||
*/
|
*/
|
||||||
struct devfreq_cpu_data {
|
struct devfreq_cpu_data {
|
||||||
|
struct list_head node;
|
||||||
|
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
unsigned int first_cpu;
|
unsigned int first_cpu;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
/*
|
/*
|
||||||
* linux/drivers/devfreq/governor_passive.c
|
* linux/drivers/devfreq/governor_passive.c
|
||||||
*
|
*
|
||||||
@ -18,6 +18,22 @@
|
|||||||
|
|
||||||
#define HZ_PER_KHZ 1000
|
#define HZ_PER_KHZ 1000
|
||||||
|
|
||||||
|
static struct devfreq_cpu_data *
|
||||||
|
get_parent_cpu_data(struct devfreq_passive_data *p_data,
|
||||||
|
struct cpufreq_policy *policy)
|
||||||
|
{
|
||||||
|
struct devfreq_cpu_data *parent_cpu_data;
|
||||||
|
|
||||||
|
if (!p_data || !policy)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
list_for_each_entry(parent_cpu_data, &p_data->cpu_data_list, node)
|
||||||
|
if (parent_cpu_data->first_cpu == cpumask_first(policy->related_cpus))
|
||||||
|
return parent_cpu_data;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned long get_target_freq_by_required_opp(struct device *p_dev,
|
static unsigned long get_target_freq_by_required_opp(struct device *p_dev,
|
||||||
struct opp_table *p_opp_table,
|
struct opp_table *p_opp_table,
|
||||||
struct opp_table *opp_table,
|
struct opp_table *opp_table,
|
||||||
@ -51,14 +67,24 @@ static int get_target_freq_with_cpufreq(struct devfreq *devfreq,
|
|||||||
struct devfreq_passive_data *p_data =
|
struct devfreq_passive_data *p_data =
|
||||||
(struct devfreq_passive_data *)devfreq->data;
|
(struct devfreq_passive_data *)devfreq->data;
|
||||||
struct devfreq_cpu_data *parent_cpu_data;
|
struct devfreq_cpu_data *parent_cpu_data;
|
||||||
|
struct cpufreq_policy *policy;
|
||||||
unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent;
|
unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent;
|
||||||
unsigned long dev_min, dev_max;
|
unsigned long dev_min, dev_max;
|
||||||
unsigned long freq = 0;
|
unsigned long freq = 0;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
for_each_online_cpu(cpu) {
|
for_each_online_cpu(cpu) {
|
||||||
parent_cpu_data = p_data->parent_cpu_data[cpu];
|
policy = cpufreq_cpu_get(cpu);
|
||||||
if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu)
|
if (!policy) {
|
||||||
|
ret = -EINVAL;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent_cpu_data = get_parent_cpu_data(p_data, policy);
|
||||||
|
if (!parent_cpu_data) {
|
||||||
|
cpufreq_cpu_put(policy);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get target freq via required opps */
|
/* Get target freq via required opps */
|
||||||
cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ;
|
cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ;
|
||||||
@ -67,6 +93,7 @@ static int get_target_freq_with_cpufreq(struct devfreq *devfreq,
|
|||||||
devfreq->opp_table, &cpu_cur);
|
devfreq->opp_table, &cpu_cur);
|
||||||
if (freq) {
|
if (freq) {
|
||||||
*target_freq = max(freq, *target_freq);
|
*target_freq = max(freq, *target_freq);
|
||||||
|
cpufreq_cpu_put(policy);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,9 +108,10 @@ static int get_target_freq_with_cpufreq(struct devfreq *devfreq,
|
|||||||
freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100);
|
freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100);
|
||||||
|
|
||||||
*target_freq = max(freq, *target_freq);
|
*target_freq = max(freq, *target_freq);
|
||||||
|
cpufreq_cpu_put(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_target_freq_with_devfreq(struct devfreq *devfreq,
|
static int get_target_freq_with_devfreq(struct devfreq *devfreq,
|
||||||
@ -168,12 +196,11 @@ static int cpufreq_passive_notifier_call(struct notifier_block *nb,
|
|||||||
unsigned int cur_freq;
|
unsigned int cur_freq;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (event != CPUFREQ_POSTCHANGE || !freqs ||
|
if (event != CPUFREQ_POSTCHANGE || !freqs)
|
||||||
!p_data->parent_cpu_data[freqs->policy->cpu])
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu];
|
parent_cpu_data = get_parent_cpu_data(p_data, freqs->policy);
|
||||||
if (parent_cpu_data->cur_freq == freqs->new)
|
if (!parent_cpu_data || parent_cpu_data->cur_freq == freqs->new)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cur_freq = parent_cpu_data->cur_freq;
|
cur_freq = parent_cpu_data->cur_freq;
|
||||||
@ -196,7 +223,7 @@ static int cpufreq_passive_unregister_notifier(struct devfreq *devfreq)
|
|||||||
struct devfreq_passive_data *p_data
|
struct devfreq_passive_data *p_data
|
||||||
= (struct devfreq_passive_data *)devfreq->data;
|
= (struct devfreq_passive_data *)devfreq->data;
|
||||||
struct devfreq_cpu_data *parent_cpu_data;
|
struct devfreq_cpu_data *parent_cpu_data;
|
||||||
int cpu, ret;
|
int cpu, ret = 0;
|
||||||
|
|
||||||
if (p_data->nb.notifier_call) {
|
if (p_data->nb.notifier_call) {
|
||||||
ret = cpufreq_unregister_notifier(&p_data->nb,
|
ret = cpufreq_unregister_notifier(&p_data->nb,
|
||||||
@ -206,16 +233,26 @@ static int cpufreq_passive_unregister_notifier(struct devfreq *devfreq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for_each_possible_cpu(cpu) {
|
for_each_possible_cpu(cpu) {
|
||||||
parent_cpu_data = p_data->parent_cpu_data[cpu];
|
struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
|
||||||
if (!parent_cpu_data)
|
if (!policy) {
|
||||||
|
ret = -EINVAL;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent_cpu_data = get_parent_cpu_data(p_data, policy);
|
||||||
|
if (!parent_cpu_data) {
|
||||||
|
cpufreq_cpu_put(policy);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_del(&parent_cpu_data->node);
|
||||||
if (parent_cpu_data->opp_table)
|
if (parent_cpu_data->opp_table)
|
||||||
dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
|
dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
|
||||||
kfree(parent_cpu_data);
|
kfree(parent_cpu_data);
|
||||||
|
cpufreq_cpu_put(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
|
static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
|
||||||
@ -230,6 +267,9 @@ static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
|
|||||||
unsigned int cpu;
|
unsigned int cpu;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
p_data->cpu_data_list
|
||||||
|
= (struct list_head)LIST_HEAD_INIT(p_data->cpu_data_list);
|
||||||
|
|
||||||
p_data->nb.notifier_call = cpufreq_passive_notifier_call;
|
p_data->nb.notifier_call = cpufreq_passive_notifier_call;
|
||||||
ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER);
|
ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@ -239,15 +279,18 @@ static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for_each_possible_cpu(cpu) {
|
for_each_possible_cpu(cpu) {
|
||||||
if (p_data->parent_cpu_data[cpu])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
policy = cpufreq_cpu_get(cpu);
|
policy = cpufreq_cpu_get(cpu);
|
||||||
if (!policy) {
|
if (!policy) {
|
||||||
ret = -EPROBE_DEFER;
|
ret = -EPROBE_DEFER;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent_cpu_data = get_parent_cpu_data(p_data, policy);
|
||||||
|
if (parent_cpu_data) {
|
||||||
|
cpufreq_cpu_put(policy);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
parent_cpu_data = kzalloc(sizeof(*parent_cpu_data),
|
parent_cpu_data = kzalloc(sizeof(*parent_cpu_data),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!parent_cpu_data) {
|
if (!parent_cpu_data) {
|
||||||
@ -276,7 +319,7 @@ static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
|
|||||||
parent_cpu_data->min_freq = policy->cpuinfo.min_freq;
|
parent_cpu_data->min_freq = policy->cpuinfo.min_freq;
|
||||||
parent_cpu_data->max_freq = policy->cpuinfo.max_freq;
|
parent_cpu_data->max_freq = policy->cpuinfo.max_freq;
|
||||||
|
|
||||||
p_data->parent_cpu_data[cpu] = parent_cpu_data;
|
list_add_tail(&parent_cpu_data->node, &p_data->cpu_data_list);
|
||||||
cpufreq_cpu_put(policy);
|
cpufreq_cpu_put(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +309,7 @@ enum devfreq_parent_dev_type {
|
|||||||
* @this: the devfreq instance of own device.
|
* @this: the devfreq instance of own device.
|
||||||
* @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER or
|
* @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER or
|
||||||
* CPUFREQ_TRANSITION_NOTIFIER list.
|
* CPUFREQ_TRANSITION_NOTIFIER list.
|
||||||
* @parent_cpu_data: the state min/max/current frequency of all online cpu's.
|
* @cpu_data_list: the list of cpu frequency data for all cpufreq_policy.
|
||||||
*
|
*
|
||||||
* The devfreq_passive_data have to set the devfreq instance of parent
|
* The devfreq_passive_data have to set the devfreq instance of parent
|
||||||
* device with governors except for the passive governor. But, don't need to
|
* device with governors except for the passive governor. But, don't need to
|
||||||
@ -329,7 +329,7 @@ struct devfreq_passive_data {
|
|||||||
/* For passive governor's internal use. Don't need to set them */
|
/* For passive governor's internal use. Don't need to set them */
|
||||||
struct devfreq *this;
|
struct devfreq *this;
|
||||||
struct notifier_block nb;
|
struct notifier_block nb;
|
||||||
struct devfreq_cpu_data *parent_cpu_data[NR_CPUS];
|
struct list_head cpu_data_list;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user