]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
thermal: core: Suspend thermal zones later and resume them earlier
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 7 Apr 2026 14:09:21 +0000 (16:09 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 8 Apr 2026 10:30:31 +0000 (12:30 +0200)
To avoid some undesirable interactions between thermal zone suspend
and resume with user space that is running when those operations are
carried out, move them closer to the suspend and resume of devices,
respectively, by updating dpm_prepare() to carry out thermal zone
suspend and dpm_complete() to start thermal zone resume (that will
continue asynchronously).

This also makes the code easier to follow by removing one, arguably
redundant, level of indirection represented by the thermal PM notifier.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Link: https://patch.msgid.link/2036875.PYKUYFuaPT@rafael.j.wysocki
drivers/base/power/main.c
drivers/thermal/thermal_core.c
include/linux/thermal.h

index 189de5250f256bdc1cdc33006b2c386d0794485f..e1b550664babbf245c7280385e513012bd99f6cd 100644 (file)
@@ -33,6 +33,7 @@
 #include <trace/events/power.h>
 #include <linux/cpufreq.h>
 #include <linux/devfreq.h>
+#include <linux/thermal.h>
 #include <linux/timer.h>
 #include <linux/nmi.h>
 
@@ -1282,6 +1283,8 @@ void dpm_complete(pm_message_t state)
        list_splice(&list, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
 
+       /* Start resuming thermal control */
+       thermal_pm_complete();
        /* Allow device probing and trigger re-probing of deferred devices */
        device_unblock_probing();
        trace_suspend_resume(TPS("dpm_complete"), state.event, false);
@@ -2225,6 +2228,8 @@ int dpm_prepare(pm_message_t state)
         * instead. The normal behavior will be restored in dpm_complete().
         */
        device_block_probing();
+       /* Suspend thermal control. */
+       thermal_pm_prepare();
 
        mutex_lock(&dpm_list_mtx);
        while (!list_empty(&dpm_list) && !error) {
index f0f3a628c00c9f89173f81497b6349692b983562..2f4e2dc46b8f718466c88871e0fa815aea4dad75 100644 (file)
@@ -1823,7 +1823,7 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz)
        cancel_delayed_work(&tz->poll_queue);
 }
 
-static void thermal_pm_notify_prepare(void)
+static void __thermal_pm_prepare(void)
 {
        struct thermal_zone_device *tz;
 
@@ -1835,6 +1835,19 @@ static void thermal_pm_notify_prepare(void)
                thermal_zone_pm_prepare(tz);
 }
 
+void thermal_pm_prepare(void)
+{
+       if (thermal_class_unavailable)
+               return;
+
+       __thermal_pm_prepare();
+       /*
+        * Allow any leftover thermal work items already on the worqueue to
+        * complete so they don't get in the way later.
+        */
+       flush_workqueue(thermal_wq);
+}
+
 static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
 {
        guard(thermal_zone)(tz);
@@ -1851,10 +1864,13 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
        mod_delayed_work(thermal_wq, &tz->poll_queue, 0);
 }
 
-static void thermal_pm_notify_complete(void)
+void thermal_pm_complete(void)
 {
        struct thermal_zone_device *tz;
 
+       if (thermal_class_unavailable)
+               return;
+
        guard(mutex)(&thermal_list_lock);
 
        thermal_pm_suspended = false;
@@ -1863,41 +1879,6 @@ static void thermal_pm_notify_complete(void)
                thermal_zone_pm_complete(tz);
 }
 
-static int thermal_pm_notify(struct notifier_block *nb,
-                            unsigned long mode, void *_unused)
-{
-       switch (mode) {
-       case PM_HIBERNATION_PREPARE:
-       case PM_RESTORE_PREPARE:
-       case PM_SUSPEND_PREPARE:
-               thermal_pm_notify_prepare();
-               /*
-                * Allow any leftover thermal work items already on the
-                * worqueue to complete so they don't get in the way later.
-                */
-               flush_workqueue(thermal_wq);
-               break;
-       case PM_POST_HIBERNATION:
-       case PM_POST_RESTORE:
-       case PM_POST_SUSPEND:
-               thermal_pm_notify_complete();
-               break;
-       default:
-               break;
-       }
-       return 0;
-}
-
-static struct notifier_block thermal_pm_nb = {
-       .notifier_call = thermal_pm_notify,
-       /*
-        * Run at the lowest priority to avoid interference between the thermal
-        * zone resume work items spawned by thermal_pm_notify() and the other
-        * PM notifiers.
-        */
-       .priority = INT_MIN,
-};
-
 static int __init thermal_init(void)
 {
        int result;
@@ -1924,11 +1905,6 @@ static int __init thermal_init(void)
 
        thermal_class_unavailable = false;
 
-       result = register_pm_notifier(&thermal_pm_nb);
-       if (result)
-               pr_warn("Thermal: Can not register suspend notifier, return %d\n",
-                       result);
-
        return 0;
 
 unregister_governors:
index 0b5ed682108073eba2585cd076191ef343aab3be..0ddc77aeeca26535831bd83d2323e67d4e4dcd7a 100644 (file)
@@ -273,6 +273,9 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz,
 int thermal_zone_device_enable(struct thermal_zone_device *tz);
 int thermal_zone_device_disable(struct thermal_zone_device *tz);
 void thermal_zone_device_critical(struct thermal_zone_device *tz);
+
+void thermal_pm_prepare(void);
+void thermal_pm_complete(void);
 #else
 static inline struct thermal_zone_device *thermal_zone_device_register_with_trips(
                                        const char *type,
@@ -350,6 +353,9 @@ static inline int thermal_zone_device_enable(struct thermal_zone_device *tz)
 
 static inline int thermal_zone_device_disable(struct thermal_zone_device *tz)
 { return -ENODEV; }
+
+static inline void thermal_pm_prepare(void) {}
+static inline void thermal_pm_complete(void) {}
 #endif /* CONFIG_THERMAL */
 
 #endif /* __THERMAL_H__ */