* [PATCH v1 0/2] thermal: core: Handle failed temperature checks more carefully
@ 2024-07-18 18:57 Rafael J. Wysocki
2024-07-18 19:00 ` [PATCH v1 1/2] thermal: trip: Split thermal_zone_device_set_mode() Rafael J. Wysocki
2024-07-18 19:01 ` [PATCH v1 2/2] thermal: core: Back off when polling thermal zones on errors Rafael J. Wysocki
0 siblings, 2 replies; 3+ messages in thread
From: Rafael J. Wysocki @ 2024-07-18 18:57 UTC (permalink / raw)
To: Linux PM; +Cc: LKML, Lukasz Luba, Daniel Lezcano, Neil Armstrong
Hi Everyone,
This series kind of augments
https://lore.kernel.org/linux-pm/4950004.31r3eYUQgx@rjwysocki.net/
so I'm considering adding it to 6.11.
The problem with handing temperature check errors in __thermal_zone_device_update()
after the above is that if someone has a dead thermal zone returning such errors
continuously lurking somewhere in their system, they will get a flood of
"temperature check failed" messages in the log which will be reported as a
regression. Rightfully, because these messages render the kernel log
practically unusable and the continuous and useless polling of such a thermal
zone may even prevent the system from entering deep idle states. Clearly,
something needs to be done about this.
One possible approach might be to simply disable the thermal zone in question
after the first error (that is not -EAGAIN) returned by its .get_temp()
callback, but that cannot be done because there are thermal zones in which
.get_temp() returns errors to start with, but they recover later, and they
need to be taken into account.
So the only other alternative that is not overly complicated is to add a
back-off mechanism to the polling, so the thermal zone has a chance to recover,
but the core will not wait for that forever. At one point it will just disable
the thermal zone and let user space re-enable it if that's regarded as a good
idea. This is done in the second patch and the first patch is preparatory.
Thanks!
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v1 1/2] thermal: trip: Split thermal_zone_device_set_mode()
2024-07-18 18:57 [PATCH v1 0/2] thermal: core: Handle failed temperature checks more carefully Rafael J. Wysocki
@ 2024-07-18 19:00 ` Rafael J. Wysocki
2024-07-18 19:01 ` [PATCH v1 2/2] thermal: core: Back off when polling thermal zones on errors Rafael J. Wysocki
1 sibling, 0 replies; 3+ messages in thread
From: Rafael J. Wysocki @ 2024-07-18 19:00 UTC (permalink / raw)
To: Linux PM; +Cc: LKML, Lukasz Luba, Daniel Lezcano, Neil Armstrong
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Pull a wrapper around thermal zone .change_mode() callback out of
thermal_zone_device_set_mode() because it will be used elsewhere
subsequently.
No intentional functional impact.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/thermal/thermal_core.c | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
Index: linux-pm/drivers/thermal/thermal_core.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_core.c
+++ linux-pm/drivers/thermal/thermal_core.c
@@ -272,6 +272,22 @@ static int __init thermal_register_gover
return ret;
}
+static int __thermal_zone_device_set_mode(struct thermal_zone_device *tz,
+ enum thermal_device_mode mode)
+{
+ if (tz->ops.change_mode) {
+ int ret;
+
+ ret = tz->ops.change_mode(tz, mode);
+ if (ret)
+ return ret;
+ }
+
+ tz->mode = mode;
+
+ return 0;
+}
+
/*
* Zone update section: main control loop applied to each zone while monitoring
* in polling mode. The monitoring is done using a workqueue.
@@ -540,7 +556,7 @@ monitor:
static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
enum thermal_device_mode mode)
{
- int ret = 0;
+ int ret;
mutex_lock(&tz->lock);
@@ -548,14 +564,15 @@ static int thermal_zone_device_set_mode(
if (mode == tz->mode) {
mutex_unlock(&tz->lock);
- return ret;
+ return 0;
}
- if (tz->ops.change_mode)
- ret = tz->ops.change_mode(tz, mode);
+ ret = __thermal_zone_device_set_mode(tz, mode);
+ if (ret) {
+ mutex_unlock(&tz->lock);
- if (!ret)
- tz->mode = mode;
+ return ret;
+ }
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
@@ -566,7 +583,7 @@ static int thermal_zone_device_set_mode(
else
thermal_notify_tz_disable(tz);
- return ret;
+ return 0;
}
int thermal_zone_device_enable(struct thermal_zone_device *tz)
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v1 2/2] thermal: core: Back off when polling thermal zones on errors
2024-07-18 18:57 [PATCH v1 0/2] thermal: core: Handle failed temperature checks more carefully Rafael J. Wysocki
2024-07-18 19:00 ` [PATCH v1 1/2] thermal: trip: Split thermal_zone_device_set_mode() Rafael J. Wysocki
@ 2024-07-18 19:01 ` Rafael J. Wysocki
1 sibling, 0 replies; 3+ messages in thread
From: Rafael J. Wysocki @ 2024-07-18 19:01 UTC (permalink / raw)
To: Linux PM; +Cc: LKML, Lukasz Luba, Daniel Lezcano, Neil Armstrong
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Commit a8a261774466 ("thermal: core: Call monitor_thermal_zone() if zone
temperature is invalid") introduced a polling mechanism by which the
thermal core attampts to get a valid temperature value for thermal zones
where the .get_temp() callback returns errors to start with (for
example, due to initialization ordering woes). However, this polling is
carried out periodically ad infinitum and every iteration of it causes
a message to be printed to the kernel log which means a lot of log noise
on systems where there are thermal zones that never get ready for some
reason. It is also not really useful to continuously poll thermal zones
that never respond.
To address this, modify the thermal core to increase the delay between
consecutive thermal zone temperature checks after every check that fails
until it reaches a certain maximum value. At that point, the thermal
zone in question will be disabled, but user space will be able to
reenable it if it believes that the failure is transient.
Also change the code to print messages regarding failed temperature
checks to the kernel log only twice, once when the thermal zone's
.get_temp() callback returns an error for the first time and once when
disabling the given thermal zone. In addition, a dev_crit() message
will be printed at that point if the given thermal zone contains a
critical trip point to notify the system operator about the situation.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/thermal/thermal_core.c | 58 ++++++++++++++++++++++++++++++++++++++---
drivers/thermal/thermal_core.h | 10 ++++---
2 files changed, 61 insertions(+), 7 deletions(-)
Index: linux-pm/drivers/thermal/thermal_core.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_core.c
+++ linux-pm/drivers/thermal/thermal_core.c
@@ -288,6 +288,28 @@ static int __thermal_zone_device_set_mod
return 0;
}
+static void thermal_zone_broken_disable(struct thermal_zone_device *tz)
+{
+ struct thermal_trip_desc *td;
+
+ dev_err(&tz->device, "Unable to get temperature, disabling!\n");
+ /*
+ * This function only runs for enabled thermal zones, so no need to
+ * check for the current mode.
+ */
+ __thermal_zone_device_set_mode(tz, THERMAL_DEVICE_DISABLED);
+ thermal_notify_tz_disable(tz);
+
+ for_each_trip_desc(tz, td) {
+ if (td->trip.type == THERMAL_TRIP_CRITICAL &&
+ td->trip.temperature > THERMAL_TEMP_INVALID) {
+ dev_crit(&tz->device,
+ "Disabled thermal zone with critical trip point\n");
+ return;
+ }
+ }
+}
+
/*
* Zone update section: main control loop applied to each zone while monitoring
* in polling mode. The monitoring is done using a workqueue.
@@ -308,6 +330,34 @@ static void thermal_zone_device_set_poll
cancel_delayed_work(&tz->poll_queue);
}
+static void thermal_zone_recheck(struct thermal_zone_device *tz, int error)
+{
+ if (error == -EAGAIN) {
+ thermal_zone_device_set_polling(tz, THERMAL_RECHECK_DELAY);
+ return;
+ }
+
+ /*
+ * Print the message once to reduce log noise. It will be followed by
+ * another one if the temperature cannot be determined after multiple
+ * attempts.
+ */
+ if (tz->recheck_delay_jiffies == THERMAL_RECHECK_DELAY)
+ dev_info(&tz->device, "Temperature check failed (%d)\n", error);
+
+ thermal_zone_device_set_polling(tz, tz->recheck_delay_jiffies);
+
+ tz->recheck_delay_jiffies += max(tz->recheck_delay_jiffies >> 1, 1ULL);
+ if (tz->recheck_delay_jiffies > THERMAL_MAX_RECHECK_DELAY) {
+ thermal_zone_broken_disable(tz);
+ /*
+ * Restore the original recheck delay value to allow the thermal
+ * zone to try to recover when it is reenabled by user space.
+ */
+ tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY;
+ }
+}
+
static void monitor_thermal_zone(struct thermal_zone_device *tz)
{
if (tz->mode != THERMAL_DEVICE_ENABLED)
@@ -507,10 +557,7 @@ void __thermal_zone_device_update(struct
ret = __thermal_zone_get_temp(tz, &temp);
if (ret) {
- if (ret != -EAGAIN)
- dev_info(&tz->device, "Temperature check failed (%d)\n", ret);
-
- thermal_zone_device_set_polling(tz, msecs_to_jiffies(THERMAL_RECHECK_DELAY_MS));
+ thermal_zone_recheck(tz, ret);
return;
} else if (temp <= THERMAL_TEMP_INVALID) {
/*
@@ -522,6 +569,8 @@ void __thermal_zone_device_update(struct
goto monitor;
}
+ tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY;
+
tz->last_temperature = tz->temperature;
tz->temperature = temp;
@@ -1462,6 +1511,7 @@ thermal_zone_device_register_with_trips(
thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay);
thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay);
+ tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY;
/* sys I/F */
/* Add nodes that are always present via .groups */
Index: linux-pm/drivers/thermal/thermal_core.h
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_core.h
+++ linux-pm/drivers/thermal/thermal_core.h
@@ -67,6 +67,8 @@ struct thermal_governor {
* @polling_delay_jiffies: number of jiffies to wait between polls when
* checking whether trip points have been crossed (0 for
* interrupt driven systems)
+ * @recheck_delay_jiffies: delay after a failed attempt to determine the zone
+ * temperature before trying again
* @temperature: current temperature. This is only for core code,
* drivers should use thermal_zone_get_temp() to get the
* current temperature
@@ -108,6 +110,7 @@ struct thermal_zone_device {
int num_trips;
unsigned long passive_delay_jiffies;
unsigned long polling_delay_jiffies;
+ unsigned long recheck_delay_jiffies;
int temperature;
int last_temperature;
int emul_temperature;
@@ -137,10 +140,11 @@ struct thermal_zone_device {
#define THERMAL_TEMP_INIT INT_MIN
/*
- * Default delay after a failing thermal zone temperature check before
- * attempting to check it again.
+ * Default and maximum delay after a failed thermal zone temperature check
+ * before attempting to check it again (in jiffies).
*/
-#define THERMAL_RECHECK_DELAY_MS 250
+#define THERMAL_RECHECK_DELAY msecs_to_jiffies(250)
+#define THERMAL_MAX_RECHECK_DELAY (120 * HZ)
/* Default Thermal Governor */
#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2024-07-18 19:01 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-07-18 18:57 [PATCH v1 0/2] thermal: core: Handle failed temperature checks more carefully Rafael J. Wysocki
2024-07-18 19:00 ` [PATCH v1 1/2] thermal: trip: Split thermal_zone_device_set_mode() Rafael J. Wysocki
2024-07-18 19:01 ` [PATCH v1 2/2] thermal: core: Back off when polling thermal zones on errors Rafael J. Wysocki
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®