* [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library
@ 2022-02-14 21:04 Daniel Lezcano
2022-02-14 21:04 ` [RFC PATCH 2/4] tools/thermal: Add util library Daniel Lezcano
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Daniel Lezcano @ 2022-02-14 21:04 UTC (permalink / raw)
To: daniel.lezcano, rafael
Cc: srinivas.pandruvada, linux-kernel, linux-pm, Sasha Levin,
William Breathitt Gray, Jonathan Cameron
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.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
tools/Makefile | 14 +-
tools/lib/thermal/.gitignore | 2 +
tools/lib/thermal/Build | 5 +
tools/lib/thermal/Makefile | 165 +++++++++++
tools/lib/thermal/commands.c | 339 +++++++++++++++++++++++
tools/lib/thermal/events.c | 153 ++++++++++
tools/lib/thermal/include/thermal.h | 129 +++++++++
tools/lib/thermal/libthermal.map | 25 ++
tools/lib/thermal/libthermal.pc.template | 12 +
tools/lib/thermal/sampling.c | 64 +++++
tools/lib/thermal/thermal.c | 117 ++++++++
tools/lib/thermal/thermal_nl.c | 202 ++++++++++++++
tools/lib/thermal/thermal_nl.h | 43 +++
13 files changed, 1268 insertions(+), 2 deletions(-)
create mode 100644 tools/lib/thermal/.gitignore
create mode 100644 tools/lib/thermal/Build
create mode 100644 tools/lib/thermal/Makefile
create mode 100644 tools/lib/thermal/commands.c
create mode 100644 tools/lib/thermal/events.c
create mode 100644 tools/lib/thermal/include/thermal.h
create mode 100644 tools/lib/thermal/libthermal.map
create mode 100644 tools/lib/thermal/libthermal.pc.template
create mode 100644 tools/lib/thermal/sampling.c
create mode 100644 tools/lib/thermal/thermal.c
create mode 100644 tools/lib/thermal/thermal_nl.c
create mode 100644 tools/lib/thermal/thermal_nl.h
diff --git a/tools/Makefile b/tools/Makefile
index db2f7b8ebed5..c253cbd27c06 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -31,6 +31,7 @@ help:
@echo ' bootconfig - boot config tool'
@echo ' spi - spi tools'
@echo ' tmon - thermal monitoring and tuning tool'
+ @echo ' thermal - thermal library'
@echo ' tracing - misc tracing tools'
@echo ' turbostat - Intel CPU idle stats and freq reporting tool'
@echo ' usb - USB testing tools'
@@ -85,6 +86,9 @@ perf: FORCE
selftests: FORCE
$(call descend,testing/$@)
+thermal: FORCE
+ $(call descend,lib/$@)
+
turbostat x86_energy_perf_policy intel-speed-select: FORCE
$(call descend,power/x86/$@)
@@ -101,7 +105,7 @@ all: acpi cgroup counter cpupower gpio hv firewire \
perf selftests bootconfig spi turbostat usb \
virtio vm bpf x86_energy_perf_policy \
tmon freefall iio objtool kvm_stat wmi \
- pci debugging tracing
+ pci debugging tracing thermal
acpi_install:
$(call descend,power/$(@:_install=),install)
@@ -115,6 +119,9 @@ cgroup_install counter_install firewire_install gpio_install hv_install iio_inst
selftests_install:
$(call descend,testing/$(@:_install=),install)
+thermal_install:
+ $(call descend,lib/$(@:_install=),install)
+
turbostat_install x86_energy_perf_policy_install intel-speed-select_install:
$(call descend,power/x86/$(@:_install=),install)
@@ -160,6 +167,9 @@ perf_clean:
selftests_clean:
$(call descend,testing/$(@:_clean=),clean)
+thermal_clean:
+ $(call descend,lib/thermal,clean)
+
turbostat_clean x86_energy_perf_policy_clean intel-speed-select_clean:
$(call descend,power/x86/$(@:_clean=),clean)
@@ -177,6 +187,6 @@ clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_cl
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean \
gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \
- intel-speed-select_clean tracing_clean
+ intel-speed-select_clean tracing_clean thermal_clean
.PHONY: FORCE
diff --git a/tools/lib/thermal/.gitignore b/tools/lib/thermal/.gitignore
new file mode 100644
index 000000000000..5d2aeda80fea
--- /dev/null
+++ b/tools/lib/thermal/.gitignore
@@ -0,0 +1,2 @@
+libthermal.so*
+libthermal.pc
diff --git a/tools/lib/thermal/Build b/tools/lib/thermal/Build
new file mode 100644
index 000000000000..4a892d9e24f9
--- /dev/null
+++ b/tools/lib/thermal/Build
@@ -0,0 +1,5 @@
+libthermal-y += commands.o
+libthermal-y += events.o
+libthermal-y += thermal_nl.o
+libthermal-y += sampling.o
+libthermal-y += thermal.o
diff --git a/tools/lib/thermal/Makefile b/tools/lib/thermal/Makefile
new file mode 100644
index 000000000000..606295e64440
--- /dev/null
+++ b/tools/lib/thermal/Makefile
@@ -0,0 +1,165 @@
+# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+# Most of this file is copied from tools/lib/perf/Makefile
+
+LIBTHERMAL_VERSION = 0
+LIBTHERMAL_PATCHLEVEL = 0
+LIBTHERMAL_EXTRAVERSION = 1
+
+MAKEFLAGS += --no-print-directory
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+# $(info Determined 'srctree' to be $(srctree))
+endif
+
+INSTALL = install
+
+# Use DESTDIR for installing into a different root directory.
+# This is useful for building a package. The program will be
+# installed in this directory as if it was the root directory.
+# Then the build tool can move it later.
+DESTDIR ?=
+DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
+
+include $(srctree)/tools/scripts/Makefile.include
+include $(srctree)/tools/scripts/Makefile.arch
+
+ifeq ($(LP64), 1)
+ libdir_relative = lib64
+else
+ libdir_relative = lib
+endif
+
+prefix ?=
+libdir = $(prefix)/$(libdir_relative)
+
+# Shell quotes
+libdir_SQ = $(subst ','\'',$(libdir))
+libdir_relative_SQ = $(subst ','\'',$(libdir_relative))
+
+ifeq ("$(origin V)", "command line")
+ VERBOSE = $(V)
+endif
+ifndef VERBOSE
+ VERBOSE = 0
+endif
+
+ifeq ($(VERBOSE),1)
+ Q =
+else
+ Q = @
+endif
+
+# Set compile option CFLAGS
+ifdef EXTRA_CFLAGS
+ CFLAGS := $(EXTRA_CFLAGS)
+else
+ CFLAGS := -g -Wall
+endif
+
+INCLUDES = \
+-I/usr/include/libnl3 \
+-I$(srctree)/tools/lib/thermal/include \
+-I$(srctree)/tools/lib/ \
+-I$(srctree)/tools/include \
+-I$(srctree)/tools/arch/$(SRCARCH)/include/ \
+-I$(srctree)/tools/arch/$(SRCARCH)/include/uapi \
+-I$(srctree)/tools/include/uapi
+
+# Append required CFLAGS
+override CFLAGS += $(EXTRA_WARNINGS)
+override CFLAGS += -Werror -Wall
+override CFLAGS += -fPIC
+override CFLAGS += $(INCLUDES)
+override CFLAGS += -fvisibility=hidden
+override CFGLAS += -Wl,-L.
+override CFGLAS += -Wl,-lthermal
+
+all:
+
+export srctree OUTPUT CC LD CFLAGS V
+export DESTDIR DESTDIR_SQ
+
+include $(srctree)/tools/build/Makefile.include
+
+VERSION_SCRIPT := libthermal.map
+
+PATCHLEVEL = $(LIBTHERMAL_PATCHLEVEL)
+EXTRAVERSION = $(LIBTHERMAL_EXTRAVERSION)
+VERSION = $(LIBTHERMAL_VERSION).$(LIBTHERMAL_PATCHLEVEL).$(LIBTHERMAL_EXTRAVERSION)
+
+LIBTHERMAL_SO := $(OUTPUT)libthermal.so.$(VERSION)
+LIBTHERMAL_A := $(OUTPUT)libthermal.a
+LIBTHERMAL_IN := $(OUTPUT)libthermal-in.o
+LIBTHERMAL_PC := $(OUTPUT)libthermal.pc
+LIBTHERMAL_ALL := $(LIBTHERMAL_A) $(OUTPUT)libthermal.so*
+
+THERMAL_UAPI := include/uapi/linux/thermal.h
+
+$(THERMAL_UAPI): FORCE
+ ln -sf $(srctree)/$@ $(srctree)/tools/$@
+
+$(LIBTHERMAL_IN): FORCE
+ $(Q)$(MAKE) $(build)=libthermal
+
+$(LIBTHERMAL_A): $(LIBTHERMAL_IN)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBTHERMAL_IN)
+
+$(LIBTHERMAL_SO): $(LIBTHERMAL_IN)
+ $(QUIET_LINK)$(CC) --shared -Wl,-soname,libthermal.so \
+ -Wl,--version-script=$(VERSION_SCRIPT) $^ -o $@
+ @ln -sf $(@F) $(OUTPUT)libthermal.so
+ @ln -sf $(@F) $(OUTPUT)libthermal.so.$(LIBTHERMAL_VERSION)
+
+
+libs: $(THERMAL_UAPI) $(LIBTHERMAL_A) $(LIBTHERMAL_SO) $(LIBTHERMAL_PC)
+
+all: fixdep
+ $(Q)$(MAKE) libs
+
+clean:
+ $(call QUIET_CLEAN, libthermal) $(RM) $(LIBTHERMAL_A) \
+ *.o *~ *.a *.so *.so.$(VERSION) *.so.$(LIBTHERMAL_VERSION) .*.d .*.cmd LIBTHERMAL-CFLAGS $(LIBTHERMAL_PC)
+
+$(LIBTHERMAL_PC):
+ $(QUIET_GEN)sed -e "s|@PREFIX@|$(prefix)|" \
+ -e "s|@LIBDIR@|$(libdir_SQ)|" \
+ -e "s|@VERSION@|$(VERSION)|" \
+ < libthermal.pc.template > $@
+
+define do_install_mkdir
+ if [ ! -d '$(DESTDIR_SQ)$1' ]; then \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$1'; \
+ fi
+endef
+
+define do_install
+ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
+ fi; \
+ $(INSTALL) $1 $(if $3,-m $3,) '$(DESTDIR_SQ)$2'
+endef
+
+install_lib: libs
+ $(call QUIET_INSTALL, $(LIBTHERMAL_ALL)) \
+ $(call do_install_mkdir,$(libdir_SQ)); \
+ cp -fpR $(LIBTHERMAL_ALL) $(DESTDIR)$(libdir_SQ)
+
+install_headers:
+ $(call QUIET_INSTALL, headers) \
+ $(call do_install,include/thermal.h,$(prefix)/include/thermal,644); \
+
+install_pkgconfig: $(LIBTHERMAL_PC)
+ $(call QUIET_INSTALL, $(LIBTHERMAL_PC)) \
+ $(call do_install,$(LIBTHERMAL_PC),$(libdir_SQ)/pkgconfig,644)
+
+install_doc:
+ $(Q)$(MAKE) -C Documentation install-man install-html install-examples
+
+install: install_lib install_headers install_pkgconfig
+
+FORCE:
+
+.PHONY: all install clean FORCE
diff --git a/tools/lib/thermal/commands.c b/tools/lib/thermal/commands.c
new file mode 100644
index 000000000000..3f08baa85299
--- /dev/null
+++ b/tools/lib/thermal/commands.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: LGPL-2.1+
+// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <thermal.h>
+#include "thermal_nl.h"
+
+static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
+ /* Thermal zone */
+ [THERMAL_GENL_ATTR_TZ] = { .type = NLA_NESTED },
+ [THERMAL_GENL_ATTR_TZ_ID] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_TZ_TEMP] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_TZ_TRIP] = { .type = NLA_NESTED },
+ [THERMAL_GENL_ATTR_TZ_TRIP_ID] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_TZ_TRIP_HYST] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_TZ_MODE] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT] = { .type = NLA_U32 },
+ [THERMAL_GENL_ATTR_TZ_NAME] = { .type = NLA_STRING },
+
+ /* Governor(s) */
+ [THERMAL_GENL_ATTR_TZ_GOV] = { .type = NLA_NESTED },
+ [THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = NLA_STRING },
+
+ /* Cooling devices */
+ [THERMAL_GENL_ATTR_CDEV] = { .type = NLA_NESTED },
+ [THERMAL_GENL_ATTR_CDEV_ID] = { .type = NLA_U32 },
+ [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 },
+};
+
+static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz)
+{
+ struct nlattr *attr;
+ struct thermal_zone *__tz = NULL;
+ size_t size = 0;
+ int rem;
+
+ nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ], rem) {
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_ID) {
+
+ size++;
+
+ __tz = realloc(__tz, sizeof(*__tz) * (size + 2));
+ if (!__tz)
+ return -1;
+
+ __tz[size - 1].id = nla_get_u32(attr);
+ }
+
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME)
+ nla_strlcpy(__tz[size - 1].name, attr,
+ THERMAL_NAME_LENGTH);
+ }
+
+ /*
+ * We end the array of thermal zones
+ */
+ __tz[size].id = -1;
+
+ *tz = __tz;
+
+ return 0;
+}
+
+static int parse_cdev_get(struct genl_info *info, struct thermal_cdev **cdev)
+{
+ struct nlattr *attr;
+ struct thermal_cdev *__cdev = NULL;
+ size_t size = 0;
+ int rem;
+
+ nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_CDEV], rem) {
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_ID) {
+
+ size++;
+
+ __cdev = realloc(__cdev, sizeof(*__cdev) * (size + 2));
+ if (!__cdev)
+ return -1;
+
+ __cdev[size - 1].id = nla_get_u32(attr);
+ }
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_NAME) {
+ nla_strlcpy(__cdev[size - 1].name, attr,
+ THERMAL_NAME_LENGTH);
+ }
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_CUR_STATE) {
+ __cdev[size - 1].cur_state = nla_get_u32(attr);
+ }
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_MAX_STATE) {
+ __cdev[size - 1].max_state = nla_get_u32(attr);
+ }
+ }
+
+ __cdev[size].id = -1;
+
+ *cdev = __cdev;
+
+ return 0;
+}
+
+static int parse_tz_get_trip(struct genl_info *info, struct thermal_zone *tz)
+{
+ struct nlattr *attr;
+ struct thermal_trip *__tt = NULL;
+ size_t size = 0;
+ int rem;
+
+ nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ_TRIP], rem) {
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_ID) {
+
+ size++;
+
+ __tt = realloc(__tt, sizeof(*__tt) * (size + 2));
+ if (!__tt)
+ return -1;
+
+ __tt[size - 1].id = nla_get_u32(attr);
+ }
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TYPE)
+ __tt[size - 1].type = nla_get_u32(attr);
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TEMP)
+ __tt[size - 1].temp = nla_get_u32(attr);
+
+ if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_HYST)
+ __tt[size - 1].hyst = nla_get_u32(attr);
+ }
+
+ __tt[size].id = -1;
+
+ tz->trip = __tt;
+
+ return 0;
+}
+
+static int parse_tz_get_temp(struct genl_info *info, struct thermal_zone *tz)
+{
+ int id = -1;
+
+ if (info->attrs[THERMAL_GENL_ATTR_TZ_ID])
+ id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]);
+
+ if (tz->id != id)
+ return -1;
+
+ if (info->attrs[THERMAL_GENL_ATTR_TZ_TEMP])
+ tz->temp = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]);
+
+ return 0;
+}
+
+static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz)
+{
+ int id = -1;
+
+ if (info->attrs[THERMAL_GENL_ATTR_TZ_ID])
+ id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]);
+
+ if (tz->id != id)
+ return -1;
+
+ if (info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME]) {
+ nla_strlcpy(tz->governor,
+ info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME],
+ THERMAL_NAME_LENGTH);
+ }
+
+ return 0;
+}
+
+static int handle_netlink(struct nl_cache_ops *unused,
+ struct genl_cmd *cmd,
+ struct genl_info *info, void *arg)
+{
+ switch (cmd->c_id) {
+
+ case THERMAL_GENL_CMD_TZ_GET_ID:
+ parse_tz_get(info, arg);
+ break;
+
+ case THERMAL_GENL_CMD_CDEV_GET:
+ parse_cdev_get(info, arg);
+ break;
+
+ case THERMAL_GENL_CMD_TZ_GET_TEMP:
+ parse_tz_get_temp(info, arg);
+ break;
+
+ case THERMAL_GENL_CMD_TZ_GET_TRIP:
+ parse_tz_get_trip(info, arg);
+ break;
+
+ case THERMAL_GENL_CMD_TZ_GET_GOV:
+ parse_tz_get_gov(info, arg);
+ break;
+
+ default:
+ return -1;
+ };
+
+ return 0;
+}
+
+static struct genl_cmd thermal_cmds[] = {
+ {
+ .c_id = THERMAL_GENL_CMD_TZ_GET_ID,
+ .c_name = (char *)"List thermal zones",
+ .c_msg_parser = handle_netlink,
+ .c_maxattr = THERMAL_GENL_ATTR_MAX,
+ .c_attr_policy = thermal_genl_policy,
+ },
+ {
+ .c_id = THERMAL_GENL_CMD_TZ_GET_GOV,
+ .c_name = (char *)"Get governor",
+ .c_msg_parser = handle_netlink,
+ .c_maxattr = THERMAL_GENL_ATTR_MAX,
+ .c_attr_policy = thermal_genl_policy,
+ },
+ {
+ .c_id = THERMAL_GENL_CMD_TZ_GET_TEMP,
+ .c_name = (char *)"Get thermal zone temperature",
+ .c_msg_parser = handle_netlink,
+ .c_maxattr = THERMAL_GENL_ATTR_MAX,
+ .c_attr_policy = thermal_genl_policy,
+ },
+ {
+ .c_id = THERMAL_GENL_CMD_TZ_GET_TRIP,
+ .c_name = (char *)"Get thermal zone trip points",
+ .c_msg_parser = handle_netlink,
+ .c_maxattr = THERMAL_GENL_ATTR_MAX,
+ .c_attr_policy = thermal_genl_policy,
+ },
+ {
+ .c_id = THERMAL_GENL_CMD_CDEV_GET,
+ .c_name = (char *)"Get cooling devices",
+ .c_msg_parser = handle_netlink,
+ .c_maxattr = THERMAL_GENL_ATTR_MAX,
+ .c_attr_policy = thermal_genl_policy,
+ },
+};
+
+static struct genl_ops thermal_cmd_ops = {
+ .o_name = (char *)"thermal",
+ .o_cmds = thermal_cmds,
+ .o_ncmds = ARRAY_SIZE(thermal_cmds),
+};
+
+static int thermal_genl_auto(struct thermal_handler *th, int id, int cmd,
+ int flags, void *arg)
+{
+ struct nl_msg *msg;
+ void *hdr;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id,
+ 0, flags, cmd, THERMAL_GENL_VERSION);
+ if (!hdr)
+ return -1;
+
+ if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id))
+ return -1;
+
+ if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg))
+ return -1;
+
+ nlmsg_free(msg);
+
+ return 0;
+}
+
+int thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz)
+{
+ return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET_ID,
+ NLM_F_DUMP | NLM_F_ACK, tz);
+}
+
+int thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc)
+{
+ return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET,
+ NLM_F_DUMP | NLM_F_ACK, tc);
+}
+
+int 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);
+}
+
+int 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);
+}
+
+int 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);
+}
+
+int thermal_cmd_init(struct thermal_handler *th)
+{
+ int ret;
+ int family;
+
+ if (nl_thermal_connect(&th->sk_cmd, &th->cb_cmd))
+ return -1;
+
+ ret = genl_register_family(&thermal_cmd_ops);
+ if (ret)
+ return -1;
+
+ ret = genl_ops_resolve(th->sk_cmd, &thermal_cmd_ops);
+ if (ret)
+ return -1;
+
+ family = genl_ctrl_resolve(th->sk_cmd, "nlctrl");
+ if (family != GENL_ID_CTRL)
+ return -1;
+
+ return 0;
+}
diff --git a/tools/lib/thermal/events.c b/tools/lib/thermal/events.c
new file mode 100644
index 000000000000..3e7432480f69
--- /dev/null
+++ b/tools/lib/thermal/events.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: LGPL-2.1+
+// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#include <linux/netlink.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+#include <thermal.h>
+#include "thermal_nl.h"
+
+/*
+ * Optimization: fill this array to tell which event we do want to pay
+ * attention to. That happens at init time with the ops
+ * structure. Each ops will enable the event and the general handler
+ * will be able to discard the event if there is not ops associated
+ * with it.
+ */
+static int enabled_ops[__THERMAL_GENL_EVENT_MAX];
+
+static int handle_thermal_event(struct nl_msg *n, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(n);
+ struct genlmsghdr *genlhdr = genlmsg_hdr(nlh);
+ struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
+ struct thermal_handler_param *thp = arg;
+ struct thermal_events_ops *ops = &thp->th->ops->events;
+
+ genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
+
+ arg = thp->arg;
+
+ /*
+ * This is an event we don't care of, bail out.
+ */
+ if (!enabled_ops[genlhdr->cmd])
+ return 0;
+
+ switch (genlhdr->cmd) {
+
+ case THERMAL_GENL_EVENT_TZ_CREATE:
+ return ops->tz_create(nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg);
+
+ case THERMAL_GENL_EVENT_TZ_DELETE:
+ return ops->tz_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg);
+
+ case THERMAL_GENL_EVENT_TZ_ENABLE:
+ return ops->tz_enable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg);
+
+ case THERMAL_GENL_EVENT_TZ_DISABLE:
+ return ops->tz_disable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg);
+
+ case THERMAL_GENL_EVENT_TZ_TRIP_CHANGE:
+ return ops->trip_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg);
+
+ case THERMAL_GENL_EVENT_TZ_TRIP_ADD:
+ return ops->trip_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg);
+
+ case THERMAL_GENL_EVENT_TZ_TRIP_DELETE:
+ return ops->trip_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), arg);
+
+ case THERMAL_GENL_EVENT_TZ_TRIP_UP:
+ return ops->trip_high(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg);
+
+ case THERMAL_GENL_EVENT_TZ_TRIP_DOWN:
+ return ops->trip_low(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg);
+
+ case THERMAL_GENL_EVENT_CDEV_ADD:
+ return ops->cdev_add(nla_get_string(attrs[THERMAL_GENL_ATTR_CDEV_NAME]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]), arg);
+
+ case THERMAL_GENL_EVENT_CDEV_DELETE:
+ return ops->cdev_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), arg);
+
+ case THERMAL_GENL_EVENT_CDEV_STATE_UPDATE:
+ return ops->cdev_update(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]), 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);
+ default:
+ return -1;
+ }
+}
+
+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;
+}
+
+int thermal_events_handle(struct thermal_handler *th, void *arg)
+{
+ struct thermal_handler_param thp = { .th = th, .arg = arg };
+
+ if (!th)
+ return -1;
+
+ if (nl_cb_set(th->cb_event, NL_CB_VALID, NL_CB_CUSTOM,
+ handle_thermal_event, &thp))
+ return -1;
+
+ return nl_recvmsgs(th->sk_event, th->cb_event);
+}
+
+int thermal_events_fd(struct thermal_handler *th)
+{
+ if (!th)
+ return -1;
+
+ return nl_socket_get_fd(th->sk_event);
+}
+
+int thermal_events_init(struct thermal_handler *th)
+{
+ thermal_events_ops_init(&th->ops->events);
+
+ if (nl_thermal_connect(&th->sk_event, &th->cb_event))
+ return -1;
+
+ if (nl_subscribe_thermal(th->sk_event, th->cb_event,
+ THERMAL_GENL_EVENT_GROUP_NAME))
+ return -1;
+
+ return 0;
+}
diff --git a/tools/lib/thermal/include/thermal.h b/tools/lib/thermal/include/thermal.h
new file mode 100644
index 000000000000..b6ad155ac67b
--- /dev/null
+++ b/tools/lib/thermal/include/thermal.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/* Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> */
+#ifndef __LIBTHERMAL_H
+#define __LIBTHERMAL_H
+
+#include <linux/thermal.h>
+
+#ifndef LIBTHERMAL_API
+#define LIBTHERMAL_API __attribute__((visibility("default")))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct thermal_sampling_ops {
+ int (*tz_temp)(int tz_id, int temp, void *arg);
+};
+
+struct thermal_events_ops {
+ int (*tz_create)(const char *name, int tz_id, void *arg);
+ int (*tz_delete)(int tz_id, void *arg);
+ int (*tz_enable)(int tz_id, void *arg);
+ int (*tz_disable)(int tz_id, void *arg);
+ int (*trip_high)(int tz_id, int trip_id, int temp, void *arg);
+ int (*trip_low)(int tz_id, int trip_id, int temp, void *arg);
+ int (*trip_add)(int tz_id, int trip_id, int type, int temp, int hyst, void *arg);
+ int (*trip_change)(int tz_id, int trip_id, int type, int temp, int hyst, void *arg);
+ int (*trip_delete)(int tz_id, int trip_id, void *arg);
+ int (*cdev_add)(const char *name, int cdev_id, int max_state, void *arg);
+ 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);
+};
+
+struct thermal_ops {
+ struct thermal_sampling_ops sampling;
+ struct thermal_events_ops events;
+};
+
+struct thermal_trip {
+ int id;
+ int type;
+ int temp;
+ int hyst;
+};
+
+struct thermal_zone {
+ int id;
+ int temp;
+ char name[THERMAL_NAME_LENGTH];
+ char governor[THERMAL_NAME_LENGTH];
+ struct thermal_trip *trip;
+};
+
+struct thermal_cdev {
+ int id;
+ char name[THERMAL_NAME_LENGTH];
+ int max_state;
+ int min_state;
+ int cur_state;
+};
+
+struct thermal_handler;
+
+typedef int (*cb_tz_t)(struct thermal_zone *, void *);
+
+typedef int (*cb_tt_t)(struct thermal_trip *, void *);
+
+typedef int (*cb_tc_t)(struct thermal_cdev *, 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 struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz,
+ const char *name);
+
+LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_id(struct thermal_zone *tz, int id);
+
+LIBTHERMAL_API struct thermal_zone *thermal_zone_discover(struct thermal_handler *th);
+
+LIBTHERMAL_API struct thermal_handler *thermal_init(struct thermal_ops *ops);
+
+/*
+ * Netlink thermal events
+ */
+LIBTHERMAL_API int thermal_events_init(struct thermal_handler *th);
+
+LIBTHERMAL_API int thermal_events_handle(struct thermal_handler *th, void *arg);
+
+LIBTHERMAL_API int thermal_events_fd(struct thermal_handler *th);
+
+/*
+ * Netlink thermal commands
+ */
+LIBTHERMAL_API int thermal_cmd_init(struct thermal_handler *th);
+
+LIBTHERMAL_API int thermal_cmd_get_tz(struct thermal_handler *th,
+ struct thermal_zone **tz);
+
+LIBTHERMAL_API int thermal_cmd_get_cdev(struct thermal_handler *th,
+ struct thermal_cdev **tc);
+
+LIBTHERMAL_API int thermal_cmd_get_trip(struct thermal_handler *th,
+ struct thermal_zone *tz);
+
+LIBTHERMAL_API int thermal_cmd_get_governor(struct thermal_handler *th,
+ struct thermal_zone *tz);
+
+LIBTHERMAL_API int thermal_cmd_get_temp(struct thermal_handler *th,
+ struct thermal_zone *tz);
+
+/*
+ * Netlink thermal samples
+ */
+LIBTHERMAL_API int thermal_sampling_init(struct thermal_handler *th);
+
+LIBTHERMAL_API int thermal_sampling_handle(struct thermal_handler *th, void *arg);
+
+LIBTHERMAL_API int thermal_sampling_fd(struct thermal_handler *th);
+
+#endif /* __LIBTHERMAL_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tools/lib/thermal/libthermal.map b/tools/lib/thermal/libthermal.map
new file mode 100644
index 000000000000..d5e77738c7a4
--- /dev/null
+++ b/tools/lib/thermal/libthermal.map
@@ -0,0 +1,25 @@
+LIBTHERMAL_0.0.1 {
+ global:
+ thermal_init;
+ for_each_thermal_zone;
+ for_each_thermal_trip;
+ for_each_thermal_cdev;
+ thermal_zone_find_by_name;
+ thermal_zone_find_by_id;
+ thermal_zone_discover;
+ thermal_init;
+ thermal_events_init;
+ thermal_events_handle;
+ thermal_events_fd;
+ thermal_cmd_init;
+ thermal_cmd_get_tz;
+ thermal_cmd_get_cdev;
+ thermal_cmd_get_trip;
+ thermal_cmd_get_governor;
+ thermal_cmd_get_temp;
+ thermal_sampling_init;
+ thermal_sampling_handle;
+ thermal_sampling_fd;
+local:
+ *;
+};
diff --git a/tools/lib/thermal/libthermal.pc.template b/tools/lib/thermal/libthermal.pc.template
new file mode 100644
index 000000000000..6f3769731b59
--- /dev/null
+++ b/tools/lib/thermal/libthermal.pc.template
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+prefix=@PREFIX@
+libdir=@LIBDIR@
+includedir=${prefix}/include
+
+Name: libthermal
+Description: thermal library
+Requires: libnl-3.0 libnl-genl-3.0
+Version: @VERSION@
+Libs: -L${libdir} -lnl-genl-3 -lnl-3
+Cflags: -I${includedir} -I{include}/libnl3
diff --git a/tools/lib/thermal/sampling.c b/tools/lib/thermal/sampling.c
new file mode 100644
index 000000000000..a0f28fc1eb4b
--- /dev/null
+++ b/tools/lib/thermal/sampling.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: LGPL-2.1+
+// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <thermal.h>
+#include "thermal_nl.h"
+
+static int handle_thermal_sample(struct nl_msg *n, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(n);
+ struct genlmsghdr *genlhdr = genlmsg_hdr(nlh);
+ struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
+ struct thermal_handler_param *thp = arg;
+ struct thermal_handler *th = thp->th;
+
+ genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
+
+ switch (genlhdr->cmd) {
+
+ case THERMAL_GENL_SAMPLING_TEMP:
+ return th->ops->sampling.tz_temp(
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
+ nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg);
+ default:
+ return -1;
+ }
+}
+
+int thermal_sampling_handle(struct thermal_handler *th, void *arg)
+{
+ struct thermal_handler_param thp = { .th = th, .arg = arg };
+
+ if (!th)
+ return -1;
+
+ if (nl_cb_set(th->cb_sampling, NL_CB_VALID, NL_CB_CUSTOM,
+ handle_thermal_sample, &thp))
+ return -1;
+
+ return nl_recvmsgs(th->sk_sampling, th->cb_sampling);
+}
+
+int thermal_sampling_fd(struct thermal_handler *th)
+{
+ if (!th)
+ return -1;
+
+ return nl_socket_get_fd(th->sk_sampling);
+}
+
+int thermal_sampling_init(struct thermal_handler *th)
+{
+ if (nl_thermal_connect(&th->sk_sampling, &th->cb_sampling))
+ return -1;
+
+ if (nl_subscribe_thermal(th->sk_sampling, th->cb_sampling,
+ THERMAL_GENL_SAMPLING_GROUP_NAME))
+ return -1;
+
+ return 0;
+}
diff --git a/tools/lib/thermal/thermal.c b/tools/lib/thermal/thermal.c
new file mode 100644
index 000000000000..6293a4a22310
--- /dev/null
+++ b/tools/lib/thermal/thermal.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: LGPL-2.1+
+// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#include <stdio.h>
+#include <thermal.h>
+
+#include "thermal_nl.h"
+
+int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg)
+{
+ int i, ret = 0;
+
+ for (i = 0; cdev[i].id != -1; i++)
+ ret |= cb(&cdev[i], arg);
+
+ return ret;
+}
+
+int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg)
+{
+ int i, ret = 0;
+
+ for (i = 0; tt[i].id != -1; i++)
+ ret |= cb(&tt[i], arg);
+
+ return ret;
+}
+
+int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg)
+{
+ int i, ret = 0;
+
+ for (i = 0; tz[i].id != -1; i++)
+ ret |= cb(&tz[i], arg);
+
+ return ret;
+}
+
+struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz,
+ const char *name)
+{
+ int i;
+
+ if (!name)
+ return NULL;
+
+ for (i = 0; tz[i].id != -1; i++) {
+ if (!strcmp(tz[i].name, name))
+ return &tz[i];
+ }
+
+ return NULL;
+}
+
+struct thermal_zone *thermal_zone_find_by_id(struct thermal_zone *tz, int id)
+{
+ int i;
+
+ if (id < 0)
+ return NULL;
+
+ for (i = 0; tz[i].id != -1; i++) {
+ if (tz[i].id == id)
+ return &tz[i];
+ }
+
+ return NULL;
+}
+
+static int __thermal_zone_discover(struct thermal_zone *tz, void *th)
+{
+ if (thermal_cmd_get_trip(th, tz) < 0)
+ return -1;
+
+ if (thermal_cmd_get_governor(th, tz))
+ return -1;
+
+ return 0;
+}
+
+struct thermal_zone *thermal_zone_discover(struct thermal_handler *th)
+{
+ struct thermal_zone *tz;
+
+ if (thermal_cmd_get_tz(th, &tz) < 0)
+ return NULL;
+
+ if (for_each_thermal_zone(tz, __thermal_zone_discover, th))
+ return NULL;
+
+ return tz;
+}
+
+struct thermal_handler *thermal_init(struct thermal_ops *ops)
+{
+ struct thermal_handler *th;
+
+ th = malloc(sizeof(*th));
+ if (!th)
+ return NULL;
+ th->ops = ops;
+
+ if (thermal_events_init(th))
+ goto out_free;
+
+ if (thermal_sampling_init(th))
+ goto out_free;
+
+ if (thermal_cmd_init(th))
+ goto out_free;
+
+ return th;
+
+out_free:
+ free(th);
+
+ return NULL;
+}
diff --git a/tools/lib/thermal/thermal_nl.c b/tools/lib/thermal/thermal_nl.c
new file mode 100644
index 000000000000..b3f795517abd
--- /dev/null
+++ b/tools/lib/thermal/thermal_nl.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: LGPL-2.1+
+// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/epoll.h>
+
+#include <thermal.h>
+
+#include "thermal_nl.h"
+
+struct handler_args {
+ const char *group;
+ int id;
+};
+
+static __thread int err;
+static __thread int done;
+
+static int nl_seq_check_handler(struct nl_msg *msg, void *arg)
+{
+ return NL_OK;
+}
+
+static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *nl_err,
+ void *arg)
+{
+ int *ret = arg;
+
+ if (ret)
+ *ret = nl_err->error;
+
+ return NL_STOP;
+}
+
+static int nl_finish_handler(struct nl_msg *msg, void *arg)
+{
+ int *ret = arg;
+
+ if (ret)
+ *ret = 1;
+
+ return NL_OK;
+}
+
+static int nl_ack_handler(struct nl_msg *msg, void *arg)
+{
+ int *ret = arg;
+
+ if (ret)
+ *ret = 1;
+
+ return NL_OK;
+}
+
+int nl_send_msg(struct nl_sock *sock, struct nl_cb *cb, struct nl_msg *msg,
+ int (*rx_handler)(struct nl_msg *, void *), void *data)
+{
+ if (!rx_handler)
+ return -1;
+
+ err = nl_send_auto_complete(sock, msg);
+ if (err < 0)
+ return err;
+
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data);
+
+ err = done = 0;
+
+ while (err == 0 && done == 0)
+ nl_recvmsgs(sock, cb);
+
+ return err;
+}
+
+static int nl_family_handler(struct nl_msg *msg, void *arg)
+{
+ struct handler_args *grp = arg;
+ struct nlattr *tb[CTRL_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *mcgrp;
+ int rem_mcgrp;
+
+ nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[CTRL_ATTR_MCAST_GROUPS])
+ return -1;
+
+ nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
+
+ struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
+
+ nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
+ nla_data(mcgrp), nla_len(mcgrp), NULL);
+
+ if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] ||
+ !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
+ continue;
+
+ if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
+ grp->group,
+ nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])))
+ continue;
+
+ grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int nl_get_multicast_id(struct nl_sock *sock, struct nl_cb *cb,
+ const char *family, const char *group)
+{
+ struct nl_msg *msg;
+ int ret = 0, ctrlid;
+ struct handler_args grp = {
+ .group = group,
+ .id = -ENOENT,
+ };
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ ctrlid = genl_ctrl_resolve(sock, "nlctrl");
+
+ genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
+
+ nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family);
+
+ ret = nl_send_msg(sock, cb, msg, nl_family_handler, &grp);
+ if (ret)
+ goto nla_put_failure;
+
+ ret = grp.id;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb **nl_cb)
+{
+ struct nl_cb *cb;
+ struct nl_sock *sock;
+
+ cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!cb)
+ return -1;
+
+ sock = nl_socket_alloc();
+ if (!sock)
+ goto out_cb_free;
+
+ if (genl_connect(sock))
+ goto out_socket_free;
+
+ if (nl_cb_err(cb, NL_CB_CUSTOM, nl_error_handler, &err) ||
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_finish_handler, &done) ||
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl_ack_handler, &done) ||
+ nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check_handler, &done))
+ return -1;
+
+ *nl_sock = sock;
+ *nl_cb = cb;
+
+ return 0;
+
+out_socket_free:
+ nl_socket_free(sock);
+out_cb_free:
+ nl_cb_put(cb);
+ return -1;
+}
+
+void nl_thermal_disconnect(struct nl_sock *nl_sock, struct nl_cb *nl_cb)
+{
+ nl_close(nl_sock);
+ nl_socket_free(nl_sock);
+ nl_cb_put(nl_cb);
+}
+
+int nl_subscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb,
+ const char *group)
+{
+ int mcid;
+
+ mcid = nl_get_multicast_id(nl_sock, nl_cb, THERMAL_GENL_FAMILY_NAME,
+ group);
+ if (mcid < 0)
+ return -1;
+
+ if (nl_socket_add_membership(nl_sock, mcid))
+ return -1;
+
+ return 0;
+}
diff --git a/tools/lib/thermal/thermal_nl.h b/tools/lib/thermal/thermal_nl.h
new file mode 100644
index 000000000000..54dbbad58349
--- /dev/null
+++ b/tools/lib/thermal/thermal_nl.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/* Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> */
+#ifndef __THERMAL_H
+#define __THERMAL_H
+
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/mngt.h>
+#include <netlink/genl/ctrl.h>
+
+struct thermal_handler {
+ int done;
+ int error;
+ struct thermal_ops *ops;
+ struct nl_msg *msg;
+ struct nl_sock *sk_event;
+ struct nl_sock *sk_sampling;
+ struct nl_sock *sk_cmd;
+ struct nl_cb *cb_cmd;
+ struct nl_cb *cb_event;
+ struct nl_cb *cb_sampling;
+};
+
+struct thermal_handler_param {
+ struct thermal_handler *th;
+ void *arg;
+};
+
+/*
+ * Low level netlink
+ */
+extern int nl_subscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb,
+ const char *group);
+
+extern int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb **nl_cb);
+
+extern void nl_thermal_disconnect(struct nl_sock *nl_sock, struct nl_cb *nl_cb);
+
+extern int nl_send_msg(struct nl_sock *sock, struct nl_cb *nl_cb, struct nl_msg *msg,
+ int (*rx_handler)(struct nl_msg *, void *),
+ void *data);
+
+#endif /* __THERMAL_H */
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [RFC PATCH 2/4] tools/thermal: Add util library
2022-02-14 21:04 [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library Daniel Lezcano
@ 2022-02-14 21:04 ` Daniel Lezcano
2022-02-14 21:04 ` [RFC PATCH 3/4] tools/thermal: A temperature capture tool Daniel Lezcano
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: Daniel Lezcano @ 2022-02-14 21:04 UTC (permalink / raw)
To: daniel.lezcano, rafael
Cc: srinivas.pandruvada, linux-kernel, linux-pm, Amit Kucheria, Zhang Rui
The next changes will provide a couple of tools using some common
functions provided by this library.
It provides basic wrappers for:
- mainloop
- logging
- timestamp
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
tools/thermal/lib/Build | 3 +
tools/thermal/lib/Makefile | 158 ++++++++++++++++++
.../thermal/lib/libthermal_tools.pc.template | 12 ++
tools/thermal/lib/log.c | 77 +++++++++
tools/thermal/lib/log.h | 31 ++++
tools/thermal/lib/mainloop.c | 135 +++++++++++++++
tools/thermal/lib/mainloop.h | 14 ++
tools/thermal/lib/thermal-tools.h | 10 ++
tools/thermal/lib/uptimeofday.c | 40 +++++
tools/thermal/lib/uptimeofday.h | 12 ++
10 files changed, 492 insertions(+)
create mode 100644 tools/thermal/lib/Build
create mode 100644 tools/thermal/lib/Makefile
create mode 100644 tools/thermal/lib/libthermal_tools.pc.template
create mode 100644 tools/thermal/lib/log.c
create mode 100644 tools/thermal/lib/log.h
create mode 100644 tools/thermal/lib/mainloop.c
create mode 100644 tools/thermal/lib/mainloop.h
create mode 100644 tools/thermal/lib/thermal-tools.h
create mode 100644 tools/thermal/lib/uptimeofday.c
create mode 100644 tools/thermal/lib/uptimeofday.h
diff --git a/tools/thermal/lib/Build b/tools/thermal/lib/Build
new file mode 100644
index 000000000000..06f22760a272
--- /dev/null
+++ b/tools/thermal/lib/Build
@@ -0,0 +1,3 @@
+libthermal_tools-y += mainloop.o
+libthermal_tools-y += log.o
+libthermal_tools-y += uptimeofday.o
diff --git a/tools/thermal/lib/Makefile b/tools/thermal/lib/Makefile
new file mode 100644
index 000000000000..bdf4659adfeb
--- /dev/null
+++ b/tools/thermal/lib/Makefile
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+# Most of this file is copied from tools/lib/perf/Makefile
+
+LIBTHERMAL_TOOLS_VERSION = 0
+LIBTHERMAL_TOOLS_PATCHLEVEL = 0
+LIBTHERMAL_TOOLS_EXTRAVERSION = 1
+
+MAKEFLAGS += --no-print-directory
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+# $(info Determined 'srctree' to be $(srctree))
+endif
+
+INSTALL = install
+
+# Use DESTDIR for installing into a different root directory.
+# This is useful for building a package. The program will be
+# installed in this directory as if it was the root directory.
+# Then the build tool can move it later.
+DESTDIR ?=
+DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
+
+include $(srctree)/tools/scripts/Makefile.include
+include $(srctree)/tools/scripts/Makefile.arch
+
+ifeq ($(LP64), 1)
+ libdir_relative = lib64
+else
+ libdir_relative = lib
+endif
+
+prefix ?=
+libdir = $(prefix)/$(libdir_relative)
+
+# Shell quotes
+libdir_SQ = $(subst ','\'',$(libdir))
+libdir_relative_SQ = $(subst ','\'',$(libdir_relative))
+
+ifeq ("$(origin V)", "command line")
+ VERBOSE = $(V)
+endif
+ifndef VERBOSE
+ VERBOSE = 0
+endif
+
+ifeq ($(VERBOSE),1)
+ Q =
+else
+ Q = @
+endif
+
+# Set compile option CFLAGS
+ifdef EXTRA_CFLAGS
+ CFLAGS := $(EXTRA_CFLAGS)
+else
+ CFLAGS := -g -Wall
+endif
+
+INCLUDES = \
+-I/usr/include/libnl3 \
+-I$(srctree)/tools/lib/thermal/include \
+-I$(srctree)/tools/lib/ \
+-I$(srctree)/tools/include \
+-I$(srctree)/tools/arch/$(SRCARCH)/include/ \
+-I$(srctree)/tools/arch/$(SRCARCH)/include/uapi \
+-I$(srctree)/tools/include/uapi
+
+# Append required CFLAGS
+override CFLAGS += $(EXTRA_WARNINGS)
+override CFLAGS += -Werror -Wall
+override CFLAGS += -fPIC
+override CFLAGS += $(INCLUDES)
+override CFGLAS += -Wl,-L.
+override CFGLAS += -Wl,-lthermal
+
+all:
+
+export srctree OUTPUT CC LD CFLAGS V
+export DESTDIR DESTDIR_SQ
+
+include $(srctree)/tools/build/Makefile.include
+
+PATCHLEVEL = $(LIBTHERMAL_TOOLS_PATCHLEVEL)
+EXTRAVERSION = $(LIBTHERMAL_TOOLS_EXTRAVERSION)
+VERSION = $(LIBTHERMAL_TOOLS_VERSION).$(LIBTHERMAL_TOOLS_PATCHLEVEL).$(LIBTHERMAL_TOOLS_EXTRAVERSION)
+
+LIBTHERMAL_TOOLS_SO := $(OUTPUT)libthermal_tools.so.$(VERSION)
+LIBTHERMAL_TOOLS_A := $(OUTPUT)libthermal_tools.a
+LIBTHERMAL_TOOLS_IN := $(OUTPUT)libthermal_tools-in.o
+LIBTHERMAL_TOOLS_PC := $(OUTPUT)libthermal_tools.pc
+
+LIBTHERMAL_TOOLS_ALL := $(LIBTHERMAL_TOOLS_A) $(OUTPUT)libthermal_tools.so*
+
+$(LIBTHERMAL_TOOLS_IN): FORCE
+ $(Q)$(MAKE) $(build)=libthermal_tools
+
+$(LIBTHERMAL_TOOLS_A): $(LIBTHERMAL_TOOLS_IN)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBTHERMAL_TOOLS_IN)
+
+$(LIBTHERMAL_TOOLS_SO): $(LIBTHERMAL_TOOLS_IN)
+ $(QUIET_LINK)$(CC) --shared -Wl,-soname,libthermal_tools.so $^ -o $@
+ @ln -sf $(@F) $(OUTPUT)libthermal_tools.so
+ @ln -sf $(@F) $(OUTPUT)libthermal_tools.so.$(LIBTHERMAL_TOOLS_VERSION)
+
+
+libs: $(LIBTHERMAL_TOOLS_A) $(LIBTHERMAL_TOOLS_SO) $(LIBTHERMAL_TOOLS_PC)
+
+all: fixdep
+ $(Q)$(MAKE) libs
+
+clean:
+ $(call QUIET_CLEAN, libthermal_tools) $(RM) $(LIBTHERMAL_TOOLS_A) \
+ *.o *~ *.a *.so *.so.$(VERSION) *.so.$(LIBTHERMAL_TOOLS_VERSION) .*.d .*.cmd LIBTHERMAL_TOOLS-CFLAGS $(LIBTHERMAL_TOOLS_PC)
+
+$(LIBTHERMAL_TOOLS_PC):
+ $(QUIET_GEN)sed -e "s|@PREFIX@|$(prefix)|" \
+ -e "s|@LIBDIR@|$(libdir_SQ)|" \
+ -e "s|@VERSION@|$(VERSION)|" \
+ < libthermal_tools.pc.template > $@
+
+define do_install_mkdir
+ if [ ! -d '$(DESTDIR_SQ)$1' ]; then \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$1'; \
+ fi
+endef
+
+define do_install
+ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
+ fi; \
+ $(INSTALL) $1 $(if $3,-m $3,) '$(DESTDIR_SQ)$2'
+endef
+
+install_lib: libs
+ $(call QUIET_INSTALL, $(LIBTHERMAL_TOOLS_ALL)) \
+ $(call do_install_mkdir,$(libdir_SQ)); \
+ cp -fpR $(LIBTHERMAL_TOOLS_ALL) $(DESTDIR)$(libdir_SQ)
+
+install_headers:
+ $(call QUIET_INSTALL, headers) \
+ $(call do_install,include/thermal.h,$(prefix)/include/thermal,644); \
+
+install_pkgconfig: $(LIBTHERMAL_TOOLS_PC)
+ $(call QUIET_INSTALL, $(LIBTHERMAL_TOOLS_PC)) \
+ $(call do_install,$(LIBTHERMAL_TOOLS_PC),$(libdir_SQ)/pkgconfig,644)
+
+install_doc:
+ $(Q)$(MAKE) -C Documentation install-man install-html install-examples
+
+#install: install_lib install_headers install_pkgconfig install_doc
+install: install_lib install_headers install_pkgconfig
+
+FORCE:
+
+.PHONY: all install clean FORCE
diff --git a/tools/thermal/lib/libthermal_tools.pc.template b/tools/thermal/lib/libthermal_tools.pc.template
new file mode 100644
index 000000000000..6f3769731b59
--- /dev/null
+++ b/tools/thermal/lib/libthermal_tools.pc.template
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+prefix=@PREFIX@
+libdir=@LIBDIR@
+includedir=${prefix}/include
+
+Name: libthermal
+Description: thermal library
+Requires: libnl-3.0 libnl-genl-3.0
+Version: @VERSION@
+Libs: -L${libdir} -lnl-genl-3 -lnl-3
+Cflags: -I${includedir} -I{include}/libnl3
diff --git a/tools/thermal/lib/log.c b/tools/thermal/lib/log.c
new file mode 100644
index 000000000000..620df5b3220f
--- /dev/null
+++ b/tools/thermal/lib/log.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: LGPL-2.1+
+// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include "log.h"
+
+static const char *__ident = "unknown";
+static int __options = 0;
+
+static const char *loglvl[] = {
+ [LOG_DEBUG] = "DEBUG",
+ [LOG_INFO] = "INFO",
+ [LOG_NOTICE] = "NOTICE",
+ [LOG_WARNING] = "WARN",
+ [LOG_ERR] = "ERROR",
+ [LOG_CRIT] = "CRITICAL",
+ [LOG_ALERT] = "ALERT",
+ [LOG_EMERG] = "EMERG",
+};
+
+int log_str2level(const char *lvl)
+{
+ int i;
+
+ for (i = 0; i < sizeof(loglvl) / sizeof(loglvl[LOG_DEBUG]); i++)
+ if (!strcmp(lvl, loglvl[i]))
+ return i;
+
+ return LOG_DEBUG;
+}
+
+extern void logit(int level, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+
+ if (__options & TO_SYSLOG)
+ vsyslog(level, format, args);
+
+ if (__options & TO_STDERR)
+ vfprintf(stderr, format, args);
+
+ if (__options & TO_STDOUT)
+ vfprintf(stdout, format, args);
+
+ va_end(args);
+}
+
+int log_init(int level, const char *ident, int options)
+{
+ if (!options)
+ return -1;
+
+ if (level > LOG_DEBUG)
+ return -1;
+
+ if (!ident)
+ return -1;
+
+ __ident = ident;
+ __options = options;
+
+ if (options & TO_SYSLOG) {
+ openlog(__ident, options | LOG_NDELAY, LOG_USER);
+ setlogmask(LOG_UPTO(level));
+ }
+
+ return 0;
+}
+
+void log_exit(void)
+{
+ closelog();
+}
diff --git a/tools/thermal/lib/log.h b/tools/thermal/lib/log.h
new file mode 100644
index 000000000000..9ee945e0af57
--- /dev/null
+++ b/tools/thermal/lib/log.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/* Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> */
+#ifndef __THERMAL_TOOLS_LOG_H
+#define __THERMAL_TOOLS_LOG_H
+
+#include <syslog.h>
+
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((__unused__))
+#endif
+
+#define TO_SYSLOG 0x1
+#define TO_STDOUT 0x2
+#define TO_STDERR 0x4
+
+extern void logit(int level, const char *format, ...);
+
+#define DEBUG(fmt, ...) logit(LOG_DEBUG, "%s(%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define INFO(fmt, ...) logit(LOG_INFO, fmt, ##__VA_ARGS__)
+#define NOTICE(fmt, ...) logit(LOG_NOTICE, fmt, ##__VA_ARGS__)
+#define WARN(fmt, ...) logit(LOG_WARNING, fmt, ##__VA_ARGS__)
+#define ERROR(fmt, ...) logit(LOG_ERR, fmt, ##__VA_ARGS__)
+#define CRITICAL(fmt, ...) logit(LOG_CRIT, fmt, ##__VA_ARGS__)
+#define ALERT(fmt, ...) logit(LOG_ALERT, fmt, ##__VA_ARGS__)
+#define EMERG(fmt, ...) logit(LOG_EMERG, fmt, ##__VA_ARGS__)
+
+int log_init(int level, const char *ident, int options);
+int log_str2level(const char *lvl);
+void log_exit(void);
+
+#endif
diff --git a/tools/thermal/lib/mainloop.c b/tools/thermal/lib/mainloop.c
new file mode 100644
index 000000000000..182a9533445b
--- /dev/null
+++ b/tools/thermal/lib/mainloop.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: LGPL-2.1+
+// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/epoll.h>
+#include "mainloop.h"
+#include "log.h"
+
+static int epfd = -1;
+static unsigned short nrhandler;
+static sig_atomic_t exit_mainloop = 0;
+
+struct mainloop_data {
+ mainloop_callback_t cb;
+ void *data;
+ int fd;
+};
+
+static struct mainloop_data **mds;
+
+#define MAX_EVENTS 10
+
+static void sig_exit(__maybe_unused int sig)
+{
+ exit_mainloop = 1;
+}
+
+int mainloop(unsigned int timeout)
+{
+ int i, nfds;
+ struct epoll_event events[MAX_EVENTS];
+ struct mainloop_data *md;
+
+ if (epfd < 0)
+ return -1;
+
+ for (;;) {
+
+ nfds = epoll_wait(epfd, events, MAX_EVENTS, timeout);
+
+ if (exit_mainloop)
+ return 0;
+
+ if (nfds < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+
+ /*
+ * A timeout occured. Let's send to ourself a SIGWINCH
+ * so the window get refreshed automatically. No need
+ * to use exported functions and this code stay self
+ * contained.
+ */
+ if (!nfds) {
+ kill(getpid(), SIGWINCH);
+ continue;
+ }
+
+ for (i = 0; i < nfds; i++) {
+ md = events[i].data.ptr;
+
+ if (md->cb(md->fd, md->data) > 0)
+ return 0;
+ }
+
+ }
+}
+
+int mainloop_add(int fd, mainloop_callback_t cb, void *data)
+{
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ };
+
+ struct mainloop_data *md;
+
+ if (fd >= nrhandler) {
+ mds = realloc(mds, sizeof(*mds) * (fd + 1));
+ if (!mds)
+ return -1;
+ nrhandler = fd + 1;
+ }
+
+ md = malloc(sizeof(*md));
+ if (!md)
+ return -1;
+
+ md->data = data;
+ md->cb = cb;
+ md->fd = fd;
+
+ mds[fd] = md;
+ ev.data.ptr = md;
+
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ free(md);
+ return -1;
+ }
+
+ return 0;
+}
+
+int mainloop_del(int fd)
+{
+ if (fd >= nrhandler)
+ return -1;
+
+ if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) < 0)
+ return -1;
+
+ free(mds[fd]);
+
+ return 0;
+}
+
+int mainloop_init(void)
+{
+ epfd = epoll_create(2);
+ if (epfd < 0)
+ return -1;
+
+ signal(SIGINT, sig_exit);
+ signal(SIGTERM, sig_exit);
+
+ return 0;
+}
+
+void mainloop_fini(void)
+{
+ close(epfd);
+}
diff --git a/tools/thermal/lib/mainloop.h b/tools/thermal/lib/mainloop.h
new file mode 100644
index 000000000000..eb37977a83e7
--- /dev/null
+++ b/tools/thermal/lib/mainloop.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/* Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> */
+#ifndef __THERMAL_TOOLS_MAINLOOP_H
+#define __THERMAL_TOOLS_MAINLOOP_H
+
+typedef int (*mainloop_callback_t)(int fd, void *data);
+
+extern int mainloop(unsigned int timeout);
+extern int mainloop_add(int fd, mainloop_callback_t cb, void *data);
+extern int mainloop_del(int fd);
+extern int mainloop_init(void);
+extern void mainloop_fini(void);
+
+#endif
diff --git a/tools/thermal/lib/thermal-tools.h b/tools/thermal/lib/thermal-tools.h
new file mode 100644
index 000000000000..f43939a468a3
--- /dev/null
+++ b/tools/thermal/lib/thermal-tools.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/* Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> */
+#ifndef __THERMAL_TOOLS
+#define __THERMAL_TOOLS
+
+#include "log.h"
+#include "mainloop.h"
+#include "uptimeofday.h"
+
+#endif
diff --git a/tools/thermal/lib/uptimeofday.c b/tools/thermal/lib/uptimeofday.c
new file mode 100644
index 000000000000..7ccbb272debd
--- /dev/null
+++ b/tools/thermal/lib/uptimeofday.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: LGPL-2.1+
+// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#include <stdio.h>
+#include <sys/time.h>
+#include <linux/sysinfo.h>
+#include "thermal-tools.h"
+
+static unsigned long __offset;
+static struct timeval __tv;
+
+int uptimeofday_init(void)
+{
+ struct sysinfo info;
+
+ if (sysinfo(&info))
+ return -1;
+
+ gettimeofday(&__tv, NULL);
+
+ __offset = __tv.tv_sec - info.uptime;
+
+ return 0;
+}
+
+unsigned long getuptimeofday_ms(void)
+{
+ gettimeofday(&__tv, NULL);
+
+ return ((__tv.tv_sec - __offset) * 1000) + (__tv.tv_usec / 1000);
+}
+
+struct timespec msec_to_timespec(int msec)
+{
+ struct timespec tv = {
+ .tv_sec = (time_t)(msec / 1000),
+ .tv_nsec = (msec - tv.tv_sec) * 1000000,
+ };
+
+ return tv;
+}
diff --git a/tools/thermal/lib/uptimeofday.h b/tools/thermal/lib/uptimeofday.h
new file mode 100644
index 000000000000..c0da5de41325
--- /dev/null
+++ b/tools/thermal/lib/uptimeofday.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/* Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> */
+#ifndef __THERMAL_TOOLS_UPTIMEOFDAY_H
+#define __THERMAL_TOOLS_UPTIMEOFDAY_H
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+
+int uptimeofday_init(void);
+unsigned long getuptimeofday_ms(void);
+struct timespec msec_to_timespec(int msec);
+
+#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [RFC PATCH 3/4] tools/thermal: A temperature capture tool
2022-02-14 21:04 [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library Daniel Lezcano
2022-02-14 21:04 ` [RFC PATCH 2/4] tools/thermal: Add util library Daniel Lezcano
@ 2022-02-14 21:04 ` Daniel Lezcano
2022-02-14 21:04 ` [RFC PATCH 4/4] tools/thermal: Add thermal daemon skeleton Daniel Lezcano
2022-02-16 16:40 ` [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library srinivas pandruvada
3 siblings, 0 replies; 10+ messages in thread
From: Daniel Lezcano @ 2022-02-14 21:04 UTC (permalink / raw)
To: daniel.lezcano, rafael
Cc: srinivas.pandruvada, linux-kernel, linux-pm, Amit Kucheria,
Zhang Rui, Jonathan Cameron, Sasha Levin, William Breathitt Gray
The 'thermometer' tool allows to capture the temperature of a set of
thermal zones defined in a configuration file at a specified rate.
It is designed to have the lowest possible overhead. It will write the
captured temperature per thermal zone per file so making easier to
write a gnuplot script.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
tools/Makefile | 16 +-
tools/thermal/thermometer/Build | 2 +
tools/thermal/thermometer/Makefile | 23 ++
tools/thermal/thermometer/thermometer.c | 384 +++++++++++++++++++++
tools/thermal/thermometer/thermometer.conf | 5 +
5 files changed, 427 insertions(+), 3 deletions(-)
create mode 100644 tools/thermal/thermometer/Build
create mode 100644 tools/thermal/thermometer/Makefile
create mode 100644 tools/thermal/thermometer/thermometer.c
create mode 100644 tools/thermal/thermometer/thermometer.conf
diff --git a/tools/Makefile b/tools/Makefile
index c253cbd27c06..78615f8cb463 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -31,6 +31,7 @@ help:
@echo ' bootconfig - boot config tool'
@echo ' spi - spi tools'
@echo ' tmon - thermal monitoring and tuning tool'
+ @echo ' thermometer - temperature capture tool'
@echo ' thermal - thermal library'
@echo ' tracing - misc tracing tools'
@echo ' turbostat - Intel CPU idle stats and freq reporting tool'
@@ -95,6 +96,9 @@ turbostat x86_energy_perf_policy intel-speed-select: FORCE
tmon: FORCE
$(call descend,thermal/$@)
+thermometer: FORCE
+ $(call descend,thermal/$@)
+
freefall: FORCE
$(call descend,laptop/$@)
@@ -105,7 +109,7 @@ all: acpi cgroup counter cpupower gpio hv firewire \
perf selftests bootconfig spi turbostat usb \
virtio vm bpf x86_energy_perf_policy \
tmon freefall iio objtool kvm_stat wmi \
- pci debugging tracing thermal
+ pci debugging tracing thermal thermometer
acpi_install:
$(call descend,power/$(@:_install=),install)
@@ -128,6 +132,9 @@ turbostat_install x86_energy_perf_policy_install intel-speed-select_install:
tmon_install:
$(call descend,thermal/$(@:_install=),install)
+thermometer_install:
+ $(call descend,thermal/$(@:_install=),install)
+
freefall_install:
$(call descend,laptop/$(@:_install=),install)
@@ -140,7 +147,7 @@ install: acpi_install cgroup_install counter_install cpupower_install gpio_insta
virtio_install vm_install bpf_install x86_energy_perf_policy_install \
tmon_install freefall_install objtool_install kvm_stat_install \
wmi_install pci_install debugging_install intel-speed-select_install \
- tracing_install
+ tracing_install thermometer_install
acpi_clean:
$(call descend,power/acpi,clean)
@@ -173,6 +180,9 @@ thermal_clean:
turbostat_clean x86_energy_perf_policy_clean intel-speed-select_clean:
$(call descend,power/x86/$(@:_clean=),clean)
+thermometer_clean:
+ $(call descend,thermal/thermometer,clean)
+
tmon_clean:
$(call descend,thermal/tmon,clean)
@@ -187,6 +197,6 @@ clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_cl
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean \
gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \
- intel-speed-select_clean tracing_clean thermal_clean
+ intel-speed-select_clean tracing_clean thermal_clean thermometer_clean
.PHONY: FORCE
diff --git a/tools/thermal/thermometer/Build b/tools/thermal/thermometer/Build
new file mode 100644
index 000000000000..2531dda4acdd
--- /dev/null
+++ b/tools/thermal/thermometer/Build
@@ -0,0 +1,2 @@
+thermometer-y += thermometer.o
+
diff --git a/tools/thermal/thermometer/Makefile b/tools/thermal/thermometer/Makefile
new file mode 100644
index 000000000000..7d08661b3472
--- /dev/null
+++ b/tools/thermal/thermometer/Makefile
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for cgroup tools
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+# $(info Determined 'srctree' to be $(srctree))
+endif
+
+CFLAGS = -Wall -Wextra
+CFLAGS += -I$(srctree)/tools/thermal/lib
+
+LDFLAGS = -L$(srctree)/tools/thermal/lib
+LDFLAGS += -lthermal_tools
+LDFLAGS += -lconfig
+
+all: thermometer
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+clean:
+ $(RM) thermometer
diff --git a/tools/thermal/thermometer/thermometer.c b/tools/thermal/thermometer/thermometer.c
new file mode 100644
index 000000000000..12306c715320
--- /dev/null
+++ b/tools/thermal/thermometer/thermometer.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <linux/thermal.h>
+
+#include <libconfig.h>
+
+#include "thermal_tools.h"
+
+#define CLASS_THERMAL "/sys/class/thermal"
+
+struct options {
+ int loglvl;
+ int logopt;
+ int overwrite;
+ const char *config;
+ char postfix[PATH_MAX];
+ char output[PATH_MAX];
+};
+
+struct tz_regex {
+ regex_t regex;
+ int polling;
+};
+
+struct configuration {
+ struct tz_regex *tz_regex;
+ int nr_tz_regex;
+
+};
+
+struct tz {
+ FILE *file_out;
+ int fd_temp;
+ int fd_timer;
+ int polling;
+ const char *name;
+};
+
+struct thermometer {
+ struct tz *tz;
+ int nr_tz;
+};
+
+static struct tz_regex *configuration_tz_match(const char *expr,
+ struct configuration *config)
+{
+ int i;
+
+ for (i = 0; i < config->nr_tz_regex; i++) {
+
+ if (!regexec(&config->tz_regex[i].regex, expr, 0, NULL, 0))
+ return &config->tz_regex[i];
+ }
+
+ return NULL;
+}
+
+static int configuration_init(const char *path, struct configuration *config)
+{
+ config_t cfg;
+
+ config_setting_t *tz;
+ int i, length;
+
+ config_init(&cfg);
+
+ if (!config_read_file(&cfg, path)) {
+ ERROR("Failed to parse %s:%d - %s\n", config_error_file(&cfg),
+ config_error_line(&cfg), config_error_text(&cfg));
+
+ return -1;
+ }
+
+ tz = config_lookup(&cfg, "thermal-zones");
+ if (!tz) {
+ ERROR("No thermal zone configured to be monitored\n");
+ return -1;
+ }
+
+ length = config_setting_length(tz);
+
+ INFO("Found %d thermal zone(s) regular expression\n", length);
+
+ for (i = 0; i < length; i++) {
+
+ config_setting_t *node;
+ const char *name;
+ int polling;
+
+ node = config_setting_get_elem(tz, i);
+ if (!node) {
+ ERROR("Missing node name '%d'\n", i);
+ return -1;
+ };
+
+ if (!config_setting_lookup_string(node, "name", &name)) {
+ ERROR("Thermal zone name not found\n");
+ return -1;
+ }
+
+ if (!config_setting_lookup_int(node, "polling", &polling)) {
+ ERROR("Polling value not found");
+ return -1;
+ }
+
+ config->tz_regex = realloc(config->tz_regex, sizeof(*config->tz_regex) *
+ (config->nr_tz_regex + 1));
+
+ if (regcomp(&config->tz_regex[config->nr_tz_regex].regex, name,
+ REG_NOSUB | REG_EXTENDED)) {
+ ERROR("Invalid regular expression '%s'\n", name);
+ continue;
+ }
+
+ config->tz_regex[config->nr_tz_regex].polling = polling;
+ config->nr_tz_regex++;
+
+ INFO("Thermal zone regular expression '%s' with polling %d\n",
+ name, polling);
+ }
+
+ return 0;
+}
+
+static int options_init(int argc, char *argv[], struct options *options)
+{
+ int opt;
+ time_t now = time(NULL);
+
+ strftime(options->postfix, sizeof(options->postfix),
+ "-%Y-%m-%d_%H:%M:%S", gmtime(&now));
+
+ while ((opt = getopt(argc, argv, "o:c:l:p:eswg")) != -1) {
+ switch (opt) {
+ case 'c':
+ options->config = optarg;
+ break;
+ case 'l':
+ options->loglvl = log_str2level(optarg);
+ break;
+ case 'p':
+ strcpy(options->postfix, optarg);
+ break;
+ case 'o':
+ strcpy(options->output, optarg);
+ break;
+ case 'e':
+ options->logopt |= TO_STDERR;
+ break;
+ case 's':
+ options->logopt |= TO_STDOUT;
+ break;
+ case 'g':
+ options->logopt |= TO_SYSLOG;
+ break;
+ case 'w':
+ options->overwrite = 1;
+ break;
+ default: /* '?' */
+ ERROR("Usage: %s \n", argv[0]);
+ return -1;
+ }
+ }
+
+ printf("Options;\n");
+ printf(" * config: '%s'\n", options->config);
+ printf(" * log level: '%d'\n", options->loglvl);
+ printf(" * postfix: %s\n", options->postfix);
+ printf(" * output: %s\n", options->output);
+
+ return 0;
+}
+
+static int thermometer_add_tz(const char *path, const char *name, int polling,
+ struct thermometer *thermometer)
+{
+ int fd;
+ char tz_path[PATH_MAX];
+
+ sprintf(tz_path, CLASS_THERMAL"/%s/temp", path);
+
+ fd = open(tz_path, O_RDONLY);
+ if (fd < 0) {
+ ERROR("Failed to open '%s': %m\n", tz_path);
+ return -1;
+ }
+
+ thermometer->tz = realloc(thermometer->tz,
+ sizeof(*thermometer->tz) * (thermometer->nr_tz + 1));
+ if (!thermometer->tz) {
+ ERROR("Failed to allocate thermometer->tz\n");
+ return -1;
+ }
+
+ thermometer->tz[thermometer->nr_tz].fd_temp = fd;
+ thermometer->tz[thermometer->nr_tz].name = strdup(name);
+ thermometer->tz[thermometer->nr_tz].polling = polling;
+ thermometer->nr_tz++;
+
+ INFO("Added thermal zone '%s->%s (polling:%d)'\n", path, name, polling);
+
+ return 0;
+}
+
+static int thermometer_init(struct configuration *config,
+ struct thermometer *thermometer)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ struct tz_regex *tz_regex;
+ const char *tz_dirname = "thermal_zone";
+
+ if (mainloop_init()) {
+ ERROR("Failed to start mainloop\n");
+ return -1;
+ }
+
+ dir = opendir(CLASS_THERMAL);
+ if (!dir) {
+ ERROR("failed to open '%s'\n", CLASS_THERMAL);
+ return -1;
+ }
+
+ while ((dirent = readdir(dir))) {
+ char tz_type[THERMAL_NAME_LENGTH];
+ char tz_path[PATH_MAX];
+ FILE *tz_file;
+
+ if (strncmp(dirent->d_name, tz_dirname, strlen(tz_dirname)))
+ continue;
+
+ sprintf(tz_path, CLASS_THERMAL"/%s/type", dirent->d_name);
+
+ tz_file = fopen(tz_path, "r");
+ if (!tz_file) {
+ ERROR("Failed to open '%s': %m", tz_path);
+ continue;
+ }
+
+ fscanf(tz_file, "%s", tz_type);
+
+ fclose(tz_file);
+
+ tz_regex = configuration_tz_match(tz_type, config);
+ if (!tz_regex)
+ continue;
+
+ if (thermometer_add_tz(dirent->d_name, tz_type,
+ tz_regex->polling, thermometer))
+ continue;
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+
+static int timer_callback(int fd, void *arg)
+{
+ struct tz *tz = arg;
+ char buf[16] = { 0 };
+
+ pread(tz->fd_temp, buf, sizeof(buf), 0);
+
+ fprintf(tz->file_out, "%ld %s", getuptimeofday_ms(), buf);
+
+ read(fd, buf, sizeof(buf));
+
+ return 0;
+}
+
+static int thermometer_start(struct thermometer *thermometer,
+ struct options *options)
+{
+ struct itimerspec timer_it = { 0 };
+ char *path;
+ FILE *f;
+ int i;
+
+ for (i = 0; i < thermometer->nr_tz; i++) {
+
+ asprintf(&path, "%s/%s%s", options->output,
+ thermometer->tz[i].name, options->postfix);
+
+ if (!options->overwrite && !access(path, F_OK)) {
+ ERROR("'%s' already exists\n", path);
+ return -1;
+ }
+
+ f = fopen(path, "w");
+ if (!f) {
+ ERROR("Failed to create '%s':%m\n", path);
+ return -1;
+ }
+
+ fprintf(f, "timestamp(ms) %s(°mC)\n", thermometer->tz[i].name);
+
+ thermometer->tz[i].file_out = f;
+
+ /*
+ * Create polling timer
+ */
+ thermometer->tz[i].fd_timer = timerfd_create(CLOCK_MONOTONIC, 0);
+ if (thermometer->tz[i].fd_timer < 0) {
+ ERROR("Failed to create timer for '%s': %m\n",
+ thermometer->tz[i].name);
+ return -1;
+ }
+
+ timer_it.it_interval = timer_it.it_value =
+ msec_to_timespec(thermometer->tz[i].polling);
+
+ if (timerfd_settime(thermometer->tz[i].fd_timer, 0,
+ &timer_it, NULL) < 0)
+ return -1;
+
+ if (mainloop_add(thermometer->tz[i].fd_timer, timer_callback,
+ &thermometer->tz[i]))
+ return -1;
+ }
+
+ return mainloop(-1);
+}
+
+static int thermometer_stop(struct thermometer *thermometer)
+{
+ int i;
+
+ INFO("Closing/flushing output files\n");
+
+ for (i = 0; i < thermometer->nr_tz; i++) {
+ fclose(thermometer->tz[i].file_out);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct options options = {
+ .config = "thermometer.conf",
+ .loglvl = LOG_DEBUG,
+ .output = ".",
+ };
+
+ struct configuration config = { 0 };
+
+ struct thermometer thermometer = { 0 };
+
+ if (options_init(argc, argv, &options))
+ return -1;
+
+ if (log_init(options.loglvl, argv[0], options.logopt))
+ return -1;
+
+ if (configuration_init(options.config, &config))
+ return -1;
+
+ if (uptimeofday_init())
+ return -1;
+
+ if (thermometer_init(&config, &thermometer))
+ return -1;
+
+ if (thermometer_start(&thermometer, &options))
+ return -1;
+
+ if (thermometer_stop(&thermometer))
+ return -1;
+
+ return 0;
+}
diff --git a/tools/thermal/thermometer/thermometer.conf b/tools/thermal/thermometer/thermometer.conf
new file mode 100644
index 000000000000..350907accb3e
--- /dev/null
+++ b/tools/thermal/thermometer/thermometer.conf
@@ -0,0 +1,5 @@
+
+thermal-zones = (
+ { name = "cpu[0-7]-thermal";
+ polling = 100; }
+ )
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [RFC PATCH 4/4] tools/thermal: Add thermal daemon skeleton
2022-02-14 21:04 [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library Daniel Lezcano
2022-02-14 21:04 ` [RFC PATCH 2/4] tools/thermal: Add util library Daniel Lezcano
2022-02-14 21:04 ` [RFC PATCH 3/4] tools/thermal: A temperature capture tool Daniel Lezcano
@ 2022-02-14 21:04 ` Daniel Lezcano
2022-02-14 22:34 ` srinivas pandruvada
2022-02-16 16:40 ` [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library srinivas pandruvada
3 siblings, 1 reply; 10+ messages in thread
From: Daniel Lezcano @ 2022-02-14 21:04 UTC (permalink / raw)
To: daniel.lezcano, rafael
Cc: srinivas.pandruvada, linux-kernel, linux-pm, Amit Kucheria,
Zhang Rui, Sasha Levin, Jonathan Cameron, William Breathitt Gray
This change provides a simple daemon skeleton. It provides an example
of how to use the thermal library.
The goal of this skeleton is to give a base brick for anyone
interested in writing its own thermal engine.
In the future, it will evolve with more features.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
tools/Makefile | 16 +-
tools/thermal/thermald/Build | 2 +
tools/thermal/thermald/Makefile | 27 +++
tools/thermal/thermald/thermald.c | 275 ++++++++++++++++++++++++++++++
4 files changed, 317 insertions(+), 3 deletions(-)
create mode 100644 tools/thermal/thermald/Build
create mode 100644 tools/thermal/thermald/Makefile
create mode 100644 tools/thermal/thermald/thermald.c
diff --git a/tools/Makefile b/tools/Makefile
index 78615f8cb463..a8c4a65ac953 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -32,6 +32,7 @@ help:
@echo ' spi - spi tools'
@echo ' tmon - thermal monitoring and tuning tool'
@echo ' thermometer - temperature capture tool'
+ @echo ' thermald - thermal monitoring tool'
@echo ' thermal - thermal library'
@echo ' tracing - misc tracing tools'
@echo ' turbostat - Intel CPU idle stats and freq reporting tool'
@@ -99,6 +100,9 @@ tmon: FORCE
thermometer: FORCE
$(call descend,thermal/$@)
+thermald: FORCE thermal
+ $(call descend,thermal/$@)
+
freefall: FORCE
$(call descend,laptop/$@)
@@ -109,7 +113,7 @@ all: acpi cgroup counter cpupower gpio hv firewire \
perf selftests bootconfig spi turbostat usb \
virtio vm bpf x86_energy_perf_policy \
tmon freefall iio objtool kvm_stat wmi \
- pci debugging tracing thermal thermometer
+ pci debugging tracing thermal thermometer thermald
acpi_install:
$(call descend,power/$(@:_install=),install)
@@ -135,6 +139,9 @@ tmon_install:
thermometer_install:
$(call descend,thermal/$(@:_install=),install)
+thermald_install:
+ $(call descend,thermal/$(@:_install=),install)
+
freefall_install:
$(call descend,laptop/$(@:_install=),install)
@@ -147,7 +154,7 @@ install: acpi_install cgroup_install counter_install cpupower_install gpio_insta
virtio_install vm_install bpf_install x86_energy_perf_policy_install \
tmon_install freefall_install objtool_install kvm_stat_install \
wmi_install pci_install debugging_install intel-speed-select_install \
- tracing_install thermometer_install
+ tracing_install thermometer_install thermald_install
acpi_clean:
$(call descend,power/acpi,clean)
@@ -183,6 +190,9 @@ turbostat_clean x86_energy_perf_policy_clean intel-speed-select_clean:
thermometer_clean:
$(call descend,thermal/thermometer,clean)
+thermald_clean:
+ $(call descend,thermal/thermald,clean)
+
tmon_clean:
$(call descend,thermal/tmon,clean)
@@ -197,6 +207,6 @@ clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_cl
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean \
gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \
- intel-speed-select_clean tracing_clean thermal_clean thermometer_clean
+ intel-speed-select_clean tracing_clean thermal_clean thermometer_clean thermald_clean
.PHONY: FORCE
diff --git a/tools/thermal/thermald/Build b/tools/thermal/thermald/Build
new file mode 100644
index 000000000000..23a8ff56d290
--- /dev/null
+++ b/tools/thermal/thermald/Build
@@ -0,0 +1,2 @@
+thermald-y += thermald.o
+
diff --git a/tools/thermal/thermald/Makefile b/tools/thermal/thermald/Makefile
new file mode 100644
index 000000000000..c74e87c2b233
--- /dev/null
+++ b/tools/thermal/thermald/Makefile
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for cgroup tools
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+# $(info Determined 'srctree' to be $(srctree))
+endif
+
+CFLAGS = -Wall -Wextra
+CFLAGS += -I$(srctree)/tools/thermal/lib
+CFLAGS += -I$(srctree)/tools/lib/thermal/include
+
+LDFLAGS = -L$(srctree)/tools/thermal/lib
+LDFLAGS += -L$(srctree)/tools/lib/thermal
+LDFLAGS += -lthermal_tools
+LDFLAGS += -lthermal
+LDFLAGS += -lconfig
+LDFLAGS += -lnl-genl-3 -lnl-3
+
+all: thermald
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+clean:
+ $(RM) thermald
diff --git a/tools/thermal/thermald/thermald.c b/tools/thermal/thermald/thermald.c
new file mode 100644
index 000000000000..92ced613c06e
--- /dev/null
+++ b/tools/thermal/thermald/thermald.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Thermal monitoring tool based on the thermal netlink events.
+ *
+ * Copyright (C) 2022 Linaro Ltd.
+ *
+ * Author: Daniel Lezcano <daniel.lezcano@kernel.org>
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <syslog.h>
+
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <thermal.h>
+#include "thermal-tools.h"
+
+struct options {
+ int loglevel;
+ int logopt;
+ int interactive;
+};
+
+struct thermal_data {
+ struct thermal_zone *tz;
+ struct thermal_handler *th;
+};
+
+static int show_trip(struct thermal_trip *tt, __maybe_unused void *arg)
+{
+ INFO("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, __maybe_unused void *arg)
+{
+ thermal_cmd_get_temp(arg, tz);
+
+ INFO("temperature: %d\n", tz->temp);
+
+ return 0;
+}
+
+static int show_governor(struct thermal_zone *tz, __maybe_unused void *arg)
+{
+ thermal_cmd_get_governor(arg, tz);
+
+ INFO("governor: '%s'\n", tz->governor);
+
+ return 0;
+}
+
+static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg)
+{
+ INFO("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, __maybe_unused void *arg)
+{
+ INFO("Thermal zone '%s'/%d created\n", name, tz_id);
+
+ return 0;
+}
+
+static int tz_delete(int tz_id, __maybe_unused void *arg)
+{
+ INFO("Thermal zone %d deleted\n", tz_id);
+
+ return 0;
+}
+
+static int tz_disable(int tz_id, void *arg)
+{
+ struct thermal_data *td = arg;
+ struct thermal_zone *tz = thermal_zone_find_by_id(td->tz, tz_id);
+
+ INFO("Thermal zone %d ('%s') disabled\n", tz_id, tz->name);
+
+ return 0;
+}
+
+static int tz_enable(int tz_id, void *arg)
+{
+ struct thermal_data *td = arg;
+ struct thermal_zone *tz = thermal_zone_find_by_id(td->tz, tz_id);
+
+ INFO("Thermal zone %d ('%s') enabled\n", tz_id, tz->name);
+
+ return 0;
+}
+
+static int trip_high(int tz_id, int trip_id, int temp, void *arg)
+{
+ struct thermal_data *td = arg;
+ struct thermal_zone *tz = thermal_zone_find_by_id(td->tz, tz_id);
+
+ INFO("Thermal zone %d ('%s'): trip point %d crossed way up with %d °C\n",
+ tz_id, tz->name, trip_id, temp);
+
+ return 0;
+}
+
+static int trip_low(int tz_id, int trip_id, int temp, void *arg)
+{
+ struct thermal_data *td = arg;
+ struct thermal_zone *tz = thermal_zone_find_by_id(td->tz, tz_id);
+
+ INFO("Thermal zone %d ('%s'): trip point %d crossed way down with %d °C\n",
+ tz_id, tz->name, trip_id, temp);
+
+ return 0;
+}
+
+static int trip_add(int tz_id, int trip_id, int type, int temp, int hyst, __maybe_unused void *arg)
+{
+ INFO("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, __maybe_unused void *arg)
+{
+ INFO("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, __maybe_unused void *arg)
+{
+ struct thermal_data *td = arg;
+ struct thermal_zone *tz = thermal_zone_find_by_id(td->tz, tz_id);
+
+ INFO("Trip point changed %d: id=%d, type=%d, temp=%d, hyst=%d\n",
+ tz_id, trip_id, type, temp, hyst);
+
+ tz->trip[trip_id].type = type;
+ tz->trip[trip_id].temp = temp;
+ tz->trip[trip_id].hyst = hyst;
+
+ return 0;
+}
+
+static int cdev_add(const char *name, int cdev_id, int max_state, __maybe_unused void *arg)
+{
+ INFO("Cooling device '%s'/%d (max state=%d) added\n", name, cdev_id, max_state);
+
+ return 0;
+}
+
+static int cdev_delete(int cdev_id, __maybe_unused void *arg)
+{
+ INFO("Cooling device %d deleted", cdev_id);
+
+ return 0;
+}
+
+static int cdev_update(int cdev_id, int cur_state, __maybe_unused void *arg)
+{
+ INFO("cdev:%d state:%d\n", cdev_id, cur_state);
+
+ return 0;
+}
+
+static int gov_change(int tz_id, const char *name, __maybe_unused void *arg)
+{
+ struct thermal_data *td = arg;
+ struct thermal_zone *tz = thermal_zone_find_by_id(td->tz, tz_id);
+
+ INFO("%s: governor changed %s -> %s\n", tz->name, tz->governor, name);
+
+ strcpy(tz->governor, name);
+
+ 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
+};
+
+static int thermal_event(__maybe_unused int fd, __maybe_unused void *arg)
+{
+ struct thermal_data *td = arg;
+
+ return thermal_events_handle(td->th, td);
+}
+
+static int options_init(int argc, char *argv[], struct options *options)
+{
+ int opt;
+
+ while ((opt = getopt(argc, argv, "sl:")) != -1) {
+ switch (opt) {
+ case 'l':
+ options->loglevel = log_str2level(optarg);
+ break;
+ case 's':
+ options->logopt |= TO_STDOUT;
+ break;
+ default: /* '?' */
+ ERROR("Usage: %s \n", argv[0]);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct thermal_data td;
+ struct options options = {
+ .loglevel = LOG_DEBUG,
+ .logopt = LOG_SYSLOG
+ };
+
+ if (options_init(argc, argv, &options))
+ return 1;
+
+ if (!(options.logopt & TO_STDOUT) && daemon(0, 0))
+ return 1;
+
+ if (log_init(options.loglevel, basename(argv[0]), options.logopt))
+ return 1;
+
+ td.th = thermal_init(&ops);
+ if (!td.th)
+ return 1;
+
+ td.tz = thermal_zone_discover(td.th);
+ if (!td.tz)
+ return 1;
+
+ for_each_thermal_zone(td.tz, show_tz, td.th);
+
+ if (mainloop_init())
+ return 1;
+
+ if (mainloop_add(thermal_events_fd(td.th), thermal_event, &td))
+ return 1;
+
+ return mainloop(-1);
+}
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 4/4] tools/thermal: Add thermal daemon skeleton
2022-02-14 21:04 ` [RFC PATCH 4/4] tools/thermal: Add thermal daemon skeleton Daniel Lezcano
@ 2022-02-14 22:34 ` srinivas pandruvada
2022-02-14 23:43 ` Daniel Lezcano
0 siblings, 1 reply; 10+ messages in thread
From: srinivas pandruvada @ 2022-02-14 22:34 UTC (permalink / raw)
To: Daniel Lezcano, rafael
Cc: linux-kernel, linux-pm, Amit Kucheria, Zhang Rui, Sasha Levin,
Jonathan Cameron, William Breathitt Gray
On Mon, 2022-02-14 at 22:04 +0100, Daniel Lezcano wrote:
> This change provides a simple daemon skeleton. It provides an example
> of how to use the thermal library.
>
> The goal of this skeleton is to give a base brick for anyone
> interested in writing its own thermal engine.
>
> In the future, it will evolve with more features.
>
From the code, doesn't seam to have a function to daemonize.
So may be not call a daemon if that is true. mainloop() is just an
infinite loop polling netlink messages.
Also thermald will confuse with the standard thermald distributed with
every distro. May be thermal_control_sample as this a sample for future
development.
Thanks,
Srinivas
> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> ---
> tools/Makefile | 16 +-
> tools/thermal/thermald/Build | 2 +
> tools/thermal/thermald/Makefile | 27 +++
> tools/thermal/thermald/thermald.c | 275
> ++++++++++++++++++++++++++++++
> 4 files changed, 317 insertions(+), 3 deletions(-)
> create mode 100644 tools/thermal/thermald/Build
> create mode 100644 tools/thermal/thermald/Makefile
> create mode 100644 tools/thermal/thermald/thermald.c
>
> diff --git a/tools/Makefile b/tools/Makefile
> index 78615f8cb463..a8c4a65ac953 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -32,6 +32,7 @@ help:
> @echo ' spi - spi tools'
> @echo ' tmon - thermal monitoring and
> tuning tool'
> @echo ' thermometer - temperature capture tool'
> + @echo ' thermald - thermal monitoring tool'
> @echo ' thermal - thermal library'
> @echo ' tracing - misc tracing tools'
> @echo ' turbostat - Intel CPU idle stats and
> freq reporting tool'
> @@ -99,6 +100,9 @@ tmon: FORCE
> thermometer: FORCE
> $(call descend,thermal/$@)
>
> +thermald: FORCE thermal
> + $(call descend,thermal/$@)
> +
> freefall: FORCE
> $(call descend,laptop/$@)
>
> @@ -109,7 +113,7 @@ all: acpi cgroup counter cpupower gpio hv
> firewire \
> perf selftests bootconfig spi turbostat usb \
> virtio vm bpf x86_energy_perf_policy \
> tmon freefall iio objtool kvm_stat wmi \
> - pci debugging tracing thermal thermometer
> + pci debugging tracing thermal thermometer thermald
>
> acpi_install:
> $(call descend,power/$(@:_install=),install)
> @@ -135,6 +139,9 @@ tmon_install:
> thermometer_install:
> $(call descend,thermal/$(@:_install=),install)
>
> +thermald_install:
> + $(call descend,thermal/$(@:_install=),install)
> +
> freefall_install:
> $(call descend,laptop/$(@:_install=),install)
>
> @@ -147,7 +154,7 @@ install: acpi_install cgroup_install
> counter_install cpupower_install gpio_insta
> virtio_install vm_install bpf_install
> x86_energy_perf_policy_install \
> tmon_install freefall_install objtool_install
> kvm_stat_install \
> wmi_install pci_install debugging_install intel-
> speed-select_install \
> - tracing_install thermometer_install
> + tracing_install thermometer_install thermald_install
>
> acpi_clean:
> $(call descend,power/acpi,clean)
> @@ -183,6 +190,9 @@ turbostat_clean x86_energy_perf_policy_clean
> intel-speed-select_clean:
> thermometer_clean:
> $(call descend,thermal/thermometer,clean)
>
> +thermald_clean:
> + $(call descend,thermal/thermald,clean)
> +
> tmon_clean:
> $(call descend,thermal/tmon,clean)
>
> @@ -197,6 +207,6 @@ clean: acpi_clean cgroup_clean counter_clean
> cpupower_clean hv_clean firewire_cl
> vm_clean bpf_clean iio_clean
> x86_energy_perf_policy_clean tmon_clean \
> freefall_clean build_clean libbpf_clean
> libsubcmd_clean \
> gpio_clean objtool_clean leds_clean wmi_clean
> pci_clean firmware_clean debugging_clean \
> - intel-speed-select_clean tracing_clean thermal_clean
> thermometer_clean
> + intel-speed-select_clean tracing_clean thermal_clean
> thermometer_clean thermald_clean
>
> .PHONY: FORCE
> diff --git a/tools/thermal/thermald/Build
> b/tools/thermal/thermald/Build
> new file mode 100644
> index 000000000000..23a8ff56d290
> --- /dev/null
> +++ b/tools/thermal/thermald/Build
> @@ -0,0 +1,2 @@
> +thermald-y += thermald.o
> +
> diff --git a/tools/thermal/thermald/Makefile
> b/tools/thermal/thermald/Makefile
> new file mode 100644
> index 000000000000..c74e87c2b233
> --- /dev/null
> +++ b/tools/thermal/thermald/Makefile
> @@ -0,0 +1,27 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Makefile for cgroup tools
> +
> +ifeq ($(srctree),)
> +srctree := $(patsubst %/,%,$(dir $(CURDIR)))
> +srctree := $(patsubst %/,%,$(dir $(srctree)))
> +srctree := $(patsubst %/,%,$(dir $(srctree)))
> +# $(info Determined 'srctree' to be $(srctree))
> +endif
> +
> +CFLAGS = -Wall -Wextra
> +CFLAGS += -I$(srctree)/tools/thermal/lib
> +CFLAGS += -I$(srctree)/tools/lib/thermal/include
> +
> +LDFLAGS = -L$(srctree)/tools/thermal/lib
> +LDFLAGS += -L$(srctree)/tools/lib/thermal
> +LDFLAGS += -lthermal_tools
> +LDFLAGS += -lthermal
> +LDFLAGS += -lconfig
> +LDFLAGS += -lnl-genl-3 -lnl-3
> +
> +all: thermald
> +%: %.c
> + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
> +
> +clean:
> + $(RM) thermald
> diff --git a/tools/thermal/thermald/thermald.c
> b/tools/thermal/thermald/thermald.c
> new file mode 100644
> index 000000000000..92ced613c06e
> --- /dev/null
> +++ b/tools/thermal/thermald/thermald.c
> @@ -0,0 +1,275 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Thermal monitoring tool based on the thermal netlink events.
> + *
> + * Copyright (C) 2022 Linaro Ltd.
> + *
> + * Author: Daniel Lezcano <daniel.lezcano@kernel.org>
> + */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <libgen.h>
> +#include <limits.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <signal.h>
> +#include <unistd.h>
> +
> +#include <syslog.h>
> +
> +#include <sys/epoll.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +
> +#include <thermal.h>
> +#include "thermal-tools.h"
> +
> +struct options {
> + int loglevel;
> + int logopt;
> + int interactive;
> +};
> +
> +struct thermal_data {
> + struct thermal_zone *tz;
> + struct thermal_handler *th;
> +};
> +
> +static int show_trip(struct thermal_trip *tt, __maybe_unused void
> *arg)
> +{
> + INFO("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, __maybe_unused void
> *arg)
> +{
> + thermal_cmd_get_temp(arg, tz);
> +
> + INFO("temperature: %d\n", tz->temp);
> +
> + return 0;
> +}
> +
> +static int show_governor(struct thermal_zone *tz, __maybe_unused
> void *arg)
> +{
> + thermal_cmd_get_governor(arg, tz);
> +
> + INFO("governor: '%s'\n", tz->governor);
> +
> + return 0;
> +}
> +
> +static int show_tz(struct thermal_zone *tz, __maybe_unused void
> *arg)
> +{
> + INFO("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, __maybe_unused
> void *arg)
> +{
> + INFO("Thermal zone '%s'/%d created\n", name, tz_id);
> +
> + return 0;
> +}
> +
> +static int tz_delete(int tz_id, __maybe_unused void *arg)
> +{
> + INFO("Thermal zone %d deleted\n", tz_id);
> +
> + return 0;
> +}
> +
> +static int tz_disable(int tz_id, void *arg)
> +{
> + struct thermal_data *td = arg;
> + struct thermal_zone *tz = thermal_zone_find_by_id(td->tz,
> tz_id);
> +
> + INFO("Thermal zone %d ('%s') disabled\n", tz_id, tz->name);
> +
> + return 0;
> +}
> +
> +static int tz_enable(int tz_id, void *arg)
> +{
> + struct thermal_data *td = arg;
> + struct thermal_zone *tz = thermal_zone_find_by_id(td->tz,
> tz_id);
> +
> + INFO("Thermal zone %d ('%s') enabled\n", tz_id, tz->name);
> +
> + return 0;
> +}
> +
> +static int trip_high(int tz_id, int trip_id, int temp, void *arg)
> +{
> + struct thermal_data *td = arg;
> + struct thermal_zone *tz = thermal_zone_find_by_id(td->tz,
> tz_id);
> +
> + INFO("Thermal zone %d ('%s'): trip point %d crossed way up
> with %d °C\n",
> + tz_id, tz->name, trip_id, temp);
> +
> + return 0;
> +}
> +
> +static int trip_low(int tz_id, int trip_id, int temp, void *arg)
> +{
> + struct thermal_data *td = arg;
> + struct thermal_zone *tz = thermal_zone_find_by_id(td->tz,
> tz_id);
> +
> + INFO("Thermal zone %d ('%s'): trip point %d crossed way down
> with %d °C\n",
> + tz_id, tz->name, trip_id, temp);
> +
> + return 0;
> +}
> +
> +static int trip_add(int tz_id, int trip_id, int type, int temp, int
> hyst, __maybe_unused void *arg)
> +{
> + INFO("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, __maybe_unused void
> *arg)
> +{
> + INFO("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, __maybe_unused void *arg)
> +{
> + struct thermal_data *td = arg;
> + struct thermal_zone *tz = thermal_zone_find_by_id(td->tz,
> tz_id);
> +
> + INFO("Trip point changed %d: id=%d, type=%d, temp=%d,
> hyst=%d\n",
> + tz_id, trip_id, type, temp, hyst);
> +
> + tz->trip[trip_id].type = type;
> + tz->trip[trip_id].temp = temp;
> + tz->trip[trip_id].hyst = hyst;
> +
> + return 0;
> +}
> +
> +static int cdev_add(const char *name, int cdev_id, int max_state,
> __maybe_unused void *arg)
> +{
> + INFO("Cooling device '%s'/%d (max state=%d) added\n", name,
> cdev_id, max_state);
> +
> + return 0;
> +}
> +
> +static int cdev_delete(int cdev_id, __maybe_unused void *arg)
> +{
> + INFO("Cooling device %d deleted", cdev_id);
> +
> + return 0;
> +}
> +
> +static int cdev_update(int cdev_id, int cur_state, __maybe_unused
> void *arg)
> +{
> + INFO("cdev:%d state:%d\n", cdev_id, cur_state);
> +
> + return 0;
> +}
> +
> +static int gov_change(int tz_id, const char *name, __maybe_unused
> void *arg)
> +{
> + struct thermal_data *td = arg;
> + struct thermal_zone *tz = thermal_zone_find_by_id(td->tz,
> tz_id);
> +
> + INFO("%s: governor changed %s -> %s\n", tz->name, tz-
> >governor, name);
> +
> + strcpy(tz->governor, name);
> +
> + 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
> +};
> +
> +static int thermal_event(__maybe_unused int fd, __maybe_unused void
> *arg)
> +{
> + struct thermal_data *td = arg;
> +
> + return thermal_events_handle(td->th, td);
> +}
> +
> +static int options_init(int argc, char *argv[], struct options
> *options)
> +{
> + int opt;
> +
> + while ((opt = getopt(argc, argv, "sl:")) != -1) {
> + switch (opt) {
> + case 'l':
> + options->loglevel = log_str2level(optarg);
> + break;
> + case 's':
> + options->logopt |= TO_STDOUT;
> + break;
> + default: /* '?' */
> + ERROR("Usage: %s \n", argv[0]);
> + return -1;
> + }
> + }
> +
> + return 0;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + struct thermal_data td;
> + struct options options = {
> + .loglevel = LOG_DEBUG,
> + .logopt = LOG_SYSLOG
> + };
> +
> + if (options_init(argc, argv, &options))
> + return 1;
> +
> + if (!(options.logopt & TO_STDOUT) && daemon(0, 0))
> + return 1;
> +
> + if (log_init(options.loglevel, basename(argv[0]),
> options.logopt))
> + return 1;
> +
> + td.th = thermal_init(&ops);
> + if (!td.th)
> + return 1;
> +
> + td.tz = thermal_zone_discover(td.th);
> + if (!td.tz)
> + return 1;
> +
> + for_each_thermal_zone(td.tz, show_tz, td.th);
> +
> + if (mainloop_init())
> + return 1;
> +
> + if (mainloop_add(thermal_events_fd(td.th), thermal_event,
> &td))
> + return 1;
> +
> + return mainloop(-1);
> +}
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 4/4] tools/thermal: Add thermal daemon skeleton
2022-02-14 22:34 ` srinivas pandruvada
@ 2022-02-14 23:43 ` Daniel Lezcano
2022-02-15 0:15 ` srinivas pandruvada
0 siblings, 1 reply; 10+ messages in thread
From: Daniel Lezcano @ 2022-02-14 23:43 UTC (permalink / raw)
To: srinivas pandruvada, rafael
Cc: linux-kernel, linux-pm, Amit Kucheria, Zhang Rui, Sasha Levin,
Jonathan Cameron, William Breathitt Gray
Hi Srinivas,
On 14/02/2022 23:34, srinivas pandruvada wrote:
> On Mon, 2022-02-14 at 22:04 +0100, Daniel Lezcano wrote:
>> This change provides a simple daemon skeleton. It provides an example
>> of how to use the thermal library.
>>
>> The goal of this skeleton is to give a base brick for anyone
>> interested in writing its own thermal engine.
>>
>> In the future, it will evolve with more features.
>>
> From the code, doesn't seam to have a function to daemonize.
Yes, it does, see below. If the stdout output is set, then it won't
daemonize.
> So may be not call a daemon if that is true. mainloop() is just an
> infinite loop polling netlink messages.
>
> Also thermald will confuse with the standard thermald distributed with
> every distro. May be thermal_control_sample as this a sample for future
> development.
Does thermal-engine fine ?
>> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
[ ... ]
>> tools/thermal/thermald/thermald.c | 275
[ ... ]
>> + if (!(options.logopt & TO_STDOUT) && daemon(0, 0))
>> + return 1;
[ ... ]
--
<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] 10+ messages in thread
* Re: [RFC PATCH 4/4] tools/thermal: Add thermal daemon skeleton
2022-02-14 23:43 ` Daniel Lezcano
@ 2022-02-15 0:15 ` srinivas pandruvada
0 siblings, 0 replies; 10+ messages in thread
From: srinivas pandruvada @ 2022-02-15 0:15 UTC (permalink / raw)
To: Daniel Lezcano, rafael
Cc: linux-kernel, linux-pm, Amit Kucheria, Zhang Rui, Sasha Levin,
Jonathan Cameron, William Breathitt Gray
On Tue, 2022-02-15 at 00:43 +0100, Daniel Lezcano wrote:
>
> Hi Srinivas,
>
> On 14/02/2022 23:34, srinivas pandruvada wrote:
> > On Mon, 2022-02-14 at 22:04 +0100, Daniel Lezcano wrote:
> > > This change provides a simple daemon skeleton. It provides an
> > > example
> > > of how to use the thermal library.
> > >
> > > The goal of this skeleton is to give a base brick for anyone
> > > interested in writing its own thermal engine.
> > >
> > > In the future, it will evolve with more features.
> > >
> > From the code, doesn't seam to have a function to daemonize.
>
> Yes, it does, see below. If the stdout output is set, then it won't
> daemonize.
OK.
>
>
> > So may be not call a daemon if that is true. mainloop() is just an
> > infinite loop polling netlink messages.
> >
> > Also thermald will confuse with the standard thermald distributed
> > with
> > every distro. May be thermal_control_sample as this a sample for
> > future
> > development.
>
> Does thermal-engine fine ?
fine.
Thanks,
Srinivas
>
> > > Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
>
> [ ... ]
>
> > > tools/thermal/thermald/thermald.c | 275
>
> [ ... ]
>
> > > + if (!(options.logopt & TO_STDOUT) && daemon(0, 0))
> > > + return 1;
>
> [ ... ]
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library
2022-02-14 21:04 [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library Daniel Lezcano
` (2 preceding siblings ...)
2022-02-14 21:04 ` [RFC PATCH 4/4] tools/thermal: Add thermal daemon skeleton Daniel Lezcano
@ 2022-02-16 16:40 ` srinivas pandruvada
2022-02-16 19:38 ` Daniel Lezcano
3 siblings, 1 reply; 10+ messages in thread
From: srinivas pandruvada @ 2022-02-16 16:40 UTC (permalink / raw)
To: Daniel Lezcano, rafael
Cc: linux-kernel, linux-pm, Sasha Levin, William Breathitt Gray,
Jonathan Cameron
On Mon, 2022-02-14 at 22:04 +0100, Daniel Lezcano 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.
>
Some minor comments.
May be better use some defines instead of returning "-1" for error
conditions.
>
[...]
> +struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone
> *tz,
> + const char *name)
> +{
> + int i;
> +
> + if (!name)
> + return NULL;
> +
> + for (i = 0; tz[i].id != -1; i++) {
> + if (!strcmp(tz[i].name, name))
strncmp
> + return &tz[i];
> + }
> +
> + return NULL;
> +}
> +
>
[...]
> diff --git a/tools/lib/thermal/thermal_nl.h
> b/tools/lib/thermal/thermal_nl.h
> new file mode 100644
> index 000000000000..54dbbad58349
> --- /dev/null
> +++ b/tools/lib/thermal/thermal_nl.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: LGPL-2.1+ */
> +/* Copyright (C) 2022, Linaro Ltd - Daniel Lezcano
> <daniel.lezcano@linaro.org> */
> +#ifndef __THERMAL_H
> +#define __THERMAL_H
__THERMAL_NL_H
>
[...]
> +/*
> + * Low level netlink
> + */
> +extern int nl_subscribe_thermal(struct nl_sock *nl_sock, struct
> nl_cb *nl_cb,
> + const char *group);
> +
To complete API, don't we need nl_unsubscribe which calls
nl_socket_drop_memberships()?
Thanks,
Srinivas
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library
2022-02-16 16:40 ` [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library srinivas pandruvada
@ 2022-02-16 19:38 ` Daniel Lezcano
2022-02-16 19:52 ` srinivas pandruvada
0 siblings, 1 reply; 10+ messages in thread
From: Daniel Lezcano @ 2022-02-16 19:38 UTC (permalink / raw)
To: srinivas pandruvada, rafael
Cc: linux-kernel, linux-pm, Sasha Levin, William Breathitt Gray,
Jonathan Cameron
Hi Srinivas,
thanks for the review
On 16/02/2022 17:40, srinivas pandruvada wrote:
> On Mon, 2022-02-14 at 22:04 +0100, Daniel Lezcano 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.
>>
> Some minor comments.
> May be better use some defines instead of returning "-1" for error
> conditions.
Do you suggest like an enum with different errors or just having -1
replaced by eg. THERMAL_ERROR ?
[ ... ]
>> + * Low level netlink
>> + */
>> +extern int nl_subscribe_thermal(struct nl_sock *nl_sock, struct
>> nl_cb *nl_cb,
>> + const char *group);
>> +
> To complete API, don't we need nl_unsubscribe which calls
> nl_socket_drop_memberships()?
Ah, yes. I'll add it.
Thanks
--
<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] 10+ messages in thread
* Re: [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library
2022-02-16 19:38 ` Daniel Lezcano
@ 2022-02-16 19:52 ` srinivas pandruvada
0 siblings, 0 replies; 10+ messages in thread
From: srinivas pandruvada @ 2022-02-16 19:52 UTC (permalink / raw)
To: Daniel Lezcano, rafael
Cc: linux-kernel, linux-pm, Sasha Levin, William Breathitt Gray,
Jonathan Cameron
Hi Daniel,
On Wed, 2022-02-16 at 20:38 +0100, Daniel Lezcano wrote:
>
> Hi Srinivas,
>
> thanks for the review
>
> On 16/02/2022 17:40, srinivas pandruvada wrote:
> > On Mon, 2022-02-14 at 22:04 +0100, Daniel Lezcano 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.
> > >
> > Some minor comments.
> > May be better use some defines instead of returning "-1" for error
> > conditions.
>
> Do you suggest like an enum with different errors or just having -1
> replaced by eg. THERMAL_ERROR ?
This will be fine.
Thanks,
Srinivas
>
>
> [ ... ]
>
> > > + * Low level netlink
> > > + */
> > > +extern int nl_subscribe_thermal(struct nl_sock *nl_sock, struct
> > > nl_cb *nl_cb,
> > > + const char *group);
> > > +
> > To complete API, don't we need nl_unsubscribe which calls
> > nl_socket_drop_memberships()?
> Ah, yes. I'll add it.
>
> Thanks
>
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2022-02-16 19:52 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-14 21:04 [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library Daniel Lezcano
2022-02-14 21:04 ` [RFC PATCH 2/4] tools/thermal: Add util library Daniel Lezcano
2022-02-14 21:04 ` [RFC PATCH 3/4] tools/thermal: A temperature capture tool Daniel Lezcano
2022-02-14 21:04 ` [RFC PATCH 4/4] tools/thermal: Add thermal daemon skeleton Daniel Lezcano
2022-02-14 22:34 ` srinivas pandruvada
2022-02-14 23:43 ` Daniel Lezcano
2022-02-15 0:15 ` srinivas pandruvada
2022-02-16 16:40 ` [RFC PATCH 1/4] tools/lib/thermal: Add a thermal library srinivas pandruvada
2022-02-16 19:38 ` Daniel Lezcano
2022-02-16 19:52 ` srinivas pandruvada
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).