aboutsummaryrefslogtreecommitdiff
path: root/tools/power/cpupower/utils/idle_monitor
diff options
context:
space:
mode:
Diffstat (limited to 'tools/power/cpupower/utils/idle_monitor')
-rw-r--r--tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c25
-rw-r--r--tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c2
-rw-r--r--tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c85
-rw-r--r--tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h17
-rw-r--r--tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c196
-rw-r--r--tools/power/cpupower/utils/idle_monitor/idle_monitors.def1
-rw-r--r--tools/power/cpupower/utils/idle_monitor/mperf_monitor.c177
-rw-r--r--tools/power/cpupower/utils/idle_monitor/snb_idle.c14
8 files changed, 414 insertions, 103 deletions
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/cpuidle_sysfs.c b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
index d048b96a615..bcd22a1a397 100644
--- a/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
+++ b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
@@ -134,7 +134,7 @@ static struct cpuidle_monitor *cpuidle_register(void)
/* Assume idle state count is the same for all CPUs */
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;
for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) {
diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c
index ba4bf068380..c4bae9203a6 100644
--- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c
+++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c
@@ -39,10 +39,17 @@ 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', };
+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)
{
struct timespec temp;
@@ -56,21 +63,6 @@ long long timespec_diff_us(struct timespec start, struct timespec end)
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)
{
int x;
@@ -93,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;
@@ -102,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) {
@@ -149,6 +140,10 @@ void print_results(int topology_depth, int cpu)
unsigned long long result;
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)
printf("%4d|", cpu_top.core_info[cpu].pkg);
if (topology_depth > 1)
@@ -190,9 +185,13 @@ void print_results(int topology_depth, int cpu)
}
}
}
- /* cpu offline */
- if (cpu_top.core_info[cpu].pkg == -1 ||
- cpu_top.core_info[cpu].core == -1) {
+ /*
+ * The monitor could still provide useful data, for example
+ * 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"));
return;
} else
@@ -238,7 +237,6 @@ static void parse_monitor_param(char *param)
if (hits == 0) {
printf(_("No matching monitor found in %s, "
"try -l option\n"), param);
- monitor_help();
exit(EXIT_FAILURE);
}
/* Override detected/registerd monitors array with requested one */
@@ -317,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;
}
@@ -335,37 +345,30 @@ static void cmdline(int argc, char *argv[])
int opt;
progname = basename(argv[0]);
- while ((opt = getopt(argc, argv, "+hli:m:")) != -1) {
+ while ((opt = getopt(argc, argv, "+lci:m:")) != -1) {
switch (opt) {
- case 'h':
- monitor_help();
- exit(EXIT_SUCCESS);
case 'l':
- if (mode) {
- monitor_help();
- exit(EXIT_FAILURE);
- }
+ if (mode)
+ print_wrong_arg_exit();
mode = list;
break;
case 'i':
/* only allow -i with -m or no option */
- if (mode && mode != show) {
- monitor_help();
- exit(EXIT_FAILURE);
- }
+ if (mode && mode != show)
+ print_wrong_arg_exit();
interval = atoi(optarg);
break;
case 'm':
- if (mode) {
- monitor_help();
- exit(EXIT_FAILURE);
- }
+ if (mode)
+ print_wrong_arg_exit();
mode = show;
show_monitors_param = optarg;
break;
+ case 'c':
+ wake_cpus = 1;
+ break;
default:
- monitor_help();
- exit(EXIT_FAILURE);
+ print_wrong_arg_exit();
}
}
if (!mode)
@@ -385,6 +388,10 @@ int cmd_monitor(int argc, char **argv)
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);
for (num = 0; all_monitors[num]; num++) {
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/mperf_monitor.c b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
index 63ca87a05e5..5650ab5a2c2 100644
--- a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
+++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
@@ -22,12 +22,15 @@
#define MSR_TSC 0x10
+#define MSR_AMD_HWCR 0xc0010015
+
enum mperf_id { C0 = 0, Cx, AVG_FREQ, MPERF_CSTATE_COUNT };
static int mperf_get_count_percent(unsigned int self_id, double *percent,
unsigned int cpu);
static int mperf_get_count_freq(unsigned int id, unsigned long long *count,
unsigned int cpu);
+static struct timespec time_start, time_end;
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_end;
-static unsigned long max_frequency;
static unsigned long long *mperf_previous_count;
static unsigned long long *aperf_previous_count;
static unsigned long long *mperf_current_count;
static unsigned long long *aperf_current_count;
+
/* valid flag for all CPUs. If a MSR read failed it will be zero */
static int *is_valid;
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)
@@ -97,36 +114,11 @@ static int mperf_measure_stats(unsigned int cpu)
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,
unsigned int cpu)
{
unsigned long long aperf_diff, mperf_diff, tsc_diff;
+ unsigned long long timediff;
if (!is_valid[cpu])
return -1;
@@ -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];
aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu];
- tsc_diff = tsc_at_measure_end - tsc_at_measure_start;
- *percent = 100.0 * mperf_diff / tsc_diff;
- dprint("%s: mperf_diff: %llu, tsc_diff: %llu\n",
- mperf_cstates[id].name, mperf_diff, tsc_diff);
+ 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;
+ dprint("%s: TSC Ref - mperf_diff: %llu, tsc_diff: %llu\n",
+ 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)
*percent = 100.0 - *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,
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)
return 1;
@@ -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];
aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu];
- /* Return MHz for now, might want to return KHz if column width is more
- generic */
- *count = get_average_perf(aperf_diff, mperf_diff) / 1000;
- dprint("%s: %llu\n", mperf_cstates[id].name, *count);
+ if (max_freq_mode == MAX_FREQ_TSC_REF) {
+ /* Calculate max_freq from TSC count */
+ tsc_diff = tsc_at_measure_end - tsc_at_measure_start;
+ 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;
}
@@ -178,6 +188,7 @@ static int mperf_start(void)
int cpu;
unsigned long long dbg;
+ clock_gettime(CLOCK_REALTIME, &time_start);
mperf_get_tsc(&tsc_at_measure_start);
for (cpu = 0; cpu < cpu_count; cpu++)
@@ -193,32 +204,104 @@ static int mperf_stop(void)
unsigned long long dbg;
int cpu;
- mperf_get_tsc(&tsc_at_measure_end);
-
for (cpu = 0; cpu < cpu_count; cpu++)
mperf_measure_stats(cpu);
+ mperf_get_tsc(&tsc_at_measure_end);
+ clock_gettime(CLOCK_REALTIME, &time_end);
+
mperf_get_tsc(&dbg);
dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end);
return 0;
}
-struct cpuidle_monitor mperf_monitor;
-
-struct cpuidle_monitor *mperf_register(void)
+/*
+ * Mperf register is defined to tick at P0 (maximum) frequency
+ *
+ * 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;
- if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF))
- return NULL;
-
- /* Assume min/max all the same on all cores */
+ if (!cpupower_cpu_info.caps & CPUPOWER_CAP_INV_TSC)
+ goto use_sysfs;
+
+ 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)) {
dprint("Cannot retrieve max freq from cpufreq kernel "
"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 */
is_valid = calloc(cpu_count, sizeof(int));
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++) {