linux-zen-desktop/tools/power/x86/intel-speed-select/isst-config.c

3141 lines
74 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select -- Enumerate and control features
* Copyright (c) 2019 Intel Corporation.
*/
#include <linux/isst_if.h>
#include "isst.h"
struct process_cmd_struct {
char *feature;
char *command;
void (*process_fn)(int arg);
int arg;
};
static const char *version_str = "v1.16";
static const int supported_api_ver = 2;
static struct isst_if_platform_info isst_platform_info;
static char *progname;
static int debug_flag;
static FILE *outf;
static int cpu_model;
static int cpu_stepping;
#define MAX_CPUS_IN_ONE_REQ 256
static short max_target_cpus;
static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ];
static int topo_max_cpus;
static size_t present_cpumask_size;
static cpu_set_t *present_cpumask;
static size_t target_cpumask_size;
static cpu_set_t *target_cpumask;
static int tdp_level = 0xFF;
static int fact_bucket = 0xFF;
static int fact_avx = 0xFF;
static unsigned long long fact_trl;
static int out_format_json;
static int cmd_help;
static int force_online_offline;
static int auto_mode;
static int fact_enable_fail;
static int cgroupv2;
/* clos related */
static int current_clos = -1;
static int clos_epp = -1;
static int clos_prop_prio = -1;
static int clos_min = -1;
static int clos_max = -1;
static int clos_desired = -1;
static int clos_priority_type;
struct _cpu_map {
unsigned short core_id;
unsigned short pkg_id;
unsigned short die_id;
unsigned short punit_id;
unsigned short punit_cpu;
unsigned short punit_cpu_core;
unsigned short initialized;
};
struct _cpu_map *cpu_map;
struct cpu_topology {
short cpu;
short core_id;
short pkg_id;
short die_id;
};
FILE *get_output_file(void)
{
return outf;
}
int is_debug_enabled(void)
{
return debug_flag;
}
void debug_printf(const char *format, ...)
{
va_list args;
va_start(args, format);
if (debug_flag)
vprintf(format, args);
va_end(args);
}
int is_clx_n_platform(void)
{
if (cpu_model == 0x55)
if (cpu_stepping == 0x6 || cpu_stepping == 0x7)
return 1;
return 0;
}
int is_skx_based_platform(void)
{
if (cpu_model == 0x55)
return 1;
return 0;
}
int is_spr_platform(void)
{
if (cpu_model == 0x8F)
return 1;
return 0;
}
int is_emr_platform(void)
{
if (cpu_model == 0xCF)
return 1;
return 0;
}
int is_icx_platform(void)
{
if (cpu_model == 0x6A || cpu_model == 0x6C)
return 1;
return 0;
}
static int update_cpu_model(void)
{
unsigned int ebx, ecx, edx;
unsigned int fms, family;
__cpuid(1, fms, ebx, ecx, edx);
family = (fms >> 8) & 0xf;
cpu_model = (fms >> 4) & 0xf;
if (family == 6 || family == 0xf)
cpu_model += ((fms >> 16) & 0xf) << 4;
cpu_stepping = fms & 0xf;
/* only three CascadeLake-N models are supported */
if (is_clx_n_platform()) {
FILE *fp;
size_t n = 0;
char *line = NULL;
int ret = 1;
fp = fopen("/proc/cpuinfo", "r");
if (!fp)
err(-1, "cannot open /proc/cpuinfo\n");
while (getline(&line, &n, fp) > 0) {
if (strstr(line, "model name")) {
if (strstr(line, "6252N") ||
strstr(line, "6230N") ||
strstr(line, "5218N"))
ret = 0;
break;
}
}
free(line);
fclose(fp);
return ret;
}
return 0;
}
int api_version(void)
{
return isst_platform_info.api_version;
}
/* Open a file, and exit on failure */
static FILE *fopen_or_exit(const char *path, const char *mode)
{
FILE *filep = fopen(path, mode);
if (!filep)
err(1, "%s: open failed", path);
return filep;
}
/* Parse a file containing a single int */
static int parse_int_file(int fatal, const char *fmt, ...)
{
va_list args;
char path[PATH_MAX];
FILE *filep;
int value;
va_start(args, fmt);
vsnprintf(path, sizeof(path), fmt, args);
va_end(args);
if (fatal) {
filep = fopen_or_exit(path, "r");
} else {
filep = fopen(path, "r");
if (!filep)
return -1;
}
if (fscanf(filep, "%d", &value) != 1)
err(1, "%s: failed to parse number from file", path);
fclose(filep);
return value;
}
int cpufreq_sysfs_present(void)
{
DIR *dir;
dir = opendir("/sys/devices/system/cpu/cpu0/cpufreq");
if (dir) {
closedir(dir);
return 1;
}
return 0;
}
int out_format_is_json(void)
{
return out_format_json;
}
static int get_stored_topology_info(int cpu, int *core_id, int *pkg_id, int *die_id)
{
const char *pathname = "/var/run/isst_cpu_topology.dat";
struct cpu_topology cpu_top;
FILE *fp;
int ret;
fp = fopen(pathname, "rb");
if (!fp)
return -1;
ret = fseek(fp, cpu * sizeof(cpu_top), SEEK_SET);
if (ret)
goto err_ret;
ret = fread(&cpu_top, sizeof(cpu_top), 1, fp);
if (ret != 1) {
ret = -1;
goto err_ret;
}
*pkg_id = cpu_top.pkg_id;
*core_id = cpu_top.core_id;
*die_id = cpu_top.die_id;
ret = 0;
err_ret:
fclose(fp);
return ret;
}
static void store_cpu_topology(void)
{
const char *pathname = "/var/run/isst_cpu_topology.dat";
FILE *fp;
int i;
fp = fopen(pathname, "rb");
if (fp) {
/* Mapping already exists */
fclose(fp);
return;
}
fp = fopen(pathname, "wb");
if (!fp) {
fprintf(stderr, "Can't create file:%s\n", pathname);
return;
}
fprintf(stderr, "Caching topology information\n");
for (i = 0; i < topo_max_cpus; ++i) {
struct cpu_topology cpu_top;
cpu_top.core_id = parse_int_file(0,
"/sys/devices/system/cpu/cpu%d/topology/core_id", i);
if (cpu_top.core_id < 0)
cpu_top.core_id = -1;
cpu_top.pkg_id = parse_int_file(0,
"/sys/devices/system/cpu/cpu%d/topology/physical_package_id", i);
if (cpu_top.pkg_id < 0)
cpu_top.pkg_id = -1;
cpu_top.die_id = parse_int_file(0,
"/sys/devices/system/cpu/cpu%d/topology/die_id", i);
if (cpu_top.die_id < 0)
cpu_top.die_id = -1;
cpu_top.cpu = i;
if (fwrite(&cpu_top, sizeof(cpu_top), 1, fp) != 1) {
fprintf(stderr, "Can't write to:%s\n", pathname);
break;
}
}
fclose(fp);
}
static int get_physical_package_id(int cpu)
{
int ret;
if (cpu < 0)
return -1;
if (cpu_map && cpu_map[cpu].initialized)
return cpu_map[cpu].pkg_id;
ret = parse_int_file(0,
"/sys/devices/system/cpu/cpu%d/topology/physical_package_id",
cpu);
if (ret < 0) {
int core_id, pkg_id, die_id;
ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id);
if (!ret)
return pkg_id;
}
return ret;
}
static int get_physical_core_id(int cpu)
{
int ret;
if (cpu < 0)
return -1;
if (cpu_map && cpu_map[cpu].initialized)
return cpu_map[cpu].core_id;
ret = parse_int_file(0,
"/sys/devices/system/cpu/cpu%d/topology/core_id",
cpu);
if (ret < 0) {
int core_id, pkg_id, die_id;
ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id);
if (!ret)
return core_id;
}
return ret;
}
static int get_physical_die_id(int cpu)
{
int ret;
if (cpu < 0)
return -1;
if (cpu_map && cpu_map[cpu].initialized)
return cpu_map[cpu].die_id;
ret = parse_int_file(0,
"/sys/devices/system/cpu/cpu%d/topology/die_id",
cpu);
if (ret < 0) {
int core_id, pkg_id, die_id;
ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id);
if (!ret) {
if (die_id < 0)
die_id = 0;
return die_id;
}
}
if (ret < 0)
ret = 0;
return ret;
}
static int get_physical_punit_id(int cpu)
{
if (cpu < 0)
return -1;
if (cpu_map && cpu_map[cpu].initialized)
return cpu_map[cpu].punit_id;
return -1;
}
void set_isst_id(struct isst_id *id, int cpu)
{
id->cpu = cpu;
id->pkg = get_physical_package_id(cpu);
if (id->pkg >= MAX_PACKAGE_COUNT)
id->pkg = -1;
id->die = get_physical_die_id(cpu);
if (id->die >= MAX_DIE_PER_PACKAGE)
id->die = -1;
id->punit = get_physical_punit_id(cpu);
if (id->punit >= MAX_PUNIT_PER_DIE)
id->punit = -1;
}
int is_cpu_in_power_domain(int cpu, struct isst_id *id)
{
struct isst_id tid;
set_isst_id(&tid, cpu);
if (id->pkg == tid.pkg && id->die == tid.die && id->punit == tid.punit)
return 1;
return 0;
}
int get_cpufreq_base_freq(int cpu)
{
return parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency", cpu);
}
int get_topo_max_cpus(void)
{
return topo_max_cpus;
}
static unsigned int is_cpu_online(int cpu)
{
char buffer[128];
int fd, ret;
unsigned char online;
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d/online", cpu);
fd = open(buffer, O_RDONLY);
if (fd < 0)
return fd;
ret = read(fd, &online, sizeof(online));
close(fd);
if (ret == -1)
return ret;
if (online == '1')
online = 1;
else
online = 0;
return online;
}
void set_cpu_online_offline(int cpu, int state)
{
char buffer[128];
int fd, ret;
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d/online", cpu);
fd = open(buffer, O_WRONLY);
if (fd < 0) {
if (!cpu && state) {
fprintf(stderr, "This system is not configured for CPU 0 online/offline\n");
fprintf(stderr, "Ignoring online request for CPU 0 as this is already online\n");
return;
}
err(-1, "%s open failed", buffer);
}
if (state)
ret = write(fd, "1\n", 2);
else
ret = write(fd, "0\n", 2);
if (ret == -1)
perror("Online/Offline: Operation failed\n");
close(fd);
}
static void force_all_cpus_online(void)
{
int i;
fprintf(stderr, "Forcing all CPUs online\n");
for (i = 0; i < topo_max_cpus; ++i)
set_cpu_online_offline(i, 1);
unlink("/var/run/isst_cpu_topology.dat");
}
void for_each_online_power_domain_in_set(void (*callback)(struct isst_id *, void *, void *,
void *, void *),
void *arg1, void *arg2, void *arg3,
void *arg4)
{
struct isst_id id;
int cpus[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE];
int valid_mask[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE] = {0};
int i, j, k;
memset(cpus, -1, sizeof(cpus));
for (i = 0; i < topo_max_cpus; ++i) {
int online;
if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
continue;
online = parse_int_file(
i != 0, "/sys/devices/system/cpu/cpu%d/online", i);
if (online < 0)
online = 1; /* online entry for CPU 0 needs some special configs */
if (!online)
continue;
set_isst_id(&id, i);
if (id.pkg < 0 || id.die < 0 || id.punit < 0)
continue;
valid_mask[id.pkg][id.die] = 1;
if (cpus[id.pkg][id.die][id.punit] == -1)
cpus[id.pkg][id.die][id.punit] = i;
}
for (i = 0; i < MAX_PACKAGE_COUNT; i++) {
for (j = 0; j < MAX_DIE_PER_PACKAGE; j++) {
/*
* Fix me:
* How to check a non-cpu die for a package/die with all cpu offlined?
*/
if (!valid_mask[i][j])
continue;
for (k = 0; k < MAX_PUNIT_PER_DIE; k++) {
id.cpu = cpus[i][j][k];
id.pkg = i;
id.die = j;
id.punit = k;
if (isst_is_punit_valid(&id))
callback(&id, arg1, arg2, arg3, arg4);
}
}
}
}
static void for_each_online_target_cpu_in_set(
void (*callback)(struct isst_id *, void *, void *, void *, void *), void *arg1,
void *arg2, void *arg3, void *arg4)
{
int i, found = 0;
struct isst_id id;
for (i = 0; i < topo_max_cpus; ++i) {
int online;
if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask))
continue;
if (i)
online = parse_int_file(
1, "/sys/devices/system/cpu/cpu%d/online", i);
else
online =
1; /* online entry for CPU 0 needs some special configs */
set_isst_id(&id, i);
if (online && callback) {
callback(&id, arg1, arg2, arg3, arg4);
found = 1;
}
}
if (!found)
fprintf(stderr, "No valid CPU in the list\n");
}
#define BITMASK_SIZE 32
static void set_max_cpu_num(void)
{
FILE *filep;
unsigned long dummy;
int i;
topo_max_cpus = 0;
for (i = 0; i < 256; ++i) {
char path[256];
snprintf(path, sizeof(path),
"/sys/devices/system/cpu/cpu%d/topology/thread_siblings", i);
filep = fopen(path, "r");
if (filep)
break;
}
if (!filep) {
fprintf(stderr, "Can't get max cpu number\n");
exit(0);
}
while (fscanf(filep, "%lx,", &dummy) == 1)
topo_max_cpus += BITMASK_SIZE;
fclose(filep);
debug_printf("max cpus %d\n", topo_max_cpus);
}
size_t alloc_cpu_set(cpu_set_t **cpu_set)
{
cpu_set_t *_cpu_set;
size_t size;
_cpu_set = CPU_ALLOC((topo_max_cpus + 1));
if (_cpu_set == NULL)
err(3, "CPU_ALLOC");
size = CPU_ALLOC_SIZE((topo_max_cpus + 1));
CPU_ZERO_S(size, _cpu_set);
*cpu_set = _cpu_set;
return size;
}
void free_cpu_set(cpu_set_t *cpu_set)
{
CPU_FREE(cpu_set);
}
static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE];
int get_max_punit_core_id(struct isst_id *id)
{
int max_id = 0;
int i;
for (i = 0; i < topo_max_cpus; ++i)
{
if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
continue;
if (is_cpu_in_power_domain(i, id) &&
cpu_map[i].punit_cpu_core > max_id)
max_id = cpu_map[i].punit_cpu_core;
}
return max_id;
}
int get_cpu_count(struct isst_id *id)
{
if (id->pkg < 0 || id->die < 0 || id->punit < 0)
return 0;
return cpu_cnt[id->pkg][id->die][id->punit];
}
static void update_punit_cpu_info(__u32 physical_cpu, struct _cpu_map *cpu_map)
{
if (api_version() > 1) {
/*
* MSR 0x54 format
* [15:11] PM_DOMAIN_ID
* [10:3] MODULE_ID (aka IDI_AGENT_ID)
* [2:0] LP_ID (We don't care about these bits we only
* care die and core id
* For Atom:
* [2] Always 0
* [1:0] core ID within module
* For Core
* [2:1] Always 0
* [0] thread ID
*/
cpu_map->punit_id = (physical_cpu >> 11) & 0x1f;
cpu_map->punit_cpu_core = (physical_cpu >> 3) & 0xff;
cpu_map->punit_cpu = physical_cpu & 0x7ff;
} else {
int punit_id;
/*
* MSR 0x53 format
* Format
* Bit 0 thread ID
* Bit 8:1 core ID
* Bit 13:9 punit ID
*/
cpu_map->punit_cpu = physical_cpu & 0x1ff;
cpu_map->punit_cpu_core = (cpu_map->punit_cpu >> 1); // shift to get core id
punit_id = (physical_cpu >> 9) & 0x1f;
if (punit_id >= MAX_PUNIT_PER_DIE)
punit_id = 0;
cpu_map->punit_id = punit_id;
}
}
static void create_cpu_map(void)
{
const char *pathname = "/dev/isst_interface";
size_t size;
DIR *dir;
int i, fd = 0;
struct isst_if_cpu_maps map;
/* Use calloc to make sure the memory is initialized to Zero */
cpu_map = calloc(topo_max_cpus, sizeof(*cpu_map));
if (!cpu_map)
err(3, "cpumap");
fd = open(pathname, O_RDWR);
if (fd < 0 && !is_clx_n_platform())
err(-1, "%s open failed", pathname);
size = alloc_cpu_set(&present_cpumask);
present_cpumask_size = size;
for (i = 0; i < topo_max_cpus; ++i) {
char buffer[256];
int pkg_id, die_id, core_id, punit_id;
/* check if CPU is online */
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d", i);
dir = opendir(buffer);
if (!dir)
continue;
closedir(dir);
CPU_SET_S(i, size, present_cpumask);
pkg_id = get_physical_package_id(i);
die_id = get_physical_die_id(i);
core_id = get_physical_core_id(i);
if (pkg_id < 0 || die_id < 0 || core_id < 0)
continue;
cpu_map[i].pkg_id = pkg_id;
cpu_map[i].die_id = die_id;
cpu_map[i].core_id = core_id;
punit_id = 0;
if (fd >= 0) {
map.cmd_count = 1;
map.cpu_map[0].logical_cpu = i;
debug_printf(" map logical_cpu:%d\n",
map.cpu_map[0].logical_cpu);
if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) {
perror("ISST_IF_GET_PHY_ID");
fprintf(outf, "Error: map logical_cpu:%d\n",
map.cpu_map[0].logical_cpu);
} else {
update_punit_cpu_info(map.cpu_map[0].physical_cpu, &cpu_map[i]);
}
}
cpu_map[i].initialized = 1;
cpu_cnt[pkg_id][die_id][punit_id]++;
debug_printf(
"map logical_cpu:%d core: %d die:%d pkg:%d punit:%d punit_cpu:%d punit_core:%d\n",
i, cpu_map[i].core_id, cpu_map[i].die_id,
cpu_map[i].pkg_id, cpu_map[i].punit_id,
cpu_map[i].punit_cpu, cpu_map[i].punit_cpu_core);
}
if (fd >= 0)
close(fd);
size = alloc_cpu_set(&target_cpumask);
target_cpumask_size = size;
for (i = 0; i < max_target_cpus; ++i) {
if (!CPU_ISSET_S(target_cpus[i], present_cpumask_size,
present_cpumask))
continue;
CPU_SET_S(target_cpus[i], size, target_cpumask);
}
}
void set_cpu_mask_from_punit_coremask(struct isst_id *id, unsigned long long core_mask,
size_t core_cpumask_size,
cpu_set_t *core_cpumask, int *cpu_cnt)
{
int i, cnt = 0;
if (id->cpu < 0)
return;
*cpu_cnt = 0;
for (i = 0; i < 64; ++i) {
if (core_mask & BIT_ULL(i)) {
int j;
for (j = 0; j < topo_max_cpus; ++j) {
if (!CPU_ISSET_S(j, present_cpumask_size, present_cpumask))
continue;
if (is_cpu_in_power_domain(j, id) &&
cpu_map[j].punit_cpu_core == i) {
CPU_SET_S(j, core_cpumask_size,
core_cpumask);
++cnt;
}
}
}
}
*cpu_cnt = cnt;
}
int find_phy_core_num(int logical_cpu)
{
if (logical_cpu < topo_max_cpus)
return cpu_map[logical_cpu].punit_cpu_core;
return -EINVAL;
}
int use_cgroupv2(void)
{
return cgroupv2;
}
int enable_cpuset_controller(void)
{
int fd, ret;
fd = open("/sys/fs/cgroup/cgroup.subtree_control", O_RDWR, 0);
if (fd < 0) {
debug_printf("Can't activate cpuset controller\n");
debug_printf("Either you are not root user or CGroup v2 is not supported\n");
return fd;
}
ret = write(fd, " +cpuset", strlen(" +cpuset"));
close(fd);
if (ret == -1) {
debug_printf("Can't activate cpuset controller: Write failed\n");
return ret;
}
return 0;
}
int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level)
{
int i, first, curr_index, index, ret, fd;
static char str[512], dir_name[64];
static char cpuset_cpus[128];
int str_len = sizeof(str);
DIR *dir;
snprintf(dir_name, sizeof(dir_name), "/sys/fs/cgroup/%d-%d-%d", id->pkg, id->die, id->punit);
dir = opendir(dir_name);
if (!dir) {
ret = mkdir(dir_name, 0744);
if (ret) {
debug_printf("Can't create dir:%s errno:%d\n", dir_name, errno);
return ret;
}
}
closedir(dir);
if (!level) {
sprintf(cpuset_cpus, "%s/cpuset.cpus.partition", dir_name);
fd = open(cpuset_cpus, O_RDWR, 0);
if (fd < 0) {
return fd;
}
ret = write(fd, "member", strlen("member"));
if (ret == -1) {
printf("Can't update to member\n");
return ret;
}
return 0;
}
if (!CPU_COUNT_S(mask_size, cpu_mask)) {
return -1;
}
curr_index = 0;
first = 1;
str[0] = '\0';
for (i = 0; i < get_topo_max_cpus(); ++i) {
if (!is_cpu_in_power_domain(i, id))
continue;
if (CPU_ISSET_S(i, mask_size, cpu_mask))
continue;
if (!first) {
index = snprintf(&str[curr_index],
str_len - curr_index, ",");
curr_index += index;
if (curr_index >= str_len)
break;
}
index = snprintf(&str[curr_index], str_len - curr_index, "%d",
i);
curr_index += index;
if (curr_index >= str_len)
break;
first = 0;
}
debug_printf("isolated CPUs list: package:%d curr_index:%d [%s]\n", id->pkg, curr_index ,str);
snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus", dir_name);
fd = open(cpuset_cpus, O_RDWR, 0);
if (fd < 0) {
return fd;
}
ret = write(fd, str, strlen(str));
close(fd);
if (ret == -1) {
debug_printf("Can't activate cpuset controller: Write failed\n");
return ret;
}
snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus.partition", dir_name);
fd = open(cpuset_cpus, O_RDWR, 0);
if (fd < 0) {
return fd;
}
ret = write(fd, "isolated", strlen("isolated"));
if (ret == -1) {
debug_printf("Can't update to isolated\n");
ret = write(fd, "root", strlen("root"));
if (ret == -1)
debug_printf("Can't update to root\n");
}
close(fd);
if (ret < 0)
return ret;
return 0;
}
static int isst_fill_platform_info(void)
{
const char *pathname = "/dev/isst_interface";
int fd;
if (is_clx_n_platform()) {
isst_platform_info.api_version = 1;
goto set_platform_ops;
}
fd = open(pathname, O_RDWR);
if (fd < 0)
err(-1, "%s open failed", pathname);
if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &isst_platform_info) == -1) {
perror("ISST_IF_GET_PLATFORM_INFO");
close(fd);
return -1;
}
close(fd);
if (isst_platform_info.api_version > supported_api_ver) {
printf("Incompatible API versions; Upgrade of tool is required\n");
return -1;
}
set_platform_ops:
if (isst_set_platform_ops(isst_platform_info.api_version)) {
fprintf(stderr, "Failed to set platform callbacks\n");
exit(0);
}
return 0;
}
void get_isst_status(struct isst_id *id, void *arg1, void *arg2, void *arg3, void *arg4)
{
struct isst_pkg_ctdp pkg_dev;
struct isst_id *tid = (struct isst_id *)arg2;
int *mask = (int *)arg3;
int *max_level = (int *)arg4;
int j, ret;
/* Only check the first cpu power domain */
if (id->cpu < 0 || tid->cpu >= 0)
return;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret)
return;
if (pkg_dev.enabled)
*mask |= BIT(0);
if (pkg_dev.locked)
*mask |= BIT(1);
if (*max_level < pkg_dev.levels)
*max_level = pkg_dev.levels;
for (j = 0; j <= pkg_dev.levels; ++j) {
struct isst_pkg_ctdp_level_info ctdp_level;
ret = isst_get_ctdp_control(id, j, &ctdp_level);
if (ret)
continue;
if (ctdp_level.fact_support)
*mask |= BIT(2);
if (ctdp_level.pbf_support)
*mask |= BIT(3);
}
tid->cpu = id->cpu;
tid->pkg = id->pkg;
tid->die = id->die;
tid->punit = id->punit;
}
static void isst_print_extended_platform_info(void)
{
int cp_state, cp_cap;
struct isst_id id;
int mask = 0, max_level = 0;
id.cpu = -1;
for_each_online_power_domain_in_set(get_isst_status, NULL, &id, &mask, &max_level);
if (mask & BIT(0)) {
fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is supported\n");
} else {
fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is not supported\n");
fprintf(outf, "Only performance level 0 (base level) is present\n");
}
if (mask & BIT(1))
fprintf(outf, "TDP level change control is locked\n");
else
fprintf(outf, "TDP level change control is unlocked, max level: %d\n", max_level);
if (mask & BIT(2))
fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is supported\n");
else
fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is not supported\n");
if (mask & BIT(3))
fprintf(outf, "Intel(R) SST-BF (feature base-freq) is supported\n");
else
fprintf(outf, "Intel(R) SST-BF (feature base-freq) is not supported\n");
if (isst_read_pm_config(&id, &cp_state, &cp_cap)) {
fprintf(outf, "Intel(R) SST-CP (feature core-power) status is unknown\n");
return;
}
if (cp_cap)
fprintf(outf, "Intel(R) SST-CP (feature core-power) is supported\n");
else
fprintf(outf, "Intel(R) SST-CP (feature core-power) is not supported\n");
}
static void isst_print_platform_information(void)
{
if (is_clx_n_platform()) {
fprintf(stderr, "\nThis option in not supported on this platform\n");
exit(0);
}
/* Early initialization to create working cpu_map */
set_max_cpu_num();
create_cpu_map();
fprintf(outf, "Platform: API version : %d\n",
isst_platform_info.api_version);
fprintf(outf, "Platform: Driver version : %d\n",
isst_platform_info.driver_version);
fprintf(outf, "Platform: mbox supported : %d\n",
isst_platform_info.mbox_supported);
fprintf(outf, "Platform: mmio supported : %d\n",
isst_platform_info.mmio_supported);
isst_print_extended_platform_info();
exit(0);
}
static char *local_str0, *local_str1;
static void exec_on_get_ctdp_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
int (*fn_ptr)(struct isst_id *id, void *arg);
int ret;
fn_ptr = arg1;
ret = fn_ptr(id, arg2);
if (ret)
isst_display_error_info_message(1, "get_tdp_* failed", 0, 0);
else
isst_ctdp_display_core_info(id, outf, arg3,
*(unsigned int *)arg4,
local_str0, local_str1);
}
#define _get_tdp_level(desc, suffix, object, help, str0, str1) \
static void get_tdp_##object(int arg) \
{ \
struct isst_pkg_ctdp ctdp; \
\
if (cmd_help) { \
fprintf(stderr, \
"Print %s [No command arguments are required]\n", \
help); \
exit(0); \
} \
local_str0 = str0; \
local_str1 = str1; \
isst_ctdp_display_information_start(outf); \
if (max_target_cpus) \
for_each_online_target_cpu_in_set( \
exec_on_get_ctdp_cpu, isst_get_ctdp_##suffix, \
&ctdp, desc, &ctdp.object); \
else \
for_each_online_power_domain_in_set(exec_on_get_ctdp_cpu, \
isst_get_ctdp_##suffix, \
&ctdp, desc, \
&ctdp.object); \
isst_ctdp_display_information_end(outf); \
}
_get_tdp_level("get-config-levels", levels, levels, "Max TDP level", NULL, NULL);
_get_tdp_level("get-config-version", levels, version, "TDP version", NULL, NULL);
_get_tdp_level("get-config-enabled", levels, enabled, "perf-profile enable status", "disabled", "enabled");
_get_tdp_level("get-config-current_level", levels, current_level,
"Current TDP Level", NULL, NULL);
_get_tdp_level("get-lock-status", levels, locked, "TDP lock status", "unlocked", "locked");
struct isst_pkg_ctdp clx_n_pkg_dev;
static int clx_n_get_base_ratio(void)
{
FILE *fp;
char *begin, *end, *line = NULL;
char number[5];
float value = 0;
size_t n = 0;
fp = fopen("/proc/cpuinfo", "r");
if (!fp)
err(-1, "cannot open /proc/cpuinfo\n");
while (getline(&line, &n, fp) > 0) {
if (strstr(line, "model name")) {
/* this is true for CascadeLake-N */
begin = strstr(line, "@ ") + 2;
end = strstr(line, "GHz");
strncpy(number, begin, end - begin);
value = atof(number) * 10;
break;
}
}
free(line);
fclose(fp);
return (int)(value);
}
static int clx_n_config(struct isst_id *id)
{
int i, ret;
unsigned long cpu_bf;
struct isst_pkg_ctdp_level_info *ctdp_level;
struct isst_pbf_info *pbf_info;
ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
pbf_info = &ctdp_level->pbf_info;
ctdp_level->core_cpumask_size =
alloc_cpu_set(&ctdp_level->core_cpumask);
/* find the frequency base ratio */
ctdp_level->tdp_ratio = clx_n_get_base_ratio();
if (ctdp_level->tdp_ratio == 0) {
debug_printf("CLX: cn base ratio is zero\n");
ret = -1;
goto error_ret;
}
/* find the high and low priority frequencies */
pbf_info->p1_high = 0;
pbf_info->p1_low = ~0;
for (i = 0; i < topo_max_cpus; i++) {
if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
continue;
if (!is_cpu_in_power_domain(i, id))
continue;
CPU_SET_S(i, ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
cpu_bf = parse_int_file(1,
"/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency",
i);
if (cpu_bf > pbf_info->p1_high)
pbf_info->p1_high = cpu_bf;
if (cpu_bf < pbf_info->p1_low)
pbf_info->p1_low = cpu_bf;
}
if (pbf_info->p1_high == ~0UL) {
debug_printf("CLX: maximum base frequency not set\n");
ret = -1;
goto error_ret;
}
if (pbf_info->p1_low == 0) {
debug_printf("CLX: minimum base frequency not set\n");
ret = -1;
goto error_ret;
}
/* convert frequencies back to ratios */
pbf_info->p1_high = pbf_info->p1_high / 100000;
pbf_info->p1_low = pbf_info->p1_low / 100000;
/* create high priority cpu mask */
pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask);
for (i = 0; i < topo_max_cpus; i++) {
if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
continue;
if (!is_cpu_in_power_domain(i, id))
continue;
cpu_bf = parse_int_file(1,
"/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency",
i);
cpu_bf = cpu_bf / 100000;
if (cpu_bf == pbf_info->p1_high)
CPU_SET_S(i, pbf_info->core_cpumask_size,
pbf_info->core_cpumask);
}
/* extra ctdp & pbf struct parameters */
ctdp_level->processed = 1;
ctdp_level->pbf_support = 1; /* PBF is always supported and enabled */
ctdp_level->pbf_enabled = 1;
ctdp_level->fact_support = 0; /* FACT is never supported */
ctdp_level->fact_enabled = 0;
return 0;
error_ret:
free_cpu_set(ctdp_level->core_cpumask);
return ret;
}
static void dump_clx_n_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
void *arg3, void *arg4)
{
int ret;
if (tdp_level != 0xff && tdp_level != 0) {
isst_display_error_info_message(1, "Invalid level", 1, tdp_level);
exit(0);
}
ret = clx_n_config(id);
if (ret) {
debug_printf("clx_n_config failed");
} else {
struct isst_pkg_ctdp_level_info *ctdp_level;
struct isst_pbf_info *pbf_info;
ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
pbf_info = &ctdp_level->pbf_info;
clx_n_pkg_dev.processed = 1;
isst_ctdp_display_information(id, outf, tdp_level, &clx_n_pkg_dev);
free_cpu_set(ctdp_level->core_cpumask);
free_cpu_set(pbf_info->core_cpumask);
}
}
static void dump_isst_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
void *arg3, void *arg4)
{
struct isst_pkg_ctdp pkg_dev;
int ret;
memset(&pkg_dev, 0, sizeof(pkg_dev));
ret = isst_get_process_ctdp(id, tdp_level, &pkg_dev);
if (ret) {
isst_display_error_info_message(1, "Failed to get perf-profile info on cpu", 1, id->cpu);
isst_ctdp_display_information_end(outf);
exit(1);
} else {
isst_ctdp_display_information(id, outf, tdp_level, &pkg_dev);
isst_get_process_ctdp_complete(id, &pkg_dev);
}
}
static void dump_isst_config(int arg)
{
void *fn;
if (cmd_help) {
fprintf(stderr,
"Print Intel(R) Speed Select Technology Performance profile configuration\n");
fprintf(stderr,
"including base frequency and turbo frequency configurations\n");
fprintf(stderr, "Optional: -l|--level : Specify tdp level\n");
fprintf(stderr,
"\tIf no arguments, dump information for all TDP levels\n");
exit(0);
}
if (!is_clx_n_platform())
fn = dump_isst_config_for_cpu;
else
fn = dump_clx_n_config_for_cpu;
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL);
else
for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL);
isst_ctdp_display_information_end(outf);
}
static void adjust_scaling_max_from_base_freq(int cpu);
static void set_tdp_level_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
struct isst_pkg_ctdp pkg_dev;
int ret;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret) {
isst_display_error_info_message(1, "Get TDP level failed", 0, 0);
isst_ctdp_display_information_end(outf);
exit(1);
}
if (pkg_dev.current_level == tdp_level) {
debug_printf("TDP level already set. Skipped\n");
goto display_result;
}
ret = isst_set_tdp_level(id, tdp_level);
if (ret) {
isst_display_error_info_message(1, "Set TDP level failed", 0, 0);
isst_ctdp_display_information_end(outf);
exit(1);
}
display_result:
isst_display_result(id, outf, "perf-profile", "set_tdp_level", ret);
if (force_online_offline && id->cpu >= 0) {
struct isst_pkg_ctdp_level_info ctdp_level;
/* Wait for updated base frequencies */
usleep(2000);
/* Adjusting uncore freq */
isst_adjust_uncore_freq(id, tdp_level, &ctdp_level);
fprintf(stderr, "Option is set to online/offline\n");
ctdp_level.core_cpumask_size =
alloc_cpu_set(&ctdp_level.core_cpumask);
ret = isst_get_coremask_info(id, tdp_level, &ctdp_level);
if (ret) {
isst_display_error_info_message(1, "Can't get coremask, online/offline option is ignored", 0, 0);
goto free_mask;
}
if (use_cgroupv2()) {
int ret;
fprintf(stderr, "Using cgroup v2 in lieu of online/offline\n");
ret = enable_cpuset_controller();
if (ret)
goto use_offline;
ret = isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, tdp_level);
if (ret)
goto use_offline;
goto free_mask;
}
use_offline:
if (ctdp_level.cpu_count) {
int i, max_cpus = get_topo_max_cpus();
for (i = 0; i < max_cpus; ++i) {
if (!is_cpu_in_power_domain(i, id))
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);
adjust_scaling_max_from_base_freq(i);
} else {
fprintf(stderr, "offline cpu %d\n", i);
set_cpu_online_offline(i, 0);
}
}
}
free_mask:
free_cpu_set(ctdp_level.core_cpumask);
}
}
static void set_tdp_level(int arg)
{
if (cmd_help) {
fprintf(stderr, "Set Config TDP level\n");
fprintf(stderr,
"\t Arguments: -l|--level : Specify tdp level\n");
fprintf(stderr,
"\t Optional Arguments: -o | online : online/offline for the tdp level\n");
fprintf(stderr,
"\t online/offline operation has limitations, refer to Linux hotplug documentation\n");
exit(0);
}
if (tdp_level == 0xff) {
isst_display_error_info_message(1, "Invalid command: specify tdp_level", 0, 0);
exit(1);
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(set_tdp_level_for_cpu, NULL,
NULL, NULL, NULL);
else
for_each_online_power_domain_in_set(set_tdp_level_for_cpu, NULL,
NULL, NULL, NULL);
isst_ctdp_display_information_end(outf);
}
static void clx_n_dump_pbf_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
void *arg3, void *arg4)
{
int ret;
ret = clx_n_config(id);
if (ret) {
isst_display_error_info_message(1, "clx_n_config failed", 0, 0);
} else {
struct isst_pkg_ctdp_level_info *ctdp_level;
struct isst_pbf_info *pbf_info;
ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
pbf_info = &ctdp_level->pbf_info;
isst_pbf_display_information(id, outf, tdp_level, pbf_info);
free_cpu_set(ctdp_level->core_cpumask);
free_cpu_set(pbf_info->core_cpumask);
}
}
static void dump_pbf_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
struct isst_pbf_info pbf_info;
int ret;
ret = isst_get_pbf_info(id, tdp_level, &pbf_info);
if (ret) {
isst_display_error_info_message(1, "Failed to get base-freq info at this level", 1, tdp_level);
isst_ctdp_display_information_end(outf);
exit(1);
} else {
isst_pbf_display_information(id, outf, tdp_level, &pbf_info);
free_cpu_set(pbf_info.core_cpumask);
}
}
static void dump_pbf_config(int arg)
{
void *fn;
if (cmd_help) {
fprintf(stderr,
"Print Intel(R) Speed Select Technology base frequency configuration for a TDP level\n");
fprintf(stderr,
"\tArguments: -l|--level : Specify tdp level\n");
exit(0);
}
if (tdp_level == 0xff) {
isst_display_error_info_message(1, "Invalid command: specify tdp_level", 0, 0);
exit(1);
}
if (!is_clx_n_platform())
fn = dump_pbf_config_for_cpu;
else
fn = clx_n_dump_pbf_config_for_cpu;
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL);
else
for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL);
isst_ctdp_display_information_end(outf);
}
static int set_clos_param(struct isst_id *id, int clos, int epp, int wt, int min, int max)
{
struct isst_clos_config clos_config;
int ret;
ret = isst_pm_get_clos(id, clos, &clos_config);
if (ret) {
isst_display_error_info_message(1, "isst_pm_get_clos failed", 0, 0);
return ret;
}
clos_config.clos_min = min;
clos_config.clos_max = max;
clos_config.epp = epp;
clos_config.clos_prop_prio = wt;
ret = isst_set_clos(id, clos, &clos_config);
if (ret) {
isst_display_error_info_message(1, "isst_set_clos failed", 0, 0);
return ret;
}
return 0;
}
static int set_cpufreq_scaling_min_max(int cpu, int max, int freq)
{
char buffer[128], freq_str[16];
int fd, ret, len;
if (max)
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
else
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu);
fd = open(buffer, O_WRONLY);
if (fd < 0)
return fd;
snprintf(freq_str, sizeof(freq_str), "%d", freq);
len = strlen(freq_str);
ret = write(fd, freq_str, len);
if (ret == -1) {
close(fd);
return ret;
}
close(fd);
return 0;
}
static int no_turbo(void)
{
return parse_int_file(0, "/sys/devices/system/cpu/intel_pstate/no_turbo");
}
static void adjust_scaling_max_from_base_freq(int cpu)
{
int base_freq, scaling_max_freq;
scaling_max_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
base_freq = get_cpufreq_base_freq(cpu);
if (scaling_max_freq < base_freq || no_turbo())
set_cpufreq_scaling_min_max(cpu, 1, base_freq);
}
static void adjust_scaling_min_from_base_freq(int cpu)
{
int base_freq, scaling_min_freq;
scaling_min_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu);
base_freq = get_cpufreq_base_freq(cpu);
if (scaling_min_freq < base_freq)
set_cpufreq_scaling_min_max(cpu, 0, base_freq);
}
static int set_clx_pbf_cpufreq_scaling_min_max(struct isst_id *id)
{
struct isst_pkg_ctdp_level_info *ctdp_level;
struct isst_pbf_info *pbf_info;
int i, freq, freq_high, freq_low;
int ret;
ret = clx_n_config(id);
if (ret) {
debug_printf("cpufreq_scaling_min_max failed for CLX");
return ret;
}
ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
pbf_info = &ctdp_level->pbf_info;
freq_high = pbf_info->p1_high * 100000;
freq_low = pbf_info->p1_low * 100000;
for (i = 0; i < get_topo_max_cpus(); ++i) {
if (!is_cpu_in_power_domain(i, id))
continue;
if (CPU_ISSET_S(i, pbf_info->core_cpumask_size,
pbf_info->core_cpumask))
freq = freq_high;
else
freq = freq_low;
set_cpufreq_scaling_min_max(i, 1, freq);
set_cpufreq_scaling_min_max(i, 0, freq);
}
return 0;
}
static int set_cpufreq_scaling_min_max_from_cpuinfo(int cpu, int cpuinfo_max, int scaling_max)
{
char buffer[128], min_freq[16];
int fd, ret, len;
if (!CPU_ISSET_S(cpu, present_cpumask_size, present_cpumask))
return -1;
if (cpuinfo_max)
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu);
else
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_min_freq", cpu);
fd = open(buffer, O_RDONLY);
if (fd < 0)
return fd;
len = read(fd, min_freq, sizeof(min_freq));
close(fd);
if (len < 0)
return len;
if (scaling_max)
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
else
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu);
fd = open(buffer, O_WRONLY);
if (fd < 0)
return fd;
min_freq[15] = '\0';
len = strlen(min_freq);
ret = write(fd, min_freq, len);
if (ret == -1) {
close(fd);
return ret;
}
close(fd);
return 0;
}
static void set_scaling_min_to_cpuinfo_max(struct isst_id *id)
{
int i;
if (id->cpu < 0)
return;
for (i = 0; i < get_topo_max_cpus(); ++i) {
if (!is_cpu_in_power_domain(i, id))
continue;
if (is_cpu_online(i) != 1)
continue;
adjust_scaling_max_from_base_freq(i);
set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 0);
adjust_scaling_min_from_base_freq(i);
}
}
static void set_scaling_min_to_cpuinfo_min(struct isst_id *id)
{
int i;
if (id->cpu < 0)
return;
for (i = 0; i < get_topo_max_cpus(); ++i) {
if (!is_cpu_in_power_domain(i, id))
continue;
if (is_cpu_online(i) != 1)
continue;
adjust_scaling_max_from_base_freq(i);
set_cpufreq_scaling_min_max_from_cpuinfo(i, 0, 0);
}
}
static void set_scaling_max_to_cpuinfo_max(struct isst_id *id)
{
int i;
for (i = 0; i < get_topo_max_cpus(); ++i) {
if (!is_cpu_in_power_domain(i, id))
continue;
set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 1);
}
}
static int set_core_priority_and_min(struct isst_id *id, int mask_size,
cpu_set_t *cpu_mask, int min_high,
int min_low)
{
int ret, i;
if (!CPU_COUNT_S(mask_size, cpu_mask))
return -1;
ret = set_clos_param(id, 0, 0, 0, min_high, 0xff);
if (ret)
return ret;
ret = set_clos_param(id, 1, 15, 15, min_low, 0xff);
if (ret)
return ret;
ret = set_clos_param(id, 2, 15, 15, min_low, 0xff);
if (ret)
return ret;
ret = set_clos_param(id, 3, 15, 15, min_low, 0xff);
if (ret)
return ret;
for (i = 0; i < get_topo_max_cpus(); ++i) {
int clos;
struct isst_id tid;
if (!is_cpu_in_power_domain(i, id))
continue;
if (CPU_ISSET_S(i, mask_size, cpu_mask))
clos = 0;
else
clos = 3;
debug_printf("Associate cpu: %d clos: %d\n", i, clos);
set_isst_id(&tid, i);
ret = isst_clos_associate(&tid, clos);
if (ret) {
isst_display_error_info_message(1, "isst_clos_associate failed", 0, 0);
return ret;
}
}
return 0;
}
static int set_pbf_core_power(struct isst_id *id)
{
struct isst_pbf_info pbf_info;
struct isst_pkg_ctdp pkg_dev;
int ret;
if (id->cpu < 0)
return 0;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret) {
debug_printf("isst_get_ctdp_levels failed");
return ret;
}
debug_printf("Current_level: %d\n", pkg_dev.current_level);
ret = isst_get_pbf_info(id, pkg_dev.current_level, &pbf_info);
if (ret) {
debug_printf("isst_get_pbf_info failed");
return ret;
}
debug_printf("p1_high: %d p1_low: %d\n", pbf_info.p1_high,
pbf_info.p1_low);
ret = set_core_priority_and_min(id, pbf_info.core_cpumask_size,
pbf_info.core_cpumask,
pbf_info.p1_high, pbf_info.p1_low);
if (ret) {
debug_printf("set_core_priority_and_min failed");
return ret;
}
ret = isst_pm_qos_config(id, 1, 1);
if (ret) {
debug_printf("isst_pm_qos_config failed");
return ret;
}
return 0;
}
static void set_pbf_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
struct isst_pkg_ctdp_level_info ctdp_level;
struct isst_pkg_ctdp pkg_dev;
int ret;
int status = *(int *)arg4;
if (is_clx_n_platform()) {
ret = 0;
if (status) {
set_clx_pbf_cpufreq_scaling_min_max(id);
} else {
set_scaling_max_to_cpuinfo_max(id);
set_scaling_min_to_cpuinfo_min(id);
}
goto disp_result;
}
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret) {
isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
goto disp_result;
}
ret = isst_get_ctdp_control(id, pkg_dev.current_level, &ctdp_level);
if (ret) {
isst_display_error_info_message(1, "Failed to get current level", 0, 0);
goto disp_result;
}
if (!ctdp_level.pbf_support) {
isst_display_error_info_message(1, "base-freq feature is not present at this level", 1, pkg_dev.current_level);
ret = -1;
goto disp_result;
}
if (auto_mode && status) {
ret = set_pbf_core_power(id);
if (ret)
goto disp_result;
}
ret = isst_set_pbf_fact_status(id, 1, status);
if (ret) {
debug_printf("isst_set_pbf_fact_status failed");
if (auto_mode)
isst_pm_qos_config(id, 0, 0);
} else {
if (auto_mode) {
if (status)
set_scaling_min_to_cpuinfo_max(id);
else
set_scaling_min_to_cpuinfo_min(id);
}
}
if (auto_mode && !status)
isst_pm_qos_config(id, 0, 1);
disp_result:
if (status)
isst_display_result(id, outf, "base-freq", "enable",
ret);
else
isst_display_result(id, outf, "base-freq", "disable",
ret);
}
static void set_pbf_enable(int arg)
{
int enable = arg;
if (cmd_help) {
if (enable) {
fprintf(stderr,
"Enable Intel Speed Select Technology base frequency feature\n");
if (is_clx_n_platform()) {
fprintf(stderr,
"\tOn this platform this command doesn't enable feature in the hardware.\n");
fprintf(stderr,
"\tIt updates the cpufreq scaling_min_freq to match cpufreq base_frequency.\n");
exit(0);
}
fprintf(stderr,
"\tOptional Arguments: -a|--auto : Use priority of cores to set core-power associations\n");
} else {
if (is_clx_n_platform()) {
fprintf(stderr,
"\tOn this platform this command doesn't disable feature in the hardware.\n");
fprintf(stderr,
"\tIt updates the cpufreq scaling_min_freq to match cpuinfo_min_freq\n");
exit(0);
}
fprintf(stderr,
"Disable Intel Speed Select Technology base frequency feature\n");
fprintf(stderr,
"\tOptional Arguments: -a|--auto : Also disable core-power associations\n");
}
exit(0);
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(set_pbf_for_cpu, NULL, NULL,
NULL, &enable);
else
for_each_online_power_domain_in_set(set_pbf_for_cpu, NULL, NULL,
NULL, &enable);
isst_ctdp_display_information_end(outf);
}
static void dump_fact_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
void *arg3, void *arg4)
{
struct isst_fact_info fact_info;
int ret;
ret = isst_get_fact_info(id, tdp_level, fact_bucket, &fact_info);
if (ret) {
isst_display_error_info_message(1, "Failed to get turbo-freq info at this level", 1, tdp_level);
isst_ctdp_display_information_end(outf);
exit(1);
} else {
isst_fact_display_information(id, outf, tdp_level, fact_bucket,
fact_avx, &fact_info);
}
}
static void dump_fact_config(int arg)
{
if (cmd_help) {
fprintf(stderr,
"Print complete Intel Speed Select Technology turbo frequency configuration for a TDP level. Other arguments are optional.\n");
fprintf(stderr,
"\tArguments: -l|--level : Specify tdp level\n");
fprintf(stderr,
"\tArguments: -b|--bucket : Bucket index to dump\n");
fprintf(stderr,
"\tArguments: -r|--trl-type : Specify trl type: sse|avx2|avx512\n");
exit(0);
}
if (tdp_level == 0xff) {
isst_display_error_info_message(1, "Invalid command: specify tdp_level\n", 0, 0);
exit(1);
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(dump_fact_config_for_cpu,
NULL, NULL, NULL, NULL);
else
for_each_online_power_domain_in_set(dump_fact_config_for_cpu, NULL,
NULL, NULL, NULL);
isst_ctdp_display_information_end(outf);
}
static void set_fact_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
struct isst_pkg_ctdp_level_info ctdp_level;
struct isst_pkg_ctdp pkg_dev;
int ret;
int status = *(int *)arg4;
if (status && no_turbo()) {
isst_display_error_info_message(1, "Turbo mode is disabled", 0, 0);
ret = -1;
goto disp_results;
}
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret) {
isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
goto disp_results;
}
ret = isst_get_ctdp_control(id, pkg_dev.current_level, &ctdp_level);
if (ret) {
isst_display_error_info_message(1, "Failed to get current level", 0, 0);
goto disp_results;
}
if (!ctdp_level.fact_support) {
isst_display_error_info_message(1, "turbo-freq feature is not present at this level", 1, pkg_dev.current_level);
ret = -1;
goto disp_results;
}
if (status) {
ret = isst_pm_qos_config(id, 1, 1);
if (ret)
goto disp_results;
}
ret = isst_set_pbf_fact_status(id, 0, status);
if (ret) {
debug_printf("isst_set_pbf_fact_status failed");
if (auto_mode)
isst_pm_qos_config(id, 0, 0);
goto disp_results;
}
/* Set TRL */
if (status) {
struct isst_pkg_ctdp pkg_dev;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (!ret && id->cpu >= 0)
ret = isst_set_trl(id, fact_trl);
if (ret && auto_mode)
isst_pm_qos_config(id, 0, 0);
} else {
if (auto_mode)
isst_pm_qos_config(id, 0, 0);
}
disp_results:
if (status) {
isst_display_result(id, outf, "turbo-freq", "enable", ret);
if (ret)
fact_enable_fail = ret;
} else {
/* Since we modified TRL during Fact enable, restore it */
isst_set_trl_from_current_tdp(id, fact_trl);
isst_display_result(id, outf, "turbo-freq", "disable", ret);
}
}
static void set_fact_enable(int arg)
{
int i, ret, enable = arg;
struct isst_id id;
if (cmd_help) {
if (enable) {
fprintf(stderr,
"Enable Intel Speed Select Technology Turbo frequency feature\n");
fprintf(stderr,
"Optional: -t|--trl : Specify turbo ratio limit\n");
fprintf(stderr,
"\tOptional Arguments: -a|--auto : Designate specified target CPUs with");
fprintf(stderr,
"-C|--cpu option as as high priority using core-power feature\n");
} else {
fprintf(stderr,
"Disable Intel Speed Select Technology turbo frequency feature\n");
fprintf(stderr,
"Optional: -t|--trl : Specify turbo ratio limit\n");
fprintf(stderr,
"\tOptional Arguments: -a|--auto : Also disable core-power associations\n");
}
exit(0);
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(set_fact_for_cpu, NULL, NULL,
NULL, &enable);
else
for_each_online_power_domain_in_set(set_fact_for_cpu, NULL, NULL,
NULL, &enable);
if (!fact_enable_fail && enable && auto_mode) {
/*
* When we adjust CLOS param, we have to set for siblings also.
* So for the each user specified CPU, also add the sibling
* in the present_cpu_mask.
*/
for (i = 0; i < get_topo_max_cpus(); ++i) {
char buffer[128], sibling_list[128], *cpu_str;
int fd, len;
if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask))
continue;
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", i);
fd = open(buffer, O_RDONLY);
if (fd < 0)
continue;
len = read(fd, sibling_list, sizeof(sibling_list));
close(fd);
if (len < 0)
continue;
sibling_list[127] = '\0';
cpu_str = strtok(sibling_list, ",");
while (cpu_str != NULL) {
int cpu;
sscanf(cpu_str, "%d", &cpu);
CPU_SET_S(cpu, target_cpumask_size, target_cpumask);
cpu_str = strtok(NULL, ",");
}
}
for (i = 0; i < get_topo_max_cpus(); ++i) {
int clos;
if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
continue;
if (is_cpu_online(i) != 1)
continue;
set_isst_id(&id, i);
ret = set_clos_param(&id, 0, 0, 0, 0, 0xff);
if (ret)
goto error_disp;
ret = set_clos_param(&id, 1, 15, 15, 0, 0xff);
if (ret)
goto error_disp;
ret = set_clos_param(&id, 2, 15, 15, 0, 0xff);
if (ret)
goto error_disp;
ret = set_clos_param(&id, 3, 15, 15, 0, 0xff);
if (ret)
goto error_disp;
if (CPU_ISSET_S(i, target_cpumask_size, target_cpumask))
clos = 0;
else
clos = 3;
debug_printf("Associate cpu: %d clos: %d\n", i, clos);
ret = isst_clos_associate(&id, clos);
if (ret)
goto error_disp;
}
set_isst_id(&id, -1);
isst_display_result(&id, outf, "turbo-freq --auto", "enable", 0);
}
isst_ctdp_display_information_end(outf);
return;
error_disp:
isst_display_result(&id, outf, "turbo-freq --auto", "enable", ret);
isst_ctdp_display_information_end(outf);
}
static void enable_clos_qos_config(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
int ret;
int status = *(int *)arg4;
if (is_skx_based_platform())
clos_priority_type = 1;
ret = isst_pm_qos_config(id, status, clos_priority_type);
if (ret)
isst_display_error_info_message(1, "isst_pm_qos_config failed", 0, 0);
if (status)
isst_display_result(id, outf, "core-power", "enable",
ret);
else
isst_display_result(id, outf, "core-power", "disable",
ret);
}
static void set_clos_enable(int arg)
{
int enable = arg;
if (cmd_help) {
if (enable) {
fprintf(stderr,
"Enable core-power for a package/die\n");
if (!is_skx_based_platform()) {
fprintf(stderr,
"\tClos Enable: Specify priority type with [--priority|-p]\n");
fprintf(stderr, "\t\t 0: Proportional, 1: Ordered\n");
}
} else {
fprintf(stderr,
"Disable core-power: [No command arguments are required]\n");
}
exit(0);
}
if (enable && cpufreq_sysfs_present()) {
fprintf(stderr,
"cpufreq subsystem and core-power enable will interfere with each other!\n");
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(enable_clos_qos_config, NULL,
NULL, NULL, &enable);
else
for_each_online_power_domain_in_set(enable_clos_qos_config, NULL,
NULL, NULL, &enable);
isst_ctdp_display_information_end(outf);
}
static void dump_clos_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
void *arg3, void *arg4)
{
struct isst_clos_config clos_config;
int ret;
ret = isst_pm_get_clos(id, current_clos, &clos_config);
if (ret)
isst_display_error_info_message(1, "isst_pm_get_clos failed", 0, 0);
else
isst_clos_display_information(id, outf, current_clos,
&clos_config);
}
static void dump_clos_config(int arg)
{
if (cmd_help) {
fprintf(stderr,
"Print Intel Speed Select Technology core power configuration\n");
fprintf(stderr,
"\tArguments: [-c | --clos]: Specify clos id\n");
exit(0);
}
if (current_clos < 0 || current_clos > 3) {
isst_display_error_info_message(1, "Invalid clos id\n", 0, 0);
isst_ctdp_display_information_end(outf);
exit(0);
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(dump_clos_config_for_cpu,
NULL, NULL, NULL, NULL);
else
for_each_online_power_domain_in_set(dump_clos_config_for_cpu, NULL,
NULL, NULL, NULL);
isst_ctdp_display_information_end(outf);
}
static void get_clos_info_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
int enable, ret, prio_type;
ret = isst_clos_get_clos_information(id, &enable, &prio_type);
if (ret)
isst_display_error_info_message(1, "isst_clos_get_info failed", 0, 0);
else {
int cp_state, cp_cap;
isst_read_pm_config(id, &cp_state, &cp_cap);
isst_clos_display_clos_information(id, outf, enable, prio_type,
cp_state, cp_cap);
}
}
static void dump_clos_info(int arg)
{
if (cmd_help) {
fprintf(stderr,
"Print Intel Speed Select Technology core power information\n");
fprintf(stderr, "\t Optionally specify targeted cpu id with [--cpu|-c]\n");
exit(0);
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(get_clos_info_for_cpu, NULL,
NULL, NULL, NULL);
else
for_each_online_power_domain_in_set(get_clos_info_for_cpu, NULL,
NULL, NULL, NULL);
isst_ctdp_display_information_end(outf);
}
static void set_clos_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
struct isst_clos_config clos_config;
int ret;
if (id->cpu < 0)
return;
clos_config.epp = clos_epp;
clos_config.clos_prop_prio = clos_prop_prio;
clos_config.clos_min = clos_min;
clos_config.clos_max = clos_max;
clos_config.clos_desired = clos_desired;
ret = isst_set_clos(id, current_clos, &clos_config);
if (ret)
isst_display_error_info_message(1, "isst_set_clos failed", 0, 0);
else
isst_display_result(id, outf, "core-power", "config", ret);
}
static void set_clos_config(int arg)
{
if (cmd_help) {
fprintf(stderr,
"Set core-power configuration for one of the four clos ids\n");
fprintf(stderr,
"\tSpecify targeted clos id with [--clos|-c]\n");
if (!is_skx_based_platform()) {
fprintf(stderr, "\tSpecify clos EPP with [--epp|-e]\n");
fprintf(stderr,
"\tSpecify clos Proportional Priority [--weight|-w]\n");
}
fprintf(stderr, "\tSpecify clos min in MHz with [--min|-n]\n");
fprintf(stderr, "\tSpecify clos max in MHz with [--max|-m]\n");
exit(0);
}
if (current_clos < 0 || current_clos > 3) {
isst_display_error_info_message(1, "Invalid clos id\n", 0, 0);
exit(0);
}
if (!is_skx_based_platform() && (clos_epp < 0 || clos_epp > 0x0F)) {
fprintf(stderr, "clos epp is not specified or invalid, default: 0\n");
clos_epp = 0;
}
if (!is_skx_based_platform() && (clos_prop_prio < 0 || clos_prop_prio > 0x0F)) {
fprintf(stderr,
"clos frequency weight is not specified or invalid, default: 0\n");
clos_prop_prio = 0;
}
if (clos_min < 0) {
fprintf(stderr, "clos min is not specified, default: 0\n");
clos_min = 0;
}
if (clos_max < 0) {
fprintf(stderr, "clos max is not specified, default: Max frequency (ratio 0xff)\n");
clos_max = 0xff;
}
if (clos_desired) {
fprintf(stderr, "clos desired is not supported on this platform\n");
clos_desired = 0x00;
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(set_clos_config_for_cpu, NULL,
NULL, NULL, NULL);
else
for_each_online_power_domain_in_set(set_clos_config_for_cpu, NULL,
NULL, NULL, NULL);
isst_ctdp_display_information_end(outf);
}
static void set_clos_assoc_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
int ret;
ret = isst_clos_associate(id, current_clos);
if (ret)
debug_printf("isst_clos_associate failed");
else
isst_display_result(id, outf, "core-power", "assoc", ret);
}
static void set_clos_assoc(int arg)
{
if (cmd_help) {
fprintf(stderr, "Associate a clos id to a CPU\n");
fprintf(stderr,
"\tSpecify targeted clos id with [--clos|-c]\n");
fprintf(stderr,
"\tFor example to associate clos 1 to CPU 0: issue\n");
fprintf(stderr,
"\tintel-speed-select --cpu 0 core-power assoc --clos 1\n");
exit(0);
}
if (current_clos < 0 || current_clos > 3) {
isst_display_error_info_message(1, "Invalid clos id\n", 0, 0);
exit(0);
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(set_clos_assoc_for_cpu, NULL,
NULL, NULL, NULL);
else {
isst_display_error_info_message(1, "Invalid target cpu. Specify with [-c|--cpu]", 0, 0);
}
isst_ctdp_display_information_end(outf);
}
static void get_clos_assoc_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
int clos, ret;
ret = isst_clos_get_assoc_status(id, &clos);
if (ret)
isst_display_error_info_message(1, "isst_clos_get_assoc_status failed", 0, 0);
else
isst_clos_display_assoc_information(id, outf, clos);
}
static void get_clos_assoc(int arg)
{
if (cmd_help) {
fprintf(stderr, "Get associate clos id to a CPU\n");
fprintf(stderr, "\tSpecify targeted cpu id with [--cpu|-c]\n");
exit(0);
}
if (!max_target_cpus) {
isst_display_error_info_message(1, "Invalid target cpu. Specify with [-c|--cpu]", 0, 0);
exit(0);
}
isst_ctdp_display_information_start(outf);
for_each_online_target_cpu_in_set(get_clos_assoc_for_cpu, NULL,
NULL, NULL, NULL);
isst_ctdp_display_information_end(outf);
}
static void set_turbo_mode_for_cpu(struct isst_id *id, int status)
{
int base_freq;
if (status) {
base_freq = get_cpufreq_base_freq(id->cpu);
set_cpufreq_scaling_min_max(id->cpu, 1, base_freq);
} else {
set_scaling_max_to_cpuinfo_max(id);
}
if (status) {
isst_display_result(id, outf, "turbo-mode", "enable", 0);
} else {
isst_display_result(id, outf, "turbo-mode", "disable", 0);
}
}
static void set_turbo_mode(int arg)
{
int i, enable = arg;
struct isst_id id;
if (cmd_help) {
if (enable)
fprintf(stderr, "Set turbo mode enable\n");
else
fprintf(stderr, "Set turbo mode disable\n");
exit(0);
}
isst_ctdp_display_information_start(outf);
for (i = 0; i < topo_max_cpus; ++i) {
int online;
if (i)
online = parse_int_file(
1, "/sys/devices/system/cpu/cpu%d/online", i);
else
online =
1; /* online entry for CPU 0 needs some special configs */
if (online) {
set_isst_id(&id, i);
set_turbo_mode_for_cpu(&id, enable);
}
}
isst_ctdp_display_information_end(outf);
}
static void get_set_trl(struct isst_id *id, void *arg1, void *arg2, void *arg3,
void *arg4)
{
unsigned long long trl;
int set = *(int *)arg4;
int ret;
if (set && !fact_trl) {
isst_display_error_info_message(1, "Invalid TRL. Specify with [-t|--trl]", 0, 0);
exit(0);
}
if (set) {
ret = isst_set_trl(id, fact_trl);
isst_display_result(id, outf, "turbo-mode", "set-trl", ret);
return;
}
ret = isst_get_trl(id, &trl);
if (ret)
isst_display_result(id, outf, "turbo-mode", "get-trl", ret);
else
isst_trl_display_information(id, outf, trl);
}
static void process_trl(int arg)
{
if (cmd_help) {
if (arg) {
fprintf(stderr, "Set TRL (turbo ratio limits)\n");
fprintf(stderr, "\t t|--trl: Specify turbo ratio limit for setting TRL\n");
} else {
fprintf(stderr, "Get TRL (turbo ratio limits)\n");
}
exit(0);
}
isst_ctdp_display_information_start(outf);
if (max_target_cpus)
for_each_online_target_cpu_in_set(get_set_trl, NULL,
NULL, NULL, &arg);
else
for_each_online_power_domain_in_set(get_set_trl, NULL,
NULL, NULL, &arg);
isst_ctdp_display_information_end(outf);
}
static struct process_cmd_struct clx_n_cmds[] = {
{ "perf-profile", "info", dump_isst_config, 0 },
{ "base-freq", "info", dump_pbf_config, 0 },
{ "base-freq", "enable", set_pbf_enable, 1 },
{ "base-freq", "disable", set_pbf_enable, 0 },
{ NULL, NULL, NULL, 0 }
};
static struct process_cmd_struct isst_cmds[] = {
{ "perf-profile", "get-lock-status", get_tdp_locked, 0 },
{ "perf-profile", "get-config-levels", get_tdp_levels, 0 },
{ "perf-profile", "get-config-version", get_tdp_version, 0 },
{ "perf-profile", "get-config-enabled", get_tdp_enabled, 0 },
{ "perf-profile", "get-config-current-level", get_tdp_current_level,
0 },
{ "perf-profile", "set-config-level", set_tdp_level, 0 },
{ "perf-profile", "info", dump_isst_config, 0 },
{ "base-freq", "info", dump_pbf_config, 0 },
{ "base-freq", "enable", set_pbf_enable, 1 },
{ "base-freq", "disable", set_pbf_enable, 0 },
{ "turbo-freq", "info", dump_fact_config, 0 },
{ "turbo-freq", "enable", set_fact_enable, 1 },
{ "turbo-freq", "disable", set_fact_enable, 0 },
{ "core-power", "info", dump_clos_info, 0 },
{ "core-power", "enable", set_clos_enable, 1 },
{ "core-power", "disable", set_clos_enable, 0 },
{ "core-power", "config", set_clos_config, 0 },
{ "core-power", "get-config", dump_clos_config, 0 },
{ "core-power", "assoc", set_clos_assoc, 0 },
{ "core-power", "get-assoc", get_clos_assoc, 0 },
{ "turbo-mode", "enable", set_turbo_mode, 0 },
{ "turbo-mode", "disable", set_turbo_mode, 1 },
{ "turbo-mode", "get-trl", process_trl, 0 },
{ "turbo-mode", "set-trl", process_trl, 1 },
{ NULL, NULL, NULL }
};
/*
* parse cpuset with following syntax
* 1,2,4..6,8-10 and set bits in cpu_subset
*/
void parse_cpu_command(char *optarg)
{
unsigned int start, end;
char *next;
next = optarg;
while (next && *next) {
if (*next == '-') /* no negative cpu numbers */
goto error;
start = strtoul(next, &next, 10);
if (max_target_cpus < MAX_CPUS_IN_ONE_REQ)
target_cpus[max_target_cpus++] = start;
if (*next == '\0')
break;
if (*next == ',') {
next += 1;
continue;
}
if (*next == '-') {
next += 1; /* start range */
} else if (*next == '.') {
next += 1;
if (*next == '.')
next += 1; /* start range */
else
goto error;
}
end = strtoul(next, &next, 10);
if (end <= start)
goto error;
while (++start <= end) {
if (max_target_cpus < MAX_CPUS_IN_ONE_REQ)
target_cpus[max_target_cpus++] = start;
}
if (*next == ',')
next += 1;
else if (*next != '\0')
goto error;
}
#ifdef DEBUG
{
int i;
for (i = 0; i < max_target_cpus; ++i)
printf("cpu [%d] in arg\n", target_cpus[i]);
}
#endif
return;
error:
fprintf(stderr, "\"--cpu %s\" malformed\n", optarg);
exit(-1);
}
static void parse_cmd_args(int argc, int start, char **argv)
{
int opt;
int option_index;
static struct option long_options[] = {
{ "bucket", required_argument, 0, 'b' },
{ "level", required_argument, 0, 'l' },
{ "online", required_argument, 0, 'o' },
{ "trl-type", required_argument, 0, 'r' },
{ "trl", required_argument, 0, 't' },
{ "help", no_argument, 0, 'h' },
{ "clos", required_argument, 0, 'c' },
{ "desired", required_argument, 0, 'd' },
{ "epp", required_argument, 0, 'e' },
{ "min", required_argument, 0, 'n' },
{ "max", required_argument, 0, 'm' },
{ "priority", required_argument, 0, 'p' },
{ "weight", required_argument, 0, 'w' },
{ "auto", no_argument, 0, 'a' },
{ 0, 0, 0, 0 }
};
option_index = start;
optind = start + 1;
while ((opt = getopt_long(argc, argv, "b:l:t:c:d:e:n:m:p:w:r:hoa",
long_options, &option_index)) != -1) {
switch (opt) {
case 'a':
auto_mode = 1;
break;
case 'b':
fact_bucket = atoi(optarg);
break;
case 'h':
cmd_help = 1;
break;
case 'l':
tdp_level = atoi(optarg);
break;
case 'o':
force_online_offline = 1;
break;
case 't':
sscanf(optarg, "0x%llx", &fact_trl);
break;
case 'r':
if (!strncmp(optarg, "sse", 3)) {
fact_avx = 0x01;
} else if (!strncmp(optarg, "avx2", 4)) {
fact_avx = 0x02;
} else if (!strncmp(optarg, "avx512", 6)) {
fact_avx = 0x04;
} else {
fprintf(outf, "Invalid sse,avx options\n");
exit(1);
}
break;
/* CLOS related */
case 'c':
current_clos = atoi(optarg);
break;
case 'd':
clos_desired = atoi(optarg);
clos_desired /= isst_get_disp_freq_multiplier();
break;
case 'e':
clos_epp = atoi(optarg);
if (is_skx_based_platform()) {
isst_display_error_info_message(1, "epp can't be specified on this platform", 0, 0);
exit(0);
}
break;
case 'n':
clos_min = atoi(optarg);
clos_min /= isst_get_disp_freq_multiplier();
break;
case 'm':
clos_max = atoi(optarg);
clos_max /= isst_get_disp_freq_multiplier();
break;
case 'p':
clos_priority_type = atoi(optarg);
if (is_skx_based_platform() && !clos_priority_type) {
isst_display_error_info_message(1, "Invalid clos priority type: proportional for this platform", 0, 0);
exit(0);
}
break;
case 'w':
clos_prop_prio = atoi(optarg);
if (is_skx_based_platform()) {
isst_display_error_info_message(1, "weight can't be specified on this platform", 0, 0);
exit(0);
}
break;
default:
printf("Unknown option: ignore\n");
}
}
if (argv[optind])
printf("Garbage at the end of command: ignore\n");
}
static void isst_help(void)
{
printf("perf-profile:\tAn architectural mechanism that allows multiple optimized \n\
performance profiles per system via static and/or dynamic\n\
adjustment of core count, workload, Tjmax, and\n\
TDP, etc.\n");
printf("\nCommands : For feature=perf-profile\n");
printf("\tinfo\n");
if (!is_clx_n_platform()) {
printf("\tget-lock-status\n");
printf("\tget-config-levels\n");
printf("\tget-config-version\n");
printf("\tget-config-enabled\n");
printf("\tget-config-current-level\n");
printf("\tset-config-level\n");
}
}
static void pbf_help(void)
{
printf("base-freq:\tEnables users to increase guaranteed base frequency\n\
on certain cores (high priority cores) in exchange for lower\n\
base frequency on remaining cores (low priority cores).\n");
printf("\tcommand : info\n");
printf("\tcommand : enable\n");
printf("\tcommand : disable\n");
}
static void fact_help(void)
{
printf("turbo-freq:\tEnables the ability to set different turbo ratio\n\
limits to cores based on priority.\n");
printf("\nCommand: For feature=turbo-freq\n");
printf("\tcommand : info\n");
printf("\tcommand : enable\n");
printf("\tcommand : disable\n");
}
static void turbo_mode_help(void)
{
printf("turbo-mode:\tEnables users to enable/disable turbo mode by adjusting frequency settings. Also allows to get and set turbo ratio limits (TRL).\n");
printf("\tcommand : enable\n");
printf("\tcommand : disable\n");
printf("\tcommand : get-trl\n");
printf("\tcommand : set-trl\n");
}
static void core_power_help(void)
{
printf("core-power:\tInterface that allows user to define per core/tile\n\
priority.\n");
printf("\nCommands : For feature=core-power\n");
printf("\tinfo\n");
printf("\tenable\n");
printf("\tdisable\n");
printf("\tconfig\n");
printf("\tget-config\n");
printf("\tassoc\n");
printf("\tget-assoc\n");
}
struct process_cmd_help_struct {
char *feature;
void (*process_fn)(void);
};
static struct process_cmd_help_struct isst_help_cmds[] = {
{ "perf-profile", isst_help },
{ "base-freq", pbf_help },
{ "turbo-freq", fact_help },
{ "core-power", core_power_help },
{ "turbo-mode", turbo_mode_help },
{ NULL, NULL }
};
static struct process_cmd_help_struct clx_n_help_cmds[] = {
{ "perf-profile", isst_help },
{ "base-freq", pbf_help },
{ NULL, NULL }
};
void process_command(int argc, char **argv,
struct process_cmd_help_struct *help_cmds,
struct process_cmd_struct *cmds)
{
int i = 0, matched = 0;
char *feature = argv[optind];
char *cmd = argv[optind + 1];
if (!feature || !cmd)
return;
debug_printf("feature name [%s] command [%s]\n", feature, cmd);
if (!strcmp(cmd, "-h") || !strcmp(cmd, "--help")) {
while (help_cmds[i].feature) {
if (!strcmp(help_cmds[i].feature, feature)) {
help_cmds[i].process_fn();
exit(0);
}
++i;
}
}
i = 0;
while (cmds[i].feature) {
if (!strcmp(cmds[i].feature, feature) &&
!strcmp(cmds[i].command, cmd)) {
parse_cmd_args(argc, optind + 1, argv);
cmds[i].process_fn(cmds[i].arg);
matched = 1;
break;
}
++i;
}
if (!matched)
fprintf(stderr, "Invalid command\n");
}
static void usage(void)
{
if (is_clx_n_platform()) {
fprintf(stderr, "\nThere is limited support of Intel Speed Select features on this platform.\n");
fprintf(stderr, "Everything is pre-configured using BIOS options, this tool can't enable any feature in the hardware.\n\n");
}
printf("\nUsage:\n");
printf("intel-speed-select [OPTIONS] FEATURE COMMAND COMMAND_ARGUMENTS\n");
printf("\nUse this tool to enumerate and control the Intel Speed Select Technology features:\n");
if (is_clx_n_platform())
printf("\nFEATURE : [perf-profile|base-freq]\n");
else
printf("\nFEATURE : [perf-profile|base-freq|turbo-freq|core-power|turbo-mode]\n");
printf("\nFor help on each feature, use -h|--help\n");
printf("\tFor example: intel-speed-select perf-profile -h\n");
printf("\nFor additional help on each command for a feature, use --h|--help\n");
printf("\tFor example: intel-speed-select perf-profile get-lock-status -h\n");
printf("\t\t This will print help for the command \"get-lock-status\" for the feature \"perf-profile\"\n");
printf("\nOPTIONS\n");
printf("\t[-c|--cpu] : logical cpu number\n");
printf("\t\tDefault: Die scoped for all dies in the system with multiple dies/package\n");
printf("\t\t\t Or Package scoped for all Packages when each package contains one die\n");
printf("\t[-d|--debug] : Debug mode\n");
printf("\t[-f|--format] : output format [json|text]. Default: text\n");
printf("\t[-h|--help] : Print help\n");
printf("\t[-i|--info] : Print platform information\n");
printf("\t[-a|--all-cpus-online] : Force online every CPU in the system\n");
printf("\t[-o|--out] : Output file\n");
printf("\t\t\tDefault : stderr\n");
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("\t[-g|--cgroupv2 : Try to use cgroup v2 CPU isolation instead of CPU online/offline.\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");
printf("\t\tPackage, Die, CPU, and command specific results.\n");
printf("\nExamples\n");
printf("\tTo get platform information:\n");
printf("\t\tintel-speed-select --info\n");
printf("\tTo get full perf-profile information dump:\n");
printf("\t\tintel-speed-select perf-profile info\n");
printf("\tTo get full base-freq information dump:\n");
printf("\t\tintel-speed-select base-freq info -l 0\n");
if (!is_clx_n_platform()) {
printf("\tTo get full turbo-freq information dump:\n");
printf("\t\tintel-speed-select turbo-freq info -l 0\n");
}
exit(1);
}
static void print_version(void)
{
fprintf(outf, "Version %s\n", version_str);
exit(0);
}
static void cmdline(int argc, char **argv)
{
const char *pathname = "/dev/isst_interface";
char *ptr;
FILE *fp;
int opt, force_cpus_online = 0;
int option_index = 0;
int ret;
int oob_mode = 0;
int poll_interval = -1;
int no_daemon = 0;
int mbox_delay = 0, mbox_retries = 3;
static struct option long_options[] = {
{ "all-cpus-online", no_argument, 0, 'a' },
{ "cpu", required_argument, 0, 'c' },
{ "debug", no_argument, 0, 'd' },
{ "format", required_argument, 0, 'f' },
{ "help", no_argument, 0, 'h' },
{ "info", no_argument, 0, 'i' },
{ "pause", required_argument, 0, 'p' },
{ "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' },
{ "cgroupv2", required_argument, 0, 'g' },
{ 0, 0, 0, 0 }
};
if (geteuid() != 0) {
fprintf(stderr, "Must run as root\n");
exit(0);
}
ret = update_cpu_model();
if (ret)
err(-1, "Invalid CPU model (%d)\n", cpu_model);
printf("Intel(R) Speed Select Technology\n");
printf("Executing on CPU model:%d[0x%x]\n", cpu_model, cpu_model);
if (!is_clx_n_platform()) {
fp = fopen(pathname, "rb");
if (!fp) {
fprintf(stderr, "Intel speed select drivers are not loaded on this system.\n");
fprintf(stderr, "Verify that kernel config includes CONFIG_INTEL_SPEED_SELECT_INTERFACE.\n");
fprintf(stderr, "If the config is included then this is not a supported platform.\n");
exit(0);
}
fclose(fp);
}
ret = isst_fill_platform_info();
if (ret)
goto out;
progname = argv[0];
while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:ng", long_options,
&option_index)) != -1) {
switch (opt) {
case 'a':
force_cpus_online = 1;
break;
case 'c':
parse_cpu_command(optarg);
break;
case 'd':
debug_flag = 1;
printf("Debug Mode ON\n");
break;
case 'f':
if (!strncmp(optarg, "json", 4))
out_format_json = 1;
break;
case 'h':
usage();
break;
case 'i':
isst_print_platform_information();
break;
case 'o':
if (outf)
fclose(outf);
outf = fopen_or_exit(optarg, "w");
break;
case 'p':
ret = strtol(optarg, &ptr, 10);
if (!ret)
fprintf(stderr, "Invalid pause interval, ignore\n");
else
mbox_delay = ret;
break;
case 'r':
ret = strtol(optarg, &ptr, 10);
if (!ret)
fprintf(stderr, "Invalid retry count, ignore\n");
else
mbox_retries = ret;
break;
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;
case 'g':
cgroupv2 = 1;
break;
default:
usage();
}
}
if (optind > (argc - 2) && !oob_mode) {
usage();
exit(0);
}
isst_update_platform_param(ISST_PARAM_MBOX_DELAY, mbox_delay);
isst_update_platform_param(ISST_PARAM_MBOX_RETRIES, mbox_retries);
set_max_cpu_num();
if (force_cpus_online)
force_all_cpus_online();
store_cpu_topology();
create_cpu_map();
if (oob_mode) {
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()) {
process_command(argc, argv, isst_help_cmds, isst_cmds);
} else {
process_command(argc, argv, clx_n_help_cmds, clx_n_cmds);
}
out:
free_cpu_set(present_cpumask);
free_cpu_set(target_cpumask);
}
int main(int argc, char **argv)
{
outf = stderr;
cmdline(argc, argv);
return 0;
}