Merge branches 'turbostat' and 'x86_energy_perf_policy' into utilities

This commit is contained in:
Len Brown 2026-10-31 14:29:22 -03:00
commit 8969e647d0
2 changed files with 148 additions and 97 deletions

View File

@ -29577,6 +29577,15 @@ W: https://x86-cpuid.org
F: arch/x86/include/asm/cpuid/leaf_types.h
F: tools/arch/x86/kcpuid/
X86 ENERGY PERF POLICY UTILITY
M: Len Brown <lenb@kernel.org>
L: linux-pm@vger.kernel.org
S: Supported
Q: https://patchwork.kernel.org/project/linux-pm/list/
B: https://bugzilla.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git x86_energy_perf_policy
F: tools/power/x86/x86_energy_perf_policy/
X86 ENTRY CODE
M: Andy Lutomirski <luto@kernel.org>
L: linux-kernel@vger.kernel.org

View File

@ -9,17 +9,22 @@
*/
#define _GNU_SOURCE
#include MSRHEADER
/*
* MSRHEADER macro should be defined by the build system to include the
* appropriate MSR definitions header file. Typical usage:
* gcc -DMSRHEADER=\"msr-index.h\" or similar platform-specific MSR header
*/
#include MSRHEADER /* e.g., msr-index.h with MSR_* constant definitions */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/wait.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>
@ -33,6 +38,13 @@
#define OPTARG_BALANCE_PERFORMANCE (INT_MAX - 4)
#define OPTARG_PERFORMANCE (INT_MAX - 5)
/* Maximum HWP activity window duration in microseconds */
#define HWP_WINDOW_MAX_USEC 1270000000U
/* Nominal bus clock frequency in MHz - assumed for all HWP systems */
#define BCLK_MHZ 100
#define KHZ_PER_MHZ 1000
struct msr_hwp_cap {
unsigned char highest;
unsigned char guaranteed;
@ -98,6 +110,7 @@ unsigned char update_platform_profile;
int soc_slider_balance;
int soc_slider_offset;
char platform_profile[64];
char msr_path[64];
#define PATH_TO_CPU "/sys/devices/system/cpu/"
#define SYSFS_PATH_MAX 255
@ -139,11 +152,13 @@ void usage(void)
int ratio_2_msr_perf(int ratio)
{
int msr_perf;
unsigned long long temp;
if (!bdx_highest_ratio)
return ratio;
msr_perf = ratio * 255 / bdx_highest_ratio;
temp = (unsigned long long)ratio * 255;
msr_perf = temp / bdx_highest_ratio;
if (debug)
fprintf(stderr, "%d = ratio_to_msr_perf(%d)\n", msr_perf, ratio);
@ -189,7 +204,7 @@ int parse_cmdline_epb(int i)
return ENERGY_PERF_BIAS_PERFORMANCE;
}
if (i < 0 || i > ENERGY_PERF_BIAS_POWERSAVE)
errx(1, "--epb must be from 0 to 15");
errx(1, "--epb must be 0 through 15");
return i;
}
@ -255,7 +270,7 @@ int parse_cmdline_hwp_desired(int i)
return i;
}
int parse_cmdline_hwp_window(int i)
unsigned int parse_cmdline_hwp_window(unsigned int i)
{
unsigned int exponent;
@ -269,13 +284,13 @@ int parse_cmdline_hwp_window(int i)
case OPTARG_PERFORMANCE:
return 0;
}
if (i < 0 || i > 1270000000) {
fprintf(stderr, "--hwp-window: 0 for auto; 1 - 1270000000 usec for window duration\n");
if (i > HWP_WINDOW_MAX_USEC) {
fprintf(stderr, "--hwp-window: 0 for auto; 1 - %u usec for window duration\n", HWP_WINDOW_MAX_USEC);
usage();
}
for (exponent = 0;; ++exponent) {
if (debug)
printf("%d 10^%d\n", i, exponent);
printf("%u 10^%d\n", i, exponent);
if (i <= 127)
break;
@ -283,7 +298,7 @@ int parse_cmdline_hwp_window(int i)
i = i / 10;
}
if (debug)
fprintf(stderr, "%d*10^%d: 0x%x\n", i, exponent, (exponent << 7) | i);
fprintf(stderr, "%u*10^%d: 0x%x\n", i, exponent, (exponent << 7) | i);
return (exponent << 7) | i;
}
@ -303,8 +318,8 @@ int parse_cmdline_hwp_epp(int i)
case OPTARG_PERFORMANCE:
return HWP_EPP_PERFORMANCE;
}
if (i < 0 || i > 0xff) {
fprintf(stderr, "--hwp-epp must be from 0 to 0xff\n");
if (i < 0 || i > 255) {
fprintf(stderr, "--hwp-epp must be 0 through 255\n");
usage();
}
return i;
@ -330,42 +345,36 @@ int parse_cmdline_turbo(int i)
return i;
}
int parse_optarg_string(char *s)
long parse_optarg_string(const char *s)
{
int i;
long i;
char *endptr;
if (!strncmp(s, "default", 7))
if (strcmp(s, "default") == 0)
return OPTARG_NORMAL;
if (!strncmp(s, "normal", 6))
if (strcmp(s, "normal") == 0)
return OPTARG_NORMAL;
if (!strncmp(s, "power", 9))
if (strcmp(s, "power") == 0)
return OPTARG_POWER;
if (!strncmp(s, "balance-power", 17))
if (strcmp(s, "balance-power") == 0)
return OPTARG_BALANCE_POWER;
if (!strncmp(s, "balance-performance", 19))
if (strcmp(s, "balance-performance") == 0)
return OPTARG_BALANCE_PERFORMANCE;
if (!strncmp(s, "performance", 11))
if (strcmp(s, "performance") == 0)
return OPTARG_PERFORMANCE;
errno = 0;
i = strtol(s, &endptr, 0);
if (s == endptr) {
fprintf(stderr, "no digits in \"%s\"\n", s);
if (endptr == s || errno == ERANGE || i < 0 || i > 255) {
warnx("bad value \"%s\"", s);
usage();
}
if (i == LONG_MIN || i == LONG_MAX)
errx(-1, "%s", s);
if (i > 0xFF)
errx(-1, "%d (0x%x) must be < 256", i, i);
if (i < 0)
errx(-1, "%d (0x%x) must be >= 0", i, i);
return i;
}
@ -422,9 +431,10 @@ void parse_cmdline_cpu(char *s)
int end_cpu;
startp++;
errno = 0;
end_cpu = strtol(startp, &endp, 10);
if (startp == endp)
continue;
if (startp == endp || errno == ERANGE)
errx(1, "Confused by end_cpu '%s'", startp);
while (cpu <= end_cpu) {
if (cpu > max_cpu_num)
@ -467,11 +477,11 @@ void parse_cmdline_cpu(char *s)
break;
}
errno = 0;
cpu = strtol(startp, &endp, 10);
if (startp == endp)
if (startp == endp || errno == ERANGE || cpu < 0 || cpu > max_cpu_num)
errx(1, "--cpu cpu-set: confused by '%s'", startp);
if (cpu > max_cpu_num)
errx(1, "Requested cpu%d exceeds max cpu%d", cpu, max_cpu_num);
CPU_SET_S(cpu, cpu_setsize, cpu_selected_set);
startp = endp;
}
@ -502,14 +512,15 @@ void parse_cmdline_pkg(char *s)
int end_pkg;
startp++;
errno = 0;
end_pkg = strtol(startp, &endp, 10);
if (startp == endp)
continue;
if (startp == endp || errno == ERANGE)
errx(1, "Confused by end_pkg '%s'", startp);
while (pkg <= end_pkg) {
if (pkg > max_pkg_num)
errx(1, "Requested pkg%d exceeds max pkg%d", pkg, max_pkg_num);
pkg_selected_set |= 1 << pkg;
pkg_selected_set |= 1ULL << pkg;
pkg++;
}
startp = endp;
@ -521,10 +532,12 @@ void parse_cmdline_pkg(char *s)
return;
}
errno = 0;
pkg = strtol(startp, &endp, 10);
if (pkg > max_pkg_num)
errx(1, "Requested pkg%d Exceeds max pkg%d", pkg, max_pkg_num);
pkg_selected_set |= 1 << pkg;
if (startp == endp || errno == ERANGE || pkg < 0 || pkg > max_pkg_num)
errx(1, "Confused by pkg '%s'", startp);
pkg_selected_set |= 1ULL << pkg;
startp = endp;
}
}
@ -533,8 +546,8 @@ void for_packages(unsigned long long pkg_set, int (func) (int))
{
int pkg_num;
for (pkg_num = 0; pkg_num <= max_pkg_num; ++pkg_num) {
if (pkg_set & (1UL << pkg_num))
for (pkg_num = 0; pkg_num <= max_pkg_num && pkg_num < MAX_PACKAGES; ++pkg_num) {
if (pkg_set & (1ULL << pkg_num))
func(pkg_num);
}
}
@ -544,6 +557,7 @@ static int parse_cmdline_int(const char *s, int *out)
char *endp;
long val;
errno = 0;
val = strtol(s, &endp, 0);
if (endp == s || errno == ERANGE)
return -1;
@ -558,7 +572,7 @@ static int parse_cmdline_int(const char *s, int *out)
void print_version(void)
{
printf("x86_energy_perf_policy 2026.04.25 Len Brown <lenb@kernel.org>\n");
printf("x86_energy_perf_policy 2026.10.31 Len Brown <lenb@kernel.org>\n");
}
static int platform_profile_access(int mode)
@ -572,7 +586,7 @@ static int platform_profile_access(int mode)
return 1;
}
static int platform_profile_name_is(char *name)
static int platform_profile_name_is(const char *name)
{
char buf[64];
@ -677,7 +691,7 @@ void cmdline(int argc, char **argv)
errx(1, "--platform-profile: value too long");
if (!platform_profile_access(W_OK))
errx(1, "Can not update platform-profile in '%s'", PATH_PLATFORM_PROFILE);
strcpy(platform_profile, optarg);
snprintf(platform_profile, sizeof(platform_profile), "%s", optarg);
update_platform_profile = 1;
break;
case 'm':
@ -712,13 +726,19 @@ void cmdline(int argc, char **argv)
case 't':
turbo_update_value = parse_cmdline_turbo(parse_optarg_string(optarg));
break;
case 'u':
update_hwp_use_pkg++;
if (atoi(optarg) == 0)
req_update.hwp_use_pkg = 0;
else
req_update.hwp_use_pkg = 1;
break;
case 'u': {
char *endp;
long val;
update_hwp_use_pkg++;
errno = 0;
val = strtol(optarg, &endp, 10);
if (endp == optarg || errno == ERANGE || val < 0 || val > 1)
errx(1, "--hwp-use-pkg: must be 0 or 1");
req_update.hwp_use_pkg = val;
break;
}
case 'v':
print_version();
exit(0);
@ -764,10 +784,10 @@ void err_on_hypervisor(void)
/* On VMs /proc/cpuinfo contains a "flags" entry for hypervisor */
cpuinfo = fopen_or_die("/proc/cpuinfo", "r");
buffer = malloc(4096);
buffer = calloc(4096, 1);
if (!buffer) {
fclose(cpuinfo);
err(-ENOMEM, "buffer malloc fail");
err(-ENOMEM, "buffer calloc fail");
}
if (!fread(buffer, 1024, 1, cpuinfo)) {
@ -783,7 +803,11 @@ void err_on_hypervisor(void)
err(1, "Failed to find 'flags' in /proc/cpuinfo");
}
rewind(cpuinfo);
fseek(cpuinfo, flags - buffer, SEEK_SET);
if (fseek(cpuinfo, flags - buffer, SEEK_SET) != 0) {
fclose(cpuinfo);
free(buffer);
err(1, "Failed to seek in /proc/cpuinfo");
}
if (!fgets(buffer, 4096, cpuinfo)) {
fclose(cpuinfo);
free(buffer);
@ -792,28 +816,38 @@ void err_on_hypervisor(void)
fclose(cpuinfo);
hypervisor = strstr(buffer, "hypervisor");
free(buffer);
if (hypervisor)
err(-1, "not supported on this virtual machine");
free(buffer);
}
/*
* if (use_android_msr_path)
* msr_path = /dev/msr%d // Android
* else
* msr_path = /dev/cpu/%d/msr // Linux
*/
static void set_msr_path(int cpu)
{
if (snprintf(msr_path, sizeof(msr_path), use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu) >= (int)sizeof(msr_path))
err(-1, "MSR path buffer overflow for cpu%d", cpu);
}
int get_msr(int cpu, int offset, unsigned long long *msr)
{
int retval;
char pathname[32];
int fd;
sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu);
fd = open(pathname, O_RDONLY);
set_msr_path(cpu);
fd = open(msr_path, O_RDONLY);
if (fd < 0)
err(-1, "%s open failed, try chown or chmod +r %s, or run as root", pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr");
err(-1, "%s open failed, try chown or chmod +r, or run as root", msr_path);
retval = pread(fd, msr, sizeof(*msr), offset);
if (retval != sizeof(*msr)) {
err_on_hypervisor();
err(-1, "%s offset 0x%llx read failed", pathname, (unsigned long long)offset);
err(-1, "%s offset 0x%llx read failed", msr_path, (unsigned long long)offset);
}
if (debug > 1)
@ -825,14 +859,13 @@ int get_msr(int cpu, int offset, unsigned long long *msr)
int put_msr(int cpu, int offset, unsigned long long new_msr)
{
char pathname[32];
int retval;
int fd;
sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu);
fd = open(pathname, O_RDWR);
set_msr_path(cpu);
fd = open(msr_path, O_RDWR);
if (fd < 0)
err(-1, "%s open failed, try chown or chmod +r %s, or run as root", pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr");
err(-1, "%s open failed, try chown or chmod +w, or run as root", msr_path);
retval = pwrite(fd, &new_msr, sizeof(new_msr), offset);
if (retval != sizeof(new_msr))
@ -867,14 +900,14 @@ static unsigned int read_sysfs(const char *path, char *buf, size_t buflen)
return (unsigned int)numread;
}
static unsigned int write_sysfs(const char *path, char *buf, size_t buflen)
static int write_sysfs(const char *path, char *buf, size_t buflen)
{
ssize_t numwritten;
int fd;
fd = open(path, O_WRONLY);
if (fd == -1)
return 0;
return -1;
numwritten = write(fd, buf, buflen - 1);
if (numwritten < 1) {
@ -886,7 +919,7 @@ static unsigned int write_sysfs(const char *path, char *buf, size_t buflen)
close(fd);
return (unsigned int)numwritten;
return 0;
}
static int sysfs_read_string(const char *path, char *buf, size_t buflen)
@ -911,7 +944,7 @@ static int sysfs_write_string(const char *path, const char *buf)
len = snprintf(tmp, sizeof(tmp), "%s\n", buf);
if (len < 0 || len >= (int)sizeof(tmp))
return -1;
return write_sysfs(path, tmp, (size_t)len + 1) ? 0 : -1;
return write_sysfs(path, tmp, (size_t)len + 1);
}
void print_hwp_cap(int cpu, struct msr_hwp_cap *cap, char *str)
@ -934,7 +967,7 @@ void read_hwp_cap(int cpu, struct msr_hwp_cap *cap, unsigned int msr_offset)
cap->lowest = msr_perf_2_ratio(HWP_LOWEST_PERF(msr));
}
void print_hwp_request(int cpu, struct msr_hwp_request *h, char *str)
void print_hwp_request(int cpu, struct msr_hwp_request *h, const char *str)
{
if (cpu != -1)
printf("cpu%d: ", cpu);
@ -946,7 +979,7 @@ void print_hwp_request(int cpu, struct msr_hwp_request *h, char *str)
h->hwp_min, h->hwp_max, h->hwp_desired, h->hwp_epp, h->hwp_window, h->hwp_window & 0x7F, (h->hwp_window >> 7) & 0x7, h->hwp_use_pkg);
}
void print_hwp_request_pkg(int pkg, struct msr_hwp_request *h, char *str)
void print_hwp_request_pkg(int pkg, struct msr_hwp_request *h, const char *str)
{
printf("pkg%d: ", pkg);
@ -1004,8 +1037,9 @@ static int get_epb_sysfs(int cpu)
if (!read_sysfs(path, linebuf, 3))
return -1;
errno = 0;
val = strtol(linebuf, &endp, 0);
if (endp == linebuf || errno == ERANGE)
if (endp == linebuf || errno == ERANGE || val < 0 || val > 15)
return -1;
return (int)val;
@ -1016,7 +1050,6 @@ static int set_epb_sysfs(int cpu, int val)
char path[SYSFS_PATH_MAX];
char linebuf[3];
char *endp;
int ret;
if (!has_epb)
return -1;
@ -1024,12 +1057,12 @@ static int set_epb_sysfs(int cpu, int val)
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/power/energy_perf_bias", cpu);
snprintf(linebuf, sizeof(linebuf), "%d", val);
ret = write_sysfs(path, linebuf, 3);
if (ret <= 0)
if (write_sysfs(path, linebuf, 3))
return -1;
errno = 0;
val = strtol(linebuf, &endp, 0);
if (endp == linebuf || errno == ERANGE)
if (endp == linebuf || errno == ERANGE || val < 0 || val > 15)
return -1;
return (int)val;
@ -1139,7 +1172,7 @@ int print_pkg_msrs(int pkg)
*/
int ratio_2_sysfs_khz(int ratio)
{
int bclk_khz = 100 * 1000; /* 100,000 KHz = 100 MHz */
int bclk_khz = BCLK_MHZ * KHZ_PER_MHZ; /* Convert MHz to KHz */
return ratio * bclk_khz;
}
@ -1155,12 +1188,12 @@ int ratio_2_sysfs_khz(int ratio)
*/
void update_cpufreq_scaling_freq(int is_max, int cpu, unsigned int ratio)
{
char pathname[64];
char pathname[96];
FILE *fp;
int retval;
int khz;
sprintf(pathname, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_%s_freq", cpu, is_max ? "max" : "min");
snprintf(pathname, sizeof(pathname), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_%s_freq", cpu, is_max ? "max" : "min");
fp = fopen(pathname, "w");
if (!fp) {
@ -1601,32 +1634,41 @@ void set_base_cpu(void)
err(-ENODEV, "No valid cpus found");
}
static void probe_android_msr_path(void)
/* return 0 if path exists */
static int check_msr_path(void)
{
struct stat sb;
char test_path[32];
sprintf(test_path, "/dev/msr%d", base_cpu);
if (stat(test_path, &sb) == 0)
use_android_msr_path = 1;
return stat(msr_path, &sb);
}
/*
* Check Linux MSR path
* If it fails, try modprobe and check it again
* If still no joy, enable Android path and try again
*/
void probe_dev_msr(void)
{
struct stat sb;
char pathname[32];
int retval;
probe_android_msr_path();
use_android_msr_path = 0;
set_msr_path(base_cpu);
if (check_msr_path() == 0)
return;
sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", base_cpu);
if (stat(pathname, &sb)) {
if (system("/sbin/modprobe msr > /dev/null 2>&1")) {
if (use_android_msr_path)
err(-5, "no /dev/msr0, Try \"# modprobe msr\" ");
else
err(-5, "no /dev/cpu/0/msr, Try \"# modprobe msr\" ");
}
}
retval = system("/sbin/modprobe msr > /dev/null 2>&1");
if (check_msr_path() == 0)
return;
use_android_msr_path = 1;
set_msr_path(base_cpu);
if (check_msr_path() == 0)
return;
if (WIFEXITED(retval))
retval = WEXITSTATUS(retval);
errx(-1, "\"msr\" driver support required, modprobe msr returned %d", retval);
}
static void get_cpuid_or_exit(unsigned int leaf, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)