From c4cd42ebd32b57841a0dba4b296692d201802618 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:33:28 +0200 Subject: [PATCH 01/16] 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 145b43a4dffd..b8c3a9185ae7 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; -- 2.50.1 From ee879a5ea33c49d994fc1d2a60a36afe004548ce Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:35:16 +0200 Subject: [PATCH 02/16] 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 b8c3a9185ae7..13e22fcfad18 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 f8d2e85d2408..a28f7198f917 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; -- 2.50.1 From 0dc23567c20639049ad57fd8cc2165ee9f493ab6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:39:19 +0200 Subject: [PATCH 03/16] 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 863e7a4272e6..b887e48e8c7e 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 ce0ea571ed67..d37d57d48c38 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 1b2345a697c5..46c81b147662 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 fd5527188cf9..ea4bf88d37f3 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 13e22fcfad18..363e01e12ec1 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 @@ free_mem: * 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 a28f7198f917..19a698bdbfba 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 dc374a7a1a65..403d62d3ce77 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; } -- 2.50.1 From 17f76be51c52338d9632070cce2b181a7517f03d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Oct 2024 21:42:19 +0200 Subject: [PATCH 04/16] 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 363e01e12ec1..4ea304e3ceb7 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 @@ free_mem: /** * 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 @@ free_mem: * 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, -- 2.50.1 From cba00d16a2b0b0cfc8266124266ed23ab9eae918 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:05:25 +0200 Subject: [PATCH 05/16] 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 4ea304e3ceb7..d1038d33f17e 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 19a698bdbfba..09f796fb1278 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 939d3e5f1817..d67021aeb136 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 403d62d3ce77..f2baadd8f8da 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 f0e504fd866a..37da7a8ea948 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 f3c58c708969..91f3fe8c8f07 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 1838aa729bb5..701607a953ff 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 b53fac333ec5..09474453baa5 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); -- 2.50.1 From 57f076664c4debb2fff57e42098698f1993826f4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:07:11 +0200 Subject: [PATCH 06/16] 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 d1038d33f17e..b8cd84a8c86c 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 09f796fb1278..dc84862017ea 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 -- 2.50.1 From 6f60ae72214bd57b2576c749aca5305eca158dc3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:09:18 +0200 Subject: [PATCH 07/16] 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 b8cd84a8c86c..08c227ac3d53 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); -- 2.50.1 From d1c8aa2a5c5c624f25efc78000ea9801948aea15 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:10:47 +0200 Subject: [PATCH 08/16] 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 08c227ac3d53..a78f9a81595f 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; -- 2.50.1 From 33eab804d695dc2be1491890143a64ea4d99cd42 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:12:11 +0200 Subject: [PATCH 09/16] 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 a78f9a81595f..cc1fd230b100 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); -- 2.50.1 From 6d153f52cc3452258415db6a202cc6d15daa3920 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:13:50 +0200 Subject: [PATCH 10/16] 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 cc1fd230b100..ae424eef4574 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); -- 2.50.1 From c690dce5dceef1d2818c52f3df2d3511f028632f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:15:22 +0200 Subject: [PATCH 11/16] 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 ae424eef4574..ab0c7c6396c0 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -868,6 +868,17 @@ free_mem: 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; -- 2.50.1 From c597b4e74b124aebf7e3d3f7907e2d4779d870e3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:16:54 +0200 Subject: [PATCH 12/16] 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 b887e48e8c7e..97f3d819852b 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 d37d57d48c38..4643be4f941d 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 46c81b147662..bdae60001b2c 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 dc84862017ea..04d45cd97614 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 f2baadd8f8da..030a4e4bc824 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 -- 2.50.1 From a5a98a786e5e31e85a69d30935254e8730734706 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 14 Oct 2024 16:59:46 +0200 Subject: [PATCH 13/16] 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 bdae60001b2c..5cb03923fa8f 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 ea4bf88d37f3..d1bb59f1dfbd 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 ab0c7c6396c0..f98aa396787a 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 d67021aeb136..c800504c3cfe 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 030a4e4bc824..b1152ad7acc9 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 701607a953ff..24b9055a0b6c 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 bcaa92732e14..754802478b96 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; -- 2.50.1 From af73d53e97ca5972e93ed481745d25741fa3af65 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:20:56 +0200 Subject: [PATCH 14/16] 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 f98aa396787a..6464be98df5f 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); -- 2.50.1 From dfa245f512566c8bdfbea44485471914764cf12a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 11 Oct 2024 00:22:09 +0200 Subject: [PATCH 15/16] 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 6464be98df5f..7f0d69b333a0 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) -- 2.50.1 From 1773572863c43a14a3e45f0591f28b7dec1ee52a Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 22 Oct 2024 17:51:42 +0200 Subject: [PATCH 16/16] 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 91f3fe8c8f07..315a76b01f6a 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 @@ out_cancel_nest: 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 @@ out_cancel_nest: 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 e01221e8816b..075e9ae85f3d 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 f33b6d5474d8..9b063199a789 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 232f4e8089af..cb372659a20d 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 2e6f60a36173..ba8604bdf206 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) -- 2.50.1