summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2018-02-22 11:29:44 +0530
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2018-02-27 18:22:12 +0100
commitd417e0691ac00d35c4e6b90fc3fc85631a7865ad (patch)
treef335e4959da76cd7c61542d928e72c31b68dd574
parentb24b6478e65f140610ab1ffaadc7bc6bf0be8aad (diff)
downloadlinux-d417e0691ac00d35c4e6b90fc3fc85631a7865ad.tar.gz
linux-d417e0691ac00d35c4e6b90fc3fc85631a7865ad.tar.bz2
linux-d417e0691ac00d35c4e6b90fc3fc85631a7865ad.zip
cpufreq: Validate frequency table in the core
By design, cpufreq drivers are responsible for calling cpufreq_frequency_table_cpuinfo() from their ->init() callbacks to validate the frequency table. However, if a cpufreq driver is buggy and fails to do so properly, it lead to unexpected behavior of the driver or the cpufreq core at a later point in time. It would be better if the core could validate the frequency table during driver initialization. To that end, introduce cpufreq_table_validate_and_sort() and make the cpufreq core call it right after invoking the ->init() callback of the driver and destroy the cpufreq policy if the table is invalid. For the time being the validation of the table happens twice, once from the driver and then from the core. The individual drivers will be updated separately to drop table validation if they don't need it for other reasons. The frequency table is marked "sorted" or "unsorted" by the new helper now instead of in cpufreq_table_validate_and_show(), as it should only be done after validating the table (which the drivers won't do going forward). Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> [ rjw: Subject/changelog ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/cpufreq/cpufreq.c13
-rw-r--r--drivers/cpufreq/freq_table.c16
-rw-r--r--include/linux/cpufreq.h1
3 files changed, 25 insertions, 5 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 8814c572e263..239063fb6afc 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1219,6 +1219,10 @@ static int cpufreq_online(unsigned int cpu)
goto out_free_policy;
}
+ ret = cpufreq_table_validate_and_sort(policy);
+ if (ret)
+ goto out_exit_policy;
+
down_write(&policy->rwsem);
if (new_policy) {
@@ -1249,7 +1253,7 @@ static int cpufreq_online(unsigned int cpu)
policy->cur = cpufreq_driver->get(policy->cpu);
if (!policy->cur) {
pr_err("%s: ->get() failed\n", __func__);
- goto out_exit_policy;
+ goto out_destroy_policy;
}
}
@@ -1296,7 +1300,7 @@ static int cpufreq_online(unsigned int cpu)
if (new_policy) {
ret = cpufreq_add_dev_interface(policy);
if (ret)
- goto out_exit_policy;
+ goto out_destroy_policy;
cpufreq_stats_create_table(policy);
@@ -1311,7 +1315,7 @@ static int cpufreq_online(unsigned int cpu)
__func__, cpu, ret);
/* cpufreq_policy_free() will notify based on this */
new_policy = false;
- goto out_exit_policy;
+ goto out_destroy_policy;
}
up_write(&policy->rwsem);
@@ -1326,12 +1330,13 @@ static int cpufreq_online(unsigned int cpu)
return 0;
-out_exit_policy:
+out_destroy_policy:
for_each_cpu(j, policy->real_cpus)
remove_cpu_dev_symlink(policy, get_cpu_device(j));
up_write(&policy->rwsem);
+out_exit_policy:
if (cpufreq_driver->exit)
cpufreq_driver->exit(policy);
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index 6d007f824ca7..10e119ae66dd 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -362,10 +362,24 @@ int cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
return ret;
policy->freq_table = table;
- return set_freq_table_sorted(policy);
+ return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show);
+int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ if (!policy->freq_table)
+ return 0;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
+ if (ret)
+ return ret;
+
+ return set_freq_table_sorted(policy);
+}
+
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq frequency table helpers");
MODULE_LICENSE("GPL");
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 21e8d248d956..1fe49724da9e 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -962,6 +962,7 @@ extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs;
extern struct freq_attr *cpufreq_generic_attr[];
int cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table);
+int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy);
unsigned int cpufreq_generic_get(unsigned int cpu);
int cpufreq_generic_init(struct cpufreq_policy *policy,