Skip to content

Commit ba2b137

Browse files
committed
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
Pull thermal management update from Zhang Rui: - Fix race condition in imx_thermal_probe() (Mikhail Lappo) - Add cooling device's statistics in sysfs (Viresh Kumar) * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: thermal: Add cooling device's statistics in sysfs thermal: imx: Fix race condition in imx_thermal_probe()
2 parents 71893f1 + b907b40 commit ba2b137

File tree

8 files changed

+283
-5
lines changed

8 files changed

+283
-5
lines changed

Documentation/thermal/sysfs-api.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ temperature) and throttle appropriate devices.
255255
2. sysfs attributes structure
256256

257257
RO read only value
258+
WO write only value
258259
RW read/write value
259260

260261
Thermal sysfs attributes will be represented under /sys/class/thermal.
@@ -286,6 +287,11 @@ Thermal cooling device sys I/F, created once it's registered:
286287
|---type: Type of the cooling device(processor/fan/...)
287288
|---max_state: Maximum cooling state of the cooling device
288289
|---cur_state: Current cooling state of the cooling device
290+
|---stats: Directory containing cooling device's statistics
291+
|---stats/reset: Writing any value resets the statistics
292+
|---stats/time_in_state_ms: Time (msec) spent in various cooling states
293+
|---stats/total_trans: Total number of times cooling state is changed
294+
|---stats/trans_table: Cooing state transition table
289295

290296

291297
Then next two dynamic attributes are created/removed in pairs. They represent
@@ -490,6 +496,31 @@ cur_state
490496
- cur_state == max_state means the maximum cooling.
491497
RW, Required
492498

499+
stats/reset
500+
Writing any value resets the cooling device's statistics.
501+
WO, Required
502+
503+
stats/time_in_state_ms:
504+
The amount of time spent by the cooling device in various cooling
505+
states. The output will have "<state> <time>" pair in each line, which
506+
will mean this cooling device spent <time> msec of time at <state>.
507+
Output will have one line for each of the supported states. usertime
508+
units here is 10mS (similar to other time exported in /proc).
509+
RO, Required
510+
511+
stats/total_trans:
512+
A single positive value showing the total number of times the state of a
513+
cooling device is changed.
514+
RO, Required
515+
516+
stats/trans_table:
517+
This gives fine grained information about all the cooling state
518+
transitions. The cat output here is a two dimensional matrix, where an
519+
entry <i,j> (row i, column j) represents the number of transitions from
520+
State_i to State_j. If the transition table is bigger than PAGE_SIZE,
521+
reading this will return an -EFBIG error.
522+
RO, Required
523+
493524
3. A simple implementation
494525

495526
ACPI thermal zone may support multiple trip points like critical, hot,

drivers/thermal/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ menuconfig THERMAL
1515

1616
if THERMAL
1717

18+
config THERMAL_STATISTICS
19+
bool "Thermal state transition statistics"
20+
help
21+
Export thermal state transition statistics information through sysfs.
22+
23+
If in doubt, say N.
24+
1825
config THERMAL_EMERGENCY_POWEROFF_DELAY_MS
1926
int "Emergency poweroff delay in milli-seconds"
2027
depends on THERMAL

drivers/thermal/imx_thermal.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,9 @@ static int imx_thermal_probe(struct platform_device *pdev)
637637
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
638638
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
639639

640+
data->irq_enabled = true;
641+
data->mode = THERMAL_DEVICE_ENABLED;
642+
640643
ret = devm_request_threaded_irq(&pdev->dev, data->irq,
641644
imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
642645
0, "imx_thermal", data);
@@ -649,9 +652,6 @@ static int imx_thermal_probe(struct platform_device *pdev)
649652
return ret;
650653
}
651654

652-
data->irq_enabled = true;
653-
data->mode = THERMAL_DEVICE_ENABLED;
654-
655655
return 0;
656656
}
657657

drivers/thermal/thermal_core.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -972,8 +972,8 @@ __thermal_cooling_device_register(struct device_node *np,
972972
cdev->ops = ops;
973973
cdev->updated = false;
974974
cdev->device.class = &thermal_class;
975-
thermal_cooling_device_setup_sysfs(cdev);
976975
cdev->devdata = devdata;
976+
thermal_cooling_device_setup_sysfs(cdev);
977977
dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
978978
result = device_register(&cdev->device);
979979
if (result) {
@@ -1106,6 +1106,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
11061106

11071107
ida_simple_remove(&thermal_cdev_ida, cdev->id);
11081108
device_unregister(&cdev->device);
1109+
thermal_cooling_device_destroy_sysfs(cdev);
11091110
}
11101111
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
11111112

drivers/thermal/thermal_core.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ int thermal_build_list_of_policies(char *buf);
7373
int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
7474
void thermal_zone_destroy_device_groups(struct thermal_zone_device *);
7575
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *);
76+
void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev);
7677
/* used only at binding time */
7778
ssize_t
7879
thermal_cooling_device_trip_point_show(struct device *,
@@ -84,6 +85,15 @@ ssize_t thermal_cooling_device_weight_store(struct device *,
8485
struct device_attribute *,
8586
const char *, size_t);
8687

88+
#ifdef CONFIG_THERMAL_STATISTICS
89+
void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
90+
unsigned long new_state);
91+
#else
92+
static inline void
93+
thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
94+
unsigned long new_state) {}
95+
#endif /* CONFIG_THERMAL_STATISTICS */
96+
8797
#ifdef CONFIG_THERMAL_GOV_STEP_WISE
8898
int thermal_gov_step_wise_register(void);
8999
void thermal_gov_step_wise_unregister(void);

drivers/thermal/thermal_helpers.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,10 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
187187
if (instance->target > target)
188188
target = instance->target;
189189
}
190-
cdev->ops->set_cur_state(cdev, target);
190+
191+
if (!cdev->ops->set_cur_state(cdev, target))
192+
thermal_cooling_device_stats_update(cdev, target);
193+
191194
cdev->updated = true;
192195
mutex_unlock(&cdev->lock);
193196
trace_cdev_update(cdev, target);

drivers/thermal/thermal_sysfs.c

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/err.h>
2121
#include <linux/slab.h>
2222
#include <linux/string.h>
23+
#include <linux/jiffies.h>
2324

2425
#include "thermal_core.h"
2526

@@ -721,6 +722,7 @@ thermal_cooling_device_cur_state_store(struct device *dev,
721722
result = cdev->ops->set_cur_state(cdev, state);
722723
if (result)
723724
return result;
725+
thermal_cooling_device_stats_update(cdev, state);
724726
return count;
725727
}
726728

@@ -745,14 +747,237 @@ static const struct attribute_group cooling_device_attr_group = {
745747

746748
static const struct attribute_group *cooling_device_attr_groups[] = {
747749
&cooling_device_attr_group,
750+
NULL, /* Space allocated for cooling_device_stats_attr_group */
748751
NULL,
749752
};
750753

754+
#ifdef CONFIG_THERMAL_STATISTICS
755+
struct cooling_dev_stats {
756+
spinlock_t lock;
757+
unsigned int total_trans;
758+
unsigned long state;
759+
unsigned long max_states;
760+
ktime_t last_time;
761+
ktime_t *time_in_state;
762+
unsigned int *trans_table;
763+
};
764+
765+
static void update_time_in_state(struct cooling_dev_stats *stats)
766+
{
767+
ktime_t now = ktime_get(), delta;
768+
769+
delta = ktime_sub(now, stats->last_time);
770+
stats->time_in_state[stats->state] =
771+
ktime_add(stats->time_in_state[stats->state], delta);
772+
stats->last_time = now;
773+
}
774+
775+
void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
776+
unsigned long new_state)
777+
{
778+
struct cooling_dev_stats *stats = cdev->stats;
779+
780+
spin_lock(&stats->lock);
781+
782+
if (stats->state == new_state)
783+
goto unlock;
784+
785+
update_time_in_state(stats);
786+
stats->trans_table[stats->state * stats->max_states + new_state]++;
787+
stats->state = new_state;
788+
stats->total_trans++;
789+
790+
unlock:
791+
spin_unlock(&stats->lock);
792+
}
793+
794+
static ssize_t
795+
thermal_cooling_device_total_trans_show(struct device *dev,
796+
struct device_attribute *attr,
797+
char *buf)
798+
{
799+
struct thermal_cooling_device *cdev = to_cooling_device(dev);
800+
struct cooling_dev_stats *stats = cdev->stats;
801+
int ret;
802+
803+
spin_lock(&stats->lock);
804+
ret = sprintf(buf, "%u\n", stats->total_trans);
805+
spin_unlock(&stats->lock);
806+
807+
return ret;
808+
}
809+
810+
static ssize_t
811+
thermal_cooling_device_time_in_state_show(struct device *dev,
812+
struct device_attribute *attr,
813+
char *buf)
814+
{
815+
struct thermal_cooling_device *cdev = to_cooling_device(dev);
816+
struct cooling_dev_stats *stats = cdev->stats;
817+
ssize_t len = 0;
818+
int i;
819+
820+
spin_lock(&stats->lock);
821+
update_time_in_state(stats);
822+
823+
for (i = 0; i < stats->max_states; i++) {
824+
len += sprintf(buf + len, "state%u\t%llu\n", i,
825+
ktime_to_ms(stats->time_in_state[i]));
826+
}
827+
spin_unlock(&stats->lock);
828+
829+
return len;
830+
}
831+
832+
static ssize_t
833+
thermal_cooling_device_reset_store(struct device *dev,
834+
struct device_attribute *attr,
835+
const char *buf, size_t count)
836+
{
837+
struct thermal_cooling_device *cdev = to_cooling_device(dev);
838+
struct cooling_dev_stats *stats = cdev->stats;
839+
int i, states = stats->max_states;
840+
841+
spin_lock(&stats->lock);
842+
843+
stats->total_trans = 0;
844+
stats->last_time = ktime_get();
845+
memset(stats->trans_table, 0,
846+
states * states * sizeof(*stats->trans_table));
847+
848+
for (i = 0; i < stats->max_states; i++)
849+
stats->time_in_state[i] = ktime_set(0, 0);
850+
851+
spin_unlock(&stats->lock);
852+
853+
return count;
854+
}
855+
856+
static ssize_t
857+
thermal_cooling_device_trans_table_show(struct device *dev,
858+
struct device_attribute *attr,
859+
char *buf)
860+
{
861+
struct thermal_cooling_device *cdev = to_cooling_device(dev);
862+
struct cooling_dev_stats *stats = cdev->stats;
863+
ssize_t len = 0;
864+
int i, j;
865+
866+
len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
867+
len += snprintf(buf + len, PAGE_SIZE - len, " : ");
868+
for (i = 0; i < stats->max_states; i++) {
869+
if (len >= PAGE_SIZE)
870+
break;
871+
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
872+
}
873+
if (len >= PAGE_SIZE)
874+
return PAGE_SIZE;
875+
876+
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
877+
878+
for (i = 0; i < stats->max_states; i++) {
879+
if (len >= PAGE_SIZE)
880+
break;
881+
882+
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u:", i);
883+
884+
for (j = 0; j < stats->max_states; j++) {
885+
if (len >= PAGE_SIZE)
886+
break;
887+
len += snprintf(buf + len, PAGE_SIZE - len, "%8u ",
888+
stats->trans_table[i * stats->max_states + j]);
889+
}
890+
if (len >= PAGE_SIZE)
891+
break;
892+
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
893+
}
894+
895+
if (len >= PAGE_SIZE) {
896+
pr_warn_once("Thermal transition table exceeds PAGE_SIZE. Disabling\n");
897+
return -EFBIG;
898+
}
899+
return len;
900+
}
901+
902+
static DEVICE_ATTR(total_trans, 0444, thermal_cooling_device_total_trans_show,
903+
NULL);
904+
static DEVICE_ATTR(time_in_state_ms, 0444,
905+
thermal_cooling_device_time_in_state_show, NULL);
906+
static DEVICE_ATTR(reset, 0200, NULL, thermal_cooling_device_reset_store);
907+
static DEVICE_ATTR(trans_table, 0444,
908+
thermal_cooling_device_trans_table_show, NULL);
909+
910+
static struct attribute *cooling_device_stats_attrs[] = {
911+
&dev_attr_total_trans.attr,
912+
&dev_attr_time_in_state_ms.attr,
913+
&dev_attr_reset.attr,
914+
&dev_attr_trans_table.attr,
915+
NULL
916+
};
917+
918+
static const struct attribute_group cooling_device_stats_attr_group = {
919+
.attrs = cooling_device_stats_attrs,
920+
.name = "stats"
921+
};
922+
923+
static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
924+
{
925+
struct cooling_dev_stats *stats;
926+
unsigned long states;
927+
int var;
928+
929+
if (cdev->ops->get_max_state(cdev, &states))
930+
return;
931+
932+
states++; /* Total number of states is highest state + 1 */
933+
934+
var = sizeof(*stats);
935+
var += sizeof(*stats->time_in_state) * states;
936+
var += sizeof(*stats->trans_table) * states * states;
937+
938+
stats = kzalloc(var, GFP_KERNEL);
939+
if (!stats)
940+
return;
941+
942+
stats->time_in_state = (ktime_t *)(stats + 1);
943+
stats->trans_table = (unsigned int *)(stats->time_in_state + states);
944+
cdev->stats = stats;
945+
stats->last_time = ktime_get();
946+
stats->max_states = states;
947+
948+
spin_lock_init(&stats->lock);
949+
950+
/* Fill the empty slot left in cooling_device_attr_groups */
951+
var = ARRAY_SIZE(cooling_device_attr_groups) - 2;
952+
cooling_device_attr_groups[var] = &cooling_device_stats_attr_group;
953+
}
954+
955+
static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev)
956+
{
957+
kfree(cdev->stats);
958+
cdev->stats = NULL;
959+
}
960+
961+
#else
962+
963+
static inline void
964+
cooling_device_stats_setup(struct thermal_cooling_device *cdev) {}
965+
static inline void
966+
cooling_device_stats_destroy(struct thermal_cooling_device *cdev) {}
967+
968+
#endif /* CONFIG_THERMAL_STATISTICS */
969+
751970
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *cdev)
752971
{
972+
cooling_device_stats_setup(cdev);
753973
cdev->device.groups = cooling_device_attr_groups;
754974
}
755975

976+
void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev)
977+
{
978+
cooling_device_stats_destroy(cdev);
979+
}
980+
756981
/* these helper will be used only at the time of bindig */
757982
ssize_t
758983
thermal_cooling_device_trip_point_show(struct device *dev,

0 commit comments

Comments
 (0)