tools/power/x86/intel-speed-select: OOB daemon mode
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Tue, 18 Jan 2022 23:02:57 +0000 (15:02 -0800)
committerSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Thu, 17 Feb 2022 18:57:25 +0000 (10:57 -0800)
It is possible that some out of band agent changed config level. In this
case CPUs need to be online/offline to support this config change. Add
a command line option --oob, so that this tool can run as daemon and poll
for config level change and take action. The poll interval is configurable
in seconds using config option --poll-interval.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
tools/power/x86/intel-speed-select/Build
tools/power/x86/intel-speed-select/isst-config.c
tools/power/x86/intel-speed-select/isst-daemon.c [new file with mode: 0644]
tools/power/x86/intel-speed-select/isst.h

index b61456d75190fbe65146fdb945e71eafdbad75b7..86fb9020cca2b6cd8c75c85669df9695c2ddbb92 100644 (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
index efe72fa48224ab3da1666bd9f2e60a9adc4b4a43..de5029b54050db2ad937b66b1c44cce70316341f 100644 (file)
@@ -368,7 +368,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 +409,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 +2801,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 +2837,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 +2852,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 +2881,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 +2926,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 +2956,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)
diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c
new file mode 100644 (file)
index 0000000..15a70bb
--- /dev/null
@@ -0,0 +1,239 @@
+// 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;
+               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) {
+               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;
+}
index 1aa15d5ea57cefe63bcdbbd0766005769b09d4c5..b33f2c68d2ce0d0b8098359772c87200f043e37d 100644 (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,12 @@ 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);
 #endif