[PATCH] thermal: core: Send a sysfs notification on trip points

From: Daniel Lezcano
Date: Thu Apr 02 2020 - 10:23:47 EST


Currently the userspace has no easy way to get notified when a
specific trip point was crossed. There are a couple of approaches:

- the userspace polls the sysfs temperature with usually an
unacceptable delay between the trip temperature point crossing and
the moment it is detected, or a high polling rate with an
unacceptable number of wakeup events.

- the thermal zone is set to be managed by an userspace governor in
order to receive the uevent even if the thermal zone needs to be
managed by another governor.

These changes allow to send a sysfs notification on the
trip_point_*_temp when the temperature is getting higher than the trip
point temperature. By this way, the userspace can be notified
everytime when the trip point is crossed, this is useful for the
thermal Android HAL or for notification to be sent via d-bus.

That allows the userspace to manage the applications based on specific
alerts on different thermal zones to mitigate the skin temperature,
letting the kernel governors handle the high temperature for hardware
like the CPU, the GPU or the modem.

The temperature can be oscillating around a trip point and the event
will be sent multiple times. It is up to the userspace to deal with
this situation.

The following userspace program allows to monitor those events:

struct trip_data {
int fd;
int temperature;
const char *path;
};

int main(int argc, char *argv[])
{
int efd, i;
struct trip_data *td;
struct epoll_event epe;

if (argc < 2) {
fprintf(stderr, "%s <trip1> ... <tripn>\n", argv[0]);
return 1;
}

if (argc > MAX_TRIPS) {
fprintf(stderr, "Max trip points supported: %d\n", MAX_TRIPS);
return 1;
}

efd = epoll_create(MAX_TRIPS);
if (efd < 0) {
fprintf(stderr, "epoll_create failed: %d\n", errno);
return 1;
}

for (i = 0; i < argc - 1; i++) {

FILE *f;
int temperature;

f = fopen(argv[i + 1], "r");
if (!f) {
fprintf(stderr, "Failed to open '%s': %d\n", argv[i + 1], errno);
return 1;
}

td = malloc(sizeof(*td));
if (!td) {
fprintf(stderr, "Failed to allocate trip_data\n");
return 1;
}

fscanf(f, "%d\n", &temperature);
rewind(f);

td->fd = fileno(f);
td->path = argv[i + 1];
td->temperature = temperature;

epe.events = EPOLLIN | EPOLLET;
epe.data.ptr = td;

if (epoll_ctl(efd, EPOLL_CTL_ADD, td->fd, &epe)) {
fprintf(stderr, "Failed to epoll_ctl: %d\n", errno);
return 1;
}

printf("Set '%s' for temperature '%d'\n",
td->path, td->temperature);
}

while(1) {

if (epoll_wait(efd, &epe, 1, -1) < 0) {
fprintf(stderr, "Failed to epoll_wait: %d\n", errno);
return 1;
}

td = epe.data.ptr;

printf("The trip point '%s' crossed the temperature '%d'\n",
td->path, td->temperature);
}

return 0;
}

Signed-off-by: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx>
---
drivers/thermal/thermal_core.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)

diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index c06550930979..3cbdd20252ab 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -407,6 +407,19 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
}
}

+static int thermal_trip_crossed(struct thermal_zone_device *tz, int trip)
+{
+ int trip_temp;
+
+ tz->ops->get_trip_temp(tz, trip, &trip_temp);
+
+ if (tz->last_temperature == THERMAL_TEMP_INVALID)
+ return 0;
+
+ return ((tz->last_temperature < trip_temp)) &&
+ (tz->temperature >= trip_temp));
+}
+
static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
{
enum thermal_trip_type type;
@@ -417,6 +430,16 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)

tz->ops->get_trip_type(tz, trip, &type);

+ /*
+ * This condition will be true everytime the temperature is
+ * greater than the trip point and the previous temperature
+ * was below. In this case notify the userspace via a sysfs
+ * event on the trip point.
+ */
+ if (thermal_trip_crossed(tz, trip))
+ sysfs_notify(&tz->device.kobj, NULL,
+ tz->trip_temp_attrs[trip].attr.attr.name);
+
if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT)
handle_critical_trips(tz, trip, type);
else
--
2.17.1