From 445936f9e258eca624c8239056bd8cd6e853b3fd Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 23 Sep 2024 11:59:57 +0200 Subject: thermal: core: Add user thresholds support The user thresholds mechanism is a way to have the userspace to tell the thermal framework to send a notification when a temperature limit is crossed. There is no id, no hysteresis, just the temperature and the direction of the limit crossing. That means we can be notified when a threshold is crossed the way up only, or the way down only or both ways. That allows to create hysteresis values if it is needed. A threshold can be added, deleted or flushed. The latter means all thresholds belonging to a thermal zone will be deleted. When a threshold is added: - if the same threshold (temperature and direction) exists, an error is returned - if a threshold is specified with the same temperature but a different direction, the specified direction is added - if there is no threshold with the same temperature then it is created When a threshold is deleted: - if the same threshold (temperature and direction) exists, it is deleted - if a threshold is specified with the same temperature but a different direction, the specified direction is removed - if there is no threshold with the same temperature, then an error is returned When the threshold are flushed: - All thresholds related to a thermal zone are deleted When a threshold is crossed: - the userspace does not need to know which threshold(s) have been crossed, it will be notified with the current temperature and the previous temperature - if multiple thresholds have been crossed between two updates only one notification will be send to the userspace, it is pointless to send a notification per thresholds crossed as the userspace can handle that easily when it has the temperature delta information Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20240923100005.2532430-2-daniel.lezcano@linaro.org [ rjw: Subject edit, use BIT(0) and BIT(1) in symbol definitions ] Signed-off-by: Rafael J. Wysocki --- include/linux/thermal.h | 3 +++ include/uapi/linux/thermal.h | 2 ++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 25ea8fe2313e..bcaa92732e14 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -56,6 +56,9 @@ enum thermal_notify_event { THERMAL_TZ_UNBIND_CDEV, /* Cooling dev is unbind from the thermal zone */ THERMAL_INSTANCE_WEIGHT_CHANGED, /* Thermal instance weight changed */ THERMAL_TZ_RESUME, /* Thermal zone is resuming after system sleep */ + THERMAL_TZ_ADD_THRESHOLD, /* Threshold added */ + THERMAL_TZ_DEL_THRESHOLD, /* Threshold deleted */ + THERMAL_TZ_FLUSH_THRESHOLDS, /* All thresholds deleted */ }; /** diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h index fc78bf3aead7..2e6f60a36173 100644 --- a/include/uapi/linux/thermal.h +++ b/include/uapi/linux/thermal.h @@ -3,6 +3,8 @@ #define _UAPI_LINUX_THERMAL_H #define THERMAL_NAME_LENGTH 20 +#define THERMAL_THRESHOLD_WAY_UP BIT(0) +#define THERMAL_THRESHOLD_WAY_DOWN BIT(1) enum thermal_device_mode { THERMAL_DEVICE_DISABLED = 0, -- cgit v1.2.3 From a5a98a786e5e31e85a69d30935254e8730734706 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 14 Oct 2024 16:59:46 +0200 Subject: 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(-) (limited to 'include') 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; -- cgit v1.2.3 From 1773572863c43a14a3e45f0591f28b7dec1ee52a Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 22 Oct 2024 17:51:42 +0200 Subject: 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(-) (limited to 'include') 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) -- cgit v1.2.3