diff options
Diffstat (limited to 'tools/power/cpupower/utils')
28 files changed, 5124 insertions, 0 deletions
diff --git a/tools/power/cpupower/utils/builtin.h b/tools/power/cpupower/utils/builtin.h new file mode 100644 index 00000000000..c10496fbe3c --- /dev/null +++ b/tools/power/cpupower/utils/builtin.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #ifndef BUILTIN_H | ||
2 | #define BUILTIN_H | ||
3 | |||
4 | extern int cmd_set(int argc, const char **argv); | ||
5 | extern int cmd_info(int argc, const char **argv); | ||
6 | extern int cmd_freq_set(int argc, const char **argv); | ||
7 | extern int cmd_freq_info(int argc, const char **argv); | ||
8 | extern int cmd_idle_info(int argc, const char **argv); | ||
9 | extern int cmd_monitor(int argc, const char **argv); | ||
10 | |||
11 | #endif | ||
diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c new file mode 100644 index 00000000000..28953c9a7bd --- /dev/null +++ b/tools/power/cpupower/utils/cpufreq-info.c | |||
@@ -0,0 +1,668 @@ | |||
1 | /* | ||
2 | * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | */ | ||
6 | |||
7 | |||
8 | #include <unistd.h> | ||
9 | #include <stdio.h> | ||
10 | #include <errno.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <string.h> | ||
13 | |||
14 | #include <getopt.h> | ||
15 | |||
16 | #include "cpufreq.h" | ||
17 | #include "helpers/helpers.h" | ||
18 | #include "helpers/bitmask.h" | ||
19 | |||
20 | #define LINE_LEN 10 | ||
21 | |||
22 | static unsigned int count_cpus(void) | ||
23 | { | ||
24 | FILE *fp; | ||
25 | char value[LINE_LEN]; | ||
26 | unsigned int ret = 0; | ||
27 | unsigned int cpunr = 0; | ||
28 | |||
29 | fp = fopen("/proc/stat", "r"); | ||
30 | if (!fp) { | ||
31 | printf(_("Couldn't count the number of CPUs (%s: %s), assuming 1\n"), "/proc/stat", strerror(errno)); | ||
32 | return 1; | ||
33 | } | ||
34 | |||
35 | while (!feof(fp)) { | ||
36 | if (!fgets(value, LINE_LEN, fp)) | ||
37 | continue; | ||
38 | value[LINE_LEN - 1] = '\0'; | ||
39 | if (strlen(value) < (LINE_LEN - 2)) | ||
40 | continue; | ||
41 | if (strstr(value, "cpu ")) | ||
42 | continue; | ||
43 | if (sscanf(value, "cpu%d ", &cpunr) != 1) | ||
44 | continue; | ||
45 | if (cpunr > ret) | ||
46 | ret = cpunr; | ||
47 | } | ||
48 | fclose(fp); | ||
49 | |||
50 | /* cpu count starts from 0, on error return 1 (UP) */ | ||
51 | return ret + 1; | ||
52 | } | ||
53 | |||
54 | |||
55 | static void proc_cpufreq_output(void) | ||
56 | { | ||
57 | unsigned int cpu, nr_cpus; | ||
58 | struct cpufreq_policy *policy; | ||
59 | unsigned int min_pctg = 0; | ||
60 | unsigned int max_pctg = 0; | ||
61 | unsigned long min, max; | ||
62 | |||
63 | printf(_(" minimum CPU frequency - maximum CPU frequency - governor\n")); | ||
64 | |||
65 | nr_cpus = count_cpus(); | ||
66 | for (cpu = 0; cpu < nr_cpus; cpu++) { | ||
67 | policy = cpufreq_get_policy(cpu); | ||
68 | if (!policy) | ||
69 | continue; | ||
70 | |||
71 | if (cpufreq_get_hardware_limits(cpu, &min, &max)) { | ||
72 | max = 0; | ||
73 | } else { | ||
74 | min_pctg = (policy->min * 100) / max; | ||
75 | max_pctg = (policy->max * 100) / max; | ||
76 | } | ||
77 | printf("CPU%3d %9lu kHz (%3d %%) - %9lu kHz (%3d %%) - %s\n", | ||
78 | cpu , policy->min, max ? min_pctg : 0, policy->max, | ||
79 | max ? max_pctg : 0, policy->governor); | ||
80 | |||
81 | cpufreq_put_policy(policy); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | static void print_speed(unsigned long speed) | ||
86 | { | ||
87 | unsigned long tmp; | ||
88 | |||
89 | if (speed > 1000000) { | ||
90 | tmp = speed % 10000; | ||
91 | if (tmp >= 5000) | ||
92 | speed += 10000; | ||
93 | printf("%u.%02u GHz", ((unsigned int) speed/1000000), | ||
94 | ((unsigned int) (speed%1000000)/10000)); | ||
95 | } else if (speed > 100000) { | ||
96 | tmp = speed % 1000; | ||
97 | if (tmp >= 500) | ||
98 | speed += 1000; | ||
99 | printf("%u MHz", ((unsigned int) speed / 1000)); | ||
100 | } else if (speed > 1000) { | ||
101 | tmp = speed % 100; | ||
102 | if (tmp >= 50) | ||
103 | speed += 100; | ||
104 | printf("%u.%01u MHz", ((unsigned int) speed/1000), | ||
105 | ((unsigned int) (speed%1000)/100)); | ||
106 | } else | ||
107 | printf("%lu kHz", speed); | ||
108 | |||
109 | return; | ||
110 | } | ||
111 | |||
112 | static void print_duration(unsigned long duration) | ||
113 | { | ||
114 | unsigned long tmp; | ||
115 | |||
116 | if (duration > 1000000) { | ||
117 | tmp = duration % 10000; | ||
118 | if (tmp >= 5000) | ||
119 | duration += 10000; | ||
120 | printf("%u.%02u ms", ((unsigned int) duration/1000000), | ||
121 | ((unsigned int) (duration%1000000)/10000)); | ||
122 | } else if (duration > 100000) { | ||
123 | tmp = duration % 1000; | ||
124 | if (tmp >= 500) | ||
125 | duration += 1000; | ||
126 | printf("%u us", ((unsigned int) duration / 1000)); | ||
127 | } else if (duration > 1000) { | ||
128 | tmp = duration % 100; | ||
129 | if (tmp >= 50) | ||
130 | duration += 100; | ||
131 | printf("%u.%01u us", ((unsigned int) duration/1000), | ||
132 | ((unsigned int) (duration%1000)/100)); | ||
133 | } else | ||
134 | printf("%lu ns", duration); | ||
135 | |||
136 | return; | ||
137 | } | ||
138 | |||
139 | /* --boost / -b */ | ||
140 | |||
141 | static int get_boost_mode(unsigned int cpu) | ||
142 | { | ||
143 | int support, active, b_states = 0, ret, pstate_no, i; | ||
144 | /* ToDo: Make this more global */ | ||
145 | unsigned long pstates[MAX_HW_PSTATES] = {0,}; | ||
146 | |||
147 | if (cpupower_cpu_info.vendor != X86_VENDOR_AMD && | ||
148 | cpupower_cpu_info.vendor != X86_VENDOR_INTEL) | ||
149 | return 0; | ||
150 | |||
151 | ret = cpufreq_has_boost_support(cpu, &support, &active, &b_states); | ||
152 | if (ret) { | ||
153 | printf(_("Error while evaluating Boost Capabilities" | ||
154 | " on CPU %d -- are you root?\n"), cpu); | ||
155 | return ret; | ||
156 | } | ||
157 | /* P state changes via MSR are identified via cpuid 80000007 | ||
158 | on Intel and AMD, but we assume boost capable machines can do that | ||
159 | if (cpuid_eax(0x80000000) >= 0x80000007 | ||
160 | && (cpuid_edx(0x80000007) & (1 << 7))) | ||
161 | */ | ||
162 | |||
163 | printf(_(" boost state support:\n")); | ||
164 | |||
165 | printf(_(" Supported: %s\n"), support ? _("yes") : _("no")); | ||
166 | printf(_(" Active: %s\n"), active ? _("yes") : _("no")); | ||
167 | |||
168 | if (cpupower_cpu_info.vendor == X86_VENDOR_AMD && | ||
169 | cpupower_cpu_info.family >= 0x10) { | ||
170 | ret = decode_pstates(cpu, cpupower_cpu_info.family, b_states, | ||
171 | pstates, &pstate_no); | ||
172 | if (ret) | ||
173 | return ret; | ||
174 | |||
175 | printf(_(" Boost States: %d\n"), b_states); | ||
176 | printf(_(" Total States: %d\n"), pstate_no); | ||
177 | for (i = 0; i < pstate_no; i++) { | ||
178 | if (i < b_states) | ||
179 | printf(_(" Pstate-Pb%d: %luMHz (boost state)" | ||
180 | "\n"), i, pstates[i]); | ||
181 | else | ||
182 | printf(_(" Pstate-P%d: %luMHz\n"), | ||
183 | i - b_states, pstates[i]); | ||
184 | } | ||
185 | } else if (cpupower_cpu_info.caps & CPUPOWER_CAP_HAS_TURBO_RATIO) { | ||
186 | double bclk; | ||
187 | unsigned long long intel_turbo_ratio = 0; | ||
188 | unsigned int ratio; | ||
189 | |||
190 | /* Any way to autodetect this ? */ | ||
191 | if (cpupower_cpu_info.caps & CPUPOWER_CAP_IS_SNB) | ||
192 | bclk = 100.00; | ||
193 | else | ||
194 | bclk = 133.33; | ||
195 | intel_turbo_ratio = msr_intel_get_turbo_ratio(cpu); | ||
196 | dprint (" Ratio: 0x%llx - bclk: %f\n", | ||
197 | intel_turbo_ratio, bclk); | ||
198 | |||
199 | ratio = (intel_turbo_ratio >> 24) & 0xFF; | ||
200 | if (ratio) | ||
201 | printf(_(" %.0f MHz max turbo 4 active cores\n"), | ||
202 | ratio * bclk); | ||
203 | |||
204 | ratio = (intel_turbo_ratio >> 16) & 0xFF; | ||
205 | if (ratio) | ||
206 | printf(_(" %.0f MHz max turbo 3 active cores\n"), | ||
207 | ratio * bclk); | ||
208 | |||
209 | ratio = (intel_turbo_ratio >> 8) & 0xFF; | ||
210 | if (ratio) | ||
211 | printf(_(" %.0f MHz max turbo 2 active cores\n"), | ||
212 | ratio * bclk); | ||
213 | |||
214 | ratio = (intel_turbo_ratio >> 0) & 0xFF; | ||
215 | if (ratio) | ||
216 | printf(_(" %.0f MHz max turbo 1 active cores\n"), | ||
217 | ratio * bclk); | ||
218 | } | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static void debug_output_one(unsigned int cpu) | ||
223 | { | ||
224 | char *driver; | ||
225 | struct cpufreq_affected_cpus *cpus; | ||
226 | struct cpufreq_available_frequencies *freqs; | ||
227 | unsigned long min, max, freq_kernel, freq_hardware; | ||
228 | unsigned long total_trans, latency; | ||
229 | unsigned long long total_time; | ||
230 | struct cpufreq_policy *policy; | ||
231 | struct cpufreq_available_governors *governors; | ||
232 | struct cpufreq_stats *stats; | ||
233 | |||
234 | if (cpufreq_cpu_exists(cpu)) | ||
235 | return; | ||
236 | |||
237 | freq_kernel = cpufreq_get_freq_kernel(cpu); | ||
238 | freq_hardware = cpufreq_get_freq_hardware(cpu); | ||
239 | |||
240 | driver = cpufreq_get_driver(cpu); | ||
241 | if (!driver) { | ||
242 | printf(_(" no or unknown cpufreq driver is active on this CPU\n")); | ||
243 | } else { | ||
244 | printf(_(" driver: %s\n"), driver); | ||
245 | cpufreq_put_driver(driver); | ||
246 | } | ||
247 | |||
248 | cpus = cpufreq_get_related_cpus(cpu); | ||
249 | if (cpus) { | ||
250 | printf(_(" CPUs which run at the same hardware frequency: ")); | ||
251 | while (cpus->next) { | ||
252 | printf("%d ", cpus->cpu); | ||
253 | cpus = cpus->next; | ||
254 | } | ||
255 | printf("%d\n", cpus->cpu); | ||
256 | cpufreq_put_related_cpus(cpus); | ||
257 | } | ||
258 | |||
259 | cpus = cpufreq_get_affected_cpus(cpu); | ||
260 | if (cpus) { | ||
261 | printf(_(" CPUs which need to have their frequency coordinated by software: ")); | ||
262 | while (cpus->next) { | ||
263 | printf("%d ", cpus->cpu); | ||
264 | cpus = cpus->next; | ||
265 | } | ||
266 | printf("%d\n", cpus->cpu); | ||
267 | cpufreq_put_affected_cpus(cpus); | ||
268 | } | ||
269 | |||
270 | latency = cpufreq_get_transition_latency(cpu); | ||
271 | if (latency) { | ||
272 | printf(_(" maximum transition latency: ")); | ||
273 | print_duration(latency); | ||
274 | printf(".\n"); | ||
275 | } | ||
276 | |||
277 | if (!(cpufreq_get_hardware_limits(cpu, &min, &max))) { | ||
278 | printf(_(" hardware limits: ")); | ||
279 | print_speed(min); | ||
280 | printf(" - "); | ||
281 | print_speed(max); | ||
282 | printf("\n"); | ||
283 | } | ||
284 | |||
285 | freqs = cpufreq_get_available_frequencies(cpu); | ||
286 | if (freqs) { | ||
287 | printf(_(" available frequency steps: ")); | ||
288 | while (freqs->next) { | ||
289 | print_speed(freqs->frequency); | ||
290 | printf(", "); | ||
291 | freqs = freqs->next; | ||
292 | } | ||
293 | print_speed(freqs->frequency); | ||
294 | printf("\n"); | ||
295 | cpufreq_put_available_frequencies(freqs); | ||
296 | } | ||
297 | |||
298 | governors = cpufreq_get_available_governors(cpu); | ||
299 | if (governors) { | ||
300 | printf(_(" available cpufreq governors: ")); | ||
301 | while (governors->next) { | ||
302 | printf("%s, ", governors->governor); | ||
303 | governors = governors->next; | ||
304 | } | ||
305 | printf("%s\n", governors->governor); | ||
306 | cpufreq_put_available_governors(governors); | ||
307 | } | ||
308 | |||
309 | policy = cpufreq_get_policy(cpu); | ||
310 | if (policy) { | ||
311 | printf(_(" current policy: frequency should be within ")); | ||
312 | print_speed(policy->min); | ||
313 | printf(_(" and ")); | ||
314 | print_speed(policy->max); | ||
315 | |||
316 | printf(".\n "); | ||
317 | printf(_("The governor \"%s\" may" | ||
318 | " decide which speed to use\n within this range.\n"), | ||
319 | policy->governor); | ||
320 | cpufreq_put_policy(policy); | ||
321 | } | ||
322 | |||
323 | if (freq_kernel || freq_hardware) { | ||
324 | printf(_(" current CPU frequency is ")); | ||
325 | if (freq_hardware) { | ||
326 | print_speed(freq_hardware); | ||
327 | printf(_(" (asserted by call to hardware)")); | ||
328 | } else | ||
329 | print_speed(freq_kernel); | ||
330 | printf(".\n"); | ||
331 | } | ||
332 | stats = cpufreq_get_stats(cpu, &total_time); | ||
333 | if (stats) { | ||
334 | printf(_(" cpufreq stats: ")); | ||
335 | while (stats) { | ||
336 | print_speed(stats->frequency); | ||
337 | printf(":%.2f%%", (100.0 * stats->time_in_state) / total_time); | ||
338 | stats = stats->next; | ||
339 | if (stats) | ||
340 | printf(", "); | ||
341 | } | ||
342 | cpufreq_put_stats(stats); | ||
343 | total_trans = cpufreq_get_transitions(cpu); | ||
344 | if (total_trans) | ||
345 | printf(" (%lu)\n", total_trans); | ||
346 | else | ||
347 | printf("\n"); | ||
348 | } | ||
349 | get_boost_mode(cpu); | ||
350 | |||
351 | } | ||
352 | |||
353 | /* --freq / -f */ | ||
354 | |||
355 | static int get_freq_kernel(unsigned int cpu, unsigned int human) | ||
356 | { | ||
357 | unsigned long freq = cpufreq_get_freq_kernel(cpu); | ||
358 | if (!freq) | ||
359 | return -EINVAL; | ||
360 | if (human) { | ||
361 | print_speed(freq); | ||
362 | printf("\n"); | ||
363 | } else | ||
364 | printf("%lu\n", freq); | ||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | |||
369 | /* --hwfreq / -w */ | ||
370 | |||
371 | static int get_freq_hardware(unsigned int cpu, unsigned int human) | ||
372 | { | ||
373 | unsigned long freq = cpufreq_get_freq_hardware(cpu); | ||
374 | if (!freq) | ||
375 | return -EINVAL; | ||
376 | if (human) { | ||
377 | print_speed(freq); | ||
378 | printf("\n"); | ||
379 | } else | ||
380 | printf("%lu\n", freq); | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | /* --hwlimits / -l */ | ||
385 | |||
386 | static int get_hardware_limits(unsigned int cpu) | ||
387 | { | ||
388 | unsigned long min, max; | ||
389 | if (cpufreq_get_hardware_limits(cpu, &min, &max)) | ||
390 | return -EINVAL; | ||
391 | printf("%lu %lu\n", min, max); | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | /* --driver / -d */ | ||
396 | |||
397 | static int get_driver(unsigned int cpu) | ||
398 | { | ||
399 | char *driver = cpufreq_get_driver(cpu); | ||
400 | if (!driver) | ||
401 | return -EINVAL; | ||
402 | printf("%s\n", driver); | ||
403 | cpufreq_put_driver(driver); | ||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | /* --policy / -p */ | ||
408 | |||
409 | static int get_policy(unsigned int cpu) | ||
410 | { | ||
411 | struct cpufreq_policy *policy = cpufreq_get_policy(cpu); | ||
412 | if (!policy) | ||
413 | return -EINVAL; | ||
414 | printf("%lu %lu %s\n", policy->min, policy->max, policy->governor); | ||
415 | cpufreq_put_policy(policy); | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | /* --governors / -g */ | ||
420 | |||
421 | static int get_available_governors(unsigned int cpu) | ||
422 | { | ||
423 | struct cpufreq_available_governors *governors = | ||
424 | cpufreq_get_available_governors(cpu); | ||
425 | if (!governors) | ||
426 | return -EINVAL; | ||
427 | |||
428 | while (governors->next) { | ||
429 | printf("%s ", governors->governor); | ||
430 | governors = governors->next; | ||
431 | } | ||
432 | printf("%s\n", governors->governor); | ||
433 | cpufreq_put_available_governors(governors); | ||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | |||
438 | /* --affected-cpus / -a */ | ||
439 | |||
440 | static int get_affected_cpus(unsigned int cpu) | ||
441 | { | ||
442 | struct cpufreq_affected_cpus *cpus = cpufreq_get_affected_cpus(cpu); | ||
443 | if (!cpus) | ||
444 | return -EINVAL; | ||
445 | |||
446 | while (cpus->next) { | ||
447 | printf("%d ", cpus->cpu); | ||
448 | cpus = cpus->next; | ||
449 | } | ||
450 | printf("%d\n", cpus->cpu); | ||
451 | cpufreq_put_affected_cpus(cpus); | ||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | /* --related-cpus / -r */ | ||
456 | |||
457 | static int get_related_cpus(unsigned int cpu) | ||
458 | { | ||
459 | struct cpufreq_affected_cpus *cpus = cpufreq_get_related_cpus(cpu); | ||
460 | if (!cpus) | ||
461 | return -EINVAL; | ||
462 | |||
463 | while (cpus->next) { | ||
464 | printf("%d ", cpus->cpu); | ||
465 | cpus = cpus->next; | ||
466 | } | ||
467 | printf("%d\n", cpus->cpu); | ||
468 | cpufreq_put_related_cpus(cpus); | ||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | /* --stats / -s */ | ||
473 | |||
474 | static int get_freq_stats(unsigned int cpu, unsigned int human) | ||
475 | { | ||
476 | unsigned long total_trans = cpufreq_get_transitions(cpu); | ||
477 | unsigned long long total_time; | ||
478 | struct cpufreq_stats *stats = cpufreq_get_stats(cpu, &total_time); | ||
479 | while (stats) { | ||
480 | if (human) { | ||
481 | print_speed(stats->frequency); | ||
482 | printf(":%.2f%%", | ||
483 | (100.0 * stats->time_in_state) / total_time); | ||
484 | } else | ||
485 | printf("%lu:%llu", | ||
486 | stats->frequency, stats->time_in_state); | ||
487 | stats = stats->next; | ||
488 | if (stats) | ||
489 | printf(", "); | ||
490 | } | ||
491 | cpufreq_put_stats(stats); | ||
492 | if (total_trans) | ||
493 | printf(" (%lu)\n", total_trans); | ||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | /* --latency / -y */ | ||
498 | |||
499 | static int get_latency(unsigned int cpu, unsigned int human) | ||
500 | { | ||
501 | unsigned long latency = cpufreq_get_transition_latency(cpu); | ||
502 | if (!latency) | ||
503 | return -EINVAL; | ||
504 | |||
505 | if (human) { | ||
506 | print_duration(latency); | ||
507 | printf("\n"); | ||
508 | } else | ||
509 | printf("%lu\n", latency); | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static struct option info_opts[] = { | ||
514 | { .name = "debug", .has_arg = no_argument, .flag = NULL, .val = 'e'}, | ||
515 | { .name = "boost", .has_arg = no_argument, .flag = NULL, .val = 'b'}, | ||
516 | { .name = "freq", .has_arg = no_argument, .flag = NULL, .val = 'f'}, | ||
517 | { .name = "hwfreq", .has_arg = no_argument, .flag = NULL, .val = 'w'}, | ||
518 | { .name = "hwlimits", .has_arg = no_argument, .flag = NULL, .val = 'l'}, | ||
519 | { .name = "driver", .has_arg = no_argument, .flag = NULL, .val = 'd'}, | ||
520 | { .name = "policy", .has_arg = no_argument, .flag = NULL, .val = 'p'}, | ||
521 | { .name = "governors", .has_arg = no_argument, .flag = NULL, .val = 'g'}, | ||
522 | { .name = "related-cpus", .has_arg = no_argument, .flag = NULL, .val = 'r'}, | ||
523 | { .name = "affected-cpus",.has_arg = no_argument, .flag = NULL, .val = 'a'}, | ||
524 | { .name = "stats", .has_arg = no_argument, .flag = NULL, .val = 's'}, | ||
525 | { .name = "latency", .has_arg = no_argument, .flag = NULL, .val = 'y'}, | ||
526 | { .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'}, | ||
527 | { .name = "human", .has_arg = no_argument, .flag = NULL, .val = 'm'}, | ||
528 | { }, | ||
529 | }; | ||
530 | |||
531 | int cmd_freq_info(int argc, char **argv) | ||
532 | { | ||
533 | extern char *optarg; | ||
534 | extern int optind, opterr, optopt; | ||
535 | int ret = 0, cont = 1; | ||
536 | unsigned int cpu = 0; | ||
537 | unsigned int human = 0; | ||
538 | int output_param = 0; | ||
539 | |||
540 | do { | ||
541 | ret = getopt_long(argc, argv, "oefwldpgrasmyb", info_opts, NULL); | ||
542 | switch (ret) { | ||
543 | case '?': | ||
544 | output_param = '?'; | ||
545 | cont = 0; | ||
546 | break; | ||
547 | case -1: | ||
548 | cont = 0; | ||
549 | break; | ||
550 | case 'b': | ||
551 | case 'o': | ||
552 | case 'a': | ||
553 | case 'r': | ||
554 | case 'g': | ||
555 | case 'p': | ||
556 | case 'd': | ||
557 | case 'l': | ||
558 | case 'w': | ||
559 | case 'f': | ||
560 | case 'e': | ||
561 | case 's': | ||
562 | case 'y': | ||
563 | if (output_param) { | ||
564 | output_param = -1; | ||
565 | cont = 0; | ||
566 | break; | ||
567 | } | ||
568 | output_param = ret; | ||
569 | break; | ||
570 | case 'm': | ||
571 | if (human) { | ||
572 | output_param = -1; | ||
573 | cont = 0; | ||
574 | break; | ||
575 | } | ||
576 | human = 1; | ||
577 | break; | ||
578 | default: | ||
579 | fprintf(stderr, "invalid or unknown argument\n"); | ||
580 | return EXIT_FAILURE; | ||
581 | } | ||
582 | } while (cont); | ||
583 | |||
584 | switch (output_param) { | ||
585 | case 'o': | ||
586 | if (!bitmask_isallclear(cpus_chosen)) { | ||
587 | printf(_("The argument passed to this tool can't be " | ||
588 | "combined with passing a --cpu argument\n")); | ||
589 | return -EINVAL; | ||
590 | } | ||
591 | break; | ||
592 | case 0: | ||
593 | output_param = 'e'; | ||
594 | } | ||
595 | |||
596 | ret = 0; | ||
597 | |||
598 | /* Default is: show output of CPU 0 only */ | ||
599 | if (bitmask_isallclear(cpus_chosen)) | ||
600 | bitmask_setbit(cpus_chosen, 0); | ||
601 | |||
602 | switch (output_param) { | ||
603 | case -1: | ||
604 | printf(_("You can't specify more than one --cpu parameter and/or\n" | ||
605 | "more than one output-specific argument\n")); | ||
606 | return -EINVAL; | ||
607 | case '?': | ||
608 | printf(_("invalid or unknown argument\n")); | ||
609 | return -EINVAL; | ||
610 | case 'o': | ||
611 | proc_cpufreq_output(); | ||
612 | return EXIT_SUCCESS; | ||
613 | } | ||
614 | |||
615 | for (cpu = bitmask_first(cpus_chosen); | ||
616 | cpu <= bitmask_last(cpus_chosen); cpu++) { | ||
617 | |||
618 | if (!bitmask_isbitset(cpus_chosen, cpu)) | ||
619 | continue; | ||
620 | if (cpufreq_cpu_exists(cpu)) { | ||
621 | printf(_("couldn't analyze CPU %d as it doesn't seem to be present\n"), cpu); | ||
622 | continue; | ||
623 | } | ||
624 | printf(_("analyzing CPU %d:\n"), cpu); | ||
625 | |||
626 | switch (output_param) { | ||
627 | case 'b': | ||
628 | get_boost_mode(cpu); | ||
629 | break; | ||
630 | case 'e': | ||
631 | debug_output_one(cpu); | ||
632 | break; | ||
633 | case 'a': | ||
634 | ret = get_affected_cpus(cpu); | ||
635 | break; | ||
636 | case 'r': | ||
637 | ret = get_related_cpus(cpu); | ||
638 | break; | ||
639 | case 'g': | ||
640 | ret = get_available_governors(cpu); | ||
641 | break; | ||
642 | case 'p': | ||
643 | ret = get_policy(cpu); | ||
644 | break; | ||
645 | case 'd': | ||
646 | ret = get_driver(cpu); | ||
647 | break; | ||
648 | case 'l': | ||
649 | ret = get_hardware_limits(cpu); | ||
650 | break; | ||
651 | case 'w': | ||
652 | ret = get_freq_hardware(cpu, human); | ||
653 | break; | ||
654 | case 'f': | ||
655 | ret = get_freq_kernel(cpu, human); | ||
656 | break; | ||
657 | case 's': | ||
658 | ret = get_freq_stats(cpu, human); | ||
659 | break; | ||
660 | case 'y': | ||
661 | ret = get_latency(cpu, human); | ||
662 | break; | ||
663 | } | ||
664 | if (ret) | ||
665 | return ret; | ||
666 | } | ||
667 | return ret; | ||
668 | } | ||
diff --git a/tools/power/cpupower/utils/cpufreq-set.c b/tools/power/cpupower/utils/cpufreq-set.c new file mode 100644 index 00000000000..dd1539eb8c6 --- /dev/null +++ b/tools/power/cpupower/utils/cpufreq-set.c | |||
@@ -0,0 +1,331 @@ | |||
1 | /* | ||
2 | * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | */ | ||
6 | |||
7 | |||
8 | #include <unistd.h> | ||
9 | #include <stdio.h> | ||
10 | #include <errno.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <limits.h> | ||
13 | #include <string.h> | ||
14 | #include <ctype.h> | ||
15 | |||
16 | #include <getopt.h> | ||
17 | |||
18 | #include "cpufreq.h" | ||
19 | #include "helpers/helpers.h" | ||
20 | |||
21 | #define NORM_FREQ_LEN 32 | ||
22 | |||
23 | static struct option set_opts[] = { | ||
24 | { .name = "min", .has_arg = required_argument, .flag = NULL, .val = 'd'}, | ||
25 | { .name = "max", .has_arg = required_argument, .flag = NULL, .val = 'u'}, | ||
26 | { .name = "governor", .has_arg = required_argument, .flag = NULL, .val = 'g'}, | ||
27 | { .name = "freq", .has_arg = required_argument, .flag = NULL, .val = 'f'}, | ||
28 | { .name = "related", .has_arg = no_argument, .flag = NULL, .val='r'}, | ||
29 | { }, | ||
30 | }; | ||
31 | |||
32 | static void print_error(void) | ||
33 | { | ||
34 | printf(_("Error setting new values. Common errors:\n" | ||
35 | "- Do you have proper administration rights? (super-user?)\n" | ||
36 | "- Is the governor you requested available and modprobed?\n" | ||
37 | "- Trying to set an invalid policy?\n" | ||
38 | "- Trying to set a specific frequency, but userspace governor is not available,\n" | ||
39 | " for example because of hardware which cannot be set to a specific frequency\n" | ||
40 | " or because the userspace governor isn't loaded?\n")); | ||
41 | }; | ||
42 | |||
43 | struct freq_units { | ||
44 | char *str_unit; | ||
45 | int power_of_ten; | ||
46 | }; | ||
47 | |||
48 | const struct freq_units def_units[] = { | ||
49 | {"hz", -3}, | ||
50 | {"khz", 0}, /* default */ | ||
51 | {"mhz", 3}, | ||
52 | {"ghz", 6}, | ||
53 | {"thz", 9}, | ||
54 | {NULL, 0} | ||
55 | }; | ||
56 | |||
57 | static void print_unknown_arg(void) | ||
58 | { | ||
59 | printf(_("invalid or unknown argument\n")); | ||
60 | } | ||
61 | |||
62 | static unsigned long string_to_frequency(const char *str) | ||
63 | { | ||
64 | char normalized[NORM_FREQ_LEN]; | ||
65 | const struct freq_units *unit; | ||
66 | const char *scan; | ||
67 | char *end; | ||
68 | unsigned long freq; | ||
69 | int power = 0, match_count = 0, i, cp, pad; | ||
70 | |||
71 | while (*str == '0') | ||
72 | str++; | ||
73 | |||
74 | for (scan = str; isdigit(*scan) || *scan == '.'; scan++) { | ||
75 | if (*scan == '.' && match_count == 0) | ||
76 | match_count = 1; | ||
77 | else if (*scan == '.' && match_count == 1) | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | if (*scan) { | ||
82 | match_count = 0; | ||
83 | for (unit = def_units; unit->str_unit; unit++) { | ||
84 | for (i = 0; | ||
85 | scan[i] && tolower(scan[i]) == unit->str_unit[i]; | ||
86 | ++i) | ||
87 | continue; | ||
88 | if (scan[i]) | ||
89 | continue; | ||
90 | match_count++; | ||
91 | power = unit->power_of_ten; | ||
92 | } | ||
93 | if (match_count != 1) | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | /* count the number of digits to be copied */ | ||
98 | for (cp = 0; isdigit(str[cp]); cp++) | ||
99 | continue; | ||
100 | |||
101 | if (str[cp] == '.') { | ||
102 | while (power > -1 && isdigit(str[cp+1])) | ||
103 | cp++, power--; | ||
104 | } | ||
105 | if (power >= -1) /* not enough => pad */ | ||
106 | pad = power + 1; | ||
107 | else /* to much => strip */ | ||
108 | pad = 0, cp += power + 1; | ||
109 | /* check bounds */ | ||
110 | if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1) | ||
111 | return 0; | ||
112 | |||
113 | /* copy digits */ | ||
114 | for (i = 0; i < cp; i++, str++) { | ||
115 | if (*str == '.') | ||
116 | str++; | ||
117 | normalized[i] = *str; | ||
118 | } | ||
119 | /* and pad */ | ||
120 | for (; i < cp + pad; i++) | ||
121 | normalized[i] = '0'; | ||
122 | |||
123 | /* round up, down ? */ | ||
124 | match_count = (normalized[i-1] >= '5'); | ||
125 | /* and drop the decimal part */ | ||
126 | normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */ | ||
127 | |||
128 | /* final conversion (and applying rounding) */ | ||
129 | errno = 0; | ||
130 | freq = strtoul(normalized, &end, 10); | ||
131 | if (errno) | ||
132 | return 0; | ||
133 | else { | ||
134 | if (match_count && freq != ULONG_MAX) | ||
135 | freq++; | ||
136 | return freq; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol) | ||
141 | { | ||
142 | struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu); | ||
143 | int ret; | ||
144 | |||
145 | if (!cur_pol) { | ||
146 | printf(_("wrong, unknown or unhandled CPU?\n")); | ||
147 | return -EINVAL; | ||
148 | } | ||
149 | |||
150 | if (!new_pol->min) | ||
151 | new_pol->min = cur_pol->min; | ||
152 | |||
153 | if (!new_pol->max) | ||
154 | new_pol->max = cur_pol->max; | ||
155 | |||
156 | if (!new_pol->governor) | ||
157 | new_pol->governor = cur_pol->governor; | ||
158 | |||
159 | ret = cpufreq_set_policy(cpu, new_pol); | ||
160 | |||
161 | cpufreq_put_policy(cur_pol); | ||
162 | |||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | |||
167 | static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol, | ||
168 | unsigned long freq, unsigned int pc) | ||
169 | { | ||
170 | switch (pc) { | ||
171 | case 0: | ||
172 | return cpufreq_set_frequency(cpu, freq); | ||
173 | |||
174 | case 1: | ||
175 | /* if only one value of a policy is to be changed, we can | ||
176 | * use a "fast path". | ||
177 | */ | ||
178 | if (new_pol->min) | ||
179 | return cpufreq_modify_policy_min(cpu, new_pol->min); | ||
180 | else if (new_pol->max) | ||
181 | return cpufreq_modify_policy_max(cpu, new_pol->max); | ||
182 | else if (new_pol->governor) | ||
183 | return cpufreq_modify_policy_governor(cpu, | ||
184 | new_pol->governor); | ||
185 | |||
186 | default: | ||
187 | /* slow path */ | ||
188 | return do_new_policy(cpu, new_pol); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | int cmd_freq_set(int argc, char **argv) | ||
193 | { | ||
194 | extern char *optarg; | ||
195 | extern int optind, opterr, optopt; | ||
196 | int ret = 0, cont = 1; | ||
197 | int double_parm = 0, related = 0, policychange = 0; | ||
198 | unsigned long freq = 0; | ||
199 | char gov[20]; | ||
200 | unsigned int cpu; | ||
201 | |||
202 | struct cpufreq_policy new_pol = { | ||
203 | .min = 0, | ||
204 | .max = 0, | ||
205 | .governor = NULL, | ||
206 | }; | ||
207 | |||
208 | /* parameter parsing */ | ||
209 | do { | ||
210 | ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL); | ||
211 | switch (ret) { | ||
212 | case '?': | ||
213 | print_unknown_arg(); | ||
214 | return -EINVAL; | ||
215 | case -1: | ||
216 | cont = 0; | ||
217 | break; | ||
218 | case 'r': | ||
219 | if (related) | ||
220 | double_parm++; | ||
221 | related++; | ||
222 | break; | ||
223 | case 'd': | ||
224 | if (new_pol.min) | ||
225 | double_parm++; | ||
226 | policychange++; | ||
227 | new_pol.min = string_to_frequency(optarg); | ||
228 | if (new_pol.min == 0) { | ||
229 | print_unknown_arg(); | ||
230 | return -EINVAL; | ||
231 | } | ||
232 | break; | ||
233 | case 'u': | ||
234 | if (new_pol.max) | ||
235 | double_parm++; | ||
236 | policychange++; | ||
237 | new_pol.max = string_to_frequency(optarg); | ||
238 | if (new_pol.max == 0) { | ||
239 | print_unknown_arg(); | ||
240 | return -EINVAL; | ||
241 | } | ||
242 | break; | ||
243 | case 'f': | ||
244 | if (freq) | ||
245 | double_parm++; | ||
246 | freq = string_to_frequency(optarg); | ||
247 | if (freq == 0) { | ||
248 | print_unknown_arg(); | ||
249 | return -EINVAL; | ||
250 | } | ||
251 | break; | ||
252 | case 'g': | ||
253 | if (new_pol.governor) | ||
254 | double_parm++; | ||
255 | policychange++; | ||
256 | if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) { | ||
257 | print_unknown_arg(); | ||
258 | return -EINVAL; | ||
259 | } | ||
260 | if ((sscanf(optarg, "%s", gov)) != 1) { | ||
261 | print_unknown_arg(); | ||
262 | return -EINVAL; | ||
263 | } | ||
264 | new_pol.governor = gov; | ||
265 | break; | ||
266 | } | ||
267 | } while (cont); | ||
268 | |||
269 | /* parameter checking */ | ||
270 | if (double_parm) { | ||
271 | printf("the same parameter was passed more than once\n"); | ||
272 | return -EINVAL; | ||
273 | } | ||
274 | |||
275 | if (freq && policychange) { | ||
276 | printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n" | ||
277 | "-g/--governor parameters\n")); | ||
278 | return -EINVAL; | ||
279 | } | ||
280 | |||
281 | if (!freq && !policychange) { | ||
282 | printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n" | ||
283 | "-g/--governor must be passed\n")); | ||
284 | return -EINVAL; | ||
285 | } | ||
286 | |||
287 | /* Default is: set all CPUs */ | ||
288 | if (bitmask_isallclear(cpus_chosen)) | ||
289 | bitmask_setall(cpus_chosen); | ||
290 | |||
291 | /* Also set frequency settings for related CPUs if -r is passed */ | ||
292 | if (related) { | ||
293 | for (cpu = bitmask_first(cpus_chosen); | ||
294 | cpu <= bitmask_last(cpus_chosen); cpu++) { | ||
295 | struct cpufreq_affected_cpus *cpus; | ||
296 | |||
297 | if (!bitmask_isbitset(cpus_chosen, cpu) || | ||
298 | cpufreq_cpu_exists(cpu)) | ||
299 | continue; | ||
300 | |||
301 | cpus = cpufreq_get_related_cpus(cpu); | ||
302 | if (!cpus) | ||
303 | break; | ||
304 | while (cpus->next) { | ||
305 | bitmask_setbit(cpus_chosen, cpus->cpu); | ||
306 | cpus = cpus->next; | ||
307 | } | ||
308 | cpufreq_put_related_cpus(cpus); | ||
309 | } | ||
310 | } | ||
311 | |||
312 | |||
313 | /* loop over CPUs */ | ||
314 | for (cpu = bitmask_first(cpus_chosen); | ||
315 | cpu <= bitmask_last(cpus_chosen); cpu++) { | ||
316 | |||
317 | if (!bitmask_isbitset(cpus_chosen, cpu) || | ||
318 | cpufreq_cpu_exists(cpu)) | ||
319 | continue; | ||
320 | |||
321 | printf(_("Setting cpu: %d\n"), cpu); | ||
322 | ret = do_one_cpu(cpu, &new_pol, freq, policychange); | ||
323 | if (ret) | ||
324 | break; | ||
325 | } | ||
326 | |||
327 | if (ret) | ||
328 | print_error(); | ||
329 | |||
330 | return ret; | ||
331 | } | ||
diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c new file mode 100644 index 00000000000..b028267c137 --- /dev/null +++ b/tools/power/cpupower/utils/cpuidle-info.c | |||
@@ -0,0 +1,222 @@ | |||
1 | /* | ||
2 | * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> | ||
3 | * (C) 2010 Thomas Renninger <trenn@suse.de> | ||
4 | * | ||
5 | * Licensed under the terms of the GNU GPL License version 2. | ||
6 | */ | ||
7 | |||
8 | |||
9 | #include <unistd.h> | ||
10 | #include <stdio.h> | ||
11 | #include <errno.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <string.h> | ||
14 | #include <getopt.h> | ||
15 | #include <cpufreq.h> | ||
16 | |||
17 | #include "helpers/helpers.h" | ||
18 | #include "helpers/sysfs.h" | ||
19 | #include "helpers/bitmask.h" | ||
20 | |||
21 | #define LINE_LEN 10 | ||
22 | |||
23 | static void cpuidle_cpu_output(unsigned int cpu, int verbose) | ||
24 | { | ||
25 | int idlestates, idlestate; | ||
26 | char *tmp; | ||
27 | |||
28 | printf(_ ("Analyzing CPU %d:\n"), cpu); | ||
29 | |||
30 | idlestates = sysfs_get_idlestate_count(cpu); | ||
31 | if (idlestates == 0) { | ||
32 | printf(_("CPU %u: No idle states\n"), cpu); | ||
33 | return; | ||
34 | } else if (idlestates <= 0) { | ||
35 | printf(_("CPU %u: Can't read idle state info\n"), cpu); | ||
36 | return; | ||
37 | } | ||
38 | tmp = sysfs_get_idlestate_name(cpu, idlestates - 1); | ||
39 | if (!tmp) { | ||
40 | printf(_("Could not determine max idle state %u\n"), | ||
41 | idlestates - 1); | ||
42 | return; | ||
43 | } | ||
44 | |||
45 | printf(_("Number of idle states: %d\n"), idlestates); | ||
46 | |||
47 | printf(_("Available idle states:")); | ||
48 | for (idlestate = 1; idlestate < idlestates; idlestate++) { | ||
49 | tmp = sysfs_get_idlestate_name(cpu, idlestate); | ||
50 | if (!tmp) | ||
51 | continue; | ||
52 | printf(" %s", tmp); | ||
53 | free(tmp); | ||
54 | } | ||
55 | printf("\n"); | ||
56 | |||
57 | if (!verbose) | ||
58 | return; | ||
59 | |||
60 | for (idlestate = 1; idlestate < idlestates; idlestate++) { | ||
61 | tmp = sysfs_get_idlestate_name(cpu, idlestate); | ||
62 | if (!tmp) | ||
63 | continue; | ||
64 | printf("%s:\n", tmp); | ||
65 | free(tmp); | ||
66 | |||
67 | tmp = sysfs_get_idlestate_desc(cpu, idlestate); | ||
68 | if (!tmp) | ||
69 | continue; | ||
70 | printf(_("Flags/Description: %s\n"), tmp); | ||
71 | free(tmp); | ||
72 | |||
73 | printf(_("Latency: %lu\n"), | ||
74 | sysfs_get_idlestate_latency(cpu, idlestate)); | ||
75 | printf(_("Usage: %lu\n"), | ||
76 | sysfs_get_idlestate_usage(cpu, idlestate)); | ||
77 | printf(_("Duration: %llu\n"), | ||
78 | sysfs_get_idlestate_time(cpu, idlestate)); | ||
79 | } | ||
80 | printf("\n"); | ||
81 | } | ||
82 | |||
83 | static void cpuidle_general_output(void) | ||
84 | { | ||
85 | char *tmp; | ||
86 | |||
87 | tmp = sysfs_get_cpuidle_driver(); | ||
88 | if (!tmp) { | ||
89 | printf(_("Could not determine cpuidle driver\n")); | ||
90 | return; | ||
91 | } | ||
92 | |||
93 | printf(_("CPUidle driver: %s\n"), tmp); | ||
94 | free(tmp); | ||
95 | |||
96 | tmp = sysfs_get_cpuidle_governor(); | ||
97 | if (!tmp) { | ||
98 | printf(_("Could not determine cpuidle governor\n")); | ||
99 | return; | ||
100 | } | ||
101 | |||
102 | printf(_("CPUidle governor: %s\n"), tmp); | ||
103 | free(tmp); | ||
104 | } | ||
105 | |||
106 | static void proc_cpuidle_cpu_output(unsigned int cpu) | ||
107 | { | ||
108 | long max_allowed_cstate = 2000000000; | ||
109 | int cstates, cstate; | ||
110 | |||
111 | cstates = sysfs_get_idlestate_count(cpu); | ||
112 | if (cstates == 0) { | ||
113 | /* | ||
114 | * Go on and print same useless info as you'd see with | ||
115 | * cat /proc/acpi/processor/../power | ||
116 | * printf(_("CPU %u: No C-states available\n"), cpu); | ||
117 | * return; | ||
118 | */ | ||
119 | } else if (cstates <= 0) { | ||
120 | printf(_("CPU %u: Can't read C-state info\n"), cpu); | ||
121 | return; | ||
122 | } | ||
123 | /* printf("Cstates: %d\n", cstates); */ | ||
124 | |||
125 | printf(_("active state: C0\n")); | ||
126 | printf(_("max_cstate: C%u\n"), cstates-1); | ||
127 | printf(_("maximum allowed latency: %lu usec\n"), max_allowed_cstate); | ||
128 | printf(_("states:\t\n")); | ||
129 | for (cstate = 1; cstate < cstates; cstate++) { | ||
130 | printf(_(" C%d: " | ||
131 | "type[C%d] "), cstate, cstate); | ||
132 | printf(_("promotion[--] demotion[--] ")); | ||
133 | printf(_("latency[%03lu] "), | ||
134 | sysfs_get_idlestate_latency(cpu, cstate)); | ||
135 | printf(_("usage[%08lu] "), | ||
136 | sysfs_get_idlestate_usage(cpu, cstate)); | ||
137 | printf(_("duration[%020Lu] \n"), | ||
138 | sysfs_get_idlestate_time(cpu, cstate)); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | static struct option info_opts[] = { | ||
143 | { .name = "silent", .has_arg = no_argument, .flag = NULL, .val = 's'}, | ||
144 | { .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'}, | ||
145 | { }, | ||
146 | }; | ||
147 | |||
148 | static inline void cpuidle_exit(int fail) | ||
149 | { | ||
150 | exit(EXIT_FAILURE); | ||
151 | } | ||
152 | |||
153 | int cmd_idle_info(int argc, char **argv) | ||
154 | { | ||
155 | extern char *optarg; | ||
156 | extern int optind, opterr, optopt; | ||
157 | int ret = 0, cont = 1, output_param = 0, verbose = 1; | ||
158 | unsigned int cpu = 0; | ||
159 | |||
160 | do { | ||
161 | ret = getopt_long(argc, argv, "os", info_opts, NULL); | ||
162 | if (ret == -1) | ||
163 | break; | ||
164 | switch (ret) { | ||
165 | case '?': | ||
166 | output_param = '?'; | ||
167 | cont = 0; | ||
168 | break; | ||
169 | case 's': | ||
170 | verbose = 0; | ||
171 | break; | ||
172 | case -1: | ||
173 | cont = 0; | ||
174 | break; | ||
175 | case 'o': | ||
176 | if (output_param) { | ||
177 | output_param = -1; | ||
178 | cont = 0; | ||
179 | break; | ||
180 | } | ||
181 | output_param = ret; | ||
182 | break; | ||
183 | } | ||
184 | } while (cont); | ||
185 | |||
186 | switch (output_param) { | ||
187 | case -1: | ||
188 | printf(_("You can't specify more than one " | ||
189 | "output-specific argument\n")); | ||
190 | cpuidle_exit(EXIT_FAILURE); | ||
191 | case '?': | ||
192 | printf(_("invalid or unknown argument\n")); | ||
193 | cpuidle_exit(EXIT_FAILURE); | ||
194 | } | ||
195 | |||
196 | /* Default is: show output of CPU 0 only */ | ||
197 | if (bitmask_isallclear(cpus_chosen)) | ||
198 | bitmask_setbit(cpus_chosen, 0); | ||
199 | |||
200 | if (output_param == 0) | ||
201 | cpuidle_general_output(); | ||
202 | |||
203 | for (cpu = bitmask_first(cpus_chosen); | ||
204 | cpu <= bitmask_last(cpus_chosen); cpu++) { | ||
205 | |||
206 | if (!bitmask_isbitset(cpus_chosen, cpu) || | ||
207 | cpufreq_cpu_exists(cpu)) | ||
208 | continue; | ||
209 | |||
210 | switch (output_param) { | ||
211 | |||
212 | case 'o': | ||
213 | proc_cpuidle_cpu_output(cpu); | ||
214 | break; | ||
215 | case 0: | ||
216 | printf("\n"); | ||
217 | cpuidle_cpu_output(cpu, verbose); | ||
218 | break; | ||
219 | } | ||
220 | } | ||
221 | return EXIT_SUCCESS; | ||
222 | } | ||
diff --git a/tools/power/cpupower/utils/cpupower-info.c b/tools/power/cpupower/utils/cpupower-info.c new file mode 100644 index 00000000000..3f68632c28c --- /dev/null +++ b/tools/power/cpupower/utils/cpupower-info.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * (C) 2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | */ | ||
6 | |||
7 | |||
8 | #include <unistd.h> | ||
9 | #include <stdio.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <getopt.h> | ||
14 | |||
15 | #include <cpufreq.h> | ||
16 | #include "helpers/helpers.h" | ||
17 | #include "helpers/sysfs.h" | ||
18 | |||
19 | static struct option set_opts[] = { | ||
20 | { .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'}, | ||
21 | { .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'}, | ||
22 | { .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'}, | ||
23 | { }, | ||
24 | }; | ||
25 | |||
26 | static void print_wrong_arg_exit(void) | ||
27 | { | ||
28 | printf(_("invalid or unknown argument\n")); | ||
29 | exit(EXIT_FAILURE); | ||
30 | } | ||
31 | |||
32 | int cmd_info(int argc, char **argv) | ||
33 | { | ||
34 | extern char *optarg; | ||
35 | extern int optind, opterr, optopt; | ||
36 | unsigned int cpu; | ||
37 | |||
38 | union { | ||
39 | struct { | ||
40 | int sched_mc:1; | ||
41 | int sched_smt:1; | ||
42 | int perf_bias:1; | ||
43 | }; | ||
44 | int params; | ||
45 | } params = {}; | ||
46 | int ret = 0; | ||
47 | |||
48 | setlocale(LC_ALL, ""); | ||
49 | textdomain(PACKAGE); | ||
50 | |||
51 | /* parameter parsing */ | ||
52 | while ((ret = getopt_long(argc, argv, "msb", set_opts, NULL)) != -1) { | ||
53 | switch (ret) { | ||
54 | case 'b': | ||
55 | if (params.perf_bias) | ||
56 | print_wrong_arg_exit(); | ||
57 | params.perf_bias = 1; | ||
58 | break; | ||
59 | case 'm': | ||
60 | if (params.sched_mc) | ||
61 | print_wrong_arg_exit(); | ||
62 | params.sched_mc = 1; | ||
63 | break; | ||
64 | case 's': | ||
65 | if (params.sched_smt) | ||
66 | print_wrong_arg_exit(); | ||
67 | params.sched_smt = 1; | ||
68 | break; | ||
69 | default: | ||
70 | print_wrong_arg_exit(); | ||
71 | } | ||
72 | }; | ||
73 | |||
74 | if (!params.params) | ||
75 | params.params = 0x7; | ||
76 | |||
77 | /* Default is: show output of CPU 0 only */ | ||
78 | if (bitmask_isallclear(cpus_chosen)) | ||
79 | bitmask_setbit(cpus_chosen, 0); | ||
80 | |||
81 | if (params.sched_mc) { | ||
82 | ret = sysfs_get_sched("mc"); | ||
83 | printf(_("System's multi core scheduler setting: ")); | ||
84 | if (ret < 0) | ||
85 | /* if sysfs file is missing it's: errno == ENOENT */ | ||
86 | printf(_("not supported\n")); | ||
87 | else | ||
88 | printf("%d\n", ret); | ||
89 | } | ||
90 | if (params.sched_smt) { | ||
91 | ret = sysfs_get_sched("smt"); | ||
92 | printf(_("System's thread sibling scheduler setting: ")); | ||
93 | if (ret < 0) | ||
94 | /* if sysfs file is missing it's: errno == ENOENT */ | ||
95 | printf(_("not supported\n")); | ||
96 | else | ||
97 | printf("%d\n", ret); | ||
98 | } | ||
99 | |||
100 | /* Add more per cpu options here */ | ||
101 | if (!params.perf_bias) | ||
102 | return ret; | ||
103 | |||
104 | if (params.perf_bias) { | ||
105 | if (!run_as_root) { | ||
106 | params.perf_bias = 0; | ||
107 | printf(_("Intel's performance bias setting needs root privileges\n")); | ||
108 | } else if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS)) { | ||
109 | printf(_("System does not support Intel's performance" | ||
110 | " bias setting\n")); | ||
111 | params.perf_bias = 0; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | /* loop over CPUs */ | ||
116 | for (cpu = bitmask_first(cpus_chosen); | ||
117 | cpu <= bitmask_last(cpus_chosen); cpu++) { | ||
118 | |||
119 | if (!bitmask_isbitset(cpus_chosen, cpu) || | ||
120 | cpufreq_cpu_exists(cpu)) | ||
121 | continue; | ||
122 | |||
123 | printf(_("analyzing CPU %d:\n"), cpu); | ||
124 | |||
125 | if (params.perf_bias) { | ||
126 | ret = msr_intel_get_perf_bias(cpu); | ||
127 | if (ret < 0) { | ||
128 | printf(_("Could not read perf-bias value\n")); | ||
129 | break; | ||
130 | } else | ||
131 | printf(_("perf-bias: %d\n"), ret); | ||
132 | } | ||
133 | } | ||
134 | return ret; | ||
135 | } | ||
diff --git a/tools/power/cpupower/utils/cpupower-set.c b/tools/power/cpupower/utils/cpupower-set.c new file mode 100644 index 00000000000..dc4de376211 --- /dev/null +++ b/tools/power/cpupower/utils/cpupower-set.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * (C) 2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | */ | ||
6 | |||
7 | |||
8 | #include <unistd.h> | ||
9 | #include <stdio.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <getopt.h> | ||
14 | |||
15 | #include <cpufreq.h> | ||
16 | #include "helpers/helpers.h" | ||
17 | #include "helpers/sysfs.h" | ||
18 | #include "helpers/bitmask.h" | ||
19 | |||
20 | static struct option set_opts[] = { | ||
21 | { .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'}, | ||
22 | { .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'}, | ||
23 | { .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'}, | ||
24 | { }, | ||
25 | }; | ||
26 | |||
27 | static void print_wrong_arg_exit(void) | ||
28 | { | ||
29 | printf(_("invalid or unknown argument\n")); | ||
30 | exit(EXIT_FAILURE); | ||
31 | } | ||
32 | |||
33 | int cmd_set(int argc, char **argv) | ||
34 | { | ||
35 | extern char *optarg; | ||
36 | extern int optind, opterr, optopt; | ||
37 | unsigned int cpu; | ||
38 | |||
39 | union { | ||
40 | struct { | ||
41 | int sched_mc:1; | ||
42 | int sched_smt:1; | ||
43 | int perf_bias:1; | ||
44 | }; | ||
45 | int params; | ||
46 | } params; | ||
47 | int sched_mc = 0, sched_smt = 0, perf_bias = 0; | ||
48 | int ret = 0; | ||
49 | |||
50 | setlocale(LC_ALL, ""); | ||
51 | textdomain(PACKAGE); | ||
52 | |||
53 | params.params = 0; | ||
54 | /* parameter parsing */ | ||
55 | while ((ret = getopt_long(argc, argv, "m:s:b:", | ||
56 | set_opts, NULL)) != -1) { | ||
57 | switch (ret) { | ||
58 | case 'b': | ||
59 | if (params.perf_bias) | ||
60 | print_wrong_arg_exit(); | ||
61 | perf_bias = atoi(optarg); | ||
62 | if (perf_bias < 0 || perf_bias > 15) { | ||
63 | printf(_("--perf-bias param out " | ||
64 | "of range [0-%d]\n"), 15); | ||
65 | print_wrong_arg_exit(); | ||
66 | } | ||
67 | params.perf_bias = 1; | ||
68 | break; | ||
69 | case 'm': | ||
70 | if (params.sched_mc) | ||
71 | print_wrong_arg_exit(); | ||
72 | sched_mc = atoi(optarg); | ||
73 | if (sched_mc < 0 || sched_mc > 2) { | ||
74 | printf(_("--sched-mc param out " | ||
75 | "of range [0-%d]\n"), 2); | ||
76 | print_wrong_arg_exit(); | ||
77 | } | ||
78 | params.sched_mc = 1; | ||
79 | break; | ||
80 | case 's': | ||
81 | if (params.sched_smt) | ||
82 | print_wrong_arg_exit(); | ||
83 | sched_smt = atoi(optarg); | ||
84 | if (sched_smt < 0 || sched_smt > 2) { | ||
85 | printf(_("--sched-smt param out " | ||
86 | "of range [0-%d]\n"), 2); | ||
87 | print_wrong_arg_exit(); | ||
88 | } | ||
89 | params.sched_smt = 1; | ||
90 | break; | ||
91 | default: | ||
92 | print_wrong_arg_exit(); | ||
93 | } | ||
94 | }; | ||
95 | |||
96 | if (!params.params) | ||
97 | print_wrong_arg_exit(); | ||
98 | |||
99 | if (params.sched_mc) { | ||
100 | ret = sysfs_set_sched("mc", sched_mc); | ||
101 | if (ret) | ||
102 | fprintf(stderr, _("Error setting sched-mc %s\n"), | ||
103 | (ret == -ENODEV) ? "not supported" : ""); | ||
104 | } | ||
105 | if (params.sched_smt) { | ||
106 | ret = sysfs_set_sched("smt", sched_smt); | ||
107 | if (ret) | ||
108 | fprintf(stderr, _("Error setting sched-smt %s\n"), | ||
109 | (ret == -ENODEV) ? "not supported" : ""); | ||
110 | } | ||
111 | |||
112 | /* Default is: set all CPUs */ | ||
113 | if (bitmask_isallclear(cpus_chosen)) | ||
114 | bitmask_setall(cpus_chosen); | ||
115 | |||
116 | /* loop over CPUs */ | ||
117 | for (cpu = bitmask_first(cpus_chosen); | ||
118 | cpu <= bitmask_last(cpus_chosen); cpu++) { | ||
119 | |||
120 | if (!bitmask_isbitset(cpus_chosen, cpu) || | ||
121 | cpufreq_cpu_exists(cpu)) | ||
122 | continue; | ||
123 | |||
124 | if (params.perf_bias) { | ||
125 | ret = msr_intel_set_perf_bias(cpu, perf_bias); | ||
126 | if (ret) { | ||
127 | fprintf(stderr, _("Error setting perf-bias " | ||
128 | "value on CPU %d\n"), cpu); | ||
129 | break; | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | return ret; | ||
134 | } | ||
diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c new file mode 100644 index 00000000000..52bee591c1c --- /dev/null +++ b/tools/power/cpupower/utils/cpupower.c | |||
@@ -0,0 +1,214 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | * Ideas taken over from the perf userspace tool (included in the Linus | ||
7 | * kernel git repo): subcommand builtins and param parsing. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <string.h> | ||
13 | #include <unistd.h> | ||
14 | #include <errno.h> | ||
15 | |||
16 | #include "builtin.h" | ||
17 | #include "helpers/helpers.h" | ||
18 | #include "helpers/bitmask.h" | ||
19 | |||
20 | struct cmd_struct { | ||
21 | const char *cmd; | ||
22 | int (*main)(int, const char **); | ||
23 | int needs_root; | ||
24 | }; | ||
25 | |||
26 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) | ||
27 | |||
28 | static int cmd_help(int argc, const char **argv); | ||
29 | |||
30 | /* Global cpu_info object available for all binaries | ||
31 | * Info only retrieved from CPU 0 | ||
32 | * | ||
33 | * Values will be zero/unknown on non X86 archs | ||
34 | */ | ||
35 | struct cpupower_cpu_info cpupower_cpu_info; | ||
36 | int run_as_root; | ||
37 | /* Affected cpus chosen by -c/--cpu param */ | ||
38 | struct bitmask *cpus_chosen; | ||
39 | |||
40 | #ifdef DEBUG | ||
41 | int be_verbose; | ||
42 | #endif | ||
43 | |||
44 | static void print_help(void); | ||
45 | |||
46 | static struct cmd_struct commands[] = { | ||
47 | { "frequency-info", cmd_freq_info, 0 }, | ||
48 | { "frequency-set", cmd_freq_set, 1 }, | ||
49 | { "idle-info", cmd_idle_info, 0 }, | ||
50 | { "set", cmd_set, 1 }, | ||
51 | { "info", cmd_info, 0 }, | ||
52 | { "monitor", cmd_monitor, 0 }, | ||
53 | { "help", cmd_help, 0 }, | ||
54 | /* { "bench", cmd_bench, 1 }, */ | ||
55 | }; | ||
56 | |||
57 | static void print_help(void) | ||
58 | { | ||
59 | unsigned int i; | ||
60 | |||
61 | #ifdef DEBUG | ||
62 | printf(_("Usage:\tcpupower [-d|--debug] [-c|--cpu cpulist ] <command> [<args>]\n")); | ||
63 | #else | ||
64 | printf(_("Usage:\tcpupower [-c|--cpu cpulist ] <command> [<args>]\n")); | ||
65 | #endif | ||
66 | printf(_("Supported commands are:\n")); | ||
67 | for (i = 0; i < ARRAY_SIZE(commands); i++) | ||
68 | printf("\t%s\n", commands[i].cmd); | ||
69 | printf(_("\nNot all commands can make use of the -c cpulist option.\n")); | ||
70 | printf(_("\nUse 'cpupower help <command>' for getting help for above commands.\n")); | ||
71 | } | ||
72 | |||
73 | static int print_man_page(const char *subpage) | ||
74 | { | ||
75 | int len; | ||
76 | char *page; | ||
77 | |||
78 | len = 10; /* enough for "cpupower-" */ | ||
79 | if (subpage != NULL) | ||
80 | len += strlen(subpage); | ||
81 | |||
82 | page = malloc(len); | ||
83 | if (!page) | ||
84 | return -ENOMEM; | ||
85 | |||
86 | sprintf(page, "cpupower"); | ||
87 | if ((subpage != NULL) && strcmp(subpage, "help")) { | ||
88 | strcat(page, "-"); | ||
89 | strcat(page, subpage); | ||
90 | } | ||
91 | |||
92 | execlp("man", "man", page, NULL); | ||
93 | |||
94 | /* should not be reached */ | ||
95 | return -EINVAL; | ||
96 | } | ||
97 | |||
98 | static int cmd_help(int argc, const char **argv) | ||
99 | { | ||
100 | if (argc > 1) { | ||
101 | print_man_page(argv[1]); /* exits within execlp() */ | ||
102 | return EXIT_FAILURE; | ||
103 | } | ||
104 | |||
105 | print_help(); | ||
106 | return EXIT_SUCCESS; | ||
107 | } | ||
108 | |||
109 | static void print_version(void) | ||
110 | { | ||
111 | printf(PACKAGE " " VERSION "\n"); | ||
112 | printf(_("Report errors and bugs to %s, please.\n"), PACKAGE_BUGREPORT); | ||
113 | } | ||
114 | |||
115 | static void handle_options(int *argc, const char ***argv) | ||
116 | { | ||
117 | int ret, x, new_argc = 0; | ||
118 | |||
119 | if (*argc < 1) | ||
120 | return; | ||
121 | |||
122 | for (x = 0; x < *argc && ((*argv)[x])[0] == '-'; x++) { | ||
123 | const char *param = (*argv)[x]; | ||
124 | if (!strcmp(param, "-h") || !strcmp(param, "--help")) { | ||
125 | print_help(); | ||
126 | exit(EXIT_SUCCESS); | ||
127 | } else if (!strcmp(param, "-c") || !strcmp(param, "--cpu")) { | ||
128 | if (*argc < 2) { | ||
129 | print_help(); | ||
130 | exit(EXIT_FAILURE); | ||
131 | } | ||
132 | if (!strcmp((*argv)[x+1], "all")) | ||
133 | bitmask_setall(cpus_chosen); | ||
134 | else { | ||
135 | ret = bitmask_parselist( | ||
136 | (*argv)[x+1], cpus_chosen); | ||
137 | if (ret < 0) { | ||
138 | fprintf(stderr, _("Error parsing cpu " | ||
139 | "list\n")); | ||
140 | exit(EXIT_FAILURE); | ||
141 | } | ||
142 | } | ||
143 | x += 1; | ||
144 | /* Cut out param: cpupower -c 1 info -> cpupower info */ | ||
145 | new_argc += 2; | ||
146 | continue; | ||
147 | } else if (!strcmp(param, "-v") || | ||
148 | !strcmp(param, "--version")) { | ||
149 | print_version(); | ||
150 | exit(EXIT_SUCCESS); | ||
151 | #ifdef DEBUG | ||
152 | } else if (!strcmp(param, "-d") || !strcmp(param, "--debug")) { | ||
153 | be_verbose = 1; | ||
154 | new_argc++; | ||
155 | continue; | ||
156 | #endif | ||
157 | } else { | ||
158 | fprintf(stderr, "Unknown option: %s\n", param); | ||
159 | print_help(); | ||
160 | exit(EXIT_FAILURE); | ||
161 | } | ||
162 | } | ||
163 | *argc -= new_argc; | ||
164 | *argv += new_argc; | ||
165 | } | ||
166 | |||
167 | int main(int argc, const char *argv[]) | ||
168 | { | ||
169 | const char *cmd; | ||
170 | unsigned int i, ret; | ||
171 | |||
172 | cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF)); | ||
173 | |||
174 | argc--; | ||
175 | argv += 1; | ||
176 | |||
177 | handle_options(&argc, &argv); | ||
178 | |||
179 | cmd = argv[0]; | ||
180 | |||
181 | if (argc < 1) { | ||
182 | print_help(); | ||
183 | return EXIT_FAILURE; | ||
184 | } | ||
185 | |||
186 | setlocale(LC_ALL, ""); | ||
187 | textdomain(PACKAGE); | ||
188 | |||
189 | /* Turn "perf cmd --help" into "perf help cmd" */ | ||
190 | if (argc > 1 && !strcmp(argv[1], "--help")) { | ||
191 | argv[1] = argv[0]; | ||
192 | argv[0] = cmd = "help"; | ||
193 | } | ||
194 | |||
195 | get_cpu_info(0, &cpupower_cpu_info); | ||
196 | run_as_root = !getuid(); | ||
197 | |||
198 | for (i = 0; i < ARRAY_SIZE(commands); i++) { | ||
199 | struct cmd_struct *p = commands + i; | ||
200 | if (strcmp(p->cmd, cmd)) | ||
201 | continue; | ||
202 | if (!run_as_root && p->needs_root) { | ||
203 | fprintf(stderr, _("Subcommand %s needs root " | ||
204 | "privileges\n"), cmd); | ||
205 | return EXIT_FAILURE; | ||
206 | } | ||
207 | ret = p->main(argc, argv); | ||
208 | if (cpus_chosen) | ||
209 | bitmask_free(cpus_chosen); | ||
210 | return ret; | ||
211 | } | ||
212 | print_help(); | ||
213 | return EXIT_FAILURE; | ||
214 | } | ||
diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c new file mode 100644 index 00000000000..87d5605bdda --- /dev/null +++ b/tools/power/cpupower/utils/helpers/amd.c | |||
@@ -0,0 +1,137 @@ | |||
1 | #if defined(__i386__) || defined(__x86_64__) | ||
2 | #include <unistd.h> | ||
3 | #include <errno.h> | ||
4 | #include <stdio.h> | ||
5 | #include <stdint.h> | ||
6 | |||
7 | #include <pci/pci.h> | ||
8 | |||
9 | #include "helpers/helpers.h" | ||
10 | |||
11 | #define MSR_AMD_PSTATE_STATUS 0xc0010063 | ||
12 | #define MSR_AMD_PSTATE 0xc0010064 | ||
13 | #define MSR_AMD_PSTATE_LIMIT 0xc0010061 | ||
14 | |||
15 | union msr_pstate { | ||
16 | struct { | ||
17 | unsigned fid:6; | ||
18 | unsigned did:3; | ||
19 | unsigned vid:7; | ||
20 | unsigned res1:6; | ||
21 | unsigned nbdid:1; | ||
22 | unsigned res2:2; | ||
23 | unsigned nbvid:7; | ||
24 | unsigned iddval:8; | ||
25 | unsigned idddiv:2; | ||
26 | unsigned res3:21; | ||
27 | unsigned en:1; | ||
28 | } bits; | ||
29 | unsigned long long val; | ||
30 | }; | ||
31 | |||
32 | static int get_did(int family, union msr_pstate pstate) | ||
33 | { | ||
34 | int t; | ||
35 | |||
36 | if (family == 0x12) | ||
37 | t = pstate.val & 0xf; | ||
38 | else | ||
39 | t = pstate.bits.did; | ||
40 | |||
41 | return t; | ||
42 | } | ||
43 | |||
44 | static int get_cof(int family, union msr_pstate pstate) | ||
45 | { | ||
46 | int t; | ||
47 | int fid, did; | ||
48 | |||
49 | did = get_did(family, pstate); | ||
50 | |||
51 | t = 0x10; | ||
52 | fid = pstate.bits.fid; | ||
53 | if (family == 0x11) | ||
54 | t = 0x8; | ||
55 | |||
56 | return (100 * (fid + t)) >> did; | ||
57 | } | ||
58 | |||
59 | /* Needs: | ||
60 | * cpu -> the cpu that gets evaluated | ||
61 | * cpu_family -> The cpu's family (0x10, 0x12,...) | ||
62 | * boots_states -> how much boost states the machines support | ||
63 | * | ||
64 | * Fills up: | ||
65 | * pstates -> a pointer to an array of size MAX_HW_PSTATES | ||
66 | * must be initialized with zeros. | ||
67 | * All available HW pstates (including boost states) | ||
68 | * no -> amount of pstates above array got filled up with | ||
69 | * | ||
70 | * returns zero on success, -1 on failure | ||
71 | */ | ||
72 | int decode_pstates(unsigned int cpu, unsigned int cpu_family, | ||
73 | int boost_states, unsigned long *pstates, int *no) | ||
74 | { | ||
75 | int i, psmax, pscur; | ||
76 | union msr_pstate pstate; | ||
77 | unsigned long long val; | ||
78 | |||
79 | /* Only read out frequencies from HW when CPU might be boostable | ||
80 | to keep the code as short and clean as possible. | ||
81 | Otherwise frequencies are exported via ACPI tables. | ||
82 | */ | ||
83 | if (cpu_family < 0x10 || cpu_family == 0x14) | ||
84 | return -1; | ||
85 | |||
86 | if (read_msr(cpu, MSR_AMD_PSTATE_LIMIT, &val)) | ||
87 | return -1; | ||
88 | |||
89 | psmax = (val >> 4) & 0x7; | ||
90 | |||
91 | if (read_msr(cpu, MSR_AMD_PSTATE_STATUS, &val)) | ||
92 | return -1; | ||
93 | |||
94 | pscur = val & 0x7; | ||
95 | |||
96 | pscur += boost_states; | ||
97 | psmax += boost_states; | ||
98 | for (i = 0; i <= psmax; i++) { | ||
99 | if (i >= MAX_HW_PSTATES) { | ||
100 | fprintf(stderr, "HW pstates [%d] exceeding max [%d]\n", | ||
101 | psmax, MAX_HW_PSTATES); | ||
102 | return -1; | ||
103 | } | ||
104 | if (read_msr(cpu, MSR_AMD_PSTATE + i, &pstate.val)) | ||
105 | return -1; | ||
106 | pstates[i] = get_cof(cpu_family, pstate); | ||
107 | } | ||
108 | *no = i; | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | int amd_pci_get_num_boost_states(int *active, int *states) | ||
113 | { | ||
114 | struct pci_access *pci_acc; | ||
115 | int vendor_id = 0x1022; | ||
116 | int boost_dev_ids[4] = {0x1204, 0x1604, 0x1704, 0}; | ||
117 | struct pci_dev *device; | ||
118 | uint8_t val = 0; | ||
119 | |||
120 | *active = *states = 0; | ||
121 | |||
122 | device = pci_acc_init(&pci_acc, vendor_id, boost_dev_ids); | ||
123 | |||
124 | if (device == NULL) | ||
125 | return -ENODEV; | ||
126 | |||
127 | val = pci_read_byte(device, 0x15c); | ||
128 | if (val & 3) | ||
129 | *active = 1; | ||
130 | else | ||
131 | *active = 0; | ||
132 | *states = (val >> 2) & 7; | ||
133 | |||
134 | pci_cleanup(pci_acc); | ||
135 | return 0; | ||
136 | } | ||
137 | #endif /* defined(__i386__) || defined(__x86_64__) */ | ||
diff --git a/tools/power/cpupower/utils/helpers/bitmask.c b/tools/power/cpupower/utils/helpers/bitmask.c new file mode 100644 index 00000000000..5c074c60f90 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/bitmask.c | |||
@@ -0,0 +1,292 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | |||
5 | #include <helpers/bitmask.h> | ||
6 | |||
7 | /* How many bits in an unsigned long */ | ||
8 | #define bitsperlong (8 * sizeof(unsigned long)) | ||
9 | |||
10 | /* howmany(a,b) : how many elements of size b needed to hold all of a */ | ||
11 | #define howmany(x, y) (((x)+((y)-1))/(y)) | ||
12 | |||
13 | /* How many longs in mask of n bits */ | ||
14 | #define longsperbits(n) howmany(n, bitsperlong) | ||
15 | |||
16 | #define max(a, b) ((a) > (b) ? (a) : (b)) | ||
17 | |||
18 | /* | ||
19 | * Allocate and free `struct bitmask *` | ||
20 | */ | ||
21 | |||
22 | /* Allocate a new `struct bitmask` with a size of n bits */ | ||
23 | struct bitmask *bitmask_alloc(unsigned int n) | ||
24 | { | ||
25 | struct bitmask *bmp; | ||
26 | |||
27 | bmp = malloc(sizeof(*bmp)); | ||
28 | if (bmp == 0) | ||
29 | return 0; | ||
30 | bmp->size = n; | ||
31 | bmp->maskp = calloc(longsperbits(n), sizeof(unsigned long)); | ||
32 | if (bmp->maskp == 0) { | ||
33 | free(bmp); | ||
34 | return 0; | ||
35 | } | ||
36 | return bmp; | ||
37 | } | ||
38 | |||
39 | /* Free `struct bitmask` */ | ||
40 | void bitmask_free(struct bitmask *bmp) | ||
41 | { | ||
42 | if (bmp == 0) | ||
43 | return; | ||
44 | free(bmp->maskp); | ||
45 | bmp->maskp = (unsigned long *)0xdeadcdef; /* double free tripwire */ | ||
46 | free(bmp); | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * The routines _getbit() and _setbit() are the only | ||
51 | * routines that actually understand the layout of bmp->maskp[]. | ||
52 | * | ||
53 | * On little endian architectures, this could simply be an array of | ||
54 | * bytes. But the kernel layout of bitmasks _is_ visible to userspace | ||
55 | * via the sched_(set/get)affinity calls in Linux 2.6, and on big | ||
56 | * endian architectures, it is painfully obvious that this is an | ||
57 | * array of unsigned longs. | ||
58 | */ | ||
59 | |||
60 | /* Return the value (0 or 1) of bit n in bitmask bmp */ | ||
61 | static unsigned int _getbit(const struct bitmask *bmp, unsigned int n) | ||
62 | { | ||
63 | if (n < bmp->size) | ||
64 | return (bmp->maskp[n/bitsperlong] >> (n % bitsperlong)) & 1; | ||
65 | else | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | /* Set bit n in bitmask bmp to value v (0 or 1) */ | ||
70 | static void _setbit(struct bitmask *bmp, unsigned int n, unsigned int v) | ||
71 | { | ||
72 | if (n < bmp->size) { | ||
73 | if (v) | ||
74 | bmp->maskp[n/bitsperlong] |= 1UL << (n % bitsperlong); | ||
75 | else | ||
76 | bmp->maskp[n/bitsperlong] &= | ||
77 | ~(1UL << (n % bitsperlong)); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | * When parsing bitmask lists, only allow numbers, separated by one | ||
83 | * of the allowed next characters. | ||
84 | * | ||
85 | * The parameter 'sret' is the return from a sscanf "%u%c". It is | ||
86 | * -1 if the sscanf input string was empty. It is 0 if the first | ||
87 | * character in the sscanf input string was not a decimal number. | ||
88 | * It is 1 if the unsigned number matching the "%u" was the end of the | ||
89 | * input string. It is 2 if one or more additional characters followed | ||
90 | * the matched unsigned number. If it is 2, then 'nextc' is the first | ||
91 | * character following the number. The parameter 'ok_next_chars' | ||
92 | * is the nul-terminated list of allowed next characters. | ||
93 | * | ||
94 | * The mask term just scanned was ok if and only if either the numbers | ||
95 | * matching the %u were all of the input or if the next character in | ||
96 | * the input past the numbers was one of the allowed next characters. | ||
97 | */ | ||
98 | static int scan_was_ok(int sret, char nextc, const char *ok_next_chars) | ||
99 | { | ||
100 | return sret == 1 || | ||
101 | (sret == 2 && strchr(ok_next_chars, nextc) != NULL); | ||
102 | } | ||
103 | |||
104 | static const char *nexttoken(const char *q, int sep) | ||
105 | { | ||
106 | if (q) | ||
107 | q = strchr(q, sep); | ||
108 | if (q) | ||
109 | q++; | ||
110 | return q; | ||
111 | } | ||
112 | |||
113 | /* Set a single bit i in bitmask */ | ||
114 | struct bitmask *bitmask_setbit(struct bitmask *bmp, unsigned int i) | ||
115 | { | ||
116 | _setbit(bmp, i, 1); | ||
117 | return bmp; | ||
118 | } | ||
119 | |||
120 | /* Set all bits in bitmask: bmp = ~0 */ | ||
121 | struct bitmask *bitmask_setall(struct bitmask *bmp) | ||
122 | { | ||
123 | unsigned int i; | ||
124 | for (i = 0; i < bmp->size; i++) | ||
125 | _setbit(bmp, i, 1); | ||
126 | return bmp; | ||
127 | } | ||
128 | |||
129 | /* Clear all bits in bitmask: bmp = 0 */ | ||
130 | struct bitmask *bitmask_clearall(struct bitmask *bmp) | ||
131 | { | ||
132 | unsigned int i; | ||
133 | for (i = 0; i < bmp->size; i++) | ||
134 | _setbit(bmp, i, 0); | ||
135 | return bmp; | ||
136 | } | ||
137 | |||
138 | /* True if all bits are clear */ | ||
139 | int bitmask_isallclear(const struct bitmask *bmp) | ||
140 | { | ||
141 | unsigned int i; | ||
142 | for (i = 0; i < bmp->size; i++) | ||
143 | if (_getbit(bmp, i)) | ||
144 | return 0; | ||
145 | return 1; | ||
146 | } | ||
147 | |||
148 | /* True if specified bit i is set */ | ||
149 | int bitmask_isbitset(const struct bitmask *bmp, unsigned int i) | ||
150 | { | ||
151 | return _getbit(bmp, i); | ||
152 | } | ||
153 | |||
154 | /* Number of lowest set bit (min) */ | ||
155 | unsigned int bitmask_first(const struct bitmask *bmp) | ||
156 | { | ||
157 | return bitmask_next(bmp, 0); | ||
158 | } | ||
159 | |||
160 | /* Number of highest set bit (max) */ | ||
161 | unsigned int bitmask_last(const struct bitmask *bmp) | ||
162 | { | ||
163 | unsigned int i; | ||
164 | unsigned int m = bmp->size; | ||
165 | for (i = 0; i < bmp->size; i++) | ||
166 | if (_getbit(bmp, i)) | ||
167 | m = i; | ||
168 | return m; | ||
169 | } | ||
170 | |||
171 | /* Number of next set bit at or above given bit i */ | ||
172 | unsigned int bitmask_next(const struct bitmask *bmp, unsigned int i) | ||
173 | { | ||
174 | unsigned int n; | ||
175 | for (n = i; n < bmp->size; n++) | ||
176 | if (_getbit(bmp, n)) | ||
177 | break; | ||
178 | return n; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * Parses a comma-separated list of numbers and ranges of numbers, | ||
183 | * with optional ':%u' strides modifying ranges, into provided bitmask. | ||
184 | * Some examples of input lists and their equivalent simple list: | ||
185 | * Input Equivalent to | ||
186 | * 0-3 0,1,2,3 | ||
187 | * 0-7:2 0,2,4,6 | ||
188 | * 1,3,5-7 1,3,5,6,7 | ||
189 | * 0-3:2,8-15:4 0,2,8,12 | ||
190 | */ | ||
191 | int bitmask_parselist(const char *buf, struct bitmask *bmp) | ||
192 | { | ||
193 | const char *p, *q; | ||
194 | |||
195 | bitmask_clearall(bmp); | ||
196 | |||
197 | q = buf; | ||
198 | while (p = q, q = nexttoken(q, ','), p) { | ||
199 | unsigned int a; /* begin of range */ | ||
200 | unsigned int b; /* end of range */ | ||
201 | unsigned int s; /* stride */ | ||
202 | const char *c1, *c2; /* next tokens after '-' or ',' */ | ||
203 | char nextc; /* char after sscanf %u match */ | ||
204 | int sret; /* sscanf return (number of matches) */ | ||
205 | |||
206 | sret = sscanf(p, "%u%c", &a, &nextc); | ||
207 | if (!scan_was_ok(sret, nextc, ",-")) | ||
208 | goto err; | ||
209 | b = a; | ||
210 | s = 1; | ||
211 | c1 = nexttoken(p, '-'); | ||
212 | c2 = nexttoken(p, ','); | ||
213 | if (c1 != NULL && (c2 == NULL || c1 < c2)) { | ||
214 | sret = sscanf(c1, "%u%c", &b, &nextc); | ||
215 | if (!scan_was_ok(sret, nextc, ",:")) | ||
216 | goto err; | ||
217 | c1 = nexttoken(c1, ':'); | ||
218 | if (c1 != NULL && (c2 == NULL || c1 < c2)) { | ||
219 | sret = sscanf(c1, "%u%c", &s, &nextc); | ||
220 | if (!scan_was_ok(sret, nextc, ",")) | ||
221 | goto err; | ||
222 | } | ||
223 | } | ||
224 | if (!(a <= b)) | ||
225 | goto err; | ||
226 | if (b >= bmp->size) | ||
227 | goto err; | ||
228 | while (a <= b) { | ||
229 | _setbit(bmp, a, 1); | ||
230 | a += s; | ||
231 | } | ||
232 | } | ||
233 | return 0; | ||
234 | err: | ||
235 | bitmask_clearall(bmp); | ||
236 | return -1; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * emit(buf, buflen, rbot, rtop, len) | ||
241 | * | ||
242 | * Helper routine for bitmask_displaylist(). Write decimal number | ||
243 | * or range to buf+len, suppressing output past buf+buflen, with optional | ||
244 | * comma-prefix. Return len of what would be written to buf, if it | ||
245 | * all fit. | ||
246 | */ | ||
247 | |||
248 | static inline int emit(char *buf, int buflen, int rbot, int rtop, int len) | ||
249 | { | ||
250 | if (len > 0) | ||
251 | len += snprintf(buf + len, max(buflen - len, 0), ","); | ||
252 | if (rbot == rtop) | ||
253 | len += snprintf(buf + len, max(buflen - len, 0), "%d", rbot); | ||
254 | else | ||
255 | len += snprintf(buf + len, max(buflen - len, 0), "%d-%d", | ||
256 | rbot, rtop); | ||
257 | return len; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * Write decimal list representation of bmp to buf. | ||
262 | * | ||
263 | * Output format is a comma-separated list of decimal numbers and | ||
264 | * ranges. Consecutively set bits are shown as two hyphen-separated | ||
265 | * decimal numbers, the smallest and largest bit numbers set in | ||
266 | * the range. Output format is compatible with the format | ||
267 | * accepted as input by bitmap_parselist(). | ||
268 | * | ||
269 | * The return value is the number of characters which would be | ||
270 | * generated for the given input, excluding the trailing '\0', as | ||
271 | * per ISO C99. | ||
272 | */ | ||
273 | |||
274 | int bitmask_displaylist(char *buf, int buflen, const struct bitmask *bmp) | ||
275 | { | ||
276 | int len = 0; | ||
277 | /* current bit is 'cur', most recently seen range is [rbot, rtop] */ | ||
278 | unsigned int cur, rbot, rtop; | ||
279 | |||
280 | if (buflen > 0) | ||
281 | *buf = 0; | ||
282 | rbot = cur = bitmask_first(bmp); | ||
283 | while (cur < bmp->size) { | ||
284 | rtop = cur; | ||
285 | cur = bitmask_next(bmp, cur+1); | ||
286 | if (cur >= bmp->size || cur > rtop + 1) { | ||
287 | len = emit(buf, buflen, rbot, rtop, len); | ||
288 | rbot = cur; | ||
289 | } | ||
290 | } | ||
291 | return len; | ||
292 | } | ||
diff --git a/tools/power/cpupower/utils/helpers/bitmask.h b/tools/power/cpupower/utils/helpers/bitmask.h new file mode 100644 index 00000000000..eb289df4105 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/bitmask.h | |||
@@ -0,0 +1,33 @@ | |||
1 | #ifndef __CPUPOWER_BITMASK__ | ||
2 | #define __CPUPOWER_BITMASK__ | ||
3 | |||
4 | /* Taken over from libbitmask, a project initiated from sgi: | ||
5 | * Url: http://oss.sgi.com/projects/cpusets/ | ||
6 | * Unfortunately it's not very widespread, therefore relevant parts are | ||
7 | * pasted here. | ||
8 | */ | ||
9 | |||
10 | struct bitmask { | ||
11 | unsigned int size; | ||
12 | unsigned long *maskp; | ||
13 | }; | ||
14 | |||
15 | struct bitmask *bitmask_alloc(unsigned int n); | ||
16 | void bitmask_free(struct bitmask *bmp); | ||
17 | |||
18 | struct bitmask *bitmask_setbit(struct bitmask *bmp, unsigned int i); | ||
19 | struct bitmask *bitmask_setall(struct bitmask *bmp); | ||
20 | struct bitmask *bitmask_clearall(struct bitmask *bmp); | ||
21 | |||
22 | unsigned int bitmask_first(const struct bitmask *bmp); | ||
23 | unsigned int bitmask_next(const struct bitmask *bmp, unsigned int i); | ||
24 | unsigned int bitmask_last(const struct bitmask *bmp); | ||
25 | int bitmask_isallclear(const struct bitmask *bmp); | ||
26 | int bitmask_isbitset(const struct bitmask *bmp, unsigned int i); | ||
27 | |||
28 | int bitmask_parselist(const char *buf, struct bitmask *bmp); | ||
29 | int bitmask_displaylist(char *buf, int len, const struct bitmask *bmp); | ||
30 | |||
31 | |||
32 | |||
33 | #endif /*__CPUPOWER_BITMASK__ */ | ||
diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c new file mode 100644 index 00000000000..906895d21cc --- /dev/null +++ b/tools/power/cpupower/utils/helpers/cpuid.c | |||
@@ -0,0 +1,176 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <errno.h> | ||
3 | #include <string.h> | ||
4 | #include <unistd.h> | ||
5 | #include <stdlib.h> | ||
6 | |||
7 | #include "helpers/helpers.h" | ||
8 | |||
9 | static const char *cpu_vendor_table[X86_VENDOR_MAX] = { | ||
10 | "Unknown", "GenuineIntel", "AuthenticAMD", | ||
11 | }; | ||
12 | |||
13 | #if defined(__i386__) || defined(__x86_64__) | ||
14 | |||
15 | /* from gcc */ | ||
16 | #include <cpuid.h> | ||
17 | |||
18 | /* | ||
19 | * CPUID functions returning a single datum | ||
20 | * | ||
21 | * Define unsigned int cpuid_e[abcd]x(unsigned int op) | ||
22 | */ | ||
23 | #define cpuid_func(reg) \ | ||
24 | unsigned int cpuid_##reg(unsigned int op) \ | ||
25 | { \ | ||
26 | unsigned int eax, ebx, ecx, edx; \ | ||
27 | __cpuid(op, eax, ebx, ecx, edx); \ | ||
28 | return reg; \ | ||
29 | } | ||
30 | cpuid_func(eax); | ||
31 | cpuid_func(ebx); | ||
32 | cpuid_func(ecx); | ||
33 | cpuid_func(edx); | ||
34 | |||
35 | #endif /* defined(__i386__) || defined(__x86_64__) */ | ||
36 | |||
37 | /* get_cpu_info | ||
38 | * | ||
39 | * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo | ||
40 | * | ||
41 | * Returns 0 on success or a negativ error code | ||
42 | * | ||
43 | * TBD: Should there be a cpuid alternative for this if /proc is not mounted? | ||
44 | */ | ||
45 | int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info) | ||
46 | { | ||
47 | FILE *fp; | ||
48 | char value[64]; | ||
49 | unsigned int proc, x; | ||
50 | unsigned int unknown = 0xffffff; | ||
51 | unsigned int cpuid_level, ext_cpuid_level; | ||
52 | |||
53 | int ret = -EINVAL; | ||
54 | |||
55 | cpu_info->vendor = X86_VENDOR_UNKNOWN; | ||
56 | cpu_info->family = unknown; | ||
57 | cpu_info->model = unknown; | ||
58 | cpu_info->stepping = unknown; | ||
59 | cpu_info->caps = 0; | ||
60 | |||
61 | fp = fopen("/proc/cpuinfo", "r"); | ||
62 | if (!fp) | ||
63 | return -EIO; | ||
64 | |||
65 | while (!feof(fp)) { | ||
66 | if (!fgets(value, 64, fp)) | ||
67 | continue; | ||
68 | value[63 - 1] = '\0'; | ||
69 | |||
70 | if (!strncmp(value, "processor\t: ", 12)) | ||
71 | sscanf(value, "processor\t: %u", &proc); | ||
72 | |||
73 | if (proc != cpu) | ||
74 | continue; | ||
75 | |||
76 | /* Get CPU vendor */ | ||
77 | if (!strncmp(value, "vendor_id", 9)) { | ||
78 | for (x = 1; x < X86_VENDOR_MAX; x++) { | ||
79 | if (strstr(value, cpu_vendor_table[x])) | ||
80 | cpu_info->vendor = x; | ||
81 | } | ||
82 | /* Get CPU family, etc. */ | ||
83 | } else if (!strncmp(value, "cpu family\t: ", 13)) { | ||
84 | sscanf(value, "cpu family\t: %u", | ||
85 | &cpu_info->family); | ||
86 | } else if (!strncmp(value, "model\t\t: ", 9)) { | ||
87 | sscanf(value, "model\t\t: %u", | ||
88 | &cpu_info->model); | ||
89 | } else if (!strncmp(value, "stepping\t: ", 10)) { | ||
90 | sscanf(value, "stepping\t: %u", | ||
91 | &cpu_info->stepping); | ||
92 | |||
93 | /* Exit -> all values must have been set */ | ||
94 | if (cpu_info->vendor == X86_VENDOR_UNKNOWN || | ||
95 | cpu_info->family == unknown || | ||
96 | cpu_info->model == unknown || | ||
97 | cpu_info->stepping == unknown) { | ||
98 | ret = -EINVAL; | ||
99 | goto out; | ||
100 | } | ||
101 | |||
102 | ret = 0; | ||
103 | goto out; | ||
104 | } | ||
105 | } | ||
106 | ret = -ENODEV; | ||
107 | out: | ||
108 | fclose(fp); | ||
109 | /* Get some useful CPU capabilities from cpuid */ | ||
110 | if (cpu_info->vendor != X86_VENDOR_AMD && | ||
111 | cpu_info->vendor != X86_VENDOR_INTEL) | ||
112 | return ret; | ||
113 | |||
114 | cpuid_level = cpuid_eax(0); | ||
115 | ext_cpuid_level = cpuid_eax(0x80000000); | ||
116 | |||
117 | /* Invariant TSC */ | ||
118 | if (ext_cpuid_level >= 0x80000007 && | ||
119 | (cpuid_edx(0x80000007) & (1 << 8))) | ||
120 | cpu_info->caps |= CPUPOWER_CAP_INV_TSC; | ||
121 | |||
122 | /* Aperf/Mperf registers support */ | ||
123 | if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1)) | ||
124 | cpu_info->caps |= CPUPOWER_CAP_APERF; | ||
125 | |||
126 | /* AMD Boost state enable/disable register */ | ||
127 | if (cpu_info->vendor == X86_VENDOR_AMD) { | ||
128 | if (ext_cpuid_level >= 0x80000007 && | ||
129 | (cpuid_edx(0x80000007) & (1 << 9))) | ||
130 | cpu_info->caps |= CPUPOWER_CAP_AMD_CBP; | ||
131 | } | ||
132 | |||
133 | if (cpu_info->vendor == X86_VENDOR_INTEL) { | ||
134 | if (cpuid_level >= 6 && | ||
135 | (cpuid_eax(6) & (1 << 1))) | ||
136 | cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; | ||
137 | } | ||
138 | |||
139 | if (cpu_info->vendor == X86_VENDOR_INTEL) { | ||
140 | /* Intel's perf-bias MSR support */ | ||
141 | if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) | ||
142 | cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; | ||
143 | |||
144 | /* Intel's Turbo Ratio Limit support */ | ||
145 | if (cpu_info->family == 6) { | ||
146 | switch (cpu_info->model) { | ||
147 | case 0x1A: /* Core i7, Xeon 5500 series | ||
148 | * Bloomfield, Gainstown NHM-EP | ||
149 | */ | ||
150 | case 0x1E: /* Core i7 and i5 Processor | ||
151 | * Clarksfield, Lynnfield, Jasper Forest | ||
152 | */ | ||
153 | case 0x1F: /* Core i7 and i5 Processor - Nehalem */ | ||
154 | case 0x25: /* Westmere Client | ||
155 | * Clarkdale, Arrandale | ||
156 | */ | ||
157 | case 0x2C: /* Westmere EP - Gulftown */ | ||
158 | cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; | ||
159 | case 0x2A: /* SNB */ | ||
160 | case 0x2D: /* SNB Xeon */ | ||
161 | cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; | ||
162 | cpu_info->caps |= CPUPOWER_CAP_IS_SNB; | ||
163 | break; | ||
164 | case 0x2E: /* Nehalem-EX Xeon - Beckton */ | ||
165 | case 0x2F: /* Westmere-EX Xeon - Eagleton */ | ||
166 | default: | ||
167 | break; | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | |||
172 | /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", | ||
173 | cpuid_level, ext_cpuid_level, cpu_info->caps); | ||
174 | */ | ||
175 | return ret; | ||
176 | } | ||
diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h new file mode 100644 index 00000000000..2747e738efb --- /dev/null +++ b/tools/power/cpupower/utils/helpers/helpers.h | |||
@@ -0,0 +1,190 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | * Miscellaneous helpers which do not fit or are worth | ||
7 | * to put into separate headers | ||
8 | */ | ||
9 | |||
10 | #ifndef __CPUPOWERUTILS_HELPERS__ | ||
11 | #define __CPUPOWERUTILS_HELPERS__ | ||
12 | |||
13 | #include <libintl.h> | ||
14 | #include <locale.h> | ||
15 | |||
16 | #include "helpers/bitmask.h" | ||
17 | |||
18 | /* Internationalization ****************************/ | ||
19 | #ifdef NLS | ||
20 | |||
21 | #define _(String) gettext(String) | ||
22 | #ifndef gettext_noop | ||
23 | #define gettext_noop(String) String | ||
24 | #endif | ||
25 | #define N_(String) gettext_noop(String) | ||
26 | |||
27 | #else /* !NLS */ | ||
28 | |||
29 | #define _(String) String | ||
30 | #define N_(String) String | ||
31 | |||
32 | #endif | ||
33 | /* Internationalization ****************************/ | ||
34 | |||
35 | extern int run_as_root; | ||
36 | extern struct bitmask *cpus_chosen; | ||
37 | |||
38 | /* Global verbose (-d) stuff *********************************/ | ||
39 | /* | ||
40 | * define DEBUG via global Makefile variable | ||
41 | * Debug output is sent to stderr, do: | ||
42 | * cpupower monitor 2>/tmp/debug | ||
43 | * to split debug output away from normal output | ||
44 | */ | ||
45 | #ifdef DEBUG | ||
46 | extern int be_verbose; | ||
47 | |||
48 | #define dprint(fmt, ...) { \ | ||
49 | if (be_verbose) { \ | ||
50 | fprintf(stderr, "%s: " fmt, \ | ||
51 | __func__, ##__VA_ARGS__); \ | ||
52 | } \ | ||
53 | } | ||
54 | #else | ||
55 | static inline void dprint(const char *fmt, ...) { } | ||
56 | #endif | ||
57 | extern int be_verbose; | ||
58 | /* Global verbose (-v) stuff *********************************/ | ||
59 | |||
60 | /* cpuid and cpuinfo helpers **************************/ | ||
61 | enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL, | ||
62 | X86_VENDOR_AMD, X86_VENDOR_MAX}; | ||
63 | |||
64 | #define CPUPOWER_CAP_INV_TSC 0x00000001 | ||
65 | #define CPUPOWER_CAP_APERF 0x00000002 | ||
66 | #define CPUPOWER_CAP_AMD_CBP 0x00000004 | ||
67 | #define CPUPOWER_CAP_PERF_BIAS 0x00000008 | ||
68 | #define CPUPOWER_CAP_HAS_TURBO_RATIO 0x00000010 | ||
69 | #define CPUPOWER_CAP_IS_SNB 0x00000011 | ||
70 | #define CPUPOWER_CAP_INTEL_IDA 0x00000012 | ||
71 | |||
72 | #define MAX_HW_PSTATES 10 | ||
73 | |||
74 | struct cpupower_cpu_info { | ||
75 | enum cpupower_cpu_vendor vendor; | ||
76 | unsigned int family; | ||
77 | unsigned int model; | ||
78 | unsigned int stepping; | ||
79 | /* CPU capabilities read out from cpuid */ | ||
80 | unsigned long long caps; | ||
81 | }; | ||
82 | |||
83 | /* get_cpu_info | ||
84 | * | ||
85 | * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo | ||
86 | * | ||
87 | * Returns 0 on success or a negativ error code | ||
88 | * Only used on x86, below global's struct values are zero/unknown on | ||
89 | * other archs | ||
90 | */ | ||
91 | extern int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info); | ||
92 | extern struct cpupower_cpu_info cpupower_cpu_info; | ||
93 | /* cpuid and cpuinfo helpers **************************/ | ||
94 | |||
95 | |||
96 | /* CPU topology/hierarchy parsing ******************/ | ||
97 | struct cpupower_topology { | ||
98 | /* Amount of CPU cores, packages and threads per core in the system */ | ||
99 | unsigned int cores; | ||
100 | unsigned int pkgs; | ||
101 | unsigned int threads; /* per core */ | ||
102 | |||
103 | /* Array gets mallocated with cores entries, holding per core info */ | ||
104 | struct { | ||
105 | int pkg; | ||
106 | int core; | ||
107 | int cpu; | ||
108 | |||
109 | /* flags */ | ||
110 | unsigned int is_online:1; | ||
111 | } *core_info; | ||
112 | }; | ||
113 | |||
114 | extern int get_cpu_topology(struct cpupower_topology *cpu_top); | ||
115 | extern void cpu_topology_release(struct cpupower_topology cpu_top); | ||
116 | /* CPU topology/hierarchy parsing ******************/ | ||
117 | |||
118 | /* X86 ONLY ****************************************/ | ||
119 | #if defined(__i386__) || defined(__x86_64__) | ||
120 | |||
121 | #include <pci/pci.h> | ||
122 | |||
123 | /* Read/Write msr ****************************/ | ||
124 | extern int read_msr(int cpu, unsigned int idx, unsigned long long *val); | ||
125 | extern int write_msr(int cpu, unsigned int idx, unsigned long long val); | ||
126 | |||
127 | extern int msr_intel_set_perf_bias(unsigned int cpu, unsigned int val); | ||
128 | extern int msr_intel_get_perf_bias(unsigned int cpu); | ||
129 | extern unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu); | ||
130 | |||
131 | /* Read/Write msr ****************************/ | ||
132 | |||
133 | /* PCI stuff ****************************/ | ||
134 | extern int amd_pci_get_num_boost_states(int *active, int *states); | ||
135 | extern struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id, | ||
136 | int *dev_ids); | ||
137 | |||
138 | /* PCI stuff ****************************/ | ||
139 | |||
140 | /* AMD HW pstate decoding **************************/ | ||
141 | |||
142 | extern int decode_pstates(unsigned int cpu, unsigned int cpu_family, | ||
143 | int boost_states, unsigned long *pstates, int *no); | ||
144 | |||
145 | /* AMD HW pstate decoding **************************/ | ||
146 | |||
147 | extern int cpufreq_has_boost_support(unsigned int cpu, int *support, | ||
148 | int *active, int * states); | ||
149 | /* | ||
150 | * CPUID functions returning a single datum | ||
151 | */ | ||
152 | unsigned int cpuid_eax(unsigned int op); | ||
153 | unsigned int cpuid_ebx(unsigned int op); | ||
154 | unsigned int cpuid_ecx(unsigned int op); | ||
155 | unsigned int cpuid_edx(unsigned int op); | ||
156 | |||
157 | /* cpuid and cpuinfo helpers **************************/ | ||
158 | /* X86 ONLY ********************************************/ | ||
159 | #else | ||
160 | static inline int decode_pstates(unsigned int cpu, unsigned int cpu_family, | ||
161 | int boost_states, unsigned long *pstates, | ||
162 | int *no) | ||
163 | { return -1; }; | ||
164 | |||
165 | static inline int read_msr(int cpu, unsigned int idx, unsigned long long *val) | ||
166 | { return -1; }; | ||
167 | static inline int write_msr(int cpu, unsigned int idx, unsigned long long val) | ||
168 | { return -1; }; | ||
169 | static inline int msr_intel_set_perf_bias(unsigned int cpu, unsigned int val) | ||
170 | { return -1; }; | ||
171 | static inline int msr_intel_get_perf_bias(unsigned int cpu) | ||
172 | { return -1; }; | ||
173 | static inline unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu) | ||
174 | { return 0; }; | ||
175 | |||
176 | /* Read/Write msr ****************************/ | ||
177 | |||
178 | static inline int cpufreq_has_boost_support(unsigned int cpu, int *support, | ||
179 | int *active, int * states) | ||
180 | { return -1; } | ||
181 | |||
182 | /* cpuid and cpuinfo helpers **************************/ | ||
183 | |||
184 | static inline unsigned int cpuid_eax(unsigned int op) { return 0; }; | ||
185 | static inline unsigned int cpuid_ebx(unsigned int op) { return 0; }; | ||
186 | static inline unsigned int cpuid_ecx(unsigned int op) { return 0; }; | ||
187 | static inline unsigned int cpuid_edx(unsigned int op) { return 0; }; | ||
188 | #endif /* defined(__i386__) || defined(__x86_64__) */ | ||
189 | |||
190 | #endif /* __CPUPOWERUTILS_HELPERS__ */ | ||
diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c new file mode 100644 index 00000000000..1609243f5c6 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/misc.c | |||
@@ -0,0 +1,27 @@ | |||
1 | #if defined(__i386__) || defined(__x86_64__) | ||
2 | |||
3 | #include "helpers/helpers.h" | ||
4 | |||
5 | int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, | ||
6 | int *states) | ||
7 | { | ||
8 | struct cpupower_cpu_info cpu_info; | ||
9 | int ret; | ||
10 | |||
11 | *support = *active = *states = 0; | ||
12 | |||
13 | ret = get_cpu_info(0, &cpu_info); | ||
14 | if (ret) | ||
15 | return ret; | ||
16 | |||
17 | if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_CBP) { | ||
18 | *support = 1; | ||
19 | amd_pci_get_num_boost_states(active, states); | ||
20 | if (ret <= 0) | ||
21 | return ret; | ||
22 | *support = 1; | ||
23 | } else if (cpupower_cpu_info.caps & CPUPOWER_CAP_INTEL_IDA) | ||
24 | *support = *active = 1; | ||
25 | return 0; | ||
26 | } | ||
27 | #endif /* #if defined(__i386__) || defined(__x86_64__) */ | ||
diff --git a/tools/power/cpupower/utils/helpers/msr.c b/tools/power/cpupower/utils/helpers/msr.c new file mode 100644 index 00000000000..31a4b24a8bc --- /dev/null +++ b/tools/power/cpupower/utils/helpers/msr.c | |||
@@ -0,0 +1,115 @@ | |||
1 | #if defined(__i386__) || defined(__x86_64__) | ||
2 | |||
3 | #include <fcntl.h> | ||
4 | #include <stdio.h> | ||
5 | #include <unistd.h> | ||
6 | #include <stdint.h> | ||
7 | |||
8 | #include "helpers/helpers.h" | ||
9 | |||
10 | /* Intel specific MSRs */ | ||
11 | #define MSR_IA32_PERF_STATUS 0x198 | ||
12 | #define MSR_IA32_MISC_ENABLES 0x1a0 | ||
13 | #define MSR_IA32_ENERGY_PERF_BIAS 0x1b0 | ||
14 | #define MSR_NEHALEM_TURBO_RATIO_LIMIT 0x1ad | ||
15 | |||
16 | /* | ||
17 | * read_msr | ||
18 | * | ||
19 | * Will return 0 on success and -1 on failure. | ||
20 | * Possible errno values could be: | ||
21 | * EFAULT -If the read/write did not fully complete | ||
22 | * EIO -If the CPU does not support MSRs | ||
23 | * ENXIO -If the CPU does not exist | ||
24 | */ | ||
25 | |||
26 | int read_msr(int cpu, unsigned int idx, unsigned long long *val) | ||
27 | { | ||
28 | int fd; | ||
29 | char msr_file_name[64]; | ||
30 | |||
31 | sprintf(msr_file_name, "/dev/cpu/%d/msr", cpu); | ||
32 | fd = open(msr_file_name, O_RDONLY); | ||
33 | if (fd < 0) | ||
34 | return -1; | ||
35 | if (lseek(fd, idx, SEEK_CUR) == -1) | ||
36 | goto err; | ||
37 | if (read(fd, val, sizeof *val) != sizeof *val) | ||
38 | goto err; | ||
39 | close(fd); | ||
40 | return 0; | ||
41 | err: | ||
42 | close(fd); | ||
43 | return -1; | ||
44 | } | ||
45 | |||
46 | /* | ||
47 | * write_msr | ||
48 | * | ||
49 | * Will return 0 on success and -1 on failure. | ||
50 | * Possible errno values could be: | ||
51 | * EFAULT -If the read/write did not fully complete | ||
52 | * EIO -If the CPU does not support MSRs | ||
53 | * ENXIO -If the CPU does not exist | ||
54 | */ | ||
55 | int write_msr(int cpu, unsigned int idx, unsigned long long val) | ||
56 | { | ||
57 | int fd; | ||
58 | char msr_file_name[64]; | ||
59 | |||
60 | sprintf(msr_file_name, "/dev/cpu/%d/msr", cpu); | ||
61 | fd = open(msr_file_name, O_WRONLY); | ||
62 | if (fd < 0) | ||
63 | return -1; | ||
64 | if (lseek(fd, idx, SEEK_CUR) == -1) | ||
65 | goto err; | ||
66 | if (write(fd, &val, sizeof val) != sizeof val) | ||
67 | goto err; | ||
68 | close(fd); | ||
69 | return 0; | ||
70 | err: | ||
71 | close(fd); | ||
72 | return -1; | ||
73 | } | ||
74 | |||
75 | int msr_intel_get_perf_bias(unsigned int cpu) | ||
76 | { | ||
77 | unsigned long long val; | ||
78 | int ret; | ||
79 | |||
80 | if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS)) | ||
81 | return -1; | ||
82 | |||
83 | ret = read_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, &val); | ||
84 | if (ret) | ||
85 | return ret; | ||
86 | return val; | ||
87 | } | ||
88 | |||
89 | int msr_intel_set_perf_bias(unsigned int cpu, unsigned int val) | ||
90 | { | ||
91 | int ret; | ||
92 | |||
93 | if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS)) | ||
94 | return -1; | ||
95 | |||
96 | ret = write_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, val); | ||
97 | if (ret) | ||
98 | return ret; | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu) | ||
103 | { | ||
104 | unsigned long long val; | ||
105 | int ret; | ||
106 | |||
107 | if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_HAS_TURBO_RATIO)) | ||
108 | return -1; | ||
109 | |||
110 | ret = read_msr(cpu, MSR_NEHALEM_TURBO_RATIO_LIMIT, &val); | ||
111 | if (ret) | ||
112 | return ret; | ||
113 | return val; | ||
114 | } | ||
115 | #endif | ||
diff --git a/tools/power/cpupower/utils/helpers/pci.c b/tools/power/cpupower/utils/helpers/pci.c new file mode 100644 index 00000000000..cd2eb6fe41c --- /dev/null +++ b/tools/power/cpupower/utils/helpers/pci.c | |||
@@ -0,0 +1,44 @@ | |||
1 | #if defined(__i386__) || defined(__x86_64__) | ||
2 | |||
3 | #include <helpers/helpers.h> | ||
4 | |||
5 | /* | ||
6 | * pci_acc_init | ||
7 | * | ||
8 | * PCI access helper function depending on libpci | ||
9 | * | ||
10 | * **pacc : if a valid pci_dev is returned | ||
11 | * *pacc must be passed to pci_acc_cleanup to free it | ||
12 | * | ||
13 | * vendor_id : the pci vendor id matching the pci device to access | ||
14 | * dev_ids : device ids matching the pci device to access | ||
15 | * | ||
16 | * Returns : | ||
17 | * struct pci_dev which can be used with pci_{read,write}_* functions | ||
18 | * to access the PCI config space of matching pci devices | ||
19 | */ | ||
20 | struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id, | ||
21 | int *dev_ids) | ||
22 | { | ||
23 | struct pci_filter filter_nb_link = { -1, -1, -1, -1, vendor_id, 0}; | ||
24 | struct pci_dev *device; | ||
25 | unsigned int i; | ||
26 | |||
27 | *pacc = pci_alloc(); | ||
28 | if (*pacc == NULL) | ||
29 | return NULL; | ||
30 | |||
31 | pci_init(*pacc); | ||
32 | pci_scan_bus(*pacc); | ||
33 | |||
34 | for (i = 0; dev_ids[i] != 0; i++) { | ||
35 | filter_nb_link.device = dev_ids[i]; | ||
36 | for (device = (*pacc)->devices; device; device = device->next) { | ||
37 | if (pci_filter_match(&filter_nb_link, device)) | ||
38 | return device; | ||
39 | } | ||
40 | } | ||
41 | pci_cleanup(*pacc); | ||
42 | return NULL; | ||
43 | } | ||
44 | #endif /* defined(__i386__) || defined(__x86_64__) */ | ||
diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c new file mode 100644 index 00000000000..c6343024a61 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/sysfs.c | |||
@@ -0,0 +1,408 @@ | |||
1 | /* | ||
2 | * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> | ||
3 | * (C) 2011 Thomas Renninger <trenn@novell.com> Novell Inc. | ||
4 | * | ||
5 | * Licensed under the terms of the GNU GPL License version 2. | ||
6 | */ | ||
7 | |||
8 | #include <stdio.h> | ||
9 | #include <errno.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <string.h> | ||
12 | #include <sys/types.h> | ||
13 | #include <sys/stat.h> | ||
14 | #include <fcntl.h> | ||
15 | #include <unistd.h> | ||
16 | |||
17 | #include "helpers/sysfs.h" | ||
18 | |||
19 | unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) | ||
20 | { | ||
21 | int fd; | ||
22 | ssize_t numread; | ||
23 | |||
24 | fd = open(path, O_RDONLY); | ||
25 | if (fd == -1) | ||
26 | return 0; | ||
27 | |||
28 | numread = read(fd, buf, buflen - 1); | ||
29 | if (numread < 1) { | ||
30 | close(fd); | ||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | buf[numread] = '\0'; | ||
35 | close(fd); | ||
36 | |||
37 | return (unsigned int) numread; | ||
38 | } | ||
39 | |||
40 | static unsigned int sysfs_write_file(const char *path, | ||
41 | const char *value, size_t len) | ||
42 | { | ||
43 | int fd; | ||
44 | ssize_t numwrite; | ||
45 | |||
46 | fd = open(path, O_WRONLY); | ||
47 | if (fd == -1) | ||
48 | return 0; | ||
49 | |||
50 | numwrite = write(fd, value, len); | ||
51 | if (numwrite < 1) { | ||
52 | close(fd); | ||
53 | return 0; | ||
54 | } | ||
55 | close(fd); | ||
56 | return (unsigned int) numwrite; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Detect whether a CPU is online | ||
61 | * | ||
62 | * Returns: | ||
63 | * 1 -> if CPU is online | ||
64 | * 0 -> if CPU is offline | ||
65 | * negative errno values in error case | ||
66 | */ | ||
67 | int sysfs_is_cpu_online(unsigned int cpu) | ||
68 | { | ||
69 | char path[SYSFS_PATH_MAX]; | ||
70 | int fd; | ||
71 | ssize_t numread; | ||
72 | unsigned long long value; | ||
73 | char linebuf[MAX_LINE_LEN]; | ||
74 | char *endp; | ||
75 | struct stat statbuf; | ||
76 | |||
77 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu); | ||
78 | |||
79 | if (stat(path, &statbuf) != 0) | ||
80 | return 0; | ||
81 | |||
82 | /* | ||
83 | * kernel without CONFIG_HOTPLUG_CPU | ||
84 | * -> cpuX directory exists, but not cpuX/online file | ||
85 | */ | ||
86 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu); | ||
87 | if (stat(path, &statbuf) != 0) | ||
88 | return 1; | ||
89 | |||
90 | fd = open(path, O_RDONLY); | ||
91 | if (fd == -1) | ||
92 | return -errno; | ||
93 | |||
94 | numread = read(fd, linebuf, MAX_LINE_LEN - 1); | ||
95 | if (numread < 1) { | ||
96 | close(fd); | ||
97 | return -EIO; | ||
98 | } | ||
99 | linebuf[numread] = '\0'; | ||
100 | close(fd); | ||
101 | |||
102 | value = strtoull(linebuf, &endp, 0); | ||
103 | if (value > 1 || value < 0) | ||
104 | return -EINVAL; | ||
105 | |||
106 | return value; | ||
107 | } | ||
108 | |||
109 | /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ | ||
110 | |||
111 | /* | ||
112 | * helper function to read file from /sys into given buffer | ||
113 | * fname is a relative path under "cpuX/cpuidle/stateX/" dir | ||
114 | * cstates starting with 0, C0 is not counted as cstate. | ||
115 | * This means if you want C1 info, pass 0 as idlestate param | ||
116 | */ | ||
117 | unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate, | ||
118 | const char *fname, char *buf, size_t buflen) | ||
119 | { | ||
120 | char path[SYSFS_PATH_MAX]; | ||
121 | int fd; | ||
122 | ssize_t numread; | ||
123 | |||
124 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", | ||
125 | cpu, idlestate, fname); | ||
126 | |||
127 | fd = open(path, O_RDONLY); | ||
128 | if (fd == -1) | ||
129 | return 0; | ||
130 | |||
131 | numread = read(fd, buf, buflen - 1); | ||
132 | if (numread < 1) { | ||
133 | close(fd); | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | buf[numread] = '\0'; | ||
138 | close(fd); | ||
139 | |||
140 | return (unsigned int) numread; | ||
141 | } | ||
142 | |||
143 | /* read access to files which contain one numeric value */ | ||
144 | |||
145 | enum idlestate_value { | ||
146 | IDLESTATE_USAGE, | ||
147 | IDLESTATE_POWER, | ||
148 | IDLESTATE_LATENCY, | ||
149 | IDLESTATE_TIME, | ||
150 | MAX_IDLESTATE_VALUE_FILES | ||
151 | }; | ||
152 | |||
153 | static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = { | ||
154 | [IDLESTATE_USAGE] = "usage", | ||
155 | [IDLESTATE_POWER] = "power", | ||
156 | [IDLESTATE_LATENCY] = "latency", | ||
157 | [IDLESTATE_TIME] = "time", | ||
158 | }; | ||
159 | |||
160 | static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu, | ||
161 | unsigned int idlestate, | ||
162 | enum idlestate_value which) | ||
163 | { | ||
164 | unsigned long long value; | ||
165 | unsigned int len; | ||
166 | char linebuf[MAX_LINE_LEN]; | ||
167 | char *endp; | ||
168 | |||
169 | if (which >= MAX_IDLESTATE_VALUE_FILES) | ||
170 | return 0; | ||
171 | |||
172 | len = sysfs_idlestate_read_file(cpu, idlestate, | ||
173 | idlestate_value_files[which], | ||
174 | linebuf, sizeof(linebuf)); | ||
175 | if (len == 0) | ||
176 | return 0; | ||
177 | |||
178 | value = strtoull(linebuf, &endp, 0); | ||
179 | |||
180 | if (endp == linebuf || errno == ERANGE) | ||
181 | return 0; | ||
182 | |||
183 | return value; | ||
184 | } | ||
185 | |||
186 | /* read access to files which contain one string */ | ||
187 | |||
188 | enum idlestate_string { | ||
189 | IDLESTATE_DESC, | ||
190 | IDLESTATE_NAME, | ||
191 | MAX_IDLESTATE_STRING_FILES | ||
192 | }; | ||
193 | |||
194 | static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { | ||
195 | [IDLESTATE_DESC] = "desc", | ||
196 | [IDLESTATE_NAME] = "name", | ||
197 | }; | ||
198 | |||
199 | |||
200 | static char *sysfs_idlestate_get_one_string(unsigned int cpu, | ||
201 | unsigned int idlestate, | ||
202 | enum idlestate_string which) | ||
203 | { | ||
204 | char linebuf[MAX_LINE_LEN]; | ||
205 | char *result; | ||
206 | unsigned int len; | ||
207 | |||
208 | if (which >= MAX_IDLESTATE_STRING_FILES) | ||
209 | return NULL; | ||
210 | |||
211 | len = sysfs_idlestate_read_file(cpu, idlestate, | ||
212 | idlestate_string_files[which], | ||
213 | linebuf, sizeof(linebuf)); | ||
214 | if (len == 0) | ||
215 | return NULL; | ||
216 | |||
217 | result = strdup(linebuf); | ||
218 | if (result == NULL) | ||
219 | return NULL; | ||
220 | |||
221 | if (result[strlen(result) - 1] == '\n') | ||
222 | result[strlen(result) - 1] = '\0'; | ||
223 | |||
224 | return result; | ||
225 | } | ||
226 | |||
227 | unsigned long sysfs_get_idlestate_latency(unsigned int cpu, | ||
228 | unsigned int idlestate) | ||
229 | { | ||
230 | return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); | ||
231 | } | ||
232 | |||
233 | unsigned long sysfs_get_idlestate_usage(unsigned int cpu, | ||
234 | unsigned int idlestate) | ||
235 | { | ||
236 | return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE); | ||
237 | } | ||
238 | |||
239 | unsigned long long sysfs_get_idlestate_time(unsigned int cpu, | ||
240 | unsigned int idlestate) | ||
241 | { | ||
242 | return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME); | ||
243 | } | ||
244 | |||
245 | char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate) | ||
246 | { | ||
247 | return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME); | ||
248 | } | ||
249 | |||
250 | char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate) | ||
251 | { | ||
252 | return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC); | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * Returns number of supported C-states of CPU core cpu | ||
257 | * Negativ in error case | ||
258 | * Zero if cpuidle does not export any C-states | ||
259 | */ | ||
260 | int sysfs_get_idlestate_count(unsigned int cpu) | ||
261 | { | ||
262 | char file[SYSFS_PATH_MAX]; | ||
263 | struct stat statbuf; | ||
264 | int idlestates = 1; | ||
265 | |||
266 | |||
267 | snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); | ||
268 | if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) | ||
269 | return -ENODEV; | ||
270 | |||
271 | snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); | ||
272 | if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) | ||
273 | return 0; | ||
274 | |||
275 | while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { | ||
276 | snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU | ||
277 | "cpu%u/cpuidle/state%d", cpu, idlestates); | ||
278 | idlestates++; | ||
279 | } | ||
280 | idlestates--; | ||
281 | return idlestates; | ||
282 | } | ||
283 | |||
284 | /* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ | ||
285 | |||
286 | /* | ||
287 | * helper function to read file from /sys into given buffer | ||
288 | * fname is a relative path under "cpu/cpuidle/" dir | ||
289 | */ | ||
290 | static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, | ||
291 | size_t buflen) | ||
292 | { | ||
293 | char path[SYSFS_PATH_MAX]; | ||
294 | |||
295 | snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); | ||
296 | |||
297 | return sysfs_read_file(path, buf, buflen); | ||
298 | } | ||
299 | |||
300 | |||
301 | |||
302 | /* read access to files which contain one string */ | ||
303 | |||
304 | enum cpuidle_string { | ||
305 | CPUIDLE_GOVERNOR, | ||
306 | CPUIDLE_GOVERNOR_RO, | ||
307 | CPUIDLE_DRIVER, | ||
308 | MAX_CPUIDLE_STRING_FILES | ||
309 | }; | ||
310 | |||
311 | static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { | ||
312 | [CPUIDLE_GOVERNOR] = "current_governor", | ||
313 | [CPUIDLE_GOVERNOR_RO] = "current_governor_ro", | ||
314 | [CPUIDLE_DRIVER] = "current_driver", | ||
315 | }; | ||
316 | |||
317 | |||
318 | static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which) | ||
319 | { | ||
320 | char linebuf[MAX_LINE_LEN]; | ||
321 | char *result; | ||
322 | unsigned int len; | ||
323 | |||
324 | if (which >= MAX_CPUIDLE_STRING_FILES) | ||
325 | return NULL; | ||
326 | |||
327 | len = sysfs_cpuidle_read_file(cpuidle_string_files[which], | ||
328 | linebuf, sizeof(linebuf)); | ||
329 | if (len == 0) | ||
330 | return NULL; | ||
331 | |||
332 | result = strdup(linebuf); | ||
333 | if (result == NULL) | ||
334 | return NULL; | ||
335 | |||
336 | if (result[strlen(result) - 1] == '\n') | ||
337 | result[strlen(result) - 1] = '\0'; | ||
338 | |||
339 | return result; | ||
340 | } | ||
341 | |||
342 | char *sysfs_get_cpuidle_governor(void) | ||
343 | { | ||
344 | char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); | ||
345 | if (!tmp) | ||
346 | return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); | ||
347 | else | ||
348 | return tmp; | ||
349 | } | ||
350 | |||
351 | char *sysfs_get_cpuidle_driver(void) | ||
352 | { | ||
353 | return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); | ||
354 | } | ||
355 | /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ | ||
356 | |||
357 | /* | ||
358 | * Get sched_mc or sched_smt settings | ||
359 | * Pass "mc" or "smt" as argument | ||
360 | * | ||
361 | * Returns negative value on failure | ||
362 | */ | ||
363 | int sysfs_get_sched(const char *smt_mc) | ||
364 | { | ||
365 | unsigned long value; | ||
366 | char linebuf[MAX_LINE_LEN]; | ||
367 | char *endp; | ||
368 | char path[SYSFS_PATH_MAX]; | ||
369 | |||
370 | if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc)) | ||
371 | return -EINVAL; | ||
372 | |||
373 | snprintf(path, sizeof(path), | ||
374 | PATH_TO_CPU "sched_%s_power_savings", smt_mc); | ||
375 | if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0) | ||
376 | return -1; | ||
377 | value = strtoul(linebuf, &endp, 0); | ||
378 | if (endp == linebuf || errno == ERANGE) | ||
379 | return -1; | ||
380 | return value; | ||
381 | } | ||
382 | |||
383 | /* | ||
384 | * Get sched_mc or sched_smt settings | ||
385 | * Pass "mc" or "smt" as argument | ||
386 | * | ||
387 | * Returns negative value on failure | ||
388 | */ | ||
389 | int sysfs_set_sched(const char *smt_mc, int val) | ||
390 | { | ||
391 | char linebuf[MAX_LINE_LEN]; | ||
392 | char path[SYSFS_PATH_MAX]; | ||
393 | struct stat statbuf; | ||
394 | |||
395 | if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc)) | ||
396 | return -EINVAL; | ||
397 | |||
398 | snprintf(path, sizeof(path), | ||
399 | PATH_TO_CPU "sched_%s_power_savings", smt_mc); | ||
400 | sprintf(linebuf, "%d", val); | ||
401 | |||
402 | if (stat(path, &statbuf) != 0) | ||
403 | return -ENODEV; | ||
404 | |||
405 | if (sysfs_write_file(path, linebuf, MAX_LINE_LEN) == 0) | ||
406 | return -1; | ||
407 | return 0; | ||
408 | } | ||
diff --git a/tools/power/cpupower/utils/helpers/sysfs.h b/tools/power/cpupower/utils/helpers/sysfs.h new file mode 100644 index 00000000000..8cb797bbceb --- /dev/null +++ b/tools/power/cpupower/utils/helpers/sysfs.h | |||
@@ -0,0 +1,30 @@ | |||
1 | #ifndef __CPUPOWER_HELPERS_SYSFS_H__ | ||
2 | #define __CPUPOWER_HELPERS_SYSFS_H__ | ||
3 | |||
4 | #define PATH_TO_CPU "/sys/devices/system/cpu/" | ||
5 | #define MAX_LINE_LEN 255 | ||
6 | #define SYSFS_PATH_MAX 255 | ||
7 | |||
8 | extern unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen); | ||
9 | |||
10 | extern int sysfs_is_cpu_online(unsigned int cpu); | ||
11 | |||
12 | extern unsigned long sysfs_get_idlestate_latency(unsigned int cpu, | ||
13 | unsigned int idlestate); | ||
14 | extern unsigned long sysfs_get_idlestate_usage(unsigned int cpu, | ||
15 | unsigned int idlestate); | ||
16 | extern unsigned long long sysfs_get_idlestate_time(unsigned int cpu, | ||
17 | unsigned int idlestate); | ||
18 | extern char *sysfs_get_idlestate_name(unsigned int cpu, | ||
19 | unsigned int idlestate); | ||
20 | extern char *sysfs_get_idlestate_desc(unsigned int cpu, | ||
21 | unsigned int idlestate); | ||
22 | extern int sysfs_get_idlestate_count(unsigned int cpu); | ||
23 | |||
24 | extern char *sysfs_get_cpuidle_governor(void); | ||
25 | extern char *sysfs_get_cpuidle_driver(void); | ||
26 | |||
27 | extern int sysfs_get_sched(const char *smt_mc); | ||
28 | extern int sysfs_set_sched(const char *smt_mc, int val); | ||
29 | |||
30 | #endif /* __CPUPOWER_HELPERS_SYSFS_H__ */ | ||
diff --git a/tools/power/cpupower/utils/helpers/topology.c b/tools/power/cpupower/utils/helpers/topology.c new file mode 100644 index 00000000000..4eae2c47ba4 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/topology.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | * ToDo: Needs to be done more properly for AMD/Intel specifics | ||
7 | */ | ||
8 | |||
9 | /* Helper struct for qsort, must be in sync with cpupower_topology.cpu_info */ | ||
10 | /* Be careful: Need to pass unsigned to the sort, so that offlined cores are | ||
11 | in the end, but double check for -1 for offlined cpus at other places */ | ||
12 | |||
13 | #include <stdlib.h> | ||
14 | #include <stdio.h> | ||
15 | #include <unistd.h> | ||
16 | #include <errno.h> | ||
17 | #include <fcntl.h> | ||
18 | |||
19 | #include <helpers/helpers.h> | ||
20 | #include <helpers/sysfs.h> | ||
21 | |||
22 | /* returns -1 on failure, 0 on success */ | ||
23 | int sysfs_topology_read_file(unsigned int cpu, const char *fname) | ||
24 | { | ||
25 | unsigned long value; | ||
26 | char linebuf[MAX_LINE_LEN]; | ||
27 | char *endp; | ||
28 | char path[SYSFS_PATH_MAX]; | ||
29 | |||
30 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s", | ||
31 | cpu, fname); | ||
32 | if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0) | ||
33 | return -1; | ||
34 | value = strtoul(linebuf, &endp, 0); | ||
35 | if (endp == linebuf || errno == ERANGE) | ||
36 | return -1; | ||
37 | return value; | ||
38 | } | ||
39 | |||
40 | struct cpuid_core_info { | ||
41 | unsigned int pkg; | ||
42 | unsigned int thread; | ||
43 | unsigned int cpu; | ||
44 | /* flags */ | ||
45 | unsigned int is_online:1; | ||
46 | }; | ||
47 | |||
48 | static int __compare(const void *t1, const void *t2) | ||
49 | { | ||
50 | struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1; | ||
51 | struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2; | ||
52 | if (top1->pkg < top2->pkg) | ||
53 | return -1; | ||
54 | else if (top1->pkg > top2->pkg) | ||
55 | return 1; | ||
56 | else if (top1->thread < top2->thread) | ||
57 | return -1; | ||
58 | else if (top1->thread > top2->thread) | ||
59 | return 1; | ||
60 | else if (top1->cpu < top2->cpu) | ||
61 | return -1; | ||
62 | else if (top1->cpu > top2->cpu) | ||
63 | return 1; | ||
64 | else | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * Returns amount of cpus, negative on error, cpu_top must be | ||
70 | * passed to cpu_topology_release to free resources | ||
71 | * | ||
72 | * Array is sorted after ->pkg, ->core, then ->cpu | ||
73 | */ | ||
74 | int get_cpu_topology(struct cpupower_topology *cpu_top) | ||
75 | { | ||
76 | int cpu, cpus = sysconf(_SC_NPROCESSORS_CONF); | ||
77 | |||
78 | cpu_top->core_info = malloc(sizeof(struct cpupower_topology) * cpus); | ||
79 | if (cpu_top->core_info == NULL) | ||
80 | return -ENOMEM; | ||
81 | cpu_top->pkgs = cpu_top->cores = 0; | ||
82 | for (cpu = 0; cpu < cpus; cpu++) { | ||
83 | cpu_top->core_info[cpu].cpu = cpu; | ||
84 | cpu_top->core_info[cpu].is_online = sysfs_is_cpu_online(cpu); | ||
85 | cpu_top->core_info[cpu].pkg = | ||
86 | sysfs_topology_read_file(cpu, "physical_package_id"); | ||
87 | if ((int)cpu_top->core_info[cpu].pkg != -1 && | ||
88 | cpu_top->core_info[cpu].pkg > cpu_top->pkgs) | ||
89 | cpu_top->pkgs = cpu_top->core_info[cpu].pkg; | ||
90 | cpu_top->core_info[cpu].core = | ||
91 | sysfs_topology_read_file(cpu, "core_id"); | ||
92 | } | ||
93 | cpu_top->pkgs++; | ||
94 | |||
95 | qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), | ||
96 | __compare); | ||
97 | |||
98 | /* Intel's cores count is not consecutively numbered, there may | ||
99 | * be a core_id of 3, but none of 2. Assume there always is 0 | ||
100 | * Get amount of cores by counting duplicates in a package | ||
101 | for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) { | ||
102 | if (cpu_top->core_info[cpu].core == 0) | ||
103 | cpu_top->cores++; | ||
104 | */ | ||
105 | return cpus; | ||
106 | } | ||
107 | |||
108 | void cpu_topology_release(struct cpupower_topology cpu_top) | ||
109 | { | ||
110 | free(cpu_top.core_info); | ||
111 | } | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c new file mode 100644 index 00000000000..202e555988b --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c | |||
@@ -0,0 +1,338 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | * PCI initialization based on example code from: | ||
7 | * Andreas Herrmann <andreas.herrmann3@amd.com> | ||
8 | */ | ||
9 | |||
10 | #if defined(__i386__) || defined(__x86_64__) | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <stdint.h> | ||
15 | #include <time.h> | ||
16 | #include <string.h> | ||
17 | |||
18 | #include <pci/pci.h> | ||
19 | |||
20 | #include "idle_monitor/cpupower-monitor.h" | ||
21 | #include "helpers/helpers.h" | ||
22 | |||
23 | /******** PCI parts could go into own file and get shared ***************/ | ||
24 | |||
25 | #define PCI_NON_PC0_OFFSET 0xb0 | ||
26 | #define PCI_PC1_OFFSET 0xb4 | ||
27 | #define PCI_PC6_OFFSET 0xb8 | ||
28 | |||
29 | #define PCI_MONITOR_ENABLE_REG 0xe0 | ||
30 | |||
31 | #define PCI_NON_PC0_ENABLE_BIT 0 | ||
32 | #define PCI_PC1_ENABLE_BIT 1 | ||
33 | #define PCI_PC6_ENABLE_BIT 2 | ||
34 | |||
35 | #define PCI_NBP1_STAT_OFFSET 0x98 | ||
36 | #define PCI_NBP1_ACTIVE_BIT 2 | ||
37 | #define PCI_NBP1_ENTERED_BIT 1 | ||
38 | |||
39 | #define PCI_NBP1_CAP_OFFSET 0x90 | ||
40 | #define PCI_NBP1_CAPABLE_BIT 31 | ||
41 | |||
42 | #define OVERFLOW_MS 343597 /* 32 bit register filled at 12500 HZ | ||
43 | (1 tick per 80ns) */ | ||
44 | |||
45 | enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1, | ||
46 | AMD_FAM14H_STATE_NUM}; | ||
47 | |||
48 | static int fam14h_get_count_percent(unsigned int self_id, double *percent, | ||
49 | unsigned int cpu); | ||
50 | static int fam14h_nbp1_count(unsigned int id, unsigned long long *count, | ||
51 | unsigned int cpu); | ||
52 | |||
53 | static cstate_t amd_fam14h_cstates[AMD_FAM14H_STATE_NUM] = { | ||
54 | { | ||
55 | .name = "!PC0", | ||
56 | .desc = N_("Package in sleep state (PC1 or deeper)"), | ||
57 | .id = NON_PC0, | ||
58 | .range = RANGE_PACKAGE, | ||
59 | .get_count_percent = fam14h_get_count_percent, | ||
60 | }, | ||
61 | { | ||
62 | .name = "PC1", | ||
63 | .desc = N_("Processor Package C1"), | ||
64 | .id = PC1, | ||
65 | .range = RANGE_PACKAGE, | ||
66 | .get_count_percent = fam14h_get_count_percent, | ||
67 | }, | ||
68 | { | ||
69 | .name = "PC6", | ||
70 | .desc = N_("Processor Package C6"), | ||
71 | .id = PC6, | ||
72 | .range = RANGE_PACKAGE, | ||
73 | .get_count_percent = fam14h_get_count_percent, | ||
74 | }, | ||
75 | { | ||
76 | .name = "NBP1", | ||
77 | .desc = N_("North Bridge P1 boolean counter (returns 0 or 1)"), | ||
78 | .id = NBP1, | ||
79 | .range = RANGE_PACKAGE, | ||
80 | .get_count = fam14h_nbp1_count, | ||
81 | }, | ||
82 | }; | ||
83 | |||
84 | static struct pci_access *pci_acc; | ||
85 | static int pci_vendor_id = 0x1022; | ||
86 | static int pci_dev_ids[2] = {0x1716, 0}; | ||
87 | static struct pci_dev *amd_fam14h_pci_dev; | ||
88 | |||
89 | static int nbp1_entered; | ||
90 | |||
91 | struct timespec start_time; | ||
92 | static unsigned long long timediff; | ||
93 | |||
94 | #ifdef DEBUG | ||
95 | struct timespec dbg_time; | ||
96 | long dbg_timediff; | ||
97 | #endif | ||
98 | |||
99 | static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM]; | ||
100 | static unsigned long long *current_count[AMD_FAM14H_STATE_NUM]; | ||
101 | |||
102 | static int amd_fam14h_get_pci_info(struct cstate *state, | ||
103 | unsigned int *pci_offset, | ||
104 | unsigned int *enable_bit, | ||
105 | unsigned int cpu) | ||
106 | { | ||
107 | switch (state->id) { | ||
108 | case NON_PC0: | ||
109 | *enable_bit = PCI_NON_PC0_ENABLE_BIT; | ||
110 | *pci_offset = PCI_NON_PC0_OFFSET; | ||
111 | break; | ||
112 | case PC1: | ||
113 | *enable_bit = PCI_PC1_ENABLE_BIT; | ||
114 | *pci_offset = PCI_PC1_OFFSET; | ||
115 | break; | ||
116 | case PC6: | ||
117 | *enable_bit = PCI_PC6_ENABLE_BIT; | ||
118 | *pci_offset = PCI_PC6_OFFSET; | ||
119 | break; | ||
120 | case NBP1: | ||
121 | *enable_bit = PCI_NBP1_ENTERED_BIT; | ||
122 | *pci_offset = PCI_NBP1_STAT_OFFSET; | ||
123 | break; | ||
124 | default: | ||
125 | return -1; | ||
126 | }; | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int amd_fam14h_init(cstate_t *state, unsigned int cpu) | ||
131 | { | ||
132 | int enable_bit, pci_offset, ret; | ||
133 | uint32_t val; | ||
134 | |||
135 | ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu); | ||
136 | if (ret) | ||
137 | return ret; | ||
138 | |||
139 | /* NBP1 needs extra treating -> write 1 to D18F6x98 bit 1 for init */ | ||
140 | if (state->id == NBP1) { | ||
141 | val = pci_read_long(amd_fam14h_pci_dev, pci_offset); | ||
142 | val |= 1 << enable_bit; | ||
143 | val = pci_write_long(amd_fam14h_pci_dev, pci_offset, val); | ||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | /* Enable monitor */ | ||
148 | val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG); | ||
149 | dprint("Init %s: read at offset: 0x%x val: %u\n", state->name, | ||
150 | PCI_MONITOR_ENABLE_REG, (unsigned int) val); | ||
151 | val |= 1 << enable_bit; | ||
152 | pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val); | ||
153 | |||
154 | dprint("Init %s: offset: 0x%x enable_bit: %d - val: %u (%u)\n", | ||
155 | state->name, PCI_MONITOR_ENABLE_REG, enable_bit, | ||
156 | (unsigned int) val, cpu); | ||
157 | |||
158 | /* Set counter to zero */ | ||
159 | pci_write_long(amd_fam14h_pci_dev, pci_offset, 0); | ||
160 | previous_count[state->id][cpu] = 0; | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static int amd_fam14h_disable(cstate_t *state, unsigned int cpu) | ||
166 | { | ||
167 | int enable_bit, pci_offset, ret; | ||
168 | uint32_t val; | ||
169 | |||
170 | ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu); | ||
171 | if (ret) | ||
172 | return ret; | ||
173 | |||
174 | val = pci_read_long(amd_fam14h_pci_dev, pci_offset); | ||
175 | dprint("%s: offset: 0x%x %u\n", state->name, pci_offset, val); | ||
176 | if (state->id == NBP1) { | ||
177 | /* was the bit whether NBP1 got entered set? */ | ||
178 | nbp1_entered = (val & (1 << PCI_NBP1_ACTIVE_BIT)) | | ||
179 | (val & (1 << PCI_NBP1_ENTERED_BIT)); | ||
180 | |||
181 | dprint("NBP1 was %sentered - 0x%x - enable_bit: " | ||
182 | "%d - pci_offset: 0x%x\n", | ||
183 | nbp1_entered ? "" : "not ", | ||
184 | val, enable_bit, pci_offset); | ||
185 | return ret; | ||
186 | } | ||
187 | current_count[state->id][cpu] = val; | ||
188 | |||
189 | dprint("%s: Current - %llu (%u)\n", state->name, | ||
190 | current_count[state->id][cpu], cpu); | ||
191 | dprint("%s: Previous - %llu (%u)\n", state->name, | ||
192 | previous_count[state->id][cpu], cpu); | ||
193 | |||
194 | val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG); | ||
195 | val &= ~(1 << enable_bit); | ||
196 | pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val); | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static int fam14h_nbp1_count(unsigned int id, unsigned long long *count, | ||
202 | unsigned int cpu) | ||
203 | { | ||
204 | if (id == NBP1) { | ||
205 | if (nbp1_entered) | ||
206 | *count = 1; | ||
207 | else | ||
208 | *count = 0; | ||
209 | return 0; | ||
210 | } | ||
211 | return -1; | ||
212 | } | ||
213 | static int fam14h_get_count_percent(unsigned int id, double *percent, | ||
214 | unsigned int cpu) | ||
215 | { | ||
216 | unsigned long diff; | ||
217 | |||
218 | if (id >= AMD_FAM14H_STATE_NUM) | ||
219 | return -1; | ||
220 | /* residency count in 80ns -> divide through 12.5 to get us residency */ | ||
221 | diff = current_count[id][cpu] - previous_count[id][cpu]; | ||
222 | |||
223 | if (timediff == 0) | ||
224 | *percent = 0.0; | ||
225 | else | ||
226 | *percent = 100.0 * diff / timediff / 12.5; | ||
227 | |||
228 | dprint("Timediff: %llu - res~: %lu us - percent: %.2f %%\n", | ||
229 | timediff, diff * 10 / 125, *percent); | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | static int amd_fam14h_start(void) | ||
235 | { | ||
236 | int num, cpu; | ||
237 | clock_gettime(CLOCK_REALTIME, &start_time); | ||
238 | for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) { | ||
239 | for (cpu = 0; cpu < cpu_count; cpu++) | ||
240 | amd_fam14h_init(&amd_fam14h_cstates[num], cpu); | ||
241 | } | ||
242 | #ifdef DEBUG | ||
243 | clock_gettime(CLOCK_REALTIME, &dbg_time); | ||
244 | dbg_timediff = timespec_diff_us(start_time, dbg_time); | ||
245 | dprint("Enabling counters took: %lu us\n", | ||
246 | dbg_timediff); | ||
247 | #endif | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static int amd_fam14h_stop(void) | ||
252 | { | ||
253 | int num, cpu; | ||
254 | struct timespec end_time; | ||
255 | |||
256 | clock_gettime(CLOCK_REALTIME, &end_time); | ||
257 | |||
258 | for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) { | ||
259 | for (cpu = 0; cpu < cpu_count; cpu++) | ||
260 | amd_fam14h_disable(&amd_fam14h_cstates[num], cpu); | ||
261 | } | ||
262 | #ifdef DEBUG | ||
263 | clock_gettime(CLOCK_REALTIME, &dbg_time); | ||
264 | dbg_timediff = timespec_diff_us(end_time, dbg_time); | ||
265 | dprint("Disabling counters took: %lu ns\n", dbg_timediff); | ||
266 | #endif | ||
267 | timediff = timespec_diff_us(start_time, end_time); | ||
268 | if (timediff / 1000 > OVERFLOW_MS) | ||
269 | print_overflow_err((unsigned int)timediff / 1000000, | ||
270 | OVERFLOW_MS / 1000); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int is_nbp1_capable(void) | ||
276 | { | ||
277 | uint32_t val; | ||
278 | val = pci_read_long(amd_fam14h_pci_dev, PCI_NBP1_CAP_OFFSET); | ||
279 | return val & (1 << 31); | ||
280 | } | ||
281 | |||
282 | struct cpuidle_monitor *amd_fam14h_register(void) | ||
283 | { | ||
284 | int num; | ||
285 | |||
286 | if (cpupower_cpu_info.vendor != X86_VENDOR_AMD) | ||
287 | return NULL; | ||
288 | |||
289 | if (cpupower_cpu_info.family == 0x14) { | ||
290 | if (cpu_count <= 0 || cpu_count > 2) { | ||
291 | fprintf(stderr, "AMD fam14h: Invalid cpu count: %d\n", | ||
292 | cpu_count); | ||
293 | return NULL; | ||
294 | } | ||
295 | } else | ||
296 | return NULL; | ||
297 | |||
298 | /* We do not alloc for nbp1 machine wide counter */ | ||
299 | for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) { | ||
300 | previous_count[num] = calloc(cpu_count, | ||
301 | sizeof(unsigned long long)); | ||
302 | current_count[num] = calloc(cpu_count, | ||
303 | sizeof(unsigned long long)); | ||
304 | } | ||
305 | |||
306 | amd_fam14h_pci_dev = pci_acc_init(&pci_acc, pci_vendor_id, pci_dev_ids); | ||
307 | if (amd_fam14h_pci_dev == NULL || pci_acc == NULL) | ||
308 | return NULL; | ||
309 | |||
310 | if (!is_nbp1_capable()) | ||
311 | amd_fam14h_monitor.hw_states_num = AMD_FAM14H_STATE_NUM - 1; | ||
312 | |||
313 | amd_fam14h_monitor.name_len = strlen(amd_fam14h_monitor.name); | ||
314 | return &amd_fam14h_monitor; | ||
315 | } | ||
316 | |||
317 | static void amd_fam14h_unregister(void) | ||
318 | { | ||
319 | int num; | ||
320 | for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) { | ||
321 | free(previous_count[num]); | ||
322 | free(current_count[num]); | ||
323 | } | ||
324 | pci_cleanup(pci_acc); | ||
325 | } | ||
326 | |||
327 | struct cpuidle_monitor amd_fam14h_monitor = { | ||
328 | .name = "Ontario", | ||
329 | .hw_states = amd_fam14h_cstates, | ||
330 | .hw_states_num = AMD_FAM14H_STATE_NUM, | ||
331 | .start = amd_fam14h_start, | ||
332 | .stop = amd_fam14h_stop, | ||
333 | .do_register = amd_fam14h_register, | ||
334 | .unregister = amd_fam14h_unregister, | ||
335 | .needs_root = 1, | ||
336 | .overflow_s = OVERFLOW_MS / 1000, | ||
337 | }; | ||
338 | #endif /* #if defined(__i386__) || defined(__x86_64__) */ | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c new file mode 100644 index 00000000000..bcd22a1a397 --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <stdio.h> | ||
9 | #include <stdlib.h> | ||
10 | #include <stdint.h> | ||
11 | #include <string.h> | ||
12 | #include <limits.h> | ||
13 | |||
14 | #include "helpers/sysfs.h" | ||
15 | #include "helpers/helpers.h" | ||
16 | #include "idle_monitor/cpupower-monitor.h" | ||
17 | |||
18 | #define CPUIDLE_STATES_MAX 10 | ||
19 | static cstate_t cpuidle_cstates[CPUIDLE_STATES_MAX]; | ||
20 | struct cpuidle_monitor cpuidle_sysfs_monitor; | ||
21 | |||
22 | static unsigned long long **previous_count; | ||
23 | static unsigned long long **current_count; | ||
24 | struct timespec start_time; | ||
25 | static unsigned long long timediff; | ||
26 | |||
27 | static int cpuidle_get_count_percent(unsigned int id, double *percent, | ||
28 | unsigned int cpu) | ||
29 | { | ||
30 | unsigned long long statediff = current_count[cpu][id] | ||
31 | - previous_count[cpu][id]; | ||
32 | dprint("%s: - diff: %llu - percent: %f (%u)\n", | ||
33 | cpuidle_cstates[id].name, timediff, *percent, cpu); | ||
34 | |||
35 | if (timediff == 0) | ||
36 | *percent = 0.0; | ||
37 | else | ||
38 | *percent = ((100.0 * statediff) / timediff); | ||
39 | |||
40 | dprint("%s: - timediff: %llu - statediff: %llu - percent: %f (%u)\n", | ||
41 | cpuidle_cstates[id].name, timediff, statediff, *percent, cpu); | ||
42 | |||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static int cpuidle_start(void) | ||
47 | { | ||
48 | int cpu, state; | ||
49 | clock_gettime(CLOCK_REALTIME, &start_time); | ||
50 | for (cpu = 0; cpu < cpu_count; cpu++) { | ||
51 | for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num; | ||
52 | state++) { | ||
53 | previous_count[cpu][state] = | ||
54 | sysfs_get_idlestate_time(cpu, state); | ||
55 | dprint("CPU %d - State: %d - Val: %llu\n", | ||
56 | cpu, state, previous_count[cpu][state]); | ||
57 | } | ||
58 | }; | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int cpuidle_stop(void) | ||
63 | { | ||
64 | int cpu, state; | ||
65 | struct timespec end_time; | ||
66 | clock_gettime(CLOCK_REALTIME, &end_time); | ||
67 | timediff = timespec_diff_us(start_time, end_time); | ||
68 | |||
69 | for (cpu = 0; cpu < cpu_count; cpu++) { | ||
70 | for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num; | ||
71 | state++) { | ||
72 | current_count[cpu][state] = | ||
73 | sysfs_get_idlestate_time(cpu, state); | ||
74 | dprint("CPU %d - State: %d - Val: %llu\n", | ||
75 | cpu, state, previous_count[cpu][state]); | ||
76 | } | ||
77 | }; | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | void fix_up_intel_idle_driver_name(char *tmp, int num) | ||
82 | { | ||
83 | /* fix up cpuidle name for intel idle driver */ | ||
84 | if (!strncmp(tmp, "NHM-", 4)) { | ||
85 | switch (num) { | ||
86 | case 1: | ||
87 | strcpy(tmp, "C1"); | ||
88 | break; | ||
89 | case 2: | ||
90 | strcpy(tmp, "C3"); | ||
91 | break; | ||
92 | case 3: | ||
93 | strcpy(tmp, "C6"); | ||
94 | break; | ||
95 | } | ||
96 | } else if (!strncmp(tmp, "SNB-", 4)) { | ||
97 | switch (num) { | ||
98 | case 1: | ||
99 | strcpy(tmp, "C1"); | ||
100 | break; | ||
101 | case 2: | ||
102 | strcpy(tmp, "C3"); | ||
103 | break; | ||
104 | case 3: | ||
105 | strcpy(tmp, "C6"); | ||
106 | break; | ||
107 | case 4: | ||
108 | strcpy(tmp, "C7"); | ||
109 | break; | ||
110 | } | ||
111 | } else if (!strncmp(tmp, "ATM-", 4)) { | ||
112 | switch (num) { | ||
113 | case 1: | ||
114 | strcpy(tmp, "C1"); | ||
115 | break; | ||
116 | case 2: | ||
117 | strcpy(tmp, "C2"); | ||
118 | break; | ||
119 | case 3: | ||
120 | strcpy(tmp, "C4"); | ||
121 | break; | ||
122 | case 4: | ||
123 | strcpy(tmp, "C6"); | ||
124 | break; | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | |||
129 | static struct cpuidle_monitor *cpuidle_register(void) | ||
130 | { | ||
131 | int num; | ||
132 | char *tmp; | ||
133 | |||
134 | /* Assume idle state count is the same for all CPUs */ | ||
135 | cpuidle_sysfs_monitor.hw_states_num = sysfs_get_idlestate_count(0); | ||
136 | |||
137 | if (cpuidle_sysfs_monitor.hw_states_num <= 0) | ||
138 | return NULL; | ||
139 | |||
140 | for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) { | ||
141 | tmp = sysfs_get_idlestate_name(0, num); | ||
142 | if (tmp == NULL) | ||
143 | continue; | ||
144 | |||
145 | fix_up_intel_idle_driver_name(tmp, num); | ||
146 | strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1); | ||
147 | free(tmp); | ||
148 | |||
149 | tmp = sysfs_get_idlestate_desc(0, num); | ||
150 | if (tmp == NULL) | ||
151 | continue; | ||
152 | strncpy(cpuidle_cstates[num].desc, tmp, CSTATE_DESC_LEN - 1); | ||
153 | free(tmp); | ||
154 | |||
155 | cpuidle_cstates[num].range = RANGE_THREAD; | ||
156 | cpuidle_cstates[num].id = num; | ||
157 | cpuidle_cstates[num].get_count_percent = | ||
158 | cpuidle_get_count_percent; | ||
159 | }; | ||
160 | |||
161 | /* Free this at program termination */ | ||
162 | previous_count = malloc(sizeof(long long *) * cpu_count); | ||
163 | current_count = malloc(sizeof(long long *) * cpu_count); | ||
164 | for (num = 0; num < cpu_count; num++) { | ||
165 | previous_count[num] = malloc(sizeof(long long) * | ||
166 | cpuidle_sysfs_monitor.hw_states_num); | ||
167 | current_count[num] = malloc(sizeof(long long) * | ||
168 | cpuidle_sysfs_monitor.hw_states_num); | ||
169 | } | ||
170 | |||
171 | cpuidle_sysfs_monitor.name_len = strlen(cpuidle_sysfs_monitor.name); | ||
172 | return &cpuidle_sysfs_monitor; | ||
173 | } | ||
174 | |||
175 | void cpuidle_unregister(void) | ||
176 | { | ||
177 | int num; | ||
178 | |||
179 | for (num = 0; num < cpu_count; num++) { | ||
180 | free(previous_count[num]); | ||
181 | free(current_count[num]); | ||
182 | } | ||
183 | free(previous_count); | ||
184 | free(current_count); | ||
185 | } | ||
186 | |||
187 | struct cpuidle_monitor cpuidle_sysfs_monitor = { | ||
188 | .name = "Idle_Stats", | ||
189 | .hw_states = cpuidle_cstates, | ||
190 | .start = cpuidle_start, | ||
191 | .stop = cpuidle_stop, | ||
192 | .do_register = cpuidle_register, | ||
193 | .unregister = cpuidle_unregister, | ||
194 | .needs_root = 0, | ||
195 | .overflow_s = UINT_MAX, | ||
196 | }; | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c new file mode 100644 index 00000000000..0d6571e418d --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c | |||
@@ -0,0 +1,440 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | * Output format inspired by Len Brown's <lenb@kernel.org> turbostat tool. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <unistd.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <string.h> | ||
15 | #include <time.h> | ||
16 | #include <signal.h> | ||
17 | #include <sys/types.h> | ||
18 | #include <sys/wait.h> | ||
19 | #include <libgen.h> | ||
20 | |||
21 | #include "idle_monitor/cpupower-monitor.h" | ||
22 | #include "idle_monitor/idle_monitors.h" | ||
23 | #include "helpers/helpers.h" | ||
24 | |||
25 | /* Define pointers to all monitors. */ | ||
26 | #define DEF(x) & x ## _monitor , | ||
27 | struct cpuidle_monitor *all_monitors[] = { | ||
28 | #include "idle_monitors.def" | ||
29 | 0 | ||
30 | }; | ||
31 | |||
32 | static struct cpuidle_monitor *monitors[MONITORS_MAX]; | ||
33 | static unsigned int avail_monitors; | ||
34 | |||
35 | static char *progname; | ||
36 | |||
37 | enum operation_mode_e { list = 1, show, show_all }; | ||
38 | static int mode; | ||
39 | static int interval = 1; | ||
40 | static char *show_monitors_param; | ||
41 | static struct cpupower_topology cpu_top; | ||
42 | |||
43 | /* ToDo: Document this in the manpage */ | ||
44 | static char range_abbr[RANGE_MAX] = { 'T', 'C', 'P', 'M', }; | ||
45 | |||
46 | static void print_wrong_arg_exit(void) | ||
47 | { | ||
48 | printf(_("invalid or unknown argument\n")); | ||
49 | exit(EXIT_FAILURE); | ||
50 | } | ||
51 | |||
52 | long long timespec_diff_us(struct timespec start, struct timespec end) | ||
53 | { | ||
54 | struct timespec temp; | ||
55 | if ((end.tv_nsec - start.tv_nsec) < 0) { | ||
56 | temp.tv_sec = end.tv_sec - start.tv_sec - 1; | ||
57 | temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; | ||
58 | } else { | ||
59 | temp.tv_sec = end.tv_sec - start.tv_sec; | ||
60 | temp.tv_nsec = end.tv_nsec - start.tv_nsec; | ||
61 | } | ||
62 | return (temp.tv_sec * 1000000) + (temp.tv_nsec / 1000); | ||
63 | } | ||
64 | |||
65 | void print_n_spaces(int n) | ||
66 | { | ||
67 | int x; | ||
68 | for (x = 0; x < n; x++) | ||
69 | printf(" "); | ||
70 | } | ||
71 | |||
72 | /* size of s must be at least n + 1 */ | ||
73 | int fill_string_with_spaces(char *s, int n) | ||
74 | { | ||
75 | int len = strlen(s); | ||
76 | if (len > n) | ||
77 | return -1; | ||
78 | for (; len < n; len++) | ||
79 | s[len] = ' '; | ||
80 | s[len] = '\0'; | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | void print_header(int topology_depth) | ||
85 | { | ||
86 | int unsigned mon; | ||
87 | int state, need_len, pr_mon_len; | ||
88 | cstate_t s; | ||
89 | char buf[128] = ""; | ||
90 | int percent_width = 4; | ||
91 | |||
92 | fill_string_with_spaces(buf, topology_depth * 5 - 1); | ||
93 | printf("%s|", buf); | ||
94 | |||
95 | for (mon = 0; mon < avail_monitors; mon++) { | ||
96 | pr_mon_len = 0; | ||
97 | need_len = monitors[mon]->hw_states_num * (percent_width + 3) | ||
98 | - 1; | ||
99 | if (mon != 0) { | ||
100 | printf("|| "); | ||
101 | need_len--; | ||
102 | } | ||
103 | sprintf(buf, "%s", monitors[mon]->name); | ||
104 | fill_string_with_spaces(buf, need_len); | ||
105 | printf("%s", buf); | ||
106 | } | ||
107 | printf("\n"); | ||
108 | |||
109 | if (topology_depth > 2) | ||
110 | printf("PKG |"); | ||
111 | if (topology_depth > 1) | ||
112 | printf("CORE|"); | ||
113 | if (topology_depth > 0) | ||
114 | printf("CPU |"); | ||
115 | |||
116 | for (mon = 0; mon < avail_monitors; mon++) { | ||
117 | if (mon != 0) | ||
118 | printf("|| "); | ||
119 | else | ||
120 | printf(" "); | ||
121 | for (state = 0; state < monitors[mon]->hw_states_num; state++) { | ||
122 | if (state != 0) | ||
123 | printf(" | "); | ||
124 | s = monitors[mon]->hw_states[state]; | ||
125 | sprintf(buf, "%s", s.name); | ||
126 | fill_string_with_spaces(buf, percent_width); | ||
127 | printf("%s", buf); | ||
128 | } | ||
129 | printf(" "); | ||
130 | } | ||
131 | printf("\n"); | ||
132 | } | ||
133 | |||
134 | |||
135 | void print_results(int topology_depth, int cpu) | ||
136 | { | ||
137 | unsigned int mon; | ||
138 | int state, ret; | ||
139 | double percent; | ||
140 | unsigned long long result; | ||
141 | cstate_t s; | ||
142 | |||
143 | /* Be careful CPUs may got resorted for pkg value do not just use cpu */ | ||
144 | if (!bitmask_isbitset(cpus_chosen, cpu_top.core_info[cpu].cpu)) | ||
145 | return; | ||
146 | |||
147 | if (topology_depth > 2) | ||
148 | printf("%4d|", cpu_top.core_info[cpu].pkg); | ||
149 | if (topology_depth > 1) | ||
150 | printf("%4d|", cpu_top.core_info[cpu].core); | ||
151 | if (topology_depth > 0) | ||
152 | printf("%4d|", cpu_top.core_info[cpu].cpu); | ||
153 | |||
154 | for (mon = 0; mon < avail_monitors; mon++) { | ||
155 | if (mon != 0) | ||
156 | printf("||"); | ||
157 | |||
158 | for (state = 0; state < monitors[mon]->hw_states_num; state++) { | ||
159 | if (state != 0) | ||
160 | printf("|"); | ||
161 | |||
162 | s = monitors[mon]->hw_states[state]; | ||
163 | |||
164 | if (s.get_count_percent) { | ||
165 | ret = s.get_count_percent(s.id, &percent, | ||
166 | cpu_top.core_info[cpu].cpu); | ||
167 | if (ret) | ||
168 | printf("******"); | ||
169 | else if (percent >= 100.0) | ||
170 | printf("%6.1f", percent); | ||
171 | else | ||
172 | printf("%6.2f", percent); | ||
173 | } else if (s.get_count) { | ||
174 | ret = s.get_count(s.id, &result, | ||
175 | cpu_top.core_info[cpu].cpu); | ||
176 | if (ret) | ||
177 | printf("******"); | ||
178 | else | ||
179 | printf("%6llu", result); | ||
180 | } else { | ||
181 | printf(_("Monitor %s, Counter %s has no count " | ||
182 | "function. Implementation error\n"), | ||
183 | monitors[mon]->name, s.name); | ||
184 | exit(EXIT_FAILURE); | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | /* | ||
189 | * The monitor could still provide useful data, for example | ||
190 | * AMD HW counters partly sit in PCI config space. | ||
191 | * It's up to the monitor plug-in to check .is_online, this one | ||
192 | * is just for additional info. | ||
193 | */ | ||
194 | if (!cpu_top.core_info[cpu].is_online) { | ||
195 | printf(_(" *is offline\n")); | ||
196 | return; | ||
197 | } else | ||
198 | printf("\n"); | ||
199 | } | ||
200 | |||
201 | |||
202 | /* param: string passed by -m param (The list of monitors to show) | ||
203 | * | ||
204 | * Monitors must have been registered already, matching monitors | ||
205 | * are picked out and available monitors array is overridden | ||
206 | * with matching ones | ||
207 | * | ||
208 | * Monitors get sorted in the same order the user passes them | ||
209 | */ | ||
210 | |||
211 | static void parse_monitor_param(char *param) | ||
212 | { | ||
213 | unsigned int num; | ||
214 | int mon, hits = 0; | ||
215 | char *tmp = param, *token; | ||
216 | struct cpuidle_monitor *tmp_mons[MONITORS_MAX]; | ||
217 | |||
218 | |||
219 | for (mon = 0; mon < MONITORS_MAX; mon++, tmp = NULL) { | ||
220 | token = strtok(tmp, ","); | ||
221 | if (token == NULL) | ||
222 | break; | ||
223 | if (strlen(token) >= MONITOR_NAME_LEN) { | ||
224 | printf(_("%s: max monitor name length" | ||
225 | " (%d) exceeded\n"), token, MONITOR_NAME_LEN); | ||
226 | continue; | ||
227 | } | ||
228 | |||
229 | for (num = 0; num < avail_monitors; num++) { | ||
230 | if (!strcmp(monitors[num]->name, token)) { | ||
231 | dprint("Found requested monitor: %s\n", token); | ||
232 | tmp_mons[hits] = monitors[num]; | ||
233 | hits++; | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | if (hits == 0) { | ||
238 | printf(_("No matching monitor found in %s, " | ||
239 | "try -l option\n"), param); | ||
240 | exit(EXIT_FAILURE); | ||
241 | } | ||
242 | /* Override detected/registerd monitors array with requested one */ | ||
243 | memcpy(monitors, tmp_mons, | ||
244 | sizeof(struct cpuidle_monitor *) * MONITORS_MAX); | ||
245 | avail_monitors = hits; | ||
246 | } | ||
247 | |||
248 | void list_monitors(void) | ||
249 | { | ||
250 | unsigned int mon; | ||
251 | int state; | ||
252 | cstate_t s; | ||
253 | |||
254 | for (mon = 0; mon < avail_monitors; mon++) { | ||
255 | printf(_("Monitor \"%s\" (%d states) - Might overflow after %u " | ||
256 | "s\n"), | ||
257 | monitors[mon]->name, monitors[mon]->hw_states_num, | ||
258 | monitors[mon]->overflow_s); | ||
259 | |||
260 | for (state = 0; state < monitors[mon]->hw_states_num; state++) { | ||
261 | s = monitors[mon]->hw_states[state]; | ||
262 | /* | ||
263 | * ToDo show more state capabilities: | ||
264 | * percent, time (granlarity) | ||
265 | */ | ||
266 | printf("%s\t[%c] -> %s\n", s.name, range_abbr[s.range], | ||
267 | gettext(s.desc)); | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | int fork_it(char **argv) | ||
273 | { | ||
274 | int status; | ||
275 | unsigned int num; | ||
276 | unsigned long long timediff; | ||
277 | pid_t child_pid; | ||
278 | struct timespec start, end; | ||
279 | |||
280 | child_pid = fork(); | ||
281 | clock_gettime(CLOCK_REALTIME, &start); | ||
282 | |||
283 | for (num = 0; num < avail_monitors; num++) | ||
284 | monitors[num]->start(); | ||
285 | |||
286 | if (!child_pid) { | ||
287 | /* child */ | ||
288 | execvp(argv[0], argv); | ||
289 | } else { | ||
290 | /* parent */ | ||
291 | if (child_pid == -1) { | ||
292 | perror("fork"); | ||
293 | exit(1); | ||
294 | } | ||
295 | |||
296 | signal(SIGINT, SIG_IGN); | ||
297 | signal(SIGQUIT, SIG_IGN); | ||
298 | if (waitpid(child_pid, &status, 0) == -1) { | ||
299 | perror("wait"); | ||
300 | exit(1); | ||
301 | } | ||
302 | } | ||
303 | clock_gettime(CLOCK_REALTIME, &end); | ||
304 | for (num = 0; num < avail_monitors; num++) | ||
305 | monitors[num]->stop(); | ||
306 | |||
307 | timediff = timespec_diff_us(start, end); | ||
308 | if (WIFEXITED(status)) | ||
309 | printf(_("%s took %.5f seconds and exited with status %d\n"), | ||
310 | argv[0], timediff / (1000.0 * 1000), | ||
311 | WEXITSTATUS(status)); | ||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | int do_interval_measure(int i) | ||
316 | { | ||
317 | unsigned int num; | ||
318 | |||
319 | for (num = 0; num < avail_monitors; num++) { | ||
320 | dprint("HW C-state residency monitor: %s - States: %d\n", | ||
321 | monitors[num]->name, monitors[num]->hw_states_num); | ||
322 | monitors[num]->start(); | ||
323 | } | ||
324 | sleep(i); | ||
325 | for (num = 0; num < avail_monitors; num++) | ||
326 | monitors[num]->stop(); | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static void cmdline(int argc, char *argv[]) | ||
332 | { | ||
333 | int opt; | ||
334 | progname = basename(argv[0]); | ||
335 | |||
336 | while ((opt = getopt(argc, argv, "+li:m:")) != -1) { | ||
337 | switch (opt) { | ||
338 | case 'l': | ||
339 | if (mode) | ||
340 | print_wrong_arg_exit(); | ||
341 | mode = list; | ||
342 | break; | ||
343 | case 'i': | ||
344 | /* only allow -i with -m or no option */ | ||
345 | if (mode && mode != show) | ||
346 | print_wrong_arg_exit(); | ||
347 | interval = atoi(optarg); | ||
348 | break; | ||
349 | case 'm': | ||
350 | if (mode) | ||
351 | print_wrong_arg_exit(); | ||
352 | mode = show; | ||
353 | show_monitors_param = optarg; | ||
354 | break; | ||
355 | default: | ||
356 | print_wrong_arg_exit(); | ||
357 | } | ||
358 | } | ||
359 | if (!mode) | ||
360 | mode = show_all; | ||
361 | } | ||
362 | |||
363 | int cmd_monitor(int argc, char **argv) | ||
364 | { | ||
365 | unsigned int num; | ||
366 | struct cpuidle_monitor *test_mon; | ||
367 | int cpu; | ||
368 | |||
369 | cmdline(argc, argv); | ||
370 | cpu_count = get_cpu_topology(&cpu_top); | ||
371 | if (cpu_count < 0) { | ||
372 | printf(_("Cannot read number of available processors\n")); | ||
373 | return EXIT_FAILURE; | ||
374 | } | ||
375 | |||
376 | /* Default is: monitor all CPUs */ | ||
377 | if (bitmask_isallclear(cpus_chosen)) | ||
378 | bitmask_setall(cpus_chosen); | ||
379 | |||
380 | dprint("System has up to %d CPU cores\n", cpu_count); | ||
381 | |||
382 | for (num = 0; all_monitors[num]; num++) { | ||
383 | dprint("Try to register: %s\n", all_monitors[num]->name); | ||
384 | test_mon = all_monitors[num]->do_register(); | ||
385 | if (test_mon) { | ||
386 | if (test_mon->needs_root && !run_as_root) { | ||
387 | fprintf(stderr, _("Available monitor %s needs " | ||
388 | "root access\n"), test_mon->name); | ||
389 | continue; | ||
390 | } | ||
391 | monitors[avail_monitors] = test_mon; | ||
392 | dprint("%s registered\n", all_monitors[num]->name); | ||
393 | avail_monitors++; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | if (avail_monitors == 0) { | ||
398 | printf(_("No HW Cstate monitors found\n")); | ||
399 | return 1; | ||
400 | } | ||
401 | |||
402 | if (mode == list) { | ||
403 | list_monitors(); | ||
404 | exit(EXIT_SUCCESS); | ||
405 | } | ||
406 | |||
407 | if (mode == show) | ||
408 | parse_monitor_param(show_monitors_param); | ||
409 | |||
410 | dprint("Packages: %d - Cores: %d - CPUs: %d\n", | ||
411 | cpu_top.pkgs, cpu_top.cores, cpu_count); | ||
412 | |||
413 | /* | ||
414 | * if any params left, it must be a command to fork | ||
415 | */ | ||
416 | if (argc - optind) | ||
417 | fork_it(argv + optind); | ||
418 | else | ||
419 | do_interval_measure(interval); | ||
420 | |||
421 | /* ToDo: Topology parsing needs fixing first to do | ||
422 | this more generically */ | ||
423 | if (cpu_top.pkgs > 1) | ||
424 | print_header(3); | ||
425 | else | ||
426 | print_header(1); | ||
427 | |||
428 | for (cpu = 0; cpu < cpu_count; cpu++) { | ||
429 | if (cpu_top.pkgs > 1) | ||
430 | print_results(3, cpu); | ||
431 | else | ||
432 | print_results(1, cpu); | ||
433 | } | ||
434 | |||
435 | for (num = 0; num < avail_monitors; num++) | ||
436 | monitors[num]->unregister(); | ||
437 | |||
438 | cpu_topology_release(cpu_top); | ||
439 | return 0; | ||
440 | } | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h new file mode 100644 index 00000000000..9312ee1f2db --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #ifndef __CPUIDLE_INFO_HW__ | ||
9 | #define __CPUIDLE_INFO_HW__ | ||
10 | |||
11 | #include <stdarg.h> | ||
12 | #include <time.h> | ||
13 | |||
14 | #include "idle_monitor/idle_monitors.h" | ||
15 | |||
16 | #define MONITORS_MAX 20 | ||
17 | #define MONITOR_NAME_LEN 20 | ||
18 | #define CSTATE_NAME_LEN 5 | ||
19 | #define CSTATE_DESC_LEN 60 | ||
20 | |||
21 | int cpu_count; | ||
22 | |||
23 | /* Hard to define the right names ...: */ | ||
24 | enum power_range_e { | ||
25 | RANGE_THREAD, /* Lowest in topology hierarcy, AMD: core, Intel: thread | ||
26 | kernel sysfs: cpu */ | ||
27 | RANGE_CORE, /* AMD: unit, Intel: core, kernel_sysfs: core_id */ | ||
28 | RANGE_PACKAGE, /* Package, processor socket */ | ||
29 | RANGE_MACHINE, /* Machine, platform wide */ | ||
30 | RANGE_MAX }; | ||
31 | |||
32 | typedef struct cstate { | ||
33 | int id; | ||
34 | enum power_range_e range; | ||
35 | char name[CSTATE_NAME_LEN]; | ||
36 | char desc[CSTATE_DESC_LEN]; | ||
37 | |||
38 | /* either provide a percentage or a general count */ | ||
39 | int (*get_count_percent)(unsigned int self_id, double *percent, | ||
40 | unsigned int cpu); | ||
41 | int (*get_count)(unsigned int self_id, unsigned long long *count, | ||
42 | unsigned int cpu); | ||
43 | } cstate_t; | ||
44 | |||
45 | struct cpuidle_monitor { | ||
46 | /* Name must not contain whitespaces */ | ||
47 | char name[MONITOR_NAME_LEN]; | ||
48 | int name_len; | ||
49 | int hw_states_num; | ||
50 | cstate_t *hw_states; | ||
51 | int (*start) (void); | ||
52 | int (*stop) (void); | ||
53 | struct cpuidle_monitor* (*do_register) (void); | ||
54 | void (*unregister)(void); | ||
55 | unsigned int overflow_s; | ||
56 | int needs_root; | ||
57 | }; | ||
58 | |||
59 | extern long long timespec_diff_us(struct timespec start, struct timespec end); | ||
60 | |||
61 | #define print_overflow_err(mes, ov) \ | ||
62 | { \ | ||
63 | fprintf(stderr, gettext("Measure took %u seconds, but registers could " \ | ||
64 | "overflow at %u seconds, results " \ | ||
65 | "could be inaccurate\n"), mes, ov); \ | ||
66 | } | ||
67 | |||
68 | #endif /* __CPUIDLE_INFO_HW__ */ | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/idle_monitors.def b/tools/power/cpupower/utils/idle_monitor/idle_monitors.def new file mode 100644 index 00000000000..e3f8d9b2b18 --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/idle_monitors.def | |||
@@ -0,0 +1,7 @@ | |||
1 | #if defined(__i386__) || defined(__x86_64__) | ||
2 | DEF(amd_fam14h) | ||
3 | DEF(intel_nhm) | ||
4 | DEF(intel_snb) | ||
5 | DEF(mperf) | ||
6 | #endif | ||
7 | DEF(cpuidle_sysfs) | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/idle_monitors.h b/tools/power/cpupower/utils/idle_monitor/idle_monitors.h new file mode 100644 index 00000000000..4fcdeb1e07e --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/idle_monitors.h | |||
@@ -0,0 +1,18 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | * Based on the idea from Michael Matz <matz@suse.de> | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #ifndef _CPUIDLE_IDLE_MONITORS_H_ | ||
11 | #define _CPUIDLE_IDLE_MONITORS_H_ | ||
12 | |||
13 | #define DEF(x) extern struct cpuidle_monitor x ##_monitor; | ||
14 | #include "idle_monitors.def" | ||
15 | #undef DEF | ||
16 | extern struct cpuidle_monitor *all_monitors[]; | ||
17 | |||
18 | #endif /* _CPUIDLE_IDLE_MONITORS_H_ */ | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c new file mode 100644 index 00000000000..5650ab5a2c2 --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c | |||
@@ -0,0 +1,338 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | */ | ||
6 | |||
7 | #if defined(__i386__) || defined(__x86_64__) | ||
8 | |||
9 | #include <stdio.h> | ||
10 | #include <stdint.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <string.h> | ||
13 | #include <limits.h> | ||
14 | |||
15 | #include <cpufreq.h> | ||
16 | |||
17 | #include "helpers/helpers.h" | ||
18 | #include "idle_monitor/cpupower-monitor.h" | ||
19 | |||
20 | #define MSR_APERF 0xE8 | ||
21 | #define MSR_MPERF 0xE7 | ||
22 | |||
23 | #define MSR_TSC 0x10 | ||
24 | |||
25 | #define MSR_AMD_HWCR 0xc0010015 | ||
26 | |||
27 | enum mperf_id { C0 = 0, Cx, AVG_FREQ, MPERF_CSTATE_COUNT }; | ||
28 | |||
29 | static int mperf_get_count_percent(unsigned int self_id, double *percent, | ||
30 | unsigned int cpu); | ||
31 | static int mperf_get_count_freq(unsigned int id, unsigned long long *count, | ||
32 | unsigned int cpu); | ||
33 | static struct timespec time_start, time_end; | ||
34 | |||
35 | static cstate_t mperf_cstates[MPERF_CSTATE_COUNT] = { | ||
36 | { | ||
37 | .name = "C0", | ||
38 | .desc = N_("Processor Core not idle"), | ||
39 | .id = C0, | ||
40 | .range = RANGE_THREAD, | ||
41 | .get_count_percent = mperf_get_count_percent, | ||
42 | }, | ||
43 | { | ||
44 | .name = "Cx", | ||
45 | .desc = N_("Processor Core in an idle state"), | ||
46 | .id = Cx, | ||
47 | .range = RANGE_THREAD, | ||
48 | .get_count_percent = mperf_get_count_percent, | ||
49 | }, | ||
50 | |||
51 | { | ||
52 | .name = "Freq", | ||
53 | .desc = N_("Average Frequency (including boost) in MHz"), | ||
54 | .id = AVG_FREQ, | ||
55 | .range = RANGE_THREAD, | ||
56 | .get_count = mperf_get_count_freq, | ||
57 | }, | ||
58 | }; | ||
59 | |||
60 | enum MAX_FREQ_MODE { MAX_FREQ_SYSFS, MAX_FREQ_TSC_REF }; | ||
61 | static int max_freq_mode; | ||
62 | /* | ||
63 | * The max frequency mperf is ticking at (in C0), either retrieved via: | ||
64 | * 1) calculated after measurements if we know TSC ticks at mperf/P0 frequency | ||
65 | * 2) cpufreq /sys/devices/.../cpu0/cpufreq/cpuinfo_max_freq at init time | ||
66 | * 1. Is preferred as it also works without cpufreq subsystem (e.g. on Xen) | ||
67 | */ | ||
68 | static unsigned long max_frequency; | ||
69 | |||
70 | static unsigned long long tsc_at_measure_start; | ||
71 | static unsigned long long tsc_at_measure_end; | ||
72 | static unsigned long long *mperf_previous_count; | ||
73 | static unsigned long long *aperf_previous_count; | ||
74 | static unsigned long long *mperf_current_count; | ||
75 | static unsigned long long *aperf_current_count; | ||
76 | |||
77 | /* valid flag for all CPUs. If a MSR read failed it will be zero */ | ||
78 | static int *is_valid; | ||
79 | |||
80 | static int mperf_get_tsc(unsigned long long *tsc) | ||
81 | { | ||
82 | int ret; | ||
83 | ret = read_msr(0, MSR_TSC, tsc); | ||
84 | if (ret) | ||
85 | dprint("Reading TSC MSR failed, returning %llu\n", *tsc); | ||
86 | return ret; | ||
87 | } | ||
88 | |||
89 | static int mperf_init_stats(unsigned int cpu) | ||
90 | { | ||
91 | unsigned long long val; | ||
92 | int ret; | ||
93 | |||
94 | ret = read_msr(cpu, MSR_APERF, &val); | ||
95 | aperf_previous_count[cpu] = val; | ||
96 | ret |= read_msr(cpu, MSR_MPERF, &val); | ||
97 | mperf_previous_count[cpu] = val; | ||
98 | is_valid[cpu] = !ret; | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int mperf_measure_stats(unsigned int cpu) | ||
104 | { | ||
105 | unsigned long long val; | ||
106 | int ret; | ||
107 | |||
108 | ret = read_msr(cpu, MSR_APERF, &val); | ||
109 | aperf_current_count[cpu] = val; | ||
110 | ret |= read_msr(cpu, MSR_MPERF, &val); | ||
111 | mperf_current_count[cpu] = val; | ||
112 | is_valid[cpu] = !ret; | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static int mperf_get_count_percent(unsigned int id, double *percent, | ||
118 | unsigned int cpu) | ||
119 | { | ||
120 | unsigned long long aperf_diff, mperf_diff, tsc_diff; | ||
121 | unsigned long long timediff; | ||
122 | |||
123 | if (!is_valid[cpu]) | ||
124 | return -1; | ||
125 | |||
126 | if (id != C0 && id != Cx) | ||
127 | return -1; | ||
128 | |||
129 | mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu]; | ||
130 | aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu]; | ||
131 | |||
132 | if (max_freq_mode == MAX_FREQ_TSC_REF) { | ||
133 | tsc_diff = tsc_at_measure_end - tsc_at_measure_start; | ||
134 | *percent = 100.0 * mperf_diff / tsc_diff; | ||
135 | dprint("%s: TSC Ref - mperf_diff: %llu, tsc_diff: %llu\n", | ||
136 | mperf_cstates[id].name, mperf_diff, tsc_diff); | ||
137 | } else if (max_freq_mode == MAX_FREQ_SYSFS) { | ||
138 | timediff = timespec_diff_us(time_start, time_end); | ||
139 | *percent = 100.0 * mperf_diff / timediff; | ||
140 | dprint("%s: MAXFREQ - mperf_diff: %llu, time_diff: %llu\n", | ||
141 | mperf_cstates[id].name, mperf_diff, timediff); | ||
142 | } else | ||
143 | return -1; | ||
144 | |||
145 | if (id == Cx) | ||
146 | *percent = 100.0 - *percent; | ||
147 | |||
148 | dprint("%s: previous: %llu - current: %llu - (%u)\n", | ||
149 | mperf_cstates[id].name, mperf_diff, aperf_diff, cpu); | ||
150 | dprint("%s: %f\n", mperf_cstates[id].name, *percent); | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int mperf_get_count_freq(unsigned int id, unsigned long long *count, | ||
155 | unsigned int cpu) | ||
156 | { | ||
157 | unsigned long long aperf_diff, mperf_diff, time_diff, tsc_diff; | ||
158 | |||
159 | if (id != AVG_FREQ) | ||
160 | return 1; | ||
161 | |||
162 | if (!is_valid[cpu]) | ||
163 | return -1; | ||
164 | |||
165 | mperf_diff = mperf_current_count[cpu] - mperf_previous_count[cpu]; | ||
166 | aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu]; | ||
167 | |||
168 | if (max_freq_mode == MAX_FREQ_TSC_REF) { | ||
169 | /* Calculate max_freq from TSC count */ | ||
170 | tsc_diff = tsc_at_measure_end - tsc_at_measure_start; | ||
171 | time_diff = timespec_diff_us(time_start, time_end); | ||
172 | max_frequency = tsc_diff / time_diff; | ||
173 | } | ||
174 | |||
175 | *count = max_frequency * ((double)aperf_diff / mperf_diff); | ||
176 | dprint("%s: Average freq based on %s maximum frequency:\n", | ||
177 | mperf_cstates[id].name, | ||
178 | (max_freq_mode == MAX_FREQ_TSC_REF) ? "TSC calculated" : "sysfs read"); | ||
179 | dprint("%max_frequency: %lu", max_frequency); | ||
180 | dprint("aperf_diff: %llu\n", aperf_diff); | ||
181 | dprint("mperf_diff: %llu\n", mperf_diff); | ||
182 | dprint("avg freq: %llu\n", *count); | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int mperf_start(void) | ||
187 | { | ||
188 | int cpu; | ||
189 | unsigned long long dbg; | ||
190 | |||
191 | clock_gettime(CLOCK_REALTIME, &time_start); | ||
192 | mperf_get_tsc(&tsc_at_measure_start); | ||
193 | |||
194 | for (cpu = 0; cpu < cpu_count; cpu++) | ||
195 | mperf_init_stats(cpu); | ||
196 | |||
197 | mperf_get_tsc(&dbg); | ||
198 | dprint("TSC diff: %llu\n", dbg - tsc_at_measure_start); | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int mperf_stop(void) | ||
203 | { | ||
204 | unsigned long long dbg; | ||
205 | int cpu; | ||
206 | |||
207 | for (cpu = 0; cpu < cpu_count; cpu++) | ||
208 | mperf_measure_stats(cpu); | ||
209 | |||
210 | mperf_get_tsc(&tsc_at_measure_end); | ||
211 | clock_gettime(CLOCK_REALTIME, &time_end); | ||
212 | |||
213 | mperf_get_tsc(&dbg); | ||
214 | dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * Mperf register is defined to tick at P0 (maximum) frequency | ||
221 | * | ||
222 | * Instead of reading out P0 which can be tricky to read out from HW, | ||
223 | * we use TSC counter if it reliably ticks at P0/mperf frequency. | ||
224 | * | ||
225 | * Still try to fall back to: | ||
226 | * /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq | ||
227 | * on older Intel HW without invariant TSC feature. | ||
228 | * Or on AMD machines where TSC does not tick at P0 (do not exist yet, but | ||
229 | * it's still double checked (MSR_AMD_HWCR)). | ||
230 | * | ||
231 | * On these machines the user would still get useful mperf | ||
232 | * stats when acpi-cpufreq driver is loaded. | ||
233 | */ | ||
234 | static int init_maxfreq_mode(void) | ||
235 | { | ||
236 | int ret; | ||
237 | unsigned long long hwcr; | ||
238 | unsigned long min; | ||
239 | |||
240 | if (!cpupower_cpu_info.caps & CPUPOWER_CAP_INV_TSC) | ||
241 | goto use_sysfs; | ||
242 | |||
243 | if (cpupower_cpu_info.vendor == X86_VENDOR_AMD) { | ||
244 | /* MSR_AMD_HWCR tells us whether TSC runs at P0/mperf | ||
245 | * freq. | ||
246 | * A test whether hwcr is accessable/available would be: | ||
247 | * (cpupower_cpu_info.family > 0x10 || | ||
248 | * cpupower_cpu_info.family == 0x10 && | ||
249 | * cpupower_cpu_info.model >= 0x2)) | ||
250 | * This should be the case for all aperf/mperf | ||
251 | * capable AMD machines and is therefore safe to test here. | ||
252 | * Compare with Linus kernel git commit: acf01734b1747b1ec4 | ||
253 | */ | ||
254 | ret = read_msr(0, MSR_AMD_HWCR, &hwcr); | ||
255 | /* | ||
256 | * If the MSR read failed, assume a Xen system that did | ||
257 | * not explicitly provide access to it and assume TSC works | ||
258 | */ | ||
259 | if (ret != 0) { | ||
260 | dprint("TSC read 0x%x failed - assume TSC working\n", | ||
261 | MSR_AMD_HWCR); | ||
262 | return 0; | ||
263 | } else if (1 & (hwcr >> 24)) { | ||
264 | max_freq_mode = MAX_FREQ_TSC_REF; | ||
265 | return 0; | ||
266 | } else { /* Use sysfs max frequency if available */ } | ||
267 | } else if (cpupower_cpu_info.vendor == X86_VENDOR_INTEL) { | ||
268 | /* | ||
269 | * On Intel we assume mperf (in C0) is ticking at same | ||
270 | * rate than TSC | ||
271 | */ | ||
272 | max_freq_mode = MAX_FREQ_TSC_REF; | ||
273 | return 0; | ||
274 | } | ||
275 | use_sysfs: | ||
276 | if (cpufreq_get_hardware_limits(0, &min, &max_frequency)) { | ||
277 | dprint("Cannot retrieve max freq from cpufreq kernel " | ||
278 | "subsystem\n"); | ||
279 | return -1; | ||
280 | } | ||
281 | max_freq_mode = MAX_FREQ_SYSFS; | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | /* | ||
286 | * This monitor provides: | ||
287 | * | ||
288 | * 1) Average frequency a CPU resided in | ||
289 | * This always works if the CPU has aperf/mperf capabilities | ||
290 | * | ||
291 | * 2) C0 and Cx (any sleep state) time a CPU resided in | ||
292 | * Works if mperf timer stops ticking in sleep states which | ||
293 | * seem to be the case on all current HW. | ||
294 | * Both is directly retrieved from HW registers and is independent | ||
295 | * from kernel statistics. | ||
296 | */ | ||
297 | struct cpuidle_monitor mperf_monitor; | ||
298 | struct cpuidle_monitor *mperf_register(void) | ||
299 | { | ||
300 | if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) | ||
301 | return NULL; | ||
302 | |||
303 | if (init_maxfreq_mode()) | ||
304 | return NULL; | ||
305 | |||
306 | /* Free this at program termination */ | ||
307 | is_valid = calloc(cpu_count, sizeof(int)); | ||
308 | mperf_previous_count = calloc(cpu_count, sizeof(unsigned long long)); | ||
309 | aperf_previous_count = calloc(cpu_count, sizeof(unsigned long long)); | ||
310 | mperf_current_count = calloc(cpu_count, sizeof(unsigned long long)); | ||
311 | aperf_current_count = calloc(cpu_count, sizeof(unsigned long long)); | ||
312 | |||
313 | mperf_monitor.name_len = strlen(mperf_monitor.name); | ||
314 | return &mperf_monitor; | ||
315 | } | ||
316 | |||
317 | void mperf_unregister(void) | ||
318 | { | ||
319 | free(mperf_previous_count); | ||
320 | free(aperf_previous_count); | ||
321 | free(mperf_current_count); | ||
322 | free(aperf_current_count); | ||
323 | free(is_valid); | ||
324 | } | ||
325 | |||
326 | struct cpuidle_monitor mperf_monitor = { | ||
327 | .name = "Mperf", | ||
328 | .hw_states_num = MPERF_CSTATE_COUNT, | ||
329 | .hw_states = mperf_cstates, | ||
330 | .start = mperf_start, | ||
331 | .stop = mperf_stop, | ||
332 | .do_register = mperf_register, | ||
333 | .unregister = mperf_unregister, | ||
334 | .needs_root = 1, | ||
335 | .overflow_s = 922000000 /* 922337203 seconds TSC overflow | ||
336 | at 20GHz */ | ||
337 | }; | ||
338 | #endif /* #if defined(__i386__) || defined(__x86_64__) */ | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/nhm_idle.c b/tools/power/cpupower/utils/idle_monitor/nhm_idle.c new file mode 100644 index 00000000000..d2a91dd0d56 --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/nhm_idle.c | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | * Based on Len Brown's <lenb@kernel.org> turbostat tool. | ||
7 | */ | ||
8 | |||
9 | #if defined(__i386__) || defined(__x86_64__) | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <stdint.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <string.h> | ||
15 | |||
16 | #include "helpers/helpers.h" | ||
17 | #include "idle_monitor/cpupower-monitor.h" | ||
18 | |||
19 | #define MSR_PKG_C3_RESIDENCY 0x3F8 | ||
20 | #define MSR_PKG_C6_RESIDENCY 0x3F9 | ||
21 | #define MSR_CORE_C3_RESIDENCY 0x3FC | ||
22 | #define MSR_CORE_C6_RESIDENCY 0x3FD | ||
23 | |||
24 | #define MSR_TSC 0x10 | ||
25 | |||
26 | #define NHM_CSTATE_COUNT 4 | ||
27 | |||
28 | enum intel_nhm_id { C3 = 0, C6, PC3, PC6, TSC = 0xFFFF }; | ||
29 | |||
30 | static int nhm_get_count_percent(unsigned int self_id, double *percent, | ||
31 | unsigned int cpu); | ||
32 | |||
33 | static cstate_t nhm_cstates[NHM_CSTATE_COUNT] = { | ||
34 | { | ||
35 | .name = "C3", | ||
36 | .desc = N_("Processor Core C3"), | ||
37 | .id = C3, | ||
38 | .range = RANGE_CORE, | ||
39 | .get_count_percent = nhm_get_count_percent, | ||
40 | }, | ||
41 | { | ||
42 | .name = "C6", | ||
43 | .desc = N_("Processor Core C6"), | ||
44 | .id = C6, | ||
45 | .range = RANGE_CORE, | ||
46 | .get_count_percent = nhm_get_count_percent, | ||
47 | }, | ||
48 | |||
49 | { | ||
50 | .name = "PC3", | ||
51 | .desc = N_("Processor Package C3"), | ||
52 | .id = PC3, | ||
53 | .range = RANGE_PACKAGE, | ||
54 | .get_count_percent = nhm_get_count_percent, | ||
55 | }, | ||
56 | { | ||
57 | .name = "PC6", | ||
58 | .desc = N_("Processor Package C6"), | ||
59 | .id = PC6, | ||
60 | .range = RANGE_PACKAGE, | ||
61 | .get_count_percent = nhm_get_count_percent, | ||
62 | }, | ||
63 | }; | ||
64 | |||
65 | static unsigned long long tsc_at_measure_start; | ||
66 | static unsigned long long tsc_at_measure_end; | ||
67 | static unsigned long long *previous_count[NHM_CSTATE_COUNT]; | ||
68 | static unsigned long long *current_count[NHM_CSTATE_COUNT]; | ||
69 | /* valid flag for all CPUs. If a MSR read failed it will be zero */ | ||
70 | static int *is_valid; | ||
71 | |||
72 | static int nhm_get_count(enum intel_nhm_id id, unsigned long long *val, | ||
73 | unsigned int cpu) | ||
74 | { | ||
75 | int msr; | ||
76 | |||
77 | switch (id) { | ||
78 | case C3: | ||
79 | msr = MSR_CORE_C3_RESIDENCY; | ||
80 | break; | ||
81 | case C6: | ||
82 | msr = MSR_CORE_C6_RESIDENCY; | ||
83 | break; | ||
84 | case PC3: | ||
85 | msr = MSR_PKG_C3_RESIDENCY; | ||
86 | break; | ||
87 | case PC6: | ||
88 | msr = MSR_PKG_C6_RESIDENCY; | ||
89 | break; | ||
90 | case TSC: | ||
91 | msr = MSR_TSC; | ||
92 | break; | ||
93 | default: | ||
94 | return -1; | ||
95 | }; | ||
96 | if (read_msr(cpu, msr, val)) | ||
97 | return -1; | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int nhm_get_count_percent(unsigned int id, double *percent, | ||
103 | unsigned int cpu) | ||
104 | { | ||
105 | *percent = 0.0; | ||
106 | |||
107 | if (!is_valid[cpu]) | ||
108 | return -1; | ||
109 | |||
110 | *percent = (100.0 * | ||
111 | (current_count[id][cpu] - previous_count[id][cpu])) / | ||
112 | (tsc_at_measure_end - tsc_at_measure_start); | ||
113 | |||
114 | dprint("%s: previous: %llu - current: %llu - (%u)\n", | ||
115 | nhm_cstates[id].name, previous_count[id][cpu], | ||
116 | current_count[id][cpu], cpu); | ||
117 | |||
118 | dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n", | ||
119 | nhm_cstates[id].name, | ||
120 | (unsigned long long) tsc_at_measure_end - tsc_at_measure_start, | ||
121 | current_count[id][cpu] - previous_count[id][cpu], | ||
122 | *percent, cpu); | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int nhm_start(void) | ||
128 | { | ||
129 | int num, cpu; | ||
130 | unsigned long long dbg, val; | ||
131 | |||
132 | nhm_get_count(TSC, &tsc_at_measure_start, 0); | ||
133 | |||
134 | for (num = 0; num < NHM_CSTATE_COUNT; num++) { | ||
135 | for (cpu = 0; cpu < cpu_count; cpu++) { | ||
136 | is_valid[cpu] = !nhm_get_count(num, &val, cpu); | ||
137 | previous_count[num][cpu] = val; | ||
138 | } | ||
139 | } | ||
140 | nhm_get_count(TSC, &dbg, 0); | ||
141 | dprint("TSC diff: %llu\n", dbg - tsc_at_measure_start); | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static int nhm_stop(void) | ||
146 | { | ||
147 | unsigned long long val; | ||
148 | unsigned long long dbg; | ||
149 | int num, cpu; | ||
150 | |||
151 | nhm_get_count(TSC, &tsc_at_measure_end, 0); | ||
152 | |||
153 | for (num = 0; num < NHM_CSTATE_COUNT; num++) { | ||
154 | for (cpu = 0; cpu < cpu_count; cpu++) { | ||
155 | is_valid[cpu] = !nhm_get_count(num, &val, cpu); | ||
156 | current_count[num][cpu] = val; | ||
157 | } | ||
158 | } | ||
159 | nhm_get_count(TSC, &dbg, 0); | ||
160 | dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end); | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | struct cpuidle_monitor intel_nhm_monitor; | ||
166 | |||
167 | struct cpuidle_monitor *intel_nhm_register(void) | ||
168 | { | ||
169 | int num; | ||
170 | |||
171 | if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL) | ||
172 | return NULL; | ||
173 | |||
174 | if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_INV_TSC)) | ||
175 | return NULL; | ||
176 | |||
177 | if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) | ||
178 | return NULL; | ||
179 | |||
180 | /* Free this at program termination */ | ||
181 | is_valid = calloc(cpu_count, sizeof(int)); | ||
182 | for (num = 0; num < NHM_CSTATE_COUNT; num++) { | ||
183 | previous_count[num] = calloc(cpu_count, | ||
184 | sizeof(unsigned long long)); | ||
185 | current_count[num] = calloc(cpu_count, | ||
186 | sizeof(unsigned long long)); | ||
187 | } | ||
188 | |||
189 | intel_nhm_monitor.name_len = strlen(intel_nhm_monitor.name); | ||
190 | return &intel_nhm_monitor; | ||
191 | } | ||
192 | |||
193 | void intel_nhm_unregister(void) | ||
194 | { | ||
195 | int num; | ||
196 | |||
197 | for (num = 0; num < NHM_CSTATE_COUNT; num++) { | ||
198 | free(previous_count[num]); | ||
199 | free(current_count[num]); | ||
200 | } | ||
201 | free(is_valid); | ||
202 | } | ||
203 | |||
204 | struct cpuidle_monitor intel_nhm_monitor = { | ||
205 | .name = "Nehalem", | ||
206 | .hw_states_num = NHM_CSTATE_COUNT, | ||
207 | .hw_states = nhm_cstates, | ||
208 | .start = nhm_start, | ||
209 | .stop = nhm_stop, | ||
210 | .do_register = intel_nhm_register, | ||
211 | .unregister = intel_nhm_unregister, | ||
212 | .needs_root = 1, | ||
213 | .overflow_s = 922000000 /* 922337203 seconds TSC overflow | ||
214 | at 20GHz */ | ||
215 | }; | ||
216 | #endif | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/snb_idle.c b/tools/power/cpupower/utils/idle_monitor/snb_idle.c new file mode 100644 index 00000000000..a1bc07cd53e --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/snb_idle.c | |||
@@ -0,0 +1,190 @@ | |||
1 | /* | ||
2 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2. | ||
5 | * | ||
6 | * Based on Len Brown's <lenb@kernel.org> turbostat tool. | ||
7 | */ | ||
8 | |||
9 | #if defined(__i386__) || defined(__x86_64__) | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <stdint.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <string.h> | ||
15 | |||
16 | #include "helpers/helpers.h" | ||
17 | #include "idle_monitor/cpupower-monitor.h" | ||
18 | |||
19 | #define MSR_PKG_C2_RESIDENCY 0x60D | ||
20 | #define MSR_PKG_C7_RESIDENCY 0x3FA | ||
21 | #define MSR_CORE_C7_RESIDENCY 0x3FE | ||
22 | |||
23 | #define MSR_TSC 0x10 | ||
24 | |||
25 | enum intel_snb_id { C7 = 0, PC2, PC7, SNB_CSTATE_COUNT, TSC = 0xFFFF }; | ||
26 | |||
27 | static int snb_get_count_percent(unsigned int self_id, double *percent, | ||
28 | unsigned int cpu); | ||
29 | |||
30 | static cstate_t snb_cstates[SNB_CSTATE_COUNT] = { | ||
31 | { | ||
32 | .name = "C7", | ||
33 | .desc = N_("Processor Core C7"), | ||
34 | .id = C7, | ||
35 | .range = RANGE_CORE, | ||
36 | .get_count_percent = snb_get_count_percent, | ||
37 | }, | ||
38 | { | ||
39 | .name = "PC2", | ||
40 | .desc = N_("Processor Package C2"), | ||
41 | .id = PC2, | ||
42 | .range = RANGE_PACKAGE, | ||
43 | .get_count_percent = snb_get_count_percent, | ||
44 | }, | ||
45 | { | ||
46 | .name = "PC7", | ||
47 | .desc = N_("Processor Package C7"), | ||
48 | .id = PC7, | ||
49 | .range = RANGE_PACKAGE, | ||
50 | .get_count_percent = snb_get_count_percent, | ||
51 | }, | ||
52 | }; | ||
53 | |||
54 | static unsigned long long tsc_at_measure_start; | ||
55 | static unsigned long long tsc_at_measure_end; | ||
56 | static unsigned long long *previous_count[SNB_CSTATE_COUNT]; | ||
57 | static unsigned long long *current_count[SNB_CSTATE_COUNT]; | ||
58 | /* valid flag for all CPUs. If a MSR read failed it will be zero */ | ||
59 | static int *is_valid; | ||
60 | |||
61 | static int snb_get_count(enum intel_snb_id id, unsigned long long *val, | ||
62 | unsigned int cpu) | ||
63 | { | ||
64 | int msr; | ||
65 | |||
66 | switch (id) { | ||
67 | case C7: | ||
68 | msr = MSR_CORE_C7_RESIDENCY; | ||
69 | break; | ||
70 | case PC2: | ||
71 | msr = MSR_PKG_C2_RESIDENCY; | ||
72 | break; | ||
73 | case PC7: | ||
74 | msr = MSR_PKG_C7_RESIDENCY; | ||
75 | break; | ||
76 | case TSC: | ||
77 | msr = MSR_TSC; | ||
78 | break; | ||
79 | default: | ||
80 | return -1; | ||
81 | }; | ||
82 | if (read_msr(cpu, msr, val)) | ||
83 | return -1; | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int snb_get_count_percent(unsigned int id, double *percent, | ||
88 | unsigned int cpu) | ||
89 | { | ||
90 | *percent = 0.0; | ||
91 | |||
92 | if (!is_valid[cpu]) | ||
93 | return -1; | ||
94 | |||
95 | *percent = (100.0 * | ||
96 | (current_count[id][cpu] - previous_count[id][cpu])) / | ||
97 | (tsc_at_measure_end - tsc_at_measure_start); | ||
98 | |||
99 | dprint("%s: previous: %llu - current: %llu - (%u)\n", | ||
100 | snb_cstates[id].name, previous_count[id][cpu], | ||
101 | current_count[id][cpu], cpu); | ||
102 | |||
103 | dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n", | ||
104 | snb_cstates[id].name, | ||
105 | (unsigned long long) tsc_at_measure_end - tsc_at_measure_start, | ||
106 | current_count[id][cpu] - previous_count[id][cpu], | ||
107 | *percent, cpu); | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int snb_start(void) | ||
113 | { | ||
114 | int num, cpu; | ||
115 | unsigned long long val; | ||
116 | |||
117 | for (num = 0; num < SNB_CSTATE_COUNT; num++) { | ||
118 | for (cpu = 0; cpu < cpu_count; cpu++) { | ||
119 | snb_get_count(num, &val, cpu); | ||
120 | previous_count[num][cpu] = val; | ||
121 | } | ||
122 | } | ||
123 | snb_get_count(TSC, &tsc_at_measure_start, 0); | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int snb_stop(void) | ||
128 | { | ||
129 | unsigned long long val; | ||
130 | int num, cpu; | ||
131 | |||
132 | snb_get_count(TSC, &tsc_at_measure_end, 0); | ||
133 | |||
134 | for (num = 0; num < SNB_CSTATE_COUNT; num++) { | ||
135 | for (cpu = 0; cpu < cpu_count; cpu++) { | ||
136 | is_valid[cpu] = !snb_get_count(num, &val, cpu); | ||
137 | current_count[num][cpu] = val; | ||
138 | } | ||
139 | } | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | struct cpuidle_monitor intel_snb_monitor; | ||
144 | |||
145 | static struct cpuidle_monitor *snb_register(void) | ||
146 | { | ||
147 | int num; | ||
148 | |||
149 | if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL | ||
150 | || cpupower_cpu_info.family != 6) | ||
151 | return NULL; | ||
152 | |||
153 | if (cpupower_cpu_info.model != 0x2A | ||
154 | && cpupower_cpu_info.model != 0x2D) | ||
155 | return NULL; | ||
156 | |||
157 | is_valid = calloc(cpu_count, sizeof(int)); | ||
158 | for (num = 0; num < SNB_CSTATE_COUNT; num++) { | ||
159 | previous_count[num] = calloc(cpu_count, | ||
160 | sizeof(unsigned long long)); | ||
161 | current_count[num] = calloc(cpu_count, | ||
162 | sizeof(unsigned long long)); | ||
163 | } | ||
164 | intel_snb_monitor.name_len = strlen(intel_snb_monitor.name); | ||
165 | return &intel_snb_monitor; | ||
166 | } | ||
167 | |||
168 | void snb_unregister(void) | ||
169 | { | ||
170 | int num; | ||
171 | free(is_valid); | ||
172 | for (num = 0; num < SNB_CSTATE_COUNT; num++) { | ||
173 | free(previous_count[num]); | ||
174 | free(current_count[num]); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | struct cpuidle_monitor intel_snb_monitor = { | ||
179 | .name = "SandyBridge", | ||
180 | .hw_states = snb_cstates, | ||
181 | .hw_states_num = SNB_CSTATE_COUNT, | ||
182 | .start = snb_start, | ||
183 | .stop = snb_stop, | ||
184 | .do_register = snb_register, | ||
185 | .unregister = snb_unregister, | ||
186 | .needs_root = 1, | ||
187 | .overflow_s = 922000000 /* 922337203 seconds TSC overflow | ||
188 | at 20GHz */ | ||
189 | }; | ||
190 | #endif /* defined(__i386__) || defined(__x86_64__) */ | ||
diff --git a/tools/power/cpupower/utils/version-gen.sh b/tools/power/cpupower/utils/version-gen.sh new file mode 100755 index 00000000000..5ec41c55699 --- /dev/null +++ b/tools/power/cpupower/utils/version-gen.sh | |||
@@ -0,0 +1,35 @@ | |||
1 | #!/bin/sh | ||
2 | # | ||
3 | # Script which prints out the version to use for building cpupowerutils. | ||
4 | # Must be called from tools/power/cpupower/ | ||
5 | # | ||
6 | # Heavily based on tools/perf/util/PERF-VERSION-GEN . | ||
7 | |||
8 | LF=' | ||
9 | ' | ||
10 | |||
11 | # First check if there is a .git to get the version from git describe | ||
12 | # otherwise try to get the version from the kernel makefile | ||
13 | if test -d ../../../.git -o -f ../../../.git && | ||
14 | VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && | ||
15 | case "$VN" in | ||
16 | *$LF*) (exit 1) ;; | ||
17 | v[0-9]*) | ||
18 | git update-index -q --refresh | ||
19 | test -z "$(git diff-index --name-only HEAD --)" || | ||
20 | VN="$VN-dirty" ;; | ||
21 | esac | ||
22 | then | ||
23 | VN=$(echo "$VN" | sed -e 's/-/./g'); | ||
24 | else | ||
25 | eval $(grep '^VERSION[[:space:]]*=' ../../../Makefile|tr -d ' ') | ||
26 | eval $(grep '^PATCHLEVEL[[:space:]]*=' ../../../Makefile|tr -d ' ') | ||
27 | eval $(grep '^SUBLEVEL[[:space:]]*=' ../../../Makefile|tr -d ' ') | ||
28 | eval $(grep '^EXTRAVERSION[[:space:]]*=' ../../../Makefile|tr -d ' ') | ||
29 | |||
30 | VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" | ||
31 | fi | ||
32 | |||
33 | VN=$(expr "$VN" : v*'\(.*\)') | ||
34 | |||
35 | echo $VN | ||