Commit be5378f3 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/brodo/cpupowerutils

* git://git.kernel.org/pub/scm/linux/kernel/git/brodo/cpupowerutils:
  cpupower: use man(1) when calling "cpupower help subcommand"
  cpupower: make NLS truly optional
  cpupower: fix Makefile typo
  cpupower: Make monitor command -c/--cpu aware
  cpupower: Better detect offlined CPUs
  cpupower: Do not show an empty Idle_Stats monitor if no idle driver is available
  cpupower: mperf monitor - Use TSC to calculate max frequency if possible
  cpupower: avoid using symlinks
parents e5b1d9cc 498ca793
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
# Set the following to `true' to make a unstripped, unoptimized # Set the following to `true' to make a unstripped, unoptimized
# binary. Leave this set to `false' for production use. # binary. Leave this set to `false' for production use.
DEBUG ?= false DEBUG ?= true
# make the build silent. Set this to something else to make it noisy again. # make the build silent. Set this to something else to make it noisy again.
V ?= false V ?= false
...@@ -35,7 +35,7 @@ NLS ?= true ...@@ -35,7 +35,7 @@ NLS ?= true
# Set the following to 'true' to build/install the # Set the following to 'true' to build/install the
# cpufreq-bench benchmarking tool # cpufreq-bench benchmarking tool
CPUFRQ_BENCH ?= true CPUFREQ_BENCH ?= true
# Prefix to the directories we're installing to # Prefix to the directories we're installing to
DESTDIR ?= DESTDIR ?=
...@@ -137,9 +137,10 @@ CFLAGS += -pipe ...@@ -137,9 +137,10 @@ CFLAGS += -pipe
ifeq ($(strip $(NLS)),true) ifeq ($(strip $(NLS)),true)
INSTALL_NLS += install-gmo INSTALL_NLS += install-gmo
COMPILE_NLS += create-gmo COMPILE_NLS += create-gmo
CFLAGS += -DNLS
endif endif
ifeq ($(strip $(CPUFRQ_BENCH)),true) ifeq ($(strip $(CPUFREQ_BENCH)),true)
INSTALL_BENCH += install-bench INSTALL_BENCH += install-bench
COMPILE_BENCH += compile-bench COMPILE_BENCH += compile-bench
endif endif
......
default: all default: all
centrino-decode: centrino-decode.c centrino-decode: ../i386/centrino-decode.c
$(CC) $(CFLAGS) -o centrino-decode centrino-decode.c $(CC) $(CFLAGS) -o $@ $<
powernow-k8-decode: powernow-k8-decode.c powernow-k8-decode: ../i386/powernow-k8-decode.c
$(CC) $(CFLAGS) -o powernow-k8-decode powernow-k8-decode.c $(CC) $(CFLAGS) -o $@ $<
all: centrino-decode powernow-k8-decode all: centrino-decode powernow-k8-decode
......
../i386/centrino-decode.c
\ No newline at end of file
../i386/powernow-k8-decode.c
\ No newline at end of file
.TH "cpufreq-info" "1" "0.1" "Mattia Dongili" "" .TH "cpupower-frequency-info" "1" "0.1" "Mattia Dongili" ""
.SH "NAME" .SH "NAME"
.LP .LP
cpufreq\-info \- Utility to retrieve cpufreq kernel information cpupower frequency\-info \- Utility to retrieve cpufreq kernel information
.SH "SYNTAX" .SH "SYNTAX"
.LP .LP
cpufreq\-info [\fIoptions\fP] cpupower [ \-c cpulist ] frequency\-info [\fIoptions\fP]
.SH "DESCRIPTION" .SH "DESCRIPTION"
.LP .LP
A small tool which prints out cpufreq information helpful to developers and interested users. A small tool which prints out cpufreq information helpful to developers and interested users.
......
.TH "cpufreq-set" "1" "0.1" "Mattia Dongili" "" .TH "cpupower-freqency-set" "1" "0.1" "Mattia Dongili" ""
.SH "NAME" .SH "NAME"
.LP .LP
cpufreq\-set \- A small tool which allows to modify cpufreq settings. cpupower frequency\-set \- A small tool which allows to modify cpufreq settings.
.SH "SYNTAX" .SH "SYNTAX"
.LP .LP
cpufreq\-set [\fIoptions\fP] cpupower [ \-c cpu ] frequency\-set [\fIoptions\fP]
.SH "DESCRIPTION" .SH "DESCRIPTION"
.LP .LP
cpufreq\-set allows you to modify cpufreq settings without having to type e.g. "/sys/devices/system/cpu/cpu0/cpufreq/scaling_set_speed" all the time. cpupower frequency\-set allows you to modify cpufreq settings without having to type e.g. "/sys/devices/system/cpu/cpu0/cpufreq/scaling_set_speed" all the time.
.SH "OPTIONS" .SH "OPTIONS"
.LP .LP
.TP .TP
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
cpupower \- Shows and sets processor power related values cpupower \- Shows and sets processor power related values
.SH SYNOPSIS .SH SYNOPSIS
.ft B .ft B
.B cpupower [ \-c cpulist ] subcommand [ARGS] .B cpupower [ \-c cpulist ] <command> [ARGS]
.B cpupower \-v|\-\-version .B cpupower \-v|\-\-version
...@@ -13,24 +13,24 @@ cpupower \- Shows and sets processor power related values ...@@ -13,24 +13,24 @@ cpupower \- Shows and sets processor power related values
\fBcpupower \fP is a collection of tools to examine and tune power saving \fBcpupower \fP is a collection of tools to examine and tune power saving
related features of your processor. related features of your processor.
The manpages of the subcommands (cpupower\-<subcommand>(1)) provide detailed The manpages of the commands (cpupower\-<command>(1)) provide detailed
descriptions of supported features. Run \fBcpupower help\fP to get an overview descriptions of supported features. Run \fBcpupower help\fP to get an overview
of supported subcommands. of supported commands.
.SH Options .SH Options
.PP .PP
\-\-help, \-h \-\-help, \-h
.RS 4 .RS 4
Shows supported subcommands and general usage. Shows supported commands and general usage.
.RE .RE
.PP .PP
\-\-cpu cpulist, \-c cpulist \-\-cpu cpulist, \-c cpulist
.RS 4 .RS 4
Only show or set values for specific cores. Only show or set values for specific cores.
This option is not supported by all subcommands, details can be found in the This option is not supported by all commands, details can be found in the
manpages of the subcommands. manpages of the commands.
Some subcommands access all cores (typically the *\-set commands), some only Some commands access all cores (typically the *\-set commands), some only
the first core (typically the *\-info commands) by default. the first core (typically the *\-info commands) by default.
The syntax for <cpulist> is based on how the kernel exports CPU bitmasks via The syntax for <cpulist> is based on how the kernel exports CPU bitmasks via
......
...@@ -8,11 +8,4 @@ extern int cmd_freq_info(int argc, const char **argv); ...@@ -8,11 +8,4 @@ extern int cmd_freq_info(int argc, const char **argv);
extern int cmd_idle_info(int argc, const char **argv); extern int cmd_idle_info(int argc, const char **argv);
extern int cmd_monitor(int argc, const char **argv); extern int cmd_monitor(int argc, const char **argv);
extern void set_help(void);
extern void info_help(void);
extern void freq_set_help(void);
extern void freq_info_help(void);
extern void idle_info_help(void);
extern void monitor_help(void);
#endif #endif
...@@ -510,37 +510,6 @@ static int get_latency(unsigned int cpu, unsigned int human) ...@@ -510,37 +510,6 @@ static int get_latency(unsigned int cpu, unsigned int human)
return 0; return 0;
} }
void freq_info_help(void)
{
printf(_("Usage: cpupower freqinfo [options]\n"));
printf(_("Options:\n"));
printf(_(" -e, --debug Prints out debug information [default]\n"));
printf(_(" -f, --freq Get frequency the CPU currently runs at, according\n"
" to the cpufreq core *\n"));
printf(_(" -w, --hwfreq Get frequency the CPU currently runs at, by reading\n"
" it from hardware (only available to root) *\n"));
printf(_(" -l, --hwlimits Determine the minimum and maximum CPU frequency allowed *\n"));
printf(_(" -d, --driver Determines the used cpufreq kernel driver *\n"));
printf(_(" -p, --policy Gets the currently used cpufreq policy *\n"));
printf(_(" -g, --governors Determines available cpufreq governors *\n"));
printf(_(" -r, --related-cpus Determines which CPUs run at the same hardware frequency *\n"));
printf(_(" -a, --affected-cpus Determines which CPUs need to have their frequency\n"
" coordinated by software *\n"));
printf(_(" -s, --stats Shows cpufreq statistics if available\n"));
printf(_(" -y, --latency Determines the maximum latency on CPU frequency changes *\n"));
printf(_(" -b, --boost Checks for turbo or boost modes *\n"));
printf(_(" -o, --proc Prints out information like provided by the /proc/cpufreq\n"
" interface in 2.4. and early 2.6. kernels\n"));
printf(_(" -m, --human human-readable output for the -f, -w, -s and -y parameters\n"));
printf(_(" -h, --help Prints out this screen\n"));
printf("\n");
printf(_("If no argument is given, full output about\n"
"cpufreq is printed which is useful e.g. for reporting bugs.\n\n"));
printf(_("By default info of CPU 0 is shown which can be overridden\n"
"with the cpupower --cpu main command option.\n"));
}
static struct option info_opts[] = { static struct option info_opts[] = {
{ .name = "debug", .has_arg = no_argument, .flag = NULL, .val = 'e'}, { .name = "debug", .has_arg = no_argument, .flag = NULL, .val = 'e'},
{ .name = "boost", .has_arg = no_argument, .flag = NULL, .val = 'b'}, { .name = "boost", .has_arg = no_argument, .flag = NULL, .val = 'b'},
...@@ -556,7 +525,6 @@ static struct option info_opts[] = { ...@@ -556,7 +525,6 @@ static struct option info_opts[] = {
{ .name = "latency", .has_arg = no_argument, .flag = NULL, .val = 'y'}, { .name = "latency", .has_arg = no_argument, .flag = NULL, .val = 'y'},
{ .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'}, { .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'},
{ .name = "human", .has_arg = no_argument, .flag = NULL, .val = 'm'}, { .name = "human", .has_arg = no_argument, .flag = NULL, .val = 'm'},
{ .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'},
{ }, { },
}; };
...@@ -570,16 +538,12 @@ int cmd_freq_info(int argc, char **argv) ...@@ -570,16 +538,12 @@ int cmd_freq_info(int argc, char **argv)
int output_param = 0; int output_param = 0;
do { do {
ret = getopt_long(argc, argv, "hoefwldpgrasmyb", info_opts, NULL); ret = getopt_long(argc, argv, "oefwldpgrasmyb", info_opts, NULL);
switch (ret) { switch (ret) {
case '?': case '?':
output_param = '?'; output_param = '?';
cont = 0; cont = 0;
break; break;
case 'h':
output_param = 'h';
cont = 0;
break;
case -1: case -1:
cont = 0; cont = 0;
break; break;
...@@ -642,11 +606,7 @@ int cmd_freq_info(int argc, char **argv) ...@@ -642,11 +606,7 @@ int cmd_freq_info(int argc, char **argv)
return -EINVAL; return -EINVAL;
case '?': case '?':
printf(_("invalid or unknown argument\n")); printf(_("invalid or unknown argument\n"));
freq_info_help();
return -EINVAL; return -EINVAL;
case 'h':
freq_info_help();
return EXIT_SUCCESS;
case 'o': case 'o':
proc_cpufreq_output(); proc_cpufreq_output();
return EXIT_SUCCESS; return EXIT_SUCCESS;
......
...@@ -20,34 +20,11 @@ ...@@ -20,34 +20,11 @@
#define NORM_FREQ_LEN 32 #define NORM_FREQ_LEN 32
void freq_set_help(void)
{
printf(_("Usage: cpupower frequency-set [options]\n"));
printf(_("Options:\n"));
printf(_(" -d FREQ, --min FREQ new minimum CPU frequency the governor may select\n"));
printf(_(" -u FREQ, --max FREQ new maximum CPU frequency the governor may select\n"));
printf(_(" -g GOV, --governor GOV new cpufreq governor\n"));
printf(_(" -f FREQ, --freq FREQ specific frequency to be set. Requires userspace\n"
" governor to be available and loaded\n"));
printf(_(" -r, --related Switches all hardware-related CPUs\n"));
printf(_(" -h, --help Prints out this screen\n"));
printf("\n");
printf(_("Notes:\n"
"1. Omitting the -c or --cpu argument is equivalent to setting it to \"all\"\n"));
printf(_("2. The -f FREQ, --freq FREQ parameter cannot be combined with any other parameter\n"
" except the -c CPU, --cpu CPU parameter\n"
"3. FREQuencies can be passed in Hz, kHz (default), MHz, GHz, or THz\n"
" by postfixing the value with the wanted unit name, without any space\n"
" (FREQuency in kHz =^ Hz * 0.001 =^ MHz * 1000 =^ GHz * 1000000).\n"));
}
static struct option set_opts[] = { static struct option set_opts[] = {
{ .name = "min", .has_arg = required_argument, .flag = NULL, .val = 'd'}, { .name = "min", .has_arg = required_argument, .flag = NULL, .val = 'd'},
{ .name = "max", .has_arg = required_argument, .flag = NULL, .val = 'u'}, { .name = "max", .has_arg = required_argument, .flag = NULL, .val = 'u'},
{ .name = "governor", .has_arg = required_argument, .flag = NULL, .val = 'g'}, { .name = "governor", .has_arg = required_argument, .flag = NULL, .val = 'g'},
{ .name = "freq", .has_arg = required_argument, .flag = NULL, .val = 'f'}, { .name = "freq", .has_arg = required_argument, .flag = NULL, .val = 'f'},
{ .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'},
{ .name = "related", .has_arg = no_argument, .flag = NULL, .val='r'}, { .name = "related", .has_arg = no_argument, .flag = NULL, .val='r'},
{ }, { },
}; };
...@@ -80,7 +57,6 @@ const struct freq_units def_units[] = { ...@@ -80,7 +57,6 @@ const struct freq_units def_units[] = {
static void print_unknown_arg(void) static void print_unknown_arg(void)
{ {
printf(_("invalid or unknown argument\n")); printf(_("invalid or unknown argument\n"));
freq_set_help();
} }
static unsigned long string_to_frequency(const char *str) static unsigned long string_to_frequency(const char *str)
...@@ -231,14 +207,11 @@ int cmd_freq_set(int argc, char **argv) ...@@ -231,14 +207,11 @@ int cmd_freq_set(int argc, char **argv)
/* parameter parsing */ /* parameter parsing */
do { do {
ret = getopt_long(argc, argv, "d:u:g:f:hr", set_opts, NULL); ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);
switch (ret) { switch (ret) {
case '?': case '?':
print_unknown_arg(); print_unknown_arg();
return -EINVAL; return -EINVAL;
case 'h':
freq_set_help();
return 0;
case -1: case -1:
cont = 0; cont = 0;
break; break;
......
...@@ -139,30 +139,14 @@ static void proc_cpuidle_cpu_output(unsigned int cpu) ...@@ -139,30 +139,14 @@ static void proc_cpuidle_cpu_output(unsigned int cpu)
} }
} }
/* --freq / -f */
void idle_info_help(void)
{
printf(_ ("Usage: cpupower idleinfo [options]\n"));
printf(_ ("Options:\n"));
printf(_ (" -s, --silent Only show general C-state information\n"));
printf(_ (" -o, --proc Prints out information like provided by the /proc/acpi/processor/*/power\n"
" interface in older kernels\n"));
printf(_ (" -h, --help Prints out this screen\n"));
printf("\n");
}
static struct option info_opts[] = { static struct option info_opts[] = {
{ .name = "silent", .has_arg = no_argument, .flag = NULL, .val = 's'}, { .name = "silent", .has_arg = no_argument, .flag = NULL, .val = 's'},
{ .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'}, { .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'},
{ .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'},
{ }, { },
}; };
static inline void cpuidle_exit(int fail) static inline void cpuidle_exit(int fail)
{ {
idle_info_help();
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -174,7 +158,7 @@ int cmd_idle_info(int argc, char **argv) ...@@ -174,7 +158,7 @@ int cmd_idle_info(int argc, char **argv)
unsigned int cpu = 0; unsigned int cpu = 0;
do { do {
ret = getopt_long(argc, argv, "hos", info_opts, NULL); ret = getopt_long(argc, argv, "os", info_opts, NULL);
if (ret == -1) if (ret == -1)
break; break;
switch (ret) { switch (ret) {
...@@ -182,10 +166,6 @@ int cmd_idle_info(int argc, char **argv) ...@@ -182,10 +166,6 @@ int cmd_idle_info(int argc, char **argv)
output_param = '?'; output_param = '?';
cont = 0; cont = 0;
break; break;
case 'h':
output_param = 'h';
cont = 0;
break;
case 's': case 's':
verbose = 0; verbose = 0;
break; break;
...@@ -211,8 +191,6 @@ int cmd_idle_info(int argc, char **argv) ...@@ -211,8 +191,6 @@ int cmd_idle_info(int argc, char **argv)
case '?': case '?':
printf(_("invalid or unknown argument\n")); printf(_("invalid or unknown argument\n"));
cpuidle_exit(EXIT_FAILURE); cpuidle_exit(EXIT_FAILURE);
case 'h':
cpuidle_exit(EXIT_SUCCESS);
} }
/* Default is: show output of CPU 0 only */ /* Default is: show output of CPU 0 only */
......
...@@ -16,31 +16,16 @@ ...@@ -16,31 +16,16 @@
#include "helpers/helpers.h" #include "helpers/helpers.h"
#include "helpers/sysfs.h" #include "helpers/sysfs.h"
void info_help(void)
{
printf(_("Usage: cpupower info [ -b ] [ -m ] [ -s ]\n"));
printf(_("Options:\n"));
printf(_(" -b, --perf-bias Gets CPU's power vs performance policy on some\n"
" Intel models [0-15], see manpage for details\n"));
printf(_(" -m, --sched-mc Gets the kernel's multi core scheduler policy.\n"));
printf(_(" -s, --sched-smt Gets the kernel's thread sibling scheduler policy.\n"));
printf(_(" -h, --help Prints out this screen\n"));
printf(_("\nPassing no option will show all info, by default only on core 0\n"));
printf("\n");
}
static struct option set_opts[] = { static struct option set_opts[] = {
{ .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'}, { .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'},
{ .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'}, { .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'},
{ .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'}, { .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'},
{ .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'},
{ }, { },
}; };
static void print_wrong_arg_exit(void) static void print_wrong_arg_exit(void)
{ {
printf(_("invalid or unknown argument\n")); printf(_("invalid or unknown argument\n"));
info_help();
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -64,11 +49,8 @@ int cmd_info(int argc, char **argv) ...@@ -64,11 +49,8 @@ int cmd_info(int argc, char **argv)
textdomain(PACKAGE); textdomain(PACKAGE);
/* parameter parsing */ /* parameter parsing */
while ((ret = getopt_long(argc, argv, "msbh", set_opts, NULL)) != -1) { while ((ret = getopt_long(argc, argv, "msb", set_opts, NULL)) != -1) {
switch (ret) { switch (ret) {
case 'h':
info_help();
return 0;
case 'b': case 'b':
if (params.perf_bias) if (params.perf_bias)
print_wrong_arg_exit(); print_wrong_arg_exit();
......
...@@ -17,30 +17,16 @@ ...@@ -17,30 +17,16 @@
#include "helpers/sysfs.h" #include "helpers/sysfs.h"
#include "helpers/bitmask.h" #include "helpers/bitmask.h"
void set_help(void)
{
printf(_("Usage: cpupower set [ -b val ] [ -m val ] [ -s val ]\n"));
printf(_("Options:\n"));
printf(_(" -b, --perf-bias [VAL] Sets CPU's power vs performance policy on some\n"
" Intel models [0-15], see manpage for details\n"));
printf(_(" -m, --sched-mc [VAL] Sets the kernel's multi core scheduler policy.\n"));
printf(_(" -s, --sched-smt [VAL] Sets the kernel's thread sibling scheduler policy.\n"));
printf(_(" -h, --help Prints out this screen\n"));
printf("\n");
}
static struct option set_opts[] = { static struct option set_opts[] = {
{ .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'}, { .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'},
{ .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'}, { .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'},
{ .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'}, { .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'},
{ .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'},
{ }, { },
}; };
static void print_wrong_arg_exit(void) static void print_wrong_arg_exit(void)
{ {
printf(_("invalid or unknown argument\n")); printf(_("invalid or unknown argument\n"));
set_help();
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -66,12 +52,9 @@ int cmd_set(int argc, char **argv) ...@@ -66,12 +52,9 @@ int cmd_set(int argc, char **argv)
params.params = 0; params.params = 0;
/* parameter parsing */ /* parameter parsing */
while ((ret = getopt_long(argc, argv, "m:s:b:h", while ((ret = getopt_long(argc, argv, "m:s:b:",
set_opts, NULL)) != -1) { set_opts, NULL)) != -1) {
switch (ret) { switch (ret) {
case 'h':
set_help();
return 0;
case 'b': case 'b':
if (params.perf_bias) if (params.perf_bias)
print_wrong_arg_exit(); print_wrong_arg_exit();
...@@ -110,10 +93,8 @@ int cmd_set(int argc, char **argv) ...@@ -110,10 +93,8 @@ int cmd_set(int argc, char **argv)
} }
}; };
if (!params.params) { if (!params.params)
set_help(); print_wrong_arg_exit();
return -EINVAL;
}
if (params.sched_mc) { if (params.sched_mc) {
ret = sysfs_set_sched("mc", sched_mc); ret = sysfs_set_sched("mc", sched_mc);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include "builtin.h" #include "builtin.h"
#include "helpers/helpers.h" #include "helpers/helpers.h"
...@@ -19,13 +20,12 @@ ...@@ -19,13 +20,12 @@
struct cmd_struct { struct cmd_struct {
const char *cmd; const char *cmd;
int (*main)(int, const char **); int (*main)(int, const char **);
void (*usage)(void);
int needs_root; int needs_root;
}; };
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
int cmd_help(int argc, const char **argv); static int cmd_help(int argc, const char **argv);
/* Global cpu_info object available for all binaries /* Global cpu_info object available for all binaries
* Info only retrieved from CPU 0 * Info only retrieved from CPU 0
...@@ -44,55 +44,66 @@ int be_verbose; ...@@ -44,55 +44,66 @@ int be_verbose;
static void print_help(void); static void print_help(void);
static struct cmd_struct commands[] = { static struct cmd_struct commands[] = {
{ "frequency-info", cmd_freq_info, freq_info_help, 0 }, { "frequency-info", cmd_freq_info, 0 },
{ "frequency-set", cmd_freq_set, freq_set_help, 1 }, { "frequency-set", cmd_freq_set, 1 },
{ "idle-info", cmd_idle_info, idle_info_help, 0 }, { "idle-info", cmd_idle_info, 0 },
{ "set", cmd_set, set_help, 1 }, { "set", cmd_set, 1 },
{ "info", cmd_info, info_help, 0 }, { "info", cmd_info, 0 },
{ "monitor", cmd_monitor, monitor_help, 0 }, { "monitor", cmd_monitor, 0 },
{ "help", cmd_help, print_help, 0 }, { "help", cmd_help, 0 },
/* { "bench", cmd_bench, NULL, 1 }, */ /* { "bench", cmd_bench, 1 }, */
}; };
int cmd_help(int argc, const char **argv)
{
unsigned int i;
if (argc > 1) {
for (i = 0; i < ARRAY_SIZE(commands); i++) {
struct cmd_struct *p = commands + i;
if (strcmp(p->cmd, argv[1]))
continue;
if (p->usage) {
p->usage();
return EXIT_SUCCESS;
}
}
}
print_help();
if (argc == 1)
return EXIT_SUCCESS; /* cpupower help */
return EXIT_FAILURE;
}
static void print_help(void) static void print_help(void)
{ {
unsigned int i; unsigned int i;
#ifdef DEBUG #ifdef DEBUG
printf(_("cpupower [ -d ][ -c cpulist ] subcommand [ARGS]\n")); printf(_("Usage:\tcpupower [-d|--debug] [-c|--cpu cpulist ] <command> [<args>]\n"));
printf(_(" -d, --debug May increase output (stderr) on some subcommands\n"));
#else #else
printf(_("cpupower [ -c cpulist ] subcommand [ARGS]\n")); printf(_("Usage:\tcpupower [-c|--cpu cpulist ] <command> [<args>]\n"));
#endif #endif
printf(_("cpupower --version\n")); printf(_("Supported commands are:\n"));
printf(_("Supported subcommands are:\n"));
for (i = 0; i < ARRAY_SIZE(commands); i++) for (i = 0; i < ARRAY_SIZE(commands); i++)
printf("\t%s\n", commands[i].cmd); printf("\t%s\n", commands[i].cmd);
printf(_("\nSome subcommands can make use of the -c cpulist option.\n")); printf(_("\nNot all commands can make use of the -c cpulist option.\n"));
printf(_("Look at the general cpupower manpage how to use it\n")); printf(_("\nUse 'cpupower help <command>' for getting help for above commands.\n"));
printf(_("and read up the subcommand's manpage whether it is supported.\n")); }
printf(_("\nUse cpupower help subcommand for getting help for above subcommands.\n"));
static int print_man_page(const char *subpage)
{
int len;
char *page;
len = 10; /* enough for "cpupower-" */
if (subpage != NULL)
len += strlen(subpage);
page = malloc(len);
if (!page)
return -ENOMEM;
sprintf(page, "cpupower");
if ((subpage != NULL) && strcmp(subpage, "help")) {
strcat(page, "-");
strcat(page, subpage);
}
execlp("man", "man", page, NULL);
/* should not be reached */
return -EINVAL;
}
static int cmd_help(int argc, const char **argv)
{
if (argc > 1) {
print_man_page(argv[1]); /* exits within execlp() */
return EXIT_FAILURE;
}
print_help();
return EXIT_SUCCESS;
} }
static void print_version(void) static void print_version(void)
......
...@@ -16,11 +16,20 @@ ...@@ -16,11 +16,20 @@
#include "helpers/bitmask.h" #include "helpers/bitmask.h"
/* Internationalization ****************************/ /* Internationalization ****************************/
#ifdef NLS
#define _(String) gettext(String) #define _(String) gettext(String)
#ifndef gettext_noop #ifndef gettext_noop
#define gettext_noop(String) String #define gettext_noop(String) String
#endif #endif
#define N_(String) gettext_noop(String) #define N_(String) gettext_noop(String)
#else /* !NLS */
#define _(String) String
#define N_(String) String
#endif
/* Internationalization ****************************/ /* Internationalization ****************************/
extern int run_as_root; extern int run_as_root;
...@@ -96,6 +105,9 @@ struct cpupower_topology { ...@@ -96,6 +105,9 @@ struct cpupower_topology {
int pkg; int pkg;
int core; int core;
int cpu; int cpu;
/* flags */
unsigned int is_online:1;
} *core_info; } *core_info;
}; };
......
...@@ -56,6 +56,56 @@ static unsigned int sysfs_write_file(const char *path, ...@@ -56,6 +56,56 @@ static unsigned int sysfs_write_file(const char *path,
return (unsigned int) numwrite; return (unsigned int) numwrite;
} }
/*
* Detect whether a CPU is online
*
* Returns:
* 1 -> if CPU is online
* 0 -> if CPU is offline
* negative errno values in error case
*/
int sysfs_is_cpu_online(unsigned int cpu)
{
char path[SYSFS_PATH_MAX];
int fd;
ssize_t numread;
unsigned long long value;
char linebuf[MAX_LINE_LEN];
char *endp;
struct stat statbuf;
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
if (stat(path, &statbuf) != 0)
return 0;
/*
* kernel without CONFIG_HOTPLUG_CPU
* -> cpuX directory exists, but not cpuX/online file
*/
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
if (stat(path, &statbuf) != 0)
return 1;
fd = open(path, O_RDONLY);
if (fd == -1)
return -errno;
numread = read(fd, linebuf, MAX_LINE_LEN - 1);
if (numread < 1) {
close(fd);
return -EIO;
}
linebuf[numread] = '\0';
close(fd);
value = strtoull(linebuf, &endp, 0);
if (value > 1 || value < 0)
return -EINVAL;
return value;
}
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
/* /*
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
extern unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen); extern unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen);
extern int sysfs_is_cpu_online(unsigned int cpu);
extern unsigned long sysfs_get_idlestate_latency(unsigned int cpu, extern unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
unsigned int idlestate); unsigned int idlestate);
extern unsigned long sysfs_get_idlestate_usage(unsigned int cpu, extern unsigned long sysfs_get_idlestate_usage(unsigned int cpu,
......
...@@ -41,6 +41,8 @@ struct cpuid_core_info { ...@@ -41,6 +41,8 @@ struct cpuid_core_info {
unsigned int pkg; unsigned int pkg;
unsigned int thread; unsigned int thread;
unsigned int cpu; unsigned int cpu;
/* flags */
unsigned int is_online:1;
}; };
static int __compare(const void *t1, const void *t2) static int __compare(const void *t1, const void *t2)
...@@ -78,6 +80,8 @@ int get_cpu_topology(struct cpupower_topology *cpu_top) ...@@ -78,6 +80,8 @@ int get_cpu_topology(struct cpupower_topology *cpu_top)
return -ENOMEM; return -ENOMEM;
cpu_top->pkgs = cpu_top->cores = 0; cpu_top->pkgs = cpu_top->cores = 0;
for (cpu = 0; cpu < cpus; cpu++) { 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 = cpu_top->core_info[cpu].pkg =
sysfs_topology_read_file(cpu, "physical_package_id"); sysfs_topology_read_file(cpu, "physical_package_id");
if ((int)cpu_top->core_info[cpu].pkg != -1 && if ((int)cpu_top->core_info[cpu].pkg != -1 &&
...@@ -85,7 +89,6 @@ int get_cpu_topology(struct cpupower_topology *cpu_top) ...@@ -85,7 +89,6 @@ int get_cpu_topology(struct cpupower_topology *cpu_top)
cpu_top->pkgs = cpu_top->core_info[cpu].pkg; cpu_top->pkgs = cpu_top->core_info[cpu].pkg;
cpu_top->core_info[cpu].core = cpu_top->core_info[cpu].core =
sysfs_topology_read_file(cpu, "core_id"); sysfs_topology_read_file(cpu, "core_id");
cpu_top->core_info[cpu].cpu = cpu;
} }
cpu_top->pkgs++; cpu_top->pkgs++;
......
...@@ -134,7 +134,7 @@ static struct cpuidle_monitor *cpuidle_register(void) ...@@ -134,7 +134,7 @@ static struct cpuidle_monitor *cpuidle_register(void)
/* Assume idle state count is the same for all CPUs */ /* Assume idle state count is the same for all CPUs */
cpuidle_sysfs_monitor.hw_states_num = sysfs_get_idlestate_count(0); cpuidle_sysfs_monitor.hw_states_num = sysfs_get_idlestate_count(0);
if (cpuidle_sysfs_monitor.hw_states_num == 0) if (cpuidle_sysfs_monitor.hw_states_num <= 0)
return NULL; return NULL;
for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) { for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) {
......
...@@ -43,6 +43,12 @@ static struct cpupower_topology cpu_top; ...@@ -43,6 +43,12 @@ static struct cpupower_topology cpu_top;
/* ToDo: Document this in the manpage */ /* ToDo: Document this in the manpage */
static char range_abbr[RANGE_MAX] = { 'T', 'C', 'P', 'M', }; static char range_abbr[RANGE_MAX] = { 'T', 'C', 'P', 'M', };
static void print_wrong_arg_exit(void)
{
printf(_("invalid or unknown argument\n"));
exit(EXIT_FAILURE);
}
long long timespec_diff_us(struct timespec start, struct timespec end) long long timespec_diff_us(struct timespec start, struct timespec end)
{ {
struct timespec temp; struct timespec temp;
...@@ -56,21 +62,6 @@ long long timespec_diff_us(struct timespec start, struct timespec end) ...@@ -56,21 +62,6 @@ long long timespec_diff_us(struct timespec start, struct timespec end)
return (temp.tv_sec * 1000000) + (temp.tv_nsec / 1000); return (temp.tv_sec * 1000000) + (temp.tv_nsec / 1000);
} }
void monitor_help(void)
{
printf(_("cpupower monitor: [-m <mon1>,[<mon2>],.. ] command\n"));
printf(_("cpupower monitor: [-m <mon1>,[<mon2>],.. ] [ -i interval_sec ]\n"));
printf(_("cpupower monitor: -l\n"));
printf(_("\t command: pass an arbitrary command to measure specific workload\n"));
printf(_("\t -i: time intervall to measure for in seconds (default 1)\n"));
printf(_("\t -l: list available CPU sleep monitors (for use with -m)\n"));
printf(_("\t -m: show specific CPU sleep monitors only (in same order)\n"));
printf(_("\t -h: print this help\n"));
printf("\n");
printf(_("only one of: -l, -m are allowed\nIf none of them is passed,"));
printf(_(" all supported monitors are shown\n"));
}
void print_n_spaces(int n) void print_n_spaces(int n)
{ {
int x; int x;
...@@ -149,6 +140,10 @@ void print_results(int topology_depth, int cpu) ...@@ -149,6 +140,10 @@ void print_results(int topology_depth, int cpu)
unsigned long long result; unsigned long long result;
cstate_t s; cstate_t s;
/* Be careful CPUs may got resorted for pkg value do not just use cpu */
if (!bitmask_isbitset(cpus_chosen, cpu_top.core_info[cpu].cpu))
return;
if (topology_depth > 2) if (topology_depth > 2)
printf("%4d|", cpu_top.core_info[cpu].pkg); printf("%4d|", cpu_top.core_info[cpu].pkg);
if (topology_depth > 1) if (topology_depth > 1)
...@@ -190,9 +185,13 @@ void print_results(int topology_depth, int cpu) ...@@ -190,9 +185,13 @@ void print_results(int topology_depth, int cpu)
} }
} }
} }
/* cpu offline */ /*
if (cpu_top.core_info[cpu].pkg == -1 || * The monitor could still provide useful data, for example
cpu_top.core_info[cpu].core == -1) { * AMD HW counters partly sit in PCI config space.
* It's up to the monitor plug-in to check .is_online, this one
* is just for additional info.
*/
if (!cpu_top.core_info[cpu].is_online) {
printf(_(" *is offline\n")); printf(_(" *is offline\n"));
return; return;
} else } else
...@@ -238,7 +237,6 @@ static void parse_monitor_param(char *param) ...@@ -238,7 +237,6 @@ static void parse_monitor_param(char *param)
if (hits == 0) { if (hits == 0) {
printf(_("No matching monitor found in %s, " printf(_("No matching monitor found in %s, "
"try -l option\n"), param); "try -l option\n"), param);
monitor_help();
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* Override detected/registerd monitors array with requested one */ /* Override detected/registerd monitors array with requested one */
...@@ -335,37 +333,27 @@ static void cmdline(int argc, char *argv[]) ...@@ -335,37 +333,27 @@ static void cmdline(int argc, char *argv[])
int opt; int opt;
progname = basename(argv[0]); progname = basename(argv[0]);
while ((opt = getopt(argc, argv, "+hli:m:")) != -1) { while ((opt = getopt(argc, argv, "+li:m:")) != -1) {
switch (opt) { switch (opt) {
case 'h':
monitor_help();
exit(EXIT_SUCCESS);
case 'l': case 'l':
if (mode) { if (mode)
monitor_help(); print_wrong_arg_exit();
exit(EXIT_FAILURE);
}
mode = list; mode = list;
break; break;
case 'i': case 'i':
/* only allow -i with -m or no option */ /* only allow -i with -m or no option */
if (mode && mode != show) { if (mode && mode != show)
monitor_help(); print_wrong_arg_exit();
exit(EXIT_FAILURE);
}
interval = atoi(optarg); interval = atoi(optarg);
break; break;
case 'm': case 'm':
if (mode) { if (mode)
monitor_help(); print_wrong_arg_exit();
exit(EXIT_FAILURE);
}
mode = show; mode = show;
show_monitors_param = optarg; show_monitors_param = optarg;
break; break;
default: default:
monitor_help(); print_wrong_arg_exit();
exit(EXIT_FAILURE);
} }
} }
if (!mode) if (!mode)
...@@ -385,6 +373,10 @@ int cmd_monitor(int argc, char **argv) ...@@ -385,6 +373,10 @@ int cmd_monitor(int argc, char **argv)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
/* Default is: monitor all CPUs */
if (bitmask_isallclear(cpus_chosen))
bitmask_setall(cpus_chosen);
dprint("System has up to %d CPU cores\n", cpu_count); dprint("System has up to %d CPU cores\n", cpu_count);
for (num = 0; all_monitors[num]; num++) { for (num = 0; all_monitors[num]; num++) {
......
...@@ -22,12 +22,15 @@ ...@@ -22,12 +22,15 @@
#define MSR_TSC 0x10 #define MSR_TSC 0x10
#define MSR_AMD_HWCR 0xc0010015
enum mperf_id { C0 = 0, Cx, AVG_FREQ, MPERF_CSTATE_COUNT }; enum mperf_id { C0 = 0, Cx, AVG_FREQ, MPERF_CSTATE_COUNT };
static int mperf_get_count_percent(unsigned int self_id, double *percent, static int mperf_get_count_percent(unsigned int self_id, double *percent,
unsigned int cpu); unsigned int cpu);
static int mperf_get_count_freq(unsigned int id, unsigned long long *count, static int mperf_get_count_freq(unsigned int id, unsigned long long *count,
unsigned int cpu); unsigned int cpu);
static struct timespec time_start, time_end;
static cstate_t mperf_cstates[MPERF_CSTATE_COUNT] = { static cstate_t mperf_cstates[MPERF_CSTATE_COUNT] = {
{ {
...@@ -54,19 +57,33 @@ static cstate_t mperf_cstates[MPERF_CSTATE_COUNT] = { ...@@ -54,19 +57,33 @@ static cstate_t mperf_cstates[MPERF_CSTATE_COUNT] = {
}, },
}; };
enum MAX_FREQ_MODE { MAX_FREQ_SYSFS, MAX_FREQ_TSC_REF };
static int max_freq_mode;
/*
* The max frequency mperf is ticking at (in C0), either retrieved via:
* 1) calculated after measurements if we know TSC ticks at mperf/P0 frequency
* 2) cpufreq /sys/devices/.../cpu0/cpufreq/cpuinfo_max_freq at init time
* 1. Is preferred as it also works without cpufreq subsystem (e.g. on Xen)
*/
static unsigned long max_frequency;
static unsigned long long tsc_at_measure_start; static unsigned long long tsc_at_measure_start;
static unsigned long long tsc_at_measure_end; static unsigned long long tsc_at_measure_end;
static unsigned long max_frequency;
static unsigned long long *mperf_previous_count; static unsigned long long *mperf_previous_count;
static unsigned long long *aperf_previous_count; static unsigned long long *aperf_previous_count;
static unsigned long long *mperf_current_count; static unsigned long long *mperf_current_count;
static unsigned long long *aperf_current_count; static unsigned long long *aperf_current_count;
/* valid flag for all CPUs. If a MSR read failed it will be zero */ /* valid flag for all CPUs. If a MSR read failed it will be zero */
static int *is_valid; static int *is_valid;
static int mperf_get_tsc(unsigned long long *tsc) static int mperf_get_tsc(unsigned long long *tsc)
{ {
return read_msr(0, MSR_TSC, tsc); int ret;
ret = read_msr(0, MSR_TSC, tsc);
if (ret)
dprint("Reading TSC MSR failed, returning %llu\n", *tsc);
return ret;
} }
static int mperf_init_stats(unsigned int cpu) static int mperf_init_stats(unsigned int cpu)
...@@ -97,36 +114,11 @@ static int mperf_measure_stats(unsigned int cpu) ...@@ -97,36 +114,11 @@ static int mperf_measure_stats(unsigned int cpu)
return 0; return 0;
} }
/*
* get_average_perf()
*
* Returns the average performance (also considers boosted frequencies)
*
* Input:
* aperf_diff: Difference of the aperf register over a time period
* mperf_diff: Difference of the mperf register over the same time period
* max_freq: Maximum frequency (P0)
*
* Returns:
* Average performance over the time period
*/
static unsigned long get_average_perf(unsigned long long aperf_diff,
unsigned long long mperf_diff)
{
unsigned int perf_percent = 0;
if (((unsigned long)(-1) / 100) < aperf_diff) {
int shift_count = 7;
aperf_diff >>= shift_count;
mperf_diff >>= shift_count;
}
perf_percent = (aperf_diff * 100) / mperf_diff;
return (max_frequency * perf_percent) / 100;
}
static int mperf_get_count_percent(unsigned int id, double *percent, static int mperf_get_count_percent(unsigned int id, double *percent,
unsigned int cpu) unsigned int cpu)
{ {
unsigned long long aperf_diff, mperf_diff, tsc_diff; unsigned long long aperf_diff, mperf_diff, tsc_diff;
unsigned long long timediff;
if (!is_valid[cpu]) if (!is_valid[cpu])
return -1; return -1;
...@@ -136,11 +128,19 @@ static int mperf_get_count_percent(unsigned int id, double *percent, ...@@ -136,11 +128,19 @@ static int mperf_get_count_percent(unsigned int id, double *percent,
mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu]; mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu];
aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu]; aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu];
tsc_diff = tsc_at_measure_end - tsc_at_measure_start;
if (max_freq_mode == MAX_FREQ_TSC_REF) {
tsc_diff = tsc_at_measure_end - tsc_at_measure_start;
*percent = 100.0 * mperf_diff / tsc_diff; *percent = 100.0 * mperf_diff / tsc_diff;
dprint("%s: mperf_diff: %llu, tsc_diff: %llu\n", dprint("%s: TSC Ref - mperf_diff: %llu, tsc_diff: %llu\n",
mperf_cstates[id].name, mperf_diff, tsc_diff); mperf_cstates[id].name, mperf_diff, tsc_diff);
} else if (max_freq_mode == MAX_FREQ_SYSFS) {
timediff = timespec_diff_us(time_start, time_end);
*percent = 100.0 * mperf_diff / timediff;
dprint("%s: MAXFREQ - mperf_diff: %llu, time_diff: %llu\n",
mperf_cstates[id].name, mperf_diff, timediff);
} else
return -1;
if (id == Cx) if (id == Cx)
*percent = 100.0 - *percent; *percent = 100.0 - *percent;
...@@ -154,7 +154,7 @@ static int mperf_get_count_percent(unsigned int id, double *percent, ...@@ -154,7 +154,7 @@ static int mperf_get_count_percent(unsigned int id, double *percent,
static int mperf_get_count_freq(unsigned int id, unsigned long long *count, static int mperf_get_count_freq(unsigned int id, unsigned long long *count,
unsigned int cpu) unsigned int cpu)
{ {
unsigned long long aperf_diff, mperf_diff; unsigned long long aperf_diff, mperf_diff, time_diff, tsc_diff;
if (id != AVG_FREQ) if (id != AVG_FREQ)
return 1; return 1;
...@@ -165,11 +165,21 @@ static int mperf_get_count_freq(unsigned int id, unsigned long long *count, ...@@ -165,11 +165,21 @@ static int mperf_get_count_freq(unsigned int id, unsigned long long *count,
mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu]; mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu];
aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu]; aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu];
/* Return MHz for now, might want to return KHz if column width is more if (max_freq_mode == MAX_FREQ_TSC_REF) {
generic */ /* Calculate max_freq from TSC count */
*count = get_average_perf(aperf_diff, mperf_diff) / 1000; tsc_diff = tsc_at_measure_end - tsc_at_measure_start;
dprint("%s: %llu\n", mperf_cstates[id].name, *count); time_diff = timespec_diff_us(time_start, time_end);
max_frequency = tsc_diff / time_diff;
}
*count = max_frequency * ((double)aperf_diff / mperf_diff);
dprint("%s: Average freq based on %s maximum frequency:\n",
mperf_cstates[id].name,
(max_freq_mode == MAX_FREQ_TSC_REF) ? "TSC calculated" : "sysfs read");
dprint("%max_frequency: %lu", max_frequency);
dprint("aperf_diff: %llu\n", aperf_diff);
dprint("mperf_diff: %llu\n", mperf_diff);
dprint("avg freq: %llu\n", *count);
return 0; return 0;
} }
...@@ -178,6 +188,7 @@ static int mperf_start(void) ...@@ -178,6 +188,7 @@ static int mperf_start(void)
int cpu; int cpu;
unsigned long long dbg; unsigned long long dbg;
clock_gettime(CLOCK_REALTIME, &time_start);
mperf_get_tsc(&tsc_at_measure_start); mperf_get_tsc(&tsc_at_measure_start);
for (cpu = 0; cpu < cpu_count; cpu++) for (cpu = 0; cpu < cpu_count; cpu++)
...@@ -193,32 +204,104 @@ static int mperf_stop(void) ...@@ -193,32 +204,104 @@ static int mperf_stop(void)
unsigned long long dbg; unsigned long long dbg;
int cpu; int cpu;
mperf_get_tsc(&tsc_at_measure_end);
for (cpu = 0; cpu < cpu_count; cpu++) for (cpu = 0; cpu < cpu_count; cpu++)
mperf_measure_stats(cpu); mperf_measure_stats(cpu);
mperf_get_tsc(&tsc_at_measure_end);
clock_gettime(CLOCK_REALTIME, &time_end);
mperf_get_tsc(&dbg); mperf_get_tsc(&dbg);
dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end); dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end);
return 0; return 0;
} }
struct cpuidle_monitor mperf_monitor; /*
* Mperf register is defined to tick at P0 (maximum) frequency
struct cpuidle_monitor *mperf_register(void) *
* Instead of reading out P0 which can be tricky to read out from HW,
* we use TSC counter if it reliably ticks at P0/mperf frequency.
*
* Still try to fall back to:
* /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
* on older Intel HW without invariant TSC feature.
* Or on AMD machines where TSC does not tick at P0 (do not exist yet, but
* it's still double checked (MSR_AMD_HWCR)).
*
* On these machines the user would still get useful mperf
* stats when acpi-cpufreq driver is loaded.
*/
static int init_maxfreq_mode(void)
{ {
int ret;
unsigned long long hwcr;
unsigned long min; unsigned long min;
if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) if (!cpupower_cpu_info.caps & CPUPOWER_CAP_INV_TSC)
return NULL; goto use_sysfs;
/* Assume min/max all the same on all cores */ if (cpupower_cpu_info.vendor == X86_VENDOR_AMD) {
/* MSR_AMD_HWCR tells us whether TSC runs at P0/mperf
* freq.
* A test whether hwcr is accessable/available would be:
* (cpupower_cpu_info.family > 0x10 ||
* cpupower_cpu_info.family == 0x10 &&
* cpupower_cpu_info.model >= 0x2))
* This should be the case for all aperf/mperf
* capable AMD machines and is therefore safe to test here.
* Compare with Linus kernel git commit: acf01734b1747b1ec4
*/
ret = read_msr(0, MSR_AMD_HWCR, &hwcr);
/*
* If the MSR read failed, assume a Xen system that did
* not explicitly provide access to it and assume TSC works
*/
if (ret != 0) {
dprint("TSC read 0x%x failed - assume TSC working\n",
MSR_AMD_HWCR);
return 0;
} else if (1 & (hwcr >> 24)) {
max_freq_mode = MAX_FREQ_TSC_REF;
return 0;
} else { /* Use sysfs max frequency if available */ }
} else if (cpupower_cpu_info.vendor == X86_VENDOR_INTEL) {
/*
* On Intel we assume mperf (in C0) is ticking at same
* rate than TSC
*/
max_freq_mode = MAX_FREQ_TSC_REF;
return 0;
}
use_sysfs:
if (cpufreq_get_hardware_limits(0, &min, &max_frequency)) { if (cpufreq_get_hardware_limits(0, &min, &max_frequency)) {
dprint("Cannot retrieve max freq from cpufreq kernel " dprint("Cannot retrieve max freq from cpufreq kernel "
"subsystem\n"); "subsystem\n");
return NULL; return -1;
} }
max_freq_mode = MAX_FREQ_SYSFS;
return 0;
}
/*
* This monitor provides:
*
* 1) Average frequency a CPU resided in
* This always works if the CPU has aperf/mperf capabilities
*
* 2) C0 and Cx (any sleep state) time a CPU resided in
* Works if mperf timer stops ticking in sleep states which
* seem to be the case on all current HW.
* Both is directly retrieved from HW registers and is independent
* from kernel statistics.
*/
struct cpuidle_monitor mperf_monitor;
struct cpuidle_monitor *mperf_register(void)
{
if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF))
return NULL;
if (init_maxfreq_mode())
return NULL;
/* Free this at program termination */ /* Free this at program termination */
is_valid = calloc(cpu_count, sizeof(int)); is_valid = calloc(cpu_count, sizeof(int));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment