diff --git a/thermal.c b/thermal.c index 16a6f39..64a9cdf 100644 --- a/thermal.c +++ b/thermal.c @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -14,8 +15,62 @@ cpumask_t thermal_banned_cpus; -#define INVALID_NL_FD -1 -#define MAX_RECV_ERRS 2 +/* Events of thermal_genl_family */ +enum thermal_genl_event { + THERMAL_GENL_EVENT_UNSPEC, + THERMAL_GENL_EVENT_TZ_CREATE, /* Thermal zone creation */ + THERMAL_GENL_EVENT_TZ_DELETE, /* Thermal zone deletion */ + THERMAL_GENL_EVENT_TZ_DISABLE, /* Thermal zone disabled */ + THERMAL_GENL_EVENT_TZ_ENABLE, /* Thermal zone enabled */ + THERMAL_GENL_EVENT_TZ_TRIP_UP, /* Trip point crossed the way up */ + THERMAL_GENL_EVENT_TZ_TRIP_DOWN, /* Trip point crossed the way down */ + THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, /* Trip point changed */ + THERMAL_GENL_EVENT_TZ_TRIP_ADD, /* Trip point added */ + THERMAL_GENL_EVENT_TZ_TRIP_DELETE, /* Trip point deleted */ + THERMAL_GENL_EVENT_CDEV_ADD, /* Cdev bound to the thermal zone */ + THERMAL_GENL_EVENT_CDEV_DELETE, /* Cdev unbound */ + THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */ + THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */ + THERMAL_GENL_EVENT_CAPACITY_CHANGE, /* CPU capacity changed */ + __THERMAL_GENL_EVENT_MAX, +}; +#define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1) + +/* Attributes of thermal_genl_family */ +enum thermal_genl_attr { + THERMAL_GENL_ATTR_UNSPEC, + THERMAL_GENL_ATTR_TZ, + THERMAL_GENL_ATTR_TZ_ID, + THERMAL_GENL_ATTR_TZ_TEMP, + THERMAL_GENL_ATTR_TZ_TRIP, + THERMAL_GENL_ATTR_TZ_TRIP_ID, + THERMAL_GENL_ATTR_TZ_TRIP_TYPE, + THERMAL_GENL_ATTR_TZ_TRIP_TEMP, + THERMAL_GENL_ATTR_TZ_TRIP_HYST, + THERMAL_GENL_ATTR_TZ_MODE, + THERMAL_GENL_ATTR_TZ_NAME, + THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT, + THERMAL_GENL_ATTR_TZ_GOV, + THERMAL_GENL_ATTR_TZ_GOV_NAME, + THERMAL_GENL_ATTR_CDEV, + THERMAL_GENL_ATTR_CDEV_ID, + THERMAL_GENL_ATTR_CDEV_CUR_STATE, + THERMAL_GENL_ATTR_CDEV_MAX_STATE, + THERMAL_GENL_ATTR_CDEV_NAME, + THERMAL_GENL_ATTR_GOV_NAME, + THERMAL_GENL_ATTR_CAPACITY, + THERMAL_GENL_ATTR_CAPACITY_CPU_COUNT, + THERMAL_GENL_ATTR_CAPACITY_CPU_ID, + THERMAL_GENL_ATTR_CAPACITY_CPU_PERF, + THERMAL_GENL_ATTR_CAPACITY_CPU_EFF, + __THERMAL_GENL_ATTR_MAX, +}; +#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) + +#define INVALID_NL_FD -1 +#define MAX_RECV_ERRS 2 +#define SYSCONF_ERR -1 +#define INVALID_EVENT_VALUE -1 #define THERMAL_GENL_FAMILY_NAME "thermal" #define THERMAL_GENL_EVENT_GROUP_NAME "event" @@ -254,17 +309,115 @@ err_out: return error; } -static int handle_thermal_event(struct nl_msg *msg __attribute__((unused)), - void *arg __attribute__((unused))) +enum { + INDEX_CPUNUM, + INDEX_PERF, + INDEX_EFFI, + INDEX_MAX +}; + +/* + * Single netlink notification is not guaranteed to fully deliver all of + * the CPU updates per thermal event, due to the implementation choice in + * the kernel code. So, this function is intended to manage a CPU list in a + * stream of relevant notifications. + */ +static void update_banned_cpus(int cur_cpuidx, gboolean need_to_ban) { - log(TO_ALL, LOG_ERR, "thermal: not yet implemented to process thermal event.\n"); - return NL_SKIP; + static cpumask_t banmask = { 0 }, itrmask = { 0 }; + long max_cpunum = sysconf(_SC_NPROCESSORS_ONLN); + + if (need_to_ban) + cpu_set(cur_cpuidx, banmask); + + cpu_set(cur_cpuidx, itrmask); + if (cpus_weight(itrmask) < max_cpunum) + return; + + if (cpus_equal(thermal_banned_cpus, banmask)) + goto out; + + cpus_copy(thermal_banned_cpus, banmask); + need_rescan = 1; +out: + cpus_clear(banmask); + cpus_clear(itrmask); +} + +static int handle_thermal_event(struct nl_msg *msg, void *arg __attribute__((unused))) +{ + int event_data[INDEX_MAX] = { INVALID_EVENT_VALUE }; + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; + struct genlmsghdr *genlhdr; + struct nlmsghdr *msnlh; + struct nlattr *cap; + int i, remain, rc; + void *pos; + + /* get actual netlink message header */ + msnlh = nlmsg_hdr(msg); + + /* get a pointer to generic netlink header */ + genlhdr = genlmsg_hdr(msnlh); + if (genlhdr->cmd != THERMAL_GENL_EVENT_CAPACITY_CHANGE) { + log(TO_ALL, LOG_DEBUG, "thermal: no CPU capacity change.\n"); + return NL_SKIP; + } + + /* parse generic netlink message including attributes */ + rc = genlmsg_parse( + msnlh, /* a pointer to netlink message header */ + 0, /* length of user header */ + attrs, /* array to store parsed attributes */ + THERMAL_GENL_ATTR_MAX, /* maximum attribute id as expected */ + NULL); /* validation policy */ + if (rc) { + log(TO_ALL, LOG_ERR, "thermal: failed to parse message for thermal event.\n"); + return NL_SKIP; + } + + /* get start and length of payload section */ + cap = attrs[THERMAL_GENL_ATTR_CAPACITY]; + pos = nla_data(cap); + remain = nla_len(cap); + + for (i = 0; nla_ok(pos, remain); pos = nla_next(pos, &remain), i++) { + gboolean valid_event = TRUE, need_to_ban; + unsigned int value = nla_get_u32(pos); + int idx = i % INDEX_MAX; + int cur_cpuidx; + + event_data[idx] = value; + + if (idx != INDEX_EFFI) + continue; + + cur_cpuidx = event_data[INDEX_CPUNUM]; + valid_event = !!(cur_cpuidx <= NR_CPUS); + if (!valid_event) { + log(TO_ALL, LOG_WARNING, "thermal: invalid event - CPU %d\n", + cur_cpuidx); + continue; + } + + /* + * A CPU with no performance and no efficiency cannot + * handle IRQs: + */ + need_to_ban = !!(!event_data[INDEX_PERF] && !event_data[INDEX_EFFI]); + update_banned_cpus(cur_cpuidx, need_to_ban); + + log(TO_ALL, LOG_DEBUG, "thermal: event - CPU %d, efficiency %d, perf %d.\n", + cur_cpuidx, event_data[INDEX_PERF], event_data[INDEX_EFFI]); + } + + return NL_OK; } static int handler_for_debug(struct nl_msg *msg __attribute__((unused)), void *arg __attribute__((unused))) { - return NL_SKIP; + return NL_OK; } /* @@ -274,6 +427,11 @@ static gboolean register_netlink_handler(void) { int rc; + if (sysconf(_SC_NPROCESSORS_ONLN) == SYSCONF_ERR) { + log(TO_ALL, LOG_ERR, "thermal: _SC_NPROCESSORS_ONLN not available.\n"); + return TRUE; + } + rc = nl_cb_set(callback, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, handler_for_debug, NULL); if (rc) { log(TO_ALL, LOG_ERR, "thermal: debug handler registration failed.\n");