linux/tools/power/x86/intel-speed-select/isst-display.c
Srinivas Pandruvada 3fb4f7cd47 tools/power/x86: A tool to validate Intel Speed Select commands
The Intel(R) Speed select technologies contains four features.

Performance profile:An non architectural mechanism that allows multiple
optimized performance profiles per system via static and/or dynamic
adjustment of core count, workload, Tjmax, and TDP, etc. aka ISS
in the documentation.

Base Frequency: Enables users to increase guaranteed base frequency on
certain cores (high priority cores) in exchange for lower base frequency
on remaining cores (low priority cores). aka PBF in the documenation.

Turbo frequency: Enables the ability to set different turbo ratio limits
to cores based on priority. aka FACT in the documentation.

Core power: An Interface that allows user to define per core/tile
priority.

There is a multi level help for commands and options. This can be used
to check required arguments for each feature and commands for the
feature.

To start navigating the features start with

$sudo intel-speed-select --help

For help on a specific feature for example
$sudo intel-speed-select perf-profile --help

To get help for a command for a feature for example
$sudo intel-speed-select perf-profile get-lock-status --help

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Acked-by: Len Brown <len.brown@intel.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
2019-07-03 15:37:09 +03:00

480 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Intel dynamic_speed_select -- Enumerate and control features
* Copyright (c) 2019 Intel Corporation.
*/
#include "isst.h"
#define DISP_FREQ_MULTIPLIER 100000
static void printcpumask(int str_len, char *str, int mask_size,
cpu_set_t *cpu_mask)
{
int i, max_cpus = get_topo_max_cpus();
unsigned int *mask;
int size, index, curr_index;
size = max_cpus / (sizeof(unsigned int) * 8);
if (max_cpus % (sizeof(unsigned int) * 8))
size++;
mask = calloc(size, sizeof(unsigned int));
if (!mask)
return;
for (i = 0; i < max_cpus; ++i) {
int mask_index, bit_index;
if (!CPU_ISSET_S(i, mask_size, cpu_mask))
continue;
mask_index = i / (sizeof(unsigned int) * 8);
bit_index = i % (sizeof(unsigned int) * 8);
mask[mask_index] |= BIT(bit_index);
}
curr_index = 0;
for (i = size - 1; i >= 0; --i) {
index = snprintf(&str[curr_index], str_len - curr_index, "%08x",
mask[i]);
curr_index += index;
if (i) {
strncat(&str[curr_index], ",", str_len - curr_index);
curr_index++;
}
}
free(mask);
}
static void format_and_print_txt(FILE *outf, int level, char *header,
char *value)
{
char *spaces = " ";
static char delimiters[256];
int i, j = 0;
if (!level)
return;
if (level == 1) {
strcpy(delimiters, " ");
} else {
for (i = 0; i < level - 1; ++i)
j += snprintf(&delimiters[j], sizeof(delimiters) - j,
"%s", spaces);
}
if (header && value) {
fprintf(outf, "%s", delimiters);
fprintf(outf, "%s:%s\n", header, value);
} else if (header) {
fprintf(outf, "%s", delimiters);
fprintf(outf, "%s\n", header);
}
}
static int last_level;
static void format_and_print(FILE *outf, int level, char *header, char *value)
{
char *spaces = " ";
static char delimiters[256];
int i;
if (!out_format_is_json()) {
format_and_print_txt(outf, level, header, value);
return;
}
if (level == 0) {
if (header)
fprintf(outf, "{");
else
fprintf(outf, "\n}\n");
} else {
int j = 0;
for (i = 0; i < level; ++i)
j += snprintf(&delimiters[j], sizeof(delimiters) - j,
"%s", spaces);
if (last_level == level)
fprintf(outf, ",\n");
if (value) {
if (last_level != level)
fprintf(outf, "\n");
fprintf(outf, "%s\"%s\": ", delimiters, header);
fprintf(outf, "\"%s\"", value);
} else {
for (i = last_level - 1; i >= level; --i) {
int k = 0;
for (j = i; j > 0; --j)
k += snprintf(&delimiters[k],
sizeof(delimiters) - k,
"%s", spaces);
if (i == level && header)
fprintf(outf, "\n%s},", delimiters);
else
fprintf(outf, "\n%s}", delimiters);
}
if (abs(last_level - level) < 3)
fprintf(outf, "\n");
if (header)
fprintf(outf, "%s\"%s\": {", delimiters,
header);
}
}
last_level = level;
}
static void print_packag_info(int cpu, FILE *outf)
{
char header[256];
snprintf(header, sizeof(header), "package-%d",
get_physical_package_id(cpu));
format_and_print(outf, 1, header, NULL);
snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
format_and_print(outf, 2, header, NULL);
snprintf(header, sizeof(header), "cpu-%d", cpu);
format_and_print(outf, 3, header, NULL);
}
static void _isst_pbf_display_information(int cpu, FILE *outf, int level,
struct isst_pbf_info *pbf_info,
int disp_level)
{
char header[256];
char value[256];
snprintf(header, sizeof(header), "speed-select-base-freq");
format_and_print(outf, disp_level, header, NULL);
snprintf(header, sizeof(header), "high-priority-base-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
pbf_info->p1_high * DISP_FREQ_MULTIPLIER);
format_and_print(outf, disp_level + 1, header, value);
snprintf(header, sizeof(header), "high-priority-cpu-mask");
printcpumask(sizeof(value), value, pbf_info->core_cpumask_size,
pbf_info->core_cpumask);
format_and_print(outf, disp_level + 1, header, value);
snprintf(header, sizeof(header), "low-priority-base-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
pbf_info->p1_low * DISP_FREQ_MULTIPLIER);
format_and_print(outf, disp_level + 1, header, value);
snprintf(header, sizeof(header), "tjunction-temperature(C)");
snprintf(value, sizeof(value), "%d", pbf_info->t_prochot);
format_and_print(outf, disp_level + 1, header, value);
snprintf(header, sizeof(header), "thermal-design-power(W)");
snprintf(value, sizeof(value), "%d", pbf_info->tdp);
format_and_print(outf, disp_level + 1, header, value);
}
static void _isst_fact_display_information(int cpu, FILE *outf, int level,
int fact_bucket, int fact_avx,
struct isst_fact_info *fact_info,
int base_level)
{
struct isst_fact_bucket_info *bucket_info = fact_info->bucket_info;
char header[256];
char value[256];
int j;
snprintf(header, sizeof(header), "speed-select-turbo-freq");
format_and_print(outf, base_level, header, NULL);
for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) {
if (fact_bucket != 0xff && fact_bucket != j)
continue;
if (!bucket_info[j].high_priority_cores_count)
break;
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 1, header, NULL);
snprintf(header, sizeof(header), "high-priority-cores-count");
snprintf(value, sizeof(value), "%d",
bucket_info[j].high_priority_cores_count);
format_and_print(outf, base_level + 2, header, value);
if (fact_avx & 0x01) {
snprintf(header, sizeof(header),
"high-priority-max-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
bucket_info[j].sse_trl * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
}
if (fact_avx & 0x02) {
snprintf(header, sizeof(header),
"high-priority-max-avx2-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
bucket_info[j].avx_trl * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
}
if (fact_avx & 0x04) {
snprintf(header, sizeof(header),
"high-priority-max-avx512-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
bucket_info[j].avx512_trl *
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
}
}
snprintf(header, sizeof(header),
"speed-select-turbo-freq-clip-frequencies");
format_and_print(outf, base_level + 1, header, NULL);
snprintf(header, sizeof(header), "low-priority-max-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
fact_info->lp_clipping_ratio_license_sse *
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
snprintf(header, sizeof(header),
"low-priority-max-avx2-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
fact_info->lp_clipping_ratio_license_avx2 *
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
snprintf(header, sizeof(header),
"low-priority-max-avx512-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
fact_info->lp_clipping_ratio_license_avx512 *
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
}
void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
struct isst_pkg_ctdp *pkg_dev)
{
char header[256];
char value[256];
int i, base_level = 1;
print_packag_info(cpu, outf);
for (i = 0; i <= pkg_dev->levels; ++i) {
struct isst_pkg_ctdp_level_info *ctdp_level;
int j;
ctdp_level = &pkg_dev->ctdp_level[i];
if (!ctdp_level->processed)
continue;
snprintf(header, sizeof(header), "perf-profile-level-%d",
ctdp_level->level);
format_and_print(outf, base_level + 3, header, NULL);
snprintf(header, sizeof(header), "cpu-count");
j = get_cpu_count(get_physical_die_id(cpu),
get_physical_die_id(cpu));
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "enable-cpu-mask");
printcpumask(sizeof(value), value,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "thermal-design-power-ratio");
snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "base-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->tdp_ratio * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header),
"speed-select-turbo-freq-support");
snprintf(value, sizeof(value), "%d", ctdp_level->fact_support);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header),
"speed-select-base-freq-support");
snprintf(value, sizeof(value), "%d", ctdp_level->pbf_support);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header),
"speed-select-base-freq-enabled");
snprintf(value, sizeof(value), "%d", ctdp_level->pbf_enabled);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header),
"speed-select-turbo-freq-enabled");
snprintf(value, sizeof(value), "%d", ctdp_level->fact_enabled);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "thermal-design-power(W)");
snprintf(value, sizeof(value), "%d", ctdp_level->pkg_tdp);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "tjunction-max(C)");
snprintf(value, sizeof(value), "%d", ctdp_level->t_proc_hot);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "turbo-ratio-limits-sse");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header), "turbo-ratio");
snprintf(value, sizeof(value), "%d",
ctdp_level->trl_sse_active_cores[j]);
format_and_print(outf, base_level + 6, header, value);
}
snprintf(header, sizeof(header), "turbo-ratio-limits-avx");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header), "turbo-ratio");
snprintf(value, sizeof(value), "%d",
ctdp_level->trl_avx_active_cores[j]);
format_and_print(outf, base_level + 6, header, value);
}
snprintf(header, sizeof(header), "turbo-ratio-limits-avx512");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header), "turbo-ratio");
snprintf(value, sizeof(value), "%d",
ctdp_level->trl_avx_512_active_cores[j]);
format_and_print(outf, base_level + 6, header, value);
}
if (ctdp_level->pbf_support)
_isst_pbf_display_information(cpu, outf, i,
&ctdp_level->pbf_info,
base_level + 4);
if (ctdp_level->fact_support)
_isst_fact_display_information(cpu, outf, i, 0xff, 0xff,
&ctdp_level->fact_info,
base_level + 4);
}
format_and_print(outf, 1, NULL, NULL);
}
void isst_ctdp_display_information_start(FILE *outf)
{
last_level = 0;
format_and_print(outf, 0, "start", NULL);
}
void isst_ctdp_display_information_end(FILE *outf)
{
format_and_print(outf, 0, NULL, NULL);
}
void isst_pbf_display_information(int cpu, FILE *outf, int level,
struct isst_pbf_info *pbf_info)
{
print_packag_info(cpu, outf);
_isst_pbf_display_information(cpu, outf, level, pbf_info, 4);
format_and_print(outf, 1, NULL, NULL);
}
void isst_fact_display_information(int cpu, FILE *outf, int level,
int fact_bucket, int fact_avx,
struct isst_fact_info *fact_info)
{
print_packag_info(cpu, outf);
_isst_fact_display_information(cpu, outf, level, fact_bucket, fact_avx,
fact_info, 4);
format_and_print(outf, 1, NULL, NULL);
}
void isst_clos_display_information(int cpu, FILE *outf, int clos,
struct isst_clos_config *clos_config)
{
char header[256];
char value[256];
snprintf(header, sizeof(header), "package-%d",
get_physical_package_id(cpu));
format_and_print(outf, 1, header, NULL);
snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
format_and_print(outf, 2, header, NULL);
snprintf(header, sizeof(header), "cpu-%d", cpu);
format_and_print(outf, 3, header, NULL);
snprintf(header, sizeof(header), "core-power");
format_and_print(outf, 4, header, NULL);
snprintf(header, sizeof(header), "clos");
snprintf(value, sizeof(value), "%d", clos);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "epp");
snprintf(value, sizeof(value), "%d", clos_config->epp);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-proportional-priority");
snprintf(value, sizeof(value), "%d", clos_config->clos_prop_prio);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-min");
snprintf(value, sizeof(value), "%d", clos_config->clos_min);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-max");
snprintf(value, sizeof(value), "%d", clos_config->clos_max);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-desired");
snprintf(value, sizeof(value), "%d", clos_config->clos_desired);
format_and_print(outf, 5, header, value);
format_and_print(outf, 1, NULL, NULL);
}
void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd,
int result)
{
char header[256];
char value[256];
snprintf(header, sizeof(header), "package-%d",
get_physical_package_id(cpu));
format_and_print(outf, 1, header, NULL);
snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
format_and_print(outf, 2, header, NULL);
snprintf(header, sizeof(header), "cpu-%d", cpu);
format_and_print(outf, 3, header, NULL);
snprintf(header, sizeof(header), "%s", feature);
format_and_print(outf, 4, header, NULL);
snprintf(header, sizeof(header), "%s", cmd);
snprintf(value, sizeof(value), "%d", result);
format_and_print(outf, 5, header, value);
format_and_print(outf, 1, NULL, NULL);
}