Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools/lib/thermal: Add the threshold netlink ABI

The thermal framework supports the thresholds and allows the userspace
to create, delete, flush, get the list of the thresholds as well as
getting the list of the thresholds set for a specific thermal zone.

Add the netlink abstraction in the thermal library to take full
advantage of thresholds for the userspace program.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
Link: https://patch.msgid.link/20241022155147.463475-5-daniel.lezcano@linaro.org
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Daniel Lezcano and committed by
Rafael J. Wysocki
a2626724 24b216b2

+242 -15
+137 -1
tools/lib/thermal/commands.c
··· 5 5 #include <stdio.h> 6 6 #include <stdlib.h> 7 7 #include <unistd.h> 8 + #include <limits.h> 8 9 9 10 #include <thermal.h> 10 11 #include "thermal_nl.h" ··· 34 33 [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 }, 35 34 [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 }, 36 35 [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING }, 36 + 37 + /* Thresholds */ 38 + [THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED }, 39 + [THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 }, 40 + [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 }, 37 41 }; 38 42 39 43 static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz) ··· 188 182 return THERMAL_SUCCESS; 189 183 } 190 184 185 + static int parse_threshold_get(struct genl_info *info, struct thermal_zone *tz) 186 + { 187 + struct nlattr *attr; 188 + struct thermal_threshold *__tt = NULL; 189 + size_t size = 0; 190 + int rem; 191 + 192 + /* 193 + * The size contains the size of the array and we want to 194 + * access the last element, size - 1. 195 + * 196 + * The variable size is initialized to zero but it will be 197 + * then incremented by the first if() statement. The message 198 + * attributes are ordered, so the first if() statement will be 199 + * always called before the second one. If it happens that is 200 + * not the case, then it is a kernel bug. 201 + */ 202 + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_THRESHOLD], rem) { 203 + 204 + if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_TEMP) { 205 + 206 + size++; 207 + 208 + __tt = realloc(__tt, sizeof(*__tt) * (size + 2)); 209 + if (!__tt) 210 + return THERMAL_ERROR; 211 + 212 + __tt[size - 1].temperature = nla_get_u32(attr); 213 + } 214 + 215 + if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_DIRECTION) 216 + __tt[size - 1].direction = nla_get_u32(attr); 217 + } 218 + 219 + if (__tt) 220 + __tt[size].temperature = INT_MAX; 221 + 222 + tz->thresholds = __tt; 223 + 224 + return THERMAL_SUCCESS; 225 + } 226 + 191 227 static int handle_netlink(struct nl_cache_ops *unused, 192 228 struct genl_cmd *cmd, 193 229 struct genl_info *info, void *arg) ··· 256 208 257 209 case THERMAL_GENL_CMD_TZ_GET_GOV: 258 210 ret = parse_tz_get_gov(info, arg); 211 + break; 212 + 213 + case THERMAL_GENL_CMD_THRESHOLD_GET: 214 + ret = parse_threshold_get(info, arg); 259 215 break; 260 216 261 217 default: ··· 305 253 .c_maxattr = THERMAL_GENL_ATTR_MAX, 306 254 .c_attr_policy = thermal_genl_policy, 307 255 }, 256 + { 257 + .c_id = THERMAL_GENL_CMD_THRESHOLD_GET, 258 + .c_name = (char *)"Get thresholds list", 259 + .c_msg_parser = handle_netlink, 260 + .c_maxattr = THERMAL_GENL_ATTR_MAX, 261 + .c_attr_policy = thermal_genl_policy, 262 + }, 263 + { 264 + .c_id = THERMAL_GENL_CMD_THRESHOLD_ADD, 265 + .c_name = (char *)"Add a threshold", 266 + .c_msg_parser = handle_netlink, 267 + .c_maxattr = THERMAL_GENL_ATTR_MAX, 268 + .c_attr_policy = thermal_genl_policy, 269 + }, 270 + { 271 + .c_id = THERMAL_GENL_CMD_THRESHOLD_DELETE, 272 + .c_name = (char *)"Delete a threshold", 273 + .c_msg_parser = handle_netlink, 274 + .c_maxattr = THERMAL_GENL_ATTR_MAX, 275 + .c_attr_policy = thermal_genl_policy, 276 + }, 277 + { 278 + .c_id = THERMAL_GENL_CMD_THRESHOLD_FLUSH, 279 + .c_name = (char *)"Flush the thresholds", 280 + .c_msg_parser = handle_netlink, 281 + .c_maxattr = THERMAL_GENL_ATTR_MAX, 282 + .c_attr_policy = thermal_genl_policy, 283 + }, 308 284 }; 309 285 310 286 static struct genl_ops thermal_cmd_ops = { ··· 343 263 344 264 struct cmd_param { 345 265 int tz_id; 266 + int temp; 267 + int direction; 346 268 }; 347 269 348 270 typedef int (*cmd_cb_t)(struct nl_msg *, struct cmd_param *); 349 271 350 272 static int thermal_genl_tz_id_encode(struct nl_msg *msg, struct cmd_param *p) 351 273 { 352 - if (p->tz_id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) 274 + if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) 275 + return -1; 276 + 277 + return 0; 278 + } 279 + 280 + static int thermal_genl_threshold_encode(struct nl_msg *msg, struct cmd_param *p) 281 + { 282 + if (thermal_genl_tz_id_encode(msg, p)) 283 + return -1; 284 + 285 + if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp)) 286 + return -1; 287 + 288 + if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction)) 353 289 return -1; 354 290 355 291 return 0; ··· 432 336 433 337 return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, 434 338 THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); 339 + } 340 + 341 + thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th, 342 + struct thermal_zone *tz) 343 + { 344 + struct cmd_param p = { .tz_id = tz->id }; 345 + 346 + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, 347 + THERMAL_GENL_CMD_THRESHOLD_GET, 0, tz); 348 + } 349 + 350 + thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th, 351 + struct thermal_zone *tz, 352 + int temperature, 353 + int direction) 354 + { 355 + struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; 356 + 357 + return thermal_genl_auto(th, thermal_genl_threshold_encode, &p, 358 + THERMAL_GENL_CMD_THRESHOLD_ADD, 0, tz); 359 + } 360 + 361 + thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th, 362 + struct thermal_zone *tz, 363 + int temperature, 364 + int direction) 365 + { 366 + struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; 367 + 368 + return thermal_genl_auto(th, thermal_genl_threshold_encode, &p, 369 + THERMAL_GENL_CMD_THRESHOLD_DELETE, 0, tz); 370 + } 371 + 372 + thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th, 373 + struct thermal_zone *tz) 374 + { 375 + struct cmd_param p = { .tz_id = tz->id }; 376 + 377 + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, 378 + THERMAL_GENL_CMD_THRESHOLD_FLUSH, 0, tz); 435 379 } 436 380 437 381 thermal_error_t thermal_cmd_exit(struct thermal_handler *th)
+42 -13
tools/lib/thermal/events.c
··· 94 94 case THERMAL_GENL_EVENT_TZ_GOV_CHANGE: 95 95 return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 96 96 nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg); 97 + 98 + case THERMAL_GENL_EVENT_THRESHOLD_ADD: 99 + return ops->threshold_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 100 + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]), 101 + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg); 102 + 103 + case THERMAL_GENL_EVENT_THRESHOLD_DELETE: 104 + return ops->threshold_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 105 + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]), 106 + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg); 107 + 108 + case THERMAL_GENL_EVENT_THRESHOLD_FLUSH: 109 + return ops->threshold_flush(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); 110 + 111 + case THERMAL_GENL_EVENT_THRESHOLD_UP: 112 + return ops->threshold_up(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 113 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), 114 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg); 115 + 116 + case THERMAL_GENL_EVENT_THRESHOLD_DOWN: 117 + return ops->threshold_down(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 118 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), 119 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg); 120 + 97 121 default: 98 122 return -1; 99 123 } ··· 125 101 126 102 static void thermal_events_ops_init(struct thermal_events_ops *ops) 127 103 { 128 - enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; 129 - enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; 130 - enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; 131 - enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; 132 - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high; 133 - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low; 134 - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; 135 - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; 136 - enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; 137 - enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add; 138 - enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete; 139 - enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update; 140 - enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; 104 + enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; 105 + enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; 106 + enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; 107 + enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; 108 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high; 109 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low; 110 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; 111 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; 112 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; 113 + enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add; 114 + enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete; 115 + enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update; 116 + enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; 117 + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_ADD] = !!ops->threshold_add; 118 + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DELETE] = !!ops->threshold_delete; 119 + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = !!ops->threshold_flush; 120 + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_UP] = !!ops->threshold_up; 121 + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DOWN] = !!ops->threshold_down; 141 122 } 142 123 143 124 thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg)
+40
tools/lib/thermal/include/thermal.h
··· 4 4 #define __LIBTHERMAL_H 5 5 6 6 #include <linux/thermal.h> 7 + #include <sys/types.h> 7 8 8 9 #ifndef LIBTHERMAL_API 9 10 #define LIBTHERMAL_API __attribute__((visibility("default"))) 11 + #endif 12 + 13 + #ifndef THERMAL_THRESHOLD_WAY_UP 14 + #define THERMAL_THRESHOLD_WAY_UP 0x1 15 + #endif 16 + 17 + #ifndef THERMAL_THRESHOLD_WAY_DOWN 18 + #define THERMAL_THRESHOLD_WAY_DOWN 0x2 10 19 #endif 11 20 12 21 #ifdef __cplusplus ··· 40 31 int (*cdev_delete)(int cdev_id, void *arg); 41 32 int (*cdev_update)(int cdev_id, int cur_state, void *arg); 42 33 int (*gov_change)(int tz_id, const char *gov_name, void *arg); 34 + int (*threshold_add)(int tz_id, int temperature, int direction, void *arg); 35 + int (*threshold_delete)(int tz_id, int temperature, int direction, void *arg); 36 + int (*threshold_flush)(int tz_id, void *arg); 37 + int (*threshold_up)(int tz_id, int temp, int prev_temp, void *arg); 38 + int (*threshold_down)(int tz_id, int temp, int prev_temp, void *arg); 43 39 }; 44 40 45 41 struct thermal_ops { ··· 59 45 int hyst; 60 46 }; 61 47 48 + struct thermal_threshold { 49 + int temperature; 50 + int direction; 51 + }; 52 + 62 53 struct thermal_zone { 63 54 int id; 64 55 int temp; 65 56 char name[THERMAL_NAME_LENGTH]; 66 57 char governor[THERMAL_NAME_LENGTH]; 67 58 struct thermal_trip *trip; 59 + struct thermal_threshold *thresholds; 68 60 }; 69 61 70 62 struct thermal_cdev { ··· 94 74 95 75 typedef int (*cb_tc_t)(struct thermal_cdev *, void *); 96 76 77 + typedef int (*cb_th_t)(struct thermal_threshold *, void *); 78 + 97 79 LIBTHERMAL_API int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg); 98 80 99 81 LIBTHERMAL_API int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg); 100 82 101 83 LIBTHERMAL_API int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg); 84 + 85 + LIBTHERMAL_API int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg); 102 86 103 87 LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz, 104 88 const char *name); ··· 147 123 148 124 LIBTHERMAL_API thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, 149 125 struct thermal_zone *tz); 126 + 127 + LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th, 128 + struct thermal_zone *tz); 129 + 130 + LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th, 131 + struct thermal_zone *tz, 132 + int temperature, 133 + int direction); 134 + 135 + LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th, 136 + struct thermal_zone *tz, 137 + int temperature, 138 + int direction); 139 + 140 + LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th, 141 + struct thermal_zone *tz); 150 142 151 143 /* 152 144 * Netlink thermal samples
+5
tools/lib/thermal/libthermal.map
··· 4 4 for_each_thermal_zone; 5 5 for_each_thermal_trip; 6 6 for_each_thermal_cdev; 7 + for_each_thermal_threshold; 7 8 thermal_zone_find_by_name; 8 9 thermal_zone_find_by_id; 9 10 thermal_zone_discover; ··· 18 17 thermal_cmd_get_trip; 19 18 thermal_cmd_get_governor; 20 19 thermal_cmd_get_temp; 20 + thermal_cmd_threshold_get; 21 + thermal_cmd_threshold_add; 22 + thermal_cmd_threshold_delete; 23 + thermal_cmd_threshold_flush; 21 24 thermal_sampling_init; 22 25 thermal_sampling_handle; 23 26 thermal_sampling_fd;
+17
tools/lib/thermal/thermal.c
··· 1 1 // SPDX-License-Identifier: LGPL-2.1+ 2 2 // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> 3 3 #include <stdio.h> 4 + #include <limits.h> 4 5 #include <thermal.h> 5 6 6 7 #include "thermal_nl.h" 8 + 9 + int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg) 10 + { 11 + int i, ret = 0; 12 + 13 + if (!th) 14 + return 0; 15 + 16 + for (i = 0; th[i].temperature != INT_MAX; i++) 17 + ret |= cb(&th[i], arg); 18 + 19 + return ret; 20 + } 7 21 8 22 int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg) 9 23 { ··· 92 78 static int __thermal_zone_discover(struct thermal_zone *tz, void *th) 93 79 { 94 80 if (thermal_cmd_get_trip(th, tz) < 0) 81 + return -1; 82 + 83 + if (thermal_cmd_threshold_get(th, tz)) 95 84 return -1; 96 85 97 86 if (thermal_cmd_get_governor(th, tz))
+1 -1
tools/thermal/lib/Makefile
··· 3 3 4 4 LIBTHERMAL_TOOLS_VERSION = 0 5 5 LIBTHERMAL_TOOLS_PATCHLEVEL = 0 6 - LIBTHERMAL_TOOLS_EXTRAVERSION = 1 6 + LIBTHERMAL_TOOLS_EXTRAVERSION = 2 7 7 8 8 MAKEFLAGS += --no-print-directory 9 9