Re: [PATCH] tools/lib/thermal: Add a thermal library

From: Daniel Lezcano
Date: Fri Feb 04 2022 - 17:16:39 EST



Hi Srinivas,

On 04/02/2022 20:32, srinivas pandruvada wrote:
On Fri, 2022-02-04 at 18:11 +0100, Rafael J. Wysocki wrote:
CC Rui and Srinivas

On Fri, Feb 4, 2022 at 5:15 PM Daniel Lezcano
<daniel.lezcano@xxxxxxxxxx> wrote:

The thermal framework implements a netlink notification mechanism
to
be used by the userspace to have a thermal configuration discovery,
trip point changes or violation, cooling device changes
notifications,
etc...

This library provides a level of abstraction for the thermal
netlink
notification allowing the userspace to connect to the notification
mechanism more easily. The library is callback oriented.

As it is the very first iteration, the API may be subject to
changes. For this reason, the documentation will be provided after
those are stabilized.

So shouldn't this be an RFC?

Also, I would prefer documentation to be provided or at least some
intended usage examples to be given.

Signed-off-by: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx>
---
 tools/Makefile                           |  14 +-
 tools/include/uapi/linux/thermal.h       |  91 ++++++
This is duplicating the file at include/uapi/linux. I know this is done
for other tools also. In my use I am copying and using symbolic link.

Why copy and symbolic link ? Should it be create a symbolic link only ?

BTW I have potential usage of netlink targeted for next release in
linux tools.

https://github.com/spandruvada/linux-kernel/blob/intel-sst/tools/power/x86/intel-speed-select/hfi-events.c

I can replace with libthermal calls once ready. I will check the code
below next week.

Great, thanks!

In attachment a test file as an example of the library usage.


--
<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/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <thermal.h>

#define MAX_EVENTS 10

static int show_trip(struct thermal_trip *tt, void *arg)
{
printf("trip id=%d, type=%d, temp=%d, hyst=%d\n",
tt->id, tt->type, tt->temp, tt->hyst);

return 0;
}

static int show_temp(struct thermal_zone *tz, void *arg)
{
thermal_cmd_get_temp(arg, tz);

printf("temperature: %d\n", tz->temp);

return 0;
}

static int show_governor(struct thermal_zone *tz, void *arg)
{
thermal_cmd_get_governor(arg, tz);

printf("governor: '%s'\n", tz->governor);

return 0;
}

static int show_tz(struct thermal_zone *tz, void *arg)
{
printf("thermal zone '%s', id=%d\n", tz->name, tz->id);

for_each_thermal_trip(tz->trip, show_trip, NULL);

show_temp(tz, arg);

show_governor(tz, arg);

return 0;
}

static int tz_create(const char *name, int tz_id, void *arg)
{
printf("Thermal zone '%s'/%d created\n", name, tz_id);

return 0;
}

static int tz_delete(int tz_id, void *arg)
{
printf("Thermal zone %d deleted\n", tz_id);

return 0;
}

static int tz_disable(int tz_id, void *arg)
{
printf("Thermal zone %d disabled\n", tz_id);

return 0;
}

static int tz_enable(int tz_id, void *arg)
{
printf("Thermal zone %d enabled\n", tz_id);

return 0;
}

static int tz_temp(int tz_id, int temp, void *arg)
{
printf("Thermal zone %d temperature: %d\n", tz_id, temp);

return 0;
}

static int trip_high(int tz_id, int trip_id, int temp, void *arg)
{
printf("Thermal zone %d: trip point %d crossed way up with %d °C\n",
tz_id, trip_id, temp);

return 0;
}

static int trip_low(int tz_id, int trip_id, int temp, void *arg)
{
printf("Thermal zone %d: trip point %d crossed way down with %d °C\n",
tz_id, trip_id, temp);

return 0;
}

static int trip_add(int tz_id, int trip_id, int type, int temp, int hyst, void *arg)
{
printf("Trip point added %d: id=%d, type=%d, temp=%d, hyst=%d\n",
tz_id, trip_id, type, temp, hyst);

return 0;
}

static int trip_delete(int tz_id, int trip_id, void *arg)
{
printf("Trip point deleted %d: id=%d\n", tz_id, trip_id);

return 0;
}

static int trip_change(int tz_id, int trip_id, int type, int temp, int hyst, void *arg)
{
printf("Trip point changed %d: id=%d, type=%d, temp=%d, hyst=%d\n",
tz_id, trip_id, type, temp, hyst);

return 0;
}

static int cdev_add(const char *name, int cdev_id, int max_state, void *arg)
{
printf("Cooling device '%s'/%d (max state=%d) added",
name, cdev_id, max_state);

return 0;
}

static int cdev_delete(int cdev_id, void *arg)
{
printf("Cooling device %d deleted", cdev_id);

return 0;
}

static int cdev_update(int cdev_id, int cur_state, void *arg)
{
printf("cdev:%d state:%d\n", cdev_id, cur_state);

return 0;
}

static int gov_change(int tz_id, const char *name, void *arg)
{
printf("tz %d, governor=%s\n", tz_id, name);

return 0;
}

static struct thermal_ops ops = {
.sampling.tz_temp = tz_temp,
.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
};

static int stop = 0;

static void sighandler(int sig)
{
stop = 1;
};

static int thermal_netlink_get_temp_bench(struct thermal_handler *th,
struct thermal_zone *tz)
{
int nr_messages = 0;
int nr_secs = 5;
unsigned long long sum = 0;

printf("Benchmarking netlink... wait %d secs\n", nr_secs);

signal(SIGALRM, sighandler);
alarm(nr_secs);
while (!stop) {
thermal_cmd_get_temp(th, tz);
sum += tz->temp;
nr_messages++;
}

printf("Temperature reading %d msg/sec (%llu usec/msg), avg temp=%llu\n",
nr_messages / nr_secs, 1000000ULL / (nr_messages / nr_secs),
sum / nr_messages);

return 0;
}

static int thermal_sysfs_get_temp_bench(struct thermal_zone *tz)
{
int nr_messages = 0;
int nr_secs = 5;
unsigned long long sum = 0;
char path[PATH_MAX];
int fd;

snprintf(path, PATH_MAX,
"/sys/class/thermal/thermal_zone%d/temp", tz->id);

fd = open(path, O_RDONLY);
if (fd < 0)
return -1;

printf("Benchmarking sysfs... wait %d secs\n", nr_secs);

signal(SIGALRM, sighandler);
alarm(nr_secs);
stop = 0;
while (!stop) {
char buffer[128] = { 0 };

pread(fd, buffer, 127, 0);
sum += atoi(buffer);
nr_messages++;
}

if (!nr_messages) {
fprintf(stderr, "No message read\n");
return -1;
}

printf("Temperature reading %d msg/sec (%llu usec/msg), avg temp=%llu\n",
nr_messages / nr_secs, 1000000ULL / (nr_messages / nr_secs),
sum / nr_messages);

close(fd);

return 0;
}

int main(void)
{
struct thermal_zone *tz;
struct thermal_handler *th;
struct epoll_event ev;
struct epoll_event events[MAX_EVENTS];
int epollfd;
int nfds;
int i;

th = thermal_init(&ops);
if (!th)
return -1;

tz = thermal_zone_discover(th);
if (!tz)
return -1;

thermal_netlink_get_temp_bench(th, tz);

thermal_sysfs_get_temp_bench(tz);

for_each_thermal_zone(tz, show_tz, th);

epollfd = epoll_create1(0);
if (epollfd < 0)
return -1;

ev.events = EPOLLIN;
ev.data.ptr = thermal_events_handle;

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, thermal_events_fd(th), &ev) == -1)
return -1;

ev.events = EPOLLIN;
ev.data.ptr = thermal_sampling_handle;

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, thermal_sampling_fd(th), &ev) == -1)
return -1;

while (1) {

nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
for (i = 0; i < nfds; i++) {
if (events[i].data.ptr == thermal_events_handle) {
thermal_events_handle(th, NULL);
} else if (events[i].data.ptr == thermal_sampling_handle) {
thermal_sampling_handle(th, NULL);
}
}
}

return 0;
}