mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2025-01-23 15:13:11 +00:00
ipq806x: 5.15: backport devfreq new cpufreq based PASSIVE governor
Backport devfreq new cpufreq based PASSIVE governor needed for devfreq based fab and cache scaling. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
parent
5ad826f48a
commit
3a911b8c2d
@ -0,0 +1,113 @@
|
||||
From 713472e53e6e53c985e283782b0fd76b8ecfd47e Mon Sep 17 00:00:00 2001
|
||||
From: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
Date: Mon, 1 Mar 2021 02:07:29 +0900
|
||||
Subject: [PATCH 1/5] PM / devfreq: Export devfreq_get_freq_range symbol within
|
||||
devfreq
|
||||
|
||||
In order to get frequency range within devfreq governors,
|
||||
export devfreq_get_freq_range symbol within devfreq.
|
||||
|
||||
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
|
||||
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/devfreq.c | 20 ++++++++++++--------
|
||||
drivers/devfreq/governor.h | 2 ++
|
||||
2 files changed, 14 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/devfreq/devfreq.c
|
||||
+++ b/drivers/devfreq/devfreq.c
|
||||
@@ -112,16 +112,16 @@ static unsigned long find_available_max_
|
||||
}
|
||||
|
||||
/**
|
||||
- * get_freq_range() - Get the current freq range
|
||||
+ * devfreq_get_freq_range() - Get the current freq range
|
||||
* @devfreq: the devfreq instance
|
||||
* @min_freq: the min frequency
|
||||
* @max_freq: the max frequency
|
||||
*
|
||||
* This takes into consideration all constraints.
|
||||
*/
|
||||
-static void get_freq_range(struct devfreq *devfreq,
|
||||
- unsigned long *min_freq,
|
||||
- unsigned long *max_freq)
|
||||
+void devfreq_get_freq_range(struct devfreq *devfreq,
|
||||
+ unsigned long *min_freq,
|
||||
+ unsigned long *max_freq)
|
||||
{
|
||||
unsigned long *freq_table = devfreq->profile->freq_table;
|
||||
s32 qos_min_freq, qos_max_freq;
|
||||
@@ -158,6 +158,7 @@ static void get_freq_range(struct devfre
|
||||
if (*min_freq > *max_freq)
|
||||
*min_freq = *max_freq;
|
||||
}
|
||||
+EXPORT_SYMBOL(devfreq_get_freq_range);
|
||||
|
||||
/**
|
||||
* devfreq_get_freq_level() - Lookup freq_table for the frequency
|
||||
@@ -418,7 +419,7 @@ int devfreq_update_target(struct devfreq
|
||||
err = devfreq->governor->get_target_freq(devfreq, &freq);
|
||||
if (err)
|
||||
return err;
|
||||
- get_freq_range(devfreq, &min_freq, &max_freq);
|
||||
+ devfreq_get_freq_range(devfreq, &min_freq, &max_freq);
|
||||
|
||||
if (freq < min_freq) {
|
||||
freq = min_freq;
|
||||
@@ -785,6 +786,7 @@ struct devfreq *devfreq_add_device(struc
|
||||
{
|
||||
struct devfreq *devfreq;
|
||||
struct devfreq_governor *governor;
|
||||
+ unsigned long min_freq, max_freq;
|
||||
int err = 0;
|
||||
|
||||
if (!dev || !profile || !governor_name) {
|
||||
@@ -849,6 +851,8 @@ struct devfreq *devfreq_add_device(struc
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
+ devfreq_get_freq_range(devfreq, &min_freq, &max_freq);
|
||||
+
|
||||
devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev);
|
||||
devfreq->opp_table = dev_pm_opp_get_opp_table(dev);
|
||||
if (IS_ERR(devfreq->opp_table))
|
||||
@@ -1561,7 +1565,7 @@ static ssize_t min_freq_show(struct devi
|
||||
unsigned long min_freq, max_freq;
|
||||
|
||||
mutex_lock(&df->lock);
|
||||
- get_freq_range(df, &min_freq, &max_freq);
|
||||
+ devfreq_get_freq_range(df, &min_freq, &max_freq);
|
||||
mutex_unlock(&df->lock);
|
||||
|
||||
return sprintf(buf, "%lu\n", min_freq);
|
||||
@@ -1615,7 +1619,7 @@ static ssize_t max_freq_show(struct devi
|
||||
unsigned long min_freq, max_freq;
|
||||
|
||||
mutex_lock(&df->lock);
|
||||
- get_freq_range(df, &min_freq, &max_freq);
|
||||
+ devfreq_get_freq_range(df, &min_freq, &max_freq);
|
||||
mutex_unlock(&df->lock);
|
||||
|
||||
return sprintf(buf, "%lu\n", max_freq);
|
||||
@@ -1929,7 +1933,7 @@ static int devfreq_summary_show(struct s
|
||||
|
||||
mutex_lock(&devfreq->lock);
|
||||
cur_freq = devfreq->previous_freq;
|
||||
- get_freq_range(devfreq, &min_freq, &max_freq);
|
||||
+ devfreq_get_freq_range(devfreq, &min_freq, &max_freq);
|
||||
timer = devfreq->profile->timer;
|
||||
|
||||
if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL))
|
||||
--- a/drivers/devfreq/governor.h
|
||||
+++ b/drivers/devfreq/governor.h
|
||||
@@ -86,6 +86,8 @@ int devfreq_remove_governor(struct devfr
|
||||
|
||||
int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
|
||||
int devfreq_update_target(struct devfreq *devfreq, unsigned long freq);
|
||||
+void devfreq_get_freq_range(struct devfreq *devfreq, unsigned long *min_freq,
|
||||
+ unsigned long *max_freq);
|
||||
|
||||
static inline int devfreq_update_stats(struct devfreq *df)
|
||||
{
|
@ -0,0 +1,461 @@
|
||||
From a03dacb0316f74400846aaf144d6c73f4217ca08 Mon Sep 17 00:00:00 2001
|
||||
From: Saravana Kannan <skannan@codeaurora.org>
|
||||
Date: Tue, 2 Mar 2021 15:58:21 +0900
|
||||
Subject: [PATCH 2/5] PM / devfreq: Add cpu based scaling support to passive
|
||||
governor
|
||||
|
||||
Many CPU architectures have caches that can scale independent of the
|
||||
CPUs. Frequency scaling of the caches is necessary to make sure that the
|
||||
cache is not a performance bottleneck that leads to poor performance and
|
||||
power. The same idea applies for RAM/DDR.
|
||||
|
||||
To achieve this, this patch adds support for cpu based scaling to the
|
||||
passive governor. This is accomplished by taking the current frequency
|
||||
of each CPU frequency domain and then adjust the frequency of the cache
|
||||
(or any devfreq device) based on the frequency of the CPUs. It listens
|
||||
to CPU frequency transition notifiers to keep itself up to date on the
|
||||
current CPU frequency.
|
||||
|
||||
To decide the frequency of the device, the governor does one of the
|
||||
following:
|
||||
* Derives the optimal devfreq device opp from required-opps property of
|
||||
the parent cpu opp_table.
|
||||
|
||||
* Scales the device frequency in proportion to the CPU frequency. So, if
|
||||
the CPUs are running at their max frequency, the device runs at its
|
||||
max frequency. If the CPUs are running at their min frequency, the
|
||||
device runs at its min frequency. It is interpolated for frequencies
|
||||
in between.
|
||||
|
||||
Tested-by: Chen-Yu Tsai <wenst@chromium.org>
|
||||
Tested-by: Johnson Wang <johnson.wang@mediatek.com>
|
||||
Signed-off-by: Saravana Kannan <skannan@codeaurora.org>
|
||||
[Sibi: Integrated cpu-freqmap governor into passive_governor]
|
||||
Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
|
||||
[Chanwoo: Fix conflict with latest code and cleanup code]
|
||||
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
---
|
||||
drivers/devfreq/governor.h | 22 +++
|
||||
drivers/devfreq/governor_passive.c | 298 +++++++++++++++++++++++++++--
|
||||
include/linux/devfreq.h | 17 +-
|
||||
3 files changed, 323 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/drivers/devfreq/governor.h
|
||||
+++ b/drivers/devfreq/governor.h
|
||||
@@ -48,6 +48,28 @@
|
||||
#define DEVFREQ_GOV_ATTR_TIMER BIT(1)
|
||||
|
||||
/**
|
||||
+ * struct devfreq_cpu_data - Hold the per-cpu data
|
||||
+ * @dev: reference to cpu device.
|
||||
+ * @first_cpu: the cpumask of the first cpu of a policy.
|
||||
+ * @opp_table: reference to cpu opp table.
|
||||
+ * @cur_freq: the current frequency of the cpu.
|
||||
+ * @min_freq: the min frequency of the cpu.
|
||||
+ * @max_freq: the max frequency of the cpu.
|
||||
+ *
|
||||
+ * This structure stores the required cpu_data of a cpu.
|
||||
+ * This is auto-populated by the governor.
|
||||
+ */
|
||||
+struct devfreq_cpu_data {
|
||||
+ struct device *dev;
|
||||
+ unsigned int first_cpu;
|
||||
+
|
||||
+ struct opp_table *opp_table;
|
||||
+ unsigned int cur_freq;
|
||||
+ unsigned int min_freq;
|
||||
+ unsigned int max_freq;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
* struct devfreq_governor - Devfreq policy governor
|
||||
* @node: list node - contains registered devfreq governors
|
||||
* @name: Governor's name
|
||||
--- a/drivers/devfreq/governor_passive.c
|
||||
+++ b/drivers/devfreq/governor_passive.c
|
||||
@@ -8,11 +8,85 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
+#include <linux/cpu.h>
|
||||
+#include <linux/cpufreq.h>
|
||||
+#include <linux/cpumask.h>
|
||||
+#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include "governor.h"
|
||||
|
||||
-static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
|
||||
+#define HZ_PER_KHZ 1000
|
||||
+
|
||||
+static unsigned long get_target_freq_by_required_opp(struct device *p_dev,
|
||||
+ struct opp_table *p_opp_table,
|
||||
+ struct opp_table *opp_table,
|
||||
+ unsigned long *freq)
|
||||
+{
|
||||
+ struct dev_pm_opp *opp = NULL, *p_opp = NULL;
|
||||
+ unsigned long target_freq;
|
||||
+
|
||||
+ if (!p_dev || !p_opp_table || !opp_table || !freq)
|
||||
+ return 0;
|
||||
+
|
||||
+ p_opp = devfreq_recommended_opp(p_dev, freq, 0);
|
||||
+ if (IS_ERR(p_opp))
|
||||
+ return 0;
|
||||
+
|
||||
+ opp = dev_pm_opp_xlate_required_opp(p_opp_table, opp_table, p_opp);
|
||||
+ dev_pm_opp_put(p_opp);
|
||||
+
|
||||
+ if (IS_ERR(opp))
|
||||
+ return 0;
|
||||
+
|
||||
+ target_freq = dev_pm_opp_get_freq(opp);
|
||||
+ dev_pm_opp_put(opp);
|
||||
+
|
||||
+ return target_freq;
|
||||
+}
|
||||
+
|
||||
+static int get_target_freq_with_cpufreq(struct devfreq *devfreq,
|
||||
+ unsigned long *target_freq)
|
||||
+{
|
||||
+ struct devfreq_passive_data *p_data =
|
||||
+ (struct devfreq_passive_data *)devfreq->data;
|
||||
+ struct devfreq_cpu_data *parent_cpu_data;
|
||||
+ unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent;
|
||||
+ unsigned long dev_min, dev_max;
|
||||
+ unsigned long freq = 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)
|
||||
+ continue;
|
||||
+
|
||||
+ /* Get target freq via required opps */
|
||||
+ cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ;
|
||||
+ freq = get_target_freq_by_required_opp(parent_cpu_data->dev,
|
||||
+ parent_cpu_data->opp_table,
|
||||
+ devfreq->opp_table, &cpu_cur);
|
||||
+ if (freq) {
|
||||
+ *target_freq = max(freq, *target_freq);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* Use interpolation if required opps is not available */
|
||||
+ devfreq_get_freq_range(devfreq, &dev_min, &dev_max);
|
||||
+
|
||||
+ cpu_min = parent_cpu_data->min_freq;
|
||||
+ cpu_max = parent_cpu_data->max_freq;
|
||||
+ cpu_cur = parent_cpu_data->cur_freq;
|
||||
+
|
||||
+ cpu_percent = ((cpu_cur - cpu_min) * 100) / (cpu_max - cpu_min);
|
||||
+ freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100);
|
||||
+
|
||||
+ *target_freq = max(freq, *target_freq);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int get_target_freq_with_devfreq(struct devfreq *devfreq,
|
||||
unsigned long *freq)
|
||||
{
|
||||
struct devfreq_passive_data *p_data
|
||||
@@ -99,6 +173,181 @@ no_required_opp:
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
|
||||
+ unsigned long *freq)
|
||||
+{
|
||||
+ struct devfreq_passive_data *p_data =
|
||||
+ (struct devfreq_passive_data *)devfreq->data;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!p_data)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /*
|
||||
+ * If the devfreq device with passive governor has the specific method
|
||||
+ * to determine the next frequency, should use the get_target_freq()
|
||||
+ * of struct devfreq_passive_data.
|
||||
+ */
|
||||
+ if (p_data->get_target_freq)
|
||||
+ return p_data->get_target_freq(devfreq, freq);
|
||||
+
|
||||
+ switch (p_data->parent_type) {
|
||||
+ case DEVFREQ_PARENT_DEV:
|
||||
+ ret = get_target_freq_with_devfreq(devfreq, freq);
|
||||
+ break;
|
||||
+ case CPUFREQ_PARENT_DEV:
|
||||
+ ret = get_target_freq_with_cpufreq(devfreq, freq);
|
||||
+ break;
|
||||
+ default:
|
||||
+ ret = -EINVAL;
|
||||
+ dev_err(&devfreq->dev, "Invalid parent type\n");
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int cpufreq_passive_notifier_call(struct notifier_block *nb,
|
||||
+ unsigned long event, void *ptr)
|
||||
+{
|
||||
+ struct devfreq_passive_data *p_data =
|
||||
+ container_of(nb, struct devfreq_passive_data, nb);
|
||||
+ struct devfreq *devfreq = (struct devfreq *)p_data->this;
|
||||
+ struct devfreq_cpu_data *parent_cpu_data;
|
||||
+ struct cpufreq_freqs *freqs = ptr;
|
||||
+ unsigned int cur_freq;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (event != CPUFREQ_POSTCHANGE || !freqs ||
|
||||
+ !p_data->parent_cpu_data[freqs->policy->cpu])
|
||||
+ return 0;
|
||||
+
|
||||
+ parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu];
|
||||
+ if (parent_cpu_data->cur_freq == freqs->new)
|
||||
+ return 0;
|
||||
+
|
||||
+ cur_freq = parent_cpu_data->cur_freq;
|
||||
+ parent_cpu_data->cur_freq = freqs->new;
|
||||
+
|
||||
+ mutex_lock(&devfreq->lock);
|
||||
+ ret = devfreq_update_target(devfreq, freqs->new);
|
||||
+ mutex_unlock(&devfreq->lock);
|
||||
+ if (ret) {
|
||||
+ parent_cpu_data->cur_freq = cur_freq;
|
||||
+ dev_err(&devfreq->dev, "failed to update the frequency.\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+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;
|
||||
+
|
||||
+ if (p_data->nb.notifier_call) {
|
||||
+ ret = cpufreq_unregister_notifier(&p_data->nb,
|
||||
+ CPUFREQ_TRANSITION_NOTIFIER);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ parent_cpu_data = p_data->parent_cpu_data[cpu];
|
||||
+ if (!parent_cpu_data)
|
||||
+ continue;
|
||||
+
|
||||
+ if (parent_cpu_data->opp_table)
|
||||
+ dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
|
||||
+ kfree(parent_cpu_data);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
|
||||
+{
|
||||
+ struct devfreq_passive_data *p_data
|
||||
+ = (struct devfreq_passive_data *)devfreq->data;
|
||||
+ struct device *dev = devfreq->dev.parent;
|
||||
+ struct opp_table *opp_table = NULL;
|
||||
+ struct devfreq_cpu_data *parent_cpu_data;
|
||||
+ struct cpufreq_policy *policy;
|
||||
+ struct device *cpu_dev;
|
||||
+ unsigned int cpu;
|
||||
+ int ret;
|
||||
+
|
||||
+ p_data->nb.notifier_call = cpufreq_passive_notifier_call;
|
||||
+ ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to register cpufreq notifier\n");
|
||||
+ p_data->nb.notifier_call = NULL;
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ 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 = kzalloc(sizeof(*parent_cpu_data),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!parent_cpu_data) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto err_put_policy;
|
||||
+ }
|
||||
+
|
||||
+ cpu_dev = get_cpu_device(cpu);
|
||||
+ if (!cpu_dev) {
|
||||
+ dev_err(dev, "failed to get cpu device\n");
|
||||
+ ret = -ENODEV;
|
||||
+ goto err_free_cpu_data;
|
||||
+ }
|
||||
+
|
||||
+ opp_table = dev_pm_opp_get_opp_table(cpu_dev);
|
||||
+ if (IS_ERR(opp_table)) {
|
||||
+ dev_err(dev, "failed to get opp_table of cpu%d\n", cpu);
|
||||
+ ret = PTR_ERR(opp_table);
|
||||
+ goto err_free_cpu_data;
|
||||
+ }
|
||||
+
|
||||
+ parent_cpu_data->dev = cpu_dev;
|
||||
+ parent_cpu_data->opp_table = opp_table;
|
||||
+ parent_cpu_data->first_cpu = cpumask_first(policy->related_cpus);
|
||||
+ parent_cpu_data->cur_freq = policy->cur;
|
||||
+ 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;
|
||||
+ cpufreq_cpu_put(policy);
|
||||
+ }
|
||||
+
|
||||
+ mutex_lock(&devfreq->lock);
|
||||
+ ret = devfreq_update_target(devfreq, 0L);
|
||||
+ mutex_unlock(&devfreq->lock);
|
||||
+ if (ret)
|
||||
+ dev_err(dev, "failed to update the frequency\n");
|
||||
+
|
||||
+ return ret;
|
||||
+
|
||||
+err_free_cpu_data:
|
||||
+ kfree(parent_cpu_data);
|
||||
+err_put_policy:
|
||||
+ cpufreq_cpu_put(policy);
|
||||
+err:
|
||||
+ WARN_ON(cpufreq_passive_unregister_notifier(devfreq));
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int devfreq_passive_notifier_call(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
@@ -131,30 +380,55 @@ static int devfreq_passive_notifier_call
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
-static int devfreq_passive_event_handler(struct devfreq *devfreq,
|
||||
- unsigned int event, void *data)
|
||||
+static int devfreq_passive_unregister_notifier(struct devfreq *devfreq)
|
||||
+{
|
||||
+ struct devfreq_passive_data *p_data
|
||||
+ = (struct devfreq_passive_data *)devfreq->data;
|
||||
+ struct devfreq *parent = (struct devfreq *)p_data->parent;
|
||||
+ struct notifier_block *nb = &p_data->nb;
|
||||
+
|
||||
+ return devfreq_unregister_notifier(parent, nb, DEVFREQ_TRANSITION_NOTIFIER);
|
||||
+}
|
||||
+
|
||||
+static int devfreq_passive_register_notifier(struct devfreq *devfreq)
|
||||
{
|
||||
struct devfreq_passive_data *p_data
|
||||
= (struct devfreq_passive_data *)devfreq->data;
|
||||
struct devfreq *parent = (struct devfreq *)p_data->parent;
|
||||
struct notifier_block *nb = &p_data->nb;
|
||||
- int ret = 0;
|
||||
|
||||
if (!parent)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
+ nb->notifier_call = devfreq_passive_notifier_call;
|
||||
+ return devfreq_register_notifier(parent, nb, DEVFREQ_TRANSITION_NOTIFIER);
|
||||
+}
|
||||
+
|
||||
+static int devfreq_passive_event_handler(struct devfreq *devfreq,
|
||||
+ unsigned int event, void *data)
|
||||
+{
|
||||
+ struct devfreq_passive_data *p_data
|
||||
+ = (struct devfreq_passive_data *)devfreq->data;
|
||||
+ int ret = -EINVAL;
|
||||
+
|
||||
+ if (!p_data)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!p_data->this)
|
||||
+ p_data->this = devfreq;
|
||||
+
|
||||
switch (event) {
|
||||
case DEVFREQ_GOV_START:
|
||||
- if (!p_data->this)
|
||||
- p_data->this = devfreq;
|
||||
-
|
||||
- nb->notifier_call = devfreq_passive_notifier_call;
|
||||
- ret = devfreq_register_notifier(parent, nb,
|
||||
- DEVFREQ_TRANSITION_NOTIFIER);
|
||||
+ if (p_data->parent_type == DEVFREQ_PARENT_DEV)
|
||||
+ ret = devfreq_passive_register_notifier(devfreq);
|
||||
+ else if (p_data->parent_type == CPUFREQ_PARENT_DEV)
|
||||
+ ret = cpufreq_passive_register_notifier(devfreq);
|
||||
break;
|
||||
case DEVFREQ_GOV_STOP:
|
||||
- WARN_ON(devfreq_unregister_notifier(parent, nb,
|
||||
- DEVFREQ_TRANSITION_NOTIFIER));
|
||||
+ if (p_data->parent_type == DEVFREQ_PARENT_DEV)
|
||||
+ WARN_ON(devfreq_passive_unregister_notifier(devfreq));
|
||||
+ else if (p_data->parent_type == CPUFREQ_PARENT_DEV)
|
||||
+ WARN_ON(cpufreq_passive_unregister_notifier(devfreq));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
--- a/include/linux/devfreq.h
|
||||
+++ b/include/linux/devfreq.h
|
||||
@@ -38,6 +38,7 @@ enum devfreq_timer {
|
||||
|
||||
struct devfreq;
|
||||
struct devfreq_governor;
|
||||
+struct devfreq_cpu_data;
|
||||
struct thermal_cooling_device;
|
||||
|
||||
/**
|
||||
@@ -288,6 +289,11 @@ struct devfreq_simple_ondemand_data {
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE)
|
||||
+enum devfreq_parent_dev_type {
|
||||
+ DEVFREQ_PARENT_DEV,
|
||||
+ CPUFREQ_PARENT_DEV,
|
||||
+};
|
||||
+
|
||||
/**
|
||||
* struct devfreq_passive_data - ``void *data`` fed to struct devfreq
|
||||
* and devfreq_add_device
|
||||
@@ -299,8 +305,11 @@ struct devfreq_simple_ondemand_data {
|
||||
* using governors except for passive governor.
|
||||
* If the devfreq device has the specific method to decide
|
||||
* the next frequency, should use this callback.
|
||||
- * @this: the devfreq instance of own device.
|
||||
- * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER list
|
||||
+ * @parent_type: the parent type of the device.
|
||||
+ * @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.
|
||||
*
|
||||
* 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
|
||||
@@ -314,9 +323,13 @@ struct devfreq_passive_data {
|
||||
/* Optional callback to decide the next frequency of passvice device */
|
||||
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
|
||||
|
||||
+ /* Should set the type of parent device */
|
||||
+ enum devfreq_parent_dev_type parent_type;
|
||||
+
|
||||
/* 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];
|
||||
};
|
||||
#endif
|
||||
|
@ -0,0 +1,110 @@
|
||||
From 05723e71234b60a1a47313ea1a889797ec648f1c Mon Sep 17 00:00:00 2001
|
||||
From: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
Date: Tue, 2 Mar 2021 17:22:50 +0900
|
||||
Subject: [PATCH 3/5] PM / devfreq: passive: Reduce duplicate code when
|
||||
passive_devfreq case
|
||||
|
||||
In order to keep the consistent coding style between passive_devfreq
|
||||
and passive_cpufreq, use common code for handling required opp property.
|
||||
Also remove the unneed conditional statement and unify the comment
|
||||
of both passive_devfreq and passive_cpufreq when getting the target frequency.
|
||||
|
||||
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_passive.c | 66 ++++--------------------------
|
||||
1 file changed, 8 insertions(+), 58 deletions(-)
|
||||
|
||||
--- a/drivers/devfreq/governor_passive.c
|
||||
+++ b/drivers/devfreq/governor_passive.c
|
||||
@@ -93,65 +93,16 @@ static int get_target_freq_with_devfreq(
|
||||
= (struct devfreq_passive_data *)devfreq->data;
|
||||
struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent;
|
||||
unsigned long child_freq = ULONG_MAX;
|
||||
- struct dev_pm_opp *opp, *p_opp;
|
||||
int i, count;
|
||||
|
||||
- /*
|
||||
- * If the devfreq device with passive governor has the specific method
|
||||
- * to determine the next frequency, should use the get_target_freq()
|
||||
- * of struct devfreq_passive_data.
|
||||
- */
|
||||
- if (p_data->get_target_freq)
|
||||
- return p_data->get_target_freq(devfreq, freq);
|
||||
-
|
||||
- /*
|
||||
- * If the parent and passive devfreq device uses the OPP table,
|
||||
- * get the next frequency by using the OPP table.
|
||||
- */
|
||||
-
|
||||
- /*
|
||||
- * - parent devfreq device uses the governors except for passive.
|
||||
- * - passive devfreq device uses the passive governor.
|
||||
- *
|
||||
- * Each devfreq has the OPP table. After deciding the new frequency
|
||||
- * from the governor of parent devfreq device, the passive governor
|
||||
- * need to get the index of new frequency on OPP table of parent
|
||||
- * device. And then the index is used for getting the suitable
|
||||
- * new frequency for passive devfreq device.
|
||||
- */
|
||||
- if (!devfreq->profile || !devfreq->profile->freq_table
|
||||
- || devfreq->profile->max_state <= 0)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- /*
|
||||
- * The passive governor have to get the correct frequency from OPP
|
||||
- * list of parent device. Because in this case, *freq is temporary
|
||||
- * value which is decided by ondemand governor.
|
||||
- */
|
||||
- if (devfreq->opp_table && parent_devfreq->opp_table) {
|
||||
- p_opp = devfreq_recommended_opp(parent_devfreq->dev.parent,
|
||||
- freq, 0);
|
||||
- if (IS_ERR(p_opp))
|
||||
- return PTR_ERR(p_opp);
|
||||
-
|
||||
- opp = dev_pm_opp_xlate_required_opp(parent_devfreq->opp_table,
|
||||
- devfreq->opp_table, p_opp);
|
||||
- dev_pm_opp_put(p_opp);
|
||||
-
|
||||
- if (IS_ERR(opp))
|
||||
- goto no_required_opp;
|
||||
-
|
||||
- *freq = dev_pm_opp_get_freq(opp);
|
||||
- dev_pm_opp_put(opp);
|
||||
-
|
||||
- return 0;
|
||||
- }
|
||||
+ /* Get target freq via required opps */
|
||||
+ child_freq = get_target_freq_by_required_opp(parent_devfreq->dev.parent,
|
||||
+ parent_devfreq->opp_table,
|
||||
+ devfreq->opp_table, freq);
|
||||
+ if (child_freq)
|
||||
+ goto out;
|
||||
|
||||
-no_required_opp:
|
||||
- /*
|
||||
- * Get the OPP table's index of decided frequency by governor
|
||||
- * of parent device.
|
||||
- */
|
||||
+ /* Use interpolation if required opps is not available */
|
||||
for (i = 0; i < parent_devfreq->profile->max_state; i++)
|
||||
if (parent_devfreq->profile->freq_table[i] == *freq)
|
||||
break;
|
||||
@@ -159,7 +110,6 @@ no_required_opp:
|
||||
if (i == parent_devfreq->profile->max_state)
|
||||
return -EINVAL;
|
||||
|
||||
- /* Get the suitable frequency by using index of parent device. */
|
||||
if (i < devfreq->profile->max_state) {
|
||||
child_freq = devfreq->profile->freq_table[i];
|
||||
} else {
|
||||
@@ -167,7 +117,7 @@ no_required_opp:
|
||||
child_freq = devfreq->profile->freq_table[count - 1];
|
||||
}
|
||||
|
||||
- /* Return the suitable frequency for passive device. */
|
||||
+out:
|
||||
*freq = child_freq;
|
||||
|
||||
return 0;
|
@ -0,0 +1,232 @@
|
||||
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
|
||||
@@ -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
|
||||
|
@ -0,0 +1,31 @@
|
||||
From 42d2607d91c4ec37ea1970899c2d614824f3014b Mon Sep 17 00:00:00 2001
|
||||
From: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
Date: Thu, 19 May 2022 10:07:53 +0900
|
||||
Subject: [PATCH 5/5] PM / devfreq: passive: Return non-error when
|
||||
not-supported event is required
|
||||
|
||||
Each devfreq governor specifies the supported governor event
|
||||
such as GOV_START and GOV_STOP. When not-supported event is required,
|
||||
just return non-error. But, commit ce9a0d88d97a ("PM / devfreq: Add
|
||||
cpu based scaling support to passive governor") returned the error
|
||||
value. So that return non-error value when not-supported event is required.
|
||||
|
||||
Fixes: ce9a0d88d97a ("PM / devfreq: Add cpu based scaling support to passive governor")
|
||||
Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
---
|
||||
drivers/devfreq/governor_passive.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/devfreq/governor_passive.c
|
||||
+++ b/drivers/devfreq/governor_passive.c
|
||||
@@ -402,7 +402,7 @@ static int devfreq_passive_event_handler
|
||||
{
|
||||
struct devfreq_passive_data *p_data
|
||||
= (struct devfreq_passive_data *)devfreq->data;
|
||||
- int ret = -EINVAL;
|
||||
+ int ret = 0;
|
||||
|
||||
if (!p_data)
|
||||
return -EINVAL;
|
@ -0,0 +1,31 @@
|
||||
From 82c66d2bbbeda9e493487e7413769087a0b46250 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 20 Jun 2022 00:29:39 +0200
|
||||
Subject: [PATCH 1/1] PM / devfreq: Fix kernel warning with cpufreq passive
|
||||
register fail
|
||||
|
||||
Remove cpufreq_passive_unregister_notifier from
|
||||
cpufreq_passive_register_notifier in case of error as devfreq core
|
||||
already call unregister on GOV_START fail.
|
||||
|
||||
This fix the kernel always printing a WARN on governor PROBE_DEFER as
|
||||
cpufreq_passive_unregister_notifier is called two times and return
|
||||
error on the second call as the cpufreq is already unregistered.
|
||||
|
||||
Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
---
|
||||
drivers/devfreq/governor_passive.c | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
--- a/drivers/devfreq/governor_passive.c
|
||||
+++ b/drivers/devfreq/governor_passive.c
|
||||
@@ -336,7 +336,6 @@ err_free_cpu_data:
|
||||
err_put_policy:
|
||||
cpufreq_cpu_put(policy);
|
||||
err:
|
||||
- WARN_ON(cpufreq_passive_unregister_notifier(devfreq));
|
||||
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
From 8953603eb5447be52f6fc3d8fcae1b3ce9899189 Mon Sep 17 00:00:00 2001
|
||||
From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 6 Jun 2022 11:58:49 +0200
|
||||
Subject: [PATCH v4 1/4] PM / devfreq: Fix cpufreq passive unregister erroring
|
||||
on PROBE_DEFER
|
||||
|
||||
With the passive governor, the cpu based scaling can PROBE_DEFER due to
|
||||
the fact that CPU policy are not ready.
|
||||
The cpufreq passive unregister notifier is called both from the
|
||||
GOV_START errors and for the GOV_STOP and assume the notifier is
|
||||
successfully registred every time. With GOV_START failing it's wrong to
|
||||
loop over each possible CPU since the register path has failed for
|
||||
some CPU policy not ready. Change the logic and unregister the notifer
|
||||
based on the current allocated parent_cpu_data list to correctly handle
|
||||
errors and the governor unregister path.
|
||||
|
||||
Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
|
||||
Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/devfreq/governor_passive.c | 39 +++++++++++++-----------------
|
||||
1 file changed, 17 insertions(+), 22 deletions(-)
|
||||
|
||||
--- a/drivers/devfreq/governor_passive.c
|
||||
+++ b/drivers/devfreq/governor_passive.c
|
||||
@@ -34,6 +34,20 @@ get_parent_cpu_data(struct devfreq_passi
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+static void delete_parent_cpu_data(struct devfreq_passive_data *p_data)
|
||||
+{
|
||||
+ struct devfreq_cpu_data *parent_cpu_data, *tmp;
|
||||
+
|
||||
+ list_for_each_entry_safe(parent_cpu_data, tmp, &p_data->cpu_data_list, node) {
|
||||
+ 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);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static unsigned long get_target_freq_by_required_opp(struct device *p_dev,
|
||||
struct opp_table *p_opp_table,
|
||||
struct opp_table *opp_table,
|
||||
@@ -222,8 +236,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 = 0;
|
||||
+ int ret;
|
||||
|
||||
if (p_data->nb.notifier_call) {
|
||||
ret = cpufreq_unregister_notifier(&p_data->nb,
|
||||
@@ -232,27 +245,9 @@ static int cpufreq_passive_unregister_no
|
||||
return ret;
|
||||
}
|
||||
|
||||
- for_each_possible_cpu(cpu) {
|
||||
- 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);
|
||||
- }
|
||||
+ delete_parent_cpu_data(p_data);
|
||||
|
||||
- return ret;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
|
@ -0,0 +1,39 @@
|
||||
From 57e00b40033a376de3f3cf0bb9bf7590d2dd679d Mon Sep 17 00:00:00 2001
|
||||
From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 14 Jun 2022 13:06:59 +0200
|
||||
Subject: [PATCH 1/1] PM / devfreq: Fix kernel panic with cpu based scaling to
|
||||
passive gov
|
||||
|
||||
The cpufreq passive register notifier can PROBE_DEFER and the devfreq
|
||||
struct is freed and then reallocaed on probe retry.
|
||||
The current logic assume that the code can't PROBE_DEFER so the devfreq
|
||||
struct in the this variable in devfreq_passive_data is assumed to be
|
||||
(if already set) always correct.
|
||||
This cause kernel panic as the code try to access the wrong address.
|
||||
To correctly handle this, update the this variable in
|
||||
devfreq_passive_data to the devfreq reallocated struct.
|
||||
|
||||
Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
|
||||
Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
---
|
||||
drivers/devfreq/governor_passive.c | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c
|
||||
index 72c67979ebe1..091a69e1f487 100644
|
||||
--- a/drivers/devfreq/governor_passive.c
|
||||
+++ b/drivers/devfreq/governor_passive.c
|
||||
@@ -407,8 +407,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
|
||||
if (!p_data)
|
||||
return -EINVAL;
|
||||
|
||||
- if (!p_data->this)
|
||||
- p_data->this = devfreq;
|
||||
+ p_data->this = devfreq;
|
||||
|
||||
switch (event) {
|
||||
case DEVFREQ_GOV_START:
|
||||
--
|
||||
2.37.2
|
||||
|
@ -0,0 +1,269 @@
|
||||
From 46d05776a1a5dd8eb479e868f5ff4f4b97d68238 Mon Sep 17 00:00:00 2001
|
||||
From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 6 Jun 2022 12:39:19 +0200
|
||||
Subject: [PATCH v4 3/4] PM / devfreq: Rework freq_table to be local to devfreq
|
||||
struct
|
||||
|
||||
Currently we reference the freq_table to the profile defined one and we
|
||||
make changes on it. Devfreq never supported PROBE_DEFER before the cpu
|
||||
based scaling support to the passive governor and assumed that a devfreq
|
||||
device could only had error and be done with it.
|
||||
Now that a device can PROBE_DEFER a rework to the freq_table logic is
|
||||
required.
|
||||
|
||||
If a device PROBE_DEFER on the GOV_START, the freq_table is already set
|
||||
in the device profile struct and its init is skipped. This is due to the
|
||||
fact that it's common for devs to declare this kind of struct static.
|
||||
This cause the devfreq logic to find a freq table declared (freq_table
|
||||
not NULL) with random data and poiting to the old addrs freed by devm.
|
||||
|
||||
This problem CAN be solved by devs by clearing the freq_table in their
|
||||
profile struct on driver exit path but it should not be trusted and it
|
||||
looks to use a flawed logic.
|
||||
|
||||
A better solution is to move the freq_table and max_state to the
|
||||
devfreq struct and never change the profile struct.
|
||||
This permit to correctly handle PROBE_DEFER since the devfreq struct is
|
||||
reallocated and contains new values.
|
||||
Also the profile struct should only be used to init the driver and should
|
||||
not be used by the devfreq to write the freq_table if it's not provided
|
||||
by the driver.
|
||||
|
||||
Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
|
||||
Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/devfreq/devfreq.c | 71 ++++++++++++++----------------
|
||||
drivers/devfreq/governor_passive.c | 14 +++---
|
||||
include/linux/devfreq.h | 4 ++
|
||||
3 files changed, 45 insertions(+), 44 deletions(-)
|
||||
|
||||
--- a/drivers/devfreq/devfreq.c
|
||||
+++ b/drivers/devfreq/devfreq.c
|
||||
@@ -123,7 +123,7 @@ void devfreq_get_freq_range(struct devfr
|
||||
unsigned long *min_freq,
|
||||
unsigned long *max_freq)
|
||||
{
|
||||
- unsigned long *freq_table = devfreq->profile->freq_table;
|
||||
+ unsigned long *freq_table = devfreq->freq_table;
|
||||
s32 qos_min_freq, qos_max_freq;
|
||||
|
||||
lockdep_assert_held(&devfreq->lock);
|
||||
@@ -133,11 +133,11 @@ void devfreq_get_freq_range(struct devfr
|
||||
* The devfreq drivers can initialize this in either ascending or
|
||||
* descending order and devfreq core supports both.
|
||||
*/
|
||||
- if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) {
|
||||
+ if (freq_table[0] < freq_table[devfreq->max_state - 1]) {
|
||||
*min_freq = freq_table[0];
|
||||
- *max_freq = freq_table[devfreq->profile->max_state - 1];
|
||||
+ *max_freq = freq_table[devfreq->max_state - 1];
|
||||
} else {
|
||||
- *min_freq = freq_table[devfreq->profile->max_state - 1];
|
||||
+ *min_freq = freq_table[devfreq->max_state - 1];
|
||||
*max_freq = freq_table[0];
|
||||
}
|
||||
|
||||
@@ -169,8 +169,8 @@ static int devfreq_get_freq_level(struct
|
||||
{
|
||||
int lev;
|
||||
|
||||
- for (lev = 0; lev < devfreq->profile->max_state; lev++)
|
||||
- if (freq == devfreq->profile->freq_table[lev])
|
||||
+ for (lev = 0; lev < devfreq->max_state; lev++)
|
||||
+ if (freq == devfreq->freq_table[lev])
|
||||
return lev;
|
||||
|
||||
return -EINVAL;
|
||||
@@ -178,7 +178,6 @@ static int devfreq_get_freq_level(struct
|
||||
|
||||
static int set_freq_table(struct devfreq *devfreq)
|
||||
{
|
||||
- struct devfreq_dev_profile *profile = devfreq->profile;
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned long freq;
|
||||
int i, count;
|
||||
@@ -188,25 +187,22 @@ static int set_freq_table(struct devfreq
|
||||
if (count <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
- profile->max_state = count;
|
||||
- profile->freq_table = devm_kcalloc(devfreq->dev.parent,
|
||||
- profile->max_state,
|
||||
- sizeof(*profile->freq_table),
|
||||
- GFP_KERNEL);
|
||||
- if (!profile->freq_table) {
|
||||
- profile->max_state = 0;
|
||||
+ devfreq->max_state = count;
|
||||
+ devfreq->freq_table = devm_kcalloc(devfreq->dev.parent,
|
||||
+ devfreq->max_state,
|
||||
+ sizeof(*devfreq->freq_table),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!devfreq->freq_table)
|
||||
return -ENOMEM;
|
||||
- }
|
||||
|
||||
- for (i = 0, freq = 0; i < profile->max_state; i++, freq++) {
|
||||
+ for (i = 0, freq = 0; i < devfreq->max_state; i++, freq++) {
|
||||
opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq);
|
||||
if (IS_ERR(opp)) {
|
||||
- devm_kfree(devfreq->dev.parent, profile->freq_table);
|
||||
- profile->max_state = 0;
|
||||
+ devm_kfree(devfreq->dev.parent, devfreq->freq_table);
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
dev_pm_opp_put(opp);
|
||||
- profile->freq_table[i] = freq;
|
||||
+ devfreq->freq_table[i] = freq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -246,7 +242,7 @@ int devfreq_update_status(struct devfreq
|
||||
|
||||
if (lev != prev_lev) {
|
||||
devfreq->stats.trans_table[
|
||||
- (prev_lev * devfreq->profile->max_state) + lev]++;
|
||||
+ (prev_lev * devfreq->max_state) + lev]++;
|
||||
devfreq->stats.total_trans++;
|
||||
}
|
||||
|
||||
@@ -835,6 +831,9 @@ struct devfreq *devfreq_add_device(struc
|
||||
if (err < 0)
|
||||
goto err_dev;
|
||||
mutex_lock(&devfreq->lock);
|
||||
+ } else {
|
||||
+ devfreq->freq_table = devfreq->profile->freq_table;
|
||||
+ devfreq->max_state = devfreq->profile->max_state;
|
||||
}
|
||||
|
||||
devfreq->scaling_min_freq = find_available_min_freq(devfreq);
|
||||
@@ -870,8 +869,8 @@ struct devfreq *devfreq_add_device(struc
|
||||
|
||||
devfreq->stats.trans_table = devm_kzalloc(&devfreq->dev,
|
||||
array3_size(sizeof(unsigned int),
|
||||
- devfreq->profile->max_state,
|
||||
- devfreq->profile->max_state),
|
||||
+ devfreq->max_state,
|
||||
+ devfreq->max_state),
|
||||
GFP_KERNEL);
|
||||
if (!devfreq->stats.trans_table) {
|
||||
mutex_unlock(&devfreq->lock);
|
||||
@@ -880,7 +879,7 @@ struct devfreq *devfreq_add_device(struc
|
||||
}
|
||||
|
||||
devfreq->stats.time_in_state = devm_kcalloc(&devfreq->dev,
|
||||
- devfreq->profile->max_state,
|
||||
+ devfreq->max_state,
|
||||
sizeof(*devfreq->stats.time_in_state),
|
||||
GFP_KERNEL);
|
||||
if (!devfreq->stats.time_in_state) {
|
||||
@@ -1639,9 +1638,9 @@ static ssize_t available_frequencies_sho
|
||||
|
||||
mutex_lock(&df->lock);
|
||||
|
||||
- for (i = 0; i < df->profile->max_state; i++)
|
||||
+ for (i = 0; i < df->max_state; i++)
|
||||
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
|
||||
- "%lu ", df->profile->freq_table[i]);
|
||||
+ "%lu ", df->freq_table[i]);
|
||||
|
||||
mutex_unlock(&df->lock);
|
||||
/* Truncate the trailing space */
|
||||
@@ -1664,7 +1663,7 @@ static ssize_t trans_stat_show(struct de
|
||||
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
- max_state = df->profile->max_state;
|
||||
+ max_state = df->max_state;
|
||||
|
||||
if (max_state == 0)
|
||||
return sprintf(buf, "Not Supported.\n");
|
||||
@@ -1681,19 +1680,17 @@ static ssize_t trans_stat_show(struct de
|
||||
len += sprintf(buf + len, " :");
|
||||
for (i = 0; i < max_state; i++)
|
||||
len += sprintf(buf + len, "%10lu",
|
||||
- df->profile->freq_table[i]);
|
||||
+ df->freq_table[i]);
|
||||
|
||||
len += sprintf(buf + len, " time(ms)\n");
|
||||
|
||||
for (i = 0; i < max_state; i++) {
|
||||
- if (df->profile->freq_table[i]
|
||||
- == df->previous_freq) {
|
||||
+ if (df->freq_table[i] == df->previous_freq)
|
||||
len += sprintf(buf + len, "*");
|
||||
- } else {
|
||||
+ else
|
||||
len += sprintf(buf + len, " ");
|
||||
- }
|
||||
- len += sprintf(buf + len, "%10lu:",
|
||||
- df->profile->freq_table[i]);
|
||||
+
|
||||
+ len += sprintf(buf + len, "%10lu:", df->freq_table[i]);
|
||||
for (j = 0; j < max_state; j++)
|
||||
len += sprintf(buf + len, "%10u",
|
||||
df->stats.trans_table[(i * max_state) + j]);
|
||||
@@ -1717,7 +1714,7 @@ static ssize_t trans_stat_store(struct d
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
|
||||
- if (df->profile->max_state == 0)
|
||||
+ if (df->max_state == 0)
|
||||
return count;
|
||||
|
||||
err = kstrtoint(buf, 10, &value);
|
||||
@@ -1725,11 +1722,11 @@ static ssize_t trans_stat_store(struct d
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&df->lock);
|
||||
- memset(df->stats.time_in_state, 0, (df->profile->max_state *
|
||||
+ memset(df->stats.time_in_state, 0, (df->max_state *
|
||||
sizeof(*df->stats.time_in_state)));
|
||||
memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int),
|
||||
- df->profile->max_state,
|
||||
- df->profile->max_state));
|
||||
+ df->max_state,
|
||||
+ df->max_state));
|
||||
df->stats.total_trans = 0;
|
||||
df->stats.last_update = get_jiffies_64();
|
||||
mutex_unlock(&df->lock);
|
||||
--- a/drivers/devfreq/governor_passive.c
|
||||
+++ b/drivers/devfreq/governor_passive.c
|
||||
@@ -145,18 +145,18 @@ static int get_target_freq_with_devfreq(
|
||||
goto out;
|
||||
|
||||
/* Use interpolation if required opps is not available */
|
||||
- for (i = 0; i < parent_devfreq->profile->max_state; i++)
|
||||
- if (parent_devfreq->profile->freq_table[i] == *freq)
|
||||
+ for (i = 0; i < parent_devfreq->max_state; i++)
|
||||
+ if (parent_devfreq->freq_table[i] == *freq)
|
||||
break;
|
||||
|
||||
- if (i == parent_devfreq->profile->max_state)
|
||||
+ if (i == parent_devfreq->max_state)
|
||||
return -EINVAL;
|
||||
|
||||
- if (i < devfreq->profile->max_state) {
|
||||
- child_freq = devfreq->profile->freq_table[i];
|
||||
+ if (i < devfreq->max_state) {
|
||||
+ child_freq = devfreq->freq_table[i];
|
||||
} else {
|
||||
- count = devfreq->profile->max_state;
|
||||
- child_freq = devfreq->profile->freq_table[count - 1];
|
||||
+ count = devfreq->max_state;
|
||||
+ child_freq = devfreq->freq_table[count - 1];
|
||||
}
|
||||
|
||||
out:
|
||||
--- a/include/linux/devfreq.h
|
||||
+++ b/include/linux/devfreq.h
|
||||
@@ -185,6 +185,10 @@ struct devfreq {
|
||||
struct notifier_block nb;
|
||||
struct delayed_work work;
|
||||
|
||||
+ /* devfreq local freq_table */
|
||||
+ unsigned long *freq_table;
|
||||
+ unsigned int max_state;
|
||||
+
|
||||
unsigned long previous_freq;
|
||||
struct devfreq_dev_status last_status;
|
||||
|
@ -0,0 +1,28 @@
|
||||
From eee9f767c41b03a2744d4b0f0c1a144e4ff41e78 Mon Sep 17 00:00:00 2001
|
||||
From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 6 Jun 2022 13:01:02 +0200
|
||||
Subject: [PATCH v4 4/4] PM / devfreq: Mute warning on governor PROBE_DEFER
|
||||
|
||||
Don't print warning when a governor PROBE_DEFER as it's not a real
|
||||
GOV_START fail.
|
||||
|
||||
Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
|
||||
Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/devfreq/devfreq.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/devfreq/devfreq.c
|
||||
+++ b/drivers/devfreq/devfreq.c
|
||||
@@ -931,8 +931,9 @@ struct devfreq *devfreq_add_device(struc
|
||||
err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START,
|
||||
NULL);
|
||||
if (err) {
|
||||
- dev_err(dev, "%s: Unable to start governor for the device\n",
|
||||
- __func__);
|
||||
+ dev_err_probe(dev, err,
|
||||
+ "%s: Unable to start governor for the device\n",
|
||||
+ __func__);
|
||||
goto err_init;
|
||||
}
|
||||
create_sysfs_files(devfreq, devfreq->governor);
|
Loading…
Reference in New Issue
Block a user