Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,353 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
# -*- coding: utf-8 -*-
#
""" This utility can be used to debug and tune the performance of the
AMD P-State driver. It imports intel_pstate_tracer to analyze AMD P-State
trace event.
Prerequisites:
Python version 2.7.x or higher
gnuplot 5.0 or higher
gnuplot-py 1.8 or higher
(Most of the distributions have these required packages. They may be called
gnuplot-py, python-gnuplot or python3-gnuplot, gnuplot-nox, ... )
Kernel config for Linux trace is enabled
see print_help(): for Usage and Output details
"""
from __future__ import print_function
from datetime import datetime
import subprocess
import os
import time
import re
import signal
import sys
import getopt
import Gnuplot
from numpy import *
from decimal import *
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "intel_pstate_tracer"))
import intel_pstate_tracer as ipt
__license__ = "GPL version 2"
MAX_CPUS = 256
# Define the csv file columns
C_COMM = 15
C_ELAPSED = 14
C_SAMPLE = 13
C_DURATION = 12
C_LOAD = 11
C_TSC = 10
C_APERF = 9
C_MPERF = 8
C_FREQ = 7
C_MAX_PERF = 6
C_DES_PERF = 5
C_MIN_PERF = 4
C_USEC = 3
C_SEC = 2
C_CPU = 1
global sample_num, last_sec_cpu, last_usec_cpu, start_time, test_name, trace_file
getcontext().prec = 11
sample_num =0
last_sec_cpu = [0] * MAX_CPUS
last_usec_cpu = [0] * MAX_CPUS
def plot_per_cpu_freq(cpu_index):
""" Plot per cpu frequency """
file_name = 'cpu{:0>3}.csv'.format(cpu_index)
if os.path.exists(file_name):
output_png = "cpu%03d_frequency.png" % cpu_index
g_plot = ipt.common_gnuplot_settings()
g_plot('set output "' + output_png + '"')
g_plot('set yrange [0:7]')
g_plot('set ytics 0, 1')
g_plot('set ylabel "CPU Frequency (GHz)"')
g_plot('set title "{} : frequency : CPU {:0>3} : {:%F %H:%M}"'.format(test_name, cpu_index, datetime.now()))
g_plot('set ylabel "CPU frequency"')
g_plot('set key off')
ipt.set_4_plot_linestyles(g_plot)
g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_FREQ))
def plot_per_cpu_des_perf(cpu_index):
""" Plot per cpu desired perf """
file_name = 'cpu{:0>3}.csv'.format(cpu_index)
if os.path.exists(file_name):
output_png = "cpu%03d_des_perf.png" % cpu_index
g_plot = ipt.common_gnuplot_settings()
g_plot('set output "' + output_png + '"')
g_plot('set yrange [0:255]')
g_plot('set ylabel "des perf"')
g_plot('set title "{} : cpu des perf : CPU {:0>3} : {:%F %H:%M}"'.format(test_name, cpu_index, datetime.now()))
g_plot('set key off')
ipt.set_4_plot_linestyles(g_plot)
g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_DES_PERF))
def plot_per_cpu_load(cpu_index):
""" Plot per cpu load """
file_name = 'cpu{:0>3}.csv'.format(cpu_index)
if os.path.exists(file_name):
output_png = "cpu%03d_load.png" % cpu_index
g_plot = ipt.common_gnuplot_settings()
g_plot('set output "' + output_png + '"')
g_plot('set yrange [0:100]')
g_plot('set ytics 0, 10')
g_plot('set ylabel "CPU load (percent)"')
g_plot('set title "{} : cpu load : CPU {:0>3} : {:%F %H:%M}"'.format(test_name, cpu_index, datetime.now()))
g_plot('set key off')
ipt.set_4_plot_linestyles(g_plot)
g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_LOAD))
def plot_all_cpu_frequency():
""" Plot all cpu frequencies """
output_png = 'all_cpu_frequencies.png'
g_plot = ipt.common_gnuplot_settings()
g_plot('set output "' + output_png + '"')
g_plot('set ylabel "CPU Frequency (GHz)"')
g_plot('set title "{} : cpu frequencies : {:%F %H:%M}"'.format(test_name, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_FREQ)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def plot_all_cpu_des_perf():
""" Plot all cpu desired perf """
output_png = 'all_cpu_des_perf.png'
g_plot = ipt.common_gnuplot_settings()
g_plot('set output "' + output_png + '"')
g_plot('set ylabel "des perf"')
g_plot('set title "{} : cpu des perf : {:%F %H:%M}"'.format(test_name, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 255 ps 1 title i".format(C_ELAPSED, C_DES_PERF)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def plot_all_cpu_load():
""" Plot all cpu load """
output_png = 'all_cpu_load.png'
g_plot = ipt.common_gnuplot_settings()
g_plot('set output "' + output_png + '"')
g_plot('set yrange [0:100]')
g_plot('set ylabel "CPU load (percent)"')
g_plot('set title "{} : cpu load : {:%F %H:%M}"'.format(test_name, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 255 ps 1 title i".format(C_ELAPSED, C_LOAD)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def store_csv(cpu_int, time_pre_dec, time_post_dec, min_perf, des_perf, max_perf, freq_ghz, mperf, aperf, tsc, common_comm, load, duration_ms, sample_num, elapsed_time, cpu_mask):
""" Store master csv file information """
global graph_data_present
if cpu_mask[cpu_int] == 0:
return
try:
f_handle = open('cpu.csv', 'a')
string_buffer = "CPU_%03u, %05u, %06u, %u, %u, %u, %.4f, %u, %u, %u, %.2f, %.3f, %u, %.3f, %s\n" % (cpu_int, int(time_pre_dec), int(time_post_dec), int(min_perf), int(des_perf), int(max_perf), freq_ghz, int(mperf), int(aperf), int(tsc), load, duration_ms, sample_num, elapsed_time, common_comm)
f_handle.write(string_buffer)
f_handle.close()
except:
print('IO error cpu.csv')
return
graph_data_present = True;
def cleanup_data_files():
""" clean up existing data files """
if os.path.exists('cpu.csv'):
os.remove('cpu.csv')
f_handle = open('cpu.csv', 'a')
f_handle.write('common_cpu, common_secs, common_usecs, min_perf, des_perf, max_perf, freq, mperf, aperf, tsc, load, duration_ms, sample_num, elapsed_time, common_comm')
f_handle.write('\n')
f_handle.close()
def read_trace_data(file_name, cpu_mask):
""" Read and parse trace data """
global current_max_cpu
global sample_num, last_sec_cpu, last_usec_cpu, start_time
try:
data = open(file_name, 'r').read()
except:
print('Error opening ', file_name)
sys.exit(2)
for line in data.splitlines():
search_obj = \
re.search(r'(^(.*?)\[)((\d+)[^\]])(.*?)(\d+)([.])(\d+)(.*?amd_min_perf=)(\d+)(.*?amd_des_perf=)(\d+)(.*?amd_max_perf=)(\d+)(.*?freq=)(\d+)(.*?mperf=)(\d+)(.*?aperf=)(\d+)(.*?tsc=)(\d+)'
, line)
if search_obj:
cpu = search_obj.group(3)
cpu_int = int(cpu)
cpu = str(cpu_int)
time_pre_dec = search_obj.group(6)
time_post_dec = search_obj.group(8)
min_perf = search_obj.group(10)
des_perf = search_obj.group(12)
max_perf = search_obj.group(14)
freq = search_obj.group(16)
mperf = search_obj.group(18)
aperf = search_obj.group(20)
tsc = search_obj.group(22)
common_comm = search_obj.group(2).replace(' ', '')
if sample_num == 0 :
start_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000)
sample_num += 1
if last_sec_cpu[cpu_int] == 0 :
last_sec_cpu[cpu_int] = time_pre_dec
last_usec_cpu[cpu_int] = time_post_dec
else :
duration_us = (int(time_pre_dec) - int(last_sec_cpu[cpu_int])) * 1000000 + (int(time_post_dec) - int(last_usec_cpu[cpu_int]))
duration_ms = Decimal(duration_us) / Decimal(1000)
last_sec_cpu[cpu_int] = time_pre_dec
last_usec_cpu[cpu_int] = time_post_dec
elapsed_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) - start_time
load = Decimal(int(mperf)*100)/ Decimal(tsc)
freq_ghz = Decimal(freq)/Decimal(1000000)
store_csv(cpu_int, time_pre_dec, time_post_dec, min_perf, des_perf, max_perf, freq_ghz, mperf, aperf, tsc, common_comm, load, duration_ms, sample_num, elapsed_time, cpu_mask)
if cpu_int > current_max_cpu:
current_max_cpu = cpu_int
# Now separate the main overall csv file into per CPU csv files.
ipt.split_csv(current_max_cpu, cpu_mask)
def signal_handler(signal, frame):
print(' SIGINT: Forcing cleanup before exit.')
if interval:
ipt.disable_trace(trace_file)
ipt.clear_trace_file()
ipt.free_trace_buffer()
sys.exit(0)
trace_file = "/sys/kernel/tracing/events/amd_cpu/enable"
signal.signal(signal.SIGINT, signal_handler)
interval = ""
file_name = ""
cpu_list = ""
test_name = ""
memory = "10240"
graph_data_present = False;
valid1 = False
valid2 = False
cpu_mask = zeros((MAX_CPUS,), dtype=int)
try:
opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="])
except getopt.GetoptError:
ipt.print_help('amd_pstate')
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print()
sys.exit()
elif opt in ("-t", "--trace_file"):
valid1 = True
location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
file_name = os.path.join(location, arg)
elif opt in ("-i", "--interval"):
valid1 = True
interval = arg
elif opt in ("-c", "--cpu"):
cpu_list = arg
elif opt in ("-n", "--name"):
valid2 = True
test_name = arg
elif opt in ("-m", "--memory"):
memory = arg
if not (valid1 and valid2):
ipt.print_help('amd_pstate')
sys.exit()
if cpu_list:
for p in re.split("[,]", cpu_list):
if int(p) < MAX_CPUS :
cpu_mask[int(p)] = 1
else:
for i in range (0, MAX_CPUS):
cpu_mask[i] = 1
if not os.path.exists('results'):
os.mkdir('results')
ipt.fix_ownership('results')
os.chdir('results')
if os.path.exists(test_name):
print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.')
sys.exit()
os.mkdir(test_name)
ipt.fix_ownership(test_name)
os.chdir(test_name)
cur_version = sys.version_info
print('python version (should be >= 2.7):')
print(cur_version)
cleanup_data_files()
if interval:
file_name = "/sys/kernel/tracing/trace"
ipt.clear_trace_file()
ipt.set_trace_buffer_size(memory)
ipt.enable_trace(trace_file)
time.sleep(int(interval))
ipt.disable_trace(trace_file)
current_max_cpu = 0
read_trace_data(file_name, cpu_mask)
if interval:
ipt.clear_trace_file()
ipt.free_trace_buffer()
if graph_data_present == False:
print('No valid data to plot')
sys.exit(2)
for cpu_no in range(0, current_max_cpu + 1):
plot_per_cpu_freq(cpu_no)
plot_per_cpu_des_perf(cpu_no)
plot_per_cpu_load(cpu_no)
plot_all_cpu_des_perf()
plot_all_cpu_frequency()
plot_all_cpu_load()
for root, dirs, files in os.walk('.'):
for f in files:
ipt.fix_ownership(f)
os.chdir('../../')
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
include/
intel-speed-select
@@ -0,0 +1 @@
intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o isst-core-mbox.o isst-core-tpmi.o
@@ -0,0 +1,66 @@
# SPDX-License-Identifier: GPL-2.0
include ../../../scripts/Makefile.include
bindir ?= /usr/bin
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
# Do not use make's built-in rules
# (this improves performance and avoids hard-to-debug behaviour);
MAKEFLAGS += -r
NL3_CFLAGS = $(shell pkg-config --cflags libnl-3.0 2>/dev/null)
ifeq ($(NL3_CFLAGS),)
NL3_CFLAGS = -I/usr/include/libnl3
endif
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include $(NL3_CFLAGS)
override LDFLAGS += -lnl-genl-3 -lnl-3
ALL_TARGETS := intel-speed-select
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
all: $(ALL_PROGRAMS)
export srctree OUTPUT CC LD CFLAGS
include $(srctree)/tools/build/Makefile.include
#
# We need the following to be outside of kernel tree
#
$(OUTPUT)include/linux/isst_if.h: ../../../../include/uapi/linux/isst_if.h
mkdir -p $(OUTPUT)include/linux 2>&1 || true
ln -sf $(CURDIR)/../../../../include/uapi/linux/isst_if.h $@
$(OUTPUT)include/linux/thermal.h: ../../../../include/uapi/linux/thermal.h
mkdir -p $(OUTPUT)include/linux 2>&1 || true
ln -sf $(CURDIR)/../../../../include/uapi/linux/thermal.h $@
prepare: $(OUTPUT)include/linux/isst_if.h $(OUTPUT)include/linux/thermal.h
ISST_IN := $(OUTPUT)intel-speed-select-in.o
$(ISST_IN): prepare FORCE
$(Q)$(MAKE) $(build)=intel-speed-select
$(OUTPUT)intel-speed-select: $(ISST_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $< $(LDFLAGS) -o $@
clean:
rm -f $(ALL_PROGRAMS)
rm -rf $(OUTPUT)include/linux/isst_if.h
find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
install: $(ALL_PROGRAMS)
install -d -m 755 $(DESTDIR)$(bindir); \
for program in $(ALL_PROGRAMS); do \
install $$program $(DESTDIR)$(bindir); \
done
FORCE:
.PHONY: all install clean FORCE prepare
@@ -0,0 +1,308 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select -- Read HFI events for OOB
* Copyright (c) 2022 Intel Corporation.
*/
/*
* This file incorporates work covered by the following copyright and
* permission notice:
* WPA Supplicant - driver interaction with Linux nl80211/cfg80211
* Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of
* BSD license.
*
* Requires
* libnl-genl-3-dev
*
* For Fedora/CenOS
* dnf install libnl3-devel
* For Ubuntu
* apt install libnl-3-dev libnl-genl-3-dev
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <linux/thermal.h>
#include "isst.h"
struct hfi_event_data {
struct nl_sock *nl_handle;
struct nl_cb *nl_cb;
};
struct hfi_event_data drv;
static int ack_handler(struct nl_msg *msg, void *arg)
{
int *err = arg;
*err = 0;
return NL_STOP;
}
static int finish_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
*ret = 0;
return NL_SKIP;
}
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
int *ret = arg;
*ret = err->error;
return NL_SKIP;
}
static int seq_check_handler(struct nl_msg *msg, void *arg)
{
return NL_OK;
}
static int send_and_recv_msgs(struct hfi_event_data *drv,
struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
void *valid_data)
{
struct nl_cb *cb;
int err = -ENOMEM;
cb = nl_cb_clone(drv->nl_cb);
if (!cb)
goto out;
err = nl_send_auto_complete(drv->nl_handle, msg);
if (err < 0)
goto out;
err = 1;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
if (valid_handler)
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
valid_handler, valid_data);
while (err > 0)
nl_recvmsgs(drv->nl_handle, cb);
out:
nl_cb_put(cb);
nlmsg_free(msg);
return err;
}
struct family_data {
const char *group;
int id;
};
static int family_handler(struct nl_msg *msg, void *arg)
{
struct family_data *res = arg;
struct nlattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *mcgrp;
int i;
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[CTRL_ATTR_MCAST_GROUPS])
return NL_SKIP;
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
nla_len(mcgrp), NULL);
if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
!tb2[CTRL_ATTR_MCAST_GRP_ID] ||
strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
res->group,
nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
continue;
res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
break;
}
return 0;
}
static int nl_get_multicast_id(struct hfi_event_data *drv,
const char *family, const char *group)
{
struct nl_msg *msg;
int ret = -1;
struct family_data res = { group, -ENOENT };
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"),
0, 0, CTRL_CMD_GETFAMILY, 0);
NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
ret = send_and_recv_msgs(drv, msg, family_handler, &res);
msg = NULL;
if (ret == 0)
ret = res.id;
nla_put_failure:
nlmsg_free(msg);
return ret;
}
struct perf_cap {
int cpu;
int perf;
int eff;
};
static void process_hfi_event(struct perf_cap *perf_cap)
{
struct isst_id id;
set_isst_id(&id, perf_cap->cpu);
process_level_change(&id);
}
static int handle_event(struct nl_msg *n, void *arg)
{
struct nlmsghdr *nlh = nlmsg_hdr(n);
struct genlmsghdr *genlhdr = genlmsg_hdr(nlh);
struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
int ret;
struct perf_cap perf_cap = {0};
ret = genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
debug_printf("Received event %d parse_rer:%d\n", genlhdr->cmd, ret);
if (genlhdr->cmd == THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE) {
struct nlattr *cap;
int j, index = 0;
debug_printf("THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE\n");
nla_for_each_nested(cap, attrs[THERMAL_GENL_ATTR_CPU_CAPABILITY], j) {
switch (index) {
case 0:
perf_cap.cpu = nla_get_u32(cap);
break;
case 1:
perf_cap.perf = nla_get_u32(cap);
break;
case 2:
perf_cap.eff = nla_get_u32(cap);
break;
default:
break;
}
++index;
if (index == 3) {
index = 0;
process_hfi_event(&perf_cap);
}
}
}
return 0;
}
static int _hfi_exit;
static int check_hf_suport(void)
{
unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
__cpuid(6, eax, ebx, ecx, edx);
if (eax & BIT(19))
return 1;
return 0;
}
int hfi_main(void)
{
struct nl_sock *sock;
struct nl_cb *cb;
int err = 0;
int mcast_id;
if (!check_hf_suport()) {
fprintf(stderr, "CPU Doesn't support HFI\n");
return -1;
}
sock = nl_socket_alloc();
if (!sock) {
fprintf(stderr, "nl_socket_alloc failed\n");
return -1;
}
if (genl_connect(sock)) {
fprintf(stderr, "genl_connect(sk_event) failed\n");
goto free_sock;
}
drv.nl_handle = sock;
drv.nl_cb = cb = nl_cb_alloc(NL_CB_DEFAULT);
if (drv.nl_cb == NULL) {
printf("Failed to allocate netlink callbacks");
goto free_sock;
}
mcast_id = nl_get_multicast_id(&drv, THERMAL_GENL_FAMILY_NAME,
THERMAL_GENL_EVENT_GROUP_NAME);
if (mcast_id < 0) {
fprintf(stderr, "nl_get_multicast_id failed\n");
goto free_sock;
}
if (nl_socket_add_membership(sock, mcast_id)) {
fprintf(stderr, "nl_socket_add_membership failed");
goto free_sock;
}
nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check_handler, 0);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handle_event, NULL);
debug_printf("hfi is initialized\n");
while (!_hfi_exit && !err) {
err = nl_recvmsgs(sock, cb);
debug_printf("nl_recv_message err:%d\n", err);
}
return 0;
/* Netlink library doesn't have calls to dealloc cb or disconnect */
free_sock:
nl_socket_free(sock);
return -1;
}
void hfi_exit(void)
{
_hfi_exit = 1;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,874 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select -- Enumerate and control features for TPMI Interface
* Copyright (c) 2022 Intel Corporation.
*/
#include <linux/isst_if.h>
#include "isst.h"
int tpmi_process_ioctl(int ioctl_no, void *info)
{
const char *pathname = "/dev/isst_interface";
int fd;
if (is_debug_enabled()) {
debug_printf("Issue IOCTL: ");
switch (ioctl_no) {
case ISST_IF_CORE_POWER_STATE:
debug_printf("ISST_IF_CORE_POWER_STATE\n");
break;
case ISST_IF_CLOS_PARAM:
debug_printf("ISST_IF_CLOS_PARAM\n");
break;
case ISST_IF_CLOS_ASSOC:
debug_printf("ISST_IF_CLOS_ASSOC\n");
break;
case ISST_IF_PERF_LEVELS:
debug_printf("ISST_IF_PERF_LEVELS\n");
break;
case ISST_IF_PERF_SET_LEVEL:
debug_printf("ISST_IF_PERF_SET_LEVEL\n");
break;
case ISST_IF_PERF_SET_FEATURE:
debug_printf("ISST_IF_PERF_SET_FEATURE\n");
break;
case ISST_IF_GET_PERF_LEVEL_INFO:
debug_printf("ISST_IF_GET_PERF_LEVEL_INFO\n");
break;
case ISST_IF_GET_PERF_LEVEL_CPU_MASK:
debug_printf("ISST_IF_GET_PERF_LEVEL_CPU_MASK\n");
break;
case ISST_IF_GET_BASE_FREQ_INFO:
debug_printf("ISST_IF_GET_BASE_FREQ_INFO\n");
break;
case ISST_IF_GET_BASE_FREQ_CPU_MASK:
debug_printf("ISST_IF_GET_BASE_FREQ_CPU_MASK\n");
break;
case ISST_IF_GET_TURBO_FREQ_INFO:
debug_printf("ISST_IF_GET_TURBO_FREQ_INFO\n");
break;
case ISST_IF_COUNT_TPMI_INSTANCES:
debug_printf("ISST_IF_COUNT_TPMI_INSTANCES\n");
break;
default:
debug_printf("%d\n", ioctl_no);
break;
}
}
fd = open(pathname, O_RDWR);
if (fd < 0)
return -1;
if (ioctl(fd, ioctl_no, info) == -1) {
debug_printf("IOCTL %d Failed\n", ioctl_no);
close(fd);
return -1;
}
close(fd);
return 0;
}
static int tpmi_get_disp_freq_multiplier(void)
{
return 1;
}
static int tpmi_get_trl_max_levels(void)
{
return TRL_MAX_LEVELS;
}
static char *tpmi_get_trl_level_name(int level)
{
switch (level) {
case 0:
return "level-0";
case 1:
return "level-1";
case 2:
return "level-2";
case 3:
return "level-3";
case 4:
return "level-4";
case 5:
return "level-5";
case 6:
return "level-6";
case 7:
return "level-7";
default:
return NULL;
}
}
static void tpmi_update_platform_param(enum isst_platform_param param, int value)
{
/* No params need to be updated for now */
}
static int tpmi_is_punit_valid(struct isst_id *id)
{
struct isst_tpmi_instance_count info;
int ret;
if (id->punit < 0)
return 0;
info.socket_id = id->pkg;
ret = tpmi_process_ioctl(ISST_IF_COUNT_TPMI_INSTANCES, &info);
if (ret == -1)
return 0;
if (info.valid_mask & BIT(id->punit))
return 1;
return 0;
}
static int tpmi_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap)
{
struct isst_core_power info;
int ret;
info.get_set = 0;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info);
if (ret == -1)
return ret;
*cp_state = info.enable;
*cp_cap = info.supported;
return 0;
}
int tpmi_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev)
{
struct isst_perf_level_info info;
int ret;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info);
if (ret == -1)
return ret;
pkg_dev->version = info.feature_rev;
pkg_dev->levels = info.max_level;
pkg_dev->locked = info.locked;
pkg_dev->current_level = info.current_level;
pkg_dev->locked = info.locked;
pkg_dev->enabled = info.enabled;
return 0;
}
static int tpmi_get_ctdp_control(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
struct isst_core_power core_power_info;
struct isst_perf_level_info info;
int level_mask;
int ret;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info);
if (ret == -1)
return -1;
if (config_index != 0xff)
level_mask = 1 << config_index;
else
level_mask = config_index;
if (!(info.level_mask & level_mask))
return -1;
if (api_version() > 2) {
ctdp_level->fact_support = info.sst_tf_support & BIT(config_index);
ctdp_level->pbf_support = info.sst_bf_support & BIT(config_index);
} else {
ctdp_level->fact_support = info.sst_tf_support;
ctdp_level->pbf_support = info.sst_bf_support;
}
ctdp_level->fact_enabled = !!(info.feature_state & BIT(1));
ctdp_level->pbf_enabled = !!(info.feature_state & BIT(0));
core_power_info.get_set = 0;
core_power_info.socket_id = id->pkg;
core_power_info.power_domain_id = id->punit;
ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &core_power_info);
if (ret == -1)
return ret;
ctdp_level->sst_cp_support = core_power_info.supported;
ctdp_level->sst_cp_enabled = core_power_info.enable;
debug_printf
("cpu:%d CONFIG_TDP_GET_TDP_CONTROL fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n",
id->cpu, ctdp_level->fact_support, ctdp_level->pbf_support,
ctdp_level->fact_enabled, ctdp_level->pbf_enabled);
return 0;
}
static int tpmi_get_tdp_info(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
struct isst_perf_level_fabric_info fabric_info;
struct isst_perf_level_data_info info;
int ret;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = config_index;
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info);
if (ret == -1)
return ret;
ctdp_level->pkg_tdp = info.thermal_design_power_w;
ctdp_level->tdp_ratio = info.tdp_ratio;
ctdp_level->sse_p1 = info.base_freq_mhz;
ctdp_level->avx2_p1 = info.base_freq_avx2_mhz;
ctdp_level->avx512_p1 = info.base_freq_avx512_mhz;
ctdp_level->amx_p1 = info.base_freq_amx_mhz;
ctdp_level->t_proc_hot = info.tjunction_max_c;
ctdp_level->mem_freq = info.max_memory_freq_mhz;
ctdp_level->cooling_type = info.cooling_type;
ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz;
ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz;
ctdp_level->uncore_pm = info.pm_fabric_freq_mhz;
fabric_info.socket_id = id->pkg;
fabric_info.power_domain_id = id->punit;
fabric_info.level = config_index;
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_FABRIC_INFO, &fabric_info);
if (ret != -1) {
ctdp_level->uncore1_p0 = fabric_info.p0_fabric_freq_mhz[1];
ctdp_level->uncore1_p1 = fabric_info.p1_fabric_freq_mhz[1];
ctdp_level->uncore1_pm = fabric_info.pm_fabric_freq_mhz[1];
}
debug_printf
("cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO tdp_ratio:%d pkg_tdp:%d ctdp_level->t_proc_hot:%d\n",
id->cpu, config_index, ctdp_level->tdp_ratio, ctdp_level->pkg_tdp,
ctdp_level->t_proc_hot);
return 0;
}
static int tpmi_get_pwr_info(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
/* TBD */
ctdp_level->pkg_max_power = 0;
ctdp_level->pkg_min_power = 0;
debug_printf
("cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO pkg_max_power:%d pkg_min_power:%d\n",
id->cpu, config_index, ctdp_level->pkg_max_power,
ctdp_level->pkg_min_power);
return 0;
}
int tpmi_get_coremask_info(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
struct isst_perf_level_cpu_mask info;
int ret, cpu_count;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = config_index;
info.punit_cpu_map = 1;
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_CPU_MASK, &info);
if (ret == -1)
return ret;
set_cpu_mask_from_punit_coremask(id, info.mask,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask, &cpu_count);
ctdp_level->cpu_count = cpu_count;
debug_printf("cpu:%d ctdp:%d core_mask ino cpu count:%d\n",
id->cpu, config_index, ctdp_level->cpu_count);
return 0;
}
static int tpmi_get_get_trls(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
struct isst_perf_level_data_info info;
int ret, i, j;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = config_index;
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info);
if (ret == -1)
return ret;
if (info.max_buckets > TRL_MAX_BUCKETS)
info.max_buckets = TRL_MAX_BUCKETS;
if (info.max_trl_levels > TRL_MAX_LEVELS)
info.max_trl_levels = TRL_MAX_LEVELS;
for (i = 0; i < info.max_trl_levels; ++i)
for (j = 0; j < info.max_buckets; ++j)
ctdp_level->trl_ratios[i][j] = info.trl_freq_mhz[i][j];
return 0;
}
static int tpmi_get_get_trl(struct isst_id *id, int config_index, int level,
int *trl)
{
struct isst_pkg_ctdp_level_info ctdp_level;
int ret, i;
ret = tpmi_get_get_trls(id, config_index, &ctdp_level);
if (ret)
return ret;
/* FIX ME: Just return for level 0 */
for (i = 0; i < 8; ++i)
trl[i] = ctdp_level.trl_ratios[0][i];
return 0;
}
static int tpmi_get_trl_bucket_info(struct isst_id *id, int config_index,
unsigned long long *buckets_info)
{
struct isst_perf_level_data_info info;
unsigned char *mask = (unsigned char *)buckets_info;
int ret, i;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = config_index;
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info);
if (ret == -1)
return ret;
if (info.max_buckets > TRL_MAX_BUCKETS)
info.max_buckets = TRL_MAX_BUCKETS;
for (i = 0; i < info.max_buckets; ++i)
mask[i] = info.bucket_core_counts[i];
debug_printf("cpu:%d TRL bucket info: 0x%llx\n", id->cpu,
*buckets_info);
return 0;
}
static int tpmi_set_tdp_level(struct isst_id *id, int tdp_level)
{
struct isst_perf_level_control info;
int ret;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = tdp_level;
ret = tpmi_process_ioctl(ISST_IF_PERF_SET_LEVEL, &info);
if (ret == -1)
return ret;
return 0;
}
static int _pbf_get_coremask_info(struct isst_id *id, int config_index,
struct isst_pbf_info *pbf_info)
{
struct isst_perf_level_cpu_mask info;
int ret, cpu_count;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = config_index;
info.punit_cpu_map = 1;
ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_CPU_MASK, &info);
if (ret == -1)
return ret;
set_cpu_mask_from_punit_coremask(id, info.mask,
pbf_info->core_cpumask_size,
pbf_info->core_cpumask, &cpu_count);
debug_printf("cpu:%d ctdp:%d pbf core_mask info cpu count:%d\n",
id->cpu, config_index, cpu_count);
return 0;
}
static int tpmi_get_pbf_info(struct isst_id *id, int level,
struct isst_pbf_info *pbf_info)
{
struct isst_base_freq_info info;
int ret;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = level;
ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_INFO, &info);
if (ret == -1)
return ret;
pbf_info->p1_low = info.low_base_freq_mhz;
pbf_info->p1_high = info.high_base_freq_mhz;
pbf_info->tdp = info.thermal_design_power_w;
pbf_info->t_prochot = info.tjunction_max_c;
debug_printf("cpu:%d ctdp:%d pbf info:%d:%d:%d:%d\n",
id->cpu, level, pbf_info->p1_low, pbf_info->p1_high,
pbf_info->tdp, pbf_info->t_prochot);
return _pbf_get_coremask_info(id, level, pbf_info);
}
#define FEATURE_ENABLE_WAIT_US 1000
#define FEATURE_ENABLE_RETRIES 5
static int tpmi_set_pbf_fact_status(struct isst_id *id, int pbf, int enable)
{
struct isst_pkg_ctdp pkg_dev;
struct isst_pkg_ctdp_level_info ctdp_level;
int current_level;
struct isst_perf_feature_control info;
int ret, i;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret)
debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu);
current_level = pkg_dev.current_level;
ret = isst_get_ctdp_control(id, current_level, &ctdp_level);
if (ret)
return ret;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.feature = 0;
if (pbf) {
if (ctdp_level.fact_enabled)
info.feature |= BIT(1);
if (enable)
info.feature |= BIT(0);
else
info.feature &= ~BIT(0);
} else {
if (enable && !ctdp_level.sst_cp_enabled)
isst_display_error_info_message(0,
"Make sure to execute before: core-power enable",
0, 0);
if (ctdp_level.pbf_enabled)
info.feature |= BIT(0);
if (enable)
info.feature |= BIT(1);
else
info.feature &= ~BIT(1);
}
ret = tpmi_process_ioctl(ISST_IF_PERF_SET_FEATURE, &info);
if (ret == -1)
return ret;
for (i = 0; i < FEATURE_ENABLE_RETRIES; ++i) {
usleep(FEATURE_ENABLE_WAIT_US);
/* Check status */
ret = isst_get_ctdp_control(id, current_level, &ctdp_level);
if (ret)
return ret;
debug_printf("pbf_enabled:%d fact_enabled:%d\n",
ctdp_level.pbf_enabled, ctdp_level.fact_enabled);
if (pbf) {
if (ctdp_level.pbf_enabled == enable)
break;
} else {
if (ctdp_level.fact_enabled == enable)
break;
}
}
if (i == FEATURE_ENABLE_RETRIES)
return -1;
return 0;
}
static int tpmi_get_fact_info(struct isst_id *id, int level, int fact_bucket,
struct isst_fact_info *fact_info)
{
struct isst_turbo_freq_info info;
int i, j;
int ret;
memset(&info, 0, sizeof(info));
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = level;
ret = tpmi_process_ioctl(ISST_IF_GET_TURBO_FREQ_INFO, &info);
if (ret == -1)
return ret;
for (i = 0; i < info.max_clip_freqs; ++i)
fact_info->lp_ratios[i] = info.lp_clip_freq_mhz[i];
if (info.max_buckets > TRL_MAX_BUCKETS)
info.max_buckets = TRL_MAX_BUCKETS;
if (info.max_trl_levels > TRL_MAX_LEVELS)
info.max_trl_levels = TRL_MAX_LEVELS;
for (i = 0; i < info.max_trl_levels; ++i) {
for (j = 0; j < info.max_buckets; ++j)
fact_info->bucket_info[j].hp_ratios[i] =
info.trl_freq_mhz[i][j];
}
for (i = 0; i < info.max_buckets; ++i)
fact_info->bucket_info[i].hp_cores = info.bucket_core_counts[i];
return 0;
}
static void _set_uncore_min_max(struct isst_id *id, int max, int freq)
{
DIR *dir;
FILE *filep;
struct dirent *entry;
char buffer[512];
unsigned int tmp_id;
int ret;
dir = opendir("/sys/devices/system/cpu/intel_uncore_frequency/");
if (!dir)
return;
while ((entry = readdir(dir)) != NULL ) {
/* Check domain_id */
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/intel_uncore_frequency/%s/domain_id", entry->d_name);
filep = fopen(buffer, "r");
if (!filep)
goto end;
ret = fscanf(filep, "%u", &tmp_id);
fclose(filep);
if (ret != 1)
goto end;
if (tmp_id != id->punit)
continue;
/* Check package_id */
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/intel_uncore_frequency/%s/package_id", entry->d_name);
filep = fopen(buffer, "r");
if (!filep)
goto end;
ret = fscanf(filep, "%u", &tmp_id);
fclose(filep);
if (ret != 1)
goto end;
if (tmp_id != id->pkg)
continue;
/* Found the right sysfs path, adjust and quit */
if (max)
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/intel_uncore_frequency/%s/max_freq_khz", entry->d_name);
else
snprintf(buffer, sizeof(buffer),
"/sys/devices/system/cpu/intel_uncore_frequency/%s/min_freq_khz", entry->d_name);
filep = fopen(buffer, "w");
if (!filep)
goto end;
fprintf(filep, "%d\n", freq);
fclose(filep);
break;
}
end:
closedir(dir);
}
static void tpmi_adjust_uncore_freq(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
struct isst_perf_level_data_info info;
int ret;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = config_index;
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info);
if (ret == -1)
return;
ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz;
ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz;
ctdp_level->uncore_pm = info.pm_fabric_freq_mhz;
if (ctdp_level->uncore_pm)
_set_uncore_min_max(id, 0, ctdp_level->uncore_pm * 100000);
if (ctdp_level->uncore_p0)
_set_uncore_min_max(id, 1, ctdp_level->uncore_p0 * 100000);
return;
}
static int tpmi_get_clos_information(struct isst_id *id, int *enable, int *type)
{
struct isst_core_power info;
int ret;
info.get_set = 0;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info);
if (ret == -1)
return ret;
*enable = info.enable;
*type = info.priority_type;
return 0;
}
static int tpmi_pm_qos_config(struct isst_id *id, int enable_clos,
int priority_type)
{
struct isst_core_power info;
int cp_state = 0, cp_cap = 0;
int i, j, ret, saved_punit;
info.get_set = 1;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.enable = enable_clos;
info.priority_type = priority_type;
saved_punit = id->punit;
/* Set for all other dies also. This is per package setting */
for (i = 0; i < MAX_PUNIT_PER_DIE; i++) {
id->punit = i;
if (isst_is_punit_valid(id)) {
info.power_domain_id = i;
ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info);
if (ret == -1) {
id->punit = saved_punit;
return ret;
}
/* Get status */
for (j = 0; j < FEATURE_ENABLE_RETRIES; ++j) {
usleep(FEATURE_ENABLE_WAIT_US);
ret = tpmi_read_pm_config(id, &cp_state, &cp_cap);
debug_printf("ret:%d cp_state:%d enable_clos:%d\n", ret,
cp_state, enable_clos);
if (ret || cp_state == enable_clos)
break;
}
if (j == FEATURE_ENABLE_RETRIES) {
id->punit = saved_punit;
return -1;
}
}
}
id->punit = saved_punit;
return 0;
}
int tpmi_pm_get_clos(struct isst_id *id, int clos,
struct isst_clos_config *clos_config)
{
struct isst_clos_param info;
int ret;
info.get_set = 0;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.clos = clos;
ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info);
if (ret == -1)
return ret;
clos_config->epp = 0;
clos_config->clos_prop_prio = info.prop_prio;
clos_config->clos_min = info.min_freq_mhz;
clos_config->clos_max = info.max_freq_mhz;
clos_config->clos_desired = 0;
debug_printf("cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos,
clos_config->clos_min, clos_config->clos_max);
return 0;
}
int tpmi_set_clos(struct isst_id *id, int clos,
struct isst_clos_config *clos_config)
{
struct isst_clos_param info;
int i, ret, saved_punit;
info.get_set = 1;
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.clos = clos;
info.prop_prio = clos_config->clos_prop_prio;
info.min_freq_mhz = clos_config->clos_min;
info.max_freq_mhz = clos_config->clos_max;
if (info.min_freq_mhz <= 0xff)
info.min_freq_mhz *= 100;
if (info.max_freq_mhz <= 0xff)
info.max_freq_mhz *= 100;
saved_punit = id->punit;
/* Set for all other dies also. This is per package setting */
for (i = 0; i < MAX_PUNIT_PER_DIE; i++) {
id->punit = i;
if (isst_is_punit_valid(id)) {
info.power_domain_id = i;
ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info);
if (ret == -1) {
id->punit = saved_punit;
return ret;
}
}
}
id->punit = saved_punit;
debug_printf("set cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos,
clos_config->clos_min, clos_config->clos_max);
return 0;
}
static int tpmi_clos_get_assoc_status(struct isst_id *id, int *clos_id)
{
struct isst_if_clos_assoc_cmds assoc_cmds;
int ret;
assoc_cmds.cmd_count = 1;
assoc_cmds.get_set = 0;
assoc_cmds.punit_cpu_map = 1;
assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu);
assoc_cmds.assoc_info[0].socket_id = id->pkg;
assoc_cmds.assoc_info[0].power_domain_id = id->punit;
ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds);
if (ret == -1)
return ret;
*clos_id = assoc_cmds.assoc_info[0].clos;
return 0;
}
static int tpmi_clos_associate(struct isst_id *id, int clos_id)
{
struct isst_if_clos_assoc_cmds assoc_cmds;
int ret;
assoc_cmds.cmd_count = 1;
assoc_cmds.get_set = 1;
assoc_cmds.punit_cpu_map = 1;
assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu);
assoc_cmds.assoc_info[0].clos = clos_id;
assoc_cmds.assoc_info[0].socket_id = id->pkg;
assoc_cmds.assoc_info[0].power_domain_id = id->punit;
ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds);
if (ret == -1)
return ret;
return 0;
}
static struct isst_platform_ops tpmi_ops = {
.get_disp_freq_multiplier = tpmi_get_disp_freq_multiplier,
.get_trl_max_levels = tpmi_get_trl_max_levels,
.get_trl_level_name = tpmi_get_trl_level_name,
.update_platform_param = tpmi_update_platform_param,
.is_punit_valid = tpmi_is_punit_valid,
.read_pm_config = tpmi_read_pm_config,
.get_config_levels = tpmi_get_config_levels,
.get_ctdp_control = tpmi_get_ctdp_control,
.get_tdp_info = tpmi_get_tdp_info,
.get_pwr_info = tpmi_get_pwr_info,
.get_coremask_info = tpmi_get_coremask_info,
.get_get_trl = tpmi_get_get_trl,
.get_get_trls = tpmi_get_get_trls,
.get_trl_bucket_info = tpmi_get_trl_bucket_info,
.set_tdp_level = tpmi_set_tdp_level,
.get_pbf_info = tpmi_get_pbf_info,
.set_pbf_fact_status = tpmi_set_pbf_fact_status,
.get_fact_info = tpmi_get_fact_info,
.adjust_uncore_freq = tpmi_adjust_uncore_freq,
.get_clos_information = tpmi_get_clos_information,
.pm_qos_config = tpmi_pm_qos_config,
.pm_get_clos = tpmi_pm_get_clos,
.set_clos = tpmi_set_clos,
.clos_get_assoc_status = tpmi_clos_get_assoc_status,
.clos_associate = tpmi_clos_associate,
};
struct isst_platform_ops *tpmi_get_platform_ops(void)
{
return &tpmi_ops;
}
@@ -0,0 +1,505 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select -- Enumerate and control features
* Copyright (c) 2019 Intel Corporation.
*/
#include "isst.h"
static struct isst_platform_ops *isst_ops;
#define CHECK_CB(_name) \
do { \
if (!isst_ops || !isst_ops->_name) { \
fprintf(stderr, "Invalid ops\n"); \
exit(0); \
} \
} while (0)
int isst_set_platform_ops(int api_version)
{
switch (api_version) {
case 1:
isst_ops = mbox_get_platform_ops();
break;
case 2:
case 3:
isst_ops = tpmi_get_platform_ops();
break;
default:
isst_ops = NULL;
break;
}
if (!isst_ops)
return -1;
return 0;
}
void isst_update_platform_param(enum isst_platform_param param, int value)
{
CHECK_CB(update_platform_param);
isst_ops->update_platform_param(param, value);
}
int isst_get_disp_freq_multiplier(void)
{
CHECK_CB(get_disp_freq_multiplier);
return isst_ops->get_disp_freq_multiplier();
}
int isst_get_trl_max_levels(void)
{
CHECK_CB(get_trl_max_levels);
return isst_ops->get_trl_max_levels();
}
char *isst_get_trl_level_name(int level)
{
CHECK_CB(get_trl_level_name);
return isst_ops->get_trl_level_name(level);
}
int isst_is_punit_valid(struct isst_id *id)
{
CHECK_CB(is_punit_valid);
return isst_ops->is_punit_valid(id);
}
int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write,
unsigned long long *req_resp)
{
struct isst_if_msr_cmds msr_cmds;
const char *pathname = "/dev/isst_interface";
FILE *outf = get_output_file();
int fd;
fd = open(pathname, O_RDWR);
if (fd < 0)
err(-1, "%s open failed", pathname);
msr_cmds.cmd_count = 1;
msr_cmds.msr_cmd[0].logical_cpu = cpu;
msr_cmds.msr_cmd[0].msr = msr;
msr_cmds.msr_cmd[0].read_write = write;
if (write)
msr_cmds.msr_cmd[0].data = *req_resp;
if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) {
perror("ISST_IF_MSR_COMMAND");
fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n",
cpu, msr, write);
} else {
if (!write)
*req_resp = msr_cmds.msr_cmd[0].data;
debug_printf(
"msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n",
cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data);
}
close(fd);
return 0;
}
int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap)
{
CHECK_CB(read_pm_config);
return isst_ops->read_pm_config(id, cp_state, cp_cap);
}
int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev)
{
CHECK_CB(get_config_levels);
return isst_ops->get_config_levels(id, pkg_dev);
}
int isst_get_ctdp_control(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
CHECK_CB(get_ctdp_control);
return isst_ops->get_ctdp_control(id, config_index, ctdp_level);
}
int isst_get_tdp_info(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
CHECK_CB(get_tdp_info);
return isst_ops->get_tdp_info(id, config_index, ctdp_level);
}
int isst_get_pwr_info(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
CHECK_CB(get_pwr_info);
return isst_ops->get_pwr_info(id, config_index, ctdp_level);
}
int isst_get_coremask_info(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
CHECK_CB(get_coremask_info);
return isst_ops->get_coremask_info(id, config_index, ctdp_level);
}
int isst_get_get_trl_from_msr(struct isst_id *id, int *trl)
{
unsigned long long msr_trl;
int ret;
ret = isst_send_msr_command(id->cpu, 0x1AD, 0, &msr_trl);
if (ret)
return ret;
trl[0] = msr_trl & GENMASK(7, 0);
trl[1] = (msr_trl & GENMASK(15, 8)) >> 8;
trl[2] = (msr_trl & GENMASK(23, 16)) >> 16;
trl[3] = (msr_trl & GENMASK(31, 24)) >> 24;
trl[4] = (msr_trl & GENMASK(39, 32)) >> 32;
trl[5] = (msr_trl & GENMASK(47, 40)) >> 40;
trl[6] = (msr_trl & GENMASK(55, 48)) >> 48;
trl[7] = (msr_trl & GENMASK(63, 56)) >> 56;
return 0;
}
int isst_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl)
{
CHECK_CB(get_get_trl);
return isst_ops->get_get_trl(id, level, avx_level, trl);
}
int isst_get_get_trls(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level)
{
CHECK_CB(get_get_trls);
return isst_ops->get_get_trls(id, level, ctdp_level);
}
int isst_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info)
{
CHECK_CB(get_trl_bucket_info);
return isst_ops->get_trl_bucket_info(id, level, buckets_info);
}
int isst_set_tdp_level(struct isst_id *id, int tdp_level)
{
CHECK_CB(set_tdp_level);
return isst_ops->set_tdp_level(id, tdp_level);
}
int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info)
{
struct isst_pkg_ctdp_level_info ctdp_level;
struct isst_pkg_ctdp pkg_dev;
int ret;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret) {
isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
return ret;
}
if (level > pkg_dev.levels) {
isst_display_error_info_message(1, "Invalid level", 1, level);
return -1;
}
ret = isst_get_ctdp_control(id, level, &ctdp_level);
if (ret)
return ret;
if (!ctdp_level.pbf_support) {
isst_display_error_info_message(1, "base-freq feature is not present at this level", 1, level);
return -1;
}
pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask);
CHECK_CB(get_pbf_info);
return isst_ops->get_pbf_info(id, level, pbf_info);
}
int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable)
{
CHECK_CB(set_pbf_fact_status);
return isst_ops->set_pbf_fact_status(id, pbf, enable);
}
int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info)
{
struct isst_pkg_ctdp_level_info ctdp_level;
struct isst_pkg_ctdp pkg_dev;
int ret;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret) {
isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
return ret;
}
if (level > pkg_dev.levels) {
isst_display_error_info_message(1, "Invalid level", 1, level);
return -1;
}
ret = isst_get_ctdp_control(id, level, &ctdp_level);
if (ret)
return ret;
if (!ctdp_level.fact_support) {
isst_display_error_info_message(1, "turbo-freq feature is not present at this level", 1, level);
return -1;
}
CHECK_CB(get_fact_info);
return isst_ops->get_fact_info(id, level, fact_bucket, fact_info);
}
int isst_get_trl(struct isst_id *id, unsigned long long *trl)
{
int ret;
ret = isst_send_msr_command(id->cpu, 0x1AD, 0, trl);
if (ret)
return ret;
return 0;
}
int isst_set_trl(struct isst_id *id, unsigned long long trl)
{
int ret;
if (!trl)
trl = 0xFFFFFFFFFFFFFFFFULL;
ret = isst_send_msr_command(id->cpu, 0x1AD, 1, &trl);
if (ret)
return ret;
return 0;
}
#define MSR_TRL_FREQ_MULTIPLIER 100
int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl)
{
unsigned long long msr_trl;
int ret;
if (id->cpu < 0)
return 0;
if (trl) {
msr_trl = trl;
} else {
struct isst_pkg_ctdp pkg_dev;
int trl[8];
int i;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret)
return ret;
ret = isst_get_get_trl(id, pkg_dev.current_level, 0, trl);
if (ret)
return ret;
msr_trl = 0;
for (i = 0; i < 8; ++i) {
unsigned long long _trl = trl[i];
/* MSR is always in 100 MHz unit */
if (isst_get_disp_freq_multiplier() == 1)
_trl /= MSR_TRL_FREQ_MULTIPLIER;
msr_trl |= (_trl << (i * 8));
}
}
ret = isst_send_msr_command(id->cpu, 0x1AD, 1, &msr_trl);
if (ret)
return ret;
return 0;
}
/* Return 1 if locked */
int isst_get_config_tdp_lock_status(struct isst_id *id)
{
unsigned long long tdp_control = 0;
int ret;
ret = isst_send_msr_command(id->cpu, 0x64b, 0, &tdp_control);
if (ret)
return ret;
ret = !!(tdp_control & BIT(31));
return ret;
}
void isst_get_process_ctdp_complete(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev)
{
int i;
if (!pkg_dev->processed)
return;
for (i = 0; i < pkg_dev->levels; ++i) {
struct isst_pkg_ctdp_level_info *ctdp_level;
ctdp_level = &pkg_dev->ctdp_level[i];
if (ctdp_level->pbf_support)
free_cpu_set(ctdp_level->pbf_info.core_cpumask);
free_cpu_set(ctdp_level->core_cpumask);
}
}
void isst_adjust_uncore_freq(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
CHECK_CB(adjust_uncore_freq);
return isst_ops->adjust_uncore_freq(id, config_index, ctdp_level);
}
int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctdp *pkg_dev)
{
int i, ret, valid = 0;
if (pkg_dev->processed)
return 0;
ret = isst_get_ctdp_levels(id, pkg_dev);
if (ret)
return ret;
debug_printf("cpu: %d ctdp enable:%d current level: %d levels:%d\n",
id->cpu, pkg_dev->enabled, pkg_dev->current_level,
pkg_dev->levels);
if (tdp_level != 0xff && tdp_level > pkg_dev->levels) {
isst_display_error_info_message(1, "Invalid level", 0, 0);
return -1;
}
if (!pkg_dev->enabled)
isst_display_error_info_message(0, "perf-profile feature is not supported, just base-config level 0 is valid", 0, 0);
for (i = 0; i <= pkg_dev->levels; ++i) {
struct isst_pkg_ctdp_level_info *ctdp_level;
if (tdp_level != 0xff && i != tdp_level)
continue;
debug_printf("cpu:%d Get Information for TDP level:%d\n", id->cpu,
i);
ctdp_level = &pkg_dev->ctdp_level[i];
ctdp_level->level = i;
ctdp_level->control_cpu = id->cpu;
ctdp_level->pkg_id = id->pkg;
ctdp_level->die_id = id->die;
ret = isst_get_ctdp_control(id, i, ctdp_level);
if (ret)
continue;
valid = 1;
pkg_dev->processed = 1;
ctdp_level->processed = 1;
if (ctdp_level->pbf_support) {
ret = isst_get_pbf_info(id, i, &ctdp_level->pbf_info);
if (!ret)
ctdp_level->pbf_found = 1;
}
if (ctdp_level->fact_support) {
ret = isst_get_fact_info(id, i, 0xff,
&ctdp_level->fact_info);
if (ret)
return ret;
}
if (!pkg_dev->enabled && is_skx_based_platform()) {
int freq;
freq = get_cpufreq_base_freq(id->cpu);
if (freq > 0) {
ctdp_level->sse_p1 = freq / 100000;
ctdp_level->tdp_ratio = ctdp_level->sse_p1;
}
isst_get_get_trl_from_msr(id, ctdp_level->trl_ratios[0]);
isst_get_trl_bucket_info(id, i, &ctdp_level->trl_cores);
continue;
}
ret = isst_get_tdp_info(id, i, ctdp_level);
if (ret)
return ret;
ret = isst_get_pwr_info(id, i, ctdp_level);
if (ret)
return ret;
ctdp_level->core_cpumask_size =
alloc_cpu_set(&ctdp_level->core_cpumask);
ret = isst_get_coremask_info(id, i, ctdp_level);
if (ret)
return ret;
ret = isst_get_trl_bucket_info(id, i, &ctdp_level->trl_cores);
if (ret)
return ret;
ret = isst_get_get_trls(id, i, ctdp_level);
if (ret)
return ret;
}
if (!valid)
isst_display_error_info_message(0, "Invalid level, Can't get TDP control information at specified levels on cpu", 1, id->cpu);
return 0;
}
int isst_clos_get_clos_information(struct isst_id *id, int *enable, int *type)
{
CHECK_CB(get_clos_information);
return isst_ops->get_clos_information(id, enable, type);
}
int isst_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type)
{
CHECK_CB(pm_qos_config);
return isst_ops->pm_qos_config(id, enable_clos, priority_type);
}
int isst_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config)
{
CHECK_CB(pm_get_clos);
return isst_ops->pm_get_clos(id, clos, clos_config);
}
int isst_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config)
{
CHECK_CB(set_clos);
return isst_ops->set_clos(id, clos, clos_config);
}
int isst_clos_get_assoc_status(struct isst_id *id, int *clos_id)
{
CHECK_CB(clos_get_assoc_status);
return isst_ops->clos_get_assoc_status(id, clos_id);
}
int isst_clos_associate(struct isst_id *id, int clos_id)
{
CHECK_CB(clos_associate);
return isst_ops->clos_associate(id, clos_id);
}
@@ -0,0 +1,263 @@
// 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][MAX_PUNIT_PER_DIE];
static time_t per_package_levels_tm[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE];
static void init_levels(void)
{
int i, j, k;
for (i = 0; i < MAX_PACKAGE_COUNT; ++i)
for (j = 0; j < MAX_DIE_PER_PACKAGE; ++j)
for (k = 0; k < MAX_PUNIT_PER_DIE; ++k)
per_package_levels_info[i][j][k] = -1;
}
void process_level_change(struct isst_id *id)
{
struct isst_pkg_ctdp_level_info ctdp_level;
struct isst_pkg_ctdp pkg_dev;
time_t tm;
int ret;
if (id->pkg < 0 || id->die < 0 || id->punit < 0) {
debug_printf("Invalid package/die info for cpu:%d\n", id->cpu);
return;
}
tm = time(NULL);
if (tm - per_package_levels_tm[id->pkg][id->die][id->punit] < 2)
return;
per_package_levels_tm[id->pkg][id->die][id->punit] = tm;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret) {
debug_printf("Can't get tdp levels for cpu:%d\n", id->cpu);
return;
}
debug_printf("Get Config level %d pkg:%d die:%d current_level:%d\n", id->cpu,
id->pkg, id->die, pkg_dev.current_level);
if (pkg_dev.locked) {
debug_printf("config TDP s locked \n");
return;
}
if (per_package_levels_info[id->pkg][id->die][id->punit] == pkg_dev.current_level)
return;
debug_printf("**Config level change for cpu:%d pkg:%d die:%d from %d to %d\n",
id->cpu, id->pkg, id->die, per_package_levels_info[id->pkg][id->die][id->punit],
pkg_dev.current_level);
per_package_levels_info[id->pkg][id->die][id->punit] = pkg_dev.current_level;
ctdp_level.core_cpumask_size =
alloc_cpu_set(&ctdp_level.core_cpumask);
ret = isst_get_coremask_info(id, pkg_dev.current_level, &ctdp_level);
if (ret) {
free_cpu_set(ctdp_level.core_cpumask);
debug_printf("Can't get core_mask:%d\n", id->cpu);
return;
}
if (use_cgroupv2()) {
int ret;
ret = enable_cpuset_controller();
if (ret)
goto use_offline;
isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask,
pkg_dev.current_level, 0);
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);
} 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 _poll_for_config_change(struct isst_id *id, void *arg1, void *arg2,
void *arg3, void *arg4)
{
process_level_change(id);
}
static void poll_for_config_change(void)
{
for_each_online_power_domain_in_set(_poll_for_config_change, NULL, NULL,
NULL, NULL);
}
static int done = 0;
static int pid_file_handle;
static void signal_handler(int sig)
{
switch (sig) {
case SIGINT:
case SIGTERM:
done = 1;
hfi_exit();
exit(0);
break;
default:
break;
}
}
static void daemonize(char *rundir, char *pidfile)
{
int pid, sid, i;
char str[10];
struct stat st;
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);
if (i < 0)
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 | O_NOFOLLOW, 0600);
if (pid_file_handle == -1) {
/* Couldn't open lock file */
exit(1);
}
if (fstat(pid_file_handle, &st) == -1)
exit(1);
if (!S_ISREG(st.st_mode))
exit(1);
/* Try to lock file */
#ifdef LOCKF_SUPPORT
if (lockf(pid_file_handle, F_TLOCK, 0) == -1) {
#else
if (flock(pid_file_handle, LOCK_EX|LOCK_NB) < 0) {
#endif
/* Couldn't get lock on lock file */
fprintf(stderr, "Couldn't get lock file %d\n", getpid());
exit(1);
}
snprintf(str, sizeof(str), "%d\n", getpid());
ret = write(pid_file_handle, str, strlen(str));
if (ret == -1)
exit(EXIT_FAILURE);
close(i);
}
int isst_daemon(int debug_mode, int poll_interval, int no_daemon)
{
int ret;
if (!no_daemon && poll_interval < 0 && !debug_mode) {
fprintf(stderr, "OOB mode is enabled and will run as daemon\n");
daemonize((char *) "/tmp/",
(char *)"/tmp/hfi-events.pid");
} else {
signal(SIGINT, signal_handler);
}
init_levels();
if (poll_interval < 0) {
ret = hfi_main();
if (ret) {
fprintf(stderr, "HFI initialization failed\n");
}
fprintf(stderr, "Must specify poll-interval\n");
return ret;
}
debug_printf("Starting loop\n");
while (!done) {
sleep(poll_interval);
poll_for_config_change();
}
return 0;
}
@@ -0,0 +1,804 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel dynamic_speed_select -- Enumerate and control features
* Copyright (c) 2019 Intel Corporation.
*/
#include "isst.h"
static void printcpulist(int str_len, char *str, int mask_size,
cpu_set_t *cpu_mask)
{
int i, first, curr_index, index;
if (!CPU_COUNT_S(mask_size, cpu_mask)) {
snprintf(str, str_len, "none");
return;
}
curr_index = 0;
first = 1;
for (i = 0; i < get_topo_max_cpus(); ++i) {
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;
}
}
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 (curr_index >= str_len)
break;
if (i) {
strncat(&str[curr_index], ",", str_len - curr_index);
curr_index++;
}
if (curr_index >= str_len)
break;
}
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 int print_package_info(struct isst_id *id, FILE *outf)
{
char header[256];
int level = 1;
if (out_format_is_json()) {
if (api_version() > 1) {
if (id->die < 0 && id->cpu < 0)
snprintf(header, sizeof(header),
"package-%d:die-IO:powerdomain-%d:cpu-None",
id->pkg, id->punit);
else if (id->cpu < 0)
snprintf(header, sizeof(header),
"package-%d:die-%d:powerdomain-%d:cpu-None",
id->pkg, id->die, id->punit);
else
snprintf(header, sizeof(header),
"package-%d:die-%d:powerdomain-%d:cpu-%d",
id->pkg, id->die, id->punit, id->cpu);
} else {
snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d",
id->pkg, id->die, id->cpu);
}
format_and_print(outf, level, header, NULL);
return 1;
}
snprintf(header, sizeof(header), "package-%d", id->pkg);
format_and_print(outf, level++, header, NULL);
if (id->die < 0)
snprintf(header, sizeof(header), "die-IO");
else
snprintf(header, sizeof(header), "die-%d", id->die);
format_and_print(outf, level++, header, NULL);
if (api_version() > 1) {
snprintf(header, sizeof(header), "powerdomain-%d", id->punit);
format_and_print(outf, level++, header, NULL);
}
if (id->cpu < 0)
snprintf(header, sizeof(header), "cpu-None");
else
snprintf(header, sizeof(header), "cpu-%d", id->cpu);
format_and_print(outf, level, header, NULL);
return level;
}
static void _isst_pbf_display_information(struct isst_id *id, FILE *outf, int level,
struct isst_pbf_info *pbf_info,
int disp_level)
{
static char header[256];
static char value[1024];
snprintf(header, sizeof(header), "speed-select-base-freq-properties");
format_and_print(outf, disp_level, header, NULL);
snprintf(header, sizeof(header), "high-priority-base-frequency(MHz)");
snprintf(value, sizeof(value), "%d",
pbf_info->p1_high * isst_get_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), "high-priority-cpu-list");
printcpulist(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(MHz)");
snprintf(value, sizeof(value), "%d",
pbf_info->p1_low * isst_get_disp_freq_multiplier());
format_and_print(outf, disp_level + 1, header, value);
if (is_clx_n_platform())
return;
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(struct isst_id *id, 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;
int trl_max_levels = isst_get_trl_max_levels();
char header[256];
char value[256];
int print = 0, j;
for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) {
if (fact_bucket != 0xff && fact_bucket != j)
continue;
/* core count must be valid for CPU power domain */
if (!bucket_info[j].hp_cores && id->cpu >= 0)
break;
print = 1;
}
if (!print) {
fprintf(stderr, "Invalid bucket\n");
return;
}
snprintf(header, sizeof(header), "speed-select-turbo-freq-properties");
format_and_print(outf, base_level, header, NULL);
for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) {
int i;
if (fact_bucket != 0xff && fact_bucket != j)
continue;
if (!bucket_info[j].hp_cores)
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].hp_cores);
format_and_print(outf, base_level + 2, header, value);
for (i = 0; i < trl_max_levels; i++) {
if (!bucket_info[j].hp_ratios[i] || (fact_avx != 0xFF && !(fact_avx & (1 << i))))
continue;
if (i == 0 && api_version() == 1 && !is_emr_platform())
snprintf(header, sizeof(header),
"high-priority-max-frequency(MHz)");
else
snprintf(header, sizeof(header),
"high-priority-max-%s-frequency(MHz)", isst_get_trl_level_name(i));
snprintf(value, sizeof(value), "%d",
bucket_info[j].hp_ratios[i] * isst_get_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);
for (j = 0; j < trl_max_levels; j++) {
if (!fact_info->lp_ratios[j])
continue;
/* No AVX level name for SSE to be consistent with previous formatting */
if (j == 0 && api_version() == 1 && !is_emr_platform())
snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)");
else
snprintf(header, sizeof(header), "low-priority-max-%s-frequency(MHz)",
isst_get_trl_level_name(j));
snprintf(value, sizeof(value), "%d",
fact_info->lp_ratios[j] * isst_get_disp_freq_multiplier());
format_and_print(outf, base_level + 2, header, value);
}
}
void isst_ctdp_display_core_info(struct isst_id *id, FILE *outf, char *prefix,
unsigned int val, char *str0, char *str1)
{
char value[256];
int level = print_package_info(id, outf);
level++;
if (str0 && !val)
snprintf(value, sizeof(value), "%s", str0);
else if (str1 && val)
snprintf(value, sizeof(value), "%s", str1);
else
snprintf(value, sizeof(value), "%u", val);
format_and_print(outf, level, prefix, value);
format_and_print(outf, 1, NULL, NULL);
}
void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level,
struct isst_pkg_ctdp *pkg_dev)
{
static char header[256];
static char value[1024];
static int level;
int trl_max_levels = isst_get_trl_max_levels();
int i;
if (pkg_dev->processed)
level = print_package_info(id, outf);
for (i = 0; i <= pkg_dev->levels; ++i) {
struct isst_pkg_ctdp_level_info *ctdp_level;
int j, k;
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, level + 1, header, NULL);
if (id->cpu >= 0) {
snprintf(header, sizeof(header), "cpu-count");
j = get_cpu_count(id);
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, level + 2, header, value);
j = CPU_COUNT_S(ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
if (j) {
snprintf(header, sizeof(header), "enable-cpu-count");
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->core_cpumask_size) {
snprintf(header, sizeof(header), "enable-cpu-mask");
printcpumask(sizeof(value), value,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "enable-cpu-list");
printcpulist(sizeof(value), value,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
format_and_print(outf, level + 2, header, value);
}
}
snprintf(header, sizeof(header), "thermal-design-power-ratio");
snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio);
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "base-frequency(MHz)");
if (!ctdp_level->sse_p1)
ctdp_level->sse_p1 = ctdp_level->tdp_ratio;
snprintf(value, sizeof(value), "%d",
ctdp_level->sse_p1 * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
if (ctdp_level->avx2_p1) {
snprintf(header, sizeof(header), "base-frequency-avx2(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->avx2_p1 * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->avx512_p1) {
snprintf(header, sizeof(header), "base-frequency-avx512(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->avx512_p1 * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->uncore_pm) {
snprintf(header, sizeof(header), "uncore-frequency-min(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->uncore_pm * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->uncore_p0) {
snprintf(header, sizeof(header), "uncore-frequency-max(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->uncore_p0 * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->amx_p1) {
snprintf(header, sizeof(header), "base-frequency-amx(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->amx_p1 * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->uncore_p1) {
snprintf(header, sizeof(header), "uncore-frequency-base(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->uncore_p1 * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->uncore1_p1) {
snprintf(header, sizeof(header), "uncore-1-frequency-base(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->uncore1_p1 * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->uncore1_pm) {
snprintf(header, sizeof(header), "uncore-1-frequency-min(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->uncore1_pm * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->uncore1_p0) {
snprintf(header, sizeof(header), "uncore-1-frequency-max(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->uncore1_p0 * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->mem_freq) {
snprintf(header, sizeof(header), "max-mem-frequency(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->mem_freq);
format_and_print(outf, level + 2, header, value);
}
if (api_version() > 1) {
snprintf(header, sizeof(header), "cooling_type");
snprintf(value, sizeof(value), "%d",
ctdp_level->cooling_type);
format_and_print(outf, level + 2, header, value);
}
snprintf(header, sizeof(header),
"speed-select-turbo-freq");
if (ctdp_level->fact_support) {
if (ctdp_level->fact_enabled)
snprintf(value, sizeof(value), "enabled");
else
snprintf(value, sizeof(value), "disabled");
} else
snprintf(value, sizeof(value), "unsupported");
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header),
"speed-select-base-freq");
if (ctdp_level->pbf_support) {
if (ctdp_level->pbf_enabled)
snprintf(value, sizeof(value), "enabled");
else
snprintf(value, sizeof(value), "disabled");
} else
snprintf(value, sizeof(value), "unsupported");
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header),
"speed-select-core-power");
if (ctdp_level->sst_cp_support) {
if (ctdp_level->sst_cp_enabled)
snprintf(value, sizeof(value), "enabled");
else
snprintf(value, sizeof(value), "disabled");
} else
snprintf(value, sizeof(value), "unsupported");
format_and_print(outf, level + 2, header, value);
if (is_clx_n_platform()) {
if (ctdp_level->pbf_support)
_isst_pbf_display_information(id, outf,
tdp_level,
&ctdp_level->pbf_info,
level + 2);
continue;
}
if (ctdp_level->pkg_tdp) {
snprintf(header, sizeof(header), "thermal-design-power(W)");
snprintf(value, sizeof(value), "%d", ctdp_level->pkg_tdp);
format_and_print(outf, level + 2, header, value);
}
if (ctdp_level->t_proc_hot) {
snprintf(header, sizeof(header), "tjunction-max(C)");
snprintf(value, sizeof(value), "%d", ctdp_level->t_proc_hot);
format_and_print(outf, level + 2, header, value);
}
for (k = 0; k < trl_max_levels; k++) {
if (!ctdp_level->trl_ratios[k][0])
continue;
snprintf(header, sizeof(header), "turbo-ratio-limits-%s", isst_get_trl_level_name(k));
format_and_print(outf, level + 2, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, level + 3, header, NULL);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%llu", (ctdp_level->trl_cores >> (j * 8)) & 0xff);
format_and_print(outf, level + 4, header, value);
snprintf(header, sizeof(header), "max-turbo-frequency(MHz)");
snprintf(value, sizeof(value), "%d", ctdp_level->trl_ratios[k][j] * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 4, header, value);
}
}
if (ctdp_level->pbf_support)
_isst_pbf_display_information(id, outf, i,
&ctdp_level->pbf_info,
level + 2);
if (ctdp_level->fact_support)
_isst_fact_display_information(id, outf, i, 0xff, 0xff,
&ctdp_level->fact_info,
level + 2);
}
format_and_print(outf, 1, NULL, NULL);
}
static int start;
void isst_ctdp_display_information_start(FILE *outf)
{
last_level = 0;
format_and_print(outf, 0, "start", NULL);
start = 1;
}
void isst_ctdp_display_information_end(FILE *outf)
{
format_and_print(outf, 0, NULL, NULL);
start = 0;
}
void isst_pbf_display_information(struct isst_id *id, FILE *outf, int level,
struct isst_pbf_info *pbf_info)
{
int _level;
_level = print_package_info(id, outf);
_isst_pbf_display_information(id, outf, level, pbf_info, _level + 1);
format_and_print(outf, 1, NULL, NULL);
}
void isst_fact_display_information(struct isst_id *id, FILE *outf, int level,
int fact_bucket, int fact_avx,
struct isst_fact_info *fact_info)
{
int _level;
_level = print_package_info(id, outf);
_isst_fact_display_information(id, outf, level, fact_bucket, fact_avx,
fact_info, _level + 1);
format_and_print(outf, 1, NULL, NULL);
}
void isst_clos_display_information(struct isst_id *id, FILE *outf, int clos,
struct isst_clos_config *clos_config)
{
char header[256];
char value[256];
int level;
level = print_package_info(id, outf);
snprintf(header, sizeof(header), "core-power");
format_and_print(outf, level + 1, header, NULL);
snprintf(header, sizeof(header), "clos");
snprintf(value, sizeof(value), "%d", clos);
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "epp");
snprintf(value, sizeof(value), "%d", clos_config->epp);
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "clos-proportional-priority");
snprintf(value, sizeof(value), "%d", clos_config->clos_prop_prio);
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "clos-min");
snprintf(value, sizeof(value), "%d MHz", clos_config->clos_min * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "clos-max");
if ((clos_config->clos_max * isst_get_disp_freq_multiplier()) == 25500)
snprintf(value, sizeof(value), "Max Turbo frequency");
else
snprintf(value, sizeof(value), "%d MHz", clos_config->clos_max * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "clos-desired");
snprintf(value, sizeof(value), "%d MHz", clos_config->clos_desired * isst_get_disp_freq_multiplier());
format_and_print(outf, level + 2, header, value);
format_and_print(outf, level, NULL, NULL);
}
void isst_clos_display_clos_information(struct isst_id *id, FILE *outf,
int clos_enable, int type,
int state, int cap)
{
char header[256];
char value[256];
int level;
level = print_package_info(id, outf);
snprintf(header, sizeof(header), "core-power");
format_and_print(outf, level + 1, header, NULL);
snprintf(header, sizeof(header), "support-status");
if (cap)
snprintf(value, sizeof(value), "supported");
else
snprintf(value, sizeof(value), "unsupported");
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "enable-status");
if (state)
snprintf(value, sizeof(value), "enabled");
else
snprintf(value, sizeof(value), "disabled");
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "clos-enable-status");
if (clos_enable)
snprintf(value, sizeof(value), "enabled");
else
snprintf(value, sizeof(value), "disabled");
format_and_print(outf, level + 2, header, value);
snprintf(header, sizeof(header), "priority-type");
if (type)
snprintf(value, sizeof(value), "ordered");
else
snprintf(value, sizeof(value), "proportional");
format_and_print(outf, level + 2, header, value);
format_and_print(outf, level, NULL, NULL);
}
void isst_clos_display_assoc_information(struct isst_id *id, FILE *outf, int clos)
{
char header[256];
char value[256];
int level;
level = print_package_info(id, outf);
snprintf(header, sizeof(header), "get-assoc");
format_and_print(outf, level + 1, header, NULL);
snprintf(header, sizeof(header), "clos");
snprintf(value, sizeof(value), "%d", clos);
format_and_print(outf, level + 2, header, value);
format_and_print(outf, level, NULL, NULL);
}
void isst_display_result(struct isst_id *id, FILE *outf, char *feature, char *cmd,
int result)
{
char header[256];
char value[256];
int level = 3;
level = print_package_info(id, outf);
snprintf(header, sizeof(header), "%s", feature);
format_and_print(outf, level + 1, header, NULL);
snprintf(header, sizeof(header), "%s", cmd);
if (!result)
snprintf(value, sizeof(value), "success");
else
snprintf(value, sizeof(value), "failed(error %d)", result);
format_and_print(outf, level + 2, header, value);
format_and_print(outf, level, NULL, NULL);
}
void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg)
{
FILE *outf = get_output_file();
static int error_index;
char header[256];
char value[256];
if (!out_format_is_json()) {
if (arg_valid)
snprintf(value, sizeof(value), "%s %d", msg, arg);
else
snprintf(value, sizeof(value), "%s", msg);
if (error)
fprintf(outf, "Error: %s\n", value);
else
fprintf(outf, "Information: %s\n", value);
return;
}
if (!start)
format_and_print(outf, 0, "start", NULL);
if (error)
snprintf(header, sizeof(header), "Error%d", error_index++);
else
snprintf(header, sizeof(header), "Information:%d", error_index++);
format_and_print(outf, 1, header, NULL);
snprintf(header, sizeof(header), "message");
if (arg_valid)
snprintf(value, sizeof(value), "%s %d", msg, arg);
else
snprintf(value, sizeof(value), "%s", msg);
format_and_print(outf, 2, header, value);
format_and_print(outf, 1, NULL, NULL);
if (!start)
format_and_print(outf, 0, NULL, NULL);
}
void isst_trl_display_information(struct isst_id *id, FILE *outf, unsigned long long trl)
{
char header[256];
char value[256];
int level;
level = print_package_info(id, outf);
snprintf(header, sizeof(header), "get-trl");
format_and_print(outf, level + 1, header, NULL);
snprintf(header, sizeof(header), "trl");
snprintf(value, sizeof(value), "0x%llx", trl);
format_and_print(outf, level + 2, header, value);
format_and_print(outf, level, NULL, NULL);
}
@@ -0,0 +1,328 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel Speed Select -- Enumerate and control features
* Copyright (c) 2019 Intel Corporation.
*/
#ifndef _ISST_H_
#define _ISST_H_
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <getopt.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <cpuid.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <linux/isst_if.h>
#define BIT(x) (1 << (x))
#define BIT_ULL(nr) (1ULL << (nr))
#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))
#define GENMASK_ULL(h, l) \
(((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h))))
#define CONFIG_TDP 0x7f
#define CONFIG_TDP_GET_LEVELS_INFO 0x00
#define CONFIG_TDP_GET_TDP_CONTROL 0x01
#define CONFIG_TDP_SET_TDP_CONTROL 0x02
#define CONFIG_TDP_GET_TDP_INFO 0x03
#define CONFIG_TDP_GET_PWR_INFO 0x04
#define CONFIG_TDP_GET_TJMAX_INFO 0x05
#define CONFIG_TDP_GET_CORE_MASK 0x06
#define CONFIG_TDP_GET_TURBO_LIMIT_RATIOS 0x07
#define CONFIG_TDP_SET_LEVEL 0x08
#define CONFIG_TDP_GET_UNCORE_P0_P1_INFO 0X09
#define CONFIG_TDP_GET_P1_INFO 0x0a
#define CONFIG_TDP_GET_MEM_FREQ 0x0b
#define CONFIG_TDP_GET_RATIO_INFO 0x0c
#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES 0x10
#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS 0x11
#define CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO 0x12
#define CONFIG_TDP_PBF_GET_CORE_MASK_INFO 0x20
#define CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO 0x21
#define CONFIG_TDP_PBF_GET_TJ_MAX_INFO 0x22
#define CONFIG_TDP_PBF_GET_TDP_INFO 0X23
#define CONFIG_CLOS 0xd0
#define CLOS_PQR_ASSOC 0x00
#define CLOS_PM_CLOS 0x01
#define CLOS_PM_QOS_CONFIG 0x02
#define CLOS_STATUS 0x03
#define MBOX_CMD_WRITE_BIT 0x08
#define PM_QOS_INFO_OFFSET 0x00
#define PM_QOS_CONFIG_OFFSET 0x04
#define PM_CLOS_OFFSET 0x08
#define PQR_ASSOC_OFFSET 0x20
#define READ_PM_CONFIG 0x94
#define WRITE_PM_CONFIG 0x95
#define PM_FEATURE 0x03
#define DISP_FREQ_MULTIPLIER 100
#define MAX_PACKAGE_COUNT 32
#define MAX_DIE_PER_PACKAGE 16
#define MAX_PUNIT_PER_DIE 8
/* Unified structure to specific a CPU or a Power Domain */
struct isst_id {
int cpu;
int pkg;
int die;
int punit;
};
struct isst_clos_config {
unsigned int clos_min;
unsigned int clos_max;
unsigned char epp;
unsigned char clos_prop_prio;
unsigned char clos_desired;
};
struct isst_fact_bucket_info {
int hp_cores;
int hp_ratios[TRL_MAX_LEVELS];
};
struct isst_pbf_info {
int pbf_acticated;
int pbf_available;
size_t core_cpumask_size;
cpu_set_t *core_cpumask;
int p1_high;
int p1_low;
int t_control;
int t_prochot;
int tdp;
};
#define ISST_TRL_MAX_ACTIVE_CORES 8
#define ISST_FACT_MAX_BUCKETS 8
struct isst_fact_info {
int lp_ratios[TRL_MAX_LEVELS];
struct isst_fact_bucket_info bucket_info[ISST_FACT_MAX_BUCKETS];
};
struct isst_pkg_ctdp_level_info {
int processed;
int control_cpu;
int pkg_id;
int die_id;
int level;
int fact_support;
int pbf_support;
int fact_enabled;
int pbf_enabled;
int sst_cp_support;
int sst_cp_enabled;
int tdp_ratio;
int active;
int tdp_control;
int pkg_tdp;
int pkg_min_power;
int pkg_max_power;
int fact;
int t_proc_hot;
int cooling_type;
int uncore_p0;
int uncore_p1;
int uncore_pm;
int uncore1_p0;
int uncore1_p1;
int uncore1_pm;
int sse_p1;
int avx2_p1;
int avx512_p1;
int amx_p1;
int mem_freq;
size_t core_cpumask_size;
cpu_set_t *core_cpumask;
int cpu_count;
unsigned long long trl_cores; /* Buckets info */
int trl_ratios[TRL_MAX_LEVELS][ISST_TRL_MAX_ACTIVE_CORES];
int kobj_bucket_index;
int active_bucket;
int fact_max_index;
int fact_max_config;
int pbf_found;
int pbf_active;
struct isst_pbf_info pbf_info;
struct isst_fact_info fact_info;
};
#define ISST_MAX_TDP_LEVELS (4 + 1) /* +1 for base config */
struct isst_pkg_ctdp {
int locked;
int version;
int processed;
int levels;
int current_level;
int enabled;
struct isst_pkg_ctdp_level_info ctdp_level[ISST_MAX_TDP_LEVELS];
};
enum isst_platform_param {
ISST_PARAM_MBOX_DELAY,
ISST_PARAM_MBOX_RETRIES,
};
struct isst_platform_ops {
int (*get_disp_freq_multiplier)(void);
int (*get_trl_max_levels)(void);
char *(*get_trl_level_name)(int level);
void (*update_platform_param)(enum isst_platform_param param, int value);
int (*is_punit_valid)(struct isst_id *id);
int (*read_pm_config)(struct isst_id *id, int *cp_state, int *cp_cap);
int (*get_config_levels)(struct isst_id *id, struct isst_pkg_ctdp *pkg_ctdp);
int (*get_ctdp_control)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level);
int (*get_tdp_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level);
int (*get_pwr_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level);
int (*get_coremask_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level);
int (*get_get_trl)(struct isst_id *id, int level, int avx_level, int *trl);
int (*get_get_trls)(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level);
int (*get_trl_bucket_info)(struct isst_id *id, int level, unsigned long long *buckets_info);
int (*set_tdp_level)(struct isst_id *id, int tdp_level);
int (*get_pbf_info)(struct isst_id *id, int level, struct isst_pbf_info *pbf_info);
int (*set_pbf_fact_status)(struct isst_id *id, int pbf, int enable);
int (*get_fact_info)(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info);
void (*adjust_uncore_freq)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level);
int (*get_clos_information)(struct isst_id *id, int *enable, int *type);
int (*pm_qos_config)(struct isst_id *id, int enable_clos, int priority_type);
int (*pm_get_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config);
int (*set_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config);
int (*clos_get_assoc_status)(struct isst_id *id, int *clos_id);
int (*clos_associate)(struct isst_id *id, int clos_id);
};
extern int is_cpu_in_power_domain(int cpu, struct isst_id *id);
extern int get_topo_max_cpus(void);
extern int get_cpu_count(struct isst_id *id);
extern int get_max_punit_core_id(struct isst_id *id);
extern int api_version(void);
/* Common interfaces */
FILE *get_output_file(void);
extern int is_debug_enabled(void);
extern void debug_printf(const char *format, ...);
extern int out_format_is_json(void);
extern void set_isst_id(struct isst_id *id, int cpu);
extern size_t alloc_cpu_set(cpu_set_t **cpu_set);
extern void free_cpu_set(cpu_set_t *cpu_set);
extern int find_phy_core_num(int logical_cpu);
extern 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);
extern int isst_send_msr_command(unsigned int cpu, unsigned int command,
int write, unsigned long long *req_resp);
extern int isst_set_platform_ops(int api_version);
extern void isst_update_platform_param(enum isst_platform_param, int vale);
extern int isst_get_disp_freq_multiplier(void);
extern int isst_get_trl_max_levels(void);
extern char *isst_get_trl_level_name(int level);
extern int isst_is_punit_valid(struct isst_id *id);
extern int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev);
extern int isst_get_ctdp_control(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level);
extern int isst_get_coremask_info(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level);
extern void isst_adjust_uncore_freq(struct isst_id *id, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level);
extern int isst_get_process_ctdp(struct isst_id *id, int tdp_level,
struct isst_pkg_ctdp *pkg_dev);
extern void isst_get_process_ctdp_complete(struct isst_id *id,
struct isst_pkg_ctdp *pkg_dev);
extern void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level,
struct isst_pkg_ctdp *pkg_dev);
extern void isst_ctdp_display_core_info(struct isst_id *id, FILE *outf, char *prefix,
unsigned int val, char *str0, char *str1);
extern void isst_ctdp_display_information_start(FILE *outf);
extern void isst_ctdp_display_information_end(FILE *outf);
extern void isst_pbf_display_information(struct isst_id *id, FILE *outf, int level,
struct isst_pbf_info *info);
extern int isst_set_tdp_level(struct isst_id *id, int tdp_level);
extern int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable);
extern int isst_get_pbf_info(struct isst_id *id, int level,
struct isst_pbf_info *pbf_info);
extern int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket,
struct isst_fact_info *fact_info);
extern void isst_fact_display_information(struct isst_id *id, FILE *outf, int level,
int fact_bucket, int fact_avx,
struct isst_fact_info *fact_info);
extern int isst_set_trl(struct isst_id *id, unsigned long long trl);
extern int isst_get_trl(struct isst_id *id, unsigned long long *trl);
extern int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl);
extern int isst_get_config_tdp_lock_status(struct isst_id *id);
extern int isst_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type);
extern int isst_pm_get_clos(struct isst_id *id, int clos,
struct isst_clos_config *clos_config);
extern int isst_set_clos(struct isst_id *id, int clos,
struct isst_clos_config *clos_config);
extern int isst_clos_associate(struct isst_id *id, int clos);
extern int isst_clos_get_assoc_status(struct isst_id *id, int *clos_id);
extern void isst_clos_display_information(struct isst_id *id, FILE *outf, int clos,
struct isst_clos_config *clos_config);
extern void isst_clos_display_assoc_information(struct isst_id *id, FILE *outf, int clos);
extern void isst_display_result(struct isst_id *id, FILE *outf, char *feature, char *cmd,
int result);
extern int isst_clos_get_clos_information(struct isst_id *id, int *enable, int *type);
extern void isst_clos_display_clos_information(struct isst_id *id, FILE *outf,
int clos_enable, int type,
int state, int cap);
extern int is_clx_n_platform(void);
extern int get_cpufreq_base_freq(int cpu);
extern int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap);
extern void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg);
extern int is_skx_based_platform(void);
extern int is_spr_platform(void);
extern int is_emr_platform(void);
extern int is_icx_platform(void);
extern void isst_trl_display_information(struct isst_id *id, FILE *outf, unsigned long long trl);
extern void set_cpu_online_offline(int cpu, int state);
extern 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);
extern int isst_daemon(int debug_mode, int poll_interval, int no_daemon);
extern void process_level_change(struct isst_id *id);
extern int hfi_main(void);
extern void hfi_exit(void);
/* Interface specific callbacks */
extern struct isst_platform_ops *mbox_get_platform_ops(void);
extern struct isst_platform_ops *tpmi_get_platform_ops(void);
/* Cgroup related interface */
extern int enable_cpuset_controller(void);
extern int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask,
int level, int cpu_0_only);
extern int use_cgroupv2(void);
#endif
@@ -0,0 +1,613 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
# -*- coding: utf-8 -*-
#
""" This utility can be used to debug and tune the performance of the
intel_pstate driver. This utility can be used in two ways:
- If there is Linux trace file with pstate_sample events enabled, then
this utility can parse the trace file and generate performance plots.
- If user has not specified a trace file as input via command line parameters,
then this utility enables and collects trace data for a user specified interval
and generates performance plots.
Prerequisites:
Python version 3.6.x or higher
gnuplot 5.0 or higher
python3-gnuplot 1.8 or higher
(Most of the distributions have these required packages. They may be called
gnuplot-py, python-gnuplot or python3-gnuplot, gnuplot-nox, ... )
HWP (Hardware P-States are disabled)
Kernel config for Linux trace is enabled
see print_help(): for Usage and Output details
"""
from datetime import datetime
import subprocess
import os
import time
import re
import signal
import sys
import getopt
import Gnuplot
from numpy import *
from decimal import *
__author__ = "Srinivas Pandruvada"
__copyright__ = " Copyright (c) 2017, Intel Corporation. "
__license__ = "GPL version 2"
MAX_CPUS = 256
# Define the csv file columns
C_COMM = 18
C_GHZ = 17
C_ELAPSED = 16
C_SAMPLE = 15
C_DURATION = 14
C_LOAD = 13
C_BOOST = 12
C_FREQ = 11
C_TSC = 10
C_APERF = 9
C_MPERF = 8
C_TO = 7
C_FROM = 6
C_SCALED = 5
C_CORE = 4
C_USEC = 3
C_SEC = 2
C_CPU = 1
global sample_num, last_sec_cpu, last_usec_cpu, start_time, testname, trace_file
# 11 digits covers uptime to 115 days
getcontext().prec = 11
sample_num =0
last_sec_cpu = [0] * MAX_CPUS
last_usec_cpu = [0] * MAX_CPUS
def print_help(driver_name):
print('%s_tracer.py:'%driver_name)
print(' Usage:')
print(' If the trace file is available, then to simply parse and plot, use (sudo not required):')
print(' ./%s_tracer.py [-c cpus] -t <trace_file> -n <test_name>'%driver_name)
print(' Or')
print(' ./%s_tracer.py [--cpu cpus] ---trace_file <trace_file> --name <test_name>'%driver_name)
print(' To generate trace file, parse and plot, use (sudo required):')
print(' sudo ./%s_tracer.py [-c cpus] -i <interval> -n <test_name> -m <kbytes>'%driver_name)
print(' Or')
print(' sudo ./%s_tracer.py [--cpu cpus] --interval <interval> --name <test_name> --memory <kbytes>'%driver_name)
print(' Optional argument:')
print(' cpus: comma separated list of CPUs')
print(' kbytes: Kilo bytes of memory per CPU to allocate to the trace buffer. Default: 10240')
print(' Output:')
print(' If not already present, creates a "results/test_name" folder in the current working directory with:')
print(' cpu.csv - comma seperated values file with trace contents and some additional calculations.')
print(' cpu???.csv - comma seperated values file for CPU number ???.')
print(' *.png - a variety of PNG format plot files created from the trace contents and the additional calculations.')
print(' Notes:')
print(' Avoid the use of _ (underscore) in test names, because in gnuplot it is a subscript directive.')
print(' Maximum number of CPUs is {0:d}. If there are more the script will abort with an error.'.format(MAX_CPUS))
print(' Off-line CPUs cause the script to list some warnings, and create some empty files. Use the CPU mask feature for a clean run.')
print(' Empty y range warnings for autoscaled plots can occur and can be ignored.')
def plot_perf_busy_with_sample(cpu_index):
""" Plot method to per cpu information """
file_name = 'cpu{:0>3}.csv'.format(cpu_index)
if os.path.exists(file_name):
output_png = "cpu%03d_perf_busy_vs_samples.png" % cpu_index
g_plot = common_all_gnuplot_settings(output_png)
# autoscale this one, no set y1 range
g_plot('set y2range [0:200]')
g_plot('set y2tics 0, 10')
g_plot('set title "{} : cpu perf busy vs. sample : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
# Override common
g_plot('set xlabel "Samples"')
g_plot('set ylabel "P-State"')
g_plot('set y2label "Scaled Busy/performance/io-busy(%)"')
set_4_plot_linestyles(g_plot)
g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_SAMPLE, C_CORE))
g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_SAMPLE, C_SCALED))
g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_SAMPLE, C_BOOST))
g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_SAMPLE, C_TO))
def plot_perf_busy(cpu_index):
""" Plot some per cpu information """
file_name = 'cpu{:0>3}.csv'.format(cpu_index)
if os.path.exists(file_name):
output_png = "cpu%03d_perf_busy.png" % cpu_index
g_plot = common_all_gnuplot_settings(output_png)
# autoscale this one, no set y1 range
g_plot('set y2range [0:200]')
g_plot('set y2tics 0, 10')
g_plot('set title "{} : perf busy : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
g_plot('set ylabel "P-State"')
g_plot('set y2label "Scaled Busy/performance/io-busy(%)"')
set_4_plot_linestyles(g_plot)
g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_ELAPSED, C_CORE))
g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_ELAPSED, C_SCALED))
g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_ELAPSED, C_BOOST))
g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_ELAPSED, C_TO))
def plot_durations(cpu_index):
""" Plot per cpu durations """
file_name = 'cpu{:0>3}.csv'.format(cpu_index)
if os.path.exists(file_name):
output_png = "cpu%03d_durations.png" % cpu_index
g_plot = common_all_gnuplot_settings(output_png)
# autoscale this one, no set y range
g_plot('set title "{} : durations : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
g_plot('set ylabel "Timer Duration (MilliSeconds)"')
# override common
g_plot('set key off')
set_4_plot_linestyles(g_plot)
g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_DURATION))
def plot_loads(cpu_index):
""" Plot per cpu loads """
file_name = 'cpu{:0>3}.csv'.format(cpu_index)
if os.path.exists(file_name):
output_png = "cpu%03d_loads.png" % cpu_index
g_plot = common_all_gnuplot_settings(output_png)
g_plot('set yrange [0:100]')
g_plot('set ytics 0, 10')
g_plot('set title "{} : loads : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
g_plot('set ylabel "CPU load (percent)"')
# override common
g_plot('set key off')
set_4_plot_linestyles(g_plot)
g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_LOAD))
def plot_pstate_cpu_with_sample():
""" Plot all cpu information """
if os.path.exists('cpu.csv'):
output_png = 'all_cpu_pstates_vs_samples.png'
g_plot = common_all_gnuplot_settings(output_png)
# autoscale this one, no set y range
# override common
g_plot('set xlabel "Samples"')
g_plot('set ylabel "P-State"')
g_plot('set title "{} : cpu pstate vs. sample : {:%F %H:%M}"'.format(testname, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_SAMPLE, C_TO)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def plot_pstate_cpu():
""" Plot all cpu information from csv files """
output_png = 'all_cpu_pstates.png'
g_plot = common_all_gnuplot_settings(output_png)
# autoscale this one, no set y range
g_plot('set ylabel "P-State"')
g_plot('set title "{} : cpu pstates : {:%F %H:%M}"'.format(testname, datetime.now()))
# the following command is really cool, but doesn't work with the CPU masking option because it aborts on the first missing file.
# plot_str = 'plot for [i=0:*] file=sprintf("cpu%03d.csv",i) title_s=sprintf("cpu%03d",i) file using 16:7 pt 7 ps 1 title title_s'
#
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_TO)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def plot_load_cpu():
""" Plot all cpu loads """
output_png = 'all_cpu_loads.png'
g_plot = common_all_gnuplot_settings(output_png)
g_plot('set yrange [0:100]')
g_plot('set ylabel "CPU load (percent)"')
g_plot('set title "{} : cpu loads : {:%F %H:%M}"'.format(testname, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_LOAD)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def plot_frequency_cpu():
""" Plot all cpu frequencies """
output_png = 'all_cpu_frequencies.png'
g_plot = common_all_gnuplot_settings(output_png)
# autoscale this one, no set y range
g_plot('set ylabel "CPU Frequency (GHz)"')
g_plot('set title "{} : cpu frequencies : {:%F %H:%M}"'.format(testname, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_FREQ)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def plot_duration_cpu():
""" Plot all cpu durations """
output_png = 'all_cpu_durations.png'
g_plot = common_all_gnuplot_settings(output_png)
# autoscale this one, no set y range
g_plot('set ylabel "Timer Duration (MilliSeconds)"')
g_plot('set title "{} : cpu durations : {:%F %H:%M}"'.format(testname, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_DURATION)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def plot_scaled_cpu():
""" Plot all cpu scaled busy """
output_png = 'all_cpu_scaled.png'
g_plot = common_all_gnuplot_settings(output_png)
# autoscale this one, no set y range
g_plot('set ylabel "Scaled Busy (Unitless)"')
g_plot('set title "{} : cpu scaled busy : {:%F %H:%M}"'.format(testname, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_SCALED)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def plot_boost_cpu():
""" Plot all cpu IO Boosts """
output_png = 'all_cpu_boost.png'
g_plot = common_all_gnuplot_settings(output_png)
g_plot('set yrange [0:100]')
g_plot('set ylabel "CPU IO Boost (percent)"')
g_plot('set title "{} : cpu io boost : {:%F %H:%M}"'.format(testname, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_BOOST)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def plot_ghz_cpu():
""" Plot all cpu tsc ghz """
output_png = 'all_cpu_ghz.png'
g_plot = common_all_gnuplot_settings(output_png)
# autoscale this one, no set y range
g_plot('set ylabel "TSC Frequency (GHz)"')
g_plot('set title "{} : cpu TSC Frequencies (Sanity check calculation) : {:%F %H:%M}"'.format(testname, datetime.now()))
title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_GHZ)
g_plot('title_list = "{}"'.format(title_list))
g_plot(plot_str)
def common_all_gnuplot_settings(output_png):
""" common gnuplot settings for multiple CPUs one one graph. """
g_plot = common_gnuplot_settings()
g_plot('set output "' + output_png + '"')
return(g_plot)
def common_gnuplot_settings():
""" common gnuplot settings. """
g_plot = Gnuplot.Gnuplot(persist=1)
# The following line is for rigor only. It seems to be assumed for .csv files
g_plot('set datafile separator \",\"')
g_plot('set ytics nomirror')
g_plot('set xtics nomirror')
g_plot('set xtics font ", 10"')
g_plot('set ytics font ", 10"')
g_plot('set tics out scale 1.0')
g_plot('set grid')
g_plot('set key out horiz')
g_plot('set key bot center')
g_plot('set key samplen 2 spacing .8 font ", 9"')
g_plot('set term png size 1200, 600')
g_plot('set title font ", 11"')
g_plot('set ylabel font ", 10"')
g_plot('set xlabel font ", 10"')
g_plot('set xlabel offset 0, 0.5')
g_plot('set xlabel "Elapsed Time (Seconds)"')
return(g_plot)
def set_4_plot_linestyles(g_plot):
""" set the linestyles used for 4 plots in 1 graphs. """
g_plot('set style line 1 linetype 1 linecolor rgb "green" pointtype -1')
g_plot('set style line 2 linetype 1 linecolor rgb "red" pointtype -1')
g_plot('set style line 3 linetype 1 linecolor rgb "purple" pointtype -1')
g_plot('set style line 4 linetype 1 linecolor rgb "blue" pointtype -1')
def store_csv(cpu_int, time_pre_dec, time_post_dec, core_busy, scaled, _from, _to, mperf, aperf, tsc, freq_ghz, io_boost, common_comm, load, duration_ms, sample_num, elapsed_time, tsc_ghz, cpu_mask):
""" Store master csv file information """
global graph_data_present
if cpu_mask[cpu_int] == 0:
return
try:
f_handle = open('cpu.csv', 'a')
string_buffer = "CPU_%03u, %05u, %06u, %u, %u, %u, %u, %u, %u, %u, %.4f, %u, %.2f, %.3f, %u, %.3f, %.3f, %s\n" % (cpu_int, int(time_pre_dec), int(time_post_dec), int(core_busy), int(scaled), int(_from), int(_to), int(mperf), int(aperf), int(tsc), freq_ghz, int(io_boost), load, duration_ms, sample_num, elapsed_time, tsc_ghz, common_comm)
f_handle.write(string_buffer);
f_handle.close()
except:
print('IO error cpu.csv')
return
graph_data_present = True;
def split_csv(current_max_cpu, cpu_mask):
""" seperate the all csv file into per CPU csv files. """
if os.path.exists('cpu.csv'):
for index in range(0, current_max_cpu + 1):
if cpu_mask[int(index)] != 0:
os.system('grep -m 1 common_cpu cpu.csv > cpu{:0>3}.csv'.format(index))
os.system('grep CPU_{:0>3} cpu.csv >> cpu{:0>3}.csv'.format(index, index))
def fix_ownership(path):
"""Change the owner of the file to SUDO_UID, if required"""
uid = os.environ.get('SUDO_UID')
gid = os.environ.get('SUDO_GID')
if uid is not None:
os.chown(path, int(uid), int(gid))
def cleanup_data_files():
""" clean up existing data files """
if os.path.exists('cpu.csv'):
os.remove('cpu.csv')
f_handle = open('cpu.csv', 'a')
f_handle.write('common_cpu, common_secs, common_usecs, core_busy, scaled_busy, from, to, mperf, aperf, tsc, freq, boost, load, duration_ms, sample_num, elapsed_time, tsc_ghz, common_comm')
f_handle.write('\n')
f_handle.close()
def clear_trace_file():
""" Clear trace file """
try:
f_handle = open('/sys/kernel/tracing/trace', 'w')
f_handle.close()
except:
print('IO error clearing trace file ')
sys.exit(2)
def enable_trace(trace_file):
""" Enable trace """
try:
open(trace_file,'w').write("1")
except:
print('IO error enabling trace ')
sys.exit(2)
def disable_trace(trace_file):
""" Disable trace """
try:
open(trace_file, 'w').write("0")
except:
print('IO error disabling trace ')
sys.exit(2)
def set_trace_buffer_size(memory):
""" Set trace buffer size """
try:
with open('/sys/kernel/tracing/buffer_size_kb', 'w') as fp:
fp.write(memory)
except:
print('IO error setting trace buffer size ')
sys.exit(2)
def free_trace_buffer():
""" Free the trace buffer memory """
try:
open('/sys/kernel/tracing/buffer_size_kb'
, 'w').write("1")
except:
print('IO error freeing trace buffer ')
sys.exit(2)
def read_trace_data(filename, cpu_mask):
""" Read and parse trace data """
global current_max_cpu
global sample_num, last_sec_cpu, last_usec_cpu, start_time
try:
data = open(filename, 'r').read()
except:
print('Error opening ', filename)
sys.exit(2)
for line in data.splitlines():
search_obj = \
re.search(r'(^(.*?)\[)((\d+)[^\]])(.*?)(\d+)([.])(\d+)(.*?core_busy=)(\d+)(.*?scaled=)(\d+)(.*?from=)(\d+)(.*?to=)(\d+)(.*?mperf=)(\d+)(.*?aperf=)(\d+)(.*?tsc=)(\d+)(.*?freq=)(\d+)'
, line)
if search_obj:
cpu = search_obj.group(3)
cpu_int = int(cpu)
cpu = str(cpu_int)
time_pre_dec = search_obj.group(6)
time_post_dec = search_obj.group(8)
core_busy = search_obj.group(10)
scaled = search_obj.group(12)
_from = search_obj.group(14)
_to = search_obj.group(16)
mperf = search_obj.group(18)
aperf = search_obj.group(20)
tsc = search_obj.group(22)
freq = search_obj.group(24)
common_comm = search_obj.group(2).replace(' ', '')
# Not all kernel versions have io_boost field
io_boost = '0'
search_obj = re.search(r'.*?io_boost=(\d+)', line)
if search_obj:
io_boost = search_obj.group(1)
if sample_num == 0 :
start_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000)
sample_num += 1
if last_sec_cpu[cpu_int] == 0 :
last_sec_cpu[cpu_int] = time_pre_dec
last_usec_cpu[cpu_int] = time_post_dec
else :
duration_us = (int(time_pre_dec) - int(last_sec_cpu[cpu_int])) * 1000000 + (int(time_post_dec) - int(last_usec_cpu[cpu_int]))
duration_ms = Decimal(duration_us) / Decimal(1000)
last_sec_cpu[cpu_int] = time_pre_dec
last_usec_cpu[cpu_int] = time_post_dec
elapsed_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) - start_time
load = Decimal(int(mperf)*100)/ Decimal(tsc)
freq_ghz = Decimal(freq)/Decimal(1000000)
# Sanity check calculation, typically anomalies indicate missed samples
# However, check for 0 (should never occur)
tsc_ghz = Decimal(0)
if duration_ms != Decimal(0) :
tsc_ghz = Decimal(tsc)/duration_ms/Decimal(1000000)
store_csv(cpu_int, time_pre_dec, time_post_dec, core_busy, scaled, _from, _to, mperf, aperf, tsc, freq_ghz, io_boost, common_comm, load, duration_ms, sample_num, elapsed_time, tsc_ghz, cpu_mask)
if cpu_int > current_max_cpu:
current_max_cpu = cpu_int
# End of for each trace line loop
# Now seperate the main overall csv file into per CPU csv files.
split_csv(current_max_cpu, cpu_mask)
def signal_handler(signal, frame):
print(' SIGINT: Forcing cleanup before exit.')
if interval:
disable_trace(trace_file)
clear_trace_file()
# Free the memory
free_trace_buffer()
sys.exit(0)
if __name__ == "__main__":
trace_file = "/sys/kernel/tracing/events/power/pstate_sample/enable"
signal.signal(signal.SIGINT, signal_handler)
interval = ""
filename = ""
cpu_list = ""
testname = ""
memory = "10240"
graph_data_present = False;
valid1 = False
valid2 = False
cpu_mask = zeros((MAX_CPUS,), dtype=int)
try:
opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="])
except getopt.GetoptError:
print_help('intel_pstate')
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print_help('intel_pstate')
sys.exit()
elif opt in ("-t", "--trace_file"):
valid1 = True
location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
filename = os.path.join(location, arg)
elif opt in ("-i", "--interval"):
valid1 = True
interval = arg
elif opt in ("-c", "--cpu"):
cpu_list = arg
elif opt in ("-n", "--name"):
valid2 = True
testname = arg
elif opt in ("-m", "--memory"):
memory = arg
if not (valid1 and valid2):
print_help('intel_pstate')
sys.exit()
if cpu_list:
for p in re.split("[,]", cpu_list):
if int(p) < MAX_CPUS :
cpu_mask[int(p)] = 1
else:
for i in range (0, MAX_CPUS):
cpu_mask[i] = 1
if not os.path.exists('results'):
os.mkdir('results')
# The regular user needs to own the directory, not root.
fix_ownership('results')
os.chdir('results')
if os.path.exists(testname):
print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.')
sys.exit()
os.mkdir(testname)
# The regular user needs to own the directory, not root.
fix_ownership(testname)
os.chdir(testname)
# Temporary (or perhaps not)
cur_version = sys.version_info
print('python version (should be >= 3.6):')
print(cur_version)
# Left as "cleanup" for potential future re-run ability.
cleanup_data_files()
if interval:
filename = "/sys/kernel/tracing/trace"
clear_trace_file()
set_trace_buffer_size(memory)
enable_trace(trace_file)
print('Sleeping for ', interval, 'seconds')
time.sleep(int(interval))
disable_trace(trace_file)
current_max_cpu = 0
read_trace_data(filename, cpu_mask)
if interval:
clear_trace_file()
# Free the memory
free_trace_buffer()
if graph_data_present == False:
print('No valid data to plot')
sys.exit(2)
for cpu_no in range(0, current_max_cpu + 1):
plot_perf_busy_with_sample(cpu_no)
plot_perf_busy(cpu_no)
plot_durations(cpu_no)
plot_loads(cpu_no)
plot_pstate_cpu_with_sample()
plot_pstate_cpu()
plot_load_cpu()
plot_frequency_cpu()
plot_duration_cpu()
plot_scaled_cpu()
plot_boost_cpu()
plot_ghz_cpu()
# It is preferrable, but not necessary, that the regular user owns the files, not root.
for root, dirs, files in os.walk('.'):
for f in files:
fix_ownership(f)
os.chdir('../../')
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
turbostat
@@ -0,0 +1,58 @@
# SPDX-License-Identifier: GPL-2.0
CC = $(CROSS_COMPILE)gcc
BUILD_OUTPUT := $(CURDIR)
PREFIX ?= /usr
DESTDIR ?=
DAY := $(shell date +%Y.%m.%d)
SNAPSHOT = turbostat-$(DAY)
ifeq ("$(origin O)", "command line")
BUILD_OUTPUT := $(O)
endif
turbostat : turbostat.c
override CFLAGS += -O2 -Wall -Wextra -I../../../include
override CFLAGS += -DMSRHEADER='"../../../../arch/x86/include/asm/msr-index.h"'
override CFLAGS += -DINTEL_FAMILY_HEADER='"../../../../arch/x86/include/asm/intel-family.h"'
override CFLAGS += -DBUILD_BUG_HEADER='"../../../../include/linux/build_bug.h"'
override CFLAGS += -D_FILE_OFFSET_BITS=64
override CFLAGS += -D_FORTIFY_SOURCE=2
%: %.c
@mkdir -p $(BUILD_OUTPUT)
$(CC) $(CFLAGS) $< -o $(BUILD_OUTPUT)/$@ $(LDFLAGS) -lcap -lrt
.PHONY : clean
clean :
@rm -f $(BUILD_OUTPUT)/turbostat
@rm -f $(SNAPSHOT).tar.gz
install : turbostat
install -d $(DESTDIR)$(PREFIX)/bin
install $(BUILD_OUTPUT)/turbostat $(DESTDIR)$(PREFIX)/bin/turbostat
install -d $(DESTDIR)$(PREFIX)/share/man/man8
install -m 644 turbostat.8 $(DESTDIR)$(PREFIX)/share/man/man8
snapshot: turbostat
@rm -rf $(SNAPSHOT)
@mkdir $(SNAPSHOT)
@cp turbostat Makefile turbostat.c turbostat.8 ../../../../arch/x86/include/asm/intel-family.h $(SNAPSHOT)
@sed -e 's/^#include <linux\/bits.h>/#include "bits.h"/' ../../../../arch/x86/include/asm/msr-index.h > $(SNAPSHOT)/msr-index.h
@echo '#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))' >> $(SNAPSHOT)/msr-index.h
@echo "#define BIT(x) (1 << (x))" > $(SNAPSHOT)/bits.h
@echo "#define BIT_ULL(nr) (1ULL << (nr))" >> $(SNAPSHOT)/bits.h
@echo "#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))" >> $(SNAPSHOT)/bits.h
@echo "#define GENMASK_ULL(h, l) (((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h))))" >> $(SNAPSHOT)/bits.h
@echo '#define BUILD_BUG_ON(cond) do { enum { compile_time_check ## __COUNTER__ = 1/(!(cond)) }; } while (0)' > $(SNAPSHOT)/build_bug.h
@echo '#define __must_be_array(arr) 0' >> $(SNAPSHOT)/build_bug.h
@echo PWD=. > $(SNAPSHOT)/Makefile
@echo "CFLAGS += -DMSRHEADER='\"msr-index.h\"'" >> $(SNAPSHOT)/Makefile
@echo "CFLAGS += -DINTEL_FAMILY_HEADER='\"intel-family.h\"'" >> $(SNAPSHOT)/Makefile
@echo "CFLAGS += -DBUILD_BUG_HEADER='\"build_bug.h\"'" >> $(SNAPSHOT)/Makefile
@sed -e's/.*MSRHEADER.*//' -e's/.*INTEL_FAMILY_HEADER.*//' -e's/.*BUILD_BUG_HEADER.*//' Makefile >> $(SNAPSHOT)/Makefile
@rm -f $(SNAPSHOT).tar.gz
tar cvzf $(SNAPSHOT).tar.gz $(SNAPSHOT)
@@ -0,0 +1,586 @@
.TH TURBOSTAT 8
.SH NAME
turbostat \- Report processor frequency and idle statistics
.SH SYNOPSIS
.ft B
.B turbostat
.RB [ Options ]
.RB command
.br
.B turbostat
.RB [ Options ]
.RB [ "\--interval seconds" ]
.SH DESCRIPTION
\fBturbostat \fP reports processor topology, frequency,
idle power-state statistics, temperature and power on X86 processors.
There are two ways to invoke turbostat.
The first method is to supply a
\fBcommand\fP, which is forked and statistics are printed
in one-shot upon its completion.
The second method is to omit the command,
and turbostat displays statistics every 5 seconds interval.
The 5-second interval can be changed using the --interval option.
.PP
Some information is not available on older processors.
.SS Options
Options can be specified with a single or double '-', and only as much of the option
name as necessary to disambiguate it from others is necessary. Note that options are case-sensitive.
.PP
\fB--add attributes\fP add column with counter having specified 'attributes'. The 'location' attribute is required, all others are optional.
.nf
location: {\fBmsrDDD\fP | \fBmsr0xXXX\fP | \fB/sys/path...\fP | \fBperf/<device>/<event>\fP}
msrDDD is a decimal offset, eg. msr16
msr0xXXX is a hex offset, eg. msr0x10
/sys/path... is an absolute path to a sysfs attribute
<device> is a perf device from /sys/bus/event_source/devices/<device> eg. cstate_core
On Intel hybrid platforms, instead of one "cpu" perf device there are two, "cpu_core" and "cpu_atom" devices for P and E cores respectively.
Turbostat, in this case, allow user to use "cpu" device and will automatically detect the type of a CPU and translate it to "cpu_core" and "cpu_atom" accordingly.
For a complete example see "ADD PERF COUNTER EXAMPLE #2 (using virtual "cpu" device)".
<event> is a perf event for given device from /sys/bus/event_source/devices/<device>/events/<event> eg. c1-residency
perf/cstate_core/c1-residency would then use /sys/bus/event_source/devices/cstate_core/events/c1-residency
scope: {\fBcpu\fP | \fBcore\fP | \fBpackage\fP}
sample and print the counter for every cpu, core, or package.
default: cpu
size: {\fBu32\fP | \fBu64\fP }
MSRs are read as 64-bits, u32 truncates the displayed value to 32-bits.
default: u64
format: {\fBraw\fP | \fBdelta\fP | \fBpercent\fP | \fBaverage\fP}
'raw' shows the MSR contents in hex.
'delta' shows the difference in values during the measurement interval.
'percent' shows the delta as a percentage of the cycles elapsed.
'average' similar to raw, but also averaged for node/package summaries (or when using -S).
default: delta
name: "name_string"
Any string that does not match a key-word above is used
as the column header.
.fi
.PP
\fB--add pmt,[attr_name=attr_value, ...]\fP add column with a PMT (Intel Platform Monitoring Technology) counter in a similar way to --add option above, but require PMT metadata to be supplied to correctly read and display the counter. The metadata can be found in the Intel PMT XML files, hosted at https://github.com/intel/Intel-PMT. For a complete example see "ADD PMT COUNTER EXAMPLE".
.nf
name="name_string"
For column header.
type={\fBraw\fP}
'raw' shows the counter contents in hex.
default: raw
format={\fBraw\fP | \fBdelta\fP}
'raw' shows the counter contents in hex.
'delta' shows the difference in values during the measurement interval.
default: raw
domain={\fBcpu%u\fP | \fBcore%u\fP | \fBpackage%u\fP}
'cpu' per cpu/thread counter.
'core' per core counter.
'package' per package counter.
'%u' denotes id of the domain that the counter is associated with. For example core4 would mean that the counter is associated with core number 4.
offset=\fB%u\fP
'%u' offset within the PMT MMIO region.
lsb=\fB%u\fP
'%u' least significant bit within the 64 bit value read from 'offset'. Together with 'msb', used to form a read mask.
msb=\fB%u\fP
'%u' most significant bit within the 64 bit value read from 'offset'. Together with 'lsb', used to form a read mask.
guid=\fB%x\fP
'%x' hex identifier of the PMT MMIO region.
.fi
.PP
\fB--cpu cpu-set\fP limit output to system summary plus the specified cpu-set. If cpu-set is the string "core", then the system summary plus the first CPU in each core are printed -- eg. subsequent HT siblings are not printed. Or if cpu-set is the string "package", then the system summary plus the first CPU in each package is printed. Otherwise, the system summary plus the specified set of CPUs are printed. The cpu-set is ordered from low to high, comma delimited with ".." and "-" permitted to denote a range. eg. 1,2,8,14..17,21-44
.PP
\fB--hide column\fP do not show the specified built-in columns. May be invoked multiple times, or with a comma-separated list of column names.
.PP
\fB--enable column\fP show the specified built-in columns, which are otherwise disabled, by default. Currently the only built-in counters disabled by default are "usec", "Time_Of_Day_Seconds", "APIC" and "X2APIC".
The column name "all" can be used to enable all disabled-by-default built-in counters.
.PP
\fB--show column\fP show only the specified built-in columns. May be invoked multiple times, or with a comma-separated list of column names.
.PP
\fB--show CATEGORY --hide CATEGORY\fP Show and hide also accept a comma-separated-list of CATEGORIES of columns: "all", "topology", "idle", "frequency", "power", "cpuidle", "hwidle", "swidle", "cache", "llc", "other". "idle" (enabled by default), includes "hwidle" and "pct_idle". "cpuidle" (default disabled) includes cpuidle software invocation counters. "swidle" includes "cpuidle" plus "pct_idle". "hwidle" includes only hardware based idle residency counters. Older versions of turbostat used the term "sysfs" for what is now "swidle".
.PP
\fB--Dump\fP displays the raw counter values.
.PP
\fB--quiet\fP Do not decode and print the system configuration header information.
.PP
\fB--no-msr\fP Disable all the uses of the MSR driver.
.PP
\fB--no-perf\fP Disable all the uses of the perf API.
.PP
\fB--force\fP Force turbostat to run on an unsupported platform (minimal defaults).
.PP
\fB--interval seconds\fP overrides the default 5.0 second measurement interval.
.PP
\fB--num_iterations num\fP number of the measurement iterations.
.PP
\fB--header_iterations num\fP print header every num iterations.
.PP
\fB--out output_file\fP turbostat output is written to the specified output_file.
The file is truncated if it already exists, and it is created if it does not exist.
.PP
\fB--help\fP displays usage for the most common parameters.
.PP
\fB--Joules\fP displays energy in Joules, rather than dividing Joules by time to print power in Watts.
.PP
\fB--list\fP display column header names available for use by --show and --hide, then exit.
.PP
\fB--Summary\fP limits output to a 1-line System Summary for each interval.
.PP
\fB--TCC temperature\fP sets the Thermal Control Circuit temperature for systems which do not export that value. This is used for making sense of the Digital Thermal Sensor outputs, as they return degrees Celsius below the TCC activation temperature.
.PP
\fB--version\fP displays the version.
.PP
The \fBcommand\fP parameter forks \fBcommand\fP, and upon its exit,
displays the statistics gathered since it was forked.
.PP
.SH ROW DESCRIPTIONS
The system configuration dump (if --quiet is not used) is followed by statistics. The first row of the statistics labels the content of each column (below). The second row of statistics is the system summary line. The system summary line has a '-' in the columns for the Package, Core, and CPU. The contents of the system summary line depends on the type of column. Columns that count items (eg. IRQ) show the sum across all CPUs in the system. Columns that show a percentage show the average across all CPUs in the system. Columns that dump raw MSR values simply show 0 in the summary. After the system summary row, each row describes a specific Package/Core/CPU. Note that if the --cpu parameter is used to limit which specific CPUs are displayed, turbostat will still collect statistics for all CPUs in the system and will still show the system summary for all CPUs in the system.
.SH COLUMN DESCRIPTIONS
.PP
\fBusec\fP For each CPU, the number of microseconds elapsed during counter collection, including thread migration -- if any. This counter is disabled by default, and is enabled with "--enable usec", or --debug. On the summary row, usec refers to the total elapsed time to snapshot the procfs/sysfs and collect the counters on all cpus.
.PP
\fBTime_Of_Day_Seconds\fP For each CPU, the gettimeofday(2) value (seconds.subsec since Epoch) when the counters ending the measurement interval were collected. This column is disabled by default, and can be enabled with "--enable Time_Of_Day_Seconds" or "--debug". On the summary row, Time_Of_Day_Seconds refers to the timestamp following collection of counters on the last CPU.
.PP
\fBCore\fP processor core number. Note that multiple CPUs per core indicate support for Intel(R) Hyper-Threading Technology (HT).
.PP
\fBCPU\fP Linux CPU (logical processor) number. Yes, it is okay that on many systems the CPUs are not listed in numerical order -- for efficiency reasons, turbostat runs in topology order, so HT siblings appear together.
.PP
\fBPackage\fP processor package number -- not present on systems with a single processor package.
.PP
\fBAvg_MHz\fP number of cycles executed divided by time elapsed. Note that this includes idle-time when 0 instructions are executed.
.PP
\fBBusy%\fP percent of the measurement interval that the CPU executes instructions, aka. % of time in "C0" state.
.PP
\fBBzy_MHz\fP average clock rate while the CPU was not idle (ie. in "c0" state).
.PP
\fBTSC_MHz\fP average MHz that the TSC ran during the entire interval.
.PP
\fBIRQ\fP The number of interrupts serviced by that CPU during the measurement interval. The system total line is the sum of interrupts serviced across all CPUs. turbostat parses /proc/interrupts to generate this summary.
.PP
\fBSMI\fP The number of System Management Interrupts serviced CPU during the measurement interval. While this counter is actually per-CPU, SMI are triggered on all processors, so the number should be the same for all CPUs.
.PP
\fBLLCMRPS\fP Last Level Cache Millions of References Per Second. For CPUs with an L3 LLC, this is the number of references that CPU made to the L3 (and the number of misses that CPU made to it's L2). For CPUs with an L2 LLC, this is the number of references to the L2 (and the number of misses to the CPU's L1). The system summary row shows the sum for all CPUs. In both cases, the value displayed is the actual value divided by 1,000,000. If this value is large, then the LLC%hit column is significant. If this value is small, then the LLC%hit column is not significant.
.PP
\fBLLC%hit\fP Last Level Cache Hit Rate %. Hit Rate Percent = 100.0 * Hits/References. The system summary row shows the weighted average for all CPUs (100.0 * Sum_Hits/Sum_References).
.PP
\fBL2MRPS\fP Level-2 Cache Millions of References Per Second. For CPUs with an L2 LLC, this is the same as LLC references. The system summary row shows the sum for all CPUs. In both cases, the value displayed is the actual value divided by 1,000,000. If this value is large, then the L2%hit column is significant. If this value is small, then the L2%hit column is not significant.
.PP
\fBL2%hit\fP Level-2 Cache Hit Rate %. Hit Rate Percent = 100.0 * Hits/References. The system summary row shows the weighted average for all CPUs (100.0 * (Sum_Hits)/Sum_References).
.PP
\fBC1, C2, C3...\fP The number times Linux requested the C1, C2, C3 idle state during the measurement interval. The system summary line shows the sum for all CPUs. These are C-state names as exported in /sys/devices/system/cpu/cpu*/cpuidle/state*/name. While their names are generic, their attributes are processor specific. They the system description section of output shows what MWAIT sub-states they are mapped to on each system. These counters are in the "cpuidle" group, which is disabled, by default.
.PP
\fBC1+, C2+, C3+...\fP The idle governor idle state misprediction statistics. Indicates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a deeper idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/below file. These counters are in the "cpuidle" group, which is disabled, by default.
.PP
\fBC1-, C2-, C3-...\fP The idle governor idle state misprediction statistics. Indicates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a shallower idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/above file. These counters are in the "cpuidle" group, which is disabled, by default.
.PP
\fBC1%, C2%, C3%\fP The residency percentage that Linux requested C1, C2, C3.... The system summary is the average of all CPUs in the system. Note that these are software, reflecting what was requested. The hardware counters reflect what was actually achieved. These counters are in the "pct_idle" group, which is enabled by default.
.PP
\fBCPU%c1, CPU%c3, CPU%c6, CPU%c7\fP show the percentage residency in hardware core idle states. These numbers are from hardware residency counters and are in the "hwidle" group, which is enabled, by default.
.PP
\fBCoreTmp\fP Degrees Celsius reported by the per-core Digital Thermal Sensor.
.PP
\fBPkgTmp\fP Degrees Celsius reported by the per-package Package Thermal Monitor.
.PP
\fBCoreThr\fP Core Thermal Throttling events during the measurement interval. Note that events since boot can be find in /sys/devices/system/cpu/cpu*/thermal_throttle/*
.PP
\fBGFX%rc6\fP The percentage of time the GPU is in the "render C6" state, rc6, during the measurement interval. From /sys/class/drm/card0/power/rc6_residency_ms or /sys/class/drm/card0/gt/gt0/rc6_residency_ms or /sys/class/drm/card0/device/tile0/gtN/gtidle/idle_residency_ms depending on the graphics driver being used.
.PP
\fBGFXMHz\fP Instantaneous snapshot of what sysfs presents at the end of the measurement interval. From /sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz or /sys/class/drm/card0/gt_cur_freq_mhz or /sys/class/drm/card0/gt/gt0/rps_cur_freq_mhz or /sys/class/drm/card0/device/tile0/gtN/freq0/cur_freq depending on the graphics driver being used.
.PP
\fBGFXAMHz\fP Instantaneous snapshot of what sysfs presents at the end of the measurement interval. From /sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz or /sys/class/drm/card0/gt_act_freq_mhz or /sys/class/drm/card0/gt/gt0/rps_act_freq_mhz or /sys/class/drm/card0/device/tile0/gtN/freq0/act_freq depending on the graphics driver being used.
.PP
\fBSAM%mc6\fP The percentage of time the SA Media is in the "module C6" state, mc6, during the measurement interval. From /sys/class/drm/card0/gt/gt1/rc6_residency_ms or /sys/class/drm/card0/device/tile0/gtN/gtidle/idle_residency_ms depending on the graphics driver being used.
.PP
\fBSAMMHz\fP Instantaneous snapshot of what sysfs presents at the end of the measurement interval. From /sys/class/drm/card0/gt/gt1/rps_cur_freq_mhz or /sys/class/drm/card0/device/tile0/gtN/freq0/cur_freq depending on the graphics driver being used.
.PP
\fBSAMAMHz\fP Instantaneous snapshot of what sysfs presents at the end of the measurement interval. From /sys/class/drm/card0/gt/gt1/rps_act_freq_mhz or /sys/class/drm/card0/device/tile0/gtN/freq0/act_freq depending on the graphics driver being used.
.PP
\fBTotl%C0\fP Weighted percentage of time that CPUs are busy. If N CPUs are busy during an interval, the percentage is N * 100%.
.PP
\fBAny%C0\fP Percentage of time that at least one CPU is busy.
.PP
\fBGFX%C0\fP Percentage of time that at least one GFX compute engine is busy.
.PP
\fBCPUGFX%\fP Percentage of time that at least one CPU is busy at the same time as at least one Graphics compute engine is busy.
.PP
\fBPkg%pc2, Pkg%pc3, Pkg%pc6, Pkg%pc7\fP percentage residency in hardware package idle states. These numbers are from hardware residency counters.
.PP
\fBPkgWatt\fP Watts consumed by the whole package.
.PP
\fBCorWatt\fP Watts consumed by the core part of the package.
.PP
\fBGFXWatt\fP Watts consumed by the Graphics part of the package -- available only on client processors.
.PP
\fBRAMWatt\fP Watts consumed by the DRAM DIMMS -- available only on server processors.
.PP
\fBSysWatt\fP Watts consumed by the whole platform (RAPL PSYS).
.PP
\fBPKG_%\fP percent of the interval that RAPL throttling was active on the Package. Note that the system summary is the sum of the package throttling time, and thus may be higher than 100% on a multi-package system. Note that the meaning of this field is model specific. For example, some hardware increments this counter when RAPL responds to thermal limits, but does not increment this counter when RAPL responds to power limits. Comparing PkgWatt and PkgTmp to system limits is necessary.
.PP
\fBRAM_%\fP percent of the interval that RAPL throttling was active on DRAM.
.PP
\fBUncMHz\fP per-package uncore MHz, instantaneous sample.
.PP
\fBUMHz1.0\fP per-package uncore MHz for pm_domain=1 and fabric_cluster=0, instantaneous sample. System summary is the average of all packages.
Intel Granite Rapids systems use pm_domains 0-2 for CPUs, and 3-4 for IO, with cluster always 0.
For the "--show" and "--hide" options, use "UncMHz" to operate on all UMHz*.* as a group.
.SH TOO MUCH INFORMATION EXAMPLE
By default, turbostat dumps all possible information -- a system configuration header, followed by columns for all counters.
This is ideal for remote debugging, use the "--out" option to save everything to a text file, and get that file to the expert helping you debug.
.PP
When you are not interested in all that information, and there are several ways to see only what you want. First the "--quiet" option will skip the configuration information, and turbostat will show only the counter columns. Second, you can reduce the columns with the "--hide" and "--show" options. If you use the "--show" option, then turbostat will show only the columns you list. If you use the "--hide" option, turbostat will show all columns, except the ones you list.
.PP
To find out what columns are available for --show and --hide, the "--list" option is available. Usually, the CATEGORY names above are used to refer to groups of counters. Also, for convenience, the special string "sysfs" can be used to refer to all of the sysfs C-state counters at once:
.PP
.nf
sudo ./turbostat --show sysfs --quiet sleep 10
10.003837 sec
C1 C1E C3 C6 C7s C1% C1E% C3% C6% C7s%
4 21 2 2 459 0.14 0.82 0.00 0.00 98.93
1 17 2 2 130 0.00 0.02 0.00 0.00 99.80
0 0 0 0 31 0.00 0.00 0.00 0.00 99.95
2 1 0 0 52 1.14 6.49 0.00 0.00 92.21
1 2 0 0 52 0.00 0.08 0.00 0.00 99.86
0 0 0 0 71 0.00 0.00 0.00 0.00 99.89
0 0 0 0 25 0.00 0.00 0.00 0.00 99.96
0 0 0 0 74 0.00 0.00 0.00 0.00 99.94
0 1 0 0 24 0.00 0.00 0.00 0.00 99.84
.fi
.PP
.SH ONE SHOT COMMAND EXAMPLE
If turbostat is invoked with a command, it will fork that command
and output the statistics gathered after the command exits.
In this case, turbostat output goes to stderr, by default.
Output can instead be saved to a file using the --out option.
In this example, the "sleep 10" command is forked, and turbostat waits for it to complete before saving all statistics into "ts.out". Note that "sleep 10" is not part of turbostat, but is simply an example of a command that turbostat can fork. The "ts.out" file is what you want to edit in a very wide window, paste into a spreadsheet, or attach to a bugzilla entry.
.nf
[root@hsw]# ./turbostat -o ts.out sleep 10
[root@hsw]#
.fi
.SH PERIODIC INTERVAL EXAMPLE
Without a command to fork, turbostat displays statistics ever 5 seconds.
Periodic output goes to stdout, by default, unless --out is used to specify an output file.
The 5-second interval can be changed with the "-i sec" option.
.nf
sudo turbostat --quiet --show CPU,frequency
Core CPU Avg_MHz Busy% Bzy_MHz TSC_MHz CPU%c7 UncMhz
- - 524 12.48 4198 3096 74.53 3800
0 0 4 0.09 4081 3096 98.88 3800
0 4 1 0.02 4063 3096
1 1 2 0.06 4063 3096 99.60
1 5 2 0.05 4070 3096
2 2 4178 99.52 4199 3096 0.00
2 6 3 0.08 4159 3096
3 3 1 0.04 4046 3096 99.66
3 7 0 0.01 3989 3096
Core CPU Avg_MHz Busy% Bzy_MHz TSC_MHz CPU%c7 UncMhz
- - 525 12.52 4198 3096 74.54 3800
0 0 4 0.10 4051 3096 99.49 3800
0 4 2 0.04 3993 3096
1 1 3 0.07 4054 3096 99.56
1 5 4 0.10 4018 3096
2 2 4178 99.51 4199 3096 0.00
2 6 4 0.09 4143 3096
3 3 2 0.06 4026 3096 99.10
3 7 7 0.17 4074 3096
.fi
This example also shows the use of the --show option to show only the desired columns.
.SH SYSTEM CONFIGURATION INFORMATION EXAMPLE
By default, turbostat always dumps system configuration information
before taking measurements. In the example above, "--quiet" is used
to suppress that output. Here is an example of the configuration information:
.nf
turbostat version 2022.04.16 - Len Brown <lenb@kernel.org>
Kernel command line: BOOT_IMAGE=/boot/vmlinuz-5.18.0-rc6-00001-ge6891250e3b5 ...
CPUID(0): GenuineIntel 0x16 CPUID levels
CPUID(1): family:model:stepping 0x6:9e:9 (6:158:9) microcode 0xea
CPUID(0x80000000): max_extended_levels: 0x80000008
CPUID(1): SSE3 MONITOR - EIST TM2 TSC MSR ACPI-TM HT TM
CPUID(6): APERF, TURBO, DTS, PTM, HWP, HWPnotify, HWPwindow, HWPepp, No-HWPpkg, EPB
cpu7: MSR_IA32_MISC_ENABLE: 0x00850089 (TCC EIST MWAIT PREFETCH TURBO)
CPUID(7): SGX
cpu7: MSR_IA32_FEATURE_CONTROL: 0x00000005 (Locked )
CPUID(0x15): eax_crystal: 2 ebx_tsc: 258 ecx_crystal_hz: 0
TSC: 3096 MHz (24000000 Hz * 258 / 2 / 1000000)
CPUID(0x16): base_mhz: 3100 max_mhz: 4200 bus_mhz: 100
cpu7: MSR_MISC_PWR_MGMT: 0x00401cc0 (ENable-EIST_Coordination DISable-EPB DISable-OOB)
RAPL: 5825 sec. Joule Counter Range, at 45 Watts
cpu7: MSR_PLATFORM_INFO: 0x80839f1011f00
8 * 100.0 = 800.0 MHz max efficiency frequency
31 * 100.0 = 3100.0 MHz base frequency
cpu7: MSR_IA32_POWER_CTL: 0x002c005d (C1E auto-promotion: DISabled)
cpu7: MSR_TURBO_RATIO_LIMIT: 0x2728292a
39 * 100.0 = 3900.0 MHz max turbo 4 active cores
40 * 100.0 = 4000.0 MHz max turbo 3 active cores
41 * 100.0 = 4100.0 MHz max turbo 2 active cores
42 * 100.0 = 4200.0 MHz max turbo 1 active cores
cpu7: MSR_CONFIG_TDP_NOMINAL: 0x0000001f (base_ratio=31)
cpu7: MSR_CONFIG_TDP_LEVEL_1: 0x00000000 ()
cpu7: MSR_CONFIG_TDP_LEVEL_2: 0x00000000 ()
cpu7: MSR_CONFIG_TDP_CONTROL: 0x80000000 ( lock=1)
cpu7: MSR_TURBO_ACTIVATION_RATIO: 0x00000000 (MAX_NON_TURBO_RATIO=0 lock=0)
cpu7: MSR_PKG_CST_CONFIG_CONTROL: 0x1e008008 (UNdemote-C3, UNdemote-C1, demote-C3, demote-C1, locked, pkg-cstate-limit=8 (unlimited))
Uncore Frequency pkg0 die0: 800 - 3900 MHz (800 - 3900 MHz)
/dev/cpu_dma_latency: 2000000000 usec (default)
current_driver: intel_idle
current_governor: menu
current_governor_ro: menu
cpu7: POLL: CPUIDLE CORE POLL IDLE
cpu7: C1: MWAIT 0x00
cpu7: C1E: MWAIT 0x01
cpu7: C3: MWAIT 0x10
cpu7: C6: MWAIT 0x20
cpu7: C7s: MWAIT 0x33
cpu7: C8: MWAIT 0x40
cpu7: C9: MWAIT 0x50
cpu7: C10: MWAIT 0x60
cpu7: cpufreq driver: intel_pstate
cpu7: cpufreq governor: performance
cpufreq intel_pstate no_turbo: 0
cpu7: MSR_MISC_FEATURE_CONTROL: 0x00000000 (L2-Prefetch L2-Prefetch-pair L1-Prefetch L1-IP-Prefetch)
cpu0: MSR_PM_ENABLE: 0x00000001 (HWP)
cpu0: MSR_HWP_CAPABILITIES: 0x01101f53 (high 83 guar 31 eff 16 low 1)
cpu0: MSR_HWP_REQUEST: 0x00005353 (min 83 max 83 des 0 epp 0x0 window 0x0 pkg 0x0)
cpu0: MSR_HWP_INTERRUPT: 0x00000001 (EN_Guaranteed_Perf_Change, Dis_Excursion_Min)
cpu0: MSR_HWP_STATUS: 0x00000004 (No-Guaranteed_Perf_Change, No-Excursion_Min)
cpu0: EPB: 6 (balanced)
cpu0: MSR_RAPL_POWER_UNIT: 0x000a0e03 (0.125000 Watts, 0.000061 Joules, 0.000977 sec.)
cpu0: MSR_PKG_POWER_INFO: 0x00000168 (45 W TDP, RAPL 0 - 0 W, 0.000000 sec.)
cpu0: MSR_PKG_POWER_LIMIT: 0x42820800218208 (UNlocked)
cpu0: PKG Limit #1: ENabled (65.000 Watts, 64.000000 sec, clamp ENabled)
cpu0: PKG Limit #2: ENabled (65.000 Watts, 0.002441* sec, clamp DISabled)
cpu0: MSR_VR_CURRENT_CONFIG: 0x00000000
cpu0: PKG Limit #4: 0.000000 Watts (UNlocked)
cpu0: MSR_DRAM_POWER_LIMIT: 0x5400de00000000 (UNlocked)
cpu0: DRAM Limit: DISabled (0.000 Watts, 0.000977 sec, clamp DISabled)
cpu0: MSR_PP0_POLICY: 0
cpu0: MSR_PP0_POWER_LIMIT: 0x00000000 (UNlocked)
cpu0: Cores Limit: DISabled (0.000 Watts, 0.000977 sec, clamp DISabled)
cpu0: MSR_PP1_POLICY: 0
cpu0: MSR_PP1_POWER_LIMIT: 0x00000000 (UNlocked)
cpu0: GFX Limit: DISabled (0.000 Watts, 0.000977 sec, clamp DISabled)
cpu0: MSR_IA32_TEMPERATURE_TARGET: 0x00640000 (100 C) (100 default - 0 offset)
cpu0: MSR_IA32_PACKAGE_THERM_STATUS: 0x88200800 (68 C)
cpu0: MSR_IA32_PACKAGE_THERM_INTERRUPT: 0x00000003 (100 C, 100 C)
cpu7: MSR_PKGC3_IRTL: 0x0000884e (valid, 79872 ns)
cpu7: MSR_PKGC6_IRTL: 0x00008876 (valid, 120832 ns)
cpu7: MSR_PKGC7_IRTL: 0x00008894 (valid, 151552 ns)
cpu7: MSR_PKGC8_IRTL: 0x000088fa (valid, 256000 ns)
cpu7: MSR_PKGC9_IRTL: 0x0000894c (valid, 339968 ns)
cpu7: MSR_PKGC10_IRTL: 0x00008bf2 (valid, 1034240 ns)
.fi
.PP
The \fBmax efficiency\fP frequency, a.k.a. Low Frequency Mode, is the frequency
available at the minimum package voltage. The \fBTSC frequency\fP is the base
frequency of the processor -- this should match the brand string
in /proc/cpuinfo. This base frequency
should be sustainable on all CPUs indefinitely, given nominal power and cooling.
The remaining rows show what maximum turbo frequency is possible
depending on the number of idle cores. Note that not all information is
available on all processors.
.SH ADD COUNTER EXAMPLE
Here we limit turbostat to showing just the CPU number for cpu0 - cpu3.
We add a counter showing the 32-bit raw value of MSR 0x199 (MSR_IA32_PERF_CTL),
labeling it with the column header, "PRF_CTRL", and display it only once,
after the conclusion of a 0.1 second sleep.
.nf
sudo ./turbostat --quiet --cpu 0-3 --show CPU --add msr0x199,u32,raw,PRF_CTRL sleep .1
0.101604 sec
CPU PRF_CTRL
- 0x00000000
0 0x00000c00
1 0x00000800
2 0x00000a00
3 0x00000800
.fi
.SH ADD PERF COUNTER EXAMPLE
Here we limit turbostat to showing just the CPU number for cpu0 - cpu3.
We add a counter showing time spent in C1 core cstate,
labeling it with the column header, "pCPU%c1", and display it only once,
after the conclusion of 0.1 second sleep.
We also show CPU%c1 built-in counter that should show similar values.
.nf
sudo ./turbostat --quiet --cpu 0-3 --show CPU,CPU%c1 --add perf/cstate_core/c1-residency,cpu,delta,percent,pCPU%c1 sleep .1
0.102448 sec
CPU pCPU%c1 CPU%c1
- 34.89 34.89
0 45.99 45.99
1 45.94 45.94
2 23.83 23.83
3 23.84 23.84
.fi
.SH ADD PERF COUNTER EXAMPLE #2 (using virtual cpu device)
Here we run on hybrid, Meteor Lake platform.
We limit turbostat to show output for just cpu0 (pcore) and cpu4 (ecore).
We add a counter showing number of L3 cache misses, using virtual "cpu" device,
labeling it with the column header, "VCMISS".
We add a counter showing number of L3 cache misses, using virtual "cpu_core" device,
labeling it with the column header, "PCMISS". This will fail on ecore cpu4.
We add a counter showing number of L3 cache misses, using virtual "cpu_atom" device,
labeling it with the column header, "ECMISS". This will fail on pcore cpu0.
We display it only once, after the conclusion of 0.1 second sleep.
.nf
sudo ./turbostat --quiet --cpu 0,4 --show CPU --add perf/cpu/cache-misses,cpu,delta,VCMISS --add perf/cpu_core/cache-misses,cpu,delta,PCMISS --add perf/cpu_atom/cache-misses,cpu,delta,ECMISS sleep 5
turbostat: added_perf_counters_init_: perf/cpu_atom/cache-misses: failed to open counter on cpu0
turbostat: added_perf_counters_init_: perf/cpu_core/cache-misses: failed to open counter on cpu4
5.001207 sec
CPU ECMISS PCMISS VCMISS
- 41586506 46291219 87877749
4 83173012 0 83173040
0 0 92582439 92582458
.fi
.SH ADD PMT COUNTER EXAMPLE
Here we limit turbostat to showing just the CPU number 0.
We add two counters, showing crystal clock count and the DC6 residency.
All the parameters passed are based on the metadata found in the PMT XML files.
For the crystal clock count, we
label it with the column header, "XTAL",
we set the type to 'raw', to read the number of clock ticks in hex,
we set the format to 'delta', to display the difference in ticks during the measurement interval,
we set the domain to 'package0', to collect it and associate it with the whole package number 0,
we set the offset to '0', which is a offset of the counter within the PMT MMIO region,
we set the lsb and msb to cover all 64 bits of the read 64 bit value,
and finally we set the guid to '0x1a067102', that identifies the PMT MMIO region to which the 'offset' is applied to read the counter value.
For the DC6 residency counter, we
label it with the column header, "Die%c6",
we set the type to 'txtal_time', to obtain the percent residency value
we set the format to 'delta', to display the difference in ticks during the measurement interval,
we set the domain to 'package0', to collect it and associate it with the whole package number 0,
we set the offset to '0', which is a offset of the counter within the PMT MMIO region,
we set the lsb and msb to cover all 64 bits of the read 64 bit value,
and finally we set the guid to '0x1a067102', that identifies the PMT MMIO region to which the 'offset' is applied to read the counter value.
.nf
sudo ./turbostat --quiet --cpu 0 --show CPU --add pmt,name=XTAL,type=raw,format=delta,domain=package0,offset=0,lsb=0,msb=63,guid=0x1a067102 --add pmt,name=Die%c6,type=txtal_time,format=delta,domain=package0,offset=120,lsb=0,msb=63,guid=0x1a067102
0.104352 sec
CPU XTAL Die%c6
- 0x0000006d4d957ca7 0.00
0 0x0000006d4d957ca7 0.00
0.102448 sec
.fi
.SH INPUT
For interval-mode, turbostat will immediately end the current interval
when it sees a newline on standard input.
turbostat will then start the next interval.
Control-C will be send a SIGINT to turbostat,
which will immediately abort the program with no further processing.
.SH SIGNALS
SIGINT will interrupt interval-mode.
The end-of-interval data will be collected and displayed before turbostat exits.
SIGUSR1 will end current interval,
end-of-interval data will be collected and displayed before turbostat
starts a new interval.
.SH NOTES
.B "turbostat "
must be run as root.
Alternatively, non-root users can be enabled to run turbostat this way:
# setcap cap_sys_admin,cap_sys_rawio,cap_sys_nice=+ep path/to/turbostat
# chmod +r /dev/cpu/*/msr
# chmod +r /dev/cpu_dma_latency
.B "turbostat "
reads hardware counters, but doesn't write them.
So it will not interfere with the OS or other programs, including
multiple invocations of itself.
\fBturbostat \fP
may work poorly on Linux-2.6.20 through 2.6.29,
as \fBacpi-cpufreq \fPperiodically cleared the APERF and MPERF MSRs
in those kernels.
AVG_MHz = APERF_delta/measurement_interval. This is the actual
number of elapsed cycles divided by the entire sample interval --
including idle time. Note that this calculation is resilient
to systems lacking a non-stop TSC.
TSC_MHz = TSC_delta/measurement_interval.
On a system with an invariant TSC, this value will be constant
and will closely match the base frequency value shown
in the brand string in /proc/cpuinfo. On a system where
the TSC stops in idle, TSC_MHz will drop
below the processor's base frequency.
Busy% = MPERF_delta/TSC_delta
Bzy_MHz = TSC_delta*APERF_delta/MPERF_delta/measurement_interval
Note that these calculations depend on TSC_delta, so they
are not reliable during intervals when TSC_MHz is not running at the base frequency.
Turbostat data collection is not atomic.
Extremely short measurement intervals (much less than 1 second),
or system activity that prevents turbostat from being able
to run on all CPUS to quickly collect data, will result in
inconsistent results.
The APERF, MPERF MSRs are defined to count non-halted cycles.
Although it is not guaranteed by the architecture, turbostat assumes
that they count at TSC rate, which is true on all processors tested to date.
.SH REFERENCES
Volume 3B: System Programming Guide"
https://www.intel.com/products/processor/manuals/
.SH RUN THE LATEST VERSION
If turbostat complains that it doesn't recognize your processor,
please try the latest version.
The latest version of turbostat does not require the latest version of the Linux kernel.
However, some features, such as perf(1) counters, do require kernel support.
The latest turbostat release is available in the upstream Linux Kernel source tree.
eg. "git pull https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git"
and run make in tools/power/x86/turbostat/.
n.b. "make install" will update your system manually, but a distro update may subsequently downgrade your turbostat to an older version.
For this reason, manually installing to /usr/local/bin may be what you want.
Note that turbostat/Makefile has a "make snapshot" target, which will create a tar file
that can build without a local kernel source tree.
If the upstream version isn't new enough, the development tree can be found here:
"git pull https://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git turbostat"
If the development tree doesn't work, please contact the author via chat,
or via email with the word "turbostat" on the Subject line.
An old turbostat binary may run on unknown hardware by using "--force",
but results are unsupported.
.SH FILES
.ta
.nf
/sys/bus/event_source/devices/
/dev/cpu/*/msr
/sys/class/intel_pmt/
/sys/devices/system/cpu/
.fi
.SH "SEE ALSO"
perf(1), msr(4), vmstat(8)
.PP
.SH AUTHOR
.nf
Written by Len Brown <len.brown@intel.com>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,56 @@
# SPDX-License-Identifier: GPL-2.0
CC = $(CROSS_COMPILE)gcc
BUILD_OUTPUT := $(CURDIR)
PREFIX := /usr
DESTDIR :=
DAY := $(shell date +%Y.%m.%d)
SNAPSHOT = x86_energy_perf_policy-$(DAY)
ifeq ("$(origin O)", "command line")
BUILD_OUTPUT := $(O)
endif
x86_energy_perf_policy : x86_energy_perf_policy.c
override CFLAGS += -O2 -Wall -I../../../include
override CFLAGS += -DMSRHEADER='"../../../../arch/x86/include/asm/msr-index.h"'
override CFLAGS += -D_FORTIFY_SOURCE=2
%: %.c
@mkdir -p $(BUILD_OUTPUT)
$(CC) $(CFLAGS) $< -o $(BUILD_OUTPUT)/$@ $(LDFLAGS)
.PHONY : clean
clean :
@rm -f $(BUILD_OUTPUT)/x86_energy_perf_policy
install : x86_energy_perf_policy
install -d $(DESTDIR)$(PREFIX)/bin
install $(BUILD_OUTPUT)/x86_energy_perf_policy $(DESTDIR)$(PREFIX)/bin/x86_energy_perf_policy
install -d $(DESTDIR)$(PREFIX)/share/man/man8
install -m 644 x86_energy_perf_policy.8 $(DESTDIR)$(PREFIX)/share/man/man8
snapshot: x86_energy_perf_policy
@rm -rf $(SNAPSHOT)
@mkdir $(SNAPSHOT)
@cp x86_energy_perf_policy Makefile x86_energy_perf_policy.c x86_energy_perf_policy.8 $(SNAPSHOT)
@sed -e 's/^#include <linux\/bits.h>/#include "bits.h"/' -e 's/u64/unsigned long long/' ../../../../arch/x86/include/asm/msr-index.h > $(SNAPSHOT)/msr-index.h
@echo '#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))' >> $(SNAPSHOT)/msr-index.h
@echo "#define BIT(x) (1 << (x))" > $(SNAPSHOT)/bits.h
@echo "#define BIT_ULL(nr) (1ULL << (nr))" >> $(SNAPSHOT)/bits.h
@echo "#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))" >> $(SNAPSHOT)/bits.h
@echo "#define GENMASK_ULL(h, l) (((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h))))" >> $(SNAPSHOT)/bits.h
@echo '#define BUILD_BUG_ON(cond) do { enum { compile_time_check ## __COUNTER__ = 1/(!(cond)) }; } while (0)' > $(SNAPSHOT)/build_bug.h
@echo '#define __must_be_array(arr) 0' >> $(SNAPSHOT)/build_bug.h
@echo PWD=. > $(SNAPSHOT)/Makefile
@echo "CFLAGS += -DMSRHEADER='\"msr-index.h\"'" >> $(SNAPSHOT)/Makefile
@echo "CFLAGS += -DBUILD_BUG_HEADER='\"build_bug.h\"'" >> $(SNAPSHOT)/Makefile
@sed -e's/.*MSRHEADER.*//' Makefile >> $(SNAPSHOT)/Makefile
@rm -f $(SNAPSHOT).tar.gz
tar cvzf $(SNAPSHOT).tar.gz $(SNAPSHOT)
@@ -0,0 +1,246 @@
.\" This page Copyright (C) 2010 - 2015 Len Brown <len.brown@intel.com>
.\" Distributed under the GPL, Copyleft 1994.
.TH X86_ENERGY_PERF_POLICY 8
.SH NAME
x86_energy_perf_policy \- Manage Energy vs. Performance Policy
.SH SYNOPSIS
.B x86_energy_perf_policy
.RB "[ options ] [ scope ] [field \ value]"
.br
.RB "scope: \-\-cpu\ cpu-list | \-\-pkg\ pkg-list"
.br
.RB "cpu-list, pkg-list: # | #,# | #-# | all"
.br
.RB "field: \-\-all | \-\-epb | \-\-hwp-epp | \-\-hwp-min | \-\-hwp-max | \-\-hwp-desired"
.br
.RB "other: (\-\-force | \-\-hwp-enable | \-\-turbo-enable) value)"
.br
.RB "soc-slider: --soc-slider-balance # | --soc-slider-offset # | --platform-profile <name>"
.br
.RB "value: # | default | performance | balance-performance | balance-power | power"
.SH DESCRIPTION
\fBx86_energy_perf_policy\fP
displays and updates energy-performance policy settings specific to
Intel Architecture Processors. It summarizes settings available
in standard Linux interfaces (eg. cpufreq),
and also decodes underlying Model Specific Register (MSRs).
While \fBx86_energy_perf_policy\fP can manage energy-performance policy
using only MSR access, it prefers standard
Linux kernel interfaces, when they are available.
.SH BACKGROUND
Policy in MSR_IA32_ENERGY_PERF_BIAS (EPB)
may affect a wide range of hardware decisions,
such as how aggressively the hardware enters and exits CPU idle states (C-states)
and Processor Performance States (P-states).
This policy hint does not replace explicit OS C-state and P-state selection.
Rather, it tells the hardware how aggressively to implement those selections.
Further, it allows the OS to influence energy/performance trade-offs where there
is no software interface, such as in the opportunistic "turbo-mode" P-state range.
Note that MSR_IA32_ENERGY_PERF_BIAS is defined per CPU,
but some implementations
share a single MSR among all CPUs in each processor package.
On those systems, a write to EPB on one processor will
be visible, and will have an effect, on all CPUs
in the same processor package.
Hardware P-States (HWP) are effectively an expansion of hardware
P-state control from the opportunistic turbo-mode P-state range
to include the entire range of available P-states.
On Broadwell Xeon, the initial HWP implementation, EPB influenced HWP.
That influence was removed in subsequent generations,
where it was moved to the
Energy_Performance_Preference (EPP) field in
a pair of dedicated MSRs -- MSR_IA32_HWP_REQUEST and MSR_IA32_HWP_REQUEST_PKG.
EPP is the most commonly managed knob in HWP mode,
but MSR_IA32_HWP_REQUEST also allows the user to specify
minimum-frequency for Quality-of-Service,
and maximum-frequency for power-capping.
MSR_IA32_HWP_REQUEST is defined per-CPU.
MSR_IA32_HWP_REQUEST_PKG has the same capability as MSR_IA32_HWP_REQUEST,
but it can simultaneously set the default policy for all CPUs within a package.
A bit in per-CPU MSR_IA32_HWP_REQUEST indicates whether it is
over-ruled-by or exempt-from MSR_IA32_HWP_REQUEST_PKG.
MSR_HWP_CAPABILITIES shows the default values for the fields
in MSR_IA32_HWP_REQUEST. It is displayed when no values
are being written.
.SS SCOPE OPTIONS
.PP
\fB-c, --cpu\fP Operate on the MSR_IA32_HWP_REQUEST for each CPU in a CPU-list.
The CPU-list may be comma-separated CPU numbers, with dash for range
or the string "all". Eg. '--cpu 1,4,6-8' or '--cpu all'.
When --cpu is used, \fB--hwp-use-pkg\fP is available, which specifies whether the per-cpu
MSR_IA32_HWP_REQUEST should be over-ruled by MSR_IA32_HWP_REQUEST_PKG (1),
or exempt from MSR_IA32_HWP_REQUEST_PKG (0).
\fB-p, --pkg\fP Operate on the MSR_IA32_HWP_REQUEST_PKG for each package in the package-list.
The list is a string of individual package numbers separated
by commas, and or ranges of package numbers separated by a dash,
or the string "all".
For example '--pkg 1,3' or '--pkg all'
.SS VALUE OPTIONS
.PP
.I normal | default
Set a policy with a normal balance between performance and energy efficiency.
The processor will tolerate minor performance compromise
for potentially significant energy savings.
This is a reasonable default for most desktops and servers.
"default" is a synonym for "normal".
.PP
.I performance
Set a policy for maximum performance,
accepting no performance sacrifice for the benefit of energy efficiency.
.PP
.I balance-performance
Set a policy with a high priority on performance,
but allowing some performance loss to benefit energy efficiency.
.PP
.I balance-power
Set a policy where the performance and power are balanced.
This is the default.
.PP
.I power
Set a policy where the processor can accept
a measurable performance impact to maximize energy efficiency.
.PP
The following table shows the mapping from the value strings above to actual MSR values.
This mapping is defined in the Linux-kernel header, msr-index.h.
.nf
VALUE STRING EPB EPP
performance 0 0
balance-performance 4 128
normal, default 6 128
balance-power 8 192
power 15 255
.fi
.PP
For MSR_IA32_HWP_REQUEST performance fields
(--hwp-min, --hwp-max, --hwp-desired), the value option
is in units of 100 MHz, Eg. 12 signifies 1200 MHz.
.SS FIELD OPTIONS
\fB-a, --all value-string\fP Sets all EPB and EPP and HWP limit fields to the value associated with
the value-string. In addition, enables turbo-mode and HWP-mode, if they were previous disabled.
Thus "--all normal" will set a system without cpufreq into a well known configuration.
.PP
\fB-B, --epb\fP set EPB per-core or per-package.
See value strings in the table above.
.PP
\fB-d, --debug\fP debug increases verbosity. By default
x86_energy_perf_policy is silent for updates,
and verbose for read-only mode.
.PP
\fB-P, --hwp-epp\fP set HWP.EPP per-core or per-package.
See value strings in the table above.
.PP
\fB-m, --hwp-min\fP request HWP to not go below the specified core/bus ratio.
The "default" is the value found in IA32_HWP_CAPABILITIES.min.
.PP
\fB-M, --hwp-max\fP request HWP not exceed a the specified core/bus ratio.
The "default" is the value found in IA32_HWP_CAPABILITIES.max.
.PP
\fB-D, --hwp-desired\fP request HWP 'desired' frequency.
The "normal" setting is 0, which
corresponds to 'full autonomous' HWP control.
Non-zero performance values request a specific performance
level on this processor, specified in multiples of 100 MHz.
.PP
\fB-w, --hwp-window\fP specify integer number of microsec
in the sliding window that HWP uses to maintain average frequency.
This parameter is meaningful only when the "desired" field above is non-zero.
Default is 0, allowing the HW to choose.
.SH SOC SLIDER OPTIONS
.PP
Note that the Platform Profile Name must be "SoC Slider", and the
Platform Profile must be "balanced" for the --soc-slider-balance
and --soc-slider-offset options to take effect.
.PP
\fB--soc-slider-balance #\fP write numeric value to the SoC Slider.
Values range from 0 to 6.
Lower values result in higher performance,
and higher values improve energy efficiency.
Actual values are model specific.
.PP
\fB--soc-slider-offset #\fP write the numeric value to the Soc Slider Offset.
The slider offset is the maximum value that software allows the SoC to
autonomously add to the SoC Slider to improve energy efficiency.
The value 0 prohibits the SoC from autonomously changing the slider.
.PP
\fB--platform-profile <name>"\fP set the platform profile to <name>.
Available choices are in platform-profile-0/choices. The Soc Slider
driver currently supports "low-power", "balanced", and "performance".
.SH OTHER OPTIONS
.PP
\fB-f, --force\fP writes the specified values without bounds checking.
.PP
\fB-U, --hwp-use-pkg\fP (0 | 1), when used in conjunction with --cpu,
indicates whether the per-CPU MSR_IA32_HWP_REQUEST should be overruled (1)
or exempt (0) from per-Package MSR_IA32_HWP_REQUEST_PKG settings.
The default is exempt.
.PP
\fB-H, --hwp-enable\fP enable HardWare-P-state (HWP) mode. Once enabled, system RESET is required to disable HWP mode.
.PP
\fB-t, --turbo-enable\fP enable (1) or disable (0) turbo mode.
.PP
\fB-v, --version\fP print version and exit.
.PP
If no request to change policy is made,
the default behavior is to read
and display the current system state,
including the default capabilities.
.SH WARNING
.PP
This utility writes directly to Model Specific Registers.
There is no locking or coordination should this utility
be used to modify HWP limit fields at the same time that
intel_pstate's sysfs attributes access the same MSRs.
.PP
Note that --hwp-desired and --hwp-window are considered experimental.
Future versions of Linux reserve the right to access these
fields internally -- potentially conflicting with user-space access.
.SH EXAMPLE
.nf
# sudo x86_energy_perf_policy
cpu0: EPB 6
cpu0: HWP_REQ: min 6 max 35 des 0 epp 128 window 0x0 (0*10^0us) use_pkg 0
cpu0: HWP_CAP: low 1 eff 8 guar 27 high 35
cpu1: EPB 6
cpu1: HWP_REQ: min 6 max 35 des 0 epp 128 window 0x0 (0*10^0us) use_pkg 0
cpu1: HWP_CAP: low 1 eff 8 guar 27 high 35
cpu2: EPB 6
cpu2: HWP_REQ: min 6 max 35 des 0 epp 128 window 0x0 (0*10^0us) use_pkg 0
cpu2: HWP_CAP: low 1 eff 8 guar 27 high 35
cpu3: EPB 6
cpu3: HWP_REQ: min 6 max 35 des 0 epp 128 window 0x0 (0*10^0us) use_pkg 0
cpu3: HWP_CAP: low 1 eff 8 guar 27 high 35
.fi
.SH NOTES
.B "x86_energy_perf_policy"
runs only as root.
.SH FILES
.ta
.nf
EPB: /sys/devices/system/cpu/cpu*/power/energy_perf_bias
EPP: /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference
MSR: /dev/cpu/*/msr
Platform Profile Name: /sys/class/platform-profile/platform-profile-0/name
Platform Profile: /sys/class/platform-profile/platform-profile-0/profile
SOC Slider Balanced: /sys/module/processor_thermal_soc_slider/parameters/slider_balance
SOC Slider Balanced Offset: /sys/module/processor_thermal_soc_slider/parameters/slider_offset
.fi
.SH "SEE ALSO"
.nf
msr(4)
Intel(R) 64 and IA-32 Architectures Software Developer's Manual
.fi
.PP
.SH AUTHORS
.nf
Len Brown
File diff suppressed because it is too large Load Diff