summaryrefslogtreecommitdiff
path: root/drivers/thermal
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2024-06-25 17:27:38 +0300
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2024-06-25 17:27:38 +0300
commit06d55c4278dcbd8f69e70fed312db738b6b03410 (patch)
tree4d917fe7908bbd1cc58d7bcaab418469306446c7 /drivers/thermal
parent3995904d54384093f79f4a5a12318261c55e2ec1 (diff)
parent72196c20c38b50c4293696377145e6c4ad9a7c67 (diff)
downloadlinux-06d55c4278dcbd8f69e70fed312db738b6b03410.tar.xz
Merge branch 'thermal-core'
Merge thermal core changes for v6.11: - Fix and clean up several minor shortcomings in thermal debug (Rafael Wysocki). - Rename __thermal_zone_set_trips() to thermal_zone_set_trips() and make it use trip thresholds (Rafael Wysocki). - Use READ_ONCE() for lockless access to trip temperature and hysteresis (Rafael Wysocki). - Drop unnecessary cooling device target state checks from the Bang-Bang thermal governor (Rafael Wysocki). - Avoid invoking thermal governor .trip_crossed() callback for critical and hot trip points (Rafael Wysocki). * thermal-core: thermal: core: Avoid calling .trip_crossed() for critical and hot trips thermal: gov_bang_bang: Drop unnecessary cooling device target state checks thermal: trip: Use READ_ONCE() for lockless access to trip properties thermal: trip: Make thermal_zone_set_trips() use trip thresholds thermal: trip: Rename __thermal_zone_set_trips() to thermal_zone_set_trips() thermal: trip: Use common set of trip type names thermal/debugfs: Move some statements from under thermal_dbg->lock thermal/debugfs: Compute maximum temperature for mitigation episode as a whole thermal/debugfs: Adjust check for trips without statistics in tze_seq_show() thermal/debugfs: Fix up units in "mitigations" files thermal/debugfs: Print mitigation timestamp value in milliseconds thermal/debugfs: Do not extend mitigation episodes beyond system resume thermal/debugfs: Use helper to update trip point overstepping duration
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/gov_bang_bang.c14
-rw-r--r--drivers/thermal/thermal_core.c8
-rw-r--r--drivers/thermal/thermal_core.h4
-rw-r--r--drivers/thermal/thermal_debugfs.c110
-rw-r--r--drivers/thermal/thermal_debugfs.h2
-rw-r--r--drivers/thermal/thermal_sysfs.c19
-rw-r--r--drivers/thermal/thermal_trip.c35
7 files changed, 112 insertions, 80 deletions
diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c
index acb52c9ee10f..4a2e869b9538 100644
--- a/drivers/thermal/gov_bang_bang.c
+++ b/drivers/thermal/gov_bang_bang.c
@@ -57,24 +57,16 @@ static void bang_bang_control(struct thermal_zone_device *tz,
if (instance->trip != trip)
continue;
- if (instance->target == THERMAL_NO_TARGET)
- instance->target = 0;
-
- if (instance->target != 0 && instance->target != 1) {
+ if (instance->target != 0 && instance->target != 1 &&
+ instance->target != THERMAL_NO_TARGET)
pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n",
instance->target, instance->name);
- instance->target = 1;
- }
-
/*
* Enable the fan when the trip is crossed on the way up and
* disable it when the trip is crossed on the way down.
*/
- if (instance->target == 0 && crossed_up)
- instance->target = 1;
- else if (instance->target == 1 && !crossed_up)
- instance->target = 0;
+ instance->target = crossed_up;
dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target);
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 1b0ab2790860..2aa04c46a425 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -463,6 +463,9 @@ static void thermal_governor_trip_crossed(struct thermal_governor *governor,
const struct thermal_trip *trip,
bool crossed_up)
{
+ if (trip->type == THERMAL_TRIP_HOT || trip->type == THERMAL_TRIP_CRITICAL)
+ return;
+
if (governor->trip_crossed)
governor->trip_crossed(tz, trip, crossed_up);
}
@@ -513,13 +516,13 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
if (tz->temperature == THERMAL_TEMP_INVALID)
return;
- __thermal_zone_set_trips(tz);
-
tz->notify_event = event;
for_each_trip_desc(tz, td)
handle_thermal_trip(tz, td, &way_up_list, &way_down_list);
+ thermal_zone_set_trips(tz);
+
list_sort(&way_up_list, &way_up_list, thermal_trip_notify_cmp);
list_for_each_entry(td, &way_up_list, notify_list_node)
thermal_trip_crossed(tz, &td->trip, governor, true);
@@ -1649,6 +1652,7 @@ static void thermal_zone_device_resume(struct work_struct *work)
tz->suspended = false;
+ thermal_debug_tz_resume(tz);
thermal_zone_device_init(tz);
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 66f67e54e0c8..16b3f06832d4 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -244,7 +244,9 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz,
#define trip_to_trip_desc(__trip) \
container_of(__trip, struct thermal_trip_desc, trip)
-void __thermal_zone_set_trips(struct thermal_zone_device *tz);
+const char *thermal_trip_type_name(enum thermal_trip_type trip_type);
+
+void thermal_zone_set_trips(struct thermal_zone_device *tz);
int thermal_zone_trip_id(const struct thermal_zone_device *tz,
const struct thermal_trip *trip);
void thermal_zone_trip_updated(struct thermal_zone_device *tz,
diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c
index 942447229157..7dd67bf48571 100644
--- a/drivers/thermal/thermal_debugfs.c
+++ b/drivers/thermal/thermal_debugfs.c
@@ -94,7 +94,6 @@ struct cdev_record {
* @trip_temp: trip temperature at mitigation start
* @trip_hyst: trip hysteresis at mitigation start
* @count: the number of times the zone temperature was above the trip point
- * @max: maximum recorded temperature above the trip point
* @min: minimum recorded temperature above the trip point
* @avg: average temperature above the trip point
*/
@@ -104,7 +103,6 @@ struct trip_stats {
int trip_temp;
int trip_hyst;
int count;
- int max;
int min;
int avg;
};
@@ -122,12 +120,14 @@ struct trip_stats {
* @timestamp: first trip point crossed the way up
* @duration: total duration of the mitigation episode
* @node: a list element to be added to the list of tz events
+ * @max_temp: maximum zone temperature during this episode
* @trip_stats: per trip point statistics, flexible array
*/
struct tz_episode {
ktime_t timestamp;
ktime_t duration;
struct list_head node;
+ int max_temp;
struct trip_stats trip_stats[];
};
@@ -561,10 +561,11 @@ static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_dev
INIT_LIST_HEAD(&tze->node);
tze->timestamp = now;
tze->duration = KTIME_MIN;
+ tze->max_temp = INT_MIN;
for (i = 0; i < tz->num_trips; i++) {
+ tze->trip_stats[i].trip_temp = THERMAL_TEMP_INVALID;
tze->trip_stats[i].min = INT_MAX;
- tze->trip_stats[i].max = INT_MIN;
}
return tze;
@@ -573,20 +574,20 @@ static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_dev
void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
const struct thermal_trip *trip)
{
- struct tz_episode *tze;
- struct tz_debugfs *tz_dbg;
struct thermal_debugfs *thermal_dbg = tz->debugfs;
int trip_id = thermal_zone_trip_id(tz, trip);
ktime_t now = ktime_get();
struct trip_stats *trip_stats;
+ struct tz_debugfs *tz_dbg;
+ struct tz_episode *tze;
if (!thermal_dbg)
return;
- mutex_lock(&thermal_dbg->lock);
-
tz_dbg = &thermal_dbg->tz_dbg;
+ mutex_lock(&thermal_dbg->lock);
+
/*
* The mitigation is starting. A mitigation can contain
* several episodes where each of them is related to a
@@ -653,23 +654,33 @@ unlock:
mutex_unlock(&thermal_dbg->lock);
}
+static void tz_episode_close_trip(struct tz_episode *tze, int trip_id, ktime_t now)
+{
+ struct trip_stats *trip_stats = &tze->trip_stats[trip_id];
+ ktime_t delta = ktime_sub(now, trip_stats->timestamp);
+
+ trip_stats->duration = ktime_add(delta, trip_stats->duration);
+ /* Mark the end of mitigation for this trip point. */
+ trip_stats->timestamp = KTIME_MAX;
+}
+
void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
const struct thermal_trip *trip)
{
struct thermal_debugfs *thermal_dbg = tz->debugfs;
+ int trip_id = thermal_zone_trip_id(tz, trip);
+ ktime_t now = ktime_get();
struct tz_episode *tze;
struct tz_debugfs *tz_dbg;
- ktime_t delta, now = ktime_get();
- int trip_id = thermal_zone_trip_id(tz, trip);
int i;
if (!thermal_dbg)
return;
- mutex_lock(&thermal_dbg->lock);
-
tz_dbg = &thermal_dbg->tz_dbg;
+ mutex_lock(&thermal_dbg->lock);
+
/*
* The temperature crosses the way down but there was not
* mitigation detected before. That may happen when the
@@ -695,13 +706,7 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
- delta = ktime_sub(now, tze->trip_stats[trip_id].timestamp);
-
- tze->trip_stats[trip_id].duration =
- ktime_add(delta, tze->trip_stats[trip_id].duration);
-
- /* Mark the end of mitigation for this trip point. */
- tze->trip_stats[trip_id].timestamp = KTIME_MAX;
+ tz_episode_close_trip(tze, trip_id, now);
/*
* This event closes the mitigation as we are crossing the
@@ -724,20 +729,22 @@ void thermal_debug_update_trip_stats(struct thermal_zone_device *tz)
if (!thermal_dbg)
return;
- mutex_lock(&thermal_dbg->lock);
-
tz_dbg = &thermal_dbg->tz_dbg;
+ mutex_lock(&thermal_dbg->lock);
+
if (!tz_dbg->nr_trips)
goto out;
tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
+ if (tz->temperature > tze->max_temp)
+ tze->max_temp = tz->temperature;
+
for (i = 0; i < tz_dbg->nr_trips; i++) {
int trip_id = tz_dbg->trips_crossed[i];
struct trip_stats *trip_stats = &tze->trip_stats[trip_id];
- trip_stats->max = max(trip_stats->max, tz->temperature);
trip_stats->min = min(trip_stats->min, tz->temperature);
trip_stats->avg += (tz->temperature - trip_stats->avg) /
++trip_stats->count;
@@ -777,7 +784,6 @@ static int tze_seq_show(struct seq_file *s, void *v)
struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz;
struct thermal_trip_desc *td;
struct tz_episode *tze;
- const char *type;
u64 duration_ms;
int trip_id;
char c;
@@ -793,10 +799,10 @@ static int tze_seq_show(struct seq_file *s, void *v)
c = '=';
}
- seq_printf(s, ",-Mitigation at %lluus, duration%c%llums\n",
- ktime_to_us(tze->timestamp), c, duration_ms);
+ seq_printf(s, ",-Mitigation at %llums, duration%c%llums, max. temp=%dm°C\n",
+ ktime_to_ms(tze->timestamp), c, duration_ms, tze->max_temp);
- seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n");
+ seq_printf(s, "| trip | type | temp(m°C) | hyst(m°C) | duration(ms) | avg(m°C) | min(m°C) |\n");
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
@@ -814,16 +820,9 @@ static int tze_seq_show(struct seq_file *s, void *v)
trip_stats = &tze->trip_stats[trip_id];
/* Skip trips without any stats. */
- if (trip_stats->min > trip_stats->max)
+ if (trip_stats->trip_temp == THERMAL_TEMP_INVALID)
continue;
- if (trip->type == THERMAL_TRIP_PASSIVE)
- type = "passive";
- else if (trip->type == THERMAL_TRIP_ACTIVE)
- type = "active";
- else
- type = "hot";
-
if (trip_stats->timestamp != KTIME_MAX) {
/* Mitigation in progress. */
ktime_t delta = ktime_sub(ktime_get(),
@@ -837,15 +836,14 @@ static int tze_seq_show(struct seq_file *s, void *v)
c = ' ';
}
- seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d | %*d |\n",
+ seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d |\n",
4 , trip_id,
- 8, type,
+ 8, thermal_trip_type_name(trip->type),
9, trip_stats->trip_temp,
9, trip_stats->trip_hyst,
- c, 10, duration_ms,
+ c, 11, duration_ms,
9, trip_stats->avg,
- 9, trip_stats->min,
- 9, trip_stats->max);
+ 9, trip_stats->min);
}
return 0;
@@ -922,3 +920,39 @@ void thermal_debug_tz_remove(struct thermal_zone_device *tz)
thermal_debugfs_remove_id(thermal_dbg);
kfree(trips_crossed);
}
+
+void thermal_debug_tz_resume(struct thermal_zone_device *tz)
+{
+ struct thermal_debugfs *thermal_dbg = tz->debugfs;
+ ktime_t now = ktime_get();
+ struct tz_debugfs *tz_dbg;
+ struct tz_episode *tze;
+ int i;
+
+ if (!thermal_dbg)
+ return;
+
+ mutex_lock(&thermal_dbg->lock);
+
+ tz_dbg = &thermal_dbg->tz_dbg;
+
+ if (!tz_dbg->nr_trips)
+ goto out;
+
+ /*
+ * A mitigation episode was in progress before the preceding system
+ * suspend transition, so close it because the zone handling is starting
+ * over from scratch.
+ */
+ tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
+
+ for (i = 0; i < tz_dbg->nr_trips; i++)
+ tz_episode_close_trip(tze, tz_dbg->trips_crossed[i], now);
+
+ tze->duration = ktime_sub(now, tze->timestamp);
+
+ tz_dbg->nr_trips = 0;
+
+out:
+ mutex_unlock(&thermal_dbg->lock);
+}
diff --git a/drivers/thermal/thermal_debugfs.h b/drivers/thermal/thermal_debugfs.h
index 74ee65ee82ff..1c957ce2ec8f 100644
--- a/drivers/thermal/thermal_debugfs.h
+++ b/drivers/thermal/thermal_debugfs.h
@@ -7,6 +7,7 @@ void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev);
void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, int state);
void thermal_debug_tz_add(struct thermal_zone_device *tz);
void thermal_debug_tz_remove(struct thermal_zone_device *tz);
+void thermal_debug_tz_resume(struct thermal_zone_device *tz);
void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
const struct thermal_trip *trip);
void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
@@ -20,6 +21,7 @@ static inline void thermal_debug_cdev_state_update(const struct thermal_cooling_
int state) {}
static inline void thermal_debug_tz_add(struct thermal_zone_device *tz) {}
static inline void thermal_debug_tz_remove(struct thermal_zone_device *tz) {}
+static inline void thermal_debug_tz_resume(struct thermal_zone_device *tz) {}
static inline void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
const struct thermal_trip *trip) {};
static inline void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c
index 88211ccdfbd6..434b577950be 100644
--- a/drivers/thermal/thermal_sysfs.c
+++ b/drivers/thermal/thermal_sysfs.c
@@ -88,18 +88,7 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1)
return -EINVAL;
- switch (tz->trips[trip_id].trip.type) {
- case THERMAL_TRIP_CRITICAL:
- return sprintf(buf, "critical\n");
- case THERMAL_TRIP_HOT:
- return sprintf(buf, "hot\n");
- case THERMAL_TRIP_PASSIVE:
- return sprintf(buf, "passive\n");
- case THERMAL_TRIP_ACTIVE:
- return sprintf(buf, "active\n");
- default:
- return sprintf(buf, "unknown\n");
- }
+ return sprintf(buf, "%s\n", thermal_trip_type_name(tz->trips[trip_id].trip.type));
}
static ssize_t
@@ -150,7 +139,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
return -EINVAL;
- return sprintf(buf, "%d\n", tz->trips[trip_id].trip.temperature);
+ return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.temperature));
}
static ssize_t
@@ -174,7 +163,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
trip = &tz->trips[trip_id].trip;
if (hyst != trip->hysteresis) {
- trip->hysteresis = hyst;
+ WRITE_ONCE(trip->hysteresis, hyst);
thermal_zone_trip_updated(tz, trip);
}
@@ -194,7 +183,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
return -EINVAL;
- return sprintf(buf, "%d\n", tz->trips[trip_id].trip.hysteresis);
+ return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.hysteresis));
}
static ssize_t
diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c
index 49e63db68517..2a876d3b93aa 100644
--- a/drivers/thermal/thermal_trip.c
+++ b/drivers/thermal/thermal_trip.c
@@ -9,6 +9,21 @@
*/
#include "thermal_core.h"
+static const char *trip_type_names[] = {
+ [THERMAL_TRIP_ACTIVE] = "active",
+ [THERMAL_TRIP_PASSIVE] = "passive",
+ [THERMAL_TRIP_HOT] = "hot",
+ [THERMAL_TRIP_CRITICAL] = "critical",
+};
+
+const char *thermal_trip_type_name(enum thermal_trip_type trip_type)
+{
+ if (trip_type < THERMAL_TRIP_ACTIVE || trip_type > THERMAL_TRIP_CRITICAL)
+ return "unknown";
+
+ return trip_type_names[trip_type];
+}
+
int for_each_thermal_trip(struct thermal_zone_device *tz,
int (*cb)(struct thermal_trip *, void *),
void *data)
@@ -47,7 +62,7 @@ int thermal_zone_get_num_trips(struct thermal_zone_device *tz)
EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
/**
- * __thermal_zone_set_trips - Computes the next trip points for the driver
+ * thermal_zone_set_trips - Computes the next trip points for the driver
* @tz: a pointer to a thermal zone device structure
*
* The function computes the next temperature boundaries by browsing
@@ -61,7 +76,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
*
* It does not return a value
*/
-void __thermal_zone_set_trips(struct thermal_zone_device *tz)
+void thermal_zone_set_trips(struct thermal_zone_device *tz)
{
const struct thermal_trip_desc *td;
int low = -INT_MAX, high = INT_MAX;
@@ -73,17 +88,11 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz)
return;
for_each_trip_desc(tz, td) {
- const struct thermal_trip *trip = &td->trip;
- int trip_low;
-
- trip_low = trip->temperature - trip->hysteresis;
-
- if (trip_low < tz->temperature && trip_low > low)
- low = trip_low;
+ if (td->threshold < tz->temperature && td->threshold > low)
+ low = td->threshold;
- if (trip->temperature > tz->temperature &&
- trip->temperature < high)
- high = trip->temperature;
+ if (td->threshold > tz->temperature && td->threshold < high)
+ high = td->threshold;
}
/* No need to change trip points */
@@ -152,7 +161,7 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
if (trip->temperature == temp)
return;
- trip->temperature = temp;
+ WRITE_ONCE(trip->temperature, temp);
thermal_notify_tz_trip_change(tz, trip);
if (temp == THERMAL_TEMP_INVALID) {