Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sysfs.c
Go to the documentation of this file.
1 /*
2  * (C) 2004-2009 Dominik Brodowski <[email protected]>
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 4096
21 #define SYSFS_PATH_MAX 255
22 
23 
24 static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
25 {
26  int fd;
27  ssize_t numread;
28 
29  fd = open(path, O_RDONLY);
30  if (fd == -1)
31  return 0;
32 
33  numread = read(fd, buf, buflen - 1);
34  if (numread < 1) {
35  close(fd);
36  return 0;
37  }
38 
39  buf[numread] = '\0';
40  close(fd);
41 
42  return (unsigned int) 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 */
50 static 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 */
62 static 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  ssize_t numwrite;
69 
70  snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
71  cpu, fname);
72 
73  fd = open(path, O_WRONLY);
74  if (fd == -1)
75  return 0;
76 
77  numwrite = write(fd, value, len);
78  if (numwrite < 1) {
79  close(fd);
80  return 0;
81  }
82 
83  close(fd);
84 
85  return (unsigned int) numwrite;
86 }
87 
88 /* read access to files which contain one numeric value */
89 
100 };
101 
102 static 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 
114 static 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  len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which],
126  linebuf, sizeof(linebuf));
127 
128  if (len == 0)
129  return 0;
130 
131  value = strtoul(linebuf, &endp, 0);
132 
133  if (endp == linebuf || errno == ERANGE)
134  return 0;
135 
136  return value;
137 }
138 
139 /* read access to files which contain one string */
140 
145 };
146 
147 static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
148  [SCALING_DRIVER] = "scaling_driver",
149  [SCALING_GOVERNOR] = "scaling_governor",
150 };
151 
152 
153 static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
154  enum cpufreq_string which)
155 {
156  char linebuf[MAX_LINE_LEN];
157  char *result;
158  unsigned int len;
159 
160  if (which >= MAX_CPUFREQ_STRING_FILES)
161  return NULL;
162 
163  len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
164  linebuf, sizeof(linebuf));
165  if (len == 0)
166  return NULL;
167 
168  result = strdup(linebuf);
169  if (result == NULL)
170  return NULL;
171 
172  if (result[strlen(result) - 1] == '\n')
173  result[strlen(result) - 1] = '\0';
174 
175  return result;
176 }
177 
178 /* write access */
179 
186 };
187 
188 static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
189  [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
190  [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
191  [WRITE_SCALING_GOVERNOR] = "scaling_governor",
192  [WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
193 };
194 
195 static int sysfs_cpufreq_write_one_value(unsigned int cpu,
196  enum cpufreq_write which,
197  const char *new_value, size_t len)
198 {
199  if (which >= MAX_CPUFREQ_WRITE_FILES)
200  return 0;
201 
202  if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
203  new_value, len) != len)
204  return -ENODEV;
205 
206  return 0;
207 };
208 
209 unsigned long sysfs_get_freq_kernel(unsigned int cpu)
210 {
211  return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
212 }
213 
214 unsigned long sysfs_get_freq_hardware(unsigned int cpu)
215 {
216  return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
217 }
218 
219 unsigned long sysfs_get_freq_transition_latency(unsigned int cpu)
220 {
221  return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
222 }
223 
224 int sysfs_get_freq_hardware_limits(unsigned int cpu,
225  unsigned long *min,
226  unsigned long *max)
227 {
228  if ((!min) || (!max))
229  return -EINVAL;
230 
231  *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
232  if (!*min)
233  return -ENODEV;
234 
235  *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
236  if (!*max)
237  return -ENODEV;
238 
239  return 0;
240 }
241 
242 char *sysfs_get_freq_driver(unsigned int cpu)
243 {
244  return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
245 }
246 
247 struct cpufreq_policy *sysfs_get_freq_policy(unsigned int cpu)
248 {
249  struct cpufreq_policy *policy;
250 
251  policy = malloc(sizeof(struct cpufreq_policy));
252  if (!policy)
253  return NULL;
254 
255  policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
256  if (!policy->governor) {
257  free(policy);
258  return NULL;
259  }
260  policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
261  policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
262  if ((!policy->min) || (!policy->max)) {
263  free(policy->governor);
264  free(policy);
265  return NULL;
266  }
267 
268  return policy;
269 }
270 
275  char linebuf[MAX_LINE_LEN];
276  unsigned int pos, i;
277  unsigned int len;
278 
279  len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
280  linebuf, sizeof(linebuf));
281  if (len == 0)
282  return NULL;
283 
284  pos = 0;
285  for (i = 0; i < len; i++) {
286  if (linebuf[i] == ' ' || linebuf[i] == '\n') {
287  if (i - pos < 2)
288  continue;
289  if (current) {
290  current->next = malloc(sizeof(*current));
291  if (!current->next)
292  goto error_out;
293  current = current->next;
294  } else {
295  first = malloc(sizeof(*first));
296  if (!first)
297  goto error_out;
298  current = first;
299  }
300  current->first = first;
301  current->next = NULL;
302 
303  current->governor = malloc(i - pos + 1);
304  if (!current->governor)
305  goto error_out;
306 
307  memcpy(current->governor, linebuf + pos, i - pos);
308  current->governor[i - pos] = '\0';
309  pos = i + 1;
310  }
311  }
312 
313  return first;
314 
315  error_out:
316  while (first) {
317  current = first->next;
318  if (first->governor)
319  free(first->governor);
320  free(first);
321  first = current;
322  }
323  return NULL;
324 }
325 
326 
331  char one_value[SYSFS_PATH_MAX];
332  char linebuf[MAX_LINE_LEN];
333  unsigned int pos, i;
334  unsigned int len;
335 
336  len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
337  linebuf, sizeof(linebuf));
338  if (len == 0)
339  return NULL;
340 
341  pos = 0;
342  for (i = 0; i < len; i++) {
343  if (linebuf[i] == ' ' || linebuf[i] == '\n') {
344  if (i - pos < 2)
345  continue;
346  if (i - pos >= SYSFS_PATH_MAX)
347  goto error_out;
348  if (current) {
349  current->next = malloc(sizeof(*current));
350  if (!current->next)
351  goto error_out;
352  current = current->next;
353  } else {
354  first = malloc(sizeof(*first));
355  if (!first)
356  goto error_out;
357  current = first;
358  }
359  current->first = first;
360  current->next = NULL;
361 
362  memcpy(one_value, linebuf + pos, i - pos);
363  one_value[i - pos] = '\0';
364  if (sscanf(one_value, "%lu", &current->frequency) != 1)
365  goto error_out;
366 
367  pos = i + 1;
368  }
369  }
370 
371  return first;
372 
373  error_out:
374  while (first) {
375  current = first->next;
376  free(first);
377  first = current;
378  }
379  return NULL;
380 }
381 
382 static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
383  const char *file)
384 {
385  struct cpufreq_affected_cpus *first = 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  len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
393  if (len == 0)
394  return NULL;
395 
396  pos = 0;
397  for (i = 0; i < len; i++) {
398  if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
399  if (i - pos < 1)
400  continue;
401  if (i - pos >= SYSFS_PATH_MAX)
402  goto error_out;
403  if (current) {
404  current->next = malloc(sizeof(*current));
405  if (!current->next)
406  goto error_out;
407  current = current->next;
408  } else {
409  first = malloc(sizeof(*first));
410  if (!first)
411  goto error_out;
412  current = first;
413  }
414  current->first = first;
415  current->next = NULL;
416 
417  memcpy(one_value, linebuf + pos, i - pos);
418  one_value[i - pos] = '\0';
419 
420  if (sscanf(one_value, "%u", &current->cpu) != 1)
421  goto error_out;
422 
423  pos = i + 1;
424  }
425  }
426 
427  return first;
428 
429  error_out:
430  while (first) {
431  current = first->next;
432  free(first);
433  first = current;
434  }
435  return NULL;
436 }
437 
439 {
440  return sysfs_get_cpu_list(cpu, "affected_cpus");
441 }
442 
444 {
445  return sysfs_get_cpu_list(cpu, "related_cpus");
446 }
447 
448 struct cpufreq_stats *sysfs_get_freq_stats(unsigned int cpu,
449  unsigned long long *total_time) {
450  struct cpufreq_stats *first = NULL;
451  struct cpufreq_stats *current = NULL;
452  char one_value[SYSFS_PATH_MAX];
453  char linebuf[MAX_LINE_LEN];
454  unsigned int pos, i;
455  unsigned int len;
456 
457  len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
458  linebuf, sizeof(linebuf));
459  if (len == 0)
460  return NULL;
461 
462  *total_time = 0;
463  pos = 0;
464  for (i = 0; i < len; i++) {
465  if (i == strlen(linebuf) || linebuf[i] == '\n') {
466  if (i - pos < 2)
467  continue;
468  if ((i - pos) >= SYSFS_PATH_MAX)
469  goto error_out;
470  if (current) {
471  current->next = malloc(sizeof(*current));
472  if (!current->next)
473  goto error_out;
474  current = current->next;
475  } else {
476  first = malloc(sizeof(*first));
477  if (!first)
478  goto error_out;
479  current = first;
480  }
481  current->first = first;
482  current->next = NULL;
483 
484  memcpy(one_value, linebuf + pos, i - pos);
485  one_value[i - pos] = '\0';
486  if (sscanf(one_value, "%lu %llu",
487  &current->frequency,
488  &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 
507 unsigned long sysfs_get_freq_transitions(unsigned int cpu)
508 {
509  return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
510 }
511 
512 static 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 
547 int 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 
561 int 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 
572 int 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 
583 int 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 
630 int sysfs_set_frequency(unsigned int cpu, unsigned long target_frequency)
631 {
633  char userspace_gov[] = "userspace";
634  char freq[SYSFS_PATH_MAX];
635  int ret;
636 
637  if (!pol)
638  return -ENODEV;
639 
640  if (strncmp(pol->governor, userspace_gov, 9) != 0) {
641  ret = sysfs_modify_freq_policy_governor(cpu, userspace_gov);
642  if (ret) {
643  cpufreq_put_policy(pol);
644  return ret;
645  }
646  }
647 
648  cpufreq_put_policy(pol);
649 
650  snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
651 
652  return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
653  freq, strlen(freq));
654 }
655 
656 /* CPUFREQ sysfs access **************************************************/
657 
658 /* General sysfs access **************************************************/
659 int sysfs_cpu_exists(unsigned int cpu)
660 {
661  char file[SYSFS_PATH_MAX];
662  struct stat statbuf;
663 
664  snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/", cpu);
665 
666  if (stat(file, &statbuf) != 0)
667  return -ENOSYS;
668 
669  return S_ISDIR(statbuf.st_mode) ? 0 : -ENOSYS;
670 }
671 
672 /* General sysfs access **************************************************/