2022-06-16 20:16:04 +00:00
|
|
|
From 26984d9d581e5049bd75091d2e789b9cc3ea12e0 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Chanwoo Choi <cw00.choi@samsung.com>
|
|
|
|
Date: Wed, 27 Apr 2022 03:49:19 +0900
|
|
|
|
Subject: [PATCH 4/5] 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>
|
|
|
|
---
|
|
|
|
drivers/devfreq/governor.h | 3 ++
|
|
|
|
drivers/devfreq/governor_passive.c | 75 +++++++++++++++++++++++-------
|
|
|
|
include/linux/devfreq.h | 4 +-
|
|
|
|
3 files changed, 64 insertions(+), 18 deletions(-)
|
|
|
|
|
|
|
|
--- a/drivers/devfreq/governor.h
|
|
|
|
+++ b/drivers/devfreq/governor.h
|
|
|
|
@@ -49,6 +49,7 @@
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct devfreq_cpu_data - Hold the per-cpu data
|
|
|
|
+ * @node: list node
|
|
|
|
* @dev: reference to cpu device.
|
|
|
|
* @first_cpu: the cpumask of the first cpu of a policy.
|
|
|
|
* @opp_table: reference to cpu opp table.
|
|
|
|
@@ -60,6 +61,8 @@
|
|
|
|
* This is auto-populated by the governor.
|
|
|
|
*/
|
|
|
|
struct devfreq_cpu_data {
|
|
|
|
+ struct list_head node;
|
|
|
|
+
|
|
|
|
struct device *dev;
|
|
|
|
unsigned int first_cpu;
|
|
|
|
|
|
|
|
--- a/drivers/devfreq/governor_passive.c
|
|
|
|
+++ b/drivers/devfreq/governor_passive.c
|
|
|
|
@@ -1,4 +1,4 @@
|
|
|
|
-// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
+ // SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/*
|
|
|
|
* linux/drivers/devfreq/governor_passive.c
|
|
|
|
*
|
|
|
|
@@ -18,6 +18,22 @@
|
|
|
|
|
|
|
|
#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,
|
|
|
|
struct opp_table *p_opp_table,
|
|
|
|
struct opp_table *opp_table,
|
|
|
|
@@ -51,14 +67,24 @@ static int get_target_freq_with_cpufreq(
|
|
|
|
struct devfreq_passive_data *p_data =
|
|
|
|
(struct devfreq_passive_data *)devfreq->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 dev_min, dev_max;
|
|
|
|
unsigned long freq = 0;
|
|
|
|
+ int ret = 0;
|
|
|
|
|
|
|
|
for_each_online_cpu(cpu) {
|
|
|
|
- parent_cpu_data = p_data->parent_cpu_data[cpu];
|
|
|
|
- if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu)
|
|
|
|
+ policy = cpufreq_cpu_get(cpu);
|
|
|
|
+ if (!policy) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ 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 */
|
|
|
|
cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ;
|
|
|
|
@@ -67,6 +93,7 @@ static int get_target_freq_with_cpufreq(
|
|
|
|
devfreq->opp_table, &cpu_cur);
|
|
|
|
if (freq) {
|
|
|
|
*target_freq = max(freq, *target_freq);
|
|
|
|
+ cpufreq_cpu_put(policy);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -81,9 +108,10 @@ static int get_target_freq_with_cpufreq(
|
|
|
|
freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100);
|
|
|
|
|
|
|
|
*target_freq = max(freq, *target_freq);
|
|
|
|
+ cpufreq_cpu_put(policy);
|
|
|
|
}
|
|
|
|
|
|
|
|
- return 0;
|
|
|
|
+ return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_target_freq_with_devfreq(struct devfreq *devfreq,
|
|
|
|
@@ -168,12 +196,11 @@ static int cpufreq_passive_notifier_call
|
|
|
|
unsigned int cur_freq;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
- if (event != CPUFREQ_POSTCHANGE || !freqs ||
|
|
|
|
- !p_data->parent_cpu_data[freqs->policy->cpu])
|
|
|
|
+ if (event != CPUFREQ_POSTCHANGE || !freqs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
- parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu];
|
|
|
|
- if (parent_cpu_data->cur_freq == freqs->new)
|
|
|
|
+ parent_cpu_data = get_parent_cpu_data(p_data, freqs->policy);
|
|
|
|
+ if (!parent_cpu_data || parent_cpu_data->cur_freq == freqs->new)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cur_freq = parent_cpu_data->cur_freq;
|
|
|
|
@@ -196,7 +223,7 @@ static int cpufreq_passive_unregister_no
|
|
|
|
struct devfreq_passive_data *p_data
|
|
|
|
= (struct devfreq_passive_data *)devfreq->data;
|
|
|
|
struct devfreq_cpu_data *parent_cpu_data;
|
|
|
|
- int cpu, ret;
|
|
|
|
+ int cpu, ret = 0;
|
|
|
|
|
|
|
|
if (p_data->nb.notifier_call) {
|
|
|
|
ret = cpufreq_unregister_notifier(&p_data->nb,
|
|
|
|
@@ -206,16 +233,26 @@ static int cpufreq_passive_unregister_no
|
|
|
|
}
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
- parent_cpu_data = p_data->parent_cpu_data[cpu];
|
|
|
|
- if (!parent_cpu_data)
|
|
|
|
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
|
|
|
|
+ if (!policy) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ 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)
|
|
|
|
dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
|
|
|
|
kfree(parent_cpu_data);
|
|
|
|
+ cpufreq_cpu_put(policy);
|
|
|
|
}
|
|
|
|
|
|
|
|
- return 0;
|
|
|
|
+ return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
|
|
|
|
@@ -230,6 +267,9 @@ static int cpufreq_passive_register_noti
|
|
|
|
unsigned int cpu;
|
|
|
|
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;
|
|
|
|
ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER);
|
|
|
|
if (ret) {
|
|
|
|
@@ -239,15 +279,18 @@ static int cpufreq_passive_register_noti
|
|
|
|
}
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
- if (p_data->parent_cpu_data[cpu])
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
policy = cpufreq_cpu_get(cpu);
|
|
|
|
if (!policy) {
|
|
|
|
ret = -EPROBE_DEFER;
|
|
|
|
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),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!parent_cpu_data) {
|
|
|
|
@@ -276,7 +319,7 @@ static int cpufreq_passive_register_noti
|
|
|
|
parent_cpu_data->min_freq = policy->cpuinfo.min_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);
|
|
|
|
}
|
|
|
|
|
|
|
|
--- a/include/linux/devfreq.h
|
|
|
|
+++ b/include/linux/devfreq.h
|
2023-01-12 12:19:14 +00:00
|
|
|
@@ -310,7 +310,7 @@ enum devfreq_parent_dev_type {
|
2022-06-16 20:16:04 +00:00
|
|
|
* @this: the devfreq instance of own device.
|
|
|
|
* @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER or
|
|
|
|
* 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
|
|
|
|
* device with governors except for the passive governor. But, don't need to
|
2023-01-12 12:19:14 +00:00
|
|
|
@@ -330,7 +330,7 @@ struct devfreq_passive_data {
|
2022-06-16 20:16:04 +00:00
|
|
|
/* For passive governor's internal use. Don't need to set them */
|
|
|
|
struct devfreq *this;
|
|
|
|
struct notifier_block nb;
|
|
|
|
- struct devfreq_cpu_data *parent_cpu_data[NR_CPUS];
|
|
|
|
+ struct list_head cpu_data_list;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|