2 * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
4 * Licensed under the terms of the GNU GPL License version 2.
7 #if defined(__i386__) || defined(__x86_64__)
17 #include "helpers/helpers.h"
18 #include "idle_monitor/cpupower-monitor.h"
20 #define MSR_APERF 0xE8
21 #define MSR_MPERF 0xE7
25 enum mperf_id { C0 = 0, Cx, AVG_FREQ, MPERF_CSTATE_COUNT };
27 static int mperf_get_count_percent(unsigned int self_id, double *percent,
29 static int mperf_get_count_freq(unsigned int id, unsigned long long *count,
32 static cstate_t mperf_cstates[MPERF_CSTATE_COUNT] = {
35 .desc = N_("Processor Core not idle"),
37 .range = RANGE_THREAD,
38 .get_count_percent = mperf_get_count_percent,
42 .desc = N_("Processor Core in an idle state"),
44 .range = RANGE_THREAD,
45 .get_count_percent = mperf_get_count_percent,
50 .desc = N_("Average Frequency (including boost) in MHz"),
52 .range = RANGE_THREAD,
53 .get_count = mperf_get_count_freq,
57 static unsigned long long tsc_at_measure_start;
58 static unsigned long long tsc_at_measure_end;
59 static unsigned long max_frequency;
60 static unsigned long long *mperf_previous_count;
61 static unsigned long long *aperf_previous_count;
62 static unsigned long long *mperf_current_count;
63 static unsigned long long *aperf_current_count;
64 /* valid flag for all CPUs. If a MSR read failed it will be zero */
67 static int mperf_get_tsc(unsigned long long *tsc)
69 return read_msr(0, MSR_TSC, tsc);
72 static int mperf_init_stats(unsigned int cpu)
74 unsigned long long val;
77 ret = read_msr(cpu, MSR_APERF, &val);
78 aperf_previous_count[cpu] = val;
79 ret |= read_msr(cpu, MSR_MPERF, &val);
80 mperf_previous_count[cpu] = val;
86 static int mperf_measure_stats(unsigned int cpu)
88 unsigned long long val;
91 ret = read_msr(cpu, MSR_APERF, &val);
92 aperf_current_count[cpu] = val;
93 ret |= read_msr(cpu, MSR_MPERF, &val);
94 mperf_current_count[cpu] = val;
103 * Returns the average performance (also considers boosted frequencies)
106 * aperf_diff: Difference of the aperf register over a time period
107 * mperf_diff: Difference of the mperf register over the same time period
108 * max_freq: Maximum frequency (P0)
111 * Average performance over the time period
113 static unsigned long get_average_perf(unsigned long long aperf_diff,
114 unsigned long long mperf_diff)
116 unsigned int perf_percent = 0;
117 if (((unsigned long)(-1) / 100) < aperf_diff) {
119 aperf_diff >>= shift_count;
120 mperf_diff >>= shift_count;
122 perf_percent = (aperf_diff * 100) / mperf_diff;
123 return (max_frequency * perf_percent) / 100;
126 static int mperf_get_count_percent(unsigned int id, double *percent,
129 unsigned long long aperf_diff, mperf_diff, tsc_diff;
134 if (id != C0 && id != Cx)
137 mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu];
138 aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu];
139 tsc_diff = tsc_at_measure_end - tsc_at_measure_start;
141 *percent = 100.0 * mperf_diff / tsc_diff;
142 dprint("%s: mperf_diff: %llu, tsc_diff: %llu\n",
143 mperf_cstates[id].name, mperf_diff, tsc_diff);
146 *percent = 100.0 - *percent;
148 dprint("%s: previous: %llu - current: %llu - (%u)\n", mperf_cstates[id].name,
149 mperf_diff, aperf_diff, cpu);
150 dprint("%s: %f\n", mperf_cstates[id].name, *percent);
154 static int mperf_get_count_freq(unsigned int id, unsigned long long *count,
157 unsigned long long aperf_diff, mperf_diff;
165 mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu];
166 aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu];
168 /* Return MHz for now, might want to return KHz if column width is more
170 *count = get_average_perf(aperf_diff, mperf_diff) / 1000;
171 dprint("%s: %llu\n", mperf_cstates[id].name, *count);
176 static int mperf_start(void)
179 unsigned long long dbg;
181 mperf_get_tsc(&tsc_at_measure_start);
183 for (cpu = 0; cpu < cpu_count; cpu++)
184 mperf_init_stats(cpu);
187 dprint("TSC diff: %llu\n", dbg - tsc_at_measure_start);
191 static int mperf_stop(void)
193 unsigned long long dbg;
196 mperf_get_tsc(&tsc_at_measure_end);
198 for (cpu = 0; cpu < cpu_count; cpu++)
199 mperf_measure_stats(cpu);
202 dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end);
207 struct cpuidle_monitor mperf_monitor;
209 struct cpuidle_monitor* mperf_register(void) {
213 if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF))
216 /* Assume min/max all the same on all cores */
217 if (cpufreq_get_hardware_limits(0, &min, &max_frequency)) {
218 dprint("Cannot retrieve max freq from cpufreq kernel "
223 /* Free this at program termination */
224 is_valid = calloc(cpu_count, sizeof (int));
225 mperf_previous_count = calloc (cpu_count,
226 sizeof(unsigned long long));
227 aperf_previous_count = calloc (cpu_count,
228 sizeof(unsigned long long));
229 mperf_current_count = calloc (cpu_count,
230 sizeof(unsigned long long));
231 aperf_current_count = calloc (cpu_count,
232 sizeof(unsigned long long));
234 mperf_monitor.name_len = strlen(mperf_monitor.name);
235 return &mperf_monitor;
238 void mperf_unregister(void) {
239 free(mperf_previous_count);
240 free(aperf_previous_count);
241 free(mperf_current_count);
242 free(aperf_current_count);
246 struct cpuidle_monitor mperf_monitor = {
248 .hw_states_num = MPERF_CSTATE_COUNT,
249 .hw_states = mperf_cstates,
250 .start = mperf_start,
252 .do_register = mperf_register,
253 .unregister = mperf_unregister,
255 .overflow_s = 922000000 /* 922337203 seconds TSC overflow
258 #endif /* #if defined(__i386__) || defined(__x86_64__) */