From 445936f9e258eca624c8239056bd8cd6e853b3fd Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 23 Sep 2024 11:59:57 +0200 Subject: [PATCH 01/45] thermal: core: Add user thresholds support The user thresholds mechanism is a way to have the userspace to tell the thermal framework to send a notification when a temperature limit is crossed. There is no id, no hysteresis, just the temperature and the direction of the limit crossing. That means we can be notified when a threshold is crossed the way up only, or the way down only or both ways. That allows to create hysteresis values if it is needed. A threshold can be added, deleted or flushed. The latter means all thresholds belonging to a thermal zone will be deleted. When a threshold is added: - if the same threshold (temperature and direction) exists, an error is returned - if a threshold is specified with the same temperature but a different direction, the specified direction is added - if there is no threshold with the same temperature then it is created When a threshold is deleted: - if the same threshold (temperature and direction) exists, it is deleted - if a threshold is specified with the same temperature but a different direction, the specified direction is removed - if there is no threshold with the same temperature, then an error is returned When the threshold are flushed: - All thresholds related to a thermal zone are deleted When a threshold is crossed: - the userspace does not need to know which threshold(s) have been crossed, it will be notified with the current temperature and the previous temperature - if multiple thresholds have been crossed between two updates only one notification will be send to the userspace, it is pointless to send a notification per thresholds crossed as the userspace can handle that easily when it has the temperature delta information Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20240923100005.2532430-2-daniel.lezcano@linaro.org [ rjw: Subject edit, use BIT(0) and BIT(1) in symbol definitions ] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/Makefile | 1 + drivers/thermal/thermal_core.h | 2 + drivers/thermal/thermal_thresholds.c | 229 +++++++++++++++++++++++++++ drivers/thermal/thermal_thresholds.h | 19 +++ include/linux/thermal.h | 3 + include/uapi/linux/thermal.h | 2 + 6 files changed, 256 insertions(+) create mode 100644 drivers/thermal/thermal_thresholds.c create mode 100644 drivers/thermal/thermal_thresholds.h diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 41c4d56beb40d..1e1559bb971e5 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -6,6 +6,7 @@ CFLAGS_thermal_core.o := -I$(src) obj-$(CONFIG_THERMAL) += thermal_sys.o thermal_sys-y += thermal_core.o thermal_sysfs.o thermal_sys-y += thermal_trip.o thermal_helpers.o +thermal_sys-y += thermal_thresholds.o # netlink interface to manage the thermal framework thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index a64d39b1c86b2..1ea91d59498b2 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -13,6 +13,7 @@ #include #include "thermal_netlink.h" +#include "thermal_thresholds.h" #include "thermal_debugfs.h" struct thermal_attr { @@ -139,6 +140,7 @@ struct thermal_zone_device { #ifdef CONFIG_THERMAL_DEBUGFS struct thermal_debugfs *debugfs; #endif + struct list_head user_thresholds; struct thermal_trip_desc trips[] __counted_by(num_trips); }; diff --git a/drivers/thermal/thermal_thresholds.c b/drivers/thermal/thermal_thresholds.c new file mode 100644 index 0000000000000..f33b6d5474d8c --- /dev/null +++ b/drivers/thermal/thermal_thresholds.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2024 Linaro Limited + * + * Author: Daniel Lezcano + * + * Thermal thresholds + */ +#include +#include +#include + +#include "thermal_core.h" +#include "thermal_thresholds.h" + +int thermal_thresholds_init(struct thermal_zone_device *tz) +{ + INIT_LIST_HEAD(&tz->user_thresholds); + + return 0; +} + +void thermal_thresholds_flush(struct thermal_zone_device *tz) +{ + struct list_head *thresholds = &tz->user_thresholds; + struct user_threshold *entry, *tmp; + + lockdep_assert_held(&tz->lock); + + list_for_each_entry_safe(entry, tmp, thresholds, list_node) { + list_del(&entry->list_node); + kfree(entry); + } + + __thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS); +} + +void thermal_thresholds_exit(struct thermal_zone_device *tz) +{ + thermal_thresholds_flush(tz); +} + +static int __thermal_thresholds_cmp(void *data, + const struct list_head *l1, + const struct list_head *l2) +{ + struct user_threshold *t1 = container_of(l1, struct user_threshold, list_node); + struct user_threshold *t2 = container_of(l2, struct user_threshold, list_node); + + return t1->temperature - t2->temperature; +} + +static struct user_threshold *__thermal_thresholds_find(const struct list_head *thresholds, + int temperature) +{ + struct user_threshold *t; + + list_for_each_entry(t, thresholds, list_node) + if (t->temperature == temperature) + return t; + + return NULL; +} + +static bool __thermal_threshold_is_crossed(struct user_threshold *threshold, int temperature, + int last_temperature, int direction, + int *low, int *high) +{ + + if (temperature >= threshold->temperature) { + if (threshold->temperature > *low && + THERMAL_THRESHOLD_WAY_DOWN & threshold->direction) + *low = threshold->temperature; + + if (last_temperature < threshold->temperature && + threshold->direction & direction) + return true; + } else { + if (threshold->temperature < *high && THERMAL_THRESHOLD_WAY_UP + & threshold->direction) + *high = threshold->temperature; + + if (last_temperature >= threshold->temperature && + threshold->direction & direction) + return true; + } + + return false; +} + +static bool thermal_thresholds_handle_raising(struct list_head *thresholds, int temperature, + int last_temperature, int *low, int *high) +{ + struct user_threshold *t; + + list_for_each_entry(t, thresholds, list_node) { + if (__thermal_threshold_is_crossed(t, temperature, last_temperature, + THERMAL_THRESHOLD_WAY_UP, low, high)) + return true; + } + + return false; +} + +static bool thermal_thresholds_handle_dropping(struct list_head *thresholds, int temperature, + int last_temperature, int *low, int *high) +{ + struct user_threshold *t; + + list_for_each_entry_reverse(t, thresholds, list_node) { + if (__thermal_threshold_is_crossed(t, temperature, last_temperature, + THERMAL_THRESHOLD_WAY_DOWN, low, high)) + return true; + } + + return false; +} + +void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high) +{ + struct list_head *thresholds = &tz->user_thresholds; + + int temperature = tz->temperature; + int last_temperature = tz->last_temperature; + bool notify; + + lockdep_assert_held(&tz->lock); + + /* + * We need a second update in order to detect a threshold being crossed + */ + if (last_temperature == THERMAL_TEMP_INVALID) + return; + + /* + * The temperature is stable, so obviously we can not have + * crossed a threshold. + */ + if (last_temperature == temperature) + return; + + /* + * Since last update the temperature: + * - increased : thresholds are crossed the way up + * - decreased : thresholds are crossed the way down + */ + if (temperature > last_temperature) + notify = thermal_thresholds_handle_raising(thresholds, temperature, + last_temperature, low, high); + else + notify = thermal_thresholds_handle_dropping(thresholds, temperature, + last_temperature, low, high); + + if (notify) + pr_debug("A threshold has been crossed the way %s, with a temperature=%d, last_temperature=%d\n", + temperature > last_temperature ? "up" : "down", temperature, last_temperature); +} + +int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction) +{ + struct list_head *thresholds = &tz->user_thresholds; + struct user_threshold *t; + + lockdep_assert_held(&tz->lock); + + t = __thermal_thresholds_find(thresholds, temperature); + if (t) { + if (t->direction == direction) + return -EEXIST; + + t->direction |= direction; + } else { + + t = kmalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + + INIT_LIST_HEAD(&t->list_node); + t->temperature = temperature; + t->direction = direction; + list_add(&t->list_node, thresholds); + list_sort(NULL, thresholds, __thermal_thresholds_cmp); + } + + __thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD); + + return 0; +} + +int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction) +{ + struct list_head *thresholds = &tz->user_thresholds; + struct user_threshold *t; + + lockdep_assert_held(&tz->lock); + + t = __thermal_thresholds_find(thresholds, temperature); + if (!t) + return -ENOENT; + + if (t->direction == direction) { + list_del(&t->list_node); + kfree(t); + } else { + t->direction &= ~direction; + } + + __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD); + + return 0; +} + +int thermal_thresholds_for_each(struct thermal_zone_device *tz, + int (*cb)(struct user_threshold *, void *arg), void *arg) +{ + struct list_head *thresholds = &tz->user_thresholds; + struct user_threshold *entry; + int ret; + + lockdep_assert_held(&tz->lock); + + list_for_each_entry(entry, thresholds, list_node) { + ret = cb(entry, arg); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/thermal/thermal_thresholds.h b/drivers/thermal/thermal_thresholds.h new file mode 100644 index 0000000000000..232f4e8089af8 --- /dev/null +++ b/drivers/thermal/thermal_thresholds.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __THERMAL_THRESHOLDS_H__ +#define __THERMAL_THRESHOLDS_H__ + +struct user_threshold { + struct list_head list_node; + int temperature; + int direction; +}; + +int thermal_thresholds_init(struct thermal_zone_device *tz); +void thermal_thresholds_exit(struct thermal_zone_device *tz); +void thermal_thresholds_flush(struct thermal_zone_device *tz); +void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high); +int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction); +int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction); +int thermal_thresholds_for_each(struct thermal_zone_device *tz, + int (*cb)(struct user_threshold *, void *arg), void *arg); +#endif diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 25ea8fe2313e6..bcaa92732e14a 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -56,6 +56,9 @@ enum thermal_notify_event { THERMAL_TZ_UNBIND_CDEV, /* Cooling dev is unbind from the thermal zone */ THERMAL_INSTANCE_WEIGHT_CHANGED, /* Thermal instance weight changed */ THERMAL_TZ_RESUME, /* Thermal zone is resuming after system sleep */ + THERMAL_TZ_ADD_THRESHOLD, /* Threshold added */ + THERMAL_TZ_DEL_THRESHOLD, /* Threshold deleted */ + THERMAL_TZ_FLUSH_THRESHOLDS, /* All thresholds deleted */ }; /** diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h index fc78bf3aead79..2e6f60a361735 100644 --- a/include/uapi/linux/thermal.h +++ b/include/uapi/linux/thermal.h @@ -3,6 +3,8 @@ #define _UAPI_LINUX_THERMAL_H #define THERMAL_NAME_LENGTH 20 +#define THERMAL_THRESHOLD_WAY_UP BIT(0) +#define THERMAL_THRESHOLD_WAY_DOWN BIT(1) enum thermal_device_mode { THERMAL_DEVICE_DISABLED = 0, From 85ee9449f76430846132b0248a99a6cfb524f992 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 23 Sep 2024 11:59:58 +0200 Subject: [PATCH 02/45] thermal: core: Connect the threshold with the core Initialize, de-initialize and handle the threshold in the same place than the trip points. Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20240923100005.2532430-3-daniel.lezcano@linaro.org [ rjw: Subject edit ] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 8f03985f971c3..1e87256e86bef 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -585,6 +585,8 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, high = td->threshold; } + thermal_thresholds_handle(tz, &low, &high); + thermal_zone_set_trips(tz, low, high); list_sort(NULL, &way_up_list, thermal_trip_notify_cmp); @@ -1491,6 +1493,10 @@ thermal_zone_device_register_with_trips(const char *type, goto unregister; } + result = thermal_thresholds_init(tz); + if (result) + goto remove_hwmon; + mutex_lock(&thermal_list_lock); mutex_lock(&tz->lock); @@ -1514,6 +1520,8 @@ thermal_zone_device_register_with_trips(const char *type, return tz; +remove_hwmon: + thermal_remove_hwmon_sysfs(tz); unregister: device_del(&tz->device); release_device: @@ -1601,6 +1609,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) thermal_set_governor(tz, NULL); + thermal_thresholds_exit(tz); thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); ida_destroy(&tz->ida); From 662f920f7e390db5d1a6792a2b0ffa59b6c962fc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:05:49 +0200 Subject: [PATCH 03/45] thermal: core: Initialize thermal zones before registering them Since user space can start interacting with a new thermal zone as soon as device_register() called by thermal_zone_device_register_with_trips() returns, it is better to initialize the thermal zone before calling device_register() on it. Fixes: d0df264fbd3c ("thermal/core: Remove pointless thermal_zone_device_reset() function") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3336146.44csPzL39Z@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 1e87256e86bef..03f5ac996a7af 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1467,6 +1467,7 @@ thermal_zone_device_register_with_trips(const char *type, thermal_zone_destroy_device_groups(tz); goto remove_id; } + thermal_zone_device_init(tz); result = device_register(&tz->device); if (result) goto release_device; @@ -1509,7 +1510,6 @@ thermal_zone_device_register_with_trips(const char *type, mutex_unlock(&thermal_list_lock); - thermal_zone_device_init(tz); /* Update the new thermal zone and mark it as already updated. */ if (atomic_cmpxchg(&tz->need_update, 1, 0)) thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); From 7ddca5885718c2683d75689aa065c9a3bb317e5a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:09:23 +0200 Subject: [PATCH 04/45] thermal: core: Rearrange PM notification code Move the code run for each thermal zone by the thermal PM notify handler to separate functions. This will help to make some subsequent changes look somewhat more straightforward, among other things. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2299090.iZASKD2KPV@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 88 ++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 03f5ac996a7af..f54c2d2544a47 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1684,6 +1684,48 @@ static void thermal_zone_device_resume(struct work_struct *work) mutex_unlock(&tz->lock); } +static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) +{ + mutex_lock(&tz->lock); + + if (tz->resuming) { + /* + * thermal_zone_device_resume() queued up for this zone has not + * acquired the lock yet, so release it to let the function run + * and wait util it has done the work. + */ + mutex_unlock(&tz->lock); + + wait_for_completion(&tz->resume); + + mutex_lock(&tz->lock); + } + + tz->suspended = true; + + mutex_unlock(&tz->lock); +} + +static void thermal_zone_pm_complete(struct thermal_zone_device *tz) +{ + mutex_lock(&tz->lock); + + cancel_delayed_work(&tz->poll_queue); + + reinit_completion(&tz->resume); + tz->resuming = true; + + /* + * Replace the work function with the resume one, which will restore the + * original work function and schedule the polling work if needed. + */ + INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume); + /* Queue up the work without a delay. */ + mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0); + + mutex_unlock(&tz->lock); +} + static int thermal_pm_notify(struct notifier_block *nb, unsigned long mode, void *_unused) { @@ -1695,27 +1737,8 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_SUSPEND_PREPARE: mutex_lock(&thermal_list_lock); - list_for_each_entry(tz, &thermal_tz_list, node) { - mutex_lock(&tz->lock); - - if (tz->resuming) { - /* - * thermal_zone_device_resume() queued up for - * this zone has not acquired the lock yet, so - * release it to let the function run and wait - * util it has done the work. - */ - mutex_unlock(&tz->lock); - - wait_for_completion(&tz->resume); - - mutex_lock(&tz->lock); - } - - tz->suspended = true; - - mutex_unlock(&tz->lock); - } + list_for_each_entry(tz, &thermal_tz_list, node) + thermal_zone_pm_prepare(tz); mutex_unlock(&thermal_list_lock); break; @@ -1724,27 +1747,8 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_POST_SUSPEND: mutex_lock(&thermal_list_lock); - list_for_each_entry(tz, &thermal_tz_list, node) { - mutex_lock(&tz->lock); - - cancel_delayed_work(&tz->poll_queue); - - reinit_completion(&tz->resume); - tz->resuming = true; - - /* - * Replace the work function with the resume one, which - * will restore the original work function and schedule - * the polling work if needed. - */ - INIT_DELAYED_WORK(&tz->poll_queue, - thermal_zone_device_resume); - /* Queue up the work without a delay. */ - mod_delayed_work(system_freezable_power_efficient_wq, - &tz->poll_queue, 0); - - mutex_unlock(&tz->lock); - } + list_for_each_entry(tz, &thermal_tz_list, node) + thermal_zone_pm_complete(tz); mutex_unlock(&thermal_list_lock); break; From 26c9ab8090cda1eb3d42f491cc32d227404897da Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:11:53 +0200 Subject: [PATCH 05/45] thermal: core: Represent suspend-related thermal zone flags as bits Instead of using two separate fields in struct thermal_zone_device for representing flags related to thermal zone suspend, represent them explicitly as bits in one u8 "state" field. Subsequently, that field will be used for addressing race conditions related to thermal zone initialization and exit. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/7733910.EvYhyI6sBW@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 11 +++++------ drivers/thermal/thermal_core.h | 11 +++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f54c2d2544a47..225fa8ec6beea 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -547,7 +547,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, int low = -INT_MAX, high = INT_MAX; int temp, ret; - if (tz->suspended || tz->mode != THERMAL_DEVICE_ENABLED) + if (tz->state != TZ_STATE_READY || tz->mode != THERMAL_DEVICE_ENABLED) return; ret = __thermal_zone_get_temp(tz, &temp); @@ -1671,7 +1671,7 @@ static void thermal_zone_device_resume(struct work_struct *work) mutex_lock(&tz->lock); - tz->suspended = false; + tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING); thermal_debug_tz_resume(tz); thermal_zone_device_init(tz); @@ -1679,7 +1679,6 @@ static void thermal_zone_device_resume(struct work_struct *work) __thermal_zone_device_update(tz, THERMAL_TZ_RESUME); complete(&tz->resume); - tz->resuming = false; mutex_unlock(&tz->lock); } @@ -1688,7 +1687,7 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) { mutex_lock(&tz->lock); - if (tz->resuming) { + if (tz->state & TZ_STATE_FLAG_RESUMING) { /* * thermal_zone_device_resume() queued up for this zone has not * acquired the lock yet, so release it to let the function run @@ -1701,7 +1700,7 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) mutex_lock(&tz->lock); } - tz->suspended = true; + tz->state |= TZ_STATE_FLAG_SUSPENDED; mutex_unlock(&tz->lock); } @@ -1713,7 +1712,7 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz) cancel_delayed_work(&tz->poll_queue); reinit_completion(&tz->resume); - tz->resuming = true; + tz->state |= TZ_STATE_FLAG_RESUMING; /* * Replace the work function with the resume one, which will restore the diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 1ea91d59498b2..fd4478e15d025 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -62,6 +62,11 @@ struct thermal_governor { struct list_head governor_list; }; +#define TZ_STATE_FLAG_SUSPENDED BIT(0) +#define TZ_STATE_FLAG_RESUMING BIT(1) + +#define TZ_STATE_READY 0 + /** * struct thermal_zone_device - structure for a thermal zone * @id: unique id number for each thermal zone @@ -101,8 +106,7 @@ struct thermal_governor { * @node: node in thermal_tz_list (in thermal_core.c) * @poll_queue: delayed work for polling * @notify_event: Last notification event - * @suspended: thermal zone suspend indicator - * @resuming: indicates whether or not thermal zone resume is in progress + * @state: current state of the thermal zone * @trips: array of struct thermal_trip objects */ struct thermal_zone_device { @@ -135,8 +139,7 @@ struct thermal_zone_device { struct list_head node; struct delayed_work poll_queue; enum thermal_notify_event notify_event; - bool suspended; - bool resuming; + u8 state; #ifdef CONFIG_THERMAL_DEBUGFS struct thermal_debugfs *debugfs; #endif From 7837fa8115e0273d3cfbd3d17b3f7b7291ceac08 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:15:04 +0200 Subject: [PATCH 06/45] thermal: core: Mark thermal zones as initializing to start with After thermal_zone_device_register_with_trips() has called device_register() and it has registered the new thermal zone device with the driver core, user space may access its sysfs attributes and, among other things, it may enable the thermal zone before it is ready. To address this, introduce a new thermal zone state flag for initialization and set it before calling device_register() in thermal_zone_device_register_with_trips(). This causes __thermal_zone_device_update() to return early until the new flag is cleared. To clear it when the thermal zone is ready, introduce a new function called thermal_zone_init_complete() that will also invoke __thermal_zone_device_update() after clearing that flag (both under the thernal zone lock) and make thermal_zone_device_register_with_trips() call the new function instead of checking need_update and calling thermal_zone_device_update() when it is set. After this change, if user space enables the thermal zone prematurely, __thermal_zone_device_update() will return early for it until thermal_zone_init_complete() is called. In turn, if the thermal zone is not enabled by user space before thermal_zone_init_complete() is called, the __thermal_zone_device_update() call in it will return early because the thermal zone has not been enabled yet, but that function will be invoked again by thermal_zone_device_set_mode() when the thermal zone is enabled and it will not return early this time. The checking of need_update is not necessary any more because the __thermal_zone_device_update() calls potentially triggered by cooling device binding take place before calling thermal_zone_init_complete(), so they all will return early, which means that thermal_zone_init_complete() must call __thermal_zone_device_update() in case the thermal zone is enabled prematurely by user space. Fixes: 203d3d4aa482 ("the generic thermal sysfs driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/9360231.CDJkKcVGEf@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 16 +++++++++++++--- drivers/thermal/thermal_core.h | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 225fa8ec6beea..f43fc7b54a916 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1334,6 +1334,16 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) } EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); +static void thermal_zone_init_complete(struct thermal_zone_device *tz) +{ + mutex_lock(&tz->lock); + + tz->state &= ~TZ_STATE_FLAG_INIT; + __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + + mutex_unlock(&tz->lock); +} + /** * thermal_zone_device_register_with_trips() - register a new thermal zone device * @type: the thermal zone device type @@ -1453,6 +1463,8 @@ thermal_zone_device_register_with_trips(const char *type, tz->passive_delay_jiffies = msecs_to_jiffies(passive_delay); tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; + tz->state = TZ_STATE_FLAG_INIT; + /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz); @@ -1510,9 +1522,7 @@ thermal_zone_device_register_with_trips(const char *type, mutex_unlock(&thermal_list_lock); - /* Update the new thermal zone and mark it as already updated. */ - if (atomic_cmpxchg(&tz->need_update, 1, 0)) - thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + thermal_zone_init_complete(tz); thermal_notify_tz_create(tz); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index fd4478e15d025..95d920e8403ac 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -64,6 +64,7 @@ struct thermal_governor { #define TZ_STATE_FLAG_SUSPENDED BIT(0) #define TZ_STATE_FLAG_RESUMING BIT(1) +#define TZ_STATE_FLAG_INIT BIT(2) #define TZ_STATE_READY 0 From cdf771ab476bd9acb0948f3088a277d5c3cacc6b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:19:21 +0200 Subject: [PATCH 07/45] thermal: core: Fix race between zone registration and system suspend If the registration of a thermal zone takes place at the time when system suspend is started, thermal_pm_notify() can run before the new thermal zone is added to thermal_tz_list and its "suspended" flag will not be set. Consequently, if __thermal_zone_device_update() is called for that thermal zone, it will not return early as expected which may cause some destructive interference with the system suspend or resume flow to occur. To avoid that, make thermal_zone_init_complete() introduced previously set the "suspended" flag for new thermal zones if it runs during system suspend or resume. Fixes: 4e814173a8c4 ("thermal: core: Fix thermal zone suspend-resume synchronization") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/8490245.NyiUUSuA9g@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f43fc7b54a916..d1f5c165c5614 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -40,6 +40,8 @@ static DEFINE_MUTEX(thermal_governor_lock); static struct thermal_governor *def_governor; +static bool thermal_pm_suspended; + /* * Governor section: set of functions to handle thermal governors * @@ -1339,6 +1341,14 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz) mutex_lock(&tz->lock); tz->state &= ~TZ_STATE_FLAG_INIT; + /* + * If system suspend or resume is in progress at this point, the + * new thermal zone needs to be marked as suspended because + * thermal_pm_notify() has run already. + */ + if (thermal_pm_suspended) + tz->state |= TZ_STATE_FLAG_SUSPENDED; + __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); mutex_unlock(&tz->lock); @@ -1520,10 +1530,10 @@ thermal_zone_device_register_with_trips(const char *type, list_for_each_entry(cdev, &thermal_cdev_list, node) thermal_zone_cdev_bind(tz, cdev); - mutex_unlock(&thermal_list_lock); - thermal_zone_init_complete(tz); + mutex_unlock(&thermal_list_lock); + thermal_notify_tz_create(tz); thermal_debug_tz_add(tz); @@ -1746,6 +1756,8 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_SUSPEND_PREPARE: mutex_lock(&thermal_list_lock); + thermal_pm_suspended = true; + list_for_each_entry(tz, &thermal_tz_list, node) thermal_zone_pm_prepare(tz); @@ -1756,6 +1768,8 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_POST_SUSPEND: mutex_lock(&thermal_list_lock); + thermal_pm_suspended = false; + list_for_each_entry(tz, &thermal_tz_list, node) thermal_zone_pm_complete(tz); From d07700b474d38ea4b1130e37d28379fc202054fa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:23:10 +0200 Subject: [PATCH 08/45] thermal: core: Consolidate thermal zone locking during initialization The part of thermal zone initialization carried out under thermal_list_lock acquires the thermal zone lock and releases it multiple times back and forth which is not really necessary. Instead of doing this, make it acquire the thermal zone lock once after acquiring thermal_list_lock and release it along with that lock. For this purpose, move all of the code in question to thermal_zone_init_complete() introduced previously and provide an "unlocked" variant of thermal_zone_cdev_bind() to be invoked from there. Also notice that a thermal zone does not need to be added to thermal_tz_list under its own lock, so make the new code acquire the thermal zone lock after adding it to the list. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1920382.CQOukoFCf9@rjwysocki.net Reviewed-by: Lukasz Luba [ rjw: Rebase on top of recent thermal core changes ] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_core.c | 39 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d1f5c165c5614..5ae20a740a505 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -935,16 +935,14 @@ void print_bind_err_msg(struct thermal_zone_device *tz, cdev->type, thermal_zone_trip_id(tz, trip), ret); } -static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev) +static void __thermal_zone_cdev_bind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) { struct thermal_trip_desc *td; if (!tz->ops.should_bind) return; - mutex_lock(&tz->lock); - for_each_trip_desc(tz, td) { struct thermal_trip *trip = &td->trip; struct cooling_spec c = { @@ -961,6 +959,14 @@ static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, if (ret) print_bind_err_msg(tz, trip, cdev, ret); } +} + +static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) +{ + mutex_lock(&tz->lock); + + __thermal_zone_cdev_bind(tz, cdev); mutex_unlock(&tz->lock); } @@ -1338,8 +1344,18 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); static void thermal_zone_init_complete(struct thermal_zone_device *tz) { + struct thermal_cooling_device *cdev; + + mutex_lock(&thermal_list_lock); + + list_add_tail(&tz->node, &thermal_tz_list); + mutex_lock(&tz->lock); + /* Bind cooling devices for this zone. */ + list_for_each_entry(cdev, &thermal_cdev_list, node) + __thermal_zone_cdev_bind(tz, cdev); + tz->state &= ~TZ_STATE_FLAG_INIT; /* * If system suspend or resume is in progress at this point, the @@ -1352,6 +1368,8 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz) __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); mutex_unlock(&tz->lock); + + mutex_unlock(&thermal_list_lock); } /** @@ -1388,7 +1406,6 @@ thermal_zone_device_register_with_trips(const char *type, unsigned int polling_delay) { const struct thermal_trip *trip = trips; - struct thermal_cooling_device *cdev; struct thermal_zone_device *tz; struct thermal_trip_desc *td; int id; @@ -1520,20 +1537,8 @@ thermal_zone_device_register_with_trips(const char *type, if (result) goto remove_hwmon; - mutex_lock(&thermal_list_lock); - - mutex_lock(&tz->lock); - list_add_tail(&tz->node, &thermal_tz_list); - mutex_unlock(&tz->lock); - - /* Bind cooling devices for this zone */ - list_for_each_entry(cdev, &thermal_cdev_list, node) - thermal_zone_cdev_bind(tz, cdev); - thermal_zone_init_complete(tz); - mutex_unlock(&thermal_list_lock); - thermal_notify_tz_create(tz); thermal_debug_tz_add(tz); From 1dae3e70b473adc32f81ca1be926440f9b1de9dc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:26:12 +0200 Subject: [PATCH 09/45] thermal: core: Mark thermal zones as exiting before unregistration In analogy with a previous change in the thermal zone registration code path, to ensure that __thermal_zone_device_update() will return early for thermal zones that are going away, introduce a thermal zone state flag representing the "exit" state and set it while deleting the thermal zone from thermal_tz_list. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/4394176.ejJDZkT8p0@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 3 +++ drivers/thermal/thermal_core.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 5ae20a740a505..841d072bbe937 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1621,7 +1621,10 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) } mutex_lock(&tz->lock); + + tz->state |= TZ_STATE_FLAG_EXIT; list_del(&tz->node); + mutex_unlock(&tz->lock); /* Unbind all cdevs associated with 'this' thermal zone */ diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 95d920e8403ac..f8d2e85d24081 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -65,6 +65,7 @@ struct thermal_governor { #define TZ_STATE_FLAG_SUSPENDED BIT(0) #define TZ_STATE_FLAG_RESUMING BIT(1) #define TZ_STATE_FLAG_INIT BIT(2) +#define TZ_STATE_FLAG_EXIT BIT(3) #define TZ_STATE_READY 0 From fa4f9c967969cb04f9a37d567ecd9e1db9e9098f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:30:26 +0200 Subject: [PATCH 10/45] thermal: core: Consolidate thermal zone locking in the exit path In analogy with a previous change in the thermal zone initialization path, to avoid acquiring the thermal zone lock and releasing it multiple times back and forth unnecessarily, move all of the code running under thermal_list_lock in thermal_zone_device_unregister() into a new function called thermal_zone_exit() and make the latter acquire the thermal zone lock only once and release it along with thermal_list_lock. For this purpose, provide an "unlocked" variant of thermal_zone_cdev_unbind() to be called by thermal_zone_exit() under the thermal zone lock. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1963152.taCxCBeP46@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 66 ++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 841d072bbe937..145b43a4dffd7 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1268,15 +1268,21 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) } EXPORT_SYMBOL_GPL(thermal_cooling_device_update); -static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev) +static void __thermal_zone_cdev_unbind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) { struct thermal_trip_desc *td; - mutex_lock(&tz->lock); - for_each_trip_desc(tz, td) thermal_unbind_cdev_from_trip(tz, &td->trip, cdev); +} + +static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) +{ + mutex_lock(&tz->lock); + + __thermal_zone_cdev_unbind(tz, cdev); mutex_unlock(&tz->lock); } @@ -1596,43 +1602,49 @@ struct device *thermal_zone_device(struct thermal_zone_device *tzd) } EXPORT_SYMBOL_GPL(thermal_zone_device); -/** - * thermal_zone_device_unregister - removes the registered thermal zone device - * @tz: the thermal zone device to remove - */ -void thermal_zone_device_unregister(struct thermal_zone_device *tz) +static bool thermal_zone_exit(struct thermal_zone_device *tz) { struct thermal_cooling_device *cdev; - struct thermal_zone_device *pos = NULL; - - if (!tz) - return; - - thermal_debug_tz_remove(tz); + bool ret = true; mutex_lock(&thermal_list_lock); - list_for_each_entry(pos, &thermal_tz_list, node) - if (pos == tz) - break; - if (pos != tz) { - /* thermal zone device not found */ - mutex_unlock(&thermal_list_lock); - return; + + if (list_empty(&tz->node)) { + ret = false; + goto unlock; } mutex_lock(&tz->lock); tz->state |= TZ_STATE_FLAG_EXIT; - list_del(&tz->node); + list_del_init(&tz->node); - mutex_unlock(&tz->lock); - - /* Unbind all cdevs associated with 'this' thermal zone */ + /* Unbind all cdevs associated with this thermal zone. */ list_for_each_entry(cdev, &thermal_cdev_list, node) - thermal_zone_cdev_unbind(tz, cdev); + __thermal_zone_cdev_unbind(tz, cdev); + mutex_unlock(&tz->lock); + +unlock: mutex_unlock(&thermal_list_lock); + return ret; +} + +/** + * thermal_zone_device_unregister - removes the registered thermal zone device + * @tz: the thermal zone device to remove + */ +void thermal_zone_device_unregister(struct thermal_zone_device *tz) +{ + if (!tz) + return; + + thermal_debug_tz_remove(tz); + + if (!thermal_zone_exit(tz)) + return; + cancel_delayed_work_sync(&tz->poll_queue); thermal_set_governor(tz, NULL); From c4cd42ebd32b57841a0dba4b296692d201802618 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:33:28 +0200 Subject: [PATCH 11/45] thermal: core: Update thermal zones after cooling device binding If a new cooling device is registered and it is bound to at least one trip point in a given thermal zone, that thermal zone needs to be updated via __thermal_zone_device_update(). Instead of doing this with the help of the need_update atomic field in struct thermal_zone_device, which is not particularly straightforward, make __thermal_zone_cdev_bind() return a bool value indicating whether or not the given thermal zone needs to be updated because a new cooling device has been bound to it and update thermal_zone_cdev_bind() to call __thermal_zone_device_update() when this value is "true". No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2226302.Icojqenx9y@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 145b43a4dffd7..b8c3a9185ae72 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -935,13 +935,14 @@ void print_bind_err_msg(struct thermal_zone_device *tz, cdev->type, thermal_zone_trip_id(tz, trip), ret); } -static void __thermal_zone_cdev_bind(struct thermal_zone_device *tz, +static bool __thermal_zone_cdev_bind(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) { struct thermal_trip_desc *td; + bool update_tz = false; if (!tz->ops.should_bind) - return; + return false; for_each_trip_desc(tz, td) { struct thermal_trip *trip = &td->trip; @@ -956,9 +957,15 @@ static void __thermal_zone_cdev_bind(struct thermal_zone_device *tz, continue; ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c); - if (ret) + if (ret) { print_bind_err_msg(tz, trip, cdev, ret); + continue; + } + + update_tz = true; } + + return update_tz; } static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, @@ -966,7 +973,8 @@ static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, { mutex_lock(&tz->lock); - __thermal_zone_cdev_bind(tz, cdev); + if (__thermal_zone_cdev_bind(tz, cdev)) + __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); mutex_unlock(&tz->lock); } @@ -993,7 +1001,7 @@ __thermal_cooling_device_register(struct device_node *np, const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device *cdev; - struct thermal_zone_device *pos = NULL; + struct thermal_zone_device *pos; unsigned long current_state; int id, ret; @@ -1069,11 +1077,6 @@ __thermal_cooling_device_register(struct device_node *np, list_for_each_entry(pos, &thermal_tz_list, node) thermal_zone_cdev_bind(pos, cdev); - list_for_each_entry(pos, &thermal_tz_list, node) - if (atomic_cmpxchg(&pos->need_update, 1, 0)) - thermal_zone_device_update(pos, - THERMAL_EVENT_UNSPECIFIED); - mutex_unlock(&thermal_list_lock); return cdev; From ee879a5ea33c49d994fc1d2a60a36afe004548ce Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:35:16 +0200 Subject: [PATCH 12/45] thermal: core: Drop need_update field from struct thermal_zone_device After previous changes, the need_update field in struct thermal_zone_device is only set and never read, so drop it. No functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2495061.jE0xQCEvom@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 4 ---- drivers/thermal/thermal_core.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index b8c3a9185ae72..13e22fcfad184 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -842,7 +842,6 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, if (!result) { list_add_tail(&dev->tz_node, &tz->thermal_instances); list_add_tail(&dev->cdev_node, &cdev->thermal_instances); - atomic_set(&tz->need_update, 1); thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); } @@ -1507,9 +1506,6 @@ thermal_zone_device_register_with_trips(const char *type, if (result) goto remove_id; - /* A new thermal zone needs to be updated anyway. */ - atomic_set(&tz->need_update, 1); - result = dev_set_name(&tz->device, "thermal_zone%d", tz->id); if (result) { thermal_zone_destroy_device_groups(tz); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index f8d2e85d24081..a28f7198f9176 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -96,7 +96,6 @@ struct thermal_governor { trip point. * @prev_high_trip: the above current temperature if you've crossed a passive trip point. - * @need_update: if equals 1, thermal_zone_device_update needs to be invoked. * @ops: operations this &thermal_zone_device supports * @tzp: thermal zone parameters * @governor: pointer to the governor for this thermal zone @@ -130,7 +129,6 @@ struct thermal_zone_device { int passive; int prev_low_trip; int prev_high_trip; - atomic_t need_update; struct thermal_zone_device_ops ops; struct thermal_zone_params *tzp; struct thermal_governor *governor; From 0dc23567c20639049ad57fd8cc2165ee9f493ab6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:39:19 +0200 Subject: [PATCH 13/45] thermal: core: Move lists of thermal instances to trip descriptors In almost all places where a thermal zone's list of thermal instances is walked, there is a check to match a specific trip point and it is walked in vain whenever there are no cooling devices associated with the given trip. To address this, store the lists of thermal instances in trip point descriptors instead of storing them in thermal zones and adjust all code using those lists accordingly. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5522726.Sb9uPGUboI@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/gov_bang_bang.c | 11 ++++---- drivers/thermal/gov_fair_share.c | 16 ++++------- drivers/thermal/gov_power_allocator.c | 40 +++++++++++++-------------- drivers/thermal/gov_step_wise.c | 16 +++++------ drivers/thermal/thermal_core.c | 33 ++++++++++++---------- drivers/thermal/thermal_core.h | 5 ++-- drivers/thermal/thermal_helpers.c | 5 ++-- 7 files changed, 62 insertions(+), 64 deletions(-) diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index 863e7a4272e66..b887e48e8c7e6 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -67,6 +67,7 @@ static void bang_bang_control(struct thermal_zone_device *tz, const struct thermal_trip *trip, bool crossed_up) { + const struct thermal_trip_desc *td = trip_to_trip_desc(trip); struct thermal_instance *instance; lockdep_assert_held(&tz->lock); @@ -75,10 +76,8 @@ static void bang_bang_control(struct thermal_zone_device *tz, thermal_zone_trip_id(tz, trip), trip->temperature, tz->temperature, trip->hysteresis); - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip == trip) - bang_bang_set_instance_target(instance, crossed_up); - } + list_for_each_entry(instance, &td->thermal_instances, trip_node) + bang_bang_set_instance_target(instance, crossed_up); } static void bang_bang_manage(struct thermal_zone_device *tz) @@ -104,8 +103,8 @@ static void bang_bang_manage(struct thermal_zone_device *tz) * to the thermal zone temperature and the trip point threshold. */ turn_on = tz->temperature >= td->threshold; - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (!instance->initialized && instance->trip == trip) + list_for_each_entry(instance, &td->thermal_instances, trip_node) { + if (!instance->initialized) bang_bang_set_instance_target(instance, turn_on); } } diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c index ce0ea571ed67a..d37d57d48c389 100644 --- a/drivers/thermal/gov_fair_share.c +++ b/drivers/thermal/gov_fair_share.c @@ -44,7 +44,7 @@ static int get_trip_level(struct thermal_zone_device *tz) /** * fair_share_throttle - throttles devices associated with the given zone * @tz: thermal_zone_device - * @trip: trip point + * @td: trip point descriptor * @trip_level: number of trips crossed by the zone temperature * * Throttling Logic: This uses three parameters to calculate the new @@ -61,29 +61,23 @@ static int get_trip_level(struct thermal_zone_device *tz) * new_state of cooling device = P3 * P2 * P1 */ static void fair_share_throttle(struct thermal_zone_device *tz, - const struct thermal_trip *trip, + const struct thermal_trip_desc *td, int trip_level) { struct thermal_instance *instance; int total_weight = 0; int nr_instances = 0; - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; - + list_for_each_entry(instance, &td->thermal_instances, trip_node) { total_weight += instance->weight; nr_instances++; } - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + list_for_each_entry(instance, &td->thermal_instances, trip_node) { struct thermal_cooling_device *cdev = instance->cdev; u64 dividend; u32 divisor; - if (instance->trip != trip) - continue; - dividend = trip_level; dividend *= cdev->max_state; divisor = tz->num_trips; @@ -116,7 +110,7 @@ static void fair_share_manage(struct thermal_zone_device *tz) trip->type == THERMAL_TRIP_HOT) continue; - fair_share_throttle(tz, trip, trip_level); + fair_share_throttle(tz, td, trip_level); } } diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index 1b2345a697c5a..46c81b147662c 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -97,11 +97,9 @@ struct power_allocator_params { struct power_actor *power; }; -static bool power_actor_is_valid(struct power_allocator_params *params, - struct thermal_instance *instance) +static bool power_actor_is_valid(struct thermal_instance *instance) { - return (instance->trip == params->trip_max && - cdev_is_power_actor(instance->cdev)); + return cdev_is_power_actor(instance->cdev); } /** @@ -118,13 +116,14 @@ static bool power_actor_is_valid(struct power_allocator_params *params, static u32 estimate_sustainable_power(struct thermal_zone_device *tz) { struct power_allocator_params *params = tz->governor_data; + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); struct thermal_cooling_device *cdev; struct thermal_instance *instance; u32 sustainable_power = 0; u32 min_power; - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (!power_actor_is_valid(params, instance)) + list_for_each_entry(instance, &td->thermal_instances, trip_node) { + if (!power_actor_is_valid(instance)) continue; cdev = instance->cdev; @@ -400,6 +399,7 @@ static void divvy_up_power(struct power_actor *power, int num_actors, static void allocate_power(struct thermal_zone_device *tz, int control_temp) { struct power_allocator_params *params = tz->governor_data; + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); unsigned int num_actors = params->num_actors; struct power_actor *power = params->power; struct thermal_cooling_device *cdev; @@ -417,10 +417,10 @@ static void allocate_power(struct thermal_zone_device *tz, int control_temp) /* Clean all buffers for new power estimations */ memset(power, 0, params->buffer_size); - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + list_for_each_entry(instance, &td->thermal_instances, trip_node) { struct power_actor *pa = &power[i]; - if (!power_actor_is_valid(params, instance)) + if (!power_actor_is_valid(instance)) continue; cdev = instance->cdev; @@ -454,10 +454,10 @@ static void allocate_power(struct thermal_zone_device *tz, int control_temp) power_range); i = 0; - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + list_for_each_entry(instance, &td->thermal_instances, trip_node) { struct power_actor *pa = &power[i]; - if (!power_actor_is_valid(params, instance)) + if (!power_actor_is_valid(instance)) continue; power_actor_set_power(instance->cdev, instance, @@ -538,12 +538,13 @@ static void reset_pid_controller(struct power_allocator_params *params) static void allow_maximum_power(struct thermal_zone_device *tz) { struct power_allocator_params *params = tz->governor_data; + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); struct thermal_cooling_device *cdev; struct thermal_instance *instance; u32 req_power; - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (!power_actor_is_valid(params, instance)) + list_for_each_entry(instance, &td->thermal_instances, trip_node) { + if (!power_actor_is_valid(instance)) continue; cdev = instance->cdev; @@ -581,13 +582,11 @@ static void allow_maximum_power(struct thermal_zone_device *tz) static int check_power_actors(struct thermal_zone_device *tz, struct power_allocator_params *params) { + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); struct thermal_instance *instance; int ret = 0; - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != params->trip_max) - continue; - + list_for_each_entry(instance, &td->thermal_instances, trip_node) { if (!cdev_is_power_actor(instance->cdev)) { dev_warn(&tz->device, "power_allocator: %s is not a power actor\n", instance->cdev->type); @@ -635,14 +634,15 @@ static void power_allocator_update_tz(struct thermal_zone_device *tz, enum thermal_notify_event reason) { struct power_allocator_params *params = tz->governor_data; + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); struct thermal_instance *instance; int num_actors = 0; switch (reason) { case THERMAL_TZ_BIND_CDEV: case THERMAL_TZ_UNBIND_CDEV: - list_for_each_entry(instance, &tz->thermal_instances, tz_node) - if (power_actor_is_valid(params, instance)) + list_for_each_entry(instance, &td->thermal_instances, trip_node) + if (power_actor_is_valid(instance)) num_actors++; if (num_actors == params->num_actors) @@ -652,8 +652,8 @@ static void power_allocator_update_tz(struct thermal_zone_device *tz, break; case THERMAL_INSTANCE_WEIGHT_CHANGED: params->total_weight = 0; - list_for_each_entry(instance, &tz->thermal_instances, tz_node) - if (power_actor_is_valid(params, instance)) + list_for_each_entry(instance, &td->thermal_instances, trip_node) + if (power_actor_is_valid(instance)) params->total_weight += instance->weight; break; default: diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index fd5527188cf91..ea4bf88d37f33 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -66,9 +66,10 @@ static unsigned long get_target_state(struct thermal_instance *instance, } static void thermal_zone_trip_update(struct thermal_zone_device *tz, - const struct thermal_trip *trip, + const struct thermal_trip_desc *td, int trip_threshold) { + const struct thermal_trip *trip = &td->trip; enum thermal_trend trend = get_tz_trend(tz, trip); int trip_id = thermal_zone_trip_id(tz, trip); struct thermal_instance *instance; @@ -82,12 +83,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", trip_id, trip->type, trip_threshold, trend, throttle); - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + list_for_each_entry(instance, &td->thermal_instances, trip_node) { int old_target; - if (instance->trip != trip) - continue; - old_target = instance->target; instance->target = get_target_state(instance, trend, throttle); @@ -127,11 +125,13 @@ static void step_wise_manage(struct thermal_zone_device *tz) trip->type == THERMAL_TRIP_HOT) continue; - thermal_zone_trip_update(tz, trip, td->threshold); + thermal_zone_trip_update(tz, td, td->threshold); } - list_for_each_entry(instance, &tz->thermal_instances, tz_node) - thermal_cdev_update(instance->cdev); + for_each_trip_desc(tz, td) { + list_for_each_entry(instance, &td->thermal_instances, trip_node) + thermal_cdev_update(instance->cdev); + } } static struct thermal_governor thermal_gov_step_wise = { diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 13e22fcfad184..363e01e12ec1b 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -490,7 +490,7 @@ static void thermal_zone_device_check(struct work_struct *work) static void thermal_zone_device_init(struct thermal_zone_device *tz) { - struct thermal_instance *pos; + struct thermal_trip_desc *td; INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); @@ -498,8 +498,12 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz) tz->passive = 0; tz->prev_low_trip = -INT_MAX; tz->prev_high_trip = INT_MAX; - list_for_each_entry(pos, &tz->thermal_instances, tz_node) - pos->initialized = false; + for_each_trip_desc(tz, td) { + struct thermal_instance *instance; + + list_for_each_entry(instance, &td->thermal_instances, trip_node) + instance->initialized = false; + } } static void thermal_governor_trip_crossed(struct thermal_governor *governor, @@ -766,12 +770,12 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) * Return: 0 on success, the proper error value otherwise. */ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, - const struct thermal_trip *trip, + struct thermal_trip *trip, struct thermal_cooling_device *cdev, struct cooling_spec *cool_spec) { - struct thermal_instance *dev; - struct thermal_instance *pos; + struct thermal_trip_desc *td = trip_to_trip_desc(trip); + struct thermal_instance *dev, *instance; bool upper_no_limit; int result; @@ -834,13 +838,13 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, goto remove_trip_file; mutex_lock(&cdev->lock); - list_for_each_entry(pos, &tz->thermal_instances, tz_node) - if (pos->trip == trip && pos->cdev == cdev) { + list_for_each_entry(instance, &td->thermal_instances, trip_node) + if (instance->cdev == cdev) { result = -EEXIST; break; } if (!result) { - list_add_tail(&dev->tz_node, &tz->thermal_instances); + list_add_tail(&dev->trip_node, &td->thermal_instances); list_add_tail(&dev->cdev_node, &cdev->thermal_instances); thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); @@ -873,15 +877,16 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, * This function is usually called in the thermal zone device .unbind callback. */ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, - const struct thermal_trip *trip, + struct thermal_trip *trip, struct thermal_cooling_device *cdev) { + struct thermal_trip_desc *td = trip_to_trip_desc(trip); struct thermal_instance *pos, *next; mutex_lock(&cdev->lock); - list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { - if (pos->trip == trip && pos->cdev == cdev) { - list_del(&pos->tz_node); + list_for_each_entry_safe(pos, next, &td->thermal_instances, trip_node) { + if (pos->cdev == cdev) { + list_del(&pos->trip_node); list_del(&pos->cdev_node); thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); @@ -1462,7 +1467,6 @@ thermal_zone_device_register_with_trips(const char *type, } } - INIT_LIST_HEAD(&tz->thermal_instances); INIT_LIST_HEAD(&tz->node); ida_init(&tz->ida); mutex_init(&tz->lock); @@ -1486,6 +1490,7 @@ thermal_zone_device_register_with_trips(const char *type, tz->num_trips = num_trips; for_each_trip_desc(tz, td) { td->trip = *trip++; + INIT_LIST_HEAD(&td->thermal_instances); /* * Mark all thresholds as invalid to start with even though * this only matters for the trips that start as invalid and diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index a28f7198f9176..19a698bdbfba8 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -31,6 +31,7 @@ struct thermal_trip_desc { struct thermal_trip trip; struct thermal_trip_attrs trip_attrs; struct list_head notify_list_node; + struct list_head thermal_instances; int notify_temp; int threshold; }; @@ -100,7 +101,6 @@ struct thermal_governor { * @tzp: thermal zone parameters * @governor: pointer to the governor for this thermal zone * @governor_data: private pointer for governor data - * @thermal_instances: list of &struct thermal_instance of this thermal zone * @ida: &struct ida to generate unique id for this zone's cooling * devices * @lock: lock to protect thermal_instances list @@ -133,7 +133,6 @@ struct thermal_zone_device { struct thermal_zone_params *tzp; struct thermal_governor *governor; void *governor_data; - struct list_head thermal_instances; struct ida ida; struct mutex lock; struct list_head node; @@ -231,7 +230,7 @@ struct thermal_instance { struct device_attribute attr; char weight_attr_name[THERMAL_NAME_LENGTH]; struct device_attribute weight_attr; - struct list_head tz_node; /* node in tz->thermal_instances */ + struct list_head trip_node; /* node in trip->thermal_instances */ struct list_head cdev_node; /* node in cdev->thermal_instances */ unsigned int weight; /* The weight of the cooling device */ bool upper_no_limit; diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index dc374a7a1a659..403d62d3ce77e 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -43,10 +43,11 @@ static bool thermal_instance_present(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, const struct thermal_trip *trip) { + const struct thermal_trip_desc *td = trip_to_trip_desc(trip); struct thermal_instance *ti; - list_for_each_entry(ti, &tz->thermal_instances, tz_node) { - if (ti->trip == trip && ti->cdev == cdev) + list_for_each_entry(ti, &td->thermal_instances, trip_node) { + if (ti->cdev == cdev) return true; } From 17f76be51c52338d9632070cce2b181a7517f03d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:42:19 +0200 Subject: [PATCH 14/45] thermal: core: Pass trip descriptors to trip bind/unbind functions The code is somewhat cleaner if struct thermal_trip_desc pointers are passed to thermal_bind_cdev_to_trip(), thermal_unbind_cdev_from_trip(), and print_bind_err_msg() instead of struct thermal_trip pointers, so modify it accordingly. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2246211.NgBsaNRSFp@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 363e01e12ec1b..4ea304e3ceb73 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -759,9 +759,9 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) /** * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone * @tz: pointer to struct thermal_zone_device - * @trip: trip point the cooling devices is associated with in this zone. + * @td: descriptor of the trip point to bind @cdev to * @cdev: pointer to struct thermal_cooling_device - * @cool_spec: cooling specification for @trip and @cdev + * @cool_spec: cooling specification for the trip point and @cdev * * This interface function bind a thermal cooling device to the certain trip * point of a thermal zone device. @@ -770,11 +770,10 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) * Return: 0 on success, the proper error value otherwise. */ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, - struct thermal_trip *trip, + struct thermal_trip_desc *td, struct thermal_cooling_device *cdev, struct cooling_spec *cool_spec) { - struct thermal_trip_desc *td = trip_to_trip_desc(trip); struct thermal_instance *dev, *instance; bool upper_no_limit; int result; @@ -798,7 +797,7 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, return -ENOMEM; dev->cdev = cdev; - dev->trip = trip; + dev->trip = &td->trip; dev->upper = cool_spec->upper; dev->upper_no_limit = upper_no_limit; dev->lower = cool_spec->lower; @@ -869,7 +868,7 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, /** * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. * @tz: pointer to a struct thermal_zone_device. - * @trip: trip point the cooling devices is associated with in this zone. + * @td: descriptor of the trip point to unbind @cdev from * @cdev: pointer to a struct thermal_cooling_device. * * This interface function unbind a thermal cooling device from the certain @@ -877,10 +876,9 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, * This function is usually called in the thermal zone device .unbind callback. */ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, - struct thermal_trip *trip, + struct thermal_trip_desc *td, struct thermal_cooling_device *cdev) { - struct thermal_trip_desc *td = trip_to_trip_desc(trip); struct thermal_instance *pos, *next; mutex_lock(&cdev->lock); @@ -932,11 +930,11 @@ static struct class *thermal_class; static inline void print_bind_err_msg(struct thermal_zone_device *tz, - const struct thermal_trip *trip, + const struct thermal_trip_desc *td, struct thermal_cooling_device *cdev, int ret) { dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n", - cdev->type, thermal_zone_trip_id(tz, trip), ret); + cdev->type, thermal_zone_trip_id(tz, &td->trip), ret); } static bool __thermal_zone_cdev_bind(struct thermal_zone_device *tz, @@ -949,7 +947,6 @@ static bool __thermal_zone_cdev_bind(struct thermal_zone_device *tz, return false; for_each_trip_desc(tz, td) { - struct thermal_trip *trip = &td->trip; struct cooling_spec c = { .upper = THERMAL_NO_LIMIT, .lower = THERMAL_NO_LIMIT, @@ -957,12 +954,12 @@ static bool __thermal_zone_cdev_bind(struct thermal_zone_device *tz, }; int ret; - if (!tz->ops.should_bind(tz, trip, cdev, &c)) + if (!tz->ops.should_bind(tz, &td->trip, cdev, &c)) continue; - ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c); + ret = thermal_bind_cdev_to_trip(tz, td, cdev, &c); if (ret) { - print_bind_err_msg(tz, trip, cdev, ret); + print_bind_err_msg(tz, td, cdev, ret); continue; } @@ -1281,7 +1278,7 @@ static void __thermal_zone_cdev_unbind(struct thermal_zone_device *tz, struct thermal_trip_desc *td; for_each_trip_desc(tz, td) - thermal_unbind_cdev_from_trip(tz, &td->trip, cdev); + thermal_unbind_cdev_from_trip(tz, td, cdev); } static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, From cba00d16a2b0b0cfc8266124266ed23ab9eae918 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:05:25 +0200 Subject: [PATCH 15/45] thermal: core: Add and use thermal zone guard Add and use a guard for thermal zone locking. This allows quite a few error code paths to be simplified among other things and brings in a noticeable code size reduction for a good measure. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1930069.tdWV9SEqCh@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 61 +++++++---------------- drivers/thermal/thermal_core.h | 4 ++ drivers/thermal/thermal_debugfs.c | 25 ++++++---- drivers/thermal/thermal_helpers.c | 17 +++---- drivers/thermal/thermal_hwmon.c | 5 +- drivers/thermal/thermal_netlink.c | 21 +++----- drivers/thermal/thermal_sysfs.c | 81 +++++++++++++------------------ drivers/thermal/thermal_trip.c | 8 +-- 8 files changed, 86 insertions(+), 136 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 4ea304e3ceb73..d1038d33f17e4 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -202,16 +202,13 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *tz, int ret = -EINVAL; mutex_lock(&thermal_governor_lock); - mutex_lock(&tz->lock); - gov = __find_governor(strim(policy)); - if (!gov) - goto exit; + guard(thermal_zone)(tz); - ret = thermal_set_governor(tz, gov); + gov = __find_governor(strim(policy)); + if (gov) + ret = thermal_set_governor(tz, gov); -exit: - mutex_unlock(&tz->lock); mutex_unlock(&thermal_governor_lock); thermal_notify_tz_gov_change(tz, policy); @@ -617,26 +614,18 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, { int ret; - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); /* do nothing if mode isn't changing */ - if (mode == tz->mode) { - mutex_unlock(&tz->lock); - + if (mode == tz->mode) return 0; - } ret = __thermal_zone_device_set_mode(tz, mode); - if (ret) { - mutex_unlock(&tz->lock); - + if (ret) return ret; - } __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - mutex_unlock(&tz->lock); - if (mode == THERMAL_DEVICE_ENABLED) thermal_notify_tz_enable(tz); else @@ -665,10 +654,10 @@ static bool thermal_zone_is_present(struct thermal_zone_device *tz) void thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); + if (thermal_zone_is_present(tz)) __thermal_zone_device_update(tz, event); - mutex_unlock(&tz->lock); } EXPORT_SYMBOL_GPL(thermal_zone_device_update); @@ -972,12 +961,10 @@ static bool __thermal_zone_cdev_bind(struct thermal_zone_device *tz, static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) { - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); if (__thermal_zone_cdev_bind(tz, cdev)) __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - - mutex_unlock(&tz->lock); } /** @@ -1284,11 +1271,9 @@ static void __thermal_zone_cdev_unbind(struct thermal_zone_device *tz, static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) { - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); __thermal_zone_cdev_unbind(tz, cdev); - - mutex_unlock(&tz->lock); } /** @@ -1334,7 +1319,7 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) if (tz->ops.get_crit_temp) return tz->ops.get_crit_temp(tz, temp); - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); for_each_trip_desc(tz, td) { const struct thermal_trip *trip = &td->trip; @@ -1346,8 +1331,6 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) } } - mutex_unlock(&tz->lock); - return ret; } EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); @@ -1360,7 +1343,7 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz) list_add_tail(&tz->node, &thermal_tz_list); - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); /* Bind cooling devices for this zone. */ list_for_each_entry(cdev, &thermal_cdev_list, node) @@ -1377,8 +1360,6 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz) __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - mutex_unlock(&tz->lock); - mutex_unlock(&thermal_list_lock); } @@ -1615,7 +1596,7 @@ static bool thermal_zone_exit(struct thermal_zone_device *tz) goto unlock; } - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); tz->state |= TZ_STATE_FLAG_EXIT; list_del_init(&tz->node); @@ -1624,8 +1605,6 @@ static bool thermal_zone_exit(struct thermal_zone_device *tz) list_for_each_entry(cdev, &thermal_cdev_list, node) __thermal_zone_cdev_unbind(tz, cdev); - mutex_unlock(&tz->lock); - unlock: mutex_unlock(&thermal_list_lock); @@ -1710,7 +1689,7 @@ static void thermal_zone_device_resume(struct work_struct *work) tz = container_of(work, struct thermal_zone_device, poll_queue.work); - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING); @@ -1720,13 +1699,11 @@ static void thermal_zone_device_resume(struct work_struct *work) __thermal_zone_device_update(tz, THERMAL_TZ_RESUME); complete(&tz->resume); - - mutex_unlock(&tz->lock); } static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) { - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); if (tz->state & TZ_STATE_FLAG_RESUMING) { /* @@ -1742,13 +1719,11 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) } tz->state |= TZ_STATE_FLAG_SUSPENDED; - - mutex_unlock(&tz->lock); } static void thermal_zone_pm_complete(struct thermal_zone_device *tz) { - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); cancel_delayed_work(&tz->poll_queue); @@ -1762,8 +1737,6 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz) INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume); /* Queue up the work without a delay. */ mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0); - - mutex_unlock(&tz->lock); } static int thermal_pm_notify(struct notifier_block *nb, diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 19a698bdbfba8..09f796fb1278d 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -9,6 +9,7 @@ #ifndef __THERMAL_CORE_H__ #define __THERMAL_CORE_H__ +#include #include #include @@ -146,6 +147,9 @@ struct thermal_zone_device { struct thermal_trip_desc trips[] __counted_by(num_trips); }; +DEFINE_GUARD(thermal_zone, struct thermal_zone_device *, mutex_lock(&_T->lock), + mutex_unlock(&_T->lock)) + /* Initial thermal zone temperature. */ #define THERMAL_TEMP_INIT INT_MIN diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 939d3e5f18177..d67021aeb1367 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -885,6 +885,19 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz) tz->debugfs = thermal_dbg; } +static struct thermal_debugfs *thermal_debug_tz_clear(struct thermal_zone_device *tz) +{ + struct thermal_debugfs *thermal_dbg; + + guard(thermal_zone)(tz); + + thermal_dbg = tz->debugfs; + if (thermal_dbg) + tz->debugfs = NULL; + + return thermal_dbg; +} + void thermal_debug_tz_remove(struct thermal_zone_device *tz) { struct thermal_debugfs *thermal_dbg; @@ -892,17 +905,9 @@ void thermal_debug_tz_remove(struct thermal_zone_device *tz) struct tz_debugfs *tz_dbg; int *trips_crossed; - mutex_lock(&tz->lock); - - thermal_dbg = tz->debugfs; - if (!thermal_dbg) { - mutex_unlock(&tz->lock); + thermal_dbg = thermal_debug_tz_clear(tz); + if (!thermal_dbg) return; - } - - tz->debugfs = NULL; - - mutex_unlock(&tz->lock); tz_dbg = &thermal_dbg->tz_dbg; diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index 403d62d3ce77e..f2baadd8f8dad 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -60,13 +60,13 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, { bool ret; - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); + mutex_lock(&cdev->lock); ret = thermal_instance_present(tz, cdev, trip); mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); return ret; } @@ -138,19 +138,14 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) if (IS_ERR_OR_NULL(tz)) return -EINVAL; - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); - if (!tz->ops.get_temp) { - ret = -EINVAL; - goto unlock; - } + if (!tz->ops.get_temp) + return -EINVAL; ret = __thermal_zone_get_temp(tz, temp); if (!ret && *temp <= THERMAL_TEMP_INVALID) - ret = -ENODATA; - -unlock: - mutex_unlock(&tz->lock); + return -ENODATA; return ret; } diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index f0e504fd866ae..37da7a8ea948c 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -78,12 +78,9 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) int temperature; int ret; - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); ret = tz->ops.get_crit_temp(tz, &temperature); - - mutex_unlock(&tz->lock); - if (ret) return ret; diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index f3c58c708969c..91f3fe8c8f071 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -459,7 +459,7 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p) if (!start_trip) return -EMSGSIZE; - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); for_each_trip_desc(tz, td) { const struct thermal_trip *trip = &td->trip; @@ -469,19 +469,12 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p) nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) || nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, trip->temperature) || nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, trip->hysteresis)) - goto out_cancel_nest; + return -EMSGSIZE; } - mutex_unlock(&tz->lock); - nla_nest_end(msg, start_trip); return 0; - -out_cancel_nest: - mutex_unlock(&tz->lock); - - return -EMSGSIZE; } static int thermal_genl_cmd_tz_get_temp(struct param *p) @@ -512,7 +505,7 @@ static int thermal_genl_cmd_tz_get_temp(struct param *p) static int thermal_genl_cmd_tz_get_gov(struct param *p) { struct sk_buff *msg = p->msg; - int id, ret = 0; + int id; if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) return -EINVAL; @@ -523,16 +516,14 @@ static int thermal_genl_cmd_tz_get_gov(struct param *p) if (!tz) return -EINVAL; - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) || nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME, tz->governor->name)) - ret = -EMSGSIZE; - - mutex_unlock(&tz->lock); + return -EMSGSIZE; - return ret; + return 0; } static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev, diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 1838aa729bb50..701607a953ff4 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -50,13 +50,13 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct thermal_zone_device *tz = to_thermal_zone(dev); - int enabled; - mutex_lock(&tz->lock); - enabled = tz->mode == THERMAL_DEVICE_ENABLED; - mutex_unlock(&tz->lock); + guard(thermal_zone)(tz); - return sprintf(buf, "%s\n", enabled ? "enabled" : "disabled"); + if (tz->mode == THERMAL_DEVICE_ENABLED) + return sprintf(buf, "enabled\n"); + + return sprintf(buf, "disabled\n"); } static ssize_t @@ -103,38 +103,34 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, { struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); struct thermal_zone_device *tz = to_thermal_zone(dev); - int ret, temp; + int temp; - ret = kstrtoint(buf, 10, &temp); - if (ret) + if (kstrtoint(buf, 10, &temp)) return -EINVAL; - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); if (temp == trip->temperature) - goto unlock; + return count; /* Arrange the condition to avoid integer overflows. */ if (temp != THERMAL_TEMP_INVALID && - temp <= trip->hysteresis + THERMAL_TEMP_INVALID) { - ret = -EINVAL; - goto unlock; - } + temp <= trip->hysteresis + THERMAL_TEMP_INVALID) + return -EINVAL; if (tz->ops.set_trip_temp) { + int ret; + ret = tz->ops.set_trip_temp(tz, trip, temp); if (ret) - goto unlock; + return ret; } thermal_zone_set_trip_temp(tz, trip, temp); __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); -unlock: - mutex_unlock(&tz->lock); - - return ret ? ret : count; + return count; } static ssize_t @@ -152,16 +148,15 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, { struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); struct thermal_zone_device *tz = to_thermal_zone(dev); - int ret, hyst; + int hyst; - ret = kstrtoint(buf, 10, &hyst); - if (ret || hyst < 0) + if (kstrtoint(buf, 10, &hyst) || hyst < 0) return -EINVAL; - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); if (hyst == trip->hysteresis) - goto unlock; + return count; /* * Allow the hysteresis to be updated when the temperature is invalid @@ -171,22 +166,17 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, */ if (trip->temperature == THERMAL_TEMP_INVALID) { WRITE_ONCE(trip->hysteresis, hyst); - goto unlock; + return count; } - if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) { - ret = -EINVAL; - goto unlock; - } + if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) + return -EINVAL; thermal_zone_set_trip_hyst(tz, trip, hyst); __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); -unlock: - mutex_unlock(&tz->lock); - - return ret ? ret : count; + return count; } static ssize_t @@ -236,25 +226,26 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct thermal_zone_device *tz = to_thermal_zone(dev); - int ret = 0; int temperature; if (kstrtoint(buf, 10, &temperature)) return -EINVAL; - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); - if (!tz->ops.set_emul_temp) - tz->emul_temperature = temperature; - else - ret = tz->ops.set_emul_temp(tz, temperature); + if (tz->ops.set_emul_temp) { + int ret; - if (!ret) - __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + ret = tz->ops.set_emul_temp(tz, temperature); + if (ret) + return ret; + } else { + tz->emul_temperature = temperature; + } - mutex_unlock(&tz->lock); + __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - return ret ? ret : count; + return count; } static DEVICE_ATTR_WO(emul_temp); #endif @@ -894,13 +885,11 @@ ssize_t weight_store(struct device *dev, struct device_attribute *attr, instance = container_of(attr, struct thermal_instance, weight_attr); /* Don't race with governors using the 'weight' value */ - mutex_lock(&tz->lock); + guard(thermal_zone)(tz); instance->weight = weight; thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED); - mutex_unlock(&tz->lock); - return count; } diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index b53fac333ec5d..09474453baa58 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -45,13 +45,9 @@ int thermal_zone_for_each_trip(struct thermal_zone_device *tz, int (*cb)(struct thermal_trip *, void *), void *data) { - int ret; - - mutex_lock(&tz->lock); - ret = for_each_thermal_trip(tz, cb, data); - mutex_unlock(&tz->lock); + guard(thermal_zone)(tz); - return ret; + return for_each_thermal_trip(tz, cb, data); } EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip); From 57f076664c4debb2fff57e42098698f1993826f4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:07:11 +0200 Subject: [PATCH 16/45] thermal: core: Add and use a reverse thermal zone guard Add a guard for unlocking a locked thermal zone temporarily and use it in thermal_zone_pm_prepare(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3344086.aeNJFYEL58@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 8 +++----- drivers/thermal/thermal_core.h | 3 +++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d1038d33f17e4..b8cd84a8c86cf 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1711,11 +1711,9 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) * acquired the lock yet, so release it to let the function run * and wait util it has done the work. */ - mutex_unlock(&tz->lock); - - wait_for_completion(&tz->resume); - - mutex_lock(&tz->lock); + scoped_guard(thermal_zone_reverse, tz) { + wait_for_completion(&tz->resume); + } } tz->state |= TZ_STATE_FLAG_SUSPENDED; diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 09f796fb1278d..dc84862017ea5 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -150,6 +150,9 @@ struct thermal_zone_device { DEFINE_GUARD(thermal_zone, struct thermal_zone_device *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock)) +DEFINE_GUARD(thermal_zone_reverse, struct thermal_zone_device *, + mutex_unlock(&_T->lock), mutex_lock(&_T->lock)) + /* Initial thermal zone temperature. */ #define THERMAL_TEMP_INIT INT_MIN From 6f60ae72214bd57b2576c749aca5305eca158dc3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:09:18 +0200 Subject: [PATCH 17/45] thermal: core: Separate code running under thermal_list_lock To prepare for a subsequent change that will switch over the thermal core to using a mutex guard for thermal_list_lock management, move the code running under thermal_list_lock during the initialization and unregistration of cooling devices into separate functions. While at it, drop some comments that do not add value. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/10572828.nUPlyArG6x@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 64 +++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index b8cd84a8c86cf..08c227ac3d536 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -967,6 +967,20 @@ static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } +static void thermal_cooling_device_init_complete(struct thermal_cooling_device *cdev) +{ + struct thermal_zone_device *tz; + + mutex_lock(&thermal_list_lock); + + list_add(&cdev->node, &thermal_cdev_list); + + list_for_each_entry(tz, &thermal_tz_list, node) + thermal_zone_cdev_bind(tz, cdev); + + mutex_unlock(&thermal_list_lock); +} + /** * __thermal_cooling_device_register() - register a new thermal cooling device * @np: a pointer to a device tree node. @@ -989,7 +1003,6 @@ __thermal_cooling_device_register(struct device_node *np, const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device *cdev; - struct thermal_zone_device *pos; unsigned long current_state; int id, ret; @@ -1056,16 +1069,7 @@ __thermal_cooling_device_register(struct device_node *np, if (current_state <= cdev->max_state) thermal_debug_cdev_add(cdev, current_state); - /* Add 'this' new cdev to the global cdev list */ - mutex_lock(&thermal_list_lock); - - list_add(&cdev->node, &thermal_cdev_list); - - /* Update binding information for 'this' new cdev */ - list_for_each_entry(pos, &thermal_tz_list, node) - thermal_zone_cdev_bind(pos, cdev); - - mutex_unlock(&thermal_list_lock); + thermal_cooling_device_init_complete(cdev); return cdev; @@ -1276,38 +1280,42 @@ static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, __thermal_zone_cdev_unbind(tz, cdev); } -/** - * thermal_cooling_device_unregister - removes a thermal cooling device - * @cdev: the thermal cooling device to remove. - * - * thermal_cooling_device_unregister() must be called when a registered - * thermal cooling device is no longer needed. - */ -void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) +static bool thermal_cooling_device_exit(struct thermal_cooling_device *cdev) { struct thermal_zone_device *tz; - - if (!cdev) - return; - - thermal_debug_cdev_remove(cdev); + bool ret = true; mutex_lock(&thermal_list_lock); if (!thermal_cooling_device_present(cdev)) { - mutex_unlock(&thermal_list_lock); - return; + ret = false; + goto unlock; } list_del(&cdev->node); - /* Unbind all thermal zones associated with 'this' cdev */ list_for_each_entry(tz, &thermal_tz_list, node) thermal_zone_cdev_unbind(tz, cdev); +unlock: mutex_unlock(&thermal_list_lock); - device_unregister(&cdev->device); + return ret; +} + +/** + * thermal_cooling_device_unregister() - removes a thermal cooling device + * @cdev: Thermal cooling device to remove. + */ +void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) +{ + if (!cdev) + return; + + thermal_debug_cdev_remove(cdev); + + if (thermal_cooling_device_exit(cdev)) + device_unregister(&cdev->device); } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); From d1c8aa2a5c5c624f25efc78000ea9801948aea15 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:10:47 +0200 Subject: [PATCH 18/45] thermal: core: Manage thermal_list_lock using a mutex guard Switch over the thermal core to using a mutex guard for thermal_list_lock management. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2010397.PYKUYFuaPT@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 148 +++++++++++++++------------------ 1 file changed, 68 insertions(+), 80 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 08c227ac3d536..a78f9a81595fc 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -140,7 +140,7 @@ int thermal_register_governor(struct thermal_governor *governor) def_governor = governor; } - mutex_lock(&thermal_list_lock); + guard(mutex)(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) { /* @@ -163,7 +163,6 @@ int thermal_register_governor(struct thermal_governor *governor) } } - mutex_unlock(&thermal_list_lock); mutex_unlock(&thermal_governor_lock); return err; @@ -181,7 +180,9 @@ void thermal_unregister_governor(struct thermal_governor *governor) if (!__find_governor(governor->name)) goto exit; - mutex_lock(&thermal_list_lock); + list_del(&governor->governor_list); + + guard(mutex)(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) { if (!strncasecmp(pos->governor->name, governor->name, @@ -189,8 +190,6 @@ void thermal_unregister_governor(struct thermal_governor *governor) thermal_set_governor(pos, NULL); } - mutex_unlock(&thermal_list_lock); - list_del(&governor->governor_list); exit: mutex_unlock(&thermal_governor_lock); } @@ -688,51 +687,52 @@ int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, void *), void *data) { struct thermal_cooling_device *cdev; - int ret = 0; - mutex_lock(&thermal_list_lock); + guard(mutex)(&thermal_list_lock); + list_for_each_entry(cdev, &thermal_cdev_list, node) { + int ret; + ret = cb(cdev, data); if (ret) - break; + return ret; } - mutex_unlock(&thermal_list_lock); - return ret; + return 0; } int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *), void *data) { struct thermal_zone_device *tz; - int ret = 0; - mutex_lock(&thermal_list_lock); + guard(mutex)(&thermal_list_lock); + list_for_each_entry(tz, &thermal_tz_list, node) { + int ret; + ret = cb(tz, data); if (ret) - break; + return ret; } - mutex_unlock(&thermal_list_lock); - return ret; + return 0; } struct thermal_zone_device *thermal_zone_get_by_id(int id) { - struct thermal_zone_device *tz, *match = NULL; + struct thermal_zone_device *tz; + + guard(mutex)(&thermal_list_lock); - mutex_lock(&thermal_list_lock); list_for_each_entry(tz, &thermal_tz_list, node) { if (tz->id == id) { get_device(&tz->device); - match = tz; - break; + return tz; } } - mutex_unlock(&thermal_list_lock); - return match; + return NULL; } /* @@ -971,14 +971,12 @@ static void thermal_cooling_device_init_complete(struct thermal_cooling_device * { struct thermal_zone_device *tz; - mutex_lock(&thermal_list_lock); + guard(mutex)(&thermal_list_lock); list_add(&cdev->node, &thermal_cdev_list); list_for_each_entry(tz, &thermal_tz_list, node) thermal_zone_cdev_bind(tz, cdev); - - mutex_unlock(&thermal_list_lock); } /** @@ -1212,10 +1210,10 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) * Hold thermal_list_lock throughout the update to prevent the device * from going away while being updated. */ - mutex_lock(&thermal_list_lock); + guard(mutex)(&thermal_list_lock); if (!thermal_cooling_device_present(cdev)) - goto unlock_list; + return; /* * Update under the cdev lock to prevent the state from being set beyond @@ -1257,9 +1255,6 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) unlock: mutex_unlock(&cdev->lock); - -unlock_list: - mutex_unlock(&thermal_list_lock); } EXPORT_SYMBOL_GPL(thermal_cooling_device_update); @@ -1283,24 +1278,18 @@ static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, static bool thermal_cooling_device_exit(struct thermal_cooling_device *cdev) { struct thermal_zone_device *tz; - bool ret = true; - mutex_lock(&thermal_list_lock); + guard(mutex)(&thermal_list_lock); - if (!thermal_cooling_device_present(cdev)) { - ret = false; - goto unlock; - } + if (!thermal_cooling_device_present(cdev)) + return false; list_del(&cdev->node); list_for_each_entry(tz, &thermal_tz_list, node) thermal_zone_cdev_unbind(tz, cdev); -unlock: - mutex_unlock(&thermal_list_lock); - - return ret; + return true; } /** @@ -1347,7 +1336,7 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz) { struct thermal_cooling_device *cdev; - mutex_lock(&thermal_list_lock); + guard(mutex)(&thermal_list_lock); list_add_tail(&tz->node, &thermal_tz_list); @@ -1367,8 +1356,6 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz) tz->state |= TZ_STATE_FLAG_SUSPENDED; __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - - mutex_unlock(&thermal_list_lock); } /** @@ -1595,14 +1582,11 @@ EXPORT_SYMBOL_GPL(thermal_zone_device); static bool thermal_zone_exit(struct thermal_zone_device *tz) { struct thermal_cooling_device *cdev; - bool ret = true; - mutex_lock(&thermal_list_lock); + guard(mutex)(&thermal_list_lock); - if (list_empty(&tz->node)) { - ret = false; - goto unlock; - } + if (list_empty(&tz->node)) + return false; guard(thermal_zone)(tz); @@ -1613,10 +1597,7 @@ static bool thermal_zone_exit(struct thermal_zone_device *tz) list_for_each_entry(cdev, &thermal_cdev_list, node) __thermal_zone_cdev_unbind(tz, cdev); -unlock: - mutex_unlock(&thermal_list_lock); - - return ret; + return true; } /** @@ -1669,24 +1650,23 @@ struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name) unsigned int found = 0; if (!name) - goto exit; + return ERR_PTR(-EINVAL); + + guard(mutex)(&thermal_list_lock); - mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) { found++; ref = pos; } - mutex_unlock(&thermal_list_lock); - /* nothing has been found, thus an error code for it */ - if (found == 0) - ref = ERR_PTR(-ENODEV); - else if (found > 1) - /* Success only when an unique zone is found */ - ref = ERR_PTR(-EEXIST); + if (!found) + return ERR_PTR(-ENODEV); + + /* Success only when one zone is found. */ + if (found > 1) + return ERR_PTR(-EEXIST); -exit: return ref; } EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); @@ -1727,6 +1707,18 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) tz->state |= TZ_STATE_FLAG_SUSPENDED; } +static void thermal_pm_notify_prepare(void) +{ + struct thermal_zone_device *tz; + + guard(mutex)(&thermal_list_lock); + + thermal_pm_suspended = true; + + list_for_each_entry(tz, &thermal_tz_list, node) + thermal_zone_pm_prepare(tz); +} + static void thermal_zone_pm_complete(struct thermal_zone_device *tz) { guard(thermal_zone)(tz); @@ -1745,35 +1737,31 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz) mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0); } -static int thermal_pm_notify(struct notifier_block *nb, - unsigned long mode, void *_unused) +static void thermal_pm_notify_complete(void) { struct thermal_zone_device *tz; + guard(mutex)(&thermal_list_lock); + + thermal_pm_suspended = false; + + list_for_each_entry(tz, &thermal_tz_list, node) + 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: - mutex_lock(&thermal_list_lock); - - thermal_pm_suspended = true; - - list_for_each_entry(tz, &thermal_tz_list, node) - thermal_zone_pm_prepare(tz); - - mutex_unlock(&thermal_list_lock); + thermal_pm_notify_prepare(); break; case PM_POST_HIBERNATION: case PM_POST_RESTORE: case PM_POST_SUSPEND: - mutex_lock(&thermal_list_lock); - - thermal_pm_suspended = false; - - list_for_each_entry(tz, &thermal_tz_list, node) - thermal_zone_pm_complete(tz); - - mutex_unlock(&thermal_list_lock); + thermal_pm_notify_complete(); break; default: break; From 33eab804d695dc2be1491890143a64ea4d99cd42 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:12:11 +0200 Subject: [PATCH 19/45] thermal: core: Call thermal_governor_update_tz() outside of cdev lock Holding a cooling device lock under thermal_governor_update_tz() is not necessary and it may cause lockdep to complain if any governor's .update_tz() callback attempts to lock a cdev. For this reason, move the thermal_governor_update_tz() calls in thermal_bind_cdev_to_trip() and thermal_unbind_cdev_from_trip() from under the cdev lock. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/7749552.EvYhyI6sBW@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index a78f9a81595fc..cc1fd230b1001 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -834,13 +834,13 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, if (!result) { list_add_tail(&dev->trip_node, &td->thermal_instances); list_add_tail(&dev->cdev_node, &cdev->thermal_instances); - - thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); } mutex_unlock(&cdev->lock); - if (!result) + if (!result) { + thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); return 0; + } device_remove_file(&tz->device, &dev->weight_attr); remove_trip_file: @@ -875,9 +875,6 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, if (pos->cdev == cdev) { list_del(&pos->trip_node); list_del(&pos->cdev_node); - - thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); - mutex_unlock(&cdev->lock); goto unbind; } @@ -887,6 +884,8 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, return; unbind: + thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); + device_remove_file(&tz->device, &pos->weight_attr); device_remove_file(&tz->device, &pos->attr); sysfs_remove_link(&tz->device.kobj, pos->name); From 6d153f52cc3452258415db6a202cc6d15daa3920 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:13:50 +0200 Subject: [PATCH 20/45] thermal: core: Introduce thermal_instance_add() To reduce the number of redundant result checks in thermal_bind_cdev_to_trip() and make the code in it easier to follow, move some of it to a new function called thermal_instance_add() and make thermal_bind_cdev_to_trip() invoke that function. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3618899.iIbC2pHGDl@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 46 ++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index cc1fd230b1001..ae424eef45749 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -745,6 +745,28 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) * binding, and unbinding. */ +static int thermal_instance_add(struct thermal_instance *new_instance, + struct thermal_cooling_device *cdev, + struct thermal_trip_desc *td) +{ + struct thermal_instance *instance; + + list_for_each_entry(instance, &td->thermal_instances, trip_node) { + if (instance->cdev == cdev) + return -EEXIST; + } + + list_add_tail(&new_instance->trip_node, &td->thermal_instances); + + mutex_lock(&cdev->lock); + + list_add_tail(&new_instance->cdev_node, &cdev->thermal_instances); + + mutex_unlock(&cdev->lock); + + return 0; +} + /** * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone * @tz: pointer to struct thermal_zone_device @@ -763,7 +785,7 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, struct cooling_spec *cool_spec) { - struct thermal_instance *dev, *instance; + struct thermal_instance *dev; bool upper_no_limit; int result; @@ -825,23 +847,15 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, if (result) goto remove_trip_file; - mutex_lock(&cdev->lock); - list_for_each_entry(instance, &td->thermal_instances, trip_node) - if (instance->cdev == cdev) { - result = -EEXIST; - break; - } - if (!result) { - list_add_tail(&dev->trip_node, &td->thermal_instances); - list_add_tail(&dev->cdev_node, &cdev->thermal_instances); - } - mutex_unlock(&cdev->lock); + result = thermal_instance_add(dev, cdev, td); + if (result) + goto remove_weight_file; - if (!result) { - thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); - return 0; - } + thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); + + return 0; +remove_weight_file: device_remove_file(&tz->device, &dev->weight_attr); remove_trip_file: device_remove_file(&tz->device, &dev->attr); From c690dce5dceef1d2818c52f3df2d3511f028632f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:15:22 +0200 Subject: [PATCH 21/45] thermal: core: Introduce thermal_instance_delete() It is not necessary to walk the thermal_instances list in a trip descriptor under a cooling device lock, so acquire that lock only for deleting the given thermal instance from the list of thermal instances in the given cdev. Moreover, in analogy with the previous change that introduced thermal_instance_add(), put the code deleting the given thermal instance from the lists it is on into a separate new function called thermal_instance_delete(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3275745.5fSG56mABF@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index ae424eef45749..ab0c7c6396c02 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -868,6 +868,17 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, return result; } +static void thermal_instance_delete(struct thermal_instance *instance) +{ + list_del(&instance->trip_node); + + mutex_lock(&instance->cdev->lock); + + list_del(&instance->cdev_node); + + mutex_unlock(&instance->cdev->lock); +} + /** * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. * @tz: pointer to a struct thermal_zone_device. @@ -884,16 +895,12 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, { struct thermal_instance *pos, *next; - mutex_lock(&cdev->lock); list_for_each_entry_safe(pos, next, &td->thermal_instances, trip_node) { if (pos->cdev == cdev) { - list_del(&pos->trip_node); - list_del(&pos->cdev_node); - mutex_unlock(&cdev->lock); + thermal_instance_delete(pos); goto unbind; } } - mutex_unlock(&cdev->lock); return; From c597b4e74b124aebf7e3d3f7907e2d4779d870e3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:16:54 +0200 Subject: [PATCH 22/45] thermal: core: Introduce thermal_cdev_update_nocheck() Three thermal governors call __thermal_cdev_update() under the cdev lock without doing any checks, so in order to reduce the related code duplication, introduce a new helper function called thermal_cdev_update_nocheck() for them and make them use it. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1835097.VLH7GnMWUR@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/gov_bang_bang.c | 4 +--- drivers/thermal/gov_fair_share.c | 4 +--- drivers/thermal/gov_power_allocator.c | 5 ++--- drivers/thermal/thermal_core.h | 1 + drivers/thermal/thermal_helpers.c | 13 +++++++++++++ 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index b887e48e8c7e6..97f3d819852b2 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -30,9 +30,7 @@ static void bang_bang_set_instance_target(struct thermal_instance *instance, dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); - mutex_lock(&instance->cdev->lock); - __thermal_cdev_update(instance->cdev); - mutex_unlock(&instance->cdev->lock); + thermal_cdev_update_nocheck(instance->cdev); } /** diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c index d37d57d48c389..4643be4f941db 100644 --- a/drivers/thermal/gov_fair_share.c +++ b/drivers/thermal/gov_fair_share.c @@ -89,9 +89,7 @@ static void fair_share_throttle(struct thermal_zone_device *tz, } instance->target = div_u64(dividend, divisor); - mutex_lock(&cdev->lock); - __thermal_cdev_update(cdev); - mutex_unlock(&cdev->lock); + thermal_cdev_update_nocheck(cdev); } } diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index 46c81b147662c..bdae60001b2c9 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -322,9 +322,8 @@ power_actor_set_power(struct thermal_cooling_device *cdev, return ret; instance->target = clamp_val(state, instance->lower, instance->upper); - mutex_lock(&cdev->lock); - __thermal_cdev_update(cdev); - mutex_unlock(&cdev->lock); + + thermal_cdev_update_nocheck(cdev); return 0; } diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index dc84862017ea5..04d45cd97614f 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -215,6 +215,7 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) } void thermal_cdev_update(struct thermal_cooling_device *); +void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev); void __thermal_cdev_update(struct thermal_cooling_device *cdev); int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip); diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index f2baadd8f8dad..030a4e4bc824c 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -205,6 +205,19 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) mutex_unlock(&cdev->lock); } +/** + * thermal_cdev_update_nocheck() - Unconditionally update cooling device state + * @cdev: Target cooling device. + */ +void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev) +{ + mutex_lock(&cdev->lock); + + __thermal_cdev_update(cdev); + + mutex_unlock(&cdev->lock); +} + /** * thermal_zone_get_slope - return the slope attribute of the thermal zone * @tz: thermal zone device with the slope attribute From a5a98a786e5e31e85a69d30935254e8730734706 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 14 Oct 2024 16:59:46 +0200 Subject: [PATCH 23/45] thermal: core: Add and use cooling device guard Add and use a special guard for cooling devices. This allows quite a few error code paths to be simplified among other things and brings in code size reduction for a good measure. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5837621.DvuYhMxLoT@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/gov_power_allocator.c | 23 ++++++------ drivers/thermal/gov_step_wise.c | 6 ++-- drivers/thermal/thermal_core.c | 17 +++------ drivers/thermal/thermal_debugfs.c | 25 +++++++------ drivers/thermal/thermal_helpers.c | 19 +++------- drivers/thermal/thermal_sysfs.c | 51 ++++++++++----------------- include/linux/thermal.h | 3 ++ 7 files changed, 60 insertions(+), 84 deletions(-) diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index bdae60001b2c9..5cb03923fa8f2 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -549,18 +549,17 @@ static void allow_maximum_power(struct thermal_zone_device *tz) cdev = instance->cdev; instance->target = 0; - mutex_lock(&cdev->lock); - /* - * Call for updating the cooling devices local stats and avoid - * periods of dozen of seconds when those have not been - * maintained. - */ - cdev->ops->get_requested_power(cdev, &req_power); - - if (params->update_cdevs) - __thermal_cdev_update(cdev); - - mutex_unlock(&cdev->lock); + scoped_guard(cooling_dev, cdev) { + /* + * Call for updating the cooling devices local stats and + * avoid periods of dozen of seconds when those have not + * been maintained. + */ + cdev->ops->get_requested_power(cdev, &req_power); + + if (params->update_cdevs) + __thermal_cdev_update(cdev); + } } } diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index ea4bf88d37f33..d1bb59f1dfbd3 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -97,9 +97,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, instance->initialized = true; - mutex_lock(&instance->cdev->lock); - instance->cdev->updated = false; /* cdev needs update */ - mutex_unlock(&instance->cdev->lock); + scoped_guard(cooling_dev, instance->cdev) { + instance->cdev->updated = false; /* cdev needs update */ + } } } diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index ab0c7c6396c02..f98aa396787af 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -758,12 +758,10 @@ static int thermal_instance_add(struct thermal_instance *new_instance, list_add_tail(&new_instance->trip_node, &td->thermal_instances); - mutex_lock(&cdev->lock); + guard(cooling_dev)(cdev); list_add_tail(&new_instance->cdev_node, &cdev->thermal_instances); - mutex_unlock(&cdev->lock); - return 0; } @@ -872,11 +870,9 @@ static void thermal_instance_delete(struct thermal_instance *instance) { list_del(&instance->trip_node); - mutex_lock(&instance->cdev->lock); + guard(cooling_dev)(instance->cdev); list_del(&instance->cdev_node); - - mutex_unlock(&instance->cdev->lock); } /** @@ -1239,10 +1235,10 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) * Update under the cdev lock to prevent the state from being set beyond * the new limit concurrently. */ - mutex_lock(&cdev->lock); + guard(cooling_dev)(cdev); if (cdev->ops->get_max_state(cdev, &cdev->max_state)) - goto unlock; + return; thermal_cooling_device_stats_reinit(cdev); @@ -1269,12 +1265,9 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) } if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state) - goto unlock; + return; thermal_cooling_device_stats_update(cdev, state); - -unlock: - mutex_unlock(&cdev->lock); } EXPORT_SYMBOL_GPL(thermal_cooling_device_update); diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index d67021aeb1367..c800504c3cfe0 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -516,6 +516,19 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state) cdev->debugfs = thermal_dbg; } +static struct thermal_debugfs *thermal_debug_cdev_clear(struct thermal_cooling_device *cdev) +{ + struct thermal_debugfs *thermal_dbg; + + guard(cooling_dev)(cdev); + + thermal_dbg = cdev->debugfs; + if (thermal_dbg) + cdev->debugfs = NULL; + + return thermal_dbg; +} + /** * thermal_debug_cdev_remove - Remove a cooling device debugfs entry * @@ -527,17 +540,9 @@ void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev) { struct thermal_debugfs *thermal_dbg; - mutex_lock(&cdev->lock); - - thermal_dbg = cdev->debugfs; - if (!thermal_dbg) { - mutex_unlock(&cdev->lock); + thermal_dbg = thermal_debug_cdev_clear(cdev); + if (!thermal_dbg) return; - } - - cdev->debugfs = NULL; - - mutex_unlock(&cdev->lock); mutex_lock(&thermal_dbg->lock); diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index 030a4e4bc824c..b1152ad7acc90 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -58,17 +58,10 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, const struct thermal_trip *trip, struct thermal_cooling_device *cdev) { - bool ret; - guard(thermal_zone)(tz); + guard(cooling_dev)(cdev); - mutex_lock(&cdev->lock); - - ret = thermal_instance_present(tz, cdev, trip); - - mutex_unlock(&cdev->lock); - - return ret; + return thermal_instance_present(tz, cdev, trip); } EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev); @@ -197,12 +190,12 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev) */ void thermal_cdev_update(struct thermal_cooling_device *cdev) { - mutex_lock(&cdev->lock); + guard(cooling_dev)(cdev); + if (!cdev->updated) { __thermal_cdev_update(cdev); cdev->updated = true; } - mutex_unlock(&cdev->lock); } /** @@ -211,11 +204,9 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) */ void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev) { - mutex_lock(&cdev->lock); + guard(cooling_dev)(cdev); __thermal_cdev_update(cdev); - - mutex_unlock(&cdev->lock); } /** diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 701607a953ff4..24b9055a0b6c5 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -544,14 +544,15 @@ cur_state_store(struct device *dev, struct device_attribute *attr, if (state > cdev->max_state) return -EINVAL; - mutex_lock(&cdev->lock); + guard(cooling_dev)(cdev); result = cdev->ops->set_cur_state(cdev, state); - if (!result) - thermal_cooling_device_stats_update(cdev, state); + if (result) + return result; + + thermal_cooling_device_stats_update(cdev, state); - mutex_unlock(&cdev->lock); - return result ? result : count; + return count; } static struct device_attribute @@ -625,21 +626,18 @@ static ssize_t total_trans_show(struct device *dev, { struct thermal_cooling_device *cdev = to_cooling_device(dev); struct cooling_dev_stats *stats; - int ret = 0; + int ret; - mutex_lock(&cdev->lock); + guard(cooling_dev)(cdev); stats = cdev->stats; if (!stats) - goto unlock; + return 0; spin_lock(&stats->lock); ret = sprintf(buf, "%u\n", stats->total_trans); spin_unlock(&stats->lock); -unlock: - mutex_unlock(&cdev->lock); - return ret; } @@ -652,11 +650,11 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr, ssize_t len = 0; int i; - mutex_lock(&cdev->lock); + guard(cooling_dev)(cdev); stats = cdev->stats; if (!stats) - goto unlock; + return 0; spin_lock(&stats->lock); @@ -668,9 +666,6 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr, } spin_unlock(&stats->lock); -unlock: - mutex_unlock(&cdev->lock); - return len; } @@ -682,11 +677,11 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, struct cooling_dev_stats *stats; int i, states; - mutex_lock(&cdev->lock); + guard(cooling_dev)(cdev); stats = cdev->stats; if (!stats) - goto unlock; + return count; states = cdev->max_state + 1; @@ -702,9 +697,6 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, spin_unlock(&stats->lock); -unlock: - mutex_unlock(&cdev->lock); - return count; } @@ -716,13 +708,11 @@ static ssize_t trans_table_show(struct device *dev, ssize_t len = 0; int i, j; - mutex_lock(&cdev->lock); + guard(cooling_dev)(cdev); stats = cdev->stats; - if (!stats) { - len = -ENODATA; - goto unlock; - } + if (!stats) + return -ENODATA; len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); len += snprintf(buf + len, PAGE_SIZE - len, " : "); @@ -731,10 +721,8 @@ static ssize_t trans_table_show(struct device *dev, break; len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i); } - if (len >= PAGE_SIZE) { - len = PAGE_SIZE; - goto unlock; - } + if (len >= PAGE_SIZE) + return PAGE_SIZE; len += snprintf(buf + len, PAGE_SIZE - len, "\n"); @@ -760,9 +748,6 @@ static ssize_t trans_table_show(struct device *dev, len = -EFBIG; } -unlock: - mutex_unlock(&cdev->lock); - return len; } diff --git a/include/linux/thermal.h b/include/linux/thermal.h index bcaa92732e14a..754802478b96a 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -140,6 +140,9 @@ struct thermal_cooling_device { #endif }; +DEFINE_GUARD(cooling_dev, struct thermal_cooling_device *, mutex_lock(&_T->lock), + mutex_unlock(&_T->lock)) + /* Structure to define Thermal Zone parameters */ struct thermal_zone_params { const char *governor_name; From af73d53e97ca5972e93ed481745d25741fa3af65 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:20:56 +0200 Subject: [PATCH 24/45] thermal: core: Separate thermal zone governor initialization In preparation for a subsequent change that will switch over the thermal core to using a mutex guard for managing thermal_governor_lock, move the code running in thermal_zone_device_register_with_trips() under that lock into a separate function called thermal_zone_init_governor(). While at it, drop a useless comment. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/4408795.ejJDZkT8p0@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 36 ++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f98aa396787af..6464be98df5f0 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1345,6 +1345,25 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) } EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); +static int thermal_zone_init_governor(struct thermal_zone_device *tz) +{ + struct thermal_governor *governor; + int ret; + + mutex_lock(&thermal_governor_lock); + + if (tz->tzp) + governor = __find_governor(tz->tzp->governor_name); + else + governor = def_governor; + + ret = thermal_set_governor(tz, governor); + + mutex_unlock(&thermal_governor_lock); + + return ret; +} + static void thermal_zone_init_complete(struct thermal_zone_device *tz) { struct thermal_cooling_device *cdev; @@ -1409,7 +1428,6 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip_desc *td; int id; int result; - struct thermal_governor *governor; if (!type || strlen(type) == 0) { pr_err("No thermal zone type defined\n"); @@ -1507,21 +1525,9 @@ thermal_zone_device_register_with_trips(const char *type, if (result) goto release_device; - /* Update 'this' zone's governor information */ - mutex_lock(&thermal_governor_lock); - - if (tz->tzp) - governor = __find_governor(tz->tzp->governor_name); - else - governor = def_governor; - - result = thermal_set_governor(tz, governor); - if (result) { - mutex_unlock(&thermal_governor_lock); + result = thermal_zone_init_governor(tz); + if (result) goto unregister; - } - - mutex_unlock(&thermal_governor_lock); if (!tz->tzp || !tz->tzp->no_hwmon) { result = thermal_add_hwmon_sysfs(tz); From dfa245f512566c8bdfbea44485471914764cf12a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:22:09 +0200 Subject: [PATCH 25/45] thermal: core: Manage thermal_governor_lock using a mutex guard Switch over the thermal core to using a mutex guard for thermal_governor_lock management. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3679429.R56niFO833@rjwysocki.net Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 40 +++++++++++----------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 6464be98df5f0..7f0d69b333a03 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -124,7 +124,7 @@ int thermal_register_governor(struct thermal_governor *governor) if (!governor) return -EINVAL; - mutex_lock(&thermal_governor_lock); + guard(mutex)(&thermal_governor_lock); err = -EBUSY; if (!__find_governor(governor->name)) { @@ -163,8 +163,6 @@ int thermal_register_governor(struct thermal_governor *governor) } } - mutex_unlock(&thermal_governor_lock); - return err; } @@ -175,10 +173,10 @@ void thermal_unregister_governor(struct thermal_governor *governor) if (!governor) return; - mutex_lock(&thermal_governor_lock); + guard(mutex)(&thermal_governor_lock); if (!__find_governor(governor->name)) - goto exit; + return; list_del(&governor->governor_list); @@ -189,9 +187,6 @@ void thermal_unregister_governor(struct thermal_governor *governor) THERMAL_NAME_LENGTH)) thermal_set_governor(pos, NULL); } - -exit: - mutex_unlock(&thermal_governor_lock); } int thermal_zone_device_set_policy(struct thermal_zone_device *tz, @@ -200,16 +195,13 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *tz, struct thermal_governor *gov; int ret = -EINVAL; - mutex_lock(&thermal_governor_lock); - + guard(mutex)(&thermal_governor_lock); guard(thermal_zone)(tz); gov = __find_governor(strim(policy)); if (gov) ret = thermal_set_governor(tz, gov); - mutex_unlock(&thermal_governor_lock); - thermal_notify_tz_gov_change(tz, policy); return ret; @@ -220,15 +212,13 @@ int thermal_build_list_of_policies(char *buf) struct thermal_governor *pos; ssize_t count = 0; - mutex_lock(&thermal_governor_lock); + guard(mutex)(&thermal_governor_lock); list_for_each_entry(pos, &thermal_governor_list, governor_list) { count += sysfs_emit_at(buf, count, "%s ", pos->name); } count += sysfs_emit_at(buf, count, "\n"); - mutex_unlock(&thermal_governor_lock); - return count; } @@ -670,17 +660,18 @@ int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), void *data) { struct thermal_governor *gov; - int ret = 0; - mutex_lock(&thermal_governor_lock); + guard(mutex)(&thermal_governor_lock); + list_for_each_entry(gov, &thermal_governor_list, governor_list) { + int ret; + ret = cb(gov, data); if (ret) - break; + return ret; } - mutex_unlock(&thermal_governor_lock); - return ret; + return 0; } int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, @@ -1348,20 +1339,15 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); static int thermal_zone_init_governor(struct thermal_zone_device *tz) { struct thermal_governor *governor; - int ret; - mutex_lock(&thermal_governor_lock); + guard(mutex)(&thermal_governor_lock); if (tz->tzp) governor = __find_governor(tz->tzp->governor_name); else governor = def_governor; - ret = thermal_set_governor(tz, governor); - - mutex_unlock(&thermal_governor_lock); - - return ret; + return thermal_set_governor(tz, governor); } static void thermal_zone_init_complete(struct thermal_zone_device *tz) From 1773572863c43a14a3e45f0591f28b7dec1ee52a Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 22 Oct 2024 17:51:42 +0200 Subject: [PATCH 26/45] thermal: netlink: Add the commands and the events for the thresholds The thresholds exist but there is no notification neither action code related to them yet. These changes implement the netlink for the notifications when the thresholds are crossed, added, deleted or flushed as well as the commands which allows to get the list of the thresholds, flush them, add and delete. Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/20241022155147.463475-3-daniel.lezcano@linaro.org [ rjw: Use the thermal_zone guard for locking, subject edit ] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_netlink.c | 232 ++++++++++++++++++++++++++- drivers/thermal/thermal_netlink.h | 34 ++++ drivers/thermal/thermal_thresholds.c | 34 ++-- drivers/thermal/thermal_thresholds.h | 2 +- include/uapi/linux/thermal.h | 27 +++- 5 files changed, 301 insertions(+), 28 deletions(-) diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index 91f3fe8c8f071..315a76b01f6a1 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,11 @@ static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID] = { .type = NLA_U32 }, [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE] = { .type = NLA_U32 }, [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY] = { .type = NLA_U32 }, + + /* Thresholds */ + [THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED }, + [THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 }, }; struct param { @@ -62,6 +68,8 @@ struct param { int trip_type; int trip_hyst; int temp; + int prev_temp; + int direction; int cdev_state; int cdev_max_state; struct thermal_genl_cpu_caps *cpu_capabilities; @@ -234,6 +242,34 @@ static int thermal_genl_event_cpu_capability_change(struct param *p) return -EMSGSIZE; } +static int thermal_genl_event_threshold_add(struct param *p) +{ + if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || + nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp) || + nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction)) + return -EMSGSIZE; + + return 0; +} + +static int thermal_genl_event_threshold_flush(struct param *p) +{ + if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) + return -EMSGSIZE; + + return 0; +} + +static int thermal_genl_event_threshold_up(struct param *p) +{ + if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || + nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_PREV_TEMP, p->prev_temp) || + nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp)) + return -EMSGSIZE; + + return 0; +} + int thermal_genl_event_tz_delete(struct param *p) __attribute__((alias("thermal_genl_event_tz"))); @@ -246,6 +282,12 @@ int thermal_genl_event_tz_disable(struct param *p) int thermal_genl_event_tz_trip_down(struct param *p) __attribute__((alias("thermal_genl_event_tz_trip_up"))); +int thermal_genl_event_threshold_delete(struct param *p) + __attribute__((alias("thermal_genl_event_threshold_add"))); + +int thermal_genl_event_threshold_down(struct param *p) + __attribute__((alias("thermal_genl_event_threshold_up"))); + static cb_t event_cb[] = { [THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create, [THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete, @@ -259,6 +301,11 @@ static cb_t event_cb[] = { [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update, [THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change, [THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change, + [THERMAL_GENL_EVENT_THRESHOLD_ADD] = thermal_genl_event_threshold_add, + [THERMAL_GENL_EVENT_THRESHOLD_DELETE] = thermal_genl_event_threshold_delete, + [THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = thermal_genl_event_threshold_flush, + [THERMAL_GENL_EVENT_THRESHOLD_DOWN] = thermal_genl_event_threshold_down, + [THERMAL_GENL_EVENT_THRESHOLD_UP] = thermal_genl_event_threshold_up, }; /* @@ -401,6 +448,43 @@ int thermal_genl_cpu_capability_event(int count, } EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event); +int thermal_notify_threshold_add(const struct thermal_zone_device *tz, + int temperature, int direction) +{ + struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; + + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_ADD, &p); +} + +int thermal_notify_threshold_delete(const struct thermal_zone_device *tz, + int temperature, int direction) +{ + struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; + + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DELETE, &p); +} + +int thermal_notify_threshold_flush(const struct thermal_zone_device *tz) +{ + struct param p = { .tz_id = tz->id }; + + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_FLUSH, &p); +} + +int thermal_notify_threshold_down(const struct thermal_zone_device *tz) +{ + struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature }; + + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DOWN, &p); +} + +int thermal_notify_threshold_up(const struct thermal_zone_device *tz) +{ + struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature }; + + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_UP, &p); +} + /*************************** Command encoding ********************************/ static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz, @@ -563,12 +647,128 @@ static int thermal_genl_cmd_cdev_get(struct param *p) return ret; } +static int __thermal_genl_cmd_threshold_get(struct user_threshold *threshold, void *arg) +{ + struct sk_buff *msg = arg; + + if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, threshold->temperature) || + nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, threshold->direction)) + return -1; + + return 0; +} + +static int thermal_genl_cmd_threshold_get(struct param *p) +{ + struct sk_buff *msg = p->msg; + struct nlattr *start_trip; + int id, ret; + + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) + return -EINVAL; + + id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); + + CLASS(thermal_zone_get_by_id, tz)(id); + if (!tz) + return -EINVAL; + + start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD); + if (!start_trip) + return -EMSGSIZE; + + ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg); + if (ret) + return -EMSGSIZE; + + nla_nest_end(msg, start_trip); + + return 0; +} + +static int thermal_genl_cmd_threshold_add(struct param *p) +{ + int id, temp, direction; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] || + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] || + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]) + return -EINVAL; + + id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); + temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]); + direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]); + + CLASS(thermal_zone_get_by_id, tz)(id); + if (!tz) + return -EINVAL; + + guard(thermal_zone)(tz); + + return thermal_thresholds_add(tz, temp, direction); +} + +static int thermal_genl_cmd_threshold_delete(struct param *p) +{ + int id, temp, direction; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] || + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] || + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]) + return -EINVAL; + + id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); + temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]); + direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]); + + CLASS(thermal_zone_get_by_id, tz)(id); + if (!tz) + return -EINVAL; + + guard(thermal_zone)(tz); + + return thermal_thresholds_delete(tz, temp, direction); +} + +static int thermal_genl_cmd_threshold_flush(struct param *p) +{ + int id; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) + return -EINVAL; + + id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); + + CLASS(thermal_zone_get_by_id, tz)(id); + if (!tz) + return -EINVAL; + + guard(thermal_zone)(tz); + + thermal_thresholds_flush(tz); + + return 0; +} + static cb_t cmd_cb[] = { - [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id, - [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip, - [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp, - [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov, - [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get, + [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id, + [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip, + [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp, + [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov, + [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get, + [THERMAL_GENL_CMD_THRESHOLD_GET] = thermal_genl_cmd_threshold_get, + [THERMAL_GENL_CMD_THRESHOLD_ADD] = thermal_genl_cmd_threshold_add, + [THERMAL_GENL_CMD_THRESHOLD_DELETE] = thermal_genl_cmd_threshold_delete, + [THERMAL_GENL_CMD_THRESHOLD_FLUSH] = thermal_genl_cmd_threshold_flush, }; static int thermal_genl_cmd_dumpit(struct sk_buff *skb, @@ -679,6 +879,26 @@ static const struct genl_small_ops thermal_genl_ops[] = { .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .dumpit = thermal_genl_cmd_dumpit, }, + { + .cmd = THERMAL_GENL_CMD_THRESHOLD_GET, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = thermal_genl_cmd_doit, + }, + { + .cmd = THERMAL_GENL_CMD_THRESHOLD_ADD, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = thermal_genl_cmd_doit, + }, + { + .cmd = THERMAL_GENL_CMD_THRESHOLD_DELETE, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = thermal_genl_cmd_doit, + }, + { + .cmd = THERMAL_GENL_CMD_THRESHOLD_FLUSH, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = thermal_genl_cmd_doit, + }, }; static struct genl_family thermal_genl_family __ro_after_init = { @@ -691,7 +911,7 @@ static struct genl_family thermal_genl_family __ro_after_init = { .unbind = thermal_genl_unbind, .small_ops = thermal_genl_ops, .n_small_ops = ARRAY_SIZE(thermal_genl_ops), - .resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1, + .resv_start_op = __THERMAL_GENL_CMD_MAX, .mcgrps = thermal_genl_mcgrps, .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps), }; diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h index e01221e8816b4..075e9ae85f3d0 100644 --- a/drivers/thermal/thermal_netlink.h +++ b/drivers/thermal/thermal_netlink.h @@ -53,6 +53,13 @@ int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz, int thermal_genl_sampling_temp(int id, int temp); int thermal_genl_cpu_capability_event(int count, struct thermal_genl_cpu_caps *caps); +int thermal_notify_threshold_add(const struct thermal_zone_device *tz, + int temperature, int direction); +int thermal_notify_threshold_delete(const struct thermal_zone_device *tz, + int temperature, int direction); +int thermal_notify_threshold_flush(const struct thermal_zone_device *tz); +int thermal_notify_threshold_down(const struct thermal_zone_device *tz); +int thermal_notify_threshold_up(const struct thermal_zone_device *tz); #else static inline int thermal_netlink_init(void) { @@ -139,6 +146,33 @@ static inline int thermal_genl_cpu_capability_event(int count, struct thermal_ge return 0; } +static inline int thermal_notify_threshold_add(const struct thermal_zone_device *tz, + int temperature, int direction) +{ + return 0; +} + +static inline int thermal_notify_threshold_delete(const struct thermal_zone_device *tz, + int temperature, int direction) +{ + return 0; +} + +static inline int thermal_notify_threshold_flush(const struct thermal_zone_device *tz) +{ + return 0; +} + +static inline int thermal_notify_threshold_down(const struct thermal_zone_device *tz) +{ + return 0; +} + +static inline int thermal_notify_threshold_up(const struct thermal_zone_device *tz) +{ + return 0; +} + static inline void __init thermal_netlink_exit(void) {} #endif /* CONFIG_THERMAL_NETLINK */ diff --git a/drivers/thermal/thermal_thresholds.c b/drivers/thermal/thermal_thresholds.c index f33b6d5474d8c..9b063199a7896 100644 --- a/drivers/thermal/thermal_thresholds.c +++ b/drivers/thermal/thermal_thresholds.c @@ -32,6 +32,8 @@ void thermal_thresholds_flush(struct thermal_zone_device *tz) kfree(entry); } + thermal_notify_threshold_flush(tz); + __thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS); } @@ -122,7 +124,6 @@ void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *hi int temperature = tz->temperature; int last_temperature = tz->last_temperature; - bool notify; lockdep_assert_held(&tz->lock); @@ -144,19 +145,19 @@ void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *hi * - increased : thresholds are crossed the way up * - decreased : thresholds are crossed the way down */ - if (temperature > last_temperature) - notify = thermal_thresholds_handle_raising(thresholds, temperature, - last_temperature, low, high); - else - notify = thermal_thresholds_handle_dropping(thresholds, temperature, - last_temperature, low, high); - - if (notify) - pr_debug("A threshold has been crossed the way %s, with a temperature=%d, last_temperature=%d\n", - temperature > last_temperature ? "up" : "down", temperature, last_temperature); + if (temperature > last_temperature) { + if (thermal_thresholds_handle_raising(thresholds, temperature, + last_temperature, low, high)) + thermal_notify_threshold_up(tz); + } else { + if (thermal_thresholds_handle_dropping(thresholds, temperature, + last_temperature, low, high)) + thermal_notify_threshold_down(tz); + } } -int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction) +int thermal_thresholds_add(struct thermal_zone_device *tz, + int temperature, int direction) { struct list_head *thresholds = &tz->user_thresholds; struct user_threshold *t; @@ -182,12 +183,15 @@ int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int list_sort(NULL, thresholds, __thermal_thresholds_cmp); } + thermal_notify_threshold_add(tz, temperature, direction); + __thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD); return 0; } -int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction) +int thermal_thresholds_delete(struct thermal_zone_device *tz, + int temperature, int direction) { struct list_head *thresholds = &tz->user_thresholds; struct user_threshold *t; @@ -205,6 +209,8 @@ int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, i t->direction &= ~direction; } + thermal_notify_threshold_delete(tz, temperature, direction); + __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD); return 0; @@ -217,7 +223,7 @@ int thermal_thresholds_for_each(struct thermal_zone_device *tz, struct user_threshold *entry; int ret; - lockdep_assert_held(&tz->lock); + guard(thermal_zone)(tz); list_for_each_entry(entry, thresholds, list_node) { ret = cb(entry, arg); diff --git a/drivers/thermal/thermal_thresholds.h b/drivers/thermal/thermal_thresholds.h index 232f4e8089af8..cb372659a20d6 100644 --- a/drivers/thermal/thermal_thresholds.h +++ b/drivers/thermal/thermal_thresholds.h @@ -10,8 +10,8 @@ struct user_threshold { int thermal_thresholds_init(struct thermal_zone_device *tz); void thermal_thresholds_exit(struct thermal_zone_device *tz); -void thermal_thresholds_flush(struct thermal_zone_device *tz); void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high); +void thermal_thresholds_flush(struct thermal_zone_device *tz); int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction); int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction); int thermal_thresholds_for_each(struct thermal_zone_device *tz, diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h index 2e6f60a361735..ba8604bdf2069 100644 --- a/include/uapi/linux/thermal.h +++ b/include/uapi/linux/thermal.h @@ -20,7 +20,7 @@ enum thermal_trip_type { /* Adding event notification support elements */ #define THERMAL_GENL_FAMILY_NAME "thermal" -#define THERMAL_GENL_VERSION 0x01 +#define THERMAL_GENL_VERSION 0x02 #define THERMAL_GENL_SAMPLING_GROUP_NAME "sampling" #define THERMAL_GENL_EVENT_GROUP_NAME "event" @@ -30,6 +30,7 @@ enum thermal_genl_attr { THERMAL_GENL_ATTR_TZ, THERMAL_GENL_ATTR_TZ_ID, THERMAL_GENL_ATTR_TZ_TEMP, + THERMAL_GENL_ATTR_TZ_PREV_TEMP, THERMAL_GENL_ATTR_TZ_TRIP, THERMAL_GENL_ATTR_TZ_TRIP_ID, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, @@ -50,6 +51,9 @@ enum thermal_genl_attr { THERMAL_GENL_ATTR_CPU_CAPABILITY_ID, THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE, THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY, + THERMAL_GENL_ATTR_THRESHOLD, + THERMAL_GENL_ATTR_THRESHOLD_TEMP, + THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, __THERMAL_GENL_ATTR_MAX, }; #define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) @@ -77,6 +81,11 @@ enum thermal_genl_event { THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */ THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */ THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, /* CPU capability changed */ + THERMAL_GENL_EVENT_THRESHOLD_ADD, /* A thresold has been added */ + THERMAL_GENL_EVENT_THRESHOLD_DELETE, /* A thresold has been deleted */ + THERMAL_GENL_EVENT_THRESHOLD_FLUSH, /* All thresolds have been deleted */ + THERMAL_GENL_EVENT_THRESHOLD_UP, /* A thresold has been crossed the way up */ + THERMAL_GENL_EVENT_THRESHOLD_DOWN, /* A thresold has been crossed the way down */ __THERMAL_GENL_EVENT_MAX, }; #define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1) @@ -84,12 +93,16 @@ enum thermal_genl_event { /* Commands supported by the thermal_genl_family */ enum thermal_genl_cmd { THERMAL_GENL_CMD_UNSPEC, - THERMAL_GENL_CMD_TZ_GET_ID, /* List of thermal zones id */ - THERMAL_GENL_CMD_TZ_GET_TRIP, /* List of thermal trips */ - THERMAL_GENL_CMD_TZ_GET_TEMP, /* Get the thermal zone temperature */ - THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone governor */ - THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone mode */ - THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */ + THERMAL_GENL_CMD_TZ_GET_ID, /* List of thermal zones id */ + THERMAL_GENL_CMD_TZ_GET_TRIP, /* List of thermal trips */ + THERMAL_GENL_CMD_TZ_GET_TEMP, /* Get the thermal zone temperature */ + THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone governor */ + THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone mode */ + THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */ + THERMAL_GENL_CMD_THRESHOLD_GET, /* List of thresholds */ + THERMAL_GENL_CMD_THRESHOLD_ADD, /* Add a threshold */ + THERMAL_GENL_CMD_THRESHOLD_DELETE, /* Delete a threshold */ + THERMAL_GENL_CMD_THRESHOLD_FLUSH, /* Flush all the thresholds */ __THERMAL_GENL_CMD_MAX, }; #define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) From 24b216b2d13568c703a76137ef54a2a9531a71d8 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 22 Oct 2024 17:51:43 +0200 Subject: [PATCH 27/45] tools/lib/thermal: Make more generic the command encoding function The thermal netlink has been extended with more commands which require an encoding with more information. The generic encoding function puts the thermal zone id with the command name. It is the unique parameters. The next changes will provide more parameters to the command. Set the scene for those new parameters by making the encoding function more generic. Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/20241022155147.463475-4-daniel.lezcano@linaro.org Signed-off-by: Rafael J. Wysocki --- tools/lib/thermal/commands.c | 41 ++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/tools/lib/thermal/commands.c b/tools/lib/thermal/commands.c index 73d4d4e8d6ec0..a9223df91dcf5 100644 --- a/tools/lib/thermal/commands.c +++ b/tools/lib/thermal/commands.c @@ -261,8 +261,23 @@ static struct genl_ops thermal_cmd_ops = { .o_ncmds = ARRAY_SIZE(thermal_cmds), }; -static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int cmd, - int flags, void *arg) +struct cmd_param { + int tz_id; +}; + +typedef int (*cmd_cb_t)(struct nl_msg *, struct cmd_param *); + +static int thermal_genl_tz_id_encode(struct nl_msg *msg, struct cmd_param *p) +{ + if (p->tz_id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) + return -1; + + return 0; +} + +static thermal_error_t thermal_genl_auto(struct thermal_handler *th, cmd_cb_t cmd_cb, + struct cmd_param *param, + int cmd, int flags, void *arg) { struct nl_msg *msg; void *hdr; @@ -276,7 +291,7 @@ static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int if (!hdr) return THERMAL_ERROR; - if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id)) + if (cmd_cb && cmd_cb(msg, param)) return THERMAL_ERROR; if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg)) @@ -289,30 +304,38 @@ static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz) { - return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET_ID, + return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_TZ_GET_ID, NLM_F_DUMP | NLM_F_ACK, tz); } thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc) { - return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET, + return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_CDEV_GET, NLM_F_DUMP | NLM_F_ACK, tc); } thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz) { - return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TRIP, - 0, tz); + struct cmd_param p = { .tz_id = tz->id }; + + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, + THERMAL_GENL_CMD_TZ_GET_TRIP, 0, tz); } thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz) { - return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz); + struct cmd_param p = { .tz_id = tz->id }; + + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, + THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz); } thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz) { - return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); + struct cmd_param p = { .tz_id = tz->id }; + + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, + THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); } thermal_error_t thermal_cmd_exit(struct thermal_handler *th) From a26267248628760d02e7d03534afc59dae0c9b40 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 22 Oct 2024 17:51:44 +0200 Subject: [PATCH 28/45] tools/lib/thermal: Add the threshold netlink ABI The thermal framework supports the thresholds and allows the userspace to create, delete, flush, get the list of the thresholds as well as getting the list of the thresholds set for a specific thermal zone. Add the netlink abstraction in the thermal library to take full advantage of thresholds for the userspace program. Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/20241022155147.463475-5-daniel.lezcano@linaro.org Signed-off-by: Rafael J. Wysocki --- tools/lib/thermal/commands.c | 138 +++++++++++++++++++++++++++- tools/lib/thermal/events.c | 55 ++++++++--- tools/lib/thermal/include/thermal.h | 40 ++++++++ tools/lib/thermal/libthermal.map | 5 + tools/lib/thermal/thermal.c | 17 ++++ tools/thermal/lib/Makefile | 2 +- 6 files changed, 242 insertions(+), 15 deletions(-) diff --git a/tools/lib/thermal/commands.c b/tools/lib/thermal/commands.c index a9223df91dcf5..bcf0f14d035a7 100644 --- a/tools/lib/thermal/commands.c +++ b/tools/lib/thermal/commands.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "thermal_nl.h" @@ -33,6 +34,11 @@ static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = { [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 }, [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 }, [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING }, + + /* Thresholds */ + [THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED }, + [THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 }, }; static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz) @@ -182,6 +188,48 @@ static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz) return THERMAL_SUCCESS; } +static int parse_threshold_get(struct genl_info *info, struct thermal_zone *tz) +{ + struct nlattr *attr; + struct thermal_threshold *__tt = NULL; + size_t size = 0; + int rem; + + /* + * The size contains the size of the array and we want to + * access the last element, size - 1. + * + * The variable size is initialized to zero but it will be + * then incremented by the first if() statement. The message + * attributes are ordered, so the first if() statement will be + * always called before the second one. If it happens that is + * not the case, then it is a kernel bug. + */ + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_THRESHOLD], rem) { + + if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_TEMP) { + + size++; + + __tt = realloc(__tt, sizeof(*__tt) * (size + 2)); + if (!__tt) + return THERMAL_ERROR; + + __tt[size - 1].temperature = nla_get_u32(attr); + } + + if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_DIRECTION) + __tt[size - 1].direction = nla_get_u32(attr); + } + + if (__tt) + __tt[size].temperature = INT_MAX; + + tz->thresholds = __tt; + + return THERMAL_SUCCESS; +} + static int handle_netlink(struct nl_cache_ops *unused, struct genl_cmd *cmd, struct genl_info *info, void *arg) @@ -210,6 +258,10 @@ static int handle_netlink(struct nl_cache_ops *unused, ret = parse_tz_get_gov(info, arg); break; + case THERMAL_GENL_CMD_THRESHOLD_GET: + ret = parse_threshold_get(info, arg); + break; + default: return THERMAL_ERROR; } @@ -253,6 +305,34 @@ static struct genl_cmd thermal_cmds[] = { .c_maxattr = THERMAL_GENL_ATTR_MAX, .c_attr_policy = thermal_genl_policy, }, + { + .c_id = THERMAL_GENL_CMD_THRESHOLD_GET, + .c_name = (char *)"Get thresholds list", + .c_msg_parser = handle_netlink, + .c_maxattr = THERMAL_GENL_ATTR_MAX, + .c_attr_policy = thermal_genl_policy, + }, + { + .c_id = THERMAL_GENL_CMD_THRESHOLD_ADD, + .c_name = (char *)"Add a threshold", + .c_msg_parser = handle_netlink, + .c_maxattr = THERMAL_GENL_ATTR_MAX, + .c_attr_policy = thermal_genl_policy, + }, + { + .c_id = THERMAL_GENL_CMD_THRESHOLD_DELETE, + .c_name = (char *)"Delete a threshold", + .c_msg_parser = handle_netlink, + .c_maxattr = THERMAL_GENL_ATTR_MAX, + .c_attr_policy = thermal_genl_policy, + }, + { + .c_id = THERMAL_GENL_CMD_THRESHOLD_FLUSH, + .c_name = (char *)"Flush the thresholds", + .c_msg_parser = handle_netlink, + .c_maxattr = THERMAL_GENL_ATTR_MAX, + .c_attr_policy = thermal_genl_policy, + }, }; static struct genl_ops thermal_cmd_ops = { @@ -263,13 +343,29 @@ static struct genl_ops thermal_cmd_ops = { struct cmd_param { int tz_id; + int temp; + int direction; }; typedef int (*cmd_cb_t)(struct nl_msg *, struct cmd_param *); static int thermal_genl_tz_id_encode(struct nl_msg *msg, struct cmd_param *p) { - if (p->tz_id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) + if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) + return -1; + + return 0; +} + +static int thermal_genl_threshold_encode(struct nl_msg *msg, struct cmd_param *p) +{ + if (thermal_genl_tz_id_encode(msg, p)) + return -1; + + if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp)) + return -1; + + if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction)) return -1; return 0; @@ -338,6 +434,46 @@ thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_ THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); } +thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th, + struct thermal_zone *tz) +{ + struct cmd_param p = { .tz_id = tz->id }; + + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, + THERMAL_GENL_CMD_THRESHOLD_GET, 0, tz); +} + +thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th, + struct thermal_zone *tz, + int temperature, + int direction) +{ + struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; + + return thermal_genl_auto(th, thermal_genl_threshold_encode, &p, + THERMAL_GENL_CMD_THRESHOLD_ADD, 0, tz); +} + +thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th, + struct thermal_zone *tz, + int temperature, + int direction) +{ + struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; + + return thermal_genl_auto(th, thermal_genl_threshold_encode, &p, + THERMAL_GENL_CMD_THRESHOLD_DELETE, 0, tz); +} + +thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th, + struct thermal_zone *tz) +{ + struct cmd_param p = { .tz_id = tz->id }; + + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, + THERMAL_GENL_CMD_THRESHOLD_FLUSH, 0, tz); +} + thermal_error_t thermal_cmd_exit(struct thermal_handler *th) { if (genl_unregister_family(&thermal_cmd_ops)) diff --git a/tools/lib/thermal/events.c b/tools/lib/thermal/events.c index a7a55d1a0c4c3..bd851c8690294 100644 --- a/tools/lib/thermal/events.c +++ b/tools/lib/thermal/events.c @@ -94,6 +94,30 @@ static int handle_thermal_event(struct nl_msg *n, void *arg) case THERMAL_GENL_EVENT_TZ_GOV_CHANGE: return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg); + + case THERMAL_GENL_EVENT_THRESHOLD_ADD: + return ops->threshold_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg); + + case THERMAL_GENL_EVENT_THRESHOLD_DELETE: + return ops->threshold_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg); + + case THERMAL_GENL_EVENT_THRESHOLD_FLUSH: + return ops->threshold_flush(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); + + case THERMAL_GENL_EVENT_THRESHOLD_UP: + return ops->threshold_up(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg); + + case THERMAL_GENL_EVENT_THRESHOLD_DOWN: + return ops->threshold_down(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg); + default: return -1; } @@ -101,19 +125,24 @@ static int handle_thermal_event(struct nl_msg *n, void *arg) static void thermal_events_ops_init(struct thermal_events_ops *ops) { - enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; - enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; - enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; - enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high; - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low; - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; - enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add; - enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete; - enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update; - enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; + enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; + enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; + enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; + enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; + enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add; + enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete; + enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update; + enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_ADD] = !!ops->threshold_add; + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DELETE] = !!ops->threshold_delete; + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = !!ops->threshold_flush; + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_UP] = !!ops->threshold_up; + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DOWN] = !!ops->threshold_down; } thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg) diff --git a/tools/lib/thermal/include/thermal.h b/tools/lib/thermal/include/thermal.h index 1abc560602cfb..818ecdfb46e50 100644 --- a/tools/lib/thermal/include/thermal.h +++ b/tools/lib/thermal/include/thermal.h @@ -4,11 +4,20 @@ #define __LIBTHERMAL_H #include +#include #ifndef LIBTHERMAL_API #define LIBTHERMAL_API __attribute__((visibility("default"))) #endif +#ifndef THERMAL_THRESHOLD_WAY_UP +#define THERMAL_THRESHOLD_WAY_UP 0x1 +#endif + +#ifndef THERMAL_THRESHOLD_WAY_DOWN +#define THERMAL_THRESHOLD_WAY_DOWN 0x2 +#endif + #ifdef __cplusplus extern "C" { #endif @@ -31,6 +40,11 @@ struct thermal_events_ops { int (*cdev_delete)(int cdev_id, void *arg); int (*cdev_update)(int cdev_id, int cur_state, void *arg); int (*gov_change)(int tz_id, const char *gov_name, void *arg); + int (*threshold_add)(int tz_id, int temperature, int direction, void *arg); + int (*threshold_delete)(int tz_id, int temperature, int direction, void *arg); + int (*threshold_flush)(int tz_id, void *arg); + int (*threshold_up)(int tz_id, int temp, int prev_temp, void *arg); + int (*threshold_down)(int tz_id, int temp, int prev_temp, void *arg); }; struct thermal_ops { @@ -45,12 +59,18 @@ struct thermal_trip { int hyst; }; +struct thermal_threshold { + int temperature; + int direction; +}; + struct thermal_zone { int id; int temp; char name[THERMAL_NAME_LENGTH]; char governor[THERMAL_NAME_LENGTH]; struct thermal_trip *trip; + struct thermal_threshold *thresholds; }; struct thermal_cdev { @@ -74,12 +94,16 @@ typedef int (*cb_tt_t)(struct thermal_trip *, void *); typedef int (*cb_tc_t)(struct thermal_cdev *, void *); +typedef int (*cb_th_t)(struct thermal_threshold *, void *); + LIBTHERMAL_API int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg); LIBTHERMAL_API int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg); LIBTHERMAL_API int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg); +LIBTHERMAL_API int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg); + LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz, const char *name); @@ -124,6 +148,22 @@ LIBTHERMAL_API thermal_error_t thermal_cmd_get_governor(struct thermal_handler * LIBTHERMAL_API thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz); +LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th, + struct thermal_zone *tz); + +LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th, + struct thermal_zone *tz, + int temperature, + int direction); + +LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th, + struct thermal_zone *tz, + int temperature, + int direction); + +LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th, + struct thermal_zone *tz); + /* * Netlink thermal samples */ diff --git a/tools/lib/thermal/libthermal.map b/tools/lib/thermal/libthermal.map index d5e77738c7a41..d657176aa47fb 100644 --- a/tools/lib/thermal/libthermal.map +++ b/tools/lib/thermal/libthermal.map @@ -4,6 +4,7 @@ LIBTHERMAL_0.0.1 { for_each_thermal_zone; for_each_thermal_trip; for_each_thermal_cdev; + for_each_thermal_threshold; thermal_zone_find_by_name; thermal_zone_find_by_id; thermal_zone_discover; @@ -17,6 +18,10 @@ LIBTHERMAL_0.0.1 { thermal_cmd_get_trip; thermal_cmd_get_governor; thermal_cmd_get_temp; + thermal_cmd_threshold_get; + thermal_cmd_threshold_add; + thermal_cmd_threshold_delete; + thermal_cmd_threshold_flush; thermal_sampling_init; thermal_sampling_handle; thermal_sampling_fd; diff --git a/tools/lib/thermal/thermal.c b/tools/lib/thermal/thermal.c index 72a76dc205bce..6f02e35391594 100644 --- a/tools/lib/thermal/thermal.c +++ b/tools/lib/thermal/thermal.c @@ -1,10 +1,24 @@ // SPDX-License-Identifier: LGPL-2.1+ // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano #include +#include #include #include "thermal_nl.h" +int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg) +{ + int i, ret = 0; + + if (!th) + return 0; + + for (i = 0; th[i].temperature != INT_MAX; i++) + ret |= cb(&th[i], arg); + + return ret; +} + int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg) { int i, ret = 0; @@ -80,6 +94,9 @@ static int __thermal_zone_discover(struct thermal_zone *tz, void *th) if (thermal_cmd_get_trip(th, tz) < 0) return -1; + if (thermal_cmd_threshold_get(th, tz)) + return -1; + if (thermal_cmd_get_governor(th, tz)) return -1; diff --git a/tools/thermal/lib/Makefile b/tools/thermal/lib/Makefile index 82db451935c52..f2552f73a64c7 100644 --- a/tools/thermal/lib/Makefile +++ b/tools/thermal/lib/Makefile @@ -3,7 +3,7 @@ LIBTHERMAL_TOOLS_VERSION = 0 LIBTHERMAL_TOOLS_PATCHLEVEL = 0 -LIBTHERMAL_TOOLS_EXTRAVERSION = 1 +LIBTHERMAL_TOOLS_EXTRAVERSION = 2 MAKEFLAGS += --no-print-directory From 41b89dba7c5dd0a071c52aa2f8c87c507e30dfbe Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 22 Oct 2024 17:51:45 +0200 Subject: [PATCH 29/45] tools/thermal/thermal-engine: Take into account the thresholds API Enhance the thermal-engine skeleton with the thresholds added in the kernel and use the API exported by the thermal library. Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/20241022155147.463475-6-daniel.lezcano@linaro.org Signed-off-by: Rafael J. Wysocki --- tools/thermal/thermal-engine/thermal-engine.c | 105 +++++++++++++++--- 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/tools/thermal/thermal-engine/thermal-engine.c b/tools/thermal/thermal-engine/thermal-engine.c index 9b1476a2680f0..0764dc7547715 100644 --- a/tools/thermal/thermal-engine/thermal-engine.c +++ b/tools/thermal/thermal-engine/thermal-engine.c @@ -38,6 +38,14 @@ struct thermal_data { struct thermal_handler *th; }; +static int show_threshold(struct thermal_threshold *th, __maybe_unused void *arg) +{ + INFO("threshold temp=%d, direction=%d\n", + th->temperature, th->direction); + + return 0; +} + static int show_trip(struct thermal_trip *tt, __maybe_unused void *arg) { INFO("trip id=%d, type=%d, temp=%d, hyst=%d\n", @@ -70,6 +78,8 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg) for_each_thermal_trip(tz->trip, show_trip, NULL); + for_each_thermal_threshold(tz->thresholds, show_threshold, NULL); + show_temp(tz, arg); show_governor(tz, arg); @@ -77,6 +87,30 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg) return 0; } +static int set_threshold(struct thermal_zone *tz, __maybe_unused void *arg) +{ + struct thermal_handler *th = arg; + int thresholds[] = { 43000, 65000, 49000, 55000, 57000 }; + size_t i; + + INFO("Setting threshold for thermal zone '%s', id=%d\n", tz->name, tz->id); + + if (thermal_cmd_threshold_flush(th, tz)) { + ERROR("Failed to flush all previous thresholds\n"); + return -1; + } + + for (i = 0; i < sizeof(thresholds) / sizeof(thresholds[0]); i++) + if (thermal_cmd_threshold_add(th, tz, thresholds[i], + THERMAL_THRESHOLD_WAY_UP | + THERMAL_THRESHOLD_WAY_DOWN)) { + ERROR("Failed to set threshold\n"); + return -1; + } + + return 0; +} + static int tz_create(const char *name, int tz_id, __maybe_unused void *arg) { INFO("Thermal zone '%s'/%d created\n", name, tz_id); @@ -197,20 +231,62 @@ static int gov_change(int tz_id, const char *name, __maybe_unused void *arg) return 0; } +static int threshold_add(int tz_id, int temp, int direction, __maybe_unused void *arg) +{ + INFO("Threshold added tz_id=%d: temp=%d, direction=%d\n", tz_id, temp, direction); + + return 0; +} + +static int threshold_delete(int tz_id, int temp, int direction, __maybe_unused void *arg) +{ + INFO("Threshold deleted tz_id=%d: temp=%d, direction=%d\n", tz_id, temp, direction); + + return 0; +} + +static int threshold_flush(int tz_id, __maybe_unused void *arg) +{ + INFO("Thresholds flushed tz_id=%d\n", tz_id); + + return 0; +} + +static int threshold_up(int tz_id, int temp, int prev_temp, __maybe_unused void *arg) +{ + INFO("Threshold crossed way up tz_id=%d: temp=%d, prev_temp=%d\n", + tz_id, temp, prev_temp); + + return 0; +} + +static int threshold_down(int tz_id, int temp, int prev_temp, __maybe_unused void *arg) +{ + INFO("Threshold crossed way down tz_id=%d: temp=%d, prev_temp=%d\n", + tz_id, temp, prev_temp); + + return 0; +} + static struct thermal_ops ops = { - .events.tz_create = tz_create, - .events.tz_delete = tz_delete, - .events.tz_disable = tz_disable, - .events.tz_enable = tz_enable, - .events.trip_high = trip_high, - .events.trip_low = trip_low, - .events.trip_add = trip_add, - .events.trip_delete = trip_delete, - .events.trip_change = trip_change, - .events.cdev_add = cdev_add, - .events.cdev_delete = cdev_delete, - .events.cdev_update = cdev_update, - .events.gov_change = gov_change + .events.tz_create = tz_create, + .events.tz_delete = tz_delete, + .events.tz_disable = tz_disable, + .events.tz_enable = tz_enable, + .events.trip_high = trip_high, + .events.trip_low = trip_low, + .events.trip_add = trip_add, + .events.trip_delete = trip_delete, + .events.trip_change = trip_change, + .events.cdev_add = cdev_add, + .events.cdev_delete = cdev_delete, + .events.cdev_update = cdev_update, + .events.gov_change = gov_change, + .events.threshold_add = threshold_add, + .events.threshold_delete = threshold_delete, + .events.threshold_flush = threshold_flush, + .events.threshold_up = threshold_up, + .events.threshold_down = threshold_down, }; static int thermal_event(__maybe_unused int fd, __maybe_unused void *arg) @@ -280,6 +356,7 @@ enum { THERMAL_ENGINE_DAEMON_ERROR, THERMAL_ENGINE_LOG_ERROR, THERMAL_ENGINE_THERMAL_ERROR, + THERMAL_ENGINE_THRESHOLD_ERROR, THERMAL_ENGINE_MAINLOOP_ERROR, }; @@ -318,6 +395,8 @@ int main(int argc, char *argv[]) return THERMAL_ENGINE_THERMAL_ERROR; } + for_each_thermal_zone(td.tz, set_threshold, td.th); + for_each_thermal_zone(td.tz, show_tz, td.th); if (mainloop_init()) { From 54219ee4eaeb356e0c8e0a6609dc6b5c21e2170a Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 24 Oct 2024 12:23:03 +0200 Subject: [PATCH 30/45] thermal: thresholds: Fix thermal lock annotation issue When the thermal zone is unregistered (thermal sensor module being unloaded), no lock is held when flushing the thresholds. That results in a WARN when the lockdep validation is set in the kernel config. This has been reported by syzbot. As the thermal zone is in the process of being destroyed, there is no need to send a notification about purging the thresholds to the userspace as this one will receive a thermal zone deletion notification which imply the deletion of all the associated resources like the trip points or the user thresholds. Split the function thermal_thresholds_flush() into a lockless one without notification and its call with the lock annotation followed with the thresholds flushing notification. Please note this scenario is unlikely to happen, as the sensor drivers are usually compiled-in in order to have the thermal framework to be able to kick in at boot time if needed. Fixes: 445936f9e258 ("thermal: core: Add user thresholds support") Link: https://lore.kernel.org/all/67124175.050a0220.10f4f4.0012.GAE@google.com Reported-by: syzbot+f24dd060c1911fe54c85@syzkaller.appspotmail.com Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20241024102303.1086147-1-daniel.lezcano@linaro.org [ rjw: Subject edit, added Fixes tag ] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_thresholds.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/thermal_thresholds.c b/drivers/thermal/thermal_thresholds.c index 9b063199a7896..d9b2a0bb44fca 100644 --- a/drivers/thermal/thermal_thresholds.c +++ b/drivers/thermal/thermal_thresholds.c @@ -20,17 +20,22 @@ int thermal_thresholds_init(struct thermal_zone_device *tz) return 0; } -void thermal_thresholds_flush(struct thermal_zone_device *tz) +static void __thermal_thresholds_flush(struct thermal_zone_device *tz) { struct list_head *thresholds = &tz->user_thresholds; struct user_threshold *entry, *tmp; - lockdep_assert_held(&tz->lock); - list_for_each_entry_safe(entry, tmp, thresholds, list_node) { list_del(&entry->list_node); kfree(entry); } +} + +void thermal_thresholds_flush(struct thermal_zone_device *tz) +{ + lockdep_assert_held(&tz->lock); + + __thermal_thresholds_flush(tz); thermal_notify_threshold_flush(tz); @@ -39,7 +44,7 @@ void thermal_thresholds_flush(struct thermal_zone_device *tz) void thermal_thresholds_exit(struct thermal_zone_device *tz) { - thermal_thresholds_flush(tz); + __thermal_thresholds_flush(tz); } static int __thermal_thresholds_cmp(void *data, From 7569406e95f2353070d88ebc88e8c13698542317 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 24 Oct 2024 12:59:38 +0200 Subject: [PATCH 31/45] thermal/lib: Fix memory leak on error in thermal_genl_auto() The function thermal_genl_auto() does not free the allocated message in the error path. Fix that by putting a out label and jump to it which will free the message instead of directly returning an error. Fixes: 47c4b0de080a ("tools/lib/thermal: Add a thermal library") Reported-by: Lukasz Luba  Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/20241024105938.1095358-1-daniel.lezcano@linaro.org [ rjw: Fixed up the !msg error path, added Fixes tag ] Signed-off-by: Rafael J. Wysocki --- tools/lib/thermal/commands.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/lib/thermal/commands.c b/tools/lib/thermal/commands.c index bcf0f14d035a7..4998cec793ed8 100644 --- a/tools/lib/thermal/commands.c +++ b/tools/lib/thermal/commands.c @@ -375,6 +375,7 @@ static thermal_error_t thermal_genl_auto(struct thermal_handler *th, cmd_cb_t cm struct cmd_param *param, int cmd, int flags, void *arg) { + thermal_error_t ret = THERMAL_ERROR; struct nl_msg *msg; void *hdr; @@ -385,17 +386,19 @@ static thermal_error_t thermal_genl_auto(struct thermal_handler *th, cmd_cb_t cm hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id, 0, flags, cmd, THERMAL_GENL_VERSION); if (!hdr) - return THERMAL_ERROR; + goto out; if (cmd_cb && cmd_cb(msg, param)) - return THERMAL_ERROR; + goto out; if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg)) - return THERMAL_ERROR; + goto out; + ret = THERMAL_SUCCESS; +out: nlmsg_free(msg); - return THERMAL_SUCCESS; + return ret; } thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz) From c12629f832d21e2a87f014174387e605661e91c7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:21:42 +0200 Subject: [PATCH 32/45] thermal: core: Build sorted lists instead of sorting them later Since it is not expected that multiple trip points will be crossed in one go very often (if this happens, there are too many trip points in the given thermal zone or they are checked too rarely), quite likely it is more efficient to build a sorted list of crossed trip points than to put them on an unsorted list and sort it later. Moreover, trip points are often sorted in ascending temperature order during thermal zone registration, so building a sorted list out of them is quite straightforward and relatively inexpensive. Accordingly, make handle_thermal_trip() maintain list ordering when adding trip points to the lists and get rid of separate list sorting in __thermal_zone_device_update(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/4930656.GXAFRqVoOG@rjwysocki.net --- drivers/thermal/thermal_core.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 7f0d69b333a03..4f0eafda93498 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -409,6 +408,21 @@ static void handle_critical_trips(struct thermal_zone_device *tz, tz->ops.hot(tz); } +static void add_trip_to_sorted_list(struct thermal_trip_desc *td, + struct list_head *list) +{ + struct thermal_trip_desc *entry; + + /* Assume that the new entry is likely to be the last one. */ + list_for_each_entry_reverse(entry, list, notify_list_node) { + if (entry->notify_temp <= td->notify_temp) { + list_add(&td->notify_list_node, &entry->notify_list_node); + return; + } + } + list_add(&td->notify_list_node, list); +} + static void handle_thermal_trip(struct thermal_zone_device *tz, struct thermal_trip_desc *td, struct list_head *way_up_list, @@ -438,8 +452,8 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, * In that case, the trip temperature becomes the new threshold. */ if (tz->temperature < trip->temperature - trip->hysteresis) { - list_add(&td->notify_list_node, way_down_list); td->notify_temp = trip->temperature - trip->hysteresis; + add_trip_to_sorted_list(td, way_down_list); if (trip->type == THERMAL_TRIP_PASSIVE) { tz->passive--; @@ -454,8 +468,9 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, * if the zone temperature exceeds the trip one. The new * threshold is then set to the low temperature of the trip. */ - list_add_tail(&td->notify_list_node, way_up_list); td->notify_temp = trip->temperature; + add_trip_to_sorted_list(td, way_up_list); + td->threshold -= trip->hysteresis; if (trip->type == THERMAL_TRIP_PASSIVE) @@ -519,16 +534,6 @@ static void thermal_trip_crossed(struct thermal_zone_device *tz, thermal_governor_trip_crossed(governor, tz, trip, crossed_up); } -static int thermal_trip_notify_cmp(void *not_used, const struct list_head *a, - const struct list_head *b) -{ - struct thermal_trip_desc *tda = container_of(a, struct thermal_trip_desc, - notify_list_node); - struct thermal_trip_desc *tdb = container_of(b, struct thermal_trip_desc, - notify_list_node); - return tda->notify_temp - tdb->notify_temp; -} - void __thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { @@ -581,11 +586,9 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, thermal_zone_set_trips(tz, low, high); - list_sort(NULL, &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); - list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); list_for_each_entry_reverse(td, &way_down_list, notify_list_node) thermal_trip_crossed(tz, &td->trip, governor, false); From bd32eacd955a9e5f6de9e3a168882c3ca2525d73 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:23:27 +0200 Subject: [PATCH 33/45] thermal: core: Rename trip list node in struct thermal_trip_desc Since the list node field in struct thermal_trip_desc is going to be used for purposes other than trip crossing notification, rename it to list_node. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/2201558.irdbgypaU6@rjwysocki.net --- drivers/thermal/thermal_core.c | 10 +++++----- drivers/thermal/thermal_core.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 4f0eafda93498..e3cd8cd3502b7 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -414,13 +414,13 @@ static void add_trip_to_sorted_list(struct thermal_trip_desc *td, struct thermal_trip_desc *entry; /* Assume that the new entry is likely to be the last one. */ - list_for_each_entry_reverse(entry, list, notify_list_node) { + list_for_each_entry_reverse(entry, list, list_node) { if (entry->notify_temp <= td->notify_temp) { - list_add(&td->notify_list_node, &entry->notify_list_node); + list_add(&td->list_node, &entry->list_node); return; } } - list_add(&td->notify_list_node, list); + list_add(&td->list_node, list); } static void handle_thermal_trip(struct thermal_zone_device *tz, @@ -586,10 +586,10 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, thermal_zone_set_trips(tz, low, high); - list_for_each_entry(td, &way_up_list, notify_list_node) + list_for_each_entry(td, &way_up_list, list_node) thermal_trip_crossed(tz, &td->trip, governor, true); - list_for_each_entry_reverse(td, &way_down_list, notify_list_node) + list_for_each_entry_reverse(td, &way_down_list, list_node) thermal_trip_crossed(tz, &td->trip, governor, false); if (governor->manage) diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 04d45cd97614f..553ce4d131924 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -31,7 +31,7 @@ struct thermal_trip_attrs { struct thermal_trip_desc { struct thermal_trip trip; struct thermal_trip_attrs trip_attrs; - struct list_head notify_list_node; + struct list_head list_node; struct list_head thermal_instances; int notify_temp; int threshold; From ca70d55ab0aa4510618ebf6bc5f5da5dd33736d5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:24:57 +0200 Subject: [PATCH 34/45] thermal: core: Prepare for moving trips between sorted lists Subsequently, trips will be moved between sorted lists in multiple places, so replace add_trip_to_sorted_list() with an analogous function, move_trip_to_sorted_list(), that will move a given trip to a given sorted list. To allow list_del() used in the new function to work, initialize the list_node fields in trip descriptors where applicable so they are always valid. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/2960197.e9J7NaK4W3@rjwysocki.net --- drivers/thermal/thermal_core.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e3cd8cd3502b7..9252c89ec38e5 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -408,11 +408,17 @@ static void handle_critical_trips(struct thermal_zone_device *tz, tz->ops.hot(tz); } -static void add_trip_to_sorted_list(struct thermal_trip_desc *td, - struct list_head *list) +static void move_trip_to_sorted_list(struct thermal_trip_desc *td, + struct list_head *list) { struct thermal_trip_desc *entry; + /* + * Delete upfront and then add to make relocation within the same list + * work. + */ + list_del(&td->list_node); + /* Assume that the new entry is likely to be the last one. */ list_for_each_entry_reverse(entry, list, list_node) { if (entry->notify_temp <= td->notify_temp) { @@ -453,7 +459,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, */ if (tz->temperature < trip->temperature - trip->hysteresis) { td->notify_temp = trip->temperature - trip->hysteresis; - add_trip_to_sorted_list(td, way_down_list); + move_trip_to_sorted_list(td, way_down_list); if (trip->type == THERMAL_TRIP_PASSIVE) { tz->passive--; @@ -469,7 +475,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, * threshold is then set to the low temperature of the trip. */ td->notify_temp = trip->temperature; - add_trip_to_sorted_list(td, way_up_list); + move_trip_to_sorted_list(td, way_up_list); td->threshold -= trip->hysteresis; @@ -538,7 +544,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { struct thermal_governor *governor = thermal_get_tz_governor(tz); - struct thermal_trip_desc *td; + struct thermal_trip_desc *td, *next; LIST_HEAD(way_down_list); LIST_HEAD(way_up_list); int low = -INT_MAX, high = INT_MAX; @@ -586,11 +592,15 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, thermal_zone_set_trips(tz, low, high); - list_for_each_entry(td, &way_up_list, list_node) + list_for_each_entry_safe(td, next, &way_up_list, list_node) { thermal_trip_crossed(tz, &td->trip, governor, true); + list_del_init(&td->list_node); + } - list_for_each_entry_reverse(td, &way_down_list, list_node) + list_for_each_entry_safe_reverse(td, next, &way_down_list, list_node) { thermal_trip_crossed(tz, &td->trip, governor, false); + list_del_init(&td->list_node); + } if (governor->manage) governor->manage(tz); @@ -1484,6 +1494,7 @@ thermal_zone_device_register_with_trips(const char *type, for_each_trip_desc(tz, td) { td->trip = *trip++; INIT_LIST_HEAD(&td->thermal_instances); + INIT_LIST_HEAD(&td->list_node); /* * Mark all thresholds as invalid to start with even though * this only matters for the trips that start as invalid and From e254ec292f55fd76a554b1b2be267a02691d1042 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:26:20 +0200 Subject: [PATCH 35/45] thermal: core: Rearrange __thermal_zone_device_update() In preparation for subsequent changes, move the invocations of thermal_thresholds_handle() and thermal_zone_set_trips() in __thermal_zone_device_update() after the processing of the temporary trip lists. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/3323276.44csPzL39Z@rjwysocki.net --- drivers/thermal/thermal_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 9252c89ec38e5..f83c5c67cbe50 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -588,10 +588,6 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, high = td->threshold; } - thermal_thresholds_handle(tz, &low, &high); - - thermal_zone_set_trips(tz, low, high); - list_for_each_entry_safe(td, next, &way_up_list, list_node) { thermal_trip_crossed(tz, &td->trip, governor, true); list_del_init(&td->list_node); @@ -602,6 +598,10 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, list_del_init(&td->list_node); } + thermal_thresholds_handle(tz, &low, &high); + + thermal_zone_set_trips(tz, low, high); + if (governor->manage) governor->manage(tz); From db0a46b60033a60d54e6a5b1e760aeda01bb116c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:27:56 +0200 Subject: [PATCH 36/45] thermal: core: Pass trip descriptor to thermal_trip_crossed() In preparation for subsequent changes, modify thermal_trip_crossed() to take a trip descriptor pointer instead of a pointer to struct thermal_trip and propagate this change to thermal_zone_trip_down(). No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/10547668.nUPlyArG6x@rjwysocki.net --- drivers/thermal/thermal_core.c | 12 +++++++----- drivers/thermal/thermal_core.h | 2 +- drivers/thermal/thermal_trip.c | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f83c5c67cbe50..c8144a6bb0011 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -526,10 +526,12 @@ static void thermal_governor_trip_crossed(struct thermal_governor *governor, } static void thermal_trip_crossed(struct thermal_zone_device *tz, - const struct thermal_trip *trip, + struct thermal_trip_desc *td, struct thermal_governor *governor, bool crossed_up) { + const struct thermal_trip *trip = &td->trip; + if (crossed_up) { thermal_notify_tz_trip_up(tz, trip); thermal_debug_tz_trip_up(tz, trip); @@ -589,12 +591,12 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, } list_for_each_entry_safe(td, next, &way_up_list, list_node) { - thermal_trip_crossed(tz, &td->trip, governor, true); + thermal_trip_crossed(tz, td, governor, true); list_del_init(&td->list_node); } list_for_each_entry_safe_reverse(td, next, &way_down_list, list_node) { - thermal_trip_crossed(tz, &td->trip, governor, false); + thermal_trip_crossed(tz, td, governor, false); list_del_init(&td->list_node); } @@ -664,9 +666,9 @@ void thermal_zone_device_update(struct thermal_zone_device *tz, EXPORT_SYMBOL_GPL(thermal_zone_device_update); void thermal_zone_trip_down(struct thermal_zone_device *tz, - const struct thermal_trip *trip) + struct thermal_trip_desc *td) { - thermal_trip_crossed(tz, trip, thermal_get_tz_governor(tz), false); + thermal_trip_crossed(tz, td, thermal_get_tz_governor(tz), false); } int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 553ce4d131924..e1463ee1f7dcd 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -274,7 +274,7 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, const struct thermal_trip *trip); int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); void thermal_zone_trip_down(struct thermal_zone_device *tz, - const struct thermal_trip *trip); + struct thermal_trip_desc *td); void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, struct thermal_trip *trip, int hyst); diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 09474453baa58..37db75fb959fe 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -118,7 +118,7 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, tz->passive--; WARN_ON_ONCE(tz->passive < 0); } - thermal_zone_trip_down(tz, trip); + thermal_zone_trip_down(tz, td); } /* * Invalidate the threshold to avoid triggering a spurious From 72fb849f77d94d8f60cbb2d7251ac26de8c9e090 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:29:14 +0200 Subject: [PATCH 37/45] thermal: core: Move some trip processing to thermal_trip_crossed() Notice that some processing related to trip point crossing carried out in handle_thermal_trip() and thermal_zone_set_trip_temp() may as well be done in thermal_trip_crossed(), which allows code duplication to be reduced, so change the code accordingly. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/1982859.PYKUYFuaPT@rjwysocki.net --- drivers/thermal/thermal_core.c | 21 ++++++++++----------- drivers/thermal/thermal_trip.c | 17 ++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index c8144a6bb0011..277076658ccdc 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -460,11 +460,6 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, if (tz->temperature < trip->temperature - trip->hysteresis) { td->notify_temp = trip->temperature - trip->hysteresis; move_trip_to_sorted_list(td, way_down_list); - - if (trip->type == THERMAL_TRIP_PASSIVE) { - tz->passive--; - WARN_ON(tz->passive < 0); - } } else { td->threshold -= trip->hysteresis; } @@ -478,12 +473,6 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, move_trip_to_sorted_list(td, way_up_list); td->threshold -= trip->hysteresis; - - if (trip->type == THERMAL_TRIP_PASSIVE) - tz->passive++; - else if (trip->type == THERMAL_TRIP_CRITICAL || - trip->type == THERMAL_TRIP_HOT) - handle_critical_trips(tz, trip); } } @@ -533,9 +522,19 @@ static void thermal_trip_crossed(struct thermal_zone_device *tz, const struct thermal_trip *trip = &td->trip; if (crossed_up) { + if (trip->type == THERMAL_TRIP_PASSIVE) + tz->passive++; + else if (trip->type == THERMAL_TRIP_CRITICAL || + trip->type == THERMAL_TRIP_HOT) + handle_critical_trips(tz, trip); + thermal_notify_tz_trip_up(tz, trip); thermal_debug_tz_trip_up(tz, trip); } else { + if (trip->type == THERMAL_TRIP_PASSIVE) { + tz->passive--; + WARN_ON(tz->passive < 0); + } thermal_notify_tz_trip_down(tz, trip); thermal_debug_tz_trip_down(tz, trip); } diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 37db75fb959fe..5cafea8dc8040 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -108,18 +108,13 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, if (temp == THERMAL_TEMP_INVALID) { struct thermal_trip_desc *td = trip_to_trip_desc(trip); - if (tz->temperature >= td->threshold) { - /* - * The trip has been crossed on the way up, so some - * adjustments are needed to compensate for the lack - * of it going forward. - */ - if (trip->type == THERMAL_TRIP_PASSIVE) { - tz->passive--; - WARN_ON_ONCE(tz->passive < 0); - } + /* + * If the trip has been crossed on the way up, some adjustments + * are needed to compensate for the lack of it going forward. + */ + if (tz->temperature >= td->threshold) thermal_zone_trip_down(tz, td); - } + /* * Invalidate the threshold to avoid triggering a spurious * trip crossing notification when the trip becomes valid. From e654a0c58d828eae9b4d704972e0050871a61c07 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:32:13 +0200 Subject: [PATCH 38/45] thermal: core: Relocate functions that update trip points In preparation for subsequent changes, move two functions used for updating trip points, thermal_zone_set_trip_temp() and thermal_zone_set_trip_hyst(), to thermal_core.c. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/3248558.5fSG56mABF@rjwysocki.net --- drivers/thermal/thermal_core.c | 35 ++++++++++++++++++++++++++++++++++ drivers/thermal/thermal_trip.c | 35 ---------------------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 277076658ccdc..19c5eced452fb 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -541,6 +541,41 @@ static void thermal_trip_crossed(struct thermal_zone_device *tz, thermal_governor_trip_crossed(governor, tz, trip, crossed_up); } +void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, + struct thermal_trip *trip, int hyst) +{ + WRITE_ONCE(trip->hysteresis, hyst); + thermal_notify_tz_trip_change(tz, trip); +} + +void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, + struct thermal_trip *trip, int temp) +{ + if (trip->temperature == temp) + return; + + WRITE_ONCE(trip->temperature, temp); + thermal_notify_tz_trip_change(tz, trip); + + if (temp == THERMAL_TEMP_INVALID) { + struct thermal_trip_desc *td = trip_to_trip_desc(trip); + + /* + * If the trip has been crossed on the way up, some adjustments + * are needed to compensate for the lack of it going forward. + */ + if (tz->temperature >= td->threshold) + thermal_zone_trip_down(tz, td); + + /* + * Invalidate the threshold to avoid triggering a spurious + * trip crossing notification when the trip becomes valid. + */ + td->threshold = INT_MAX; + } +} +EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); + void __thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 5cafea8dc8040..4b8238468b534 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -88,38 +88,3 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, */ return trip_to_trip_desc(trip) - tz->trips; } - -void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, - struct thermal_trip *trip, int hyst) -{ - WRITE_ONCE(trip->hysteresis, hyst); - thermal_notify_tz_trip_change(tz, trip); -} - -void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, - struct thermal_trip *trip, int temp) -{ - if (trip->temperature == temp) - return; - - WRITE_ONCE(trip->temperature, temp); - thermal_notify_tz_trip_change(tz, trip); - - if (temp == THERMAL_TEMP_INVALID) { - struct thermal_trip_desc *td = trip_to_trip_desc(trip); - - /* - * If the trip has been crossed on the way up, some adjustments - * are needed to compensate for the lack of it going forward. - */ - if (tz->temperature >= td->threshold) - thermal_zone_trip_down(tz, td); - - /* - * Invalidate the threshold to avoid triggering a spurious - * trip crossing notification when the trip becomes valid. - */ - td->threshold = INT_MAX; - } -} -EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); From a44b5e39e41e0021925cd772d164de6ef9732f93 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:33:43 +0200 Subject: [PATCH 39/45] thermal: core: Eliminate thermal_zone_trip_down() Since thermal_zone_set_trip_temp() is now located in the same file as thermal_trip_crossed(), it can invoke the latter directly without using the thermal_zone_trip_down() wrapper that has no other users. Update thermal_zone_set_trip_temp() accordingly and drop thermal_zone_trip_down(). No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/1807510.VLH7GnMWUR@rjwysocki.net --- drivers/thermal/thermal_core.c | 8 +------- drivers/thermal/thermal_core.h | 2 -- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 19c5eced452fb..80e2974946dbb 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -565,7 +565,7 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, * are needed to compensate for the lack of it going forward. */ if (tz->temperature >= td->threshold) - thermal_zone_trip_down(tz, td); + thermal_trip_crossed(tz, td, thermal_get_tz_governor(tz), false); /* * Invalidate the threshold to avoid triggering a spurious @@ -699,12 +699,6 @@ void thermal_zone_device_update(struct thermal_zone_device *tz, } EXPORT_SYMBOL_GPL(thermal_zone_device_update); -void thermal_zone_trip_down(struct thermal_zone_device *tz, - struct thermal_trip_desc *td) -{ - thermal_trip_crossed(tz, td, thermal_get_tz_governor(tz), false); -} - int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), void *data) { diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index e1463ee1f7dcd..c4759f7222f6c 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -273,8 +273,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high); int thermal_zone_trip_id(const struct thermal_zone_device *tz, const struct thermal_trip *trip); int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); -void thermal_zone_trip_down(struct thermal_zone_device *tz, - struct thermal_trip_desc *td); void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, struct thermal_trip *trip, int hyst); From 6d5537d40ce4b95c0de5d957fc0eb51fd9b6c06f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:35:11 +0200 Subject: [PATCH 40/45] thermal: core: Use trip lists for trip crossing detection Modify the thermal core to use three lists of trip points: trips_high, containing trips with thresholds strictly above the current thermal zone temperature, trips_reached, containing trips with thresholds at or below the current zone temperature, trips_invalid, containing trips with temperature equal to THERMAL_ZONE_INVALID, where the first two lists are always sorted by the current trip threshold. For each trip in trips_high, there is no mitigation under way and the trip threshold is equal to its temperature. In turn, for each trip in trips_reached, there is mitigation under way and the trip threshold is equal to its low temperature. The trips in trips_invalid, of course, need not be taken into consideration. The idea is to make __thermal_zone_device_update() walk trips_high and trips_reached instead of walking the entire table of trip points in a thermal zone. Usually, it will only need to walk a few entries in one of the lists and check one entry in the other list, depending on the direction of the zone temperature changes, because crossing many trips by the zone temperature in one go between two consecutive temperature checks should be unlikely (if it occurs often, the thermal zone temperature should probably be checked more often either or there are too many trips). This also helps to eliminate one temporary trip list used for trip crossing notification (only one temporary list is needed for this purpose instead of two) and the remaining temporary list may be sorted by the current trip threshold value, like the trips_reached list, so the additional notify_temp field in struct thermal_trip_desc is not necessary any more. Moreover, since the trips_reached and trips_high lists are sorted, the "low" and "high" values needed by thermal_zone_set_trips() can be determined in a straightforward way by looking at one end of each list. Of course, additional work is needed in some places in order to maintain the ordering of the lists, but it is limited to situations that should be rare, like updating a trip point temperature or hysteresis, thermal zone initialization, or system resume. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/2003443.usQuhbGJ8B@rjwysocki.net [ rjw: Added a comment to thermal_zone_handle_trips() ] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_core.c | 212 ++++++++++++++++++++------------- drivers/thermal/thermal_core.h | 7 +- 2 files changed, 137 insertions(+), 82 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 80e2974946dbb..7ce4140dc2d3b 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -421,7 +421,7 @@ static void move_trip_to_sorted_list(struct thermal_trip_desc *td, /* Assume that the new entry is likely to be the last one. */ list_for_each_entry_reverse(entry, list, list_node) { - if (entry->notify_temp <= td->notify_temp) { + if (entry->threshold <= td->threshold) { list_add(&td->list_node, &entry->list_node); return; } @@ -429,53 +429,6 @@ static void move_trip_to_sorted_list(struct thermal_trip_desc *td, list_add(&td->list_node, list); } -static void handle_thermal_trip(struct thermal_zone_device *tz, - struct thermal_trip_desc *td, - struct list_head *way_up_list, - struct list_head *way_down_list) -{ - const struct thermal_trip *trip = &td->trip; - int old_threshold; - - if (trip->temperature == THERMAL_TEMP_INVALID) - return; - - /* - * If the trip temperature or hysteresis has been updated recently, - * the threshold needs to be computed again using the new values. - * However, its initial value still reflects the old ones and that - * is what needs to be compared with the previous zone temperature - * to decide which action to take. - */ - old_threshold = td->threshold; - td->threshold = trip->temperature; - - if (tz->last_temperature >= old_threshold && - tz->last_temperature != THERMAL_TEMP_INIT) { - /* - * Mitigation is under way, so it needs to stop if the zone - * temperature falls below the low temperature of the trip. - * In that case, the trip temperature becomes the new threshold. - */ - if (tz->temperature < trip->temperature - trip->hysteresis) { - td->notify_temp = trip->temperature - trip->hysteresis; - move_trip_to_sorted_list(td, way_down_list); - } else { - td->threshold -= trip->hysteresis; - } - } else if (tz->temperature >= trip->temperature) { - /* - * There is no mitigation under way, so it needs to be started - * if the zone temperature exceeds the trip one. The new - * threshold is then set to the low temperature of the trip. - */ - td->notify_temp = trip->temperature; - move_trip_to_sorted_list(td, way_up_list); - - td->threshold -= trip->hysteresis; - } -} - static void thermal_zone_device_check(struct work_struct *work) { struct thermal_zone_device *tz = container_of(work, struct @@ -484,9 +437,30 @@ static void thermal_zone_device_check(struct work_struct *work) thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } +static void move_to_trips_high(struct thermal_zone_device *tz, + struct thermal_trip_desc *td) +{ + td->threshold = td->trip.temperature; + move_trip_to_sorted_list(td, &tz->trips_high); +} + +static void move_to_trips_reached(struct thermal_zone_device *tz, + struct thermal_trip_desc *td) +{ + td->threshold = td->trip.temperature - td->trip.hysteresis; + move_trip_to_sorted_list(td, &tz->trips_reached); +} + +static void move_to_trips_invalid(struct thermal_zone_device *tz, + struct thermal_trip_desc *td) +{ + td->threshold = INT_MAX; + list_move(&td->list_node, &tz->trips_invalid); +} + static void thermal_zone_device_init(struct thermal_zone_device *tz) { - struct thermal_trip_desc *td; + struct thermal_trip_desc *td, *next; INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); @@ -500,6 +474,21 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz) list_for_each_entry(instance, &td->thermal_instances, trip_node) instance->initialized = false; } + /* + * At this point, all valid trips need to be moved to trips_high so that + * mitigation can be started if the zone temperature is above them. + */ + list_for_each_entry_safe(td, next, &tz->trips_invalid, list_node) { + if (td->trip.temperature != THERMAL_TEMP_INVALID) + move_to_trips_high(tz, td); + } + /* The trips_reached list may not be empty during system resume. */ + list_for_each_entry_safe(td, next, &tz->trips_reached, list_node) { + if (td->trip.temperature == THERMAL_TEMP_INVALID) + move_to_trips_invalid(tz, td); + else + move_to_trips_high(tz, td); + } } static void thermal_governor_trip_crossed(struct thermal_governor *governor, @@ -544,45 +533,121 @@ static void thermal_trip_crossed(struct thermal_zone_device *tz, void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, struct thermal_trip *trip, int hyst) { + struct thermal_trip_desc *td = trip_to_trip_desc(trip); + WRITE_ONCE(trip->hysteresis, hyst); thermal_notify_tz_trip_change(tz, trip); + /* + * If the zone temperature is above or at the trip tmperature, the trip + * is in the trips_reached list and its threshold is equal to its low + * temperature. It needs to stay in that list, but its threshold needs + * to be updated and the list ordering may need to be restored. + */ + if (tz->temperature >= td->threshold) + move_to_trips_reached(tz, td); } void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, struct thermal_trip *trip, int temp) { - if (trip->temperature == temp) + struct thermal_trip_desc *td = trip_to_trip_desc(trip); + int old_temp = trip->temperature; + + if (old_temp == temp) return; WRITE_ONCE(trip->temperature, temp); thermal_notify_tz_trip_change(tz, trip); - if (temp == THERMAL_TEMP_INVALID) { - struct thermal_trip_desc *td = trip_to_trip_desc(trip); + if (old_temp == THERMAL_TEMP_INVALID) { + /* + * The trip was invalid before the change, so move it to the + * trips_high list regardless of the new temperature value + * because there is no mitigation under way for it. If a + * mitigation needs to be started, the trip will be moved to the + * trips_reached list later. + */ + move_to_trips_high(tz, td); + return; + } + if (temp == THERMAL_TEMP_INVALID) { /* - * If the trip has been crossed on the way up, some adjustments - * are needed to compensate for the lack of it going forward. + * If the trip is in the trips_reached list, mitigation is under + * way for it and it needs to be stopped because the trip is + * effectively going away. */ if (tz->temperature >= td->threshold) thermal_trip_crossed(tz, td, thermal_get_tz_governor(tz), false); + move_to_trips_invalid(tz, td); + return; + } + + /* + * The trip stays on its current list, but its threshold needs to be + * updated due to the temperature change and the list ordering may need + * to be restored. + */ + if (tz->temperature >= td->threshold) + move_to_trips_reached(tz, td); + else + move_to_trips_high(tz, td); +} +EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); + +static void thermal_zone_handle_trips(struct thermal_zone_device *tz, + struct thermal_governor *governor, + int *low, int *high) +{ + struct thermal_trip_desc *td, *next; + LIST_HEAD(way_down_list); + + /* Check the trips that were below or at the zone temperature. */ + list_for_each_entry_safe_reverse(td, next, &tz->trips_reached, list_node) { + if (td->threshold <= tz->temperature) + break; + + thermal_trip_crossed(tz, td, governor, false); + /* + * The current trips_high list needs to be processed before + * adding new entries to it, so put them on a temporary list. + */ + list_move(&td->list_node, &way_down_list); + } + /* Check the trips that were previously above the zone temperature. */ + list_for_each_entry_safe(td, next, &tz->trips_high, list_node) { + if (td->threshold > tz->temperature) + break; + + thermal_trip_crossed(tz, td, governor, true); + move_to_trips_reached(tz, td); + } + /* Move all of the trips from the temporary list to trips_high. */ + list_for_each_entry_safe(td, next, &way_down_list, list_node) + move_to_trips_high(tz, td); + + if (!list_empty(&tz->trips_reached)) { + td = list_last_entry(&tz->trips_reached, + struct thermal_trip_desc, list_node); /* - * Invalidate the threshold to avoid triggering a spurious - * trip crossing notification when the trip becomes valid. + * Set the "low" value below the current trip threshold in case + * the zone temperature is at that threshold and stays there, + * which would trigger a new interrupt immediately in vain. */ - td->threshold = INT_MAX; + *low = td->threshold - 1; + } + if (!list_empty(&tz->trips_high)) { + td = list_first_entry(&tz->trips_high, + struct thermal_trip_desc, list_node); + *high = td->threshold; } } -EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); void __thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { struct thermal_governor *governor = thermal_get_tz_governor(tz); - struct thermal_trip_desc *td, *next; - LIST_HEAD(way_down_list); - LIST_HEAD(way_up_list); int low = -INT_MAX, high = INT_MAX; int temp, ret; @@ -614,25 +679,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, tz->notify_event = event; - for_each_trip_desc(tz, td) { - handle_thermal_trip(tz, td, &way_up_list, &way_down_list); - - if (td->threshold <= tz->temperature && td->threshold > low) - low = td->threshold; - - if (td->threshold >= tz->temperature && td->threshold < high) - high = td->threshold; - } - - list_for_each_entry_safe(td, next, &way_up_list, list_node) { - thermal_trip_crossed(tz, td, governor, true); - list_del_init(&td->list_node); - } - - list_for_each_entry_safe_reverse(td, next, &way_down_list, list_node) { - thermal_trip_crossed(tz, td, governor, false); - list_del_init(&td->list_node); - } + thermal_zone_handle_trips(tz, governor, &low, &high); thermal_thresholds_handle(tz, &low, &high); @@ -1501,6 +1548,9 @@ thermal_zone_device_register_with_trips(const char *type, } INIT_LIST_HEAD(&tz->node); + INIT_LIST_HEAD(&tz->trips_high); + INIT_LIST_HEAD(&tz->trips_reached); + INIT_LIST_HEAD(&tz->trips_invalid); ida_init(&tz->ida); mutex_init(&tz->lock); init_completion(&tz->removal); @@ -1530,7 +1580,7 @@ thermal_zone_device_register_with_trips(const char *type, * this only matters for the trips that start as invalid and * become valid later. */ - td->threshold = INT_MAX; + move_to_trips_invalid(tz, td); } tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index c4759f7222f6c..be271e7c8f414 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -33,7 +33,6 @@ struct thermal_trip_desc { struct thermal_trip_attrs trip_attrs; struct list_head list_node; struct list_head thermal_instances; - int notify_temp; int threshold; }; @@ -78,6 +77,9 @@ struct thermal_governor { * @device: &struct device for this thermal zone * @removal: removal completion * @resume: resume completion + * @trips_high: trips above the current zone temperature + * @trips_reached: trips below or at the current zone temperature + * @trips_invalid: trips with invalid temperature * @mode: current mode of this thermal zone * @devdata: private pointer for device private data * @num_trips: number of trip points the thermal zone supports @@ -118,6 +120,9 @@ struct thermal_zone_device { struct completion removal; struct completion resume; struct attribute_group trips_attribute_group; + struct list_head trips_high; + struct list_head trips_reached; + struct list_head trips_invalid; enum thermal_device_mode mode; void *devdata; int num_trips; From 43bac1026fdc0251e03e387fc347f84363e3780f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 16 Oct 2024 13:36:03 +0200 Subject: [PATCH 41/45] thermal: core: Relocate thermal zone initialization routine Move thermal_zone_device_init() along with thermal_zone_device_check() closer to the callers of the former, where they fit better together. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/1906685.CQOukoFCf9@rjwysocki.net --- drivers/thermal/thermal_core.c | 82 +++++++++++++++++----------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 7ce4140dc2d3b..19a3894ad752a 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -429,14 +429,6 @@ static void move_trip_to_sorted_list(struct thermal_trip_desc *td, list_add(&td->list_node, list); } -static void thermal_zone_device_check(struct work_struct *work) -{ - struct thermal_zone_device *tz = container_of(work, struct - thermal_zone_device, - poll_queue.work); - thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); -} - static void move_to_trips_high(struct thermal_zone_device *tz, struct thermal_trip_desc *td) { @@ -458,39 +450,6 @@ static void move_to_trips_invalid(struct thermal_zone_device *tz, list_move(&td->list_node, &tz->trips_invalid); } -static void thermal_zone_device_init(struct thermal_zone_device *tz) -{ - struct thermal_trip_desc *td, *next; - - INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); - - tz->temperature = THERMAL_TEMP_INIT; - tz->passive = 0; - tz->prev_low_trip = -INT_MAX; - tz->prev_high_trip = INT_MAX; - for_each_trip_desc(tz, td) { - struct thermal_instance *instance; - - list_for_each_entry(instance, &td->thermal_instances, trip_node) - instance->initialized = false; - } - /* - * At this point, all valid trips need to be moved to trips_high so that - * mitigation can be started if the zone temperature is above them. - */ - list_for_each_entry_safe(td, next, &tz->trips_invalid, list_node) { - if (td->trip.temperature != THERMAL_TEMP_INVALID) - move_to_trips_high(tz, td); - } - /* The trips_reached list may not be empty during system resume. */ - list_for_each_entry_safe(td, next, &tz->trips_reached, list_node) { - if (td->trip.temperature == THERMAL_TEMP_INVALID) - move_to_trips_invalid(tz, td); - else - move_to_trips_high(tz, td); - } -} - static void thermal_governor_trip_crossed(struct thermal_governor *governor, struct thermal_zone_device *tz, const struct thermal_trip *trip, @@ -1426,6 +1385,47 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) } EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); +static void thermal_zone_device_check(struct work_struct *work) +{ + struct thermal_zone_device *tz = container_of(work, struct + thermal_zone_device, + poll_queue.work); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); +} + +static void thermal_zone_device_init(struct thermal_zone_device *tz) +{ + struct thermal_trip_desc *td, *next; + + INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); + + tz->temperature = THERMAL_TEMP_INIT; + tz->passive = 0; + tz->prev_low_trip = -INT_MAX; + tz->prev_high_trip = INT_MAX; + for_each_trip_desc(tz, td) { + struct thermal_instance *instance; + + list_for_each_entry(instance, &td->thermal_instances, trip_node) + instance->initialized = false; + } + /* + * At this point, all valid trips need to be moved to trips_high so that + * mitigation can be started if the zone temperature is above them. + */ + list_for_each_entry_safe(td, next, &tz->trips_invalid, list_node) { + if (td->trip.temperature != THERMAL_TEMP_INVALID) + move_to_trips_high(tz, td); + } + /* The trips_reached list may not be empty during system resume. */ + list_for_each_entry_safe(td, next, &tz->trips_reached, list_node) { + if (td->trip.temperature == THERMAL_TEMP_INVALID) + move_to_trips_invalid(tz, td); + else + move_to_trips_high(tz, td); + } +} + static int thermal_zone_init_governor(struct thermal_zone_device *tz) { struct thermal_governor *governor; From 08eb0493f6b3583b2efc90665a3e6980faee56a9 Mon Sep 17 00:00:00 2001 From: ZhengShaobo Date: Mon, 21 Oct 2024 20:11:38 +0800 Subject: [PATCH 42/45] thermal: gov_power_allocator: Granted power set to max when nobody request power When total_req_power is 0, divvy_up_power() will set granted_power to 0, and cdev will be limited to the lowest performance. If our polling delay is set to 200ms, it means that cdev cannot perform better within 200ms even if cdev has a sudden load. This will affect the performance of cdev and is not as expected. For this reason, if nobody requests power, then set the granted power to the max_power. Signed-off-by: ZhengShaobo Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/20241021121138.422-1-zhengshaobo1@xiaomi.com [ rjw: Fixed up tags ] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/gov_power_allocator.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index 5cb03923fa8f2..ac6fa6b8f99f0 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -354,11 +354,19 @@ static void divvy_up_power(struct power_actor *power, int num_actors, u32 extra_power = 0; int i; - /* - * Prevent division by 0 if none of the actors request power. - */ - if (!total_req_power) - total_req_power = 1; + if (!total_req_power) { + /* + * Nobody requested anything, just give everybody + * the maximum power + */ + for (i = 0; i < num_actors; i++) { + struct power_actor *pa = &power[i]; + + pa->granted_power = pa->max_power; + } + + return; + } for (i = 0; i < num_actors; i++) { struct power_actor *pa = &power[i]; From 9782dd101f343d7f1cad824dc26ad355341e1f3b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 10 Nov 2024 14:04:47 +0100 Subject: [PATCH 43/45] thermal: testing: Simplify tt_get_tt_zone() Notice that tt_get_tt_zone() need not use the ret variable in the tt_thermal_zones list walk and make it return right after incrementing the reference counter of the matching entry. No functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/6120304.lOV4Wx5bFT@rjwysocki.net --- drivers/thermal/testing/zone.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c index c6d8c66f40f98..fb83d2501284d 100644 --- a/drivers/thermal/testing/zone.c +++ b/drivers/thermal/testing/zone.c @@ -288,19 +288,14 @@ static struct tt_thermal_zone *tt_get_tt_zone(const char *arg) guard(mutex)(&tt_thermal_zones_lock); - ret = -EINVAL; list_for_each_entry(tt_zone, &tt_thermal_zones, list_node) { if (tt_zone->id == id) { tt_zone->refcount++; - ret = 0; - break; + return tt_zone; } } - if (ret) - return ERR_PTR(ret); - - return tt_zone; + return ERR_PTR(-EINVAL); } static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone) From 1c426fd09ca85fb77f120f7933e39eb9df99a39a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 10 Nov 2024 14:06:01 +0100 Subject: [PATCH 44/45] thermal: testing: Use DEFINE_FREE() and __free() to simplify code Use DEFINE_FREE() to define a __free function for dropping thermal zone template reference counters and use it along with __free() to simplify code in some places. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/4628747.LvFx2qVVIh@rjwysocki.net [ rjw: Add variable initialization to address compiler warning ] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/testing/zone.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c index fb83d2501284d..2619d3df5bd9d 100644 --- a/drivers/thermal/testing/zone.c +++ b/drivers/thermal/testing/zone.c @@ -305,6 +305,9 @@ static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone) tt_zone->refcount--; } +DEFINE_FREE(put_tt_zone, struct tt_thermal_zone *, + if (!IS_ERR_OR_NULL(_T)) tt_put_tt_zone(_T)) + static void tt_zone_add_trip_work_fn(struct work_struct *work) { struct tt_work *tt_work = tt_work_of_work(work); @@ -327,9 +330,9 @@ static void tt_zone_add_trip_work_fn(struct work_struct *work) int tt_zone_add_trip(const char *arg) { + struct tt_thermal_zone *tt_zone __free(put_tt_zone) = NULL; struct tt_work *tt_work __free(kfree); struct tt_trip *tt_trip __free(kfree); - struct tt_thermal_zone *tt_zone; int id; tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); @@ -345,10 +348,8 @@ int tt_zone_add_trip(const char *arg) return PTR_ERR(tt_zone); id = ida_alloc(&tt_zone->ida, GFP_KERNEL); - if (id < 0) { - tt_put_tt_zone(tt_zone); + if (id < 0) return id; - } tt_trip->trip.type = THERMAL_TRIP_ACTIVE; tt_trip->trip.temperature = THERMAL_TEMP_INVALID; @@ -361,7 +362,7 @@ int tt_zone_add_trip(const char *arg) tt_zone->num_trips++; INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn); - tt_work->tt_zone = tt_zone; + tt_work->tt_zone = no_free_ptr(tt_zone); tt_work->tt_trip = no_free_ptr(tt_trip); schedule_work(&(no_free_ptr(tt_work)->work)); @@ -420,23 +421,18 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) int tt_zone_reg(const char *arg) { - struct tt_thermal_zone *tt_zone; - int ret; + struct tt_thermal_zone *tt_zone __free(put_tt_zone); tt_zone = tt_get_tt_zone(arg); if (IS_ERR(tt_zone)) return PTR_ERR(tt_zone); - ret = tt_zone_register_tz(tt_zone); - - tt_put_tt_zone(tt_zone); - - return ret; + return tt_zone_register_tz(tt_zone); } int tt_zone_unreg(const char *arg) { - struct tt_thermal_zone *tt_zone; + struct tt_thermal_zone *tt_zone __free(put_tt_zone); tt_zone = tt_get_tt_zone(arg); if (IS_ERR(tt_zone)) @@ -444,8 +440,6 @@ int tt_zone_unreg(const char *arg) tt_zone_unregister_tz(tt_zone); - tt_put_tt_zone(tt_zone); - return 0; } From 0104dcdaad3a7afd141e79a5fb817a92ada910ac Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 14 Nov 2024 17:25:09 +0100 Subject: [PATCH 45/45] thermal: testing: Initialize some variables annoteded with _free() Variables annotated with __free() need to be initialized if the function can return before they get updated for the first time or the attempt to free the memory pointed to by them upon function return may crash the kernel. Fix this issue in some places in the thermal testing code. Fixes: f6a034f2df42 ("thermal: Introduce a debugfs-based testing facility") Reported-by: Dan Carpenter Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/12576267.O9o76ZdvQC@rjwysocki.net --- drivers/thermal/testing/zone.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c index 2619d3df5bd9d..1f4e450100e2f 100644 --- a/drivers/thermal/testing/zone.c +++ b/drivers/thermal/testing/zone.c @@ -185,7 +185,7 @@ static void tt_add_tz_work_fn(struct work_struct *work) int tt_add_tz(void) { struct tt_thermal_zone *tt_zone __free(kfree); - struct tt_work *tt_work __free(kfree); + struct tt_work *tt_work __free(kfree) = NULL; int ret; tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL); @@ -237,7 +237,7 @@ static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone) int tt_del_tz(const char *arg) { - struct tt_work *tt_work __free(kfree); + struct tt_work *tt_work __free(kfree) = NULL; struct tt_thermal_zone *tt_zone, *aux; int ret; int id; @@ -331,8 +331,8 @@ static void tt_zone_add_trip_work_fn(struct work_struct *work) int tt_zone_add_trip(const char *arg) { struct tt_thermal_zone *tt_zone __free(put_tt_zone) = NULL; + struct tt_trip *tt_trip __free(kfree) = NULL; struct tt_work *tt_work __free(kfree); - struct tt_trip *tt_trip __free(kfree); int id; tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); @@ -387,7 +387,7 @@ static struct thermal_zone_device_ops tt_zone_ops = { static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) { - struct thermal_trip *trips __free(kfree); + struct thermal_trip *trips __free(kfree) = NULL; struct thermal_zone_device *tz; struct tt_trip *tt_trip; int i;