PM / devfreq: passive: Keep cpufreq_policy for possible cpus
authorChanwoo Choi <cw00.choi@samsung.com>
Tue, 26 Apr 2022 18:49:19 +0000 (03:49 +0900)
committerChanwoo Choi <cw00.choi@samsung.com>
Tue, 17 May 2022 09:24:39 +0000 (18:24 +0900)
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
drivers/devfreq/governor_passive.c
include/linux/devfreq.h

index 335c4a4..0adfebc 100644 (file)
@@ -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;
 
index ffcce61..7306e94 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-only
+       // SPDX-License-Identifier: GPL-2.0-only
 /*
  * linux/drivers/devfreq/governor_passive.c
  *
 
 #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 *devfreq,
        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(struct devfreq *devfreq,
                                        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(struct devfreq *devfreq,
                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(struct notifier_block *nb,
        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_notifier(struct devfreq *devfreq)
        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_notifier(struct devfreq *devfreq)
        }
 
        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_notifier(struct devfreq *devfreq)
        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_notifier(struct devfreq *devfreq)
        }
 
        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_notifier(struct devfreq *devfreq)
                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);
        }
 
index b1e4a6f..dc10bee 100644 (file)
@@ -309,7 +309,7 @@ enum devfreq_parent_dev_type {
  * @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
@@ -329,7 +329,7 @@ struct devfreq_passive_data {
        /* 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