From 68f34fad760b68e878aced43934cc02b9d1bed89 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Thu, 9 Jul 2026 17:13:42 -0500 Subject: cpupower: Add generic CPPC performance display Arm64 machines, and possibly others, use the standard ACPI defined CPPC infrastructure. cpupower has CPPC support, but it's largely written around the intricacies of AMD processors, using platform MSRs to avoid shortcomings in the specification. Add a generic CPPC display that depends only on standardized fields. The computed frequency values are best effort and rely on the FW providing optional values that can be used to derive a meaningful frequency at a given unique performance level. Link: https://lore.kernel.org/r/20260709221344.1919794-2-jeremy.linton@arm.com Signed-off-by: Jeremy Linton Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/cppc.c | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tools/power/cpupower/utils/helpers/cppc.c (limited to 'tools') diff --git a/tools/power/cpupower/utils/helpers/cppc.c b/tools/power/cpupower/utils/helpers/cppc.c new file mode 100644 index 000000000000..3493ce8551ea --- /dev/null +++ b/tools/power/cpupower/utils/helpers/cppc.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include + +#include "helpers/helpers.h" +#include "cpufreq.h" +#include "acpi_cppc.h" + +#define cppc_to_frequency(perf) (roundf(slope * (perf) + intercept)) + +void cppc_show_perf_and_freq(unsigned int cpu, int no_rounding) +{ + int64_t nominal = acpi_cppc_get_data(cpu, NOMINAL_PERF); + int64_t nominal_freq = acpi_cppc_get_data(cpu, NOMINAL_FREQ) * 1000; + int64_t lowest = acpi_cppc_get_data(cpu, LOWEST_PERF); + int64_t lowest_freq = acpi_cppc_get_data(cpu, LOWEST_FREQ) * 1000; + unsigned long non_linear = acpi_cppc_get_data(cpu, LOWEST_NONLINEAR_PERF); + unsigned long highest = acpi_cppc_get_data(cpu, HIGHEST_PERF); + float slope, intercept; + + /* do the optional freq fields look invalid? */ + if (!nominal_freq || !lowest_freq || nominal == lowest) + return; + + slope = (float)(nominal_freq - lowest_freq) / (nominal - lowest); + intercept = lowest_freq - slope * lowest; + + printf(_(" CPPC limits:\n")); + printf(_(" Highest Performance: %lu. Maximum Frequency: "), + highest); + /* + * If boost isn't active, the cpuinfo_max doesn't indicate real max + * frequency. + */ + print_speed(cppc_to_frequency(highest), no_rounding); + printf(".\n"); + + printf(_(" Nominal Performance: %lu. Nominal Frequency: "), + acpi_cppc_get_data(cpu, NOMINAL_PERF)); + print_speed(nominal_freq, no_rounding); + printf(".\n"); + + printf(_(" Lowest Non-linear Performance: %lu. Lowest Non-linear Frequency: "), + non_linear); + print_speed(cppc_to_frequency(non_linear), no_rounding); + printf(".\n"); + + printf(_(" Lowest Performance: %lu. Lowest Frequency: "), + acpi_cppc_get_data(cpu, LOWEST_PERF)); + print_speed(lowest_freq, no_rounding); + printf(".\n"); +} -- cgit From 6b8ff068542a62c0fd58a7134282d48dd8a729c9 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Thu, 9 Jul 2026 17:13:43 -0500 Subject: cpupower: Build and call CPPC information on non-AMD processors Now that we have a generic CPPC printout, call it on !AMD processors. If it fails to detect CPPC, or the registers don't look reasonable then it will exit without printing anything. Link: https://lore.kernel.org/r/20260709221344.1919794-3-jeremy.linton@arm.com Signed-off-by: Jeremy Linton Signed-off-by: Shuah Khan --- tools/power/cpupower/Makefile | 2 +- tools/power/cpupower/utils/cpufreq-info.c | 3 ++- tools/power/cpupower/utils/helpers/helpers.h | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index 969716dfe8de..cd8b7315fe74 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -131,7 +131,7 @@ override CFLAGS += -DVERSION=\"$(VERSION)\" -DPACKAGE=\"$(PACKAGE)\" \ UTIL_OBJS = utils/helpers/amd.o utils/helpers/msr.o \ utils/helpers/sysfs.o utils/helpers/misc.o utils/helpers/cpuid.o \ - utils/helpers/pci.o utils/helpers/bitmask.o \ + utils/helpers/pci.o utils/helpers/bitmask.o utils/helpers/cppc.o \ utils/idle_monitor/nhm_idle.o utils/idle_monitor/snb_idle.o \ utils/idle_monitor/hsw_ext_idle.o \ utils/idle_monitor/amd_fam14h_idle.o utils/idle_monitor/cpuidle_sysfs.o \ diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 5a242b491a9d..105f06e690cc 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -477,12 +477,13 @@ static int get_latency(unsigned int cpu, unsigned int human) } /* --performance / -c */ - static int get_perf_cap(unsigned int cpu) { if (cpupower_cpu_info.vendor == X86_VENDOR_AMD && cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) amd_pstate_show_perf_and_freq(cpu, no_rounding); + else + cppc_show_perf_and_freq(cpu, no_rounding); return 0; } diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index a3ad80b9c2c2..9c5126b63966 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -221,4 +221,6 @@ void print_online_cpus(void); void print_offline_cpus(void); void print_speed(unsigned long speed, int no_rounding); +void cppc_show_perf_and_freq(unsigned int cpu, int no_rounding); + #endif /* __CPUPOWERUTILS_HELPERS__ */ -- cgit From 5100bd356cd315112c4e27e66e4f0129800eabd2 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Thu, 9 Jul 2026 17:13:44 -0500 Subject: cpupower: Print kernel and hardware frequency information The kernel asserted frequency from scaling_cur_freq may not always match the hardware reported frequency from cpuinfo_cur_freq. Print both values when they are available, and only print the unavailable message on x86 when the hardware frequency can't be read. Link: https://lore.kernel.org/r/20260709221344.1919794-4-jeremy.linton@arm.com Signed-off-by: Jeremy Linton Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/cpufreq-info.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 105f06e690cc..11629ae49f98 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -270,10 +270,10 @@ static int get_freq_hardware(unsigned int cpu, unsigned int human) { unsigned long freq; - if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) + freq = cpufreq_get_freq_hardware(cpu); + if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF) && !freq) return -EINVAL; - freq = cpufreq_get_freq_hardware(cpu); printf(_(" current CPU frequency: ")); if (!freq) { printf("Unable to call hardware\n"); @@ -514,8 +514,8 @@ static void debug_output_one(unsigned int cpu) get_available_governors(cpu); get_policy(cpu); - if (get_freq_hardware(cpu, 1) < 0) - get_freq_kernel(cpu, 1); + get_freq_hardware(cpu, 1); + get_freq_kernel(cpu, 1); get_boost_mode(cpu); get_perf_cap(cpu); } -- cgit From eeed6071ceda7104e3397dca24cfd3dc73948150 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Mon, 20 Jul 2026 13:14:54 -0500 Subject: cpupower: Add libm to cpupower for generic CPPC view The patch ("cpupower: Add generic CPPC performance display") uses roundf() but didn't include libm explicitly. This results in build breaks in environments where its not automatically inlined. Add libm to the cpupower makefile to correct this. Fixes: 68f34fad760b ("cpupower: Add generic CPPC performance display") Signed-off-by: Jeremy Linton Signed-off-by: Shuah Khan --- tools/power/cpupower/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index cd8b7315fe74..ab428e336d87 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -236,9 +236,9 @@ $(OUTPUT)%.o: %.c $(OUTPUT)cpupower: $(UTIL_OBJS) $(OUTPUT)$(LIBCPUPOWER) $(ECHO) " CC " $@ ifeq ($(strip $(STATIC)),true) - $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lrt -lpci -L$(OUTPUT) -o $@ + $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lm -lrt -lpci -L$(OUTPUT) -o $@ else - $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lcpupower -lrt -lpci -L$(OUTPUT) -o $@ + $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lm -lcpupower -lrt -lpci -L$(OUTPUT) -o $@ endif $(QUIET) $(STRIPCMD) $@ -- cgit From 87bc1e34986d906129ed387e74fdb13de9c5fa89 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 24 Jun 2026 14:27:47 +0200 Subject: tools/power: intel_pstate_tracer: avoid optional imports for help intel_pstate_tracer imports Gnuplot and numpy before parsing command-line options. As a result, even "-h" fails if those optional runtime modules are not installed. Move the imports to the paths that need them. This lets the help and invalid-argument paths describe usage without requiring plotting/data dependencies. While there, fix a typo in the help text and matching comments. Signed-off-by: Yousef Alhouseen Acked-by: Srinivas Pandruvada Link: https://patch.msgid.link/20260624122747.5418-1-alhouseenyousef@gmail.com Signed-off-by: Rafael J. Wysocki --- .../x86/intel_pstate_tracer/intel_pstate_tracer.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py index 38cfbdcdedb7..64001bc80f68 100755 --- a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py +++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py @@ -32,8 +32,6 @@ import re import signal import sys import getopt -import Gnuplot -from numpy import * from decimal import * __author__ = "Srinivas Pandruvada" @@ -88,8 +86,8 @@ def print_help(driver_name): 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(' cpu.csv - comma separated values file with trace contents and some additional calculations.') + print(' cpu???.csv - comma separated 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.') @@ -295,6 +293,8 @@ def common_all_gnuplot_settings(output_png): def common_gnuplot_settings(): """ common gnuplot settings. """ + import Gnuplot + 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 \",\"') @@ -343,7 +343,7 @@ def store_csv(cpu_int, time_pre_dec, time_post_dec, core_busy, scaled, _from, _t graph_data_present = True; def split_csv(current_max_cpu, cpu_mask): - """ seperate the all csv file into per CPU csv files. """ + """ separate the main csv file into per CPU csv files. """ if os.path.exists('cpu.csv'): for index in range(0, current_max_cpu + 1): @@ -482,7 +482,7 @@ def read_trace_data(filename, 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. +# Now separate the main overall csv file into per CPU csv files. split_csv(current_max_cpu, cpu_mask) def signal_handler(signal, frame): @@ -508,8 +508,6 @@ if __name__ == "__main__": 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: @@ -538,6 +536,10 @@ if __name__ == "__main__": print_help('intel_pstate') sys.exit() + from numpy import zeros + + cpu_mask = zeros((MAX_CPUS,), dtype=int) + if cpu_list: for p in re.split("[,]", cpu_list): if int(p) < MAX_CPUS : -- cgit From c7aa1af7b327bb9af163fbd7b83ade6ac07845fd Mon Sep 17 00:00:00 2001 From: Jinseok Kim Date: Mon, 6 Jul 2026 23:38:53 +0900 Subject: selftests/cpufreq: Remove unused local variables from switch_show_governor() switch_show_governor() assigns the current governor and frequency to local variables before switching governors. However, these variables are never referenced afterwards. The function does not restore the previous governor or use the saved frequency, as backup_governor() and restore_governor() already handle state preservation elsewhere. Signed-off-by: Jinseok Kim Acked-by: Viresh Kumar Link: https://patch.msgid.link/20260706143857.3306-1-always.starving0@gmail.com Signed-off-by: Rafael J. Wysocki --- tools/testing/selftests/cpufreq/governor.sh | 5 ----- 1 file changed, 5 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/cpufreq/governor.sh b/tools/testing/selftests/cpufreq/governor.sh index fe37df79c087..212ef1cf43d5 100755 --- a/tools/testing/selftests/cpufreq/governor.sh +++ b/tools/testing/selftests/cpufreq/governor.sh @@ -100,11 +100,6 @@ switch_governor() # $1: policy, $2: governor switch_show_governor() { - cur_gov=find_current_governor - if [ $cur_gov == "userspace" ]; then - cur_freq=find_current_freq - fi - # switch governor __switch_governor $1 $2 -- cgit From 60e32ff9ed850db22d16ff91a17e99a9efbca8cf Mon Sep 17 00:00:00 2001 From: Jinseok Kim Date: Mon, 6 Jul 2026 23:38:54 +0900 Subject: selftests/cpufreq: Remove unnecessary sudo from quick_shuffle() The cpufreq selftests are always executed through main.sh, which verifies that the test is run as root before dispatching any test case. Therefore, invoking sudo inside quick_shuffle() is redundant and may cause failures in environments where sudo is unavailable. Signed-off-by: Jinseok Kim Acked-by: Viresh Kumar Link: https://patch.msgid.link/20260706143857.3306-2-always.starving0@gmail.com Signed-off-by: Rafael J. Wysocki --- tools/testing/selftests/cpufreq/special-tests.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/cpufreq/special-tests.sh b/tools/testing/selftests/cpufreq/special-tests.sh index 8d40505dc468..f45eb525f3b1 100755 --- a/tools/testing/selftests/cpufreq/special-tests.sh +++ b/tools/testing/selftests/cpufreq/special-tests.sh @@ -65,8 +65,8 @@ quick_shuffle() # this is called concurrently from governor_race for I in `seq 1000` do - echo ondemand | sudo tee $CPUFREQROOT/policy*/scaling_governor & - echo userspace | sudo tee $CPUFREQROOT/policy*/scaling_governor & + echo ondemand | tee $CPUFREQROOT/policy*/scaling_governor & + echo userspace | tee $CPUFREQROOT/policy*/scaling_governor & done } -- cgit From 4763f0db60a538c29d882fb6ab9e9371fbeb697c Mon Sep 17 00:00:00 2001 From: Yiwei Lin Date: Wed, 8 Jul 2026 00:36:47 +0800 Subject: kselftest: cpufreq: Backup and restore governor for sptests After executing cpufreq sptest, the system governor will be overwritten with the governor switched during the test. Restore this setting to maintain consistency before and after the test. Signed-off-by: Yiwei Lin Acked-by: Viresh Kumar Link: https://patch.msgid.link/20260707163647.6646-1-s921975628@gmail.com Signed-off-by: Rafael J. Wysocki --- tools/testing/selftests/cpufreq/governor.sh | 21 +++++++++++++++++++++ tools/testing/selftests/cpufreq/special-tests.sh | 14 ++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/cpufreq/governor.sh b/tools/testing/selftests/cpufreq/governor.sh index 212ef1cf43d5..cf59e63f8e14 100755 --- a/tools/testing/selftests/cpufreq/governor.sh +++ b/tools/testing/selftests/cpufreq/governor.sh @@ -16,6 +16,12 @@ source cpufreq.sh CUR_GOV= CUR_FREQ= +# Per-policy backup, keyed by policy so multiple policies can be saved at once +# (backup_governor/restore_governor also keep CUR_GOV/CUR_FREQ for callers that +# read them directly). +declare -A SAVED_GOVERNORS +declare -A SAVED_FREQS + # Find governor's directory path # $1: policy, $2: governor find_gov_directory() @@ -39,11 +45,13 @@ find_current_governor() backup_governor() { CUR_GOV=$(find_current_governor $1) + SAVED_GOVERNORS[$1]=$CUR_GOV printf "Governor backup done for $1: $CUR_GOV\n" if [ $CUR_GOV == "userspace" ]; then CUR_FREQ=$(find_current_freq $1) + SAVED_FREQS[$1]=$CUR_FREQ printf "Governor frequency backup done for $1: $CUR_FREQ\n" fi @@ -53,11 +61,13 @@ backup_governor() # $1: policy restore_governor() { + CUR_GOV=${SAVED_GOVERNORS[$1]} __switch_governor $1 $CUR_GOV printf "Governor restored for $1 to $CUR_GOV\n" if [ $CUR_GOV == "userspace" ]; then + CUR_FREQ=${SAVED_FREQS[$1]} set_cpu_frequency $1 $CUR_FREQ printf "Governor frequency restored for $1: $CUR_FREQ\n" fi @@ -65,6 +75,17 @@ restore_governor() printf "\n" } +# Save/restore governors for every policy at once +save_all_governors() +{ + for_each_policy backup_governor +} + +restore_all_governors() +{ + for_each_policy restore_governor +} + # param: # $1: policy, $2: governor __switch_governor() diff --git a/tools/testing/selftests/cpufreq/special-tests.sh b/tools/testing/selftests/cpufreq/special-tests.sh index f45eb525f3b1..e87ed7c8e5e5 100755 --- a/tools/testing/selftests/cpufreq/special-tests.sh +++ b/tools/testing/selftests/cpufreq/special-tests.sh @@ -40,7 +40,9 @@ simple_lockdep() { printf "** Test: Running ${FUNCNAME[0]} **\n" + save_all_governors for_each_policy __simple_lockdep + restore_all_governors } # Test 2 @@ -56,7 +58,10 @@ concurrent_lockdep() { printf "** Test: Running ${FUNCNAME[0]} **\n" + save_all_governors for_each_policy_concurrent __concurrent_lockdep + wait + restore_all_governors } # Test 3 @@ -68,17 +73,23 @@ quick_shuffle() echo ondemand | tee $CPUFREQROOT/policy*/scaling_governor & echo userspace | tee $CPUFREQROOT/policy*/scaling_governor & done + wait } governor_race() { printf "** Test: Running ${FUNCNAME[0]} **\n" + save_all_governors + # run 8 concurrent instances for I in `seq 8` do quick_shuffle & done + wait + + restore_all_governors } # Test 4 @@ -112,5 +123,8 @@ hotplug_with_updates_cpu() hotplug_with_updates() { + save_all_governors for_each_non_boot_cpu hotplug_with_updates_cpu + wait + restore_all_governors } -- cgit From adfe0057326ffbc2cd5ff69a57ec0e50b5e74095 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 24 Jul 2026 03:45:37 +0900 Subject: cpupower: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Link: https://lore.kernel.org/linux-pm/20260723184538.3888637-36-ekffu200098@gmail.com/raw Signed-off-by: Sang-Heon Jeon Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/powercap-info.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'tools') diff --git a/tools/power/cpupower/utils/powercap-info.c b/tools/power/cpupower/utils/powercap-info.c index e53033488218..88a5edb76315 100644 --- a/tools/power/cpupower/utils/powercap-info.c +++ b/tools/power/cpupower/utils/powercap-info.c @@ -47,8 +47,6 @@ static int powercap_print_one_zone(struct powercap_zone *zone) printf("\n"); - if (ret != 0) - return ret; return ret; } -- cgit