Skip to content

Commit 790930f

Browse files
committed
thermal: core: Introduce thermal_cooling_device_update()
Introduce a core thermal API function, thermal_cooling_device_update(), for updating the max_state value for a cooling device and rearranging its statistics in sysfs after a possible change of its ->get_max_state() callback return value. That callback is now invoked only once, during cooling device registration, to populate the max_state field in the cooling device object, so if its return value changes, it needs to be invoked again and the new return value needs to be stored as max_state. Moreover, the statistics presented in sysfs need to be rearranged in general, because there may not be enough room in them to store data for all of the possible states (in the case when max_state grows). The new function takes care of that (and some other minor things related to it), but some extra locking and lockdep annotations are added in several places too to protect against crashes in the cases when the statistics are not present or when a stale max_state value might be used by sysfs attributes. Note that the actual user of the new function will be added separately. Link: https://lore.kernel.org/linux-pm/[email protected]/ Signed-off-by: Rafael J. Wysocki <[email protected]> Tested-by: Zhang Rui <[email protected]> Reviewed-by: Zhang Rui <[email protected]>
1 parent c43198a commit 790930f

File tree

4 files changed

+150
-10
lines changed

4 files changed

+150
-10
lines changed

drivers/thermal/thermal_core.c

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
613613
struct thermal_instance *pos;
614614
struct thermal_zone_device *pos1;
615615
struct thermal_cooling_device *pos2;
616+
bool upper_no_limit;
616617
int result;
617618

618619
if (trip >= tz->num_trips || trip < 0)
@@ -632,7 +633,13 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
632633

633634
/* lower default 0, upper default max_state */
634635
lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
635-
upper = upper == THERMAL_NO_LIMIT ? cdev->max_state : upper;
636+
637+
if (upper == THERMAL_NO_LIMIT) {
638+
upper = cdev->max_state;
639+
upper_no_limit = true;
640+
} else {
641+
upper_no_limit = false;
642+
}
636643

637644
if (lower > upper || upper > cdev->max_state)
638645
return -EINVAL;
@@ -644,6 +651,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
644651
dev->cdev = cdev;
645652
dev->trip = trip;
646653
dev->upper = upper;
654+
dev->upper_no_limit = upper_no_limit;
647655
dev->lower = lower;
648656
dev->target = THERMAL_NO_TARGET;
649657
dev->weight = weight;
@@ -1057,6 +1065,79 @@ static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev)
10571065
return false;
10581066
}
10591067

1068+
/**
1069+
* thermal_cooling_device_update - Update a cooling device object
1070+
* @cdev: Target cooling device.
1071+
*
1072+
* Update @cdev to reflect a change of the underlying hardware or platform.
1073+
*
1074+
* Must be called when the maximum cooling state of @cdev becomes invalid and so
1075+
* its .get_max_state() callback needs to be run to produce the new maximum
1076+
* cooling state value.
1077+
*/
1078+
void thermal_cooling_device_update(struct thermal_cooling_device *cdev)
1079+
{
1080+
struct thermal_instance *ti;
1081+
unsigned long state;
1082+
1083+
if (IS_ERR_OR_NULL(cdev))
1084+
return;
1085+
1086+
/*
1087+
* Hold thermal_list_lock throughout the update to prevent the device
1088+
* from going away while being updated.
1089+
*/
1090+
mutex_lock(&thermal_list_lock);
1091+
1092+
if (!thermal_cooling_device_present(cdev))
1093+
goto unlock_list;
1094+
1095+
/*
1096+
* Update under the cdev lock to prevent the state from being set beyond
1097+
* the new limit concurrently.
1098+
*/
1099+
mutex_lock(&cdev->lock);
1100+
1101+
if (cdev->ops->get_max_state(cdev, &cdev->max_state))
1102+
goto unlock;
1103+
1104+
thermal_cooling_device_stats_reinit(cdev);
1105+
1106+
list_for_each_entry(ti, &cdev->thermal_instances, cdev_node) {
1107+
if (ti->upper == cdev->max_state)
1108+
continue;
1109+
1110+
if (ti->upper < cdev->max_state) {
1111+
if (ti->upper_no_limit)
1112+
ti->upper = cdev->max_state;
1113+
1114+
continue;
1115+
}
1116+
1117+
ti->upper = cdev->max_state;
1118+
if (ti->lower > ti->upper)
1119+
ti->lower = ti->upper;
1120+
1121+
if (ti->target == THERMAL_NO_TARGET)
1122+
continue;
1123+
1124+
if (ti->target > ti->upper)
1125+
ti->target = ti->upper;
1126+
}
1127+
1128+
if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state)
1129+
goto unlock;
1130+
1131+
thermal_cooling_device_stats_update(cdev, state);
1132+
1133+
unlock:
1134+
mutex_unlock(&cdev->lock);
1135+
1136+
unlock_list:
1137+
mutex_unlock(&thermal_list_lock);
1138+
}
1139+
EXPORT_SYMBOL_GPL(thermal_cooling_device_update);
1140+
10601141
static void __unbind(struct thermal_zone_device *tz, int mask,
10611142
struct thermal_cooling_device *cdev)
10621143
{

drivers/thermal/thermal_core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ struct thermal_instance {
101101
struct list_head tz_node; /* node in tz->thermal_instances */
102102
struct list_head cdev_node; /* node in cdev->thermal_instances */
103103
unsigned int weight; /* The weight of the cooling device */
104+
bool upper_no_limit;
104105
};
105106

106107
#define to_thermal_zone(_dev) \
@@ -127,6 +128,7 @@ int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
127128
void thermal_zone_destroy_device_groups(struct thermal_zone_device *);
128129
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *);
129130
void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev);
131+
void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev);
130132
/* used only at binding time */
131133
ssize_t trip_point_show(struct device *, struct device_attribute *, char *);
132134
ssize_t weight_show(struct device *, struct device_attribute *, char *);

drivers/thermal/thermal_sysfs.c

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,8 @@ void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
685685
{
686686
struct cooling_dev_stats *stats = cdev->stats;
687687

688+
lockdep_assert_held(&cdev->lock);
689+
688690
if (!stats)
689691
return;
690692

@@ -706,13 +708,22 @@ static ssize_t total_trans_show(struct device *dev,
706708
struct device_attribute *attr, char *buf)
707709
{
708710
struct thermal_cooling_device *cdev = to_cooling_device(dev);
709-
struct cooling_dev_stats *stats = cdev->stats;
710-
int ret;
711+
struct cooling_dev_stats *stats;
712+
int ret = 0;
713+
714+
mutex_lock(&cdev->lock);
715+
716+
stats = cdev->stats;
717+
if (!stats)
718+
goto unlock;
711719

712720
spin_lock(&stats->lock);
713721
ret = sprintf(buf, "%u\n", stats->total_trans);
714722
spin_unlock(&stats->lock);
715723

724+
unlock:
725+
mutex_unlock(&cdev->lock);
726+
716727
return ret;
717728
}
718729

@@ -721,11 +732,18 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
721732
char *buf)
722733
{
723734
struct thermal_cooling_device *cdev = to_cooling_device(dev);
724-
struct cooling_dev_stats *stats = cdev->stats;
735+
struct cooling_dev_stats *stats;
725736
ssize_t len = 0;
726737
int i;
727738

739+
mutex_lock(&cdev->lock);
740+
741+
stats = cdev->stats;
742+
if (!stats)
743+
goto unlock;
744+
728745
spin_lock(&stats->lock);
746+
729747
update_time_in_state(stats);
730748

731749
for (i = 0; i <= cdev->max_state; i++) {
@@ -734,6 +752,9 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
734752
}
735753
spin_unlock(&stats->lock);
736754

755+
unlock:
756+
mutex_unlock(&cdev->lock);
757+
737758
return len;
738759
}
739760

@@ -742,8 +763,16 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
742763
size_t count)
743764
{
744765
struct thermal_cooling_device *cdev = to_cooling_device(dev);
745-
struct cooling_dev_stats *stats = cdev->stats;
746-
int i, states = cdev->max_state + 1;
766+
struct cooling_dev_stats *stats;
767+
int i, states;
768+
769+
mutex_lock(&cdev->lock);
770+
771+
stats = cdev->stats;
772+
if (!stats)
773+
goto unlock;
774+
775+
states = cdev->max_state + 1;
747776

748777
spin_lock(&stats->lock);
749778

@@ -757,26 +786,39 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
757786

758787
spin_unlock(&stats->lock);
759788

789+
unlock:
790+
mutex_unlock(&cdev->lock);
791+
760792
return count;
761793
}
762794

763795
static ssize_t trans_table_show(struct device *dev,
764796
struct device_attribute *attr, char *buf)
765797
{
766798
struct thermal_cooling_device *cdev = to_cooling_device(dev);
767-
struct cooling_dev_stats *stats = cdev->stats;
799+
struct cooling_dev_stats *stats;
768800
ssize_t len = 0;
769801
int i, j;
770802

803+
mutex_lock(&cdev->lock);
804+
805+
stats = cdev->stats;
806+
if (!stats) {
807+
len = -ENODATA;
808+
goto unlock;
809+
}
810+
771811
len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
772812
len += snprintf(buf + len, PAGE_SIZE - len, " : ");
773813
for (i = 0; i <= cdev->max_state; i++) {
774814
if (len >= PAGE_SIZE)
775815
break;
776816
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
777817
}
778-
if (len >= PAGE_SIZE)
779-
return PAGE_SIZE;
818+
if (len >= PAGE_SIZE) {
819+
len = PAGE_SIZE;
820+
goto unlock;
821+
}
780822

781823
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
782824

@@ -799,8 +841,12 @@ static ssize_t trans_table_show(struct device *dev,
799841

800842
if (len >= PAGE_SIZE) {
801843
pr_warn_once("Thermal transition table exceeds PAGE_SIZE. Disabling\n");
802-
return -EFBIG;
844+
len = -EFBIG;
803845
}
846+
847+
unlock:
848+
mutex_unlock(&cdev->lock);
849+
804850
return len;
805851
}
806852

@@ -830,6 +876,8 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
830876
unsigned long states = cdev->max_state + 1;
831877
int var;
832878

879+
lockdep_assert_held(&cdev->lock);
880+
833881
var = sizeof(*stats);
834882
var += sizeof(*stats->time_in_state) * states;
835883
var += sizeof(*stats->trans_table) * states * states;
@@ -855,6 +903,8 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
855903

856904
static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev)
857905
{
906+
lockdep_assert_held(&cdev->lock);
907+
858908
kfree(cdev->stats);
859909
cdev->stats = NULL;
860910
}
@@ -879,6 +929,12 @@ void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev)
879929
cooling_device_stats_destroy(cdev);
880930
}
881931

932+
void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev)
933+
{
934+
cooling_device_stats_destroy(cdev);
935+
cooling_device_stats_setup(cdev);
936+
}
937+
882938
/* these helper will be used only at the time of bindig */
883939
ssize_t
884940
trip_point_show(struct device *dev, struct device_attribute *attr, char *buf)

include/linux/thermal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ devm_thermal_of_cooling_device_register(struct device *dev,
384384
struct device_node *np,
385385
char *type, void *devdata,
386386
const struct thermal_cooling_device_ops *ops);
387+
void thermal_cooling_device_update(struct thermal_cooling_device *);
387388
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
388389
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
389390
int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);

0 commit comments

Comments
 (0)