forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
668 lines
15 KiB
668 lines
15 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* (C) 2004-2009 Dominik Brodowski <[email protected]> |
|
*/ |
|
|
|
|
|
#include <unistd.h> |
|
#include <stdio.h> |
|
#include <errno.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <limits.h> |
|
|
|
#include <getopt.h> |
|
|
|
#include "cpufreq.h" |
|
#include "helpers/sysfs.h" |
|
#include "helpers/helpers.h" |
|
#include "helpers/bitmask.h" |
|
|
|
#define LINE_LEN 10 |
|
|
|
static unsigned int count_cpus(void) |
|
{ |
|
FILE *fp; |
|
char value[LINE_LEN]; |
|
unsigned int ret = 0; |
|
unsigned int cpunr = 0; |
|
|
|
fp = fopen("/proc/stat", "r"); |
|
if (!fp) { |
|
printf(_("Couldn't count the number of CPUs (%s: %s), assuming 1\n"), "/proc/stat", strerror(errno)); |
|
return 1; |
|
} |
|
|
|
while (!feof(fp)) { |
|
if (!fgets(value, LINE_LEN, fp)) |
|
continue; |
|
value[LINE_LEN - 1] = '\0'; |
|
if (strlen(value) < (LINE_LEN - 2)) |
|
continue; |
|
if (strstr(value, "cpu ")) |
|
continue; |
|
if (sscanf(value, "cpu%d ", &cpunr) != 1) |
|
continue; |
|
if (cpunr > ret) |
|
ret = cpunr; |
|
} |
|
fclose(fp); |
|
|
|
/* cpu count starts from 0, on error return 1 (UP) */ |
|
return ret + 1; |
|
} |
|
|
|
|
|
static void proc_cpufreq_output(void) |
|
{ |
|
unsigned int cpu, nr_cpus; |
|
struct cpufreq_policy *policy; |
|
unsigned int min_pctg = 0; |
|
unsigned int max_pctg = 0; |
|
unsigned long min, max; |
|
|
|
printf(_(" minimum CPU frequency - maximum CPU frequency - governor\n")); |
|
|
|
nr_cpus = count_cpus(); |
|
for (cpu = 0; cpu < nr_cpus; cpu++) { |
|
policy = cpufreq_get_policy(cpu); |
|
if (!policy) |
|
continue; |
|
|
|
if (cpufreq_get_hardware_limits(cpu, &min, &max)) { |
|
max = 0; |
|
} else { |
|
min_pctg = (policy->min * 100) / max; |
|
max_pctg = (policy->max * 100) / max; |
|
} |
|
printf("CPU%3d %9lu kHz (%3d %%) - %9lu kHz (%3d %%) - %s\n", |
|
cpu , policy->min, max ? min_pctg : 0, policy->max, |
|
max ? max_pctg : 0, policy->governor); |
|
|
|
cpufreq_put_policy(policy); |
|
} |
|
} |
|
|
|
static int no_rounding; |
|
static void print_speed(unsigned long speed) |
|
{ |
|
unsigned long tmp; |
|
|
|
if (no_rounding) { |
|
if (speed > 1000000) |
|
printf("%u.%06u GHz", ((unsigned int) speed/1000000), |
|
((unsigned int) speed%1000000)); |
|
else if (speed > 1000) |
|
printf("%u.%03u MHz", ((unsigned int) speed/1000), |
|
(unsigned int) (speed%1000)); |
|
else |
|
printf("%lu kHz", speed); |
|
} else { |
|
if (speed > 1000000) { |
|
tmp = speed%10000; |
|
if (tmp >= 5000) |
|
speed += 10000; |
|
printf("%u.%02u GHz", ((unsigned int) speed/1000000), |
|
((unsigned int) (speed%1000000)/10000)); |
|
} else if (speed > 100000) { |
|
tmp = speed%1000; |
|
if (tmp >= 500) |
|
speed += 1000; |
|
printf("%u MHz", ((unsigned int) speed/1000)); |
|
} else if (speed > 1000) { |
|
tmp = speed%100; |
|
if (tmp >= 50) |
|
speed += 100; |
|
printf("%u.%01u MHz", ((unsigned int) speed/1000), |
|
((unsigned int) (speed%1000)/100)); |
|
} |
|
} |
|
|
|
return; |
|
} |
|
|
|
static void print_duration(unsigned long duration) |
|
{ |
|
unsigned long tmp; |
|
|
|
if (no_rounding) { |
|
if (duration > 1000000) |
|
printf("%u.%06u ms", ((unsigned int) duration/1000000), |
|
((unsigned int) duration%1000000)); |
|
else if (duration > 100000) |
|
printf("%u us", ((unsigned int) duration/1000)); |
|
else if (duration > 1000) |
|
printf("%u.%03u us", ((unsigned int) duration/1000), |
|
((unsigned int) duration%1000)); |
|
else |
|
printf("%lu ns", duration); |
|
} else { |
|
if (duration > 1000000) { |
|
tmp = duration%10000; |
|
if (tmp >= 5000) |
|
duration += 10000; |
|
printf("%u.%02u ms", ((unsigned int) duration/1000000), |
|
((unsigned int) (duration%1000000)/10000)); |
|
} else if (duration > 100000) { |
|
tmp = duration%1000; |
|
if (tmp >= 500) |
|
duration += 1000; |
|
printf("%u us", ((unsigned int) duration / 1000)); |
|
} else if (duration > 1000) { |
|
tmp = duration%100; |
|
if (tmp >= 50) |
|
duration += 100; |
|
printf("%u.%01u us", ((unsigned int) duration/1000), |
|
((unsigned int) (duration%1000)/100)); |
|
} else |
|
printf("%lu ns", duration); |
|
} |
|
return; |
|
} |
|
|
|
static int get_boost_mode_x86(unsigned int cpu) |
|
{ |
|
int support, active, b_states = 0, ret, pstate_no, i; |
|
/* ToDo: Make this more global */ |
|
unsigned long pstates[MAX_HW_PSTATES] = {0,}; |
|
|
|
ret = cpufreq_has_boost_support(cpu, &support, &active, &b_states); |
|
if (ret) { |
|
printf(_("Error while evaluating Boost Capabilities" |
|
" on CPU %d -- are you root?\n"), cpu); |
|
return ret; |
|
} |
|
/* P state changes via MSR are identified via cpuid 80000007 |
|
on Intel and AMD, but we assume boost capable machines can do that |
|
if (cpuid_eax(0x80000000) >= 0x80000007 |
|
&& (cpuid_edx(0x80000007) & (1 << 7))) |
|
*/ |
|
|
|
printf(_(" boost state support:\n")); |
|
|
|
printf(_(" Supported: %s\n"), support ? _("yes") : _("no")); |
|
printf(_(" Active: %s\n"), active ? _("yes") : _("no")); |
|
|
|
if ((cpupower_cpu_info.vendor == X86_VENDOR_AMD && |
|
cpupower_cpu_info.family >= 0x10) || |
|
cpupower_cpu_info.vendor == X86_VENDOR_HYGON) { |
|
ret = decode_pstates(cpu, b_states, pstates, &pstate_no); |
|
if (ret) |
|
return ret; |
|
|
|
printf(_(" Boost States: %d\n"), b_states); |
|
printf(_(" Total States: %d\n"), pstate_no); |
|
for (i = 0; i < pstate_no; i++) { |
|
if (!pstates[i]) |
|
continue; |
|
if (i < b_states) |
|
printf(_(" Pstate-Pb%d: %luMHz (boost state)" |
|
"\n"), i, pstates[i]); |
|
else |
|
printf(_(" Pstate-P%d: %luMHz\n"), |
|
i - b_states, pstates[i]); |
|
} |
|
} else if (cpupower_cpu_info.caps & CPUPOWER_CAP_HAS_TURBO_RATIO) { |
|
double bclk; |
|
unsigned long long intel_turbo_ratio = 0; |
|
unsigned int ratio; |
|
|
|
/* Any way to autodetect this ? */ |
|
if (cpupower_cpu_info.caps & CPUPOWER_CAP_IS_SNB) |
|
bclk = 100.00; |
|
else |
|
bclk = 133.33; |
|
intel_turbo_ratio = msr_intel_get_turbo_ratio(cpu); |
|
dprint (" Ratio: 0x%llx - bclk: %f\n", |
|
intel_turbo_ratio, bclk); |
|
|
|
ratio = (intel_turbo_ratio >> 24) & 0xFF; |
|
if (ratio) |
|
printf(_(" %.0f MHz max turbo 4 active cores\n"), |
|
ratio * bclk); |
|
|
|
ratio = (intel_turbo_ratio >> 16) & 0xFF; |
|
if (ratio) |
|
printf(_(" %.0f MHz max turbo 3 active cores\n"), |
|
ratio * bclk); |
|
|
|
ratio = (intel_turbo_ratio >> 8) & 0xFF; |
|
if (ratio) |
|
printf(_(" %.0f MHz max turbo 2 active cores\n"), |
|
ratio * bclk); |
|
|
|
ratio = (intel_turbo_ratio >> 0) & 0xFF; |
|
if (ratio) |
|
printf(_(" %.0f MHz max turbo 1 active cores\n"), |
|
ratio * bclk); |
|
} |
|
return 0; |
|
} |
|
|
|
/* --boost / -b */ |
|
|
|
static int get_boost_mode(unsigned int cpu) |
|
{ |
|
struct cpufreq_available_frequencies *freqs; |
|
|
|
if (cpupower_cpu_info.vendor == X86_VENDOR_AMD || |
|
cpupower_cpu_info.vendor == X86_VENDOR_HYGON || |
|
cpupower_cpu_info.vendor == X86_VENDOR_INTEL) |
|
return get_boost_mode_x86(cpu); |
|
|
|
freqs = cpufreq_get_boost_frequencies(cpu); |
|
if (freqs) { |
|
printf(_(" boost frequency steps: ")); |
|
while (freqs->next) { |
|
print_speed(freqs->frequency); |
|
printf(", "); |
|
freqs = freqs->next; |
|
} |
|
print_speed(freqs->frequency); |
|
printf("\n"); |
|
cpufreq_put_available_frequencies(freqs); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/* --freq / -f */ |
|
|
|
static int get_freq_kernel(unsigned int cpu, unsigned int human) |
|
{ |
|
unsigned long freq = cpufreq_get_freq_kernel(cpu); |
|
printf(_(" current CPU frequency: ")); |
|
if (!freq) { |
|
printf(_(" Unable to call to kernel\n")); |
|
return -EINVAL; |
|
} |
|
if (human) { |
|
print_speed(freq); |
|
} else |
|
printf("%lu", freq); |
|
printf(_(" (asserted by call to kernel)\n")); |
|
return 0; |
|
} |
|
|
|
|
|
/* --hwfreq / -w */ |
|
|
|
static int get_freq_hardware(unsigned int cpu, unsigned int human) |
|
{ |
|
unsigned long freq = cpufreq_get_freq_hardware(cpu); |
|
printf(_(" current CPU frequency: ")); |
|
if (!freq) { |
|
printf("Unable to call hardware\n"); |
|
return -EINVAL; |
|
} |
|
if (human) { |
|
print_speed(freq); |
|
} else |
|
printf("%lu", freq); |
|
printf(_(" (asserted by call to hardware)\n")); |
|
return 0; |
|
} |
|
|
|
/* --hwlimits / -l */ |
|
|
|
static int get_hardware_limits(unsigned int cpu, unsigned int human) |
|
{ |
|
unsigned long min, max; |
|
|
|
if (cpufreq_get_hardware_limits(cpu, &min, &max)) { |
|
printf(_("Not Available\n")); |
|
return -EINVAL; |
|
} |
|
|
|
if (human) { |
|
printf(_(" hardware limits: ")); |
|
print_speed(min); |
|
printf(" - "); |
|
print_speed(max); |
|
printf("\n"); |
|
} else { |
|
printf("%lu %lu\n", min, max); |
|
} |
|
return 0; |
|
} |
|
|
|
/* --driver / -d */ |
|
|
|
static int get_driver(unsigned int cpu) |
|
{ |
|
char *driver = cpufreq_get_driver(cpu); |
|
if (!driver) { |
|
printf(_(" no or unknown cpufreq driver is active on this CPU\n")); |
|
return -EINVAL; |
|
} |
|
printf(" driver: %s\n", driver); |
|
cpufreq_put_driver(driver); |
|
return 0; |
|
} |
|
|
|
/* --policy / -p */ |
|
|
|
static int get_policy(unsigned int cpu) |
|
{ |
|
struct cpufreq_policy *policy = cpufreq_get_policy(cpu); |
|
if (!policy) { |
|
printf(_(" Unable to determine current policy\n")); |
|
return -EINVAL; |
|
} |
|
printf(_(" current policy: frequency should be within ")); |
|
print_speed(policy->min); |
|
printf(_(" and ")); |
|
print_speed(policy->max); |
|
|
|
printf(".\n "); |
|
printf(_("The governor \"%s\" may decide which speed to use\n" |
|
" within this range.\n"), |
|
policy->governor); |
|
cpufreq_put_policy(policy); |
|
return 0; |
|
} |
|
|
|
/* --governors / -g */ |
|
|
|
static int get_available_governors(unsigned int cpu) |
|
{ |
|
struct cpufreq_available_governors *governors = |
|
cpufreq_get_available_governors(cpu); |
|
|
|
printf(_(" available cpufreq governors: ")); |
|
if (!governors) { |
|
printf(_("Not Available\n")); |
|
return -EINVAL; |
|
} |
|
|
|
while (governors->next) { |
|
printf("%s ", governors->governor); |
|
governors = governors->next; |
|
} |
|
printf("%s\n", governors->governor); |
|
cpufreq_put_available_governors(governors); |
|
return 0; |
|
} |
|
|
|
|
|
/* --affected-cpus / -a */ |
|
|
|
static int get_affected_cpus(unsigned int cpu) |
|
{ |
|
struct cpufreq_affected_cpus *cpus = cpufreq_get_affected_cpus(cpu); |
|
|
|
printf(_(" CPUs which need to have their frequency coordinated by software: ")); |
|
if (!cpus) { |
|
printf(_("Not Available\n")); |
|
return -EINVAL; |
|
} |
|
|
|
while (cpus->next) { |
|
printf("%d ", cpus->cpu); |
|
cpus = cpus->next; |
|
} |
|
printf("%d\n", cpus->cpu); |
|
cpufreq_put_affected_cpus(cpus); |
|
return 0; |
|
} |
|
|
|
/* --related-cpus / -r */ |
|
|
|
static int get_related_cpus(unsigned int cpu) |
|
{ |
|
struct cpufreq_affected_cpus *cpus = cpufreq_get_related_cpus(cpu); |
|
|
|
printf(_(" CPUs which run at the same hardware frequency: ")); |
|
if (!cpus) { |
|
printf(_("Not Available\n")); |
|
return -EINVAL; |
|
} |
|
|
|
while (cpus->next) { |
|
printf("%d ", cpus->cpu); |
|
cpus = cpus->next; |
|
} |
|
printf("%d\n", cpus->cpu); |
|
cpufreq_put_related_cpus(cpus); |
|
return 0; |
|
} |
|
|
|
/* --stats / -s */ |
|
|
|
static int get_freq_stats(unsigned int cpu, unsigned int human) |
|
{ |
|
unsigned long total_trans = cpufreq_get_transitions(cpu); |
|
unsigned long long total_time; |
|
struct cpufreq_stats *stats = cpufreq_get_stats(cpu, &total_time); |
|
while (stats) { |
|
if (human) { |
|
print_speed(stats->frequency); |
|
printf(":%.2f%%", |
|
(100.0 * stats->time_in_state) / total_time); |
|
} else |
|
printf("%lu:%llu", |
|
stats->frequency, stats->time_in_state); |
|
stats = stats->next; |
|
if (stats) |
|
printf(", "); |
|
} |
|
cpufreq_put_stats(stats); |
|
if (total_trans) |
|
printf(" (%lu)\n", total_trans); |
|
return 0; |
|
} |
|
|
|
/* --latency / -y */ |
|
|
|
static int get_latency(unsigned int cpu, unsigned int human) |
|
{ |
|
unsigned long latency = cpufreq_get_transition_latency(cpu); |
|
|
|
printf(_(" maximum transition latency: ")); |
|
if (!latency || latency == UINT_MAX) { |
|
printf(_(" Cannot determine or is not supported.\n")); |
|
return -EINVAL; |
|
} |
|
|
|
if (human) { |
|
print_duration(latency); |
|
printf("\n"); |
|
} else |
|
printf("%lu\n", latency); |
|
return 0; |
|
} |
|
|
|
static void debug_output_one(unsigned int cpu) |
|
{ |
|
struct cpufreq_available_frequencies *freqs; |
|
|
|
get_driver(cpu); |
|
get_related_cpus(cpu); |
|
get_affected_cpus(cpu); |
|
get_latency(cpu, 1); |
|
get_hardware_limits(cpu, 1); |
|
|
|
freqs = cpufreq_get_available_frequencies(cpu); |
|
if (freqs) { |
|
printf(_(" available frequency steps: ")); |
|
while (freqs->next) { |
|
print_speed(freqs->frequency); |
|
printf(", "); |
|
freqs = freqs->next; |
|
} |
|
print_speed(freqs->frequency); |
|
printf("\n"); |
|
cpufreq_put_available_frequencies(freqs); |
|
} |
|
|
|
get_available_governors(cpu); |
|
get_policy(cpu); |
|
if (get_freq_hardware(cpu, 1) < 0) |
|
get_freq_kernel(cpu, 1); |
|
get_boost_mode(cpu); |
|
} |
|
|
|
static struct option info_opts[] = { |
|
{"debug", no_argument, NULL, 'e'}, |
|
{"boost", no_argument, NULL, 'b'}, |
|
{"freq", no_argument, NULL, 'f'}, |
|
{"hwfreq", no_argument, NULL, 'w'}, |
|
{"hwlimits", no_argument, NULL, 'l'}, |
|
{"driver", no_argument, NULL, 'd'}, |
|
{"policy", no_argument, NULL, 'p'}, |
|
{"governors", no_argument, NULL, 'g'}, |
|
{"related-cpus", no_argument, NULL, 'r'}, |
|
{"affected-cpus", no_argument, NULL, 'a'}, |
|
{"stats", no_argument, NULL, 's'}, |
|
{"latency", no_argument, NULL, 'y'}, |
|
{"proc", no_argument, NULL, 'o'}, |
|
{"human", no_argument, NULL, 'm'}, |
|
{"no-rounding", no_argument, NULL, 'n'}, |
|
{ }, |
|
}; |
|
|
|
int cmd_freq_info(int argc, char **argv) |
|
{ |
|
extern char *optarg; |
|
extern int optind, opterr, optopt; |
|
int ret = 0, cont = 1; |
|
unsigned int cpu = 0; |
|
unsigned int human = 0; |
|
int output_param = 0; |
|
|
|
do { |
|
ret = getopt_long(argc, argv, "oefwldpgrasmybn", info_opts, |
|
NULL); |
|
switch (ret) { |
|
case '?': |
|
output_param = '?'; |
|
cont = 0; |
|
break; |
|
case -1: |
|
cont = 0; |
|
break; |
|
case 'b': |
|
case 'o': |
|
case 'a': |
|
case 'r': |
|
case 'g': |
|
case 'p': |
|
case 'd': |
|
case 'l': |
|
case 'w': |
|
case 'f': |
|
case 'e': |
|
case 's': |
|
case 'y': |
|
if (output_param) { |
|
output_param = -1; |
|
cont = 0; |
|
break; |
|
} |
|
output_param = ret; |
|
break; |
|
case 'm': |
|
if (human) { |
|
output_param = -1; |
|
cont = 0; |
|
break; |
|
} |
|
human = 1; |
|
break; |
|
case 'n': |
|
no_rounding = 1; |
|
break; |
|
default: |
|
fprintf(stderr, "invalid or unknown argument\n"); |
|
return EXIT_FAILURE; |
|
} |
|
} while (cont); |
|
|
|
switch (output_param) { |
|
case 'o': |
|
if (!bitmask_isallclear(cpus_chosen)) { |
|
printf(_("The argument passed to this tool can't be " |
|
"combined with passing a --cpu argument\n")); |
|
return -EINVAL; |
|
} |
|
break; |
|
case 0: |
|
output_param = 'e'; |
|
} |
|
|
|
ret = 0; |
|
|
|
/* Default is: show output of CPU 0 only */ |
|
if (bitmask_isallclear(cpus_chosen)) |
|
bitmask_setbit(cpus_chosen, 0); |
|
|
|
switch (output_param) { |
|
case -1: |
|
printf(_("You can't specify more than one --cpu parameter and/or\n" |
|
"more than one output-specific argument\n")); |
|
return -EINVAL; |
|
case '?': |
|
printf(_("invalid or unknown argument\n")); |
|
return -EINVAL; |
|
case 'o': |
|
proc_cpufreq_output(); |
|
return EXIT_SUCCESS; |
|
} |
|
|
|
for (cpu = bitmask_first(cpus_chosen); |
|
cpu <= bitmask_last(cpus_chosen); cpu++) { |
|
|
|
if (!bitmask_isbitset(cpus_chosen, cpu)) |
|
continue; |
|
|
|
printf(_("analyzing CPU %d:\n"), cpu); |
|
|
|
if (sysfs_is_cpu_online(cpu) != 1) { |
|
printf(_(" *is offline\n")); |
|
printf("\n"); |
|
continue; |
|
} |
|
|
|
switch (output_param) { |
|
case 'b': |
|
get_boost_mode(cpu); |
|
break; |
|
case 'e': |
|
debug_output_one(cpu); |
|
break; |
|
case 'a': |
|
ret = get_affected_cpus(cpu); |
|
break; |
|
case 'r': |
|
ret = get_related_cpus(cpu); |
|
break; |
|
case 'g': |
|
ret = get_available_governors(cpu); |
|
break; |
|
case 'p': |
|
ret = get_policy(cpu); |
|
break; |
|
case 'd': |
|
ret = get_driver(cpu); |
|
break; |
|
case 'l': |
|
ret = get_hardware_limits(cpu, human); |
|
break; |
|
case 'w': |
|
ret = get_freq_hardware(cpu, human); |
|
break; |
|
case 'f': |
|
ret = get_freq_kernel(cpu, human); |
|
break; |
|
case 's': |
|
ret = get_freq_stats(cpu, human); |
|
break; |
|
case 'y': |
|
ret = get_latency(cpu, human); |
|
break; |
|
} |
|
if (ret) |
|
return ret; |
|
} |
|
return ret; |
|
}
|
|
|