[RFC PATCH] thermal: update thermal stats table when max cooling state changed

From: Zhang Rui
Date: Wed Apr 01 2020 - 23:18:44 EST


The maximum cooling state of a cooling device may be changed at
runtime. Thus the statistics table must be updated to handle the real
maximum cooling states supported.

This fixes an OOB issue when updating the statistics of the processor
cooling device, because it only supports 1 cooling state before cpufreq
driver loaded.

Fixes: 8ea229511e06 ("thermal: Add cooling device's statistics in sysfs")
Reported-by: Takashi Iwai <tiwai@xxxxxxx>
Signed-off-by: Zhang Rui <rui.zhang@xxxxxxxxx>
---
drivers/thermal/thermal_sysfs.c | 38 +++++++++++++++++++++++++++------
1 file changed, 31 insertions(+), 7 deletions(-)

diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c
index aa99edb4dff7..c69173eb4b24 100644
--- a/drivers/thermal/thermal_sysfs.c
+++ b/drivers/thermal/thermal_sysfs.c
@@ -755,6 +755,9 @@ struct cooling_dev_stats {
unsigned int *trans_table;
};

+static int cooling_device_stats_table_update(struct thermal_cooling_device *cdev);
+static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev);
+
static void update_time_in_state(struct cooling_dev_stats *stats)
{
ktime_t now = ktime_get(), delta;
@@ -768,8 +771,12 @@ static void update_time_in_state(struct cooling_dev_stats *stats)
void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
unsigned long new_state)
{
- struct cooling_dev_stats *stats = cdev->stats;
+ struct cooling_dev_stats *stats;

+ if (cooling_device_stats_table_update(cdev))
+ return;
+
+ stats = cdev->stats;
spin_lock(&stats->lock);

if (stats->state == new_state)
@@ -904,24 +911,32 @@ static const struct attribute_group cooling_device_stats_attr_group = {
.name = "stats"
};

-static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
+static int cooling_device_stats_table_update(struct thermal_cooling_device *cdev)
{
struct cooling_dev_stats *stats;
unsigned long states;
- int var;
+ int var, ret;

- if (cdev->ops->get_max_state(cdev, &states))
- return;
+ ret = cdev->ops->get_max_state(cdev, &states);
+ if (ret)
+ return ret;

states++; /* Total number of states is highest state + 1 */

+ stats = cdev->stats;
+ if (stats) {
+ if (stats->max_states == states)
+ return 0;
+ else
+ cooling_device_stats_destroy(cdev);
+ }
+
var = sizeof(*stats);
var += sizeof(*stats->time_in_state) * states;
var += sizeof(*stats->trans_table) * states * states;
-
stats = kzalloc(var, GFP_KERNEL);
if (!stats)
- return;
+ return -ENOMEM;

stats->time_in_state = (ktime_t *)(stats + 1);
stats->trans_table = (unsigned int *)(stats->time_in_state + states);
@@ -930,6 +945,15 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
stats->max_states = states;

spin_lock_init(&stats->lock);
+ return 0;
+}
+
+static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
+{
+ int var;
+
+ if (cooling_device_stats_table_update(cdev))
+ return;

/* Fill the empty slot left in cooling_device_attr_groups */
var = ARRAY_SIZE(cooling_device_attr_groups) - 2;
--
2.17.1