Pull intel-speed-select utility changes for 5.18 from Srinivas Pandruvada.

* 'intel-sst-thermal' of https://github.com/spandruvada/linux-kernel:
  tools/power/x86/intel-speed-select: v1.12 release
  tools/power/x86/intel-speed-select: HFI support
  tools/power/x86/intel-speed-select: OOB daemon mode
This commit is contained in:
Rafael J. Wysocki 2022-02-17 20:20:19 +01:00
commit 2045d38a65
6 changed files with 617 additions and 15 deletions

View File

@ -1 +1 @@
intel-speed-select-y += isst-config.o isst-core.o isst-display.o
intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o

View File

@ -13,8 +13,8 @@ endif
# Do not use make's built-in rules
# (this improves performance and avoids hard-to-debug behaviour);
MAKEFLAGS += -r
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include -I/usr/include/libnl3
override LDFLAGS += -lnl-genl-3 -lnl-3
ALL_TARGETS := intel-speed-select
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
@ -31,7 +31,11 @@ $(OUTPUT)include/linux/isst_if.h: ../../../../include/uapi/linux/isst_if.h
mkdir -p $(OUTPUT)include/linux 2>&1 || true
ln -sf $(CURDIR)/../../../../include/uapi/linux/isst_if.h $@
prepare: $(OUTPUT)include/linux/isst_if.h
$(OUTPUT)include/linux/thermal.h: ../../../../include/uapi/linux/thermal.h
mkdir -p $(OUTPUT)include/linux 2>&1 || true
ln -sf $(CURDIR)/../../../../include/uapi/linux/thermal.h $@
prepare: $(OUTPUT)include/linux/isst_if.h $(OUTPUT)include/linux/thermal.h
ISST_IN := $(OUTPUT)intel-speed-select-in.o

View File

@ -0,0 +1,309 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select -- Read HFI events for OOB
* Copyright (c) 2022 Intel Corporation.
*/
/*
* This file incorporates work covered by the following copyright and
* permission notice:
* WPA Supplicant - driver interaction with Linux nl80211/cfg80211
* Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of
* BSD license.
*
* Requires
* libnl-genl-3-dev
*
* For Fedora/CenOS
* dnf install libnl3-devel
* For Ubuntu
* apt install libnl-3-dev libnl-genl-3-dev
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <linux/thermal.h>
#include "isst.h"
struct hfi_event_data {
struct nl_sock *nl_handle;
struct nl_cb *nl_cb;
};
struct hfi_event_data drv;
static int ack_handler(struct nl_msg *msg, void *arg)
{
int *err = arg;
*err = 0;
return NL_STOP;
}
static int finish_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
*ret = 0;
return NL_SKIP;
}
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
int *ret = arg;
*ret = err->error;
return NL_SKIP;
}
static int seq_check_handler(struct nl_msg *msg, void *arg)
{
return NL_OK;
}
static int send_and_recv_msgs(struct hfi_event_data *drv,
struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
void *valid_data)
{
struct nl_cb *cb;
int err = -ENOMEM;
cb = nl_cb_clone(drv->nl_cb);
if (!cb)
goto out;
err = nl_send_auto_complete(drv->nl_handle, msg);
if (err < 0)
goto out;
err = 1;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
if (valid_handler)
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
valid_handler, valid_data);
while (err > 0)
nl_recvmsgs(drv->nl_handle, cb);
out:
nl_cb_put(cb);
nlmsg_free(msg);
return err;
}
struct family_data {
const char *group;
int id;
};
static int family_handler(struct nl_msg *msg, void *arg)
{
struct family_data *res = arg;
struct nlattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *mcgrp;
int i;
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[CTRL_ATTR_MCAST_GROUPS])
return NL_SKIP;
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
nla_len(mcgrp), NULL);
if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
!tb2[CTRL_ATTR_MCAST_GRP_ID] ||
strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
res->group,
nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
continue;
res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
break;
};
return 0;
}
static int nl_get_multicast_id(struct hfi_event_data *drv,
const char *family, const char *group)
{
struct nl_msg *msg;
int ret = -1;
struct family_data res = { group, -ENOENT };
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"),
0, 0, CTRL_CMD_GETFAMILY, 0);
NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
ret = send_and_recv_msgs(drv, msg, family_handler, &res);
msg = NULL;
if (ret == 0)
ret = res.id;
nla_put_failure:
nlmsg_free(msg);
return ret;
}
struct perf_cap {
int cpu;
int perf;
int eff;
};
static void process_hfi_event(struct perf_cap *perf_cap)
{
process_level_change(perf_cap->cpu);
}
static int handle_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];
int ret;
struct perf_cap perf_cap;
ret = genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
debug_printf("Received event %d parse_rer:%d\n", genlhdr->cmd, ret);
if (genlhdr->cmd == THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE) {
struct nlattr *cap;
int j, index = 0;
debug_printf("THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE\n");
nla_for_each_nested(cap, attrs[THERMAL_GENL_ATTR_CPU_CAPABILITY], j) {
switch (index) {
case 0:
perf_cap.cpu = nla_get_u32(cap);
break;
case 1:
perf_cap.perf = nla_get_u32(cap);
break;
case 2:
perf_cap.eff = nla_get_u32(cap);
break;
default:
break;
}
++index;
if (index == 3) {
index = 0;
process_hfi_event(&perf_cap);
}
}
}
return 0;
}
static int _hfi_exit;
static int check_hf_suport(void)
{
unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
__cpuid(6, eax, ebx, ecx, edx);
if (eax & BIT(19))
return 1;
return 0;
}
int hfi_main(void)
{
struct nl_sock *sock;
struct nl_cb *cb;
int err = 0;
int mcast_id;
int no_block = 0;
if (!check_hf_suport()) {
fprintf(stderr, "CPU Doesn't support HFI\n");
return -1;
}
sock = nl_socket_alloc();
if (!sock) {
fprintf(stderr, "nl_socket_alloc failed\n");
return -1;
}
if (genl_connect(sock)) {
fprintf(stderr, "genl_connect(sk_event) failed\n");
goto free_sock;
}
drv.nl_handle = sock;
drv.nl_cb = cb = nl_cb_alloc(NL_CB_DEFAULT);
if (drv.nl_cb == NULL) {
printf("Failed to allocate netlink callbacks");
goto free_sock;
}
mcast_id = nl_get_multicast_id(&drv, THERMAL_GENL_FAMILY_NAME,
THERMAL_GENL_EVENT_GROUP_NAME);
if (mcast_id < 0) {
fprintf(stderr, "nl_get_multicast_id failed\n");
goto free_sock;
}
if (nl_socket_add_membership(sock, mcast_id)) {
fprintf(stderr, "nl_socket_add_membership failed");
goto free_sock;
}
nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check_handler, 0);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handle_event, NULL);
if (no_block)
nl_socket_set_nonblocking(sock);
debug_printf("hfi is initialized\n");
while (!_hfi_exit && !err) {
err = nl_recvmsgs(sock, cb);
debug_printf("nl_recv_message err:%d\n", err);
}
return 0;
/* Netlink library doesn't have calls to dealloc cb or disconnect */
free_sock:
nl_socket_free(sock);
return -1;
}
void hfi_exit(void)
{
_hfi_exit = 1;
}

View File

@ -15,7 +15,8 @@ struct process_cmd_struct {
int arg;
};
static const char *version_str = "v1.11";
static const char *version_str = "v1.12";
static const int supported_api_ver = 1;
static struct isst_if_platform_info isst_platform_info;
static char *progname;
@ -368,7 +369,7 @@ int get_topo_max_cpus(void)
return topo_max_cpus;
}
static void set_cpu_online_offline(int cpu, int state)
void set_cpu_online_offline(int cpu, int state)
{
char buffer[128];
int fd, ret;
@ -409,12 +410,10 @@ static void force_all_cpus_online(void)
unlink("/var/run/isst_cpu_topology.dat");
}
#define MAX_PACKAGE_COUNT 8
#define MAX_DIE_PER_PACKAGE 2
static void for_each_online_package_in_set(void (*callback)(int, void *, void *,
void *, void *),
void *arg1, void *arg2, void *arg3,
void *arg4)
void for_each_online_package_in_set(void (*callback)(int, void *, void *,
void *, void *),
void *arg1, void *arg2, void *arg3,
void *arg4)
{
int max_packages[MAX_PACKAGE_COUNT * MAX_PACKAGE_COUNT];
int pkg_index = 0, i;
@ -2803,7 +2802,9 @@ static void usage(void)
printf("\t[-p|--pause] : Delay between two mail box commands in milliseconds\n");
printf("\t[-r|--retry] : Retry count for mail box commands on failure, default 3\n");
printf("\t[-v|--version] : Print version\n");
printf("\t[-b|--oob : Start a daemon to process HFI events for perf profile change from Out of Band agent.\n");
printf("\t[-n|--no-daemon : Don't run as daemon. By default --oob will turn on daemon mode\n");
printf("\t[-w|--delay : Delay for reading config level state change in OOB poll mode.\n");
printf("\nResult format\n");
printf("\tResult display uses a common format for each command:\n");
printf("\tResults are formatted in text/JSON with\n");
@ -2837,6 +2838,9 @@ static void cmdline(int argc, char **argv)
int opt, force_cpus_online = 0;
int option_index = 0;
int ret;
int oob_mode = 0;
int poll_interval = -1;
int no_daemon = 0;
static struct option long_options[] = {
{ "all-cpus-online", no_argument, 0, 'a' },
@ -2849,6 +2853,9 @@ static void cmdline(int argc, char **argv)
{ "out", required_argument, 0, 'o' },
{ "retry", required_argument, 0, 'r' },
{ "version", no_argument, 0, 'v' },
{ "oob", no_argument, 0, 'b' },
{ "no-daemon", no_argument, 0, 'n' },
{ "poll-interval", required_argument, 0, 'w' },
{ 0, 0, 0, 0 }
};
@ -2875,7 +2882,7 @@ static void cmdline(int argc, char **argv)
}
progname = argv[0];
while ((opt = getopt_long_only(argc, argv, "+c:df:hio:va", long_options,
while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:n", long_options,
&option_index)) != -1) {
switch (opt) {
case 'a':
@ -2920,12 +2927,26 @@ static void cmdline(int argc, char **argv)
case 'v':
print_version();
break;
case 'b':
oob_mode = 1;
break;
case 'n':
no_daemon = 1;
break;
case 'w':
ret = strtol(optarg, &ptr, 10);
if (!ret) {
fprintf(stderr, "Invalid poll interval count\n");
exit(0);
}
poll_interval = ret;
break;
default:
usage();
}
}
if (optind > (argc - 2)) {
if (optind > (argc - 2) && !oob_mode) {
usage();
exit(0);
}
@ -2936,6 +2957,17 @@ static void cmdline(int argc, char **argv)
set_cpu_present_cpu_mask();
set_cpu_target_cpu_mask();
if (oob_mode) {
create_cpu_map();
if (debug_flag)
fprintf(stderr, "OOB mode is enabled in debug mode\n");
ret = isst_daemon(debug_flag, poll_interval, no_daemon);
if (ret)
fprintf(stderr, "OOB mode enable failed\n");
goto out;
}
if (!is_clx_n_platform()) {
ret = isst_fill_platform_info();
if (ret)

View File

@ -0,0 +1,244 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select -- Allow speed select to daemonize
* Copyright (c) 2022 Intel Corporation.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <time.h>
#include "isst.h"
static int per_package_levels_info[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE];
static time_t per_package_levels_tm[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE];
static void init_levels(void)
{
int i, j;
for (i = 0; i < MAX_PACKAGE_COUNT; ++i)
for (j = 0; j < MAX_DIE_PER_PACKAGE; ++j)
per_package_levels_info[i][j] = -1;
}
void process_level_change(int cpu)
{
struct isst_pkg_ctdp_level_info ctdp_level;
int pkg_id = get_physical_package_id(cpu);
int die_id = get_physical_die_id(cpu);
struct isst_pkg_ctdp pkg_dev;
time_t tm;
int ret;
if (pkg_id >= MAX_PACKAGE_COUNT || die_id > MAX_DIE_PER_PACKAGE) {
debug_printf("Invalid package/die info for cpu:%d\n", cpu);
return;
}
tm = time(NULL);
if (tm - per_package_levels_tm[pkg_id][die_id] < 2 )
return;
per_package_levels_tm[pkg_id][die_id] = tm;
ret = isst_get_ctdp_levels(cpu, &pkg_dev);
if (ret) {
debug_printf("Can't get tdp levels for cpu:%d\n", cpu);
return;
}
debug_printf("Get Config level %d pkg:%d die:%d current_level:%d \n", cpu,
pkg_id, die_id, pkg_dev.current_level);
if (pkg_dev.locked) {
debug_printf("config TDP s locked \n");
return;
}
if (per_package_levels_info[pkg_id][die_id] == pkg_dev.current_level)
return;
debug_printf("**Config level change for cpu:%d pkg:%d die:%d from %d to %d\n",
cpu, pkg_id, die_id, per_package_levels_info[pkg_id][die_id],
pkg_dev.current_level);
per_package_levels_info[pkg_id][die_id] = pkg_dev.current_level;
ctdp_level.core_cpumask_size =
alloc_cpu_set(&ctdp_level.core_cpumask);
ret = isst_get_coremask_info(cpu, pkg_dev.current_level, &ctdp_level);
if (ret) {
free_cpu_set(ctdp_level.core_cpumask);
debug_printf("Can't get core_mask:%d\n", cpu);
return;
}
if (ctdp_level.cpu_count) {
int i, max_cpus = get_topo_max_cpus();
for (i = 0; i < max_cpus; ++i) {
if (pkg_id != get_physical_package_id(i) || die_id != get_physical_die_id(i))
continue;
if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) {
fprintf(stderr, "online cpu %d\n", i);
set_cpu_online_offline(i, 1);
} else {
fprintf(stderr, "offline cpu %d\n", i);
set_cpu_online_offline(i, 0);
}
}
}
free_cpu_set(ctdp_level.core_cpumask);
}
static void _poll_for_config_change(int cpu, void *arg1, void *arg2,
void *arg3, void *arg4)
{
process_level_change(cpu);
}
static void poll_for_config_change(void)
{
for_each_online_package_in_set(_poll_for_config_change, NULL, NULL,
NULL, NULL);
}
static int done = 0;
static int pid_file_handle;
static void signal_handler(int sig)
{
switch (sig) {
case SIGINT:
case SIGTERM:
done = 1;
hfi_exit();
exit(0);
break;
default:
break;
}
}
static void daemonize(char *rundir, char *pidfile)
{
int pid, sid, i;
char str[10];
struct sigaction sig_actions;
sigset_t sig_set;
int ret;
if (getppid() == 1)
return;
sigemptyset(&sig_set);
sigaddset(&sig_set, SIGCHLD);
sigaddset(&sig_set, SIGTSTP);
sigaddset(&sig_set, SIGTTOU);
sigaddset(&sig_set, SIGTTIN);
sigprocmask(SIG_BLOCK, &sig_set, NULL);
sig_actions.sa_handler = signal_handler;
sigemptyset(&sig_actions.sa_mask);
sig_actions.sa_flags = 0;
sigaction(SIGHUP, &sig_actions, NULL);
sigaction(SIGTERM, &sig_actions, NULL);
sigaction(SIGINT, &sig_actions, NULL);
pid = fork();
if (pid < 0) {
/* Could not fork */
exit(EXIT_FAILURE);
}
if (pid > 0)
exit(EXIT_SUCCESS);
umask(027);
sid = setsid();
if (sid < 0)
exit(EXIT_FAILURE);
/* close all descriptors */
for (i = getdtablesize(); i >= 0; --i)
close(i);
i = open("/dev/null", O_RDWR);
ret = dup(i);
if (ret == -1)
exit(EXIT_FAILURE);
ret = dup(i);
if (ret == -1)
exit(EXIT_FAILURE);
ret = chdir(rundir);
if (ret == -1)
exit(EXIT_FAILURE);
pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600);
if (pid_file_handle == -1) {
/* Couldn't open lock file */
exit(1);
}
/* Try to lock file */
#ifdef LOCKF_SUPPORT
if (lockf(pid_file_handle, F_TLOCK, 0) == -1) {
#else
if (flock(pid_file_handle, LOCK_EX|LOCK_NB) < 0) {
#endif
/* Couldn't get lock on lock file */
fprintf(stderr, "Couldn't get lock file %d\n", getpid());
exit(1);
}
snprintf(str, sizeof(str), "%d\n", getpid());
ret = write(pid_file_handle, str, strlen(str));
if (ret == -1)
exit(EXIT_FAILURE);
close(i);
}
int isst_daemon(int debug_mode, int poll_interval, int no_daemon)
{
int ret;
if (!no_daemon && poll_interval < 0 && !debug_mode) {
fprintf(stderr, "OOB mode is enabled and will run as daemon\n");
daemonize((char *) "/tmp/",
(char *)"/tmp/hfi-events.pid");
} else {
signal(SIGINT, signal_handler);
}
init_levels();
if (poll_interval < 0) {
ret = hfi_main();
if (ret) {
fprintf(stderr, "HFI initialization failed\n");
}
fprintf(stderr, "Must specify poll-interval\n");
return ret;
}
debug_printf("Starting loop\n");
while (!done) {
sleep(poll_interval);
poll_for_config_change();
}
return 0;
}

View File

@ -76,6 +76,9 @@
#define DISP_FREQ_MULTIPLIER 100
#define MAX_PACKAGE_COUNT 8
#define MAX_DIE_PER_PACKAGE 2
struct isst_clos_config {
int pkg_id;
int die_id;
@ -260,4 +263,14 @@ extern int is_skx_based_platform(void);
extern int is_spr_platform(void);
extern int is_icx_platform(void);
extern void isst_trl_display_information(int cpu, FILE *outf, unsigned long long trl);
extern void set_cpu_online_offline(int cpu, int state);
extern void for_each_online_package_in_set(void (*callback)(int, void *, void *,
void *, void *),
void *arg1, void *arg2, void *arg3,
void *arg4);
extern int isst_daemon(int debug_mode, int poll_interval, int no_daemon);
extern void process_level_change(int cpu);
extern int hfi_main(void);
extern void hfi_exit(void);
#endif