Skip to content

Commit a7d7e08

Browse files
rafaeljwnvidia-bfigg
authored andcommitted
thermal: core: Introduce thermal_cooling_device_update()
BugLink: https://bugs.launchpad.net/bugs/2042697 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/53ec1f06f61c984100868926f282647e57ecfb2d.camel@intel.com/ Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Tested-by: Zhang Rui <rui.zhang@intel.com> Reviewed-by: Zhang Rui <rui.zhang@intel.com> (cherry picked from commit 790930f) Acked-by: Nathan Hartman <nhartman@nvidia.com> Acked-by: Jamie Nguyen <jamien@nvidia.com> Acked-by: Brad Figg <bfigg@nvidia.com> Acked-by: Ian May <ian.may@canonical.com> Acked-by: Jacob Martin <jacob.martin@canonical.com>
1 parent a716ecd commit a7d7e08

4 files changed

Lines changed: 150 additions & 10 deletions

File tree

drivers/thermal/thermal_core.c

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
618618
struct thermal_instance *pos;
619619
struct thermal_zone_device *pos1;
620620
struct thermal_cooling_device *pos2;
621+
bool upper_no_limit;
621622
int result;
622623

623624
if (trip >= tz->num_trips || trip < 0)
@@ -637,7 +638,13 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
637638

638639
/* lower default 0, upper default max_state */
639640
lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
640-
upper = upper == THERMAL_NO_LIMIT ? cdev->max_state : upper;
641+
642+
if (upper == THERMAL_NO_LIMIT) {
643+
upper = cdev->max_state;
644+
upper_no_limit = true;
645+
} else {
646+
upper_no_limit = false;
647+
}
641648

642649
if (lower > upper || upper > cdev->max_state)
643650
return -EINVAL;
@@ -649,6 +656,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
649656
dev->cdev = cdev;
650657
dev->trip = trip;
651658
dev->upper = upper;
659+
dev->upper_no_limit = upper_no_limit;
652660
dev->lower = lower;
653661
dev->target = THERMAL_NO_TARGET;
654662
dev->weight = weight;
@@ -1064,6 +1072,79 @@ static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev)
10641072
return false;
10651073
}
10661074

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

drivers/thermal/thermal_core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ struct thermal_instance {
9797
struct list_head tz_node; /* node in tz->thermal_instances */
9898
struct list_head cdev_node; /* node in cdev->thermal_instances */
9999
unsigned int weight; /* The weight of the cooling device */
100+
bool upper_no_limit;
100101
};
101102

102103
#define to_thermal_zone(_dev) \
@@ -121,6 +122,7 @@ int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
121122
void thermal_zone_destroy_device_groups(struct thermal_zone_device *);
122123
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *);
123124
void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev);
125+
void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev);
124126
/* used only at binding time */
125127
ssize_t trip_point_show(struct device *, struct device_attribute *, char *);
126128
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
@@ -732,6 +732,8 @@ void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
732732
{
733733
struct cooling_dev_stats *stats = cdev->stats;
734734

735+
lockdep_assert_held(&cdev->lock);
736+
735737
if (!stats)
736738
return;
737739

@@ -753,13 +755,22 @@ static ssize_t total_trans_show(struct device *dev,
753755
struct device_attribute *attr, char *buf)
754756
{
755757
struct thermal_cooling_device *cdev = to_cooling_device(dev);
756-
struct cooling_dev_stats *stats = cdev->stats;
757-
int ret;
758+
struct cooling_dev_stats *stats;
759+
int ret = 0;
760+
761+
mutex_lock(&cdev->lock);
762+
763+
stats = cdev->stats;
764+
if (!stats)
765+
goto unlock;
758766

759767
spin_lock(&stats->lock);
760768
ret = sprintf(buf, "%u\n", stats->total_trans);
761769
spin_unlock(&stats->lock);
762770

771+
unlock:
772+
mutex_unlock(&cdev->lock);
773+
763774
return ret;
764775
}
765776

@@ -768,11 +779,18 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
768779
char *buf)
769780
{
770781
struct thermal_cooling_device *cdev = to_cooling_device(dev);
771-
struct cooling_dev_stats *stats = cdev->stats;
782+
struct cooling_dev_stats *stats;
772783
ssize_t len = 0;
773784
int i;
774785

786+
mutex_lock(&cdev->lock);
787+
788+
stats = cdev->stats;
789+
if (!stats)
790+
goto unlock;
791+
775792
spin_lock(&stats->lock);
793+
776794
update_time_in_state(stats);
777795

778796
for (i = 0; i <= cdev->max_state; i++) {
@@ -781,6 +799,9 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
781799
}
782800
spin_unlock(&stats->lock);
783801

802+
unlock:
803+
mutex_unlock(&cdev->lock);
804+
784805
return len;
785806
}
786807

@@ -789,8 +810,16 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
789810
size_t count)
790811
{
791812
struct thermal_cooling_device *cdev = to_cooling_device(dev);
792-
struct cooling_dev_stats *stats = cdev->stats;
793-
int i, states = cdev->max_state + 1;
813+
struct cooling_dev_stats *stats;
814+
int i, states;
815+
816+
mutex_lock(&cdev->lock);
817+
818+
stats = cdev->stats;
819+
if (!stats)
820+
goto unlock;
821+
822+
states = cdev->max_state + 1;
794823

795824
spin_lock(&stats->lock);
796825

@@ -804,26 +833,39 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
804833

805834
spin_unlock(&stats->lock);
806835

836+
unlock:
837+
mutex_unlock(&cdev->lock);
838+
807839
return count;
808840
}
809841

810842
static ssize_t trans_table_show(struct device *dev,
811843
struct device_attribute *attr, char *buf)
812844
{
813845
struct thermal_cooling_device *cdev = to_cooling_device(dev);
814-
struct cooling_dev_stats *stats = cdev->stats;
846+
struct cooling_dev_stats *stats;
815847
ssize_t len = 0;
816848
int i, j;
817849

850+
mutex_lock(&cdev->lock);
851+
852+
stats = cdev->stats;
853+
if (!stats) {
854+
len = -ENODATA;
855+
goto unlock;
856+
}
857+
818858
len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
819859
len += snprintf(buf + len, PAGE_SIZE - len, " : ");
820860
for (i = 0; i <= cdev->max_state; i++) {
821861
if (len >= PAGE_SIZE)
822862
break;
823863
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
824864
}
825-
if (len >= PAGE_SIZE)
826-
return PAGE_SIZE;
865+
if (len >= PAGE_SIZE) {
866+
len = PAGE_SIZE;
867+
goto unlock;
868+
}
827869

828870
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
829871

@@ -846,8 +888,12 @@ static ssize_t trans_table_show(struct device *dev,
846888

847889
if (len >= PAGE_SIZE) {
848890
pr_warn_once("Thermal transition table exceeds PAGE_SIZE. Disabling\n");
849-
return -EFBIG;
891+
len = -EFBIG;
850892
}
893+
894+
unlock:
895+
mutex_unlock(&cdev->lock);
896+
851897
return len;
852898
}
853899

@@ -877,6 +923,8 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
877923
unsigned long states = cdev->max_state + 1;
878924
int var;
879925

926+
lockdep_assert_held(&cdev->lock);
927+
880928
var = sizeof(*stats);
881929
var += sizeof(*stats->time_in_state) * states;
882930
var += sizeof(*stats->trans_table) * states * states;
@@ -902,6 +950,8 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
902950

903951
static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev)
904952
{
953+
lockdep_assert_held(&cdev->lock);
954+
905955
kfree(cdev->stats);
906956
cdev->stats = NULL;
907957
}
@@ -926,6 +976,12 @@ void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev)
926976
cooling_device_stats_destroy(cdev);
927977
}
928978

979+
void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev)
980+
{
981+
cooling_device_stats_destroy(cdev);
982+
cooling_device_stats_setup(cdev);
983+
}
984+
929985
/* these helper will be used only at the time of bindig */
930986
ssize_t
931987
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
@@ -365,6 +365,7 @@ devm_thermal_of_cooling_device_register(struct device *dev,
365365
struct device_node *np,
366366
char *type, void *devdata,
367367
const struct thermal_cooling_device_ops *ops);
368+
void thermal_cooling_device_update(struct thermal_cooling_device *);
368369
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
369370
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
370371
int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);

0 commit comments

Comments
 (0)