aboutsummaryrefslogtreecommitdiffstats
path: root/tools/power/cpupower/lib/sysfs.c
diff options
context:
space:
mode:
authorDominik Brodowski <linux@dominikbrodowski.net>2011-03-30 10:30:11 -0400
committerDominik Brodowski <linux@dominikbrodowski.net>2011-07-29 12:35:36 -0400
commit7fe2f6399a84760a9af8896ac152728250f82adb (patch)
treefa4bf236359b8d6d9f8d6ff823ddd3e839da5768 /tools/power/cpupower/lib/sysfs.c
parent02f8c6aee8df3cdc935e9bdd4f2d020306035dbe (diff)
cpupowerutils - cpufrequtils extended with quite some features
CPU power consumption vs performance tuning is no longer limited to CPU frequency switching anymore: deep sleep states, traditional dynamic frequency scaling and hidden turbo/boost frequencies are tied close together and depend on each other. The first two exist on different architectures like PPC, Itanium and ARM, the latter (so far) only on X86. On X86 the APU (CPU+GPU) will only run most efficiently if CPU and GPU has proper power management in place. Users and Developers want to have *one* tool to get an overview what their system supports and to monitor and debug CPU power management in detail. The tool should compile and work on as many architectures as possible. Once this tool stabilizes a bit, it is intended to replace the Intel-specific tools in tools/power/x86 Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'tools/power/cpupower/lib/sysfs.c')
-rw-r--r--tools/power/cpupower/lib/sysfs.c671
1 files changed, 671 insertions, 0 deletions
diff --git a/tools/power/cpupower/lib/sysfs.c b/tools/power/cpupower/lib/sysfs.c
new file mode 100644
index 000000000000..c9b061fe87b1
--- /dev/null
+++ b/tools/power/cpupower/lib/sysfs.c
@@ -0,0 +1,671 @@
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#include <stdio.h>
8#include <errno.h>
9#include <stdlib.h>
10#include <string.h>
11#include <limits.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <fcntl.h>
15#include <unistd.h>
16
17#include "cpufreq.h"
18
19#define PATH_TO_CPU "/sys/devices/system/cpu/"
20#define MAX_LINE_LEN 255
21#define SYSFS_PATH_MAX 255
22
23
24static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
25{
26 int fd;
27 size_t numread;
28
29 if ( ( fd = open(path, O_RDONLY) ) == -1 )
30 return 0;
31
32 numread = read(fd, buf, buflen - 1);
33 if ( numread < 1 )
34 {
35 close(fd);
36 return 0;
37 }
38
39 buf[numread] = '\0';
40 close(fd);
41
42 return numread;
43}
44
45
46/* CPUFREQ sysfs access **************************************************/
47
48/* helper function to read file from /sys into given buffer */
49/* fname is a relative path under "cpuX/cpufreq" dir */
50static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
51 char *buf, size_t buflen)
52{
53 char path[SYSFS_PATH_MAX];
54
55 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
56 cpu, fname);
57 return sysfs_read_file(path, buf, buflen);
58}
59
60/* helper function to write a new value to a /sys file */
61/* fname is a relative path under "cpuX/cpufreq" dir */
62static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
63 const char *fname,
64 const char *value, size_t len)
65{
66 char path[SYSFS_PATH_MAX];
67 int fd;
68 size_t numwrite;
69
70 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
71 cpu, fname);
72
73 if ( ( fd = open(path, O_WRONLY) ) == -1 )
74 return 0;
75
76 numwrite = write(fd, value, len);
77 if ( numwrite < 1 )
78 {
79 close(fd);
80 return 0;
81 }
82
83 close(fd);
84
85 return numwrite;
86}
87
88/* read access to files which contain one numeric value */
89
90enum cpufreq_value {
91 CPUINFO_CUR_FREQ,
92 CPUINFO_MIN_FREQ,
93 CPUINFO_MAX_FREQ,
94 CPUINFO_LATENCY,
95 SCALING_CUR_FREQ,
96 SCALING_MIN_FREQ,
97 SCALING_MAX_FREQ,
98 STATS_NUM_TRANSITIONS,
99 MAX_CPUFREQ_VALUE_READ_FILES
100};
101
102static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
103 [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
104 [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
105 [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
106 [CPUINFO_LATENCY] = "cpuinfo_transition_latency",
107 [SCALING_CUR_FREQ] = "scaling_cur_freq",
108 [SCALING_MIN_FREQ] = "scaling_min_freq",
109 [SCALING_MAX_FREQ] = "scaling_max_freq",
110 [STATS_NUM_TRANSITIONS] = "stats/total_trans"
111};
112
113
114static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
115 enum cpufreq_value which)
116{
117 unsigned long value;
118 unsigned int len;
119 char linebuf[MAX_LINE_LEN];
120 char *endp;
121
122 if ( which >= MAX_CPUFREQ_VALUE_READ_FILES )
123 return 0;
124
125 if ( ( len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which],
126 linebuf, sizeof(linebuf))) == 0 )
127 return 0;
128
129 value = strtoul(linebuf, &endp, 0);
130
131 if ( endp == linebuf || errno == ERANGE )
132 return 0;
133
134 return value;
135}
136
137/* read access to files which contain one string */
138
139enum cpufreq_string {
140 SCALING_DRIVER,
141 SCALING_GOVERNOR,
142 MAX_CPUFREQ_STRING_FILES
143};
144
145static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
146 [SCALING_DRIVER] = "scaling_driver",
147 [SCALING_GOVERNOR] = "scaling_governor",
148};
149
150
151static char * sysfs_cpufreq_get_one_string(unsigned int cpu,
152 enum cpufreq_string which)
153{
154 char linebuf[MAX_LINE_LEN];
155 char *result;
156 unsigned int len;
157
158 if (which >= MAX_CPUFREQ_STRING_FILES)
159 return NULL;
160
161 if ( ( len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
162 linebuf, sizeof(linebuf))) == 0 )
163 return NULL;
164
165 if ( ( result = strdup(linebuf) ) == NULL )
166 return NULL;
167
168 if (result[strlen(result) - 1] == '\n')
169 result[strlen(result) - 1] = '\0';
170
171 return result;
172}
173
174/* write access */
175
176enum cpufreq_write {
177 WRITE_SCALING_MIN_FREQ,
178 WRITE_SCALING_MAX_FREQ,
179 WRITE_SCALING_GOVERNOR,
180 WRITE_SCALING_SET_SPEED,
181 MAX_CPUFREQ_WRITE_FILES
182};
183
184static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
185 [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
186 [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
187 [WRITE_SCALING_GOVERNOR] = "scaling_governor",
188 [WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
189};
190
191static int sysfs_cpufreq_write_one_value(unsigned int cpu,
192 enum cpufreq_write which,
193 const char *new_value, size_t len)
194{
195 if (which >= MAX_CPUFREQ_WRITE_FILES)
196 return 0;
197
198 if ( sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
199 new_value, len) != len )
200 return -ENODEV;
201
202 return 0;
203};
204
205unsigned long sysfs_get_freq_kernel(unsigned int cpu)
206{
207 return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
208}
209
210unsigned long sysfs_get_freq_hardware(unsigned int cpu)
211{
212 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
213}
214
215unsigned long sysfs_get_freq_transition_latency(unsigned int cpu)
216{
217 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
218}
219
220int sysfs_get_freq_hardware_limits(unsigned int cpu,
221 unsigned long *min,
222 unsigned long *max)
223{
224 if ((!min) || (!max))
225 return -EINVAL;
226
227 *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
228 if (!*min)
229 return -ENODEV;
230
231 *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
232 if (!*max)
233 return -ENODEV;
234
235 return 0;
236}
237
238char * sysfs_get_freq_driver(unsigned int cpu) {
239 return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
240}
241
242struct cpufreq_policy * sysfs_get_freq_policy(unsigned int cpu) {
243 struct cpufreq_policy *policy;
244
245 policy = malloc(sizeof(struct cpufreq_policy));
246 if (!policy)
247 return NULL;
248
249 policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
250 if (!policy->governor) {
251 free(policy);
252 return NULL;
253 }
254 policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
255 policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
256 if ((!policy->min) || (!policy->max)) {
257 free(policy->governor);
258 free(policy);
259 return NULL;
260 }
261
262 return policy;
263}
264
265struct cpufreq_available_governors *
266sysfs_get_freq_available_governors(unsigned int cpu) {
267 struct cpufreq_available_governors *first = NULL;
268 struct cpufreq_available_governors *current = NULL;
269 char linebuf[MAX_LINE_LEN];
270 unsigned int pos, i;
271 unsigned int len;
272
273 if ( ( len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
274 linebuf, sizeof(linebuf))) == 0 )
275 {
276 return NULL;
277 }
278
279 pos = 0;
280 for ( i = 0; i < len; i++ )
281 {
282 if ( linebuf[i] == ' ' || linebuf[i] == '\n' )
283 {
284 if ( i - pos < 2 )
285 continue;
286 if ( current ) {
287 current->next = malloc(sizeof *current );
288 if ( ! current->next )
289 goto error_out;
290 current = current->next;
291 } else {
292 first = malloc( sizeof *first );
293 if ( ! first )
294 goto error_out;
295 current = first;
296 }
297 current->first = first;
298 current->next = NULL;
299
300 current->governor = malloc(i - pos + 1);
301 if ( ! current->governor )
302 goto error_out;
303
304 memcpy( current->governor, linebuf + pos, i - pos);
305 current->governor[i - pos] = '\0';
306 pos = i + 1;
307 }
308 }
309
310 return first;
311
312 error_out:
313 while ( first ) {
314 current = first->next;
315 if ( first->governor )
316 free( first->governor );
317 free( first );
318 first = current;
319 }
320 return NULL;
321}
322
323
324struct cpufreq_available_frequencies *
325sysfs_get_available_frequencies(unsigned int cpu) {
326 struct cpufreq_available_frequencies *first = NULL;
327 struct cpufreq_available_frequencies *current = NULL;
328 char one_value[SYSFS_PATH_MAX];
329 char linebuf[MAX_LINE_LEN];
330 unsigned int pos, i;
331 unsigned int len;
332
333 if ( ( len = sysfs_cpufreq_read_file(cpu,
334 "scaling_available_frequencies",
335 linebuf, sizeof(linebuf))) == 0 )
336 {
337 return NULL;
338 }
339
340 pos = 0;
341 for ( i = 0; i < len; i++ )
342 {
343 if ( linebuf[i] == ' ' || linebuf[i] == '\n' )
344 {
345 if ( i - pos < 2 )
346 continue;
347 if ( i - pos >= SYSFS_PATH_MAX )
348 goto error_out;
349 if ( current ) {
350 current->next = malloc(sizeof *current );
351 if ( ! current->next )
352 goto error_out;
353 current = current->next;
354 } else {
355 first = malloc(sizeof *first );
356 if ( ! first )
357 goto error_out;
358 current = first;
359 }
360 current->first = first;
361 current->next = NULL;
362
363 memcpy(one_value, linebuf + pos, i - pos);
364 one_value[i - pos] = '\0';
365 if ( sscanf(one_value, "%lu", &current->frequency) != 1 )
366 goto error_out;
367
368 pos = i + 1;
369 }
370 }
371
372 return first;
373
374 error_out:
375 while ( first ) {
376 current = first->next;
377 free(first);
378 first = current;
379 }
380 return NULL;
381}
382
383static struct cpufreq_affected_cpus * sysfs_get_cpu_list(unsigned int cpu,
384 const char *file) {
385 struct cpufreq_affected_cpus *first = NULL;
386 struct cpufreq_affected_cpus *current = NULL;
387 char one_value[SYSFS_PATH_MAX];
388 char linebuf[MAX_LINE_LEN];
389 unsigned int pos, i;
390 unsigned int len;
391
392 if ( ( len = sysfs_cpufreq_read_file(cpu, file, linebuf,
393 sizeof(linebuf))) == 0 )
394 {
395 return NULL;
396 }
397
398 pos = 0;
399 for ( i = 0; i < len; i++ )
400 {
401 if ( i == len || linebuf[i] == ' ' || linebuf[i] == '\n' )
402 {
403 if ( i - pos < 1 )
404 continue;
405 if ( i - pos >= SYSFS_PATH_MAX )
406 goto error_out;
407 if ( current ) {
408 current->next = malloc(sizeof *current);
409 if ( ! current->next )
410 goto error_out;
411 current = current->next;
412 } else {
413 first = malloc(sizeof *first);
414 if ( ! first )
415 goto error_out;
416 current = first;
417 }
418 current->first = first;
419 current->next = NULL;
420
421 memcpy(one_value, linebuf + pos, i - pos);
422 one_value[i - pos] = '\0';
423
424 if ( sscanf(one_value, "%u", &current->cpu) != 1 )
425 goto error_out;
426
427 pos = i + 1;
428 }
429 }
430
431 return first;
432
433 error_out:
434 while (first) {
435 current = first->next;
436 free(first);
437 first = current;
438 }
439 return NULL;
440}
441
442struct cpufreq_affected_cpus * sysfs_get_freq_affected_cpus(unsigned int cpu) {
443 return sysfs_get_cpu_list(cpu, "affected_cpus");
444}
445
446struct cpufreq_affected_cpus * sysfs_get_freq_related_cpus(unsigned int cpu) {
447 return sysfs_get_cpu_list(cpu, "related_cpus");
448}
449
450struct cpufreq_stats * sysfs_get_freq_stats(unsigned int cpu, unsigned long long *total_time) {
451 struct cpufreq_stats *first = NULL;
452 struct cpufreq_stats *current = NULL;
453 char one_value[SYSFS_PATH_MAX];
454 char linebuf[MAX_LINE_LEN];
455 unsigned int pos, i;
456 unsigned int len;
457
458 if ( ( len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
459 linebuf, sizeof(linebuf))) == 0 )
460 return NULL;
461
462 *total_time = 0;
463 pos = 0;
464 for ( i = 0; i < len; i++ )
465 {
466 if ( i == strlen(linebuf) || linebuf[i] == '\n' )
467 {
468 if ( i - pos < 2 )
469 continue;
470 if ( (i - pos) >= SYSFS_PATH_MAX )
471 goto error_out;
472 if ( current ) {
473 current->next = malloc(sizeof *current );
474 if ( ! current->next )
475 goto error_out;
476 current = current->next;
477 } else {
478 first = malloc(sizeof *first );
479 if ( ! first )
480 goto error_out;
481 current = first;
482 }
483 current->first = first;
484 current->next = NULL;
485
486 memcpy(one_value, linebuf + pos, i - pos);
487 one_value[i - pos] = '\0';
488 if ( sscanf(one_value, "%lu %llu", &current->frequency, &current->time_in_state) != 2 )
489 goto error_out;
490
491 *total_time = *total_time + current->time_in_state;
492 pos = i + 1;
493 }
494 }
495
496 return first;
497
498 error_out:
499 while ( first ) {
500 current = first->next;
501 free(first);
502 first = current;
503 }
504 return NULL;
505}
506
507unsigned long sysfs_get_freq_transitions(unsigned int cpu)
508{
509 return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
510}
511
512static int verify_gov(char *new_gov, char *passed_gov)
513{
514 unsigned int i, j=0;
515
516 if (!passed_gov || (strlen(passed_gov) > 19))
517 return -EINVAL;
518
519 strncpy(new_gov, passed_gov, 20);
520 for (i=0;i<20;i++) {
521 if (j) {
522 new_gov[i] = '\0';
523 continue;
524 }
525 if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z')) {
526 continue;
527 }
528 if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z')) {
529 continue;
530 }
531 if (new_gov[i] == '-') {
532 continue;
533 }
534 if (new_gov[i] == '_') {
535 continue;
536 }
537 if (new_gov[i] == '\0') {
538 j = 1;
539 continue;
540 }
541 return -EINVAL;
542 }
543 new_gov[19] = '\0';
544 return 0;
545}
546
547int sysfs_modify_freq_policy_governor(unsigned int cpu, char *governor)
548{
549 char new_gov[SYSFS_PATH_MAX];
550
551 if (!governor)
552 return -EINVAL;
553
554 if (verify_gov(new_gov, governor))
555 return -EINVAL;
556
557 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
558 new_gov, strlen(new_gov));
559};
560
561int sysfs_modify_freq_policy_max(unsigned int cpu, unsigned long max_freq)
562{
563 char value[SYSFS_PATH_MAX];
564
565 snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
566
567 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
568 value, strlen(value));
569};
570
571
572int sysfs_modify_freq_policy_min(unsigned int cpu, unsigned long min_freq)
573{
574 char value[SYSFS_PATH_MAX];
575
576 snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
577
578 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
579 value, strlen(value));
580};
581
582
583int sysfs_set_freq_policy(unsigned int cpu, struct cpufreq_policy *policy)
584{
585 char min[SYSFS_PATH_MAX];
586 char max[SYSFS_PATH_MAX];
587 char gov[SYSFS_PATH_MAX];
588 int ret;
589 unsigned long old_min;
590 int write_max_first;
591
592 if (!policy || !(policy->governor))
593 return -EINVAL;
594
595 if (policy->max < policy->min)
596 return -EINVAL;
597
598 if (verify_gov(gov, policy->governor))
599 return -EINVAL;
600
601 snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
602 snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
603
604 old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
605 write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
606
607 if (write_max_first) {
608 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
609 max, strlen(max));
610 if (ret)
611 return ret;
612 }
613
614 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
615 strlen(min));
616 if (ret)
617 return ret;
618
619 if (!write_max_first) {
620 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
621 max, strlen(max));
622 if (ret)
623 return ret;
624 }
625
626 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
627 gov, strlen(gov));
628}
629
630int sysfs_set_frequency(unsigned int cpu, unsigned long target_frequency) {
631 struct cpufreq_policy *pol = sysfs_get_freq_policy(cpu);
632 char userspace_gov[] = "userspace";
633 char freq[SYSFS_PATH_MAX];
634 int ret;
635
636 if (!pol)
637 return -ENODEV;
638
639 if (strncmp(pol->governor, userspace_gov, 9) != 0) {
640 ret = sysfs_modify_freq_policy_governor(cpu, userspace_gov);
641 if (ret) {
642 cpufreq_put_policy(pol);
643 return (ret);
644 }
645 }
646
647 cpufreq_put_policy(pol);
648
649 snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
650
651 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
652 freq, strlen(freq));
653}
654
655/* CPUFREQ sysfs access **************************************************/
656
657/* General sysfs access **************************************************/
658int sysfs_cpu_exists(unsigned int cpu)
659{
660 char file[SYSFS_PATH_MAX];
661 struct stat statbuf;
662
663 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/", cpu);
664
665 if ( stat(file, &statbuf) != 0 )
666 return -ENOSYS;
667
668 return S_ISDIR(statbuf.st_mode) ? 0 : -ENOSYS;
669}
670
671/* General sysfs access **************************************************/