diff options
Diffstat (limited to 'tools/power/cpupower/utils')
21 files changed, 746 insertions, 275 deletions
diff --git a/tools/power/cpupower/utils/builtin.h b/tools/power/cpupower/utils/builtin.h index c10496fbe3c..2284c8ea4e2 100644 --- a/tools/power/cpupower/utils/builtin.h +++ b/tools/power/cpupower/utils/builtin.h @@ -5,6 +5,7 @@ extern int cmd_set(int argc, const char **argv); extern int cmd_info(int argc, const char **argv); extern int cmd_freq_set(int argc, const char **argv); extern int cmd_freq_info(int argc, const char **argv); +extern int cmd_idle_set(int argc, const char **argv); extern int cmd_idle_info(int argc, const char **argv); extern int cmd_monitor(int argc, const char **argv); diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 28953c9a7bd..b4b90a97662 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -82,29 +82,42 @@ static void proc_cpufreq_output(void) } } +static int no_rounding; static void print_speed(unsigned long speed) { unsigned long tmp; - 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)); - } else - printf("%lu kHz", speed); + if (no_rounding) { + if (speed > 1000000) + printf("%u.%06u GHz", ((unsigned int) speed/1000000), + ((unsigned int) speed%1000000)); + else if (speed > 100000) + printf("%u MHz", (unsigned int) speed); + 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; } @@ -113,26 +126,38 @@ static void print_duration(unsigned long duration) { unsigned long tmp; - 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); - + 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; } @@ -525,6 +550,7 @@ static struct option info_opts[] = { { .name = "latency", .has_arg = no_argument, .flag = NULL, .val = 'y'}, { .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'}, { .name = "human", .has_arg = no_argument, .flag = NULL, .val = 'm'}, + { .name = "no-rounding", .has_arg = no_argument, .flag = NULL, .val = 'n'}, { }, }; @@ -538,7 +564,8 @@ int cmd_freq_info(int argc, char **argv) int output_param = 0; do { - ret = getopt_long(argc, argv, "oefwldpgrasmyb", info_opts, NULL); + ret = getopt_long(argc, argv, "oefwldpgrasmybn", info_opts, + NULL); switch (ret) { case '?': output_param = '?'; @@ -575,6 +602,9 @@ int cmd_freq_info(int argc, char **argv) } human = 1; break; + case 'n': + no_rounding = 1; + break; default: fprintf(stderr, "invalid or unknown argument\n"); return EXIT_FAILURE; diff --git a/tools/power/cpupower/utils/cpufreq-set.c b/tools/power/cpupower/utils/cpufreq-set.c index dd1539eb8c6..a416de80c55 100644 --- a/tools/power/cpupower/utils/cpufreq-set.c +++ b/tools/power/cpupower/utils/cpufreq-set.c @@ -257,7 +257,7 @@ int cmd_freq_set(int argc, char **argv) print_unknown_arg(); return -EINVAL; } - if ((sscanf(optarg, "%s", gov)) != 1) { + if ((sscanf(optarg, "%19s", gov)) != 1) { print_unknown_arg(); return -EINVAL; } diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c index b028267c137..75e66de7e7a 100644 --- a/tools/power/cpupower/utils/cpuidle-info.c +++ b/tools/power/cpupower/utils/cpuidle-info.c @@ -22,7 +22,7 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose) { - int idlestates, idlestate; + unsigned int idlestates, idlestate; char *tmp; printf(_ ("Analyzing CPU %d:\n"), cpu); @@ -31,21 +31,11 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose) if (idlestates == 0) { printf(_("CPU %u: No idle states\n"), cpu); return; - } else if (idlestates <= 0) { - printf(_("CPU %u: Can't read idle state info\n"), cpu); - return; - } - tmp = sysfs_get_idlestate_name(cpu, idlestates - 1); - if (!tmp) { - printf(_("Could not determine max idle state %u\n"), - idlestates - 1); - return; } printf(_("Number of idle states: %d\n"), idlestates); - printf(_("Available idle states:")); - for (idlestate = 1; idlestate < idlestates; idlestate++) { + for (idlestate = 0; idlestate < idlestates; idlestate++) { tmp = sysfs_get_idlestate_name(cpu, idlestate); if (!tmp) continue; @@ -57,11 +47,15 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose) if (!verbose) return; - for (idlestate = 1; idlestate < idlestates; idlestate++) { + for (idlestate = 0; idlestate < idlestates; idlestate++) { + int disabled = sysfs_is_idlestate_disabled(cpu, idlestate); + /* Disabled interface not supported on older kernels */ + if (disabled < 0) + disabled = 0; tmp = sysfs_get_idlestate_name(cpu, idlestate); if (!tmp) continue; - printf("%s:\n", tmp); + printf("%s%s:\n", tmp, (disabled) ? " (DISABLED) " : ""); free(tmp); tmp = sysfs_get_idlestate_desc(cpu, idlestate); @@ -106,21 +100,13 @@ static void cpuidle_general_output(void) static void proc_cpuidle_cpu_output(unsigned int cpu) { long max_allowed_cstate = 2000000000; - int cstates, cstate; + unsigned int cstate, cstates; cstates = sysfs_get_idlestate_count(cpu); if (cstates == 0) { - /* - * Go on and print same useless info as you'd see with - * cat /proc/acpi/processor/../power - * printf(_("CPU %u: No C-states available\n"), cpu); - * return; - */ - } else if (cstates <= 0) { - printf(_("CPU %u: Can't read C-state info\n"), cpu); + printf(_("CPU %u: No C-states info\n"), cpu); return; } - /* printf("Cstates: %d\n", cstates); */ printf(_("active state: C0\n")); printf(_("max_cstate: C%u\n"), cstates-1); diff --git a/tools/power/cpupower/utils/cpuidle-set.c b/tools/power/cpupower/utils/cpuidle-set.c new file mode 100644 index 00000000000..d45d8d775c0 --- /dev/null +++ b/tools/power/cpupower/utils/cpuidle-set.c @@ -0,0 +1,181 @@ +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <ctype.h> + +#include <getopt.h> + +#include "cpufreq.h" +#include "helpers/helpers.h" +#include "helpers/sysfs.h" + +static struct option info_opts[] = { + { .name = "disable", + .has_arg = required_argument, .flag = NULL, .val = 'd'}, + { .name = "enable", + .has_arg = required_argument, .flag = NULL, .val = 'e'}, + { .name = "disable-by-latency", + .has_arg = required_argument, .flag = NULL, .val = 'D'}, + { .name = "enable-all", + .has_arg = no_argument, .flag = NULL, .val = 'E'}, + { }, +}; + + +int cmd_idle_set(int argc, char **argv) +{ + extern char *optarg; + extern int optind, opterr, optopt; + int ret = 0, cont = 1, param = 0, disabled; + unsigned long long latency = 0, state_latency; + unsigned int cpu = 0, idlestate = 0, idlestates = 0; + char *endptr; + + do { + ret = getopt_long(argc, argv, "d:e:ED:", info_opts, NULL); + if (ret == -1) + break; + switch (ret) { + case '?': + param = '?'; + cont = 0; + break; + case 'd': + if (param) { + param = -1; + cont = 0; + break; + } + param = ret; + idlestate = atoi(optarg); + break; + case 'e': + if (param) { + param = -1; + cont = 0; + break; + } + param = ret; + idlestate = atoi(optarg); + break; + case 'D': + if (param) { + param = -1; + cont = 0; + break; + } + param = ret; + latency = strtoull(optarg, &endptr, 10); + if (*endptr != '\0') { + printf(_("Bad latency value: %s\n"), optarg); + exit(EXIT_FAILURE); + } + break; + case 'E': + if (param) { + param = -1; + cont = 0; + break; + } + param = ret; + break; + case -1: + cont = 0; + break; + } + } while (cont); + + switch (param) { + case -1: + printf(_("You can't specify more than one " + "output-specific argument\n")); + exit(EXIT_FAILURE); + case '?': + printf(_("invalid or unknown argument\n")); + exit(EXIT_FAILURE); + } + + /* Default is: set all CPUs */ + if (bitmask_isallclear(cpus_chosen)) + bitmask_setall(cpus_chosen); + + for (cpu = bitmask_first(cpus_chosen); + cpu <= bitmask_last(cpus_chosen); cpu++) { + + if (!bitmask_isbitset(cpus_chosen, cpu)) + continue; + + if (sysfs_is_cpu_online(cpu) != 1) + continue; + + idlestates = sysfs_get_idlestate_count(cpu); + if (idlestates <= 0) + continue; + + switch (param) { + case 'd': + ret = sysfs_idlestate_disable(cpu, idlestate, 1); + if (ret == 0) + printf(_("Idlestate %u disabled on CPU %u\n"), idlestate, cpu); + else if (ret == -1) + printf(_("Idlestate %u not available on CPU %u\n"), + idlestate, cpu); + else if (ret == -2) + printf(_("Idlestate disabling not supported by kernel\n")); + else + printf(_("Idlestate %u not disabled on CPU %u\n"), + idlestate, cpu); + break; + case 'e': + ret = sysfs_idlestate_disable(cpu, idlestate, 0); + if (ret == 0) + printf(_("Idlestate %u enabled on CPU %u\n"), idlestate, cpu); + else if (ret == -1) + printf(_("Idlestate %u not available on CPU %u\n"), + idlestate, cpu); + else if (ret == -2) + printf(_("Idlestate enabling not supported by kernel\n")); + else + printf(_("Idlestate %u not enabled on CPU %u\n"), + idlestate, cpu); + break; + case 'D': + for (idlestate = 0; idlestate < idlestates; idlestate++) { + disabled = sysfs_is_idlestate_disabled + (cpu, idlestate); + state_latency = sysfs_get_idlestate_latency + (cpu, idlestate); + printf("CPU: %u - idlestate %u - state_latency: %llu - latency: %llu\n", + cpu, idlestate, state_latency, latency); + if (disabled == 1 || latency > state_latency) + continue; + ret = sysfs_idlestate_disable + (cpu, idlestate, 1); + if (ret == 0) + printf(_("Idlestate %u disabled on CPU %u\n"), idlestate, cpu); + } + break; + case 'E': + for (idlestate = 0; idlestate < idlestates; idlestate++) { + disabled = sysfs_is_idlestate_disabled + (cpu, idlestate); + if (disabled == 1) { + ret = sysfs_idlestate_disable + (cpu, idlestate, 0); + if (ret == 0) + printf(_("Idlestate %u enabled on CPU %u\n"), idlestate, cpu); + } + } + break; + default: + /* Not reachable with proper args checking */ + printf(_("Invalid or unknown argument\n")); + exit(EXIT_FAILURE); + break; + } + } + return EXIT_SUCCESS; +} diff --git a/tools/power/cpupower/utils/cpupower-info.c b/tools/power/cpupower/utils/cpupower-info.c index 3f68632c28c..136d979e958 100644 --- a/tools/power/cpupower/utils/cpupower-info.c +++ b/tools/power/cpupower/utils/cpupower-info.c @@ -18,8 +18,6 @@ static struct option set_opts[] = { { .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'}, - { .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'}, - { .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'}, { }, }; @@ -37,8 +35,6 @@ int cmd_info(int argc, char **argv) union { struct { - int sched_mc:1; - int sched_smt:1; int perf_bias:1; }; int params; @@ -49,23 +45,13 @@ int cmd_info(int argc, char **argv) textdomain(PACKAGE); /* parameter parsing */ - while ((ret = getopt_long(argc, argv, "msb", set_opts, NULL)) != -1) { + while ((ret = getopt_long(argc, argv, "b", set_opts, NULL)) != -1) { switch (ret) { case 'b': if (params.perf_bias) print_wrong_arg_exit(); params.perf_bias = 1; break; - case 'm': - if (params.sched_mc) - print_wrong_arg_exit(); - params.sched_mc = 1; - break; - case 's': - if (params.sched_smt) - print_wrong_arg_exit(); - params.sched_smt = 1; - break; default: print_wrong_arg_exit(); } @@ -78,25 +64,6 @@ int cmd_info(int argc, char **argv) if (bitmask_isallclear(cpus_chosen)) bitmask_setbit(cpus_chosen, 0); - if (params.sched_mc) { - ret = sysfs_get_sched("mc"); - printf(_("System's multi core scheduler setting: ")); - if (ret < 0) - /* if sysfs file is missing it's: errno == ENOENT */ - printf(_("not supported\n")); - else - printf("%d\n", ret); - } - if (params.sched_smt) { - ret = sysfs_get_sched("smt"); - printf(_("System's thread sibling scheduler setting: ")); - if (ret < 0) - /* if sysfs file is missing it's: errno == ENOENT */ - printf(_("not supported\n")); - else - printf("%d\n", ret); - } - /* Add more per cpu options here */ if (!params.perf_bias) return ret; @@ -125,11 +92,12 @@ int cmd_info(int argc, char **argv) if (params.perf_bias) { ret = msr_intel_get_perf_bias(cpu); if (ret < 0) { - printf(_("Could not read perf-bias value\n")); - break; + fprintf(stderr, + _("Could not read perf-bias value[%d]\n"), ret); + exit(EXIT_FAILURE); } else printf(_("perf-bias: %d\n"), ret); } } - return ret; + return 0; } diff --git a/tools/power/cpupower/utils/cpupower-set.c b/tools/power/cpupower/utils/cpupower-set.c index dc4de376211..573c75f8e3f 100644 --- a/tools/power/cpupower/utils/cpupower-set.c +++ b/tools/power/cpupower/utils/cpupower-set.c @@ -18,9 +18,7 @@ #include "helpers/bitmask.h" static struct option set_opts[] = { - { .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'}, - { .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'}, - { .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'}, + { .name = "perf-bias", .has_arg = required_argument, .flag = NULL, .val = 'b'}, { }, }; @@ -38,13 +36,11 @@ int cmd_set(int argc, char **argv) union { struct { - int sched_mc:1; - int sched_smt:1; int perf_bias:1; }; int params; } params; - int sched_mc = 0, sched_smt = 0, perf_bias = 0; + int perf_bias = 0; int ret = 0; setlocale(LC_ALL, ""); @@ -52,7 +48,7 @@ int cmd_set(int argc, char **argv) params.params = 0; /* parameter parsing */ - while ((ret = getopt_long(argc, argv, "m:s:b:", + while ((ret = getopt_long(argc, argv, "b:", set_opts, NULL)) != -1) { switch (ret) { case 'b': @@ -66,28 +62,6 @@ int cmd_set(int argc, char **argv) } params.perf_bias = 1; break; - case 'm': - if (params.sched_mc) - print_wrong_arg_exit(); - sched_mc = atoi(optarg); - if (sched_mc < 0 || sched_mc > 2) { - printf(_("--sched-mc param out " - "of range [0-%d]\n"), 2); - print_wrong_arg_exit(); - } - params.sched_mc = 1; - break; - case 's': - if (params.sched_smt) - print_wrong_arg_exit(); - sched_smt = atoi(optarg); - if (sched_smt < 0 || sched_smt > 2) { - printf(_("--sched-smt param out " - "of range [0-%d]\n"), 2); - print_wrong_arg_exit(); - } - params.sched_smt = 1; - break; default: print_wrong_arg_exit(); } @@ -96,19 +70,6 @@ int cmd_set(int argc, char **argv) if (!params.params) print_wrong_arg_exit(); - if (params.sched_mc) { - ret = sysfs_set_sched("mc", sched_mc); - if (ret) - fprintf(stderr, _("Error setting sched-mc %s\n"), - (ret == -ENODEV) ? "not supported" : ""); - } - if (params.sched_smt) { - ret = sysfs_set_sched("smt", sched_smt); - if (ret) - fprintf(stderr, _("Error setting sched-smt %s\n"), - (ret == -ENODEV) ? "not supported" : ""); - } - /* Default is: set all CPUs */ if (bitmask_isallclear(cpus_chosen)) bitmask_setall(cpus_chosen); diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c index 52bee591c1c..7cdcf88659c 100644 --- a/tools/power/cpupower/utils/cpupower.c +++ b/tools/power/cpupower/utils/cpupower.c @@ -12,17 +12,14 @@ #include <string.h> #include <unistd.h> #include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/utsname.h> #include "builtin.h" #include "helpers/helpers.h" #include "helpers/bitmask.h" -struct cmd_struct { - const char *cmd; - int (*main)(int, const char **); - int needs_root; -}; - #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) static int cmd_help(int argc, const char **argv); @@ -43,10 +40,17 @@ int be_verbose; static void print_help(void); +struct cmd_struct { + const char *cmd; + int (*main)(int, const char **); + int needs_root; +}; + static struct cmd_struct commands[] = { { "frequency-info", cmd_freq_info, 0 }, { "frequency-set", cmd_freq_set, 1 }, { "idle-info", cmd_idle_info, 0 }, + { "idle-set", cmd_idle_set, 1 }, { "set", cmd_set, 1 }, { "info", cmd_info, 0 }, { "monitor", cmd_monitor, 0 }, @@ -168,6 +172,8 @@ int main(int argc, const char *argv[]) { const char *cmd; unsigned int i, ret; + struct stat statbuf; + struct utsname uts; cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF)); @@ -194,6 +200,15 @@ int main(int argc, const char *argv[]) get_cpu_info(0, &cpupower_cpu_info); run_as_root = !getuid(); + if (run_as_root) { + ret = uname(&uts); + if (!ret && !strcmp(uts.machine, "x86_64") && + stat("/dev/cpu/0/msr", &statbuf) != 0) { + if (system("modprobe msr") == -1) + fprintf(stderr, _("MSR access not available.\n")); + } + } + for (i = 0; i < ARRAY_SIZE(commands); i++) { struct cmd_struct *p = commands + i; diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index 87d5605bdda..6437ef39aee 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -112,14 +112,12 @@ int decode_pstates(unsigned int cpu, unsigned int cpu_family, int amd_pci_get_num_boost_states(int *active, int *states) { struct pci_access *pci_acc; - int vendor_id = 0x1022; - int boost_dev_ids[4] = {0x1204, 0x1604, 0x1704, 0}; struct pci_dev *device; uint8_t val = 0; *active = *states = 0; - device = pci_acc_init(&pci_acc, vendor_id, boost_dev_ids); + device = pci_slot_func_init(&pci_acc, 0x18, 4); if (device == NULL) return -ENODEV; diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c index 906895d21cc..93b0aa74ca0 100644 --- a/tools/power/cpupower/utils/helpers/cpuid.c +++ b/tools/power/cpupower/utils/helpers/cpuid.c @@ -158,6 +158,8 @@ out: cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; case 0x2A: /* SNB */ case 0x2D: /* SNB Xeon */ + case 0x3A: /* IVB */ + case 0x3E: /* IVB Xeon */ cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; cpu_info->caps |= CPUPOWER_CAP_IS_SNB; break; diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 2747e738efb..aa9e95486a2 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -66,8 +66,8 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL, #define CPUPOWER_CAP_AMD_CBP 0x00000004 #define CPUPOWER_CAP_PERF_BIAS 0x00000008 #define CPUPOWER_CAP_HAS_TURBO_RATIO 0x00000010 -#define CPUPOWER_CAP_IS_SNB 0x00000011 -#define CPUPOWER_CAP_INTEL_IDA 0x00000012 +#define CPUPOWER_CAP_IS_SNB 0x00000020 +#define CPUPOWER_CAP_INTEL_IDA 0x00000040 #define MAX_HW_PSTATES 10 @@ -92,6 +92,14 @@ extern int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info); extern struct cpupower_cpu_info cpupower_cpu_info; /* cpuid and cpuinfo helpers **************************/ +struct cpuid_core_info { + int pkg; + int core; + int cpu; + + /* flags */ + unsigned int is_online:1; +}; /* CPU topology/hierarchy parsing ******************/ struct cpupower_topology { @@ -101,18 +109,12 @@ struct cpupower_topology { unsigned int threads; /* per core */ /* Array gets mallocated with cores entries, holding per core info */ - struct { - int pkg; - int core; - int cpu; - - /* flags */ - unsigned int is_online:1; - } *core_info; + struct cpuid_core_info *core_info; }; extern int get_cpu_topology(struct cpupower_topology *cpu_top); extern void cpu_topology_release(struct cpupower_topology cpu_top); + /* CPU topology/hierarchy parsing ******************/ /* X86 ONLY ****************************************/ @@ -132,8 +134,11 @@ extern unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu); /* PCI stuff ****************************/ extern int amd_pci_get_num_boost_states(int *active, int *states); -extern struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id, - int *dev_ids); +extern struct pci_dev *pci_acc_init(struct pci_access **pacc, int domain, + int bus, int slot, int func, int vendor, + int dev); +extern struct pci_dev *pci_slot_func_init(struct pci_access **pacc, + int slot, int func); /* PCI stuff ****************************/ diff --git a/tools/power/cpupower/utils/helpers/pci.c b/tools/power/cpupower/utils/helpers/pci.c index cd2eb6fe41c..9690798e644 100644 --- a/tools/power/cpupower/utils/helpers/pci.c +++ b/tools/power/cpupower/utils/helpers/pci.c @@ -10,19 +10,24 @@ * **pacc : if a valid pci_dev is returned * *pacc must be passed to pci_acc_cleanup to free it * - * vendor_id : the pci vendor id matching the pci device to access - * dev_ids : device ids matching the pci device to access + * domain: domain + * bus: bus + * slot: slot + * func: func + * vendor: vendor + * device: device + * Pass -1 for one of the six above to match any * * Returns : * struct pci_dev which can be used with pci_{read,write}_* functions * to access the PCI config space of matching pci devices */ -struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id, - int *dev_ids) +struct pci_dev *pci_acc_init(struct pci_access **pacc, int domain, int bus, + int slot, int func, int vendor, int dev) { - struct pci_filter filter_nb_link = { -1, -1, -1, -1, vendor_id, 0}; + struct pci_filter filter_nb_link = { domain, bus, slot, func, + vendor, dev }; struct pci_dev *device; - unsigned int i; *pacc = pci_alloc(); if (*pacc == NULL) @@ -31,14 +36,20 @@ struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id, pci_init(*pacc); pci_scan_bus(*pacc); - for (i = 0; dev_ids[i] != 0; i++) { - filter_nb_link.device = dev_ids[i]; - for (device = (*pacc)->devices; device; device = device->next) { - if (pci_filter_match(&filter_nb_link, device)) - return device; - } + for (device = (*pacc)->devices; device; device = device->next) { + if (pci_filter_match(&filter_nb_link, device)) + return device; } pci_cleanup(*pacc); return NULL; } + +/* Typically one wants to get a specific slot(device)/func of the root domain + and bus */ +struct pci_dev *pci_slot_func_init(struct pci_access **pacc, int slot, + int func) +{ + return pci_acc_init(pacc, 0, 0, slot, func, -1, -1); +} + #endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c index c6343024a61..851c7a16ca4 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.c +++ b/tools/power/cpupower/utils/helpers/sysfs.c @@ -37,25 +37,6 @@ unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) return (unsigned int) numread; } -static unsigned int sysfs_write_file(const char *path, - const char *value, size_t len) -{ - int fd; - ssize_t numwrite; - - fd = open(path, O_WRONLY); - if (fd == -1) - return 0; - - numwrite = write(fd, value, len); - if (numwrite < 1) { - close(fd); - return 0; - } - close(fd); - return (unsigned int) numwrite; -} - /* * Detect whether a CPU is online * @@ -108,6 +89,33 @@ int sysfs_is_cpu_online(unsigned int cpu) /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ + +/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ + +/* + * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir + * exists. + * For example the functionality to disable c-states was introduced in later + * kernel versions, this function can be used to explicitly check for this + * feature. + * + * returns 1 if the file exists, 0 otherwise. + */ +unsigned int sysfs_idlestate_file_exists(unsigned int cpu, + unsigned int idlestate, + const char *fname) +{ + char path[SYSFS_PATH_MAX]; + struct stat statbuf; + + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", + cpu, idlestate, fname); + if (stat(path, &statbuf) != 0) + return 0; + return 1; +} + /* * helper function to read file from /sys into given buffer * fname is a relative path under "cpuX/cpuidle/stateX/" dir @@ -140,6 +148,40 @@ unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate, return (unsigned int) numread; } +/* + * helper function to write a new value to a /sys file + * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir + * + * Returns the number of bytes written or 0 on error + */ +static +unsigned int sysfs_idlestate_write_file(unsigned int cpu, + unsigned int idlestate, + const char *fname, + const char *value, size_t len) +{ + char path[SYSFS_PATH_MAX]; + int fd; + ssize_t numwrite; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", + cpu, idlestate, fname); + + fd = open(path, O_WRONLY); + if (fd == -1) + return 0; + + numwrite = write(fd, value, len); + if (numwrite < 1) { + close(fd); + return 0; + } + + close(fd); + + return (unsigned int) numwrite; +} + /* read access to files which contain one numeric value */ enum idlestate_value { @@ -147,6 +189,7 @@ enum idlestate_value { IDLESTATE_POWER, IDLESTATE_LATENCY, IDLESTATE_TIME, + IDLESTATE_DISABLE, MAX_IDLESTATE_VALUE_FILES }; @@ -155,6 +198,7 @@ static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = { [IDLESTATE_POWER] = "power", [IDLESTATE_LATENCY] = "latency", [IDLESTATE_TIME] = "time", + [IDLESTATE_DISABLE] = "disable", }; static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu, @@ -224,8 +268,59 @@ static char *sysfs_idlestate_get_one_string(unsigned int cpu, return result; } +/* + * Returns: + * 1 if disabled + * 0 if enabled + * -1 if idlestate is not available + * -2 if disabling is not supported by the kernel + */ +int sysfs_is_idlestate_disabled(unsigned int cpu, + unsigned int idlestate) +{ + if (sysfs_get_idlestate_count(cpu) <= idlestate) + return -1; + + if (!sysfs_idlestate_file_exists(cpu, idlestate, + idlestate_value_files[IDLESTATE_DISABLE])) + return -2; + return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); +} + +/* + * Pass 1 as last argument to disable or 0 to enable the state + * Returns: + * 0 on success + * negative values on error, for example: + * -1 if idlestate is not available + * -2 if disabling is not supported by the kernel + * -3 No write access to disable/enable C-states + */ +int sysfs_idlestate_disable(unsigned int cpu, + unsigned int idlestate, + unsigned int disable) +{ + char value[SYSFS_PATH_MAX]; + int bytes_written; + + if (sysfs_get_idlestate_count(cpu) <= idlestate) + return -1; + + if (!sysfs_idlestate_file_exists(cpu, idlestate, + idlestate_value_files[IDLESTATE_DISABLE])) + return -2; + + snprintf(value, SYSFS_PATH_MAX, "%u", disable); + + bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable", + value, sizeof(disable)); + if (bytes_written) + return 0; + return -3; +} + unsigned long sysfs_get_idlestate_latency(unsigned int cpu, - unsigned int idlestate) + unsigned int idlestate) { return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); } @@ -257,7 +352,7 @@ char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate) * Negativ in error case * Zero if cpuidle does not export any C-states */ -int sysfs_get_idlestate_count(unsigned int cpu) +unsigned int sysfs_get_idlestate_count(unsigned int cpu) { char file[SYSFS_PATH_MAX]; struct stat statbuf; @@ -362,22 +457,7 @@ char *sysfs_get_cpuidle_driver(void) */ int sysfs_get_sched(const char *smt_mc) { - unsigned long value; - char linebuf[MAX_LINE_LEN]; - char *endp; - char path[SYSFS_PATH_MAX]; - - if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc)) - return -EINVAL; - - snprintf(path, sizeof(path), - PATH_TO_CPU "sched_%s_power_savings", smt_mc); - if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0) - return -1; - value = strtoul(linebuf, &endp, 0); - if (endp == linebuf || errno == ERANGE) - return -1; - return value; + return -ENODEV; } /* @@ -388,21 +468,5 @@ int sysfs_get_sched(const char *smt_mc) */ int sysfs_set_sched(const char *smt_mc, int val) { - char linebuf[MAX_LINE_LEN]; - char path[SYSFS_PATH_MAX]; - struct stat statbuf; - - if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc)) - return -EINVAL; - - snprintf(path, sizeof(path), - PATH_TO_CPU "sched_%s_power_savings", smt_mc); - sprintf(linebuf, "%d", val); - - if (stat(path, &statbuf) != 0) - return -ENODEV; - - if (sysfs_write_file(path, linebuf, MAX_LINE_LEN) == 0) - return -1; - return 0; + return -ENODEV; } diff --git a/tools/power/cpupower/utils/helpers/sysfs.h b/tools/power/cpupower/utils/helpers/sysfs.h index 8cb797bbceb..d28f11fedbd 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.h +++ b/tools/power/cpupower/utils/helpers/sysfs.h @@ -7,8 +7,16 @@ extern unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen); +extern unsigned int sysfs_idlestate_file_exists(unsigned int cpu, + unsigned int idlestate, + const char *fname); + extern int sysfs_is_cpu_online(unsigned int cpu); +extern int sysfs_is_idlestate_disabled(unsigned int cpu, + unsigned int idlestate); +extern int sysfs_idlestate_disable(unsigned int cpu, unsigned int idlestate, + unsigned int disable); extern unsigned long sysfs_get_idlestate_latency(unsigned int cpu, unsigned int idlestate); extern unsigned long sysfs_get_idlestate_usage(unsigned int cpu, @@ -19,7 +27,7 @@ extern char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate); extern char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate); -extern int sysfs_get_idlestate_count(unsigned int cpu); +extern unsigned int sysfs_get_idlestate_count(unsigned int cpu); extern char *sysfs_get_cpuidle_governor(void); extern char *sysfs_get_cpuidle_driver(void); diff --git a/tools/power/cpupower/utils/helpers/topology.c b/tools/power/cpupower/utils/helpers/topology.c index 4eae2c47ba4..c13120af519 100644 --- a/tools/power/cpupower/utils/helpers/topology.c +++ b/tools/power/cpupower/utils/helpers/topology.c @@ -20,9 +20,8 @@ #include <helpers/sysfs.h> /* returns -1 on failure, 0 on success */ -int sysfs_topology_read_file(unsigned int cpu, const char *fname) +static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result) { - unsigned long value; char linebuf[MAX_LINE_LEN]; char *endp; char path[SYSFS_PATH_MAX]; @@ -31,20 +30,12 @@ int sysfs_topology_read_file(unsigned int cpu, const char *fname) cpu, fname); if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0) return -1; - value = strtoul(linebuf, &endp, 0); + *result = strtol(linebuf, &endp, 0); if (endp == linebuf || errno == ERANGE) return -1; - return value; + return 0; } -struct cpuid_core_info { - unsigned int pkg; - unsigned int thread; - unsigned int cpu; - /* flags */ - unsigned int is_online:1; -}; - static int __compare(const void *t1, const void *t2) { struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1; @@ -53,9 +44,9 @@ static int __compare(const void *t1, const void *t2) return -1; else if (top1->pkg > top2->pkg) return 1; - else if (top1->thread < top2->thread) + else if (top1->core < top2->core) return -1; - else if (top1->thread > top2->thread) + else if (top1->core > top2->core) return 1; else if (top1->cpu < top2->cpu) return -1; @@ -73,28 +64,42 @@ static int __compare(const void *t1, const void *t2) */ int get_cpu_topology(struct cpupower_topology *cpu_top) { - int cpu, cpus = sysconf(_SC_NPROCESSORS_CONF); + int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF); - cpu_top->core_info = malloc(sizeof(struct cpupower_topology) * cpus); + cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus); if (cpu_top->core_info == NULL) return -ENOMEM; cpu_top->pkgs = cpu_top->cores = 0; for (cpu = 0; cpu < cpus; cpu++) { cpu_top->core_info[cpu].cpu = cpu; cpu_top->core_info[cpu].is_online = sysfs_is_cpu_online(cpu); - cpu_top->core_info[cpu].pkg = - sysfs_topology_read_file(cpu, "physical_package_id"); - if ((int)cpu_top->core_info[cpu].pkg != -1 && - cpu_top->core_info[cpu].pkg > cpu_top->pkgs) - cpu_top->pkgs = cpu_top->core_info[cpu].pkg; - cpu_top->core_info[cpu].core = - sysfs_topology_read_file(cpu, "core_id"); + if(sysfs_topology_read_file( + cpu, + "physical_package_id", + &(cpu_top->core_info[cpu].pkg)) < 0) + return -1; + if(sysfs_topology_read_file( + cpu, + "core_id", + &(cpu_top->core_info[cpu].core)) < 0) + return -1; } - cpu_top->pkgs++; qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), __compare); + /* Count the number of distinct pkgs values. This works + because the primary sort of the core_info struct was just + done by pkg value. */ + last_pkg = cpu_top->core_info[0].pkg; + for(cpu = 1; cpu < cpus; cpu++) { + if(cpu_top->core_info[cpu].pkg != last_pkg) { + last_pkg = cpu_top->core_info[cpu].pkg; + cpu_top->pkgs++; + } + } + cpu_top->pkgs++; + /* Intel's cores count is not consecutively numbered, there may * be a core_id of 3, but none of 2. Assume there always is 0 * Get amount of cores by counting duplicates in a package diff --git a/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c index 202e555988b..2116df9ad83 100644 --- a/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c @@ -20,8 +20,6 @@ #include "idle_monitor/cpupower-monitor.h" #include "helpers/helpers.h" -/******** PCI parts could go into own file and get shared ***************/ - #define PCI_NON_PC0_OFFSET 0xb0 #define PCI_PC1_OFFSET 0xb4 #define PCI_PC6_OFFSET 0xb8 @@ -82,10 +80,7 @@ static cstate_t amd_fam14h_cstates[AMD_FAM14H_STATE_NUM] = { }; static struct pci_access *pci_acc; -static int pci_vendor_id = 0x1022; -static int pci_dev_ids[2] = {0x1716, 0}; static struct pci_dev *amd_fam14h_pci_dev; - static int nbp1_entered; struct timespec start_time; @@ -286,13 +281,13 @@ struct cpuidle_monitor *amd_fam14h_register(void) if (cpupower_cpu_info.vendor != X86_VENDOR_AMD) return NULL; - if (cpupower_cpu_info.family == 0x14) { - if (cpu_count <= 0 || cpu_count > 2) { - fprintf(stderr, "AMD fam14h: Invalid cpu count: %d\n", - cpu_count); - return NULL; - } - } else + if (cpupower_cpu_info.family == 0x14) + strncpy(amd_fam14h_monitor.name, "Fam_14h", + MONITOR_NAME_LEN - 1); + else if (cpupower_cpu_info.family == 0x12) + strncpy(amd_fam14h_monitor.name, "Fam_12h", + MONITOR_NAME_LEN - 1); + else return NULL; /* We do not alloc for nbp1 machine wide counter */ @@ -303,7 +298,9 @@ struct cpuidle_monitor *amd_fam14h_register(void) sizeof(unsigned long long)); } - amd_fam14h_pci_dev = pci_acc_init(&pci_acc, pci_vendor_id, pci_dev_ids); + /* We need PCI device: Slot 18, Func 6, compare with BKDG + for fam 12h/14h */ + amd_fam14h_pci_dev = pci_slot_func_init(&pci_acc, 0x18, 6); if (amd_fam14h_pci_dev == NULL || pci_acc == NULL) return NULL; @@ -325,7 +322,7 @@ static void amd_fam14h_unregister(void) } struct cpuidle_monitor amd_fam14h_monitor = { - .name = "Ontario", + .name = "", .hw_states = amd_fam14h_cstates, .hw_states_num = AMD_FAM14H_STATE_NUM, .start = amd_fam14h_start, diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c index 0d6571e418d..c4bae9203a6 100644 --- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c @@ -39,6 +39,7 @@ static int mode; static int interval = 1; static char *show_monitors_param; static struct cpupower_topology cpu_top; +static unsigned int wake_cpus; /* ToDo: Document this in the manpage */ static char range_abbr[RANGE_MAX] = { 'T', 'C', 'P', 'M', }; @@ -84,7 +85,7 @@ int fill_string_with_spaces(char *s, int n) void print_header(int topology_depth) { int unsigned mon; - int state, need_len, pr_mon_len; + int state, need_len; cstate_t s; char buf[128] = ""; int percent_width = 4; @@ -93,7 +94,6 @@ void print_header(int topology_depth) printf("%s|", buf); for (mon = 0; mon < avail_monitors; mon++) { - pr_mon_len = 0; need_len = monitors[mon]->hw_states_num * (percent_width + 3) - 1; if (mon != 0) { @@ -315,16 +315,28 @@ int fork_it(char **argv) int do_interval_measure(int i) { unsigned int num; + int cpu; + + if (wake_cpus) + for (cpu = 0; cpu < cpu_count; cpu++) + bind_cpu(cpu); for (num = 0; num < avail_monitors; num++) { dprint("HW C-state residency monitor: %s - States: %d\n", monitors[num]->name, monitors[num]->hw_states_num); monitors[num]->start(); } + sleep(i); + + if (wake_cpus) + for (cpu = 0; cpu < cpu_count; cpu++) + bind_cpu(cpu); + for (num = 0; num < avail_monitors; num++) monitors[num]->stop(); + return 0; } @@ -333,7 +345,7 @@ static void cmdline(int argc, char *argv[]) int opt; progname = basename(argv[0]); - while ((opt = getopt(argc, argv, "+li:m:")) != -1) { + while ((opt = getopt(argc, argv, "+lci:m:")) != -1) { switch (opt) { case 'l': if (mode) @@ -352,6 +364,9 @@ static void cmdline(int argc, char *argv[]) mode = show; show_monitors_param = optarg; break; + case 'c': + wake_cpus = 1; + break; default: print_wrong_arg_exit(); } diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h index 9312ee1f2db..9e43f3371fb 100644 --- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h @@ -65,4 +65,21 @@ extern long long timespec_diff_us(struct timespec start, struct timespec end); "could be inaccurate\n"), mes, ov); \ } + +/* Taken over from x86info project sources -> return 0 on success */ +#include <sched.h> +#include <sys/types.h> +#include <unistd.h> +static inline int bind_cpu(int cpu) +{ + cpu_set_t set; + + if (sched_getaffinity(getpid(), sizeof(set), &set) == 0) { + CPU_ZERO(&set); + CPU_SET(cpu, &set); + return sched_setaffinity(getpid(), sizeof(set), &set); + } + return 1; +} + #endif /* __CPUIDLE_INFO_HW__ */ diff --git a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c new file mode 100644 index 00000000000..ebeaba6571a --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c @@ -0,0 +1,196 @@ +/* + * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. + * + * Licensed under the terms of the GNU GPL License version 2. + * + * Based on SandyBridge monitor. Implements the new package C-states + * (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU. + */ + +#if defined(__i386__) || defined(__x86_64__) + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "helpers/helpers.h" +#include "idle_monitor/cpupower-monitor.h" + +#define MSR_PKG_C8_RESIDENCY 0x00000630 +#define MSR_PKG_C9_RESIDENCY 0x00000631 +#define MSR_PKG_C10_RESIDENCY 0x00000632 + +#define MSR_TSC 0x10 + +enum intel_hsw_ext_id { PC8 = 0, PC9, PC10, HSW_EXT_CSTATE_COUNT, + TSC = 0xFFFF }; + +static int hsw_ext_get_count_percent(unsigned int self_id, double *percent, + unsigned int cpu); + +static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = { + { + .name = "PC8", + .desc = N_("Processor Package C8"), + .id = PC8, + .range = RANGE_PACKAGE, + .get_count_percent = hsw_ext_get_count_percent, + }, + { + .name = "PC9", + .desc = N_("Processor Package C9"), + .desc = N_("Processor Package C2"), + .id = PC9, + .range = RANGE_PACKAGE, + .get_count_percent = hsw_ext_get_count_percent, + }, + { + .name = "PC10", + .desc = N_("Processor Package C10"), + .id = PC10, + .range = RANGE_PACKAGE, + .get_count_percent = hsw_ext_get_count_percent, + }, +}; + +static unsigned long long tsc_at_measure_start; +static unsigned long long tsc_at_measure_end; +static unsigned long long *previous_count[HSW_EXT_CSTATE_COUNT]; +static unsigned long long *current_count[HSW_EXT_CSTATE_COUNT]; +/* valid flag for all CPUs. If a MSR read failed it will be zero */ +static int *is_valid; + +static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val, + unsigned int cpu) +{ + int msr; + + switch (id) { + case PC8: + msr = MSR_PKG_C8_RESIDENCY; + break; + case PC9: + msr = MSR_PKG_C9_RESIDENCY; + break; + case PC10: + msr = MSR_PKG_C10_RESIDENCY; + break; + case TSC: + msr = MSR_TSC; + break; + default: + return -1; + }; + if (read_msr(cpu, msr, val)) + return -1; + return 0; +} + +static int hsw_ext_get_count_percent(unsigned int id, double *percent, + unsigned int cpu) +{ + *percent = 0.0; + + if (!is_valid[cpu]) + return -1; + + *percent = (100.0 * + (current_count[id][cpu] - previous_count[id][cpu])) / + (tsc_at_measure_end - tsc_at_measure_start); + + dprint("%s: previous: %llu - current: %llu - (%u)\n", + hsw_ext_cstates[id].name, previous_count[id][cpu], + current_count[id][cpu], cpu); + + dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n", + hsw_ext_cstates[id].name, + (unsigned long long) tsc_at_measure_end - tsc_at_measure_start, + current_count[id][cpu] - previous_count[id][cpu], + *percent, cpu); + + return 0; +} + +static int hsw_ext_start(void) +{ + int num, cpu; + unsigned long long val; + + for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { + for (cpu = 0; cpu < cpu_count; cpu++) { + hsw_ext_get_count(num, &val, cpu); + previous_count[num][cpu] = val; + } + } + hsw_ext_get_count(TSC, &tsc_at_measure_start, 0); + return 0; +} + +static int hsw_ext_stop(void) +{ + unsigned long long val; + int num, cpu; + + hsw_ext_get_count(TSC, &tsc_at_measure_end, 0); + + for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { + for (cpu = 0; cpu < cpu_count; cpu++) { + is_valid[cpu] = !hsw_ext_get_count(num, &val, cpu); + current_count[num][cpu] = val; + } + } + return 0; +} + +struct cpuidle_monitor intel_hsw_ext_monitor; + +static struct cpuidle_monitor *hsw_ext_register(void) +{ + int num; + + if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL + || cpupower_cpu_info.family != 6) + return NULL; + + switch (cpupower_cpu_info.model) { + case 0x45: /* HSW */ + break; + default: + return NULL; + } + + is_valid = calloc(cpu_count, sizeof(int)); + for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { + previous_count[num] = calloc(cpu_count, + sizeof(unsigned long long)); + current_count[num] = calloc(cpu_count, + sizeof(unsigned long long)); + } + intel_hsw_ext_monitor.name_len = strlen(intel_hsw_ext_monitor.name); + return &intel_hsw_ext_monitor; +} + +void hsw_ext_unregister(void) +{ + int num; + free(is_valid); + for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { + free(previous_count[num]); + free(current_count[num]); + } +} + +struct cpuidle_monitor intel_hsw_ext_monitor = { + .name = "HaswellExtended", + .hw_states = hsw_ext_cstates, + .hw_states_num = HSW_EXT_CSTATE_COUNT, + .start = hsw_ext_start, + .stop = hsw_ext_stop, + .do_register = hsw_ext_register, + .unregister = hsw_ext_unregister, + .needs_root = 1, + .overflow_s = 922000000 /* 922337203 seconds TSC overflow + at 20GHz */ +}; +#endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/tools/power/cpupower/utils/idle_monitor/idle_monitors.def b/tools/power/cpupower/utils/idle_monitor/idle_monitors.def index e3f8d9b2b18..0d6ba4dbb9c 100644 --- a/tools/power/cpupower/utils/idle_monitor/idle_monitors.def +++ b/tools/power/cpupower/utils/idle_monitor/idle_monitors.def @@ -2,6 +2,7 @@ DEF(amd_fam14h) DEF(intel_nhm) DEF(intel_snb) +DEF(intel_hsw_ext) DEF(mperf) #endif DEF(cpuidle_sysfs) diff --git a/tools/power/cpupower/utils/idle_monitor/snb_idle.c b/tools/power/cpupower/utils/idle_monitor/snb_idle.c index a1bc07cd53e..efc8a69c9ab 100644 --- a/tools/power/cpupower/utils/idle_monitor/snb_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/snb_idle.c @@ -150,9 +150,19 @@ static struct cpuidle_monitor *snb_register(void) || cpupower_cpu_info.family != 6) return NULL; - if (cpupower_cpu_info.model != 0x2A - && cpupower_cpu_info.model != 0x2D) + switch (cpupower_cpu_info.model) { + case 0x2A: /* SNB */ + case 0x2D: /* SNB Xeon */ + case 0x3A: /* IVB */ + case 0x3E: /* IVB Xeon */ + case 0x3C: /* HSW */ + case 0x3F: /* HSW */ + case 0x45: /* HSW */ + case 0x46: /* HSW */ + break; + default: return NULL; + } is_valid = calloc(cpu_count, sizeof(int)); for (num = 0; num < SNB_CSTATE_COUNT; num++) { |
