thermal: cpu_cooling: Fix NULL dereference in cpufreq_state2power

Currently all CPU cooling devices share a
`struct thermal_cooling_device_ops` instance. The thermal core uses the
presence of functions in this struct to determine if a cooling device
has a power model (see cdev_is_power_actor). cpu_cooling.c adds the
power model functions to the shared struct when a device is registered
with a power model.

Therefore, if a CPU cooling device is registered using
[of_]cpufreq_power_cooling_register, _all_ devices will be determined to
have a power model, including any registered with
[of_]cpufreq_cooling_register. This can result in cpufreq_state2power
being called on a device where dyn_power_table is NULL.

With this commit, instead of having a shared thermal_cooling_device_ops
which is mutated, we have two versions: one with the power functions and
one without.

Signed-off-by: Brendan Jackman <brendan.jackman@arm.com>
Cc: Amit Daniel Kachhap <amit.kachhap@gmail.com>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Javi Merino <javi.merino@arm.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Acked-by: Javi Merino <javi.merino@arm.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
This commit is contained in:
Brendan Jackman 2016-08-17 16:14:59 +01:00 коммит произвёл Zhang Rui
Родитель 694d0d0bb2
Коммит a305a4387a
1 изменённых файлов: 16 добавлений и 5 удалений

Просмотреть файл

@ -740,12 +740,22 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev,
} }
/* Bind cpufreq callbacks to thermal cooling device ops */ /* Bind cpufreq callbacks to thermal cooling device ops */
static struct thermal_cooling_device_ops cpufreq_cooling_ops = { static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
.get_max_state = cpufreq_get_max_state, .get_max_state = cpufreq_get_max_state,
.get_cur_state = cpufreq_get_cur_state, .get_cur_state = cpufreq_get_cur_state,
.set_cur_state = cpufreq_set_cur_state, .set_cur_state = cpufreq_set_cur_state,
}; };
static struct thermal_cooling_device_ops cpufreq_power_cooling_ops = {
.get_max_state = cpufreq_get_max_state,
.get_cur_state = cpufreq_get_cur_state,
.set_cur_state = cpufreq_set_cur_state,
.get_requested_power = cpufreq_get_requested_power,
.state2power = cpufreq_state2power,
.power2state = cpufreq_power2state,
};
/* Notifier for cpufreq policy change */ /* Notifier for cpufreq policy change */
static struct notifier_block thermal_cpufreq_notifier_block = { static struct notifier_block thermal_cpufreq_notifier_block = {
.notifier_call = cpufreq_thermal_notifier, .notifier_call = cpufreq_thermal_notifier,
@ -795,6 +805,7 @@ __cpufreq_cooling_register(struct device_node *np,
struct cpumask temp_mask; struct cpumask temp_mask;
unsigned int freq, i, num_cpus; unsigned int freq, i, num_cpus;
int ret; int ret;
struct thermal_cooling_device_ops *cooling_ops;
cpumask_and(&temp_mask, clip_cpus, cpu_online_mask); cpumask_and(&temp_mask, clip_cpus, cpu_online_mask);
policy = cpufreq_cpu_get(cpumask_first(&temp_mask)); policy = cpufreq_cpu_get(cpumask_first(&temp_mask));
@ -850,10 +861,6 @@ __cpufreq_cooling_register(struct device_node *np,
cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus); cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
if (capacitance) { if (capacitance) {
cpufreq_cooling_ops.get_requested_power =
cpufreq_get_requested_power;
cpufreq_cooling_ops.state2power = cpufreq_state2power;
cpufreq_cooling_ops.power2state = cpufreq_power2state;
cpufreq_dev->plat_get_static_power = plat_static_func; cpufreq_dev->plat_get_static_power = plat_static_func;
ret = build_dyn_power_table(cpufreq_dev, capacitance); ret = build_dyn_power_table(cpufreq_dev, capacitance);
@ -861,6 +868,10 @@ __cpufreq_cooling_register(struct device_node *np,
cool_dev = ERR_PTR(ret); cool_dev = ERR_PTR(ret);
goto free_table; goto free_table;
} }
cooling_ops = &cpufreq_power_cooling_ops;
} else {
cooling_ops = &cpufreq_cooling_ops;
} }
ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
@ -885,7 +896,7 @@ __cpufreq_cooling_register(struct device_node *np,
cpufreq_dev->id); cpufreq_dev->id);
cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev, cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
&cpufreq_cooling_ops); cooling_ops);
if (IS_ERR(cool_dev)) if (IS_ERR(cool_dev))
goto remove_idr; goto remove_idr;