* [PATCH v4 0/6] Add thermal user thresholds support
@ 2024-09-23 9:59 Daniel Lezcano
2024-09-23 9:59 ` [PATCH v4 1/6] thermal/core: Add " Daniel Lezcano
` (5 more replies)
0 siblings, 6 replies; 15+ messages in thread
From: Daniel Lezcano @ 2024-09-23 9:59 UTC (permalink / raw)
To: daniel.lezcano, rafael; +Cc: linux-pm, lukasz.luba, quic_manafm
The trip points are a firmware description of the temperature limits
of a specific thermal zone where we associate an action which is done
by the kernel. The time resolution is low.
The userspace has to deal with a more complex thermal management based
on heuristics from different information coming from different
places. The logic is much more complex but based on a bigger time
resolution, usually one second based.
The purpose of the userspace is to monitor the temperatures from
different places and take actions. However, it can not be constantly
reading the temperature to detect when a temperature threshold has
been reached. This is especially bad for mobile or embedded system as
that will lead to an unacceptable number of wakeup to check the
temperature with nothing to do.
On the other side, the sensors are now most of the time interrupt
driven. That means the thermal framework will use the temperature trip
points to program the sensor to trigger an interrupt when a
temperature limit is crossed.
Unfortunately, the userspace can not benefit this feature and current
solutions found here and there, iow out-of-tree, are to add fake trip
points in the firmware and enable the writable trip points.
This is bad for different reasons, the trip points are for in-kernel
actions, the semantic of their types is used by the thermal framework
and by adding trip points in the device tree is a way to overcome the
current limitation but tampering with how the thermal framework is
supposed to work. The writable trip points is a way to adjust a
temperature limit given a specific platform if the firmware is not
accurate enough and TBH it is more a debug feature from my POV.
The user thresholds mechanism is a way to have the userspace to tell
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
temperature 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.
Those thresholds are refered as user thresholds in order to do the
difference with the trip points which are similar.
An user threshold can be added, deleted or flushed. The latter means
all user thresholds belonging to a thermal zone will be deleted.
When one or several user thresholds are crossed, an event is sent to
the userspace.
All aforementioned actions and events lead to a notification to the
userspace. A user threshold change (add, delete and flush) is notified
to the userspace with the process id responsible of the action.
Along with the kernel changes, the thermal library has been extended
to provide the different API to deal with the new user threshold
netlink events and commands.
In addition, the thermal-engine skeleton uses these new API by
flushing and adding user thresholds as well as getting the
notification about these actions.
Overall the series has been tested with the thermal-engine skeleton
and some selftests which are not part of this series.
Changelog:
V4:
- Fix missing stubs when THERMAL_NETLINK=n (kernel test robot)
V3:
- the first patch of the v2 series has been merged
- Modified the description to split the information between the
cover letter and the patch 1 description (Rafael)
- Made the thresholds code as part of the core (Rafael)
- Converted the thresholds into a list and directly declared in
the thermal zone device structure (Rafael)
- Changed the name of the field in the thermal zone device
structure to user_thresholds (Rafael)
- Added #include "thermal_thresholds.h" (Rafael)
- Combined the conditions in the function
__thermal_threshold_is_crossed (Rafael)
- Moved the function thermal_thresholds_flush() before
thermal_thresholds_exit() (Rafael)
- Change thermal_thresholds_handle() to return void (Rafael)
- Move the list field on top the of the structure threshold and
renamed it list_node (Rafael)
- Changed THERMAL_THRESHOLD_* notifications to
THERMAL_TZ_THRESHOLD_* (Rafael)
V2:
- Compute min and max in thermal_zone_device_update() but keep
the loop as it is (Rafael)
- Include slab.h to fix compilation warnings on some architectures
with kmalloc and kfree (kernel test robot)
Daniel Lezcano (6):
thermal/core: Add user thresholds support
thermal/core: Connect the threshold with the core
thermal/netlink: Add the commands and the events for the thresholds
tools/lib/thermal: Make more generic the command encoding function
tools/lib/thermal: Add the threshold netlink ABI
tools/thermal/thermal-engine: Take into account the thresholds API
drivers/thermal/Makefile | 1 +
drivers/thermal/thermal_core.c | 9 +
drivers/thermal/thermal_core.h | 2 +
drivers/thermal/thermal_netlink.c | 239 +++++++++++++++++-
drivers/thermal/thermal_netlink.h | 34 +++
drivers/thermal/thermal_thresholds.c | 235 +++++++++++++++++
drivers/thermal/thermal_thresholds.h | 19 ++
include/linux/thermal.h | 3 +
include/uapi/linux/thermal.h | 30 ++-
tools/lib/thermal/commands.c | 167 +++++++++++-
tools/lib/thermal/events.c | 58 ++++-
tools/lib/thermal/include/thermal.h | 40 +++
tools/lib/thermal/libthermal.map | 5 +
tools/lib/thermal/thermal.c | 17 ++
tools/thermal/lib/Makefile | 2 +-
tools/thermal/thermal-engine/thermal-engine.c | 109 +++++++-
16 files changed, 921 insertions(+), 49 deletions(-)
create mode 100644 drivers/thermal/thermal_thresholds.c
create mode 100644 drivers/thermal/thermal_thresholds.h
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 1/6] thermal/core: Add user thresholds support
2024-09-23 9:59 [PATCH v4 0/6] Add thermal user thresholds support Daniel Lezcano
@ 2024-09-23 9:59 ` Daniel Lezcano
2024-10-01 19:57 ` Rafael J. Wysocki
2024-09-23 9:59 ` [PATCH v4 2/6] thermal/core: Connect the threshold with the core Daniel Lezcano
` (4 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Daniel Lezcano @ 2024-09-23 9:59 UTC (permalink / raw)
To: daniel.lezcano, rafael; +Cc: linux-pm, lukasz.luba, quic_manafm
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 <daniel.lezcano@linaro.org>
---
drivers/thermal/Makefile | 1 +
drivers/thermal/thermal_core.h | 2 +
drivers/thermal/thermal_thresholds.c | 229 +++++++++++++++++++++++++++
drivers/thermal/thermal_thresholds.h | 19 +++
include/linux/thermal.h | 3 +
include/uapi/linux/thermal.h | 2 +
6 files changed, 256 insertions(+)
create mode 100644 drivers/thermal/thermal_thresholds.c
create mode 100644 drivers/thermal/thermal_thresholds.h
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 41c4d56beb40..1e1559bb971e 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -6,6 +6,7 @@ CFLAGS_thermal_core.o := -I$(src)
obj-$(CONFIG_THERMAL) += thermal_sys.o
thermal_sys-y += thermal_core.o thermal_sysfs.o
thermal_sys-y += thermal_trip.o thermal_helpers.o
+thermal_sys-y += thermal_thresholds.o
# netlink interface to manage the thermal framework
thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 50b858aa173a..8f320d17d927 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -13,6 +13,7 @@
#include <linux/thermal.h>
#include "thermal_netlink.h"
+#include "thermal_thresholds.h"
#include "thermal_debugfs.h"
struct thermal_attr {
@@ -139,6 +140,7 @@ struct thermal_zone_device {
#ifdef CONFIG_THERMAL_DEBUGFS
struct thermal_debugfs *debugfs;
#endif
+ struct list_head user_thresholds;
struct thermal_trip_desc trips[] __counted_by(num_trips);
};
diff --git a/drivers/thermal/thermal_thresholds.c b/drivers/thermal/thermal_thresholds.c
new file mode 100644
index 000000000000..f33b6d5474d8
--- /dev/null
+++ b/drivers/thermal/thermal_thresholds.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2024 Linaro Limited
+ *
+ * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
+ *
+ * Thermal thresholds
+ */
+#include <linux/list.h>
+#include <linux/list_sort.h>
+#include <linux/slab.h>
+
+#include "thermal_core.h"
+#include "thermal_thresholds.h"
+
+int thermal_thresholds_init(struct thermal_zone_device *tz)
+{
+ INIT_LIST_HEAD(&tz->user_thresholds);
+
+ return 0;
+}
+
+void thermal_thresholds_flush(struct thermal_zone_device *tz)
+{
+ struct list_head *thresholds = &tz->user_thresholds;
+ struct user_threshold *entry, *tmp;
+
+ lockdep_assert_held(&tz->lock);
+
+ list_for_each_entry_safe(entry, tmp, thresholds, list_node) {
+ list_del(&entry->list_node);
+ kfree(entry);
+ }
+
+ __thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS);
+}
+
+void thermal_thresholds_exit(struct thermal_zone_device *tz)
+{
+ thermal_thresholds_flush(tz);
+}
+
+static int __thermal_thresholds_cmp(void *data,
+ const struct list_head *l1,
+ const struct list_head *l2)
+{
+ struct user_threshold *t1 = container_of(l1, struct user_threshold, list_node);
+ struct user_threshold *t2 = container_of(l2, struct user_threshold, list_node);
+
+ return t1->temperature - t2->temperature;
+}
+
+static struct user_threshold *__thermal_thresholds_find(const struct list_head *thresholds,
+ int temperature)
+{
+ struct user_threshold *t;
+
+ list_for_each_entry(t, thresholds, list_node)
+ if (t->temperature == temperature)
+ return t;
+
+ return NULL;
+}
+
+static bool __thermal_threshold_is_crossed(struct user_threshold *threshold, int temperature,
+ int last_temperature, int direction,
+ int *low, int *high)
+{
+
+ if (temperature >= threshold->temperature) {
+ if (threshold->temperature > *low &&
+ THERMAL_THRESHOLD_WAY_DOWN & threshold->direction)
+ *low = threshold->temperature;
+
+ if (last_temperature < threshold->temperature &&
+ threshold->direction & direction)
+ return true;
+ } else {
+ if (threshold->temperature < *high && THERMAL_THRESHOLD_WAY_UP
+ & threshold->direction)
+ *high = threshold->temperature;
+
+ if (last_temperature >= threshold->temperature &&
+ threshold->direction & direction)
+ return true;
+ }
+
+ return false;
+}
+
+static bool thermal_thresholds_handle_raising(struct list_head *thresholds, int temperature,
+ int last_temperature, int *low, int *high)
+{
+ struct user_threshold *t;
+
+ list_for_each_entry(t, thresholds, list_node) {
+ if (__thermal_threshold_is_crossed(t, temperature, last_temperature,
+ THERMAL_THRESHOLD_WAY_UP, low, high))
+ return true;
+ }
+
+ return false;
+}
+
+static bool thermal_thresholds_handle_dropping(struct list_head *thresholds, int temperature,
+ int last_temperature, int *low, int *high)
+{
+ struct user_threshold *t;
+
+ list_for_each_entry_reverse(t, thresholds, list_node) {
+ if (__thermal_threshold_is_crossed(t, temperature, last_temperature,
+ THERMAL_THRESHOLD_WAY_DOWN, low, high))
+ return true;
+ }
+
+ return false;
+}
+
+void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high)
+{
+ struct list_head *thresholds = &tz->user_thresholds;
+
+ int temperature = tz->temperature;
+ int last_temperature = tz->last_temperature;
+ bool notify;
+
+ lockdep_assert_held(&tz->lock);
+
+ /*
+ * We need a second update in order to detect a threshold being crossed
+ */
+ if (last_temperature == THERMAL_TEMP_INVALID)
+ return;
+
+ /*
+ * The temperature is stable, so obviously we can not have
+ * crossed a threshold.
+ */
+ if (last_temperature == temperature)
+ return;
+
+ /*
+ * Since last update the temperature:
+ * - increased : thresholds are crossed the way up
+ * - decreased : thresholds are crossed the way down
+ */
+ if (temperature > last_temperature)
+ notify = thermal_thresholds_handle_raising(thresholds, temperature,
+ last_temperature, low, high);
+ else
+ notify = thermal_thresholds_handle_dropping(thresholds, temperature,
+ last_temperature, low, high);
+
+ if (notify)
+ pr_debug("A threshold has been crossed the way %s, with a temperature=%d, last_temperature=%d\n",
+ temperature > last_temperature ? "up" : "down", temperature, last_temperature);
+}
+
+int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction)
+{
+ struct list_head *thresholds = &tz->user_thresholds;
+ struct user_threshold *t;
+
+ lockdep_assert_held(&tz->lock);
+
+ t = __thermal_thresholds_find(thresholds, temperature);
+ if (t) {
+ if (t->direction == direction)
+ return -EEXIST;
+
+ t->direction |= direction;
+ } else {
+
+ t = kmalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&t->list_node);
+ t->temperature = temperature;
+ t->direction = direction;
+ list_add(&t->list_node, thresholds);
+ list_sort(NULL, thresholds, __thermal_thresholds_cmp);
+ }
+
+ __thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD);
+
+ return 0;
+}
+
+int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction)
+{
+ struct list_head *thresholds = &tz->user_thresholds;
+ struct user_threshold *t;
+
+ lockdep_assert_held(&tz->lock);
+
+ t = __thermal_thresholds_find(thresholds, temperature);
+ if (!t)
+ return -ENOENT;
+
+ if (t->direction == direction) {
+ list_del(&t->list_node);
+ kfree(t);
+ } else {
+ t->direction &= ~direction;
+ }
+
+ __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
+
+ return 0;
+}
+
+int thermal_thresholds_for_each(struct thermal_zone_device *tz,
+ int (*cb)(struct user_threshold *, void *arg), void *arg)
+{
+ struct list_head *thresholds = &tz->user_thresholds;
+ struct user_threshold *entry;
+ int ret;
+
+ lockdep_assert_held(&tz->lock);
+
+ list_for_each_entry(entry, thresholds, list_node) {
+ ret = cb(entry, arg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/thermal/thermal_thresholds.h b/drivers/thermal/thermal_thresholds.h
new file mode 100644
index 000000000000..232f4e8089af
--- /dev/null
+++ b/drivers/thermal/thermal_thresholds.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __THERMAL_THRESHOLDS_H__
+#define __THERMAL_THRESHOLDS_H__
+
+struct user_threshold {
+ struct list_head list_node;
+ int temperature;
+ int direction;
+};
+
+int thermal_thresholds_init(struct thermal_zone_device *tz);
+void thermal_thresholds_exit(struct thermal_zone_device *tz);
+void thermal_thresholds_flush(struct thermal_zone_device *tz);
+void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high);
+int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction);
+int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction);
+int thermal_thresholds_for_each(struct thermal_zone_device *tz,
+ int (*cb)(struct user_threshold *, void *arg), void *arg);
+#endif
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 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..3e7c1c2e71a7 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 0x1
+#define THERMAL_THRESHOLD_WAY_DOWN 0x2
enum thermal_device_mode {
THERMAL_DEVICE_DISABLED = 0,
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 2/6] thermal/core: Connect the threshold with the core
2024-09-23 9:59 [PATCH v4 0/6] Add thermal user thresholds support Daniel Lezcano
2024-09-23 9:59 ` [PATCH v4 1/6] thermal/core: Add " Daniel Lezcano
@ 2024-09-23 9:59 ` Daniel Lezcano
2024-09-23 9:59 ` [PATCH v4 3/6] thermal/netlink: Add the commands and the events for the thresholds Daniel Lezcano
` (3 subsequent siblings)
5 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2024-09-23 9:59 UTC (permalink / raw)
To: daniel.lezcano, rafael; +Cc: linux-pm, lukasz.luba, quic_manafm
Initialize, de-initialize and handle the threshold in the same place
than the trip points.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
drivers/thermal/thermal_core.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 073d02e21352..90937c23b0e6 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -585,6 +585,8 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
high = td->threshold;
}
+ thermal_thresholds_handle(tz, &low, &high);
+
thermal_zone_set_trips(tz, low, high);
list_sort(NULL, &way_up_list, thermal_trip_notify_cmp);
@@ -1490,6 +1492,10 @@ thermal_zone_device_register_with_trips(const char *type,
goto unregister;
}
+ result = thermal_thresholds_init(tz);
+ if (result)
+ goto remove_hwmon;
+
mutex_lock(&thermal_list_lock);
mutex_lock(&tz->lock);
@@ -1513,6 +1519,8 @@ thermal_zone_device_register_with_trips(const char *type,
return tz;
+remove_hwmon:
+ thermal_remove_hwmon_sysfs(tz);
unregister:
device_del(&tz->device);
release_device:
@@ -1600,6 +1608,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
thermal_set_governor(tz, NULL);
+ thermal_thresholds_exit(tz);
thermal_remove_hwmon_sysfs(tz);
ida_free(&thermal_tz_ida, tz->id);
ida_destroy(&tz->ida);
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 3/6] thermal/netlink: Add the commands and the events for the thresholds
2024-09-23 9:59 [PATCH v4 0/6] Add thermal user thresholds support Daniel Lezcano
2024-09-23 9:59 ` [PATCH v4 1/6] thermal/core: Add " Daniel Lezcano
2024-09-23 9:59 ` [PATCH v4 2/6] thermal/core: Connect the threshold with the core Daniel Lezcano
@ 2024-09-23 9:59 ` Daniel Lezcano
2024-10-02 12:53 ` Rafael J. Wysocki
2024-09-23 10:00 ` [PATCH v4 4/6] tools/lib/thermal: Make more generic the command encoding function Daniel Lezcano
` (2 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Daniel Lezcano @ 2024-09-23 9:59 UTC (permalink / raw)
To: daniel.lezcano, rafael; +Cc: linux-pm, lukasz.luba, quic_manafm
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.
As different processes in userspace can interact with the thresholds,
the process id responsible of the action (add, delete or flush) will
be added in the notification. This way a thermal engine is able to
detect if another process is interfering with the thresholds. A
process id of zero is the kernel as it is by convention usually.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
drivers/thermal/thermal_netlink.c | 239 ++++++++++++++++++++++++++-
drivers/thermal/thermal_netlink.h | 34 ++++
drivers/thermal/thermal_thresholds.c | 38 +++--
drivers/thermal/thermal_thresholds.h | 6 +-
include/uapi/linux/thermal.h | 28 +++-
5 files changed, 313 insertions(+), 32 deletions(-)
diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c
index 97157c453630..8d92ebeb72fc 100644
--- a/drivers/thermal/thermal_netlink.c
+++ b/drivers/thermal/thermal_netlink.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/kernel.h>
+#include <net/sock.h>
#include <net/genetlink.h>
#include <uapi/linux/thermal.h>
@@ -49,12 +50,19 @@ 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_WAY] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_THRESHOLD_PID] = { .type = NLA_U32 },
};
struct param {
struct nlattr **attrs;
struct sk_buff *msg;
const char *name;
+ pid_t pid;
int tz_id;
int cdev_id;
int trip_id;
@@ -62,6 +70,8 @@ struct param {
int trip_type;
int trip_hyst;
int temp;
+ int last_temp;
+ int direction;
int cdev_state;
int cdev_max_state;
struct thermal_genl_cpu_caps *cpu_capabilities;
@@ -234,6 +244,36 @@ static int thermal_genl_event_cpu_capability_change(struct param *p)
return -EMSGSIZE;
}
+static int thermal_genl_event_threshold_add(struct param *p)
+{
+ if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
+ nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp) ||
+ nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_WAY, p->direction) ||
+ nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_PID, p->pid))
+ 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),
+ nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_PID, p->pid))
+ 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_LAST_TEMP, p->last_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 +286,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 +305,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 +452,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, int pid)
+{
+ struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction, .pid = pid };
+
+ 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, int pid)
+{
+ struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction, .pid = pid };
+
+ return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DELETE, &p);
+}
+
+int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, int pid)
+{
+ struct param p = { .tz_id = tz->id, .pid = pid };
+
+ 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, .last_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, .last_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,
@@ -575,12 +663,130 @@ static int thermal_genl_cmd_cdev_get(struct param *p)
return ret;
}
+static int __thermal_genl_cmd_threshold_get(struct user_threshold *threshold, void *arg)
+{
+ struct sk_buff *msg = arg;
+
+ if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, threshold->temperature) ||
+ nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_WAY, threshold->direction))
+ return -1;
+
+ return 0;
+}
+
+static int thermal_genl_cmd_threshold_get(struct param *p)
+{
+ struct thermal_zone_device *tz;
+ 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]);
+
+ tz = thermal_zone_get_by_id(id);
+ if (!tz)
+ return -EINVAL;
+
+ start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD);
+ if (!start_trip)
+ return -EMSGSIZE;
+
+ mutex_lock(&tz->lock);
+ ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg);
+ mutex_unlock(&tz->lock);
+
+ if (ret)
+ return -EMSGSIZE;
+
+ nla_nest_end(msg, start_trip);
+
+ return 0;
+}
+
+static int thermal_genl_cmd_threshold_add(struct param *p)
+{
+ struct thermal_zone_device *tz;
+ int id, temp, direction, ret = 0;
+
+ if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
+ !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
+ !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY])
+ 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_WAY]);
+
+ tz = thermal_zone_get_by_id(id);
+ if (!tz)
+ return -EINVAL;
+
+ mutex_lock(&tz->lock);
+ ret = thermal_thresholds_add(tz, temp, direction, p->pid);
+ mutex_unlock(&tz->lock);
+
+ return ret;
+}
+
+static int thermal_genl_cmd_threshold_delete(struct param *p)
+{
+ struct thermal_zone_device *tz;
+ int id, temp, direction, ret = 0;
+
+ if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
+ !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
+ !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY])
+ 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_WAY]);
+
+ tz = thermal_zone_get_by_id(id);
+ if (!tz)
+ return -EINVAL;
+
+ mutex_lock(&tz->lock);
+ ret = thermal_thresholds_delete(tz, temp, direction, p->pid);
+ mutex_unlock(&tz->lock);
+
+ return ret;
+}
+
+static int thermal_genl_cmd_threshold_flush(struct param *p)
+{
+ struct thermal_zone_device *tz;
+ int id;
+
+ if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
+ return -EINVAL;
+
+ id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
+
+ tz = thermal_zone_get_by_id(id);
+ if (!tz)
+ return -EINVAL;
+
+ mutex_lock(&tz->lock);
+ thermal_thresholds_flush(tz, p->pid);
+ mutex_unlock(&tz->lock);
+
+ 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,
@@ -623,6 +829,7 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb,
if (!msg)
return -ENOMEM;
p.msg = msg;
+ p.pid = task_tgid_vnr(current);
hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd);
if (!hdr)
@@ -691,6 +898,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 = {
@@ -703,7 +930,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..3f85c636029c 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, pid_t pid);
+int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
+ int temperature, int direction, pid_t pid);
+int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, pid_t pid);
+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, pid_t pid)
+{
+ return 0;
+}
+
+static inline int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
+ int temperature, int direction, pid_t pid)
+{
+ return 0;
+}
+
+static inline int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, pid_t pid)
+{
+ 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..6d9a0cf8031f 100644
--- a/drivers/thermal/thermal_thresholds.c
+++ b/drivers/thermal/thermal_thresholds.c
@@ -20,7 +20,7 @@ int thermal_thresholds_init(struct thermal_zone_device *tz)
return 0;
}
-void thermal_thresholds_flush(struct thermal_zone_device *tz)
+void thermal_thresholds_flush(struct thermal_zone_device *tz, pid_t pid)
{
struct list_head *thresholds = &tz->user_thresholds;
struct user_threshold *entry, *tmp;
@@ -32,12 +32,14 @@ void thermal_thresholds_flush(struct thermal_zone_device *tz)
kfree(entry);
}
+ thermal_notify_threshold_flush(tz, pid);
+
__thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS);
}
void thermal_thresholds_exit(struct thermal_zone_device *tz)
{
- thermal_thresholds_flush(tz);
+ thermal_thresholds_flush(tz, 0);
}
static int __thermal_thresholds_cmp(void *data,
@@ -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, pid_t pid)
{
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, pid);
+
__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, pid_t pid)
{
struct list_head *thresholds = &tz->user_thresholds;
struct user_threshold *t;
@@ -205,8 +209,10 @@ int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, i
t->direction &= ~direction;
}
- __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
+ thermal_notify_threshold_delete(tz, temperature, direction, pid);
+ __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
+
return 0;
}
diff --git a/drivers/thermal/thermal_thresholds.h b/drivers/thermal/thermal_thresholds.h
index 232f4e8089af..f97ffb715d2e 100644
--- a/drivers/thermal/thermal_thresholds.h
+++ b/drivers/thermal/thermal_thresholds.h
@@ -10,10 +10,10 @@ 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);
-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);
+void thermal_thresholds_flush(struct thermal_zone_device *tz, pid_t pid);
+int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction, pid_t pid);
+int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction, pid_t pid);
int thermal_thresholds_for_each(struct thermal_zone_device *tz,
int (*cb)(struct user_threshold *, void *arg), void *arg);
#endif
diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h
index 3e7c1c2e71a7..bcbaf62a1727 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_LAST_TEMP,
THERMAL_GENL_ATTR_TZ_TRIP,
THERMAL_GENL_ATTR_TZ_TRIP_ID,
THERMAL_GENL_ATTR_TZ_TRIP_TYPE,
@@ -50,6 +51,10 @@ 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_WAY,
+ THERMAL_GENL_ATTR_THRESHOLD_PID,
__THERMAL_GENL_ATTR_MAX,
};
#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1)
@@ -77,6 +82,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 +94,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.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 4/6] tools/lib/thermal: Make more generic the command encoding function
2024-09-23 9:59 [PATCH v4 0/6] Add thermal user thresholds support Daniel Lezcano
` (2 preceding siblings ...)
2024-09-23 9:59 ` [PATCH v4 3/6] thermal/netlink: Add the commands and the events for the thresholds Daniel Lezcano
@ 2024-09-23 10:00 ` Daniel Lezcano
2024-09-23 10:00 ` [PATCH v4 5/6] tools/lib/thermal: Add the threshold netlink ABI Daniel Lezcano
2024-09-23 10:00 ` [PATCH v4 6/6] tools/thermal/thermal-engine: Take into account the thresholds API Daniel Lezcano
5 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2024-09-23 10:00 UTC (permalink / raw)
To: daniel.lezcano, rafael; +Cc: linux-pm, lukasz.luba, quic_manafm
The thermal netlink has been extended with more commands which require
an encoding with more information. The generic encoding function puts
the thermal zone id with the command name. It is the unique
parameters.
The next changes will provide more parameters to the command. Set the
scene for those new parameters by making the encoding function more
generic.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
tools/lib/thermal/commands.c | 41 ++++++++++++++++++++++++++++--------
1 file changed, 32 insertions(+), 9 deletions(-)
diff --git a/tools/lib/thermal/commands.c b/tools/lib/thermal/commands.c
index 73d4d4e8d6ec..a9223df91dcf 100644
--- a/tools/lib/thermal/commands.c
+++ b/tools/lib/thermal/commands.c
@@ -261,8 +261,23 @@ static struct genl_ops thermal_cmd_ops = {
.o_ncmds = ARRAY_SIZE(thermal_cmds),
};
-static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int cmd,
- int flags, void *arg)
+struct cmd_param {
+ int tz_id;
+};
+
+typedef int (*cmd_cb_t)(struct nl_msg *, struct cmd_param *);
+
+static int thermal_genl_tz_id_encode(struct nl_msg *msg, struct cmd_param *p)
+{
+ if (p->tz_id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
+ return -1;
+
+ return 0;
+}
+
+static thermal_error_t thermal_genl_auto(struct thermal_handler *th, cmd_cb_t cmd_cb,
+ struct cmd_param *param,
+ int cmd, int flags, void *arg)
{
struct nl_msg *msg;
void *hdr;
@@ -276,7 +291,7 @@ static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int
if (!hdr)
return THERMAL_ERROR;
- if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id))
+ if (cmd_cb && cmd_cb(msg, param))
return THERMAL_ERROR;
if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg))
@@ -289,30 +304,38 @@ static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int
thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz)
{
- return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET_ID,
+ return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_TZ_GET_ID,
NLM_F_DUMP | NLM_F_ACK, tz);
}
thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc)
{
- return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET,
+ return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_CDEV_GET,
NLM_F_DUMP | NLM_F_ACK, tc);
}
thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz)
{
- return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TRIP,
- 0, tz);
+ struct cmd_param p = { .tz_id = tz->id };
+
+ return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
+ THERMAL_GENL_CMD_TZ_GET_TRIP, 0, tz);
}
thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz)
{
- return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz);
+ struct cmd_param p = { .tz_id = tz->id };
+
+ return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
+ THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz);
}
thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz)
{
- return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz);
+ struct cmd_param p = { .tz_id = tz->id };
+
+ return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
+ THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz);
}
thermal_error_t thermal_cmd_exit(struct thermal_handler *th)
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 5/6] tools/lib/thermal: Add the threshold netlink ABI
2024-09-23 9:59 [PATCH v4 0/6] Add thermal user thresholds support Daniel Lezcano
` (3 preceding siblings ...)
2024-09-23 10:00 ` [PATCH v4 4/6] tools/lib/thermal: Make more generic the command encoding function Daniel Lezcano
@ 2024-09-23 10:00 ` Daniel Lezcano
2024-09-23 10:00 ` [PATCH v4 6/6] tools/thermal/thermal-engine: Take into account the thresholds API Daniel Lezcano
5 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2024-09-23 10:00 UTC (permalink / raw)
To: daniel.lezcano, rafael; +Cc: linux-pm, lukasz.luba, quic_manafm
The thermal framework supports the thresholds and allows the userspace
to create, delete, flush, get the list of the thresholds as well as
getting the list of the thresholds set for a specific thermal zone.
Add the netlink abstraction in the thermal library to take full
advantage of thresholds for the userspace program.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
tools/lib/thermal/commands.c | 128 +++++++++++++++++++++++++++-
tools/lib/thermal/events.c | 58 ++++++++++---
tools/lib/thermal/include/thermal.h | 40 +++++++++
tools/lib/thermal/libthermal.map | 5 ++
tools/lib/thermal/thermal.c | 17 ++++
tools/thermal/lib/Makefile | 2 +-
6 files changed, 235 insertions(+), 15 deletions(-)
diff --git a/tools/lib/thermal/commands.c b/tools/lib/thermal/commands.c
index a9223df91dcf..12539a519b81 100644
--- a/tools/lib/thermal/commands.c
+++ b/tools/lib/thermal/commands.c
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <limits.h>
#include <thermal.h>
#include "thermal_nl.h"
@@ -33,6 +34,11 @@ static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
[THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 },
[THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 },
[THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING },
+
+ /* Thresholds */
+ [THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED },
+ [THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_THRESHOLD_WAY] = { .type = NLA_U32 },
};
static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz)
@@ -182,6 +188,38 @@ static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz)
return THERMAL_SUCCESS;
}
+static int parse_threshold_get(struct genl_info *info, struct thermal_zone *tz)
+{
+ struct nlattr *attr;
+ struct thermal_threshold *__tt = NULL;
+ size_t size = 0;
+ int rem;
+
+ nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_THRESHOLD], rem) {
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_TEMP) {
+
+ size++;
+
+ __tt = realloc(__tt, sizeof(*__tt) * (size + 2));
+ if (!__tt)
+ return THERMAL_ERROR;
+
+ __tt[size - 1].temperature = nla_get_u32(attr);
+ }
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_WAY)
+ __tt[size - 1].direction = nla_get_u32(attr);
+ }
+
+ if (__tt)
+ __tt[size].temperature = INT_MAX;
+
+ tz->thresholds = __tt;
+
+ return THERMAL_SUCCESS;
+}
+
static int handle_netlink(struct nl_cache_ops *unused,
struct genl_cmd *cmd,
struct genl_info *info, void *arg)
@@ -210,6 +248,10 @@ static int handle_netlink(struct nl_cache_ops *unused,
ret = parse_tz_get_gov(info, arg);
break;
+ case THERMAL_GENL_CMD_THRESHOLD_GET:
+ ret = parse_threshold_get(info, arg);
+ break;
+
default:
return THERMAL_ERROR;
}
@@ -253,6 +295,34 @@ static struct genl_cmd thermal_cmds[] = {
.c_maxattr = THERMAL_GENL_ATTR_MAX,
.c_attr_policy = thermal_genl_policy,
},
+ {
+ .c_id = THERMAL_GENL_CMD_THRESHOLD_GET,
+ .c_name = (char *)"Get thresholds list",
+ .c_msg_parser = handle_netlink,
+ .c_maxattr = THERMAL_GENL_ATTR_MAX,
+ .c_attr_policy = thermal_genl_policy,
+ },
+ {
+ .c_id = THERMAL_GENL_CMD_THRESHOLD_ADD,
+ .c_name = (char *)"Add a threshold",
+ .c_msg_parser = handle_netlink,
+ .c_maxattr = THERMAL_GENL_ATTR_MAX,
+ .c_attr_policy = thermal_genl_policy,
+ },
+ {
+ .c_id = THERMAL_GENL_CMD_THRESHOLD_DELETE,
+ .c_name = (char *)"Delete a threshold",
+ .c_msg_parser = handle_netlink,
+ .c_maxattr = THERMAL_GENL_ATTR_MAX,
+ .c_attr_policy = thermal_genl_policy,
+ },
+ {
+ .c_id = THERMAL_GENL_CMD_THRESHOLD_FLUSH,
+ .c_name = (char *)"Flush the thresholds",
+ .c_msg_parser = handle_netlink,
+ .c_maxattr = THERMAL_GENL_ATTR_MAX,
+ .c_attr_policy = thermal_genl_policy,
+ },
};
static struct genl_ops thermal_cmd_ops = {
@@ -263,13 +333,29 @@ static struct genl_ops thermal_cmd_ops = {
struct cmd_param {
int tz_id;
+ int temp;
+ int direction;
};
typedef int (*cmd_cb_t)(struct nl_msg *, struct cmd_param *);
static int thermal_genl_tz_id_encode(struct nl_msg *msg, struct cmd_param *p)
{
- if (p->tz_id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
+ if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
+ return -1;
+
+ return 0;
+}
+
+static int thermal_genl_threshold_encode(struct nl_msg *msg, struct cmd_param *p)
+{
+ if (thermal_genl_tz_id_encode(msg, p))
+ return -1;
+
+ if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp))
+ return -1;
+
+ if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_WAY, p->direction))
return -1;
return 0;
@@ -338,6 +424,46 @@ thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_
THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz);
}
+thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th,
+ struct thermal_zone *tz)
+{
+ struct cmd_param p = { .tz_id = tz->id };
+
+ return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
+ THERMAL_GENL_CMD_THRESHOLD_GET, 0, tz);
+}
+
+thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th,
+ struct thermal_zone *tz,
+ int temperature,
+ int direction)
+{
+ struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
+
+ return thermal_genl_auto(th, thermal_genl_threshold_encode, &p,
+ THERMAL_GENL_CMD_THRESHOLD_ADD, 0, tz);
+}
+
+thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th,
+ struct thermal_zone *tz,
+ int temperature,
+ int direction)
+{
+ struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
+
+ return thermal_genl_auto(th, thermal_genl_threshold_encode, &p,
+ THERMAL_GENL_CMD_THRESHOLD_DELETE, 0, tz);
+}
+
+thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th,
+ struct thermal_zone *tz)
+{
+ struct cmd_param p = { .tz_id = tz->id };
+
+ return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
+ THERMAL_GENL_CMD_THRESHOLD_FLUSH, 0, tz);
+}
+
thermal_error_t thermal_cmd_exit(struct thermal_handler *th)
{
if (genl_unregister_family(&thermal_cmd_ops))
diff --git a/tools/lib/thermal/events.c b/tools/lib/thermal/events.c
index a7a55d1a0c4c..a009381b30ec 100644
--- a/tools/lib/thermal/events.c
+++ b/tools/lib/thermal/events.c
@@ -94,6 +94,33 @@ static int handle_thermal_event(struct nl_msg *n, void *arg)
case THERMAL_GENL_EVENT_TZ_GOV_CHANGE:
return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg);
+
+ case THERMAL_GENL_EVENT_THRESHOLD_ADD:
+ return ops->threshold_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_PID]), arg);
+
+ case THERMAL_GENL_EVENT_THRESHOLD_DELETE:
+ return ops->threshold_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_PID]), arg);
+
+ case THERMAL_GENL_EVENT_THRESHOLD_FLUSH:
+ return ops->threshold_flush(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_PID]), arg);
+
+ case THERMAL_GENL_EVENT_THRESHOLD_UP:
+ return ops->threshold_up(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_LAST_TEMP]), arg);
+
+ case THERMAL_GENL_EVENT_THRESHOLD_DOWN:
+ return ops->threshold_down(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_LAST_TEMP]), arg);
+
default:
return -1;
}
@@ -101,19 +128,24 @@ static int handle_thermal_event(struct nl_msg *n, void *arg)
static void thermal_events_ops_init(struct thermal_events_ops *ops)
{
- enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create;
- enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete;
- enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable;
- enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable;
- enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high;
- enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low;
- enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change;
- enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add;
- enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete;
- enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add;
- enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete;
- enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update;
- enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete;
+ enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add;
+ enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete;
+ enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update;
+ enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change;
+ enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_ADD] = !!ops->threshold_add;
+ enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DELETE] = !!ops->threshold_delete;
+ enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = !!ops->threshold_flush;
+ enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_UP] = !!ops->threshold_up;
+ enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DOWN] = !!ops->threshold_down;
}
thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg)
diff --git a/tools/lib/thermal/include/thermal.h b/tools/lib/thermal/include/thermal.h
index 1abc560602cf..0571ec0bf438 100644
--- a/tools/lib/thermal/include/thermal.h
+++ b/tools/lib/thermal/include/thermal.h
@@ -4,11 +4,20 @@
#define __LIBTHERMAL_H
#include <linux/thermal.h>
+#include <sys/types.h>
#ifndef LIBTHERMAL_API
#define LIBTHERMAL_API __attribute__((visibility("default")))
#endif
+#ifndef THERMAL_THRESHOLD_WAY_UP
+#define THERMAL_THRESHOLD_WAY_UP 0x1
+#endif
+
+#ifndef THERMAL_THRESHOLD_WAY_DOWN
+#define THERMAL_THRESHOLD_WAY_DOWN 0x2
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -31,6 +40,11 @@ struct thermal_events_ops {
int (*cdev_delete)(int cdev_id, void *arg);
int (*cdev_update)(int cdev_id, int cur_state, void *arg);
int (*gov_change)(int tz_id, const char *gov_name, void *arg);
+ int (*threshold_add)(int tz_id, int temperature, int direction, pid_t pid, void *arg);
+ int (*threshold_delete)(int tz_id, int temperature, int direction, pid_t pid, void *arg);
+ int (*threshold_flush)(int tz_id, pid_t pid, void *arg);
+ int (*threshold_up)(int tz_id, int temp, int last_temp, void *arg);
+ int (*threshold_down)(int tz_id, int temp, int last_temp, void *arg);
};
struct thermal_ops {
@@ -45,12 +59,18 @@ struct thermal_trip {
int hyst;
};
+struct thermal_threshold {
+ int temperature;
+ int direction;
+};
+
struct thermal_zone {
int id;
int temp;
char name[THERMAL_NAME_LENGTH];
char governor[THERMAL_NAME_LENGTH];
struct thermal_trip *trip;
+ struct thermal_threshold *thresholds;
};
struct thermal_cdev {
@@ -74,12 +94,16 @@ typedef int (*cb_tt_t)(struct thermal_trip *, void *);
typedef int (*cb_tc_t)(struct thermal_cdev *, void *);
+typedef int (*cb_th_t)(struct thermal_threshold *, void *);
+
LIBTHERMAL_API int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg);
LIBTHERMAL_API int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg);
LIBTHERMAL_API int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg);
+LIBTHERMAL_API int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg);
+
LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz,
const char *name);
@@ -124,6 +148,22 @@ LIBTHERMAL_API thermal_error_t thermal_cmd_get_governor(struct thermal_handler *
LIBTHERMAL_API thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th,
struct thermal_zone *tz);
+LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th,
+ struct thermal_zone *tz);
+
+LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th,
+ struct thermal_zone *tz,
+ int temperature,
+ int direction);
+
+LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th,
+ struct thermal_zone *tz,
+ int temperature,
+ int direction);
+
+LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th,
+ struct thermal_zone *tz);
+
/*
* Netlink thermal samples
*/
diff --git a/tools/lib/thermal/libthermal.map b/tools/lib/thermal/libthermal.map
index d5e77738c7a4..d657176aa47f 100644
--- a/tools/lib/thermal/libthermal.map
+++ b/tools/lib/thermal/libthermal.map
@@ -4,6 +4,7 @@ LIBTHERMAL_0.0.1 {
for_each_thermal_zone;
for_each_thermal_trip;
for_each_thermal_cdev;
+ for_each_thermal_threshold;
thermal_zone_find_by_name;
thermal_zone_find_by_id;
thermal_zone_discover;
@@ -17,6 +18,10 @@ LIBTHERMAL_0.0.1 {
thermal_cmd_get_trip;
thermal_cmd_get_governor;
thermal_cmd_get_temp;
+ thermal_cmd_threshold_get;
+ thermal_cmd_threshold_add;
+ thermal_cmd_threshold_delete;
+ thermal_cmd_threshold_flush;
thermal_sampling_init;
thermal_sampling_handle;
thermal_sampling_fd;
diff --git a/tools/lib/thermal/thermal.c b/tools/lib/thermal/thermal.c
index 72a76dc205bc..4851744d482e 100644
--- a/tools/lib/thermal/thermal.c
+++ b/tools/lib/thermal/thermal.c
@@ -1,10 +1,24 @@
// SPDX-License-Identifier: LGPL-2.1+
// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
#include <stdio.h>
+#include <limits.h>
#include <thermal.h>
#include "thermal_nl.h"
+int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg)
+{
+ int i, ret = 0;
+
+ if (!th)
+ return 0;
+
+ for (i = 0; th[i].temperature != INT_MAX; i++)
+ ret |= cb(&th[i], arg);
+
+ return ret;
+}
+
int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg)
{
int i, ret = 0;
@@ -80,6 +94,9 @@ static int __thermal_zone_discover(struct thermal_zone *tz, void *th)
if (thermal_cmd_get_trip(th, tz) < 0)
return -1;
+ if (thermal_cmd_threshold_get(th, tz) < 0)
+ return -1;
+
if (thermal_cmd_get_governor(th, tz))
return -1;
diff --git a/tools/thermal/lib/Makefile b/tools/thermal/lib/Makefile
index 82db451935c5..f2552f73a64c 100644
--- a/tools/thermal/lib/Makefile
+++ b/tools/thermal/lib/Makefile
@@ -3,7 +3,7 @@
LIBTHERMAL_TOOLS_VERSION = 0
LIBTHERMAL_TOOLS_PATCHLEVEL = 0
-LIBTHERMAL_TOOLS_EXTRAVERSION = 1
+LIBTHERMAL_TOOLS_EXTRAVERSION = 2
MAKEFLAGS += --no-print-directory
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 6/6] tools/thermal/thermal-engine: Take into account the thresholds API
2024-09-23 9:59 [PATCH v4 0/6] Add thermal user thresholds support Daniel Lezcano
` (4 preceding siblings ...)
2024-09-23 10:00 ` [PATCH v4 5/6] tools/lib/thermal: Add the threshold netlink ABI Daniel Lezcano
@ 2024-09-23 10:00 ` Daniel Lezcano
5 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2024-09-23 10:00 UTC (permalink / raw)
To: daniel.lezcano, rafael; +Cc: linux-pm, lukasz.luba, quic_manafm
Enhance the thermal-engine skeleton with the thresholds added in the
kernel and use the API exported by the thermal library.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
tools/thermal/thermal-engine/thermal-engine.c | 109 +++++++++++++++---
1 file changed, 96 insertions(+), 13 deletions(-)
diff --git a/tools/thermal/thermal-engine/thermal-engine.c b/tools/thermal/thermal-engine/thermal-engine.c
index 9b1476a2680f..ddc30a27acda 100644
--- a/tools/thermal/thermal-engine/thermal-engine.c
+++ b/tools/thermal/thermal-engine/thermal-engine.c
@@ -38,6 +38,14 @@ struct thermal_data {
struct thermal_handler *th;
};
+static int show_threshold(struct thermal_threshold *th, __maybe_unused void *arg)
+{
+ INFO("threshold temp=%d, direction=%d\n",
+ th->temperature, th->direction);
+
+ return 0;
+}
+
static int show_trip(struct thermal_trip *tt, __maybe_unused void *arg)
{
INFO("trip id=%d, type=%d, temp=%d, hyst=%d\n",
@@ -70,6 +78,8 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg)
for_each_thermal_trip(tz->trip, show_trip, NULL);
+ for_each_thermal_threshold(tz->thresholds, show_threshold, NULL);
+
show_temp(tz, arg);
show_governor(tz, arg);
@@ -77,6 +87,30 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg)
return 0;
}
+static int set_threshold(struct thermal_zone *tz, __maybe_unused void *arg)
+{
+ struct thermal_handler *th = arg;
+ int thresholds[] = { 43000, 65000, 49000, 55000, 57000 };
+ size_t i;
+
+ INFO("Setting threshold for thermal zone '%s', id=%d\n", tz->name, tz->id);
+
+ if (thermal_cmd_threshold_flush(th, tz)) {
+ ERROR("Failed to flush all previous thresholds\n");
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(thresholds) / sizeof(thresholds[0]); i++)
+ if (thermal_cmd_threshold_add(th, tz, thresholds[i],
+ THERMAL_THRESHOLD_WAY_UP |
+ THERMAL_THRESHOLD_WAY_DOWN)) {
+ ERROR("Failed to set threshold\n");
+ return -1;
+ }
+
+ return 0;
+}
+
static int tz_create(const char *name, int tz_id, __maybe_unused void *arg)
{
INFO("Thermal zone '%s'/%d created\n", name, tz_id);
@@ -197,20 +231,66 @@ static int gov_change(int tz_id, const char *name, __maybe_unused void *arg)
return 0;
}
+static int threshold_add(int tz_id, int temp, int direction,
+ pid_t pid, __maybe_unused void *arg)
+{
+ INFO("Threshold added by pid=%d, tz_id=%d: temp=%d, direction=%d\n",
+ pid, tz_id, temp, direction);
+
+ return 0;
+}
+
+static int threshold_delete(int tz_id, int temp, int direction,
+ pid_t pid, __maybe_unused void *arg)
+{
+ INFO("Threshold deleted by pid=%d, tz_id=%d: temp=%d, direction=%d\n",
+ pid, tz_id, temp, direction);
+
+ return 0;
+}
+
+static int threshold_flush(int tz_id, pid_t pid, __maybe_unused void *arg)
+{
+ INFO("Thresholds flushed by pid=%d, tz_id=%d\n", pid, tz_id);
+
+ return 0;
+}
+
+static int threshold_up(int tz_id, int temp, int last_temp, __maybe_unused void *arg)
+{
+ INFO("Threshold crossed way up tz_id=%d: temp=%d, last_temp=%d\n",
+ tz_id, temp, last_temp);
+
+ return 0;
+}
+
+static int threshold_down(int tz_id, int temp, int last_temp, __maybe_unused void *arg)
+{
+ INFO("Threshold crossed way down tz_id=%d: temp=%d, last_temp=%d\n",
+ tz_id, temp, last_temp);
+
+ return 0;
+}
+
static struct thermal_ops ops = {
- .events.tz_create = tz_create,
- .events.tz_delete = tz_delete,
- .events.tz_disable = tz_disable,
- .events.tz_enable = tz_enable,
- .events.trip_high = trip_high,
- .events.trip_low = trip_low,
- .events.trip_add = trip_add,
- .events.trip_delete = trip_delete,
- .events.trip_change = trip_change,
- .events.cdev_add = cdev_add,
- .events.cdev_delete = cdev_delete,
- .events.cdev_update = cdev_update,
- .events.gov_change = gov_change
+ .events.tz_create = tz_create,
+ .events.tz_delete = tz_delete,
+ .events.tz_disable = tz_disable,
+ .events.tz_enable = tz_enable,
+ .events.trip_high = trip_high,
+ .events.trip_low = trip_low,
+ .events.trip_add = trip_add,
+ .events.trip_delete = trip_delete,
+ .events.trip_change = trip_change,
+ .events.cdev_add = cdev_add,
+ .events.cdev_delete = cdev_delete,
+ .events.cdev_update = cdev_update,
+ .events.gov_change = gov_change,
+ .events.threshold_add = threshold_add,
+ .events.threshold_delete = threshold_delete,
+ .events.threshold_flush = threshold_flush,
+ .events.threshold_up = threshold_up,
+ .events.threshold_down = threshold_down,
};
static int thermal_event(__maybe_unused int fd, __maybe_unused void *arg)
@@ -280,6 +360,7 @@ enum {
THERMAL_ENGINE_DAEMON_ERROR,
THERMAL_ENGINE_LOG_ERROR,
THERMAL_ENGINE_THERMAL_ERROR,
+ THERMAL_ENGINE_THRESHOLD_ERROR,
THERMAL_ENGINE_MAINLOOP_ERROR,
};
@@ -318,6 +399,8 @@ int main(int argc, char *argv[])
return THERMAL_ENGINE_THERMAL_ERROR;
}
+ for_each_thermal_zone(td.tz, set_threshold, td.th);
+
for_each_thermal_zone(td.tz, show_tz, td.th);
if (mainloop_init()) {
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v4 1/6] thermal/core: Add user thresholds support
2024-09-23 9:59 ` [PATCH v4 1/6] thermal/core: Add " Daniel Lezcano
@ 2024-10-01 19:57 ` Rafael J. Wysocki
2024-10-02 12:22 ` Rafael J. Wysocki
0 siblings, 1 reply; 15+ messages in thread
From: Rafael J. Wysocki @ 2024-10-01 19:57 UTC (permalink / raw)
To: Daniel Lezcano; +Cc: rafael, linux-pm, lukasz.luba, quic_manafm
On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
<daniel.lezcano@linaro.org> wrote:
>
> 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 <daniel.lezcano@linaro.org>
> ---
> drivers/thermal/Makefile | 1 +
> drivers/thermal/thermal_core.h | 2 +
> drivers/thermal/thermal_thresholds.c | 229 +++++++++++++++++++++++++++
> drivers/thermal/thermal_thresholds.h | 19 +++
> include/linux/thermal.h | 3 +
> include/uapi/linux/thermal.h | 2 +
> 6 files changed, 256 insertions(+)
> create mode 100644 drivers/thermal/thermal_thresholds.c
> create mode 100644 drivers/thermal/thermal_thresholds.h
>
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 41c4d56beb40..1e1559bb971e 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -6,6 +6,7 @@ CFLAGS_thermal_core.o := -I$(src)
> obj-$(CONFIG_THERMAL) += thermal_sys.o
> thermal_sys-y += thermal_core.o thermal_sysfs.o
> thermal_sys-y += thermal_trip.o thermal_helpers.o
> +thermal_sys-y += thermal_thresholds.o
>
> # netlink interface to manage the thermal framework
> thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
> diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
> index 50b858aa173a..8f320d17d927 100644
> --- a/drivers/thermal/thermal_core.h
> +++ b/drivers/thermal/thermal_core.h
> @@ -13,6 +13,7 @@
> #include <linux/thermal.h>
>
> #include "thermal_netlink.h"
> +#include "thermal_thresholds.h"
> #include "thermal_debugfs.h"
>
> struct thermal_attr {
> @@ -139,6 +140,7 @@ struct thermal_zone_device {
> #ifdef CONFIG_THERMAL_DEBUGFS
> struct thermal_debugfs *debugfs;
> #endif
> + struct list_head user_thresholds;
> struct thermal_trip_desc trips[] __counted_by(num_trips);
> };
>
> diff --git a/drivers/thermal/thermal_thresholds.c b/drivers/thermal/thermal_thresholds.c
> new file mode 100644
> index 000000000000..f33b6d5474d8
> --- /dev/null
> +++ b/drivers/thermal/thermal_thresholds.c
> @@ -0,0 +1,229 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2024 Linaro Limited
> + *
> + * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
> + *
> + * Thermal thresholds
> + */
> +#include <linux/list.h>
> +#include <linux/list_sort.h>
> +#include <linux/slab.h>
> +
> +#include "thermal_core.h"
> +#include "thermal_thresholds.h"
> +
> +int thermal_thresholds_init(struct thermal_zone_device *tz)
> +{
> + INIT_LIST_HEAD(&tz->user_thresholds);
> +
> + return 0;
> +}
> +
> +void thermal_thresholds_flush(struct thermal_zone_device *tz)
> +{
> + struct list_head *thresholds = &tz->user_thresholds;
> + struct user_threshold *entry, *tmp;
> +
> + lockdep_assert_held(&tz->lock);
> +
> + list_for_each_entry_safe(entry, tmp, thresholds, list_node) {
> + list_del(&entry->list_node);
> + kfree(entry);
> + }
> +
> + __thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS);
> +}
> +
> +void thermal_thresholds_exit(struct thermal_zone_device *tz)
> +{
> + thermal_thresholds_flush(tz);
> +}
> +
> +static int __thermal_thresholds_cmp(void *data,
> + const struct list_head *l1,
> + const struct list_head *l2)
> +{
> + struct user_threshold *t1 = container_of(l1, struct user_threshold, list_node);
> + struct user_threshold *t2 = container_of(l2, struct user_threshold, list_node);
> +
> + return t1->temperature - t2->temperature;
> +}
> +
> +static struct user_threshold *__thermal_thresholds_find(const struct list_head *thresholds,
> + int temperature)
> +{
> + struct user_threshold *t;
> +
> + list_for_each_entry(t, thresholds, list_node)
> + if (t->temperature == temperature)
> + return t;
> +
> + return NULL;
> +}
> +
> +static bool __thermal_threshold_is_crossed(struct user_threshold *threshold, int temperature,
> + int last_temperature, int direction,
> + int *low, int *high)
> +{
> +
> + if (temperature >= threshold->temperature) {
> + if (threshold->temperature > *low &&
> + THERMAL_THRESHOLD_WAY_DOWN & threshold->direction)
> + *low = threshold->temperature;
> +
> + if (last_temperature < threshold->temperature &&
> + threshold->direction & direction)
> + return true;
> + } else {
> + if (threshold->temperature < *high && THERMAL_THRESHOLD_WAY_UP
> + & threshold->direction)
> + *high = threshold->temperature;
> +
> + if (last_temperature >= threshold->temperature &&
> + threshold->direction & direction)
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static bool thermal_thresholds_handle_raising(struct list_head *thresholds, int temperature,
> + int last_temperature, int *low, int *high)
> +{
> + struct user_threshold *t;
> +
> + list_for_each_entry(t, thresholds, list_node) {
> + if (__thermal_threshold_is_crossed(t, temperature, last_temperature,
> + THERMAL_THRESHOLD_WAY_UP, low, high))
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static bool thermal_thresholds_handle_dropping(struct list_head *thresholds, int temperature,
> + int last_temperature, int *low, int *high)
> +{
> + struct user_threshold *t;
> +
> + list_for_each_entry_reverse(t, thresholds, list_node) {
> + if (__thermal_threshold_is_crossed(t, temperature, last_temperature,
> + THERMAL_THRESHOLD_WAY_DOWN, low, high))
> + return true;
> + }
> +
> + return false;
> +}
> +
> +void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high)
> +{
> + struct list_head *thresholds = &tz->user_thresholds;
> +
> + int temperature = tz->temperature;
> + int last_temperature = tz->last_temperature;
> + bool notify;
> +
> + lockdep_assert_held(&tz->lock);
> +
> + /*
> + * We need a second update in order to detect a threshold being crossed
> + */
> + if (last_temperature == THERMAL_TEMP_INVALID)
> + return;
> +
> + /*
> + * The temperature is stable, so obviously we can not have
> + * crossed a threshold.
> + */
> + if (last_temperature == temperature)
> + return;
> +
> + /*
> + * Since last update the temperature:
> + * - increased : thresholds are crossed the way up
> + * - decreased : thresholds are crossed the way down
> + */
> + if (temperature > last_temperature)
> + notify = thermal_thresholds_handle_raising(thresholds, temperature,
> + last_temperature, low, high);
> + else
> + notify = thermal_thresholds_handle_dropping(thresholds, temperature,
> + last_temperature, low, high);
> +
> + if (notify)
> + pr_debug("A threshold has been crossed the way %s, with a temperature=%d, last_temperature=%d\n",
> + temperature > last_temperature ? "up" : "down", temperature, last_temperature);
> +}
> +
> +int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction)
> +{
> + struct list_head *thresholds = &tz->user_thresholds;
> + struct user_threshold *t;
> +
> + lockdep_assert_held(&tz->lock);
> +
> + t = __thermal_thresholds_find(thresholds, temperature);
> + if (t) {
> + if (t->direction == direction)
> + return -EEXIST;
> +
> + t->direction |= direction;
> + } else {
> +
> + t = kmalloc(sizeof(*t), GFP_KERNEL);
> + if (!t)
> + return -ENOMEM;
> +
> + INIT_LIST_HEAD(&t->list_node);
> + t->temperature = temperature;
> + t->direction = direction;
> + list_add(&t->list_node, thresholds);
> + list_sort(NULL, thresholds, __thermal_thresholds_cmp);
> + }
> +
> + __thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD);
> +
> + return 0;
> +}
> +
> +int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction)
> +{
> + struct list_head *thresholds = &tz->user_thresholds;
> + struct user_threshold *t;
> +
> + lockdep_assert_held(&tz->lock);
> +
> + t = __thermal_thresholds_find(thresholds, temperature);
> + if (!t)
> + return -ENOENT;
> +
> + if (t->direction == direction) {
> + list_del(&t->list_node);
> + kfree(t);
> + } else {
> + t->direction &= ~direction;
> + }
> +
> + __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
> +
> + return 0;
> +}
> +
> +int thermal_thresholds_for_each(struct thermal_zone_device *tz,
> + int (*cb)(struct user_threshold *, void *arg), void *arg)
> +{
> + struct list_head *thresholds = &tz->user_thresholds;
> + struct user_threshold *entry;
> + int ret;
> +
> + lockdep_assert_held(&tz->lock);
> +
> + list_for_each_entry(entry, thresholds, list_node) {
> + ret = cb(entry, arg);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/thermal/thermal_thresholds.h b/drivers/thermal/thermal_thresholds.h
> new file mode 100644
> index 000000000000..232f4e8089af
> --- /dev/null
> +++ b/drivers/thermal/thermal_thresholds.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __THERMAL_THRESHOLDS_H__
> +#define __THERMAL_THRESHOLDS_H__
> +
> +struct user_threshold {
> + struct list_head list_node;
> + int temperature;
> + int direction;
> +};
> +
> +int thermal_thresholds_init(struct thermal_zone_device *tz);
> +void thermal_thresholds_exit(struct thermal_zone_device *tz);
> +void thermal_thresholds_flush(struct thermal_zone_device *tz);
> +void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high);
> +int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction);
> +int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction);
> +int thermal_thresholds_for_each(struct thermal_zone_device *tz,
> + int (*cb)(struct user_threshold *, void *arg), void *arg);
> +#endif
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index 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..3e7c1c2e71a7 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 0x1
> +#define THERMAL_THRESHOLD_WAY_DOWN 0x2
It would be somewhat better to use BIT(0) and BIT(1) here IMO, but
apart from that this patch and patch [2/6] are fine with me (even
though my implementation of threshold crossing detection would be
different).
I still have questions regarding patch [3/6] though, but I'll need to
take a fresh look at it tomorrow.
>
> enum thermal_device_mode {
> THERMAL_DEVICE_DISABLED = 0,
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4 1/6] thermal/core: Add user thresholds support
2024-10-01 19:57 ` Rafael J. Wysocki
@ 2024-10-02 12:22 ` Rafael J. Wysocki
2024-10-09 15:40 ` Daniel Lezcano
0 siblings, 1 reply; 15+ messages in thread
From: Rafael J. Wysocki @ 2024-10-02 12:22 UTC (permalink / raw)
To: Daniel Lezcano; +Cc: linux-pm, lukasz.luba, quic_manafm
On Tue, Oct 1, 2024 at 9:57 PM Rafael J. Wysocki <rafael@kernel.org> wrote:
>
> On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
> <daniel.lezcano@linaro.org> wrote:
> >
> > 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 <daniel.lezcano@linaro.org>
> > ---
> > drivers/thermal/Makefile | 1 +
> > drivers/thermal/thermal_core.h | 2 +
> > drivers/thermal/thermal_thresholds.c | 229 +++++++++++++++++++++++++++
> > drivers/thermal/thermal_thresholds.h | 19 +++
> > include/linux/thermal.h | 3 +
> > include/uapi/linux/thermal.h | 2 +
> > 6 files changed, 256 insertions(+)
> > create mode 100644 drivers/thermal/thermal_thresholds.c
> > create mode 100644 drivers/thermal/thermal_thresholds.h
> >
> > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > index 41c4d56beb40..1e1559bb971e 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -6,6 +6,7 @@ CFLAGS_thermal_core.o := -I$(src)
> > obj-$(CONFIG_THERMAL) += thermal_sys.o
> > thermal_sys-y += thermal_core.o thermal_sysfs.o
> > thermal_sys-y += thermal_trip.o thermal_helpers.o
> > +thermal_sys-y += thermal_thresholds.o
> >
> > # netlink interface to manage the thermal framework
> > thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
> > diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
> > index 50b858aa173a..8f320d17d927 100644
> > --- a/drivers/thermal/thermal_core.h
> > +++ b/drivers/thermal/thermal_core.h
> > @@ -13,6 +13,7 @@
> > #include <linux/thermal.h>
> >
> > #include "thermal_netlink.h"
> > +#include "thermal_thresholds.h"
> > #include "thermal_debugfs.h"
> >
> > struct thermal_attr {
> > @@ -139,6 +140,7 @@ struct thermal_zone_device {
> > #ifdef CONFIG_THERMAL_DEBUGFS
> > struct thermal_debugfs *debugfs;
> > #endif
> > + struct list_head user_thresholds;
> > struct thermal_trip_desc trips[] __counted_by(num_trips);
> > };
> >
> > diff --git a/drivers/thermal/thermal_thresholds.c b/drivers/thermal/thermal_thresholds.c
> > new file mode 100644
> > index 000000000000..f33b6d5474d8
> > --- /dev/null
> > +++ b/drivers/thermal/thermal_thresholds.c
> > @@ -0,0 +1,229 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2024 Linaro Limited
> > + *
> > + * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
> > + *
> > + * Thermal thresholds
> > + */
> > +#include <linux/list.h>
> > +#include <linux/list_sort.h>
> > +#include <linux/slab.h>
> > +
> > +#include "thermal_core.h"
> > +#include "thermal_thresholds.h"
> > +
> > +int thermal_thresholds_init(struct thermal_zone_device *tz)
> > +{
> > + INIT_LIST_HEAD(&tz->user_thresholds);
> > +
> > + return 0;
> > +}
> > +
> > +void thermal_thresholds_flush(struct thermal_zone_device *tz)
> > +{
> > + struct list_head *thresholds = &tz->user_thresholds;
> > + struct user_threshold *entry, *tmp;
> > +
> > + lockdep_assert_held(&tz->lock);
> > +
> > + list_for_each_entry_safe(entry, tmp, thresholds, list_node) {
> > + list_del(&entry->list_node);
> > + kfree(entry);
> > + }
> > +
> > + __thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS);
> > +}
> > +
> > +void thermal_thresholds_exit(struct thermal_zone_device *tz)
> > +{
> > + thermal_thresholds_flush(tz);
> > +}
> > +
> > +static int __thermal_thresholds_cmp(void *data,
> > + const struct list_head *l1,
> > + const struct list_head *l2)
> > +{
> > + struct user_threshold *t1 = container_of(l1, struct user_threshold, list_node);
> > + struct user_threshold *t2 = container_of(l2, struct user_threshold, list_node);
> > +
> > + return t1->temperature - t2->temperature;
> > +}
> > +
> > +static struct user_threshold *__thermal_thresholds_find(const struct list_head *thresholds,
> > + int temperature)
> > +{
> > + struct user_threshold *t;
> > +
> > + list_for_each_entry(t, thresholds, list_node)
> > + if (t->temperature == temperature)
> > + return t;
> > +
> > + return NULL;
> > +}
> > +
> > +static bool __thermal_threshold_is_crossed(struct user_threshold *threshold, int temperature,
> > + int last_temperature, int direction,
> > + int *low, int *high)
> > +{
> > +
> > + if (temperature >= threshold->temperature) {
> > + if (threshold->temperature > *low &&
> > + THERMAL_THRESHOLD_WAY_DOWN & threshold->direction)
> > + *low = threshold->temperature;
> > +
> > + if (last_temperature < threshold->temperature &&
> > + threshold->direction & direction)
> > + return true;
> > + } else {
> > + if (threshold->temperature < *high && THERMAL_THRESHOLD_WAY_UP
> > + & threshold->direction)
> > + *high = threshold->temperature;
> > +
> > + if (last_temperature >= threshold->temperature &&
> > + threshold->direction & direction)
> > + return true;
> > + }
> > +
> > + return false;
> > +}
> > +
> > +static bool thermal_thresholds_handle_raising(struct list_head *thresholds, int temperature,
> > + int last_temperature, int *low, int *high)
> > +{
> > + struct user_threshold *t;
> > +
> > + list_for_each_entry(t, thresholds, list_node) {
> > + if (__thermal_threshold_is_crossed(t, temperature, last_temperature,
> > + THERMAL_THRESHOLD_WAY_UP, low, high))
> > + return true;
> > + }
> > +
> > + return false;
> > +}
> > +
> > +static bool thermal_thresholds_handle_dropping(struct list_head *thresholds, int temperature,
> > + int last_temperature, int *low, int *high)
> > +{
> > + struct user_threshold *t;
> > +
> > + list_for_each_entry_reverse(t, thresholds, list_node) {
> > + if (__thermal_threshold_is_crossed(t, temperature, last_temperature,
> > + THERMAL_THRESHOLD_WAY_DOWN, low, high))
> > + return true;
> > + }
> > +
> > + return false;
> > +}
> > +
> > +void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high)
> > +{
> > + struct list_head *thresholds = &tz->user_thresholds;
> > +
> > + int temperature = tz->temperature;
> > + int last_temperature = tz->last_temperature;
> > + bool notify;
> > +
> > + lockdep_assert_held(&tz->lock);
> > +
> > + /*
> > + * We need a second update in order to detect a threshold being crossed
> > + */
> > + if (last_temperature == THERMAL_TEMP_INVALID)
> > + return;
> > +
> > + /*
> > + * The temperature is stable, so obviously we can not have
> > + * crossed a threshold.
> > + */
> > + if (last_temperature == temperature)
> > + return;
> > +
> > + /*
> > + * Since last update the temperature:
> > + * - increased : thresholds are crossed the way up
> > + * - decreased : thresholds are crossed the way down
> > + */
> > + if (temperature > last_temperature)
> > + notify = thermal_thresholds_handle_raising(thresholds, temperature,
> > + last_temperature, low, high);
> > + else
> > + notify = thermal_thresholds_handle_dropping(thresholds, temperature,
> > + last_temperature, low, high);
> > +
> > + if (notify)
> > + pr_debug("A threshold has been crossed the way %s, with a temperature=%d, last_temperature=%d\n",
> > + temperature > last_temperature ? "up" : "down", temperature, last_temperature);
> > +}
> > +
> > +int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction)
> > +{
> > + struct list_head *thresholds = &tz->user_thresholds;
> > + struct user_threshold *t;
> > +
> > + lockdep_assert_held(&tz->lock);
> > +
> > + t = __thermal_thresholds_find(thresholds, temperature);
> > + if (t) {
> > + if (t->direction == direction)
> > + return -EEXIST;
> > +
> > + t->direction |= direction;
> > + } else {
> > +
> > + t = kmalloc(sizeof(*t), GFP_KERNEL);
> > + if (!t)
> > + return -ENOMEM;
> > +
> > + INIT_LIST_HEAD(&t->list_node);
> > + t->temperature = temperature;
> > + t->direction = direction;
> > + list_add(&t->list_node, thresholds);
> > + list_sort(NULL, thresholds, __thermal_thresholds_cmp);
> > + }
> > +
> > + __thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD);
> > +
> > + return 0;
> > +}
> > +
> > +int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction)
> > +{
> > + struct list_head *thresholds = &tz->user_thresholds;
> > + struct user_threshold *t;
> > +
> > + lockdep_assert_held(&tz->lock);
> > +
> > + t = __thermal_thresholds_find(thresholds, temperature);
> > + if (!t)
> > + return -ENOENT;
> > +
> > + if (t->direction == direction) {
> > + list_del(&t->list_node);
> > + kfree(t);
> > + } else {
> > + t->direction &= ~direction;
> > + }
> > +
> > + __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
> > +
> > + return 0;
> > +}
> > +
> > +int thermal_thresholds_for_each(struct thermal_zone_device *tz,
> > + int (*cb)(struct user_threshold *, void *arg), void *arg)
> > +{
> > + struct list_head *thresholds = &tz->user_thresholds;
> > + struct user_threshold *entry;
> > + int ret;
> > +
> > + lockdep_assert_held(&tz->lock);
> > +
> > + list_for_each_entry(entry, thresholds, list_node) {
> > + ret = cb(entry, arg);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > diff --git a/drivers/thermal/thermal_thresholds.h b/drivers/thermal/thermal_thresholds.h
> > new file mode 100644
> > index 000000000000..232f4e8089af
> > --- /dev/null
> > +++ b/drivers/thermal/thermal_thresholds.h
> > @@ -0,0 +1,19 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef __THERMAL_THRESHOLDS_H__
> > +#define __THERMAL_THRESHOLDS_H__
> > +
> > +struct user_threshold {
> > + struct list_head list_node;
> > + int temperature;
> > + int direction;
> > +};
> > +
> > +int thermal_thresholds_init(struct thermal_zone_device *tz);
> > +void thermal_thresholds_exit(struct thermal_zone_device *tz);
> > +void thermal_thresholds_flush(struct thermal_zone_device *tz);
> > +void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high);
> > +int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction);
> > +int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction);
> > +int thermal_thresholds_for_each(struct thermal_zone_device *tz,
> > + int (*cb)(struct user_threshold *, void *arg), void *arg);
> > +#endif
> > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > index 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..3e7c1c2e71a7 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 0x1
> > +#define THERMAL_THRESHOLD_WAY_DOWN 0x2
>
> It would be somewhat better to use BIT(0) and BIT(1) here IMO, but
> apart from that this patch and patch [2/6] are fine with me (even
> though my implementation of threshold crossing detection would be
> different).
I'm inclined to apply these 2 patches with the change mentioned above,
so that I can base my 6.13 work on them.
I'll send my comments for the [3/6] shortly.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4 3/6] thermal/netlink: Add the commands and the events for the thresholds
2024-09-23 9:59 ` [PATCH v4 3/6] thermal/netlink: Add the commands and the events for the thresholds Daniel Lezcano
@ 2024-10-02 12:53 ` Rafael J. Wysocki
2024-10-12 17:36 ` Daniel Lezcano
0 siblings, 1 reply; 15+ messages in thread
From: Rafael J. Wysocki @ 2024-10-02 12:53 UTC (permalink / raw)
To: Daniel Lezcano; +Cc: rafael, linux-pm, lukasz.luba, quic_manafm
On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
<daniel.lezcano@linaro.org> wrote:
>
> 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.
The last three commands need special privileges I would think because
they essentially cause the kernel to either allocate or free memory
and they may interfere with what the other processes in user space do.
For instance, one process may flush the thresholds for a given thermal
zone while another process is using them.
What controls the level of privilege required to use these commands?
> As different processes in userspace can interact with the thresholds,
> the process id responsible of the action (add, delete or flush) will
> be added in the notification.
This may leak PIDs between containers which has been pointed out as an
issue (for example, see
https://lore.kernel.org/linux-pm/20240704-umsatz-drollig-38db6b84da7b@brauner/).
OTOH, the thermal engine should not need the extra information because
it knows which thresholds were added by it, so any other thresholds
would be added by someone else, wouldn't they?
> This way a thermal engine is able to
> detect if another process is interfering with the thresholds. A
> process id of zero is the kernel as it is by convention usually.
>
> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> ---
> drivers/thermal/thermal_netlink.c | 239 ++++++++++++++++++++++++++-
> drivers/thermal/thermal_netlink.h | 34 ++++
> drivers/thermal/thermal_thresholds.c | 38 +++--
> drivers/thermal/thermal_thresholds.h | 6 +-
> include/uapi/linux/thermal.h | 28 +++-
> 5 files changed, 313 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c
> index 97157c453630..8d92ebeb72fc 100644
> --- a/drivers/thermal/thermal_netlink.c
> +++ b/drivers/thermal/thermal_netlink.c
> @@ -9,6 +9,7 @@
> #include <linux/module.h>
> #include <linux/notifier.h>
> #include <linux/kernel.h>
> +#include <net/sock.h>
> #include <net/genetlink.h>
> #include <uapi/linux/thermal.h>
>
> @@ -49,12 +50,19 @@ 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_WAY] = { .type = NLA_U32 },
It would be better to call this THERMAL_GENL_ATTR_THRESHOLD_DIR IMV.
> + [THERMAL_GENL_ATTR_THRESHOLD_PID] = { .type = NLA_U32 },
> };
>
> struct param {
> struct nlattr **attrs;
> struct sk_buff *msg;
> const char *name;
> + pid_t pid;
I'd rather not add it as mentioned above.
> int tz_id;
> int cdev_id;
> int trip_id;
> @@ -62,6 +70,8 @@ struct param {
> int trip_type;
> int trip_hyst;
> int temp;
> + int last_temp;
Or prev_temp? It should be less ambiguous than last_temp.
> + int direction;
> int cdev_state;
> int cdev_max_state;
> struct thermal_genl_cpu_caps *cpu_capabilities;
> @@ -234,6 +244,36 @@ static int thermal_genl_event_cpu_capability_change(struct param *p)
> return -EMSGSIZE;
> }
>
> +static int thermal_genl_event_threshold_add(struct param *p)
> +{
> + if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
> + nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp) ||
> + nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_WAY, p->direction) ||
> + nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_PID, p->pid))
> + 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),
> + nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_PID, p->pid))
> + 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_LAST_TEMP, p->last_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 +286,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 +305,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 +452,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, int pid)
> +{
> + struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction, .pid = pid };
> +
> + 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, int pid)
> +{
> + struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction, .pid = pid };
> +
> + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DELETE, &p);
> +}
> +
> +int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, int pid)
> +{
> + struct param p = { .tz_id = tz->id, .pid = pid };
> +
> + 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, .last_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, .last_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,
> @@ -575,12 +663,130 @@ static int thermal_genl_cmd_cdev_get(struct param *p)
> return ret;
> }
>
> +static int __thermal_genl_cmd_threshold_get(struct user_threshold *threshold, void *arg)
> +{
> + struct sk_buff *msg = arg;
> +
> + if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, threshold->temperature) ||
> + nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_WAY, threshold->direction))
> + return -1;
> +
> + return 0;
> +}
> +
> +static int thermal_genl_cmd_threshold_get(struct param *p)
> +{
> + struct thermal_zone_device *tz;
> + 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]);
> +
> + tz = thermal_zone_get_by_id(id);
> + if (!tz)
> + return -EINVAL;
What prevents the thermal zone from going away right here?
It looks like thermal_zone_get_by_id() should do a get_device() on the
thermal zone object it is about to return and then the caller should
do a put_device() on it.
Granted, this problem is present already in the thermal netlink code,
so it needs to be fixed (I'm going to send a patch to address it) and
this patch will need to be adjusted.
> +
> + start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD);
> + if (!start_trip)
> + return -EMSGSIZE;
> +
> + mutex_lock(&tz->lock);
> + ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg);
> + mutex_unlock(&tz->lock);
I think that the locking can be moved to
thermal_thresholds_for_each(). At least there are no other callers of
it AFAICS.
> +
> + if (ret)
> + return -EMSGSIZE;
> +
> + nla_nest_end(msg, start_trip);
> +
> + return 0;
> +}
> +
> +static int thermal_genl_cmd_threshold_add(struct param *p)
> +{
> + struct thermal_zone_device *tz;
> + int id, temp, direction, ret = 0;
> +
> + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
> + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
> + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY])
> + 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_WAY]);
> +
> + tz = thermal_zone_get_by_id(id);
> + if (!tz)
> + return -EINVAL;
> +
> + mutex_lock(&tz->lock);
> + ret = thermal_thresholds_add(tz, temp, direction, p->pid);
> + mutex_unlock(&tz->lock);
> +
> + return ret;
> +}
> +
> +static int thermal_genl_cmd_threshold_delete(struct param *p)
> +{
> + struct thermal_zone_device *tz;
> + int id, temp, direction, ret = 0;
> +
> + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
> + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
> + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY])
> + 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_WAY]);
> +
> + tz = thermal_zone_get_by_id(id);
> + if (!tz)
> + return -EINVAL;
> +
> + mutex_lock(&tz->lock);
> + ret = thermal_thresholds_delete(tz, temp, direction, p->pid);
> + mutex_unlock(&tz->lock);
> +
> + return ret;
> +}
> +
> +static int thermal_genl_cmd_threshold_flush(struct param *p)
> +{
> + struct thermal_zone_device *tz;
> + int id;
> +
> + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
> + return -EINVAL;
> +
> + id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
> +
> + tz = thermal_zone_get_by_id(id);
> + if (!tz)
> + return -EINVAL;
> +
> + mutex_lock(&tz->lock);
> + thermal_thresholds_flush(tz, p->pid);
> + mutex_unlock(&tz->lock);
> +
> + 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,
> @@ -623,6 +829,7 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb,
> if (!msg)
> return -ENOMEM;
> p.msg = msg;
> + p.pid = task_tgid_vnr(current);
>
> hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd);
> if (!hdr)
> @@ -691,6 +898,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 = {
> @@ -703,7 +930,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..3f85c636029c 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, pid_t pid);
> +int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
> + int temperature, int direction, pid_t pid);
> +int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, pid_t pid);
> +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, pid_t pid)
> +{
> + return 0;
> +}
> +
> +static inline int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
> + int temperature, int direction, pid_t pid)
> +{
> + return 0;
> +}
> +
> +static inline int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, pid_t pid)
> +{
> + 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..6d9a0cf8031f 100644
> --- a/drivers/thermal/thermal_thresholds.c
> +++ b/drivers/thermal/thermal_thresholds.c
> @@ -20,7 +20,7 @@ int thermal_thresholds_init(struct thermal_zone_device *tz)
> return 0;
> }
>
> -void thermal_thresholds_flush(struct thermal_zone_device *tz)
> +void thermal_thresholds_flush(struct thermal_zone_device *tz, pid_t pid)
> {
> struct list_head *thresholds = &tz->user_thresholds;
> struct user_threshold *entry, *tmp;
> @@ -32,12 +32,14 @@ void thermal_thresholds_flush(struct thermal_zone_device *tz)
> kfree(entry);
> }
>
> + thermal_notify_threshold_flush(tz, pid);
> +
> __thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS);
> }
>
> void thermal_thresholds_exit(struct thermal_zone_device *tz)
> {
> - thermal_thresholds_flush(tz);
> + thermal_thresholds_flush(tz, 0);
> }
>
> static int __thermal_thresholds_cmp(void *data,
> @@ -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, pid_t pid)
> {
> 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, pid);
> +
> __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, pid_t pid)
> {
> struct list_head *thresholds = &tz->user_thresholds;
> struct user_threshold *t;
> @@ -205,8 +209,10 @@ int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, i
> t->direction &= ~direction;
> }
>
> - __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
> + thermal_notify_threshold_delete(tz, temperature, direction, pid);
>
> + __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
> +
> return 0;
> }
>
> diff --git a/drivers/thermal/thermal_thresholds.h b/drivers/thermal/thermal_thresholds.h
> index 232f4e8089af..f97ffb715d2e 100644
> --- a/drivers/thermal/thermal_thresholds.h
> +++ b/drivers/thermal/thermal_thresholds.h
> @@ -10,10 +10,10 @@ 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);
> -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);
> +void thermal_thresholds_flush(struct thermal_zone_device *tz, pid_t pid);
> +int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction, pid_t pid);
> +int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction, pid_t pid);
> int thermal_thresholds_for_each(struct thermal_zone_device *tz,
> int (*cb)(struct user_threshold *, void *arg), void *arg);
> #endif
> diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h
> index 3e7c1c2e71a7..bcbaf62a1727 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_LAST_TEMP,
> THERMAL_GENL_ATTR_TZ_TRIP,
> THERMAL_GENL_ATTR_TZ_TRIP_ID,
> THERMAL_GENL_ATTR_TZ_TRIP_TYPE,
> @@ -50,6 +51,10 @@ 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_WAY,
> + THERMAL_GENL_ATTR_THRESHOLD_PID,
> __THERMAL_GENL_ATTR_MAX,
> };
> #define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1)
> @@ -77,6 +82,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 +94,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.43.0
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4 1/6] thermal/core: Add user thresholds support
2024-10-02 12:22 ` Rafael J. Wysocki
@ 2024-10-09 15:40 ` Daniel Lezcano
2024-10-09 15:59 ` Rafael J. Wysocki
0 siblings, 1 reply; 15+ messages in thread
From: Daniel Lezcano @ 2024-10-09 15:40 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: linux-pm, lukasz.luba, quic_manafm
Hi Rafael,
On 02/10/2024 14:22, Rafael J. Wysocki wrote:
> On Tue, Oct 1, 2024 at 9:57 PM Rafael J. Wysocki <rafael@kernel.org> wrote:
>>
>> On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
>> <daniel.lezcano@linaro.org> wrote:
>>>
>>> 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.
[ ... ]
> I'm inclined to apply these 2 patches with the change mentioned above,
> so that I can base my 6.13 work on them.
I was expecting you to pick these two patches and do the modifications
but I don't see them in your tree. Did I misunderstood your comment?
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4 1/6] thermal/core: Add user thresholds support
2024-10-09 15:40 ` Daniel Lezcano
@ 2024-10-09 15:59 ` Rafael J. Wysocki
2024-10-09 16:44 ` Daniel Lezcano
0 siblings, 1 reply; 15+ messages in thread
From: Rafael J. Wysocki @ 2024-10-09 15:59 UTC (permalink / raw)
To: Daniel Lezcano; +Cc: Rafael J. Wysocki, linux-pm, lukasz.luba, quic_manafm
Hi Daniel,
On Wed, Oct 9, 2024 at 5:40 PM Daniel Lezcano <daniel.lezcano@linaro.org> wrote:
>
>
> Hi Rafael,
>
> On 02/10/2024 14:22, Rafael J. Wysocki wrote:
> > On Tue, Oct 1, 2024 at 9:57 PM Rafael J. Wysocki <rafael@kernel.org> wrote:
> >>
> >> On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
> >> <daniel.lezcano@linaro.org> wrote:
> >>>
> >>> 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.
>
> [ ... ]
>
>
> > I'm inclined to apply these 2 patches with the change mentioned above,
> > so that I can base my 6.13 work on them.
>
> I was expecting you to pick these two patches and do the modifications
> but I don't see them in your tree. Did I misunderstood your comment?
That's still the plan, I've been waiting for you to respond and confirm.
I gather that this is OK then.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4 1/6] thermal/core: Add user thresholds support
2024-10-09 15:59 ` Rafael J. Wysocki
@ 2024-10-09 16:44 ` Daniel Lezcano
2024-10-10 21:44 ` Rafael J. Wysocki
0 siblings, 1 reply; 15+ messages in thread
From: Daniel Lezcano @ 2024-10-09 16:44 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: linux-pm, lukasz.luba, quic_manafm
On 09/10/2024 17:59, Rafael J. Wysocki wrote:
> Hi Daniel,
>
> On Wed, Oct 9, 2024 at 5:40 PM Daniel Lezcano <daniel.lezcano@linaro.org> wrote:
>>
>>
>> Hi Rafael,
>>
>> On 02/10/2024 14:22, Rafael J. Wysocki wrote:
>>> On Tue, Oct 1, 2024 at 9:57 PM Rafael J. Wysocki <rafael@kernel.org> wrote:
>>>>
>>>> On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
>>>> <daniel.lezcano@linaro.org> wrote:
>>>>>
>>>>> 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.
>>
>> [ ... ]
>>
>>
>>> I'm inclined to apply these 2 patches with the change mentioned above,
>>> so that I can base my 6.13 work on them.
>>
>> I was expecting you to pick these two patches and do the modifications
>> but I don't see them in your tree. Did I misunderstood your comment?
>
> That's still the plan, I've been waiting for you to respond and confirm.
>
> I gather that this is OK then.
Yes, it is ;)
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4 1/6] thermal/core: Add user thresholds support
2024-10-09 16:44 ` Daniel Lezcano
@ 2024-10-10 21:44 ` Rafael J. Wysocki
0 siblings, 0 replies; 15+ messages in thread
From: Rafael J. Wysocki @ 2024-10-10 21:44 UTC (permalink / raw)
To: Daniel Lezcano; +Cc: Rafael J. Wysocki, linux-pm, lukasz.luba, quic_manafm
On Wed, Oct 9, 2024 at 6:44 PM Daniel Lezcano <daniel.lezcano@linaro.org> wrote:
>
> On 09/10/2024 17:59, Rafael J. Wysocki wrote:
> > Hi Daniel,
> >
> > On Wed, Oct 9, 2024 at 5:40 PM Daniel Lezcano <daniel.lezcano@linaro.org> wrote:
> >>
> >>
> >> Hi Rafael,
> >>
> >> On 02/10/2024 14:22, Rafael J. Wysocki wrote:
> >>> On Tue, Oct 1, 2024 at 9:57 PM Rafael J. Wysocki <rafael@kernel.org> wrote:
> >>>>
> >>>> On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
> >>>> <daniel.lezcano@linaro.org> wrote:
> >>>>>
> >>>>> 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.
> >>
> >> [ ... ]
> >>
> >>
> >>> I'm inclined to apply these 2 patches with the change mentioned above,
> >>> so that I can base my 6.13 work on them.
> >>
> >> I was expecting you to pick these two patches and do the modifications
> >> but I don't see them in your tree. Did I misunderstood your comment?
> >
> > That's still the plan, I've been waiting for you to respond and confirm.
> >
> > I gather that this is OK then.
>
> Yes, it is ;)
Applied now, sorry for the delay.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4 3/6] thermal/netlink: Add the commands and the events for the thresholds
2024-10-02 12:53 ` Rafael J. Wysocki
@ 2024-10-12 17:36 ` Daniel Lezcano
0 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2024-10-12 17:36 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: linux-pm, lukasz.luba, quic_manafm
On 02/10/2024 14:53, Rafael J. Wysocki wrote:
> On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
> <daniel.lezcano@linaro.org> wrote:
>>
>> 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.
>
> The last three commands need special privileges I would think because
> they essentially cause the kernel to either allocate or free memory
> and they may interfere with what the other processes in user space do.
> For instance, one process may flush the thresholds for a given thermal
> zone while another process is using them.
>
> What controls the level of privilege required to use these commands?
I would say we can start with CAP_SYS_ADMIN and then if needed we can
introduce later a new capability CAP_SYS_THERMAL ?
>> As different processes in userspace can interact with the thresholds,
>> the process id responsible of the action (add, delete or flush) will
>> be added in the notification.
>
> This may leak PIDs between containers which has been pointed out as an
> issue (for example, see
> https://lore.kernel.org/linux-pm/20240704-umsatz-drollig-38db6b84da7b@brauner/).
> OTOH, the thermal engine should not need the extra information because
> it knows which thresholds were added by it, so any other thresholds
> would be added by someone else, wouldn't they?
Ok I will remove this information.
>> This way a thermal engine is able to
>> detect if another process is interfering with the thresholds. A
>> process id of zero is the kernel as it is by convention usually.
>>
>> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
>> ---
>> drivers/thermal/thermal_netlink.c | 239 ++++++++++++++++++++++++++-
>> drivers/thermal/thermal_netlink.h | 34 ++++
>> drivers/thermal/thermal_thresholds.c | 38 +++--
>> drivers/thermal/thermal_thresholds.h | 6 +-
>> include/uapi/linux/thermal.h | 28 +++-
>> 5 files changed, 313 insertions(+), 32 deletions(-)
>>
>> diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c
>> index 97157c453630..8d92ebeb72fc 100644
>> --- a/drivers/thermal/thermal_netlink.c
>> +++ b/drivers/thermal/thermal_netlink.c
>> @@ -9,6 +9,7 @@
>> #include <linux/module.h>
>> #include <linux/notifier.h>
>> #include <linux/kernel.h>
>> +#include <net/sock.h>
>> #include <net/genetlink.h>
>> #include <uapi/linux/thermal.h>
>>
>> @@ -49,12 +50,19 @@ 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_WAY] = { .type = NLA_U32 },
>
> It would be better to call this THERMAL_GENL_ATTR_THRESHOLD_DIR IMV.
Sure
>> + [THERMAL_GENL_ATTR_THRESHOLD_PID] = { .type = NLA_U32 },
>> };
>>
>> struct param {
>> struct nlattr **attrs;
>> struct sk_buff *msg;
>> const char *name;
>> + pid_t pid;
>
> I'd rather not add it as mentioned above.
ok, no pid info
>> int tz_id;
>> int cdev_id;
>> int trip_id;
>> @@ -62,6 +70,8 @@ struct param {
>> int trip_type;
>> int trip_hyst;
>> int temp;
>> + int last_temp;
>
> Or prev_temp? It should be less ambiguous than last_temp.
Makes sens
>> + int direction;
>> int cdev_state;
>> int cdev_max_state;
>> struct thermal_genl_cpu_caps *cpu_capabilities;
>> @@ -234,6 +244,36 @@ static int thermal_genl_event_cpu_capability_change(struct param *p)
>> return -EMSGSIZE;
>> }
[ ... ]
>> +static int thermal_genl_cmd_threshold_get(struct param *p)
>> +{
>> + struct thermal_zone_device *tz;
>> + 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]);
>> +
>> + tz = thermal_zone_get_by_id(id);
>> + if (!tz)
>> + return -EINVAL;
>
> What prevents the thermal zone from going away right here?
>
> It looks like thermal_zone_get_by_id() should do a get_device() on the
> thermal zone object it is about to return and then the caller should
> do a put_device() on it.
>
> Granted, this problem is present already in the thermal netlink code,
> so it needs to be fixed (I'm going to send a patch to address it) and
> this patch will need to be adjusted.
Ok, I'll have a look at the recent patches
>> +
>> + start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD);
>> + if (!start_trip)
>> + return -EMSGSIZE;
>> +
>> + mutex_lock(&tz->lock);
>> + ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg);
>> + mutex_unlock(&tz->lock);
>
> I think that the locking can be moved to
> thermal_thresholds_for_each(). At least there are no other callers of
> it AFAICS.
>
Ok
Thanks for the review
-- Daniel
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2024-10-12 17:36 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-23 9:59 [PATCH v4 0/6] Add thermal user thresholds support Daniel Lezcano
2024-09-23 9:59 ` [PATCH v4 1/6] thermal/core: Add " Daniel Lezcano
2024-10-01 19:57 ` Rafael J. Wysocki
2024-10-02 12:22 ` Rafael J. Wysocki
2024-10-09 15:40 ` Daniel Lezcano
2024-10-09 15:59 ` Rafael J. Wysocki
2024-10-09 16:44 ` Daniel Lezcano
2024-10-10 21:44 ` Rafael J. Wysocki
2024-09-23 9:59 ` [PATCH v4 2/6] thermal/core: Connect the threshold with the core Daniel Lezcano
2024-09-23 9:59 ` [PATCH v4 3/6] thermal/netlink: Add the commands and the events for the thresholds Daniel Lezcano
2024-10-02 12:53 ` Rafael J. Wysocki
2024-10-12 17:36 ` Daniel Lezcano
2024-09-23 10:00 ` [PATCH v4 4/6] tools/lib/thermal: Make more generic the command encoding function Daniel Lezcano
2024-09-23 10:00 ` [PATCH v4 5/6] tools/lib/thermal: Add the threshold netlink ABI Daniel Lezcano
2024-09-23 10:00 ` [PATCH v4 6/6] tools/thermal/thermal-engine: Take into account the thresholds API Daniel Lezcano
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).