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 4096
21 #define SYSFS_PATH_MAX 255
22 
23 
sysfs_read_file(const char * path,char * buf,size_t buflen)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 */
sysfs_cpufreq_read_file(unsigned int cpu,const char * fname,char * buf,size_t buflen)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 */
sysfs_cpufreq_write_file(unsigned int cpu,const char * fname,const char * value,size_t len)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 
90 enum 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 
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 
sysfs_cpufreq_get_one_value(unsigned int cpu,enum cpufreq_value which)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 
141 enum cpufreq_string {
142 	SCALING_DRIVER,
143 	SCALING_GOVERNOR,
144 	MAX_CPUFREQ_STRING_FILES
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 
sysfs_cpufreq_get_one_string(unsigned int cpu,enum cpufreq_string which)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 
180 enum cpufreq_write {
181 	WRITE_SCALING_MIN_FREQ,
182 	WRITE_SCALING_MAX_FREQ,
183 	WRITE_SCALING_GOVERNOR,
184 	WRITE_SCALING_SET_SPEED,
185 	MAX_CPUFREQ_WRITE_FILES
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 
sysfs_cpufreq_write_one_value(unsigned int cpu,enum cpufreq_write which,const char * new_value,size_t len)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 
sysfs_get_freq_kernel(unsigned int cpu)209 unsigned long sysfs_get_freq_kernel(unsigned int cpu)
210 {
211 	return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
212 }
213 
sysfs_get_freq_hardware(unsigned int cpu)214 unsigned long sysfs_get_freq_hardware(unsigned int cpu)
215 {
216 	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
217 }
218 
sysfs_get_freq_transition_latency(unsigned int cpu)219 unsigned long sysfs_get_freq_transition_latency(unsigned int cpu)
220 {
221 	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
222 }
223 
sysfs_get_freq_hardware_limits(unsigned int cpu,unsigned long * min,unsigned long * max)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 
sysfs_get_freq_driver(unsigned int cpu)242 char *sysfs_get_freq_driver(unsigned int cpu)
243 {
244 	return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
245 }
246 
sysfs_get_freq_policy(unsigned int cpu)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 
271 struct cpufreq_available_governors *
sysfs_get_freq_available_governors(unsigned int cpu)272 sysfs_get_freq_available_governors(unsigned int cpu) {
273 	struct cpufreq_available_governors *first = NULL;
274 	struct cpufreq_available_governors *current = NULL;
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 
327 struct cpufreq_available_frequencies *
sysfs_get_available_frequencies(unsigned int cpu)328 sysfs_get_available_frequencies(unsigned int cpu) {
329 	struct cpufreq_available_frequencies *first = NULL;
330 	struct cpufreq_available_frequencies *current = NULL;
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 
sysfs_get_cpu_list(unsigned int cpu,const char * file)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;
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 	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 
sysfs_get_freq_affected_cpus(unsigned int cpu)438 struct cpufreq_affected_cpus *sysfs_get_freq_affected_cpus(unsigned int cpu)
439 {
440 	return sysfs_get_cpu_list(cpu, "affected_cpus");
441 }
442 
sysfs_get_freq_related_cpus(unsigned int cpu)443 struct cpufreq_affected_cpus *sysfs_get_freq_related_cpus(unsigned int cpu)
444 {
445 	return sysfs_get_cpu_list(cpu, "related_cpus");
446 }
447 
sysfs_get_freq_stats(unsigned int cpu,unsigned long long * total_time)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 
sysfs_get_freq_transitions(unsigned int cpu)507 unsigned long sysfs_get_freq_transitions(unsigned int cpu)
508 {
509 	return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
510 }
511 
verify_gov(char * new_gov,char * passed_gov)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 
sysfs_modify_freq_policy_governor(unsigned int cpu,char * governor)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 
sysfs_modify_freq_policy_max(unsigned int cpu,unsigned long max_freq)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 
sysfs_modify_freq_policy_min(unsigned int cpu,unsigned long min_freq)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 
sysfs_set_freq_policy(unsigned int cpu,struct cpufreq_policy * policy)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 
sysfs_set_frequency(unsigned int cpu,unsigned long target_frequency)630 int sysfs_set_frequency(unsigned int cpu, unsigned long target_frequency)
631 {
632 	struct cpufreq_policy *pol = sysfs_get_freq_policy(cpu);
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 **************************************************/
sysfs_cpu_exists(unsigned int cpu)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 **************************************************/
673