File Index
Files and directories
Sign in
FILE

cpufreq.c

File preview with syntax highlighting.

linux-6.19.14/tools/power/cpupower/lib/cpufreq.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
  4 */
  5
  6
  7#include <stdio.h>
  8#include <errno.h>
  9#include <stdlib.h>
 10#include <string.h>
 11#include <sys/types.h>
 12#include <sys/stat.h>
 13#include <fcntl.h>
 14#include <unistd.h>
 15
 16#include "cpufreq.h"
 17#include "cpupower_intern.h"
 18
 19/* CPUFREQ sysfs access **************************************************/
 20
 21/* helper function to read file from /sys into given buffer */
 22/* fname is a relative path under "cpuX/cpufreq" dir */
 23static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
 24					    char *buf, size_t buflen)
 25{
 26	char path[SYSFS_PATH_MAX];
 27
 28	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
 29			 cpu, fname);
 30	return cpupower_read_sysfs(path, buf, buflen);
 31}
 32
 33/* helper function to write a new value to a /sys file */
 34/* fname is a relative path under "cpuX/cpufreq" dir */
 35static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
 36					     const char *fname,
 37					     const char *value, size_t len)
 38{
 39	char path[SYSFS_PATH_MAX];
 40	int fd;
 41	ssize_t numwrite;
 42
 43	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
 44			 cpu, fname);
 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
 56	close(fd);
 57
 58	return (unsigned int) numwrite;
 59}
 60
 61/* read access to files which contain one numeric value */
 62
 63enum cpufreq_value {
 64	CPUINFO_CUR_FREQ,
 65	CPUINFO_MIN_FREQ,
 66	CPUINFO_MAX_FREQ,
 67	CPUINFO_LATENCY,
 68	SCALING_CUR_FREQ,
 69	SCALING_MIN_FREQ,
 70	SCALING_MAX_FREQ,
 71	STATS_NUM_TRANSITIONS,
 72	MAX_CPUFREQ_VALUE_READ_FILES
 73};
 74
 75static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
 76	[CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
 77	[CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
 78	[CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
 79	[CPUINFO_LATENCY]  = "cpuinfo_transition_latency",
 80	[SCALING_CUR_FREQ] = "scaling_cur_freq",
 81	[SCALING_MIN_FREQ] = "scaling_min_freq",
 82	[SCALING_MAX_FREQ] = "scaling_max_freq",
 83	[STATS_NUM_TRANSITIONS] = "stats/total_trans"
 84};
 85
 86unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu,
 87						 const char **table,
 88						 unsigned int index,
 89						 unsigned int size)
 90{
 91	unsigned long value;
 92	unsigned int len;
 93	char linebuf[MAX_LINE_LEN];
 94	char *endp;
 95
 96	if (!table || index >= size || !table[index])
 97		return 0;
 98
 99	len = sysfs_cpufreq_read_file(cpu, table[index], linebuf,
100				      sizeof(linebuf));
101
102	if (len == 0)
103		return 0;
104
105	if (!strcmp(linebuf, "enabled\n"))
106		return 1;
107	if (!strcmp(linebuf, "disabled\n"))
108		return 0;
109	value = strtoul(linebuf, &endp, 0);
110
111	if (endp == linebuf || errno == ERANGE)
112		return 0;
113
114	return value;
115}
116
117static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
118						 enum cpufreq_value which)
119{
120	return cpufreq_get_sysfs_value_from_table(cpu, cpufreq_value_files,
121						  which,
122						  MAX_CPUFREQ_VALUE_READ_FILES);
123}
124
125/* read access to files which contain one string */
126
127enum cpufreq_string {
128	SCALING_DRIVER,
129	SCALING_GOVERNOR,
130	ENERGY_PERFORMANCE_PREFERENCE,
131	MAX_CPUFREQ_STRING_FILES
132};
133
134static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
135	[SCALING_DRIVER] = "scaling_driver",
136	[SCALING_GOVERNOR] = "scaling_governor",
137	[ENERGY_PERFORMANCE_PREFERENCE] = "energy_performance_preference",
138};
139
140
141static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
142					  enum cpufreq_string which)
143{
144	char linebuf[MAX_LINE_LEN];
145	char *result;
146	unsigned int len;
147
148	if (which >= MAX_CPUFREQ_STRING_FILES)
149		return NULL;
150
151	len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
152				linebuf, sizeof(linebuf));
153	if (len == 0)
154		return NULL;
155
156	result = strdup(linebuf);
157	if (result == NULL)
158		return NULL;
159
160	if (result[strlen(result) - 1] == '\n')
161		result[strlen(result) - 1] = '\0';
162
163	return result;
164}
165
166/* write access */
167
168enum cpufreq_write {
169	WRITE_SCALING_MIN_FREQ,
170	WRITE_SCALING_MAX_FREQ,
171	WRITE_SCALING_GOVERNOR,
172	WRITE_SCALING_SET_SPEED,
173	MAX_CPUFREQ_WRITE_FILES
174};
175
176static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
177	[WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
178	[WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
179	[WRITE_SCALING_GOVERNOR] = "scaling_governor",
180	[WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
181};
182
183static int sysfs_cpufreq_write_one_value(unsigned int cpu,
184					 enum cpufreq_write which,
185					 const char *new_value, size_t len)
186{
187	if (which >= MAX_CPUFREQ_WRITE_FILES)
188		return 0;
189
190	if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
191					new_value, len) != len)
192		return -ENODEV;
193
194	return 0;
195};
196
197unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
198{
199	return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
200}
201
202unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
203{
204	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
205}
206
207unsigned long cpufreq_get_transition_latency(unsigned int cpu)
208{
209	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
210}
211
212char *cpufreq_get_energy_performance_preference(unsigned int cpu)
213{
214	return sysfs_cpufreq_get_one_string(cpu, ENERGY_PERFORMANCE_PREFERENCE);
215}
216
217void cpufreq_put_energy_performance_preference(char *ptr)
218{
219	if (!ptr)
220		return;
221	free(ptr);
222}
223
224int cpufreq_get_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
242char *cpufreq_get_driver(unsigned int cpu)
243{
244	return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
245}
246
247void cpufreq_put_driver(char *ptr)
248{
249	if (!ptr)
250		return;
251	free(ptr);
252}
253
254struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu)
255{
256	struct cpufreq_policy *policy;
257
258	policy = malloc(sizeof(struct cpufreq_policy));
259	if (!policy)
260		return NULL;
261
262	policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
263	if (!policy->governor) {
264		free(policy);
265		return NULL;
266	}
267	policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
268	policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
269	if ((!policy->min) || (!policy->max)) {
270		free(policy->governor);
271		free(policy);
272		return NULL;
273	}
274
275	return policy;
276}
277
278void cpufreq_put_policy(struct cpufreq_policy *policy)
279{
280	if ((!policy) || (!policy->governor))
281		return;
282
283	free(policy->governor);
284	policy->governor = NULL;
285	free(policy);
286}
287
288struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned
289								int cpu)
290{
291	struct cpufreq_available_governors *first = NULL;
292	struct cpufreq_available_governors *current = NULL;
293	char linebuf[MAX_LINE_LEN];
294	unsigned int pos, i;
295	unsigned int len;
296
297	len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
298				linebuf, sizeof(linebuf));
299	if (len == 0)
300		return NULL;
301
302	pos = 0;
303	for (i = 0; i < len; i++) {
304		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
305			if (i - pos < 2)
306				continue;
307			if (current) {
308				current->next = malloc(sizeof(*current));
309				if (!current->next)
310					goto error_out;
311				current = current->next;
312			} else {
313				first = malloc(sizeof(*first));
314				if (!first)
315					return NULL;
316				current = first;
317			}
318			current->first = first;
319			current->next = NULL;
320
321			current->governor = malloc(i - pos + 1);
322			if (!current->governor)
323				goto error_out;
324
325			memcpy(current->governor, linebuf + pos, i - pos);
326			current->governor[i - pos] = '\0';
327			pos = i + 1;
328		}
329	}
330
331	return first;
332
333 error_out:
334	while (first) {
335		current = first->next;
336		if (first->governor)
337			free(first->governor);
338		free(first);
339		first = current;
340	}
341	return NULL;
342}
343
344void cpufreq_put_available_governors(struct cpufreq_available_governors *any)
345{
346	struct cpufreq_available_governors *tmp, *next;
347
348	if (!any)
349		return;
350
351	tmp = any->first;
352	while (tmp) {
353		next = tmp->next;
354		if (tmp->governor)
355			free(tmp->governor);
356		free(tmp);
357		tmp = next;
358	}
359}
360
361
362struct cpufreq_available_frequencies
363*cpufreq_get_available_frequencies(unsigned int cpu)
364{
365	struct cpufreq_available_frequencies *first = NULL;
366	struct cpufreq_available_frequencies *current = NULL;
367	char one_value[SYSFS_PATH_MAX];
368	char linebuf[MAX_LINE_LEN];
369	unsigned int pos, i;
370	unsigned int len;
371
372	len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
373				      linebuf, sizeof(linebuf));
374	if (len == 0)
375		return NULL;
376
377	pos = 0;
378	for (i = 0; i < len; i++) {
379		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
380			if (i - pos < 2)
381				continue;
382			if (i - pos >= SYSFS_PATH_MAX)
383				goto error_out;
384			if (current) {
385				current->next = malloc(sizeof(*current));
386				if (!current->next)
387					goto error_out;
388				current = current->next;
389			} else {
390				first = malloc(sizeof(*first));
391				if (!first)
392					return NULL;
393				current = first;
394			}
395			current->first = first;
396			current->next = NULL;
397
398			memcpy(one_value, linebuf + pos, i - pos);
399			one_value[i - pos] = '\0';
400			if (sscanf(one_value, "%lu", &current->frequency) != 1)
401				goto error_out;
402
403			pos = i + 1;
404		}
405	}
406
407	return first;
408
409 error_out:
410	while (first) {
411		current = first->next;
412		free(first);
413		first = current;
414	}
415	return NULL;
416}
417
418struct cpufreq_available_frequencies
419*cpufreq_get_boost_frequencies(unsigned int cpu)
420{
421	struct cpufreq_available_frequencies *first = NULL;
422	struct cpufreq_available_frequencies *current = NULL;
423	char one_value[SYSFS_PATH_MAX];
424	char linebuf[MAX_LINE_LEN];
425	unsigned int pos, i;
426	unsigned int len;
427
428	len = sysfs_cpufreq_read_file(cpu, "scaling_boost_frequencies",
429				      linebuf, sizeof(linebuf));
430	if (len == 0)
431		return NULL;
432
433	pos = 0;
434	for (i = 0; i < len; i++) {
435		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
436			if (i - pos < 2)
437				continue;
438			if (i - pos >= SYSFS_PATH_MAX)
439				goto error_out;
440			if (current) {
441				current->next = malloc(sizeof(*current));
442				if (!current->next)
443					goto error_out;
444				current = current->next;
445			} else {
446				first = malloc(sizeof(*first));
447				if (!first)
448					return NULL;
449				current = first;
450			}
451			current->first = first;
452			current->next = NULL;
453
454			memcpy(one_value, linebuf + pos, i - pos);
455			one_value[i - pos] = '\0';
456			if (sscanf(one_value, "%lu", &current->frequency) != 1)
457				goto error_out;
458
459			pos = i + 1;
460		}
461	}
462
463	return first;
464
465 error_out:
466	while (first) {
467		current = first->next;
468		free(first);
469		first = current;
470	}
471	return NULL;
472}
473
474void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *any)
475{
476	struct cpufreq_available_frequencies *tmp, *next;
477
478	if (!any)
479		return;
480
481	tmp = any->first;
482	while (tmp) {
483		next = tmp->next;
484		free(tmp);
485		tmp = next;
486	}
487}
488
489void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any)
490{
491	cpufreq_put_available_frequencies(any);
492}
493
494static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
495							const char *file)
496{
497	struct cpufreq_affected_cpus *first = NULL;
498	struct cpufreq_affected_cpus *current = NULL;
499	char one_value[SYSFS_PATH_MAX];
500	char linebuf[MAX_LINE_LEN];
501	unsigned int pos, i;
502	unsigned int len;
503
504	len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
505	if (len == 0)
506		return NULL;
507
508	pos = 0;
509	for (i = 0; i < len; i++) {
510		if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
511			if (i - pos  < 1)
512				continue;
513			if (i - pos >= SYSFS_PATH_MAX)
514				goto error_out;
515			if (current) {
516				current->next = malloc(sizeof(*current));
517				if (!current->next)
518					goto error_out;
519				current = current->next;
520			} else {
521				first = malloc(sizeof(*first));
522				if (!first)
523					return NULL;
524				current = first;
525			}
526			current->first = first;
527			current->next = NULL;
528
529			memcpy(one_value, linebuf + pos, i - pos);
530			one_value[i - pos] = '\0';
531
532			if (sscanf(one_value, "%u", &current->cpu) != 1)
533				goto error_out;
534
535			pos = i + 1;
536		}
537	}
538
539	return first;
540
541 error_out:
542	while (first) {
543		current = first->next;
544		free(first);
545		first = current;
546	}
547	return NULL;
548}
549
550struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
551{
552	return sysfs_get_cpu_list(cpu, "affected_cpus");
553}
554
555void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any)
556{
557	struct cpufreq_affected_cpus *tmp, *next;
558
559	if (!any)
560		return;
561
562	tmp = any->first;
563	while (tmp) {
564		next = tmp->next;
565		free(tmp);
566		tmp = next;
567	}
568}
569
570
571struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
572{
573	return sysfs_get_cpu_list(cpu, "related_cpus");
574}
575
576void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
577{
578	cpufreq_put_affected_cpus(any);
579}
580
581static int verify_gov(char *new_gov, char *passed_gov)
582{
583	unsigned int i, j = 0;
584
585	if (!passed_gov || (strlen(passed_gov) > 19))
586		return -EINVAL;
587
588	strncpy(new_gov, passed_gov, 20);
589	for (i = 0; i < 20; i++) {
590		if (j) {
591			new_gov[i] = '\0';
592			continue;
593		}
594		if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
595			continue;
596
597		if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
598			continue;
599
600		if (new_gov[i] == '-')
601			continue;
602
603		if (new_gov[i] == '_')
604			continue;
605
606		if (new_gov[i] == '\0') {
607			j = 1;
608			continue;
609		}
610		return -EINVAL;
611	}
612	new_gov[19] = '\0';
613	return 0;
614}
615
616int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy)
617{
618	char min[SYSFS_PATH_MAX];
619	char max[SYSFS_PATH_MAX];
620	char gov[SYSFS_PATH_MAX];
621	int ret;
622	unsigned long old_min;
623	int write_max_first;
624
625	if (!policy || !(policy->governor))
626		return -EINVAL;
627
628	if (policy->max < policy->min)
629		return -EINVAL;
630
631	if (verify_gov(gov, policy->governor))
632		return -EINVAL;
633
634	snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
635	snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
636
637	old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
638	write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
639
640	if (write_max_first) {
641		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
642						    max, strlen(max));
643		if (ret)
644			return ret;
645	}
646
647	ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
648					    strlen(min));
649	if (ret)
650		return ret;
651
652	if (!write_max_first) {
653		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
654						    max, strlen(max));
655		if (ret)
656			return ret;
657	}
658
659	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
660					     gov, strlen(gov));
661}
662
663
664int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq)
665{
666	char value[SYSFS_PATH_MAX];
667
668	snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
669
670	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
671					     value, strlen(value));
672}
673
674
675int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq)
676{
677	char value[SYSFS_PATH_MAX];
678
679	snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
680
681	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
682					     value, strlen(value));
683}
684
685int cpufreq_modify_policy_governor(unsigned int cpu, char *governor)
686{
687	char new_gov[SYSFS_PATH_MAX];
688
689	if ((!governor) || (strlen(governor) > 19))
690		return -EINVAL;
691
692	if (verify_gov(new_gov, governor))
693		return -EINVAL;
694
695	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
696					     new_gov, strlen(new_gov));
697}
698
699int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency)
700{
701	struct cpufreq_policy *pol = cpufreq_get_policy(cpu);
702	char userspace_gov[] = "userspace";
703	char freq[SYSFS_PATH_MAX];
704	int ret;
705
706	if (!pol)
707		return -ENODEV;
708
709	if (strncmp(pol->governor, userspace_gov, 9) != 0) {
710		ret = cpufreq_modify_policy_governor(cpu, userspace_gov);
711		if (ret) {
712			cpufreq_put_policy(pol);
713			return ret;
714		}
715	}
716
717	cpufreq_put_policy(pol);
718
719	snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
720
721	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
722					     freq, strlen(freq));
723}
724
725struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,
726					unsigned long long *total_time)
727{
728	struct cpufreq_stats *first = NULL;
729	struct cpufreq_stats *current = NULL;
730	char one_value[SYSFS_PATH_MAX];
731	char linebuf[MAX_LINE_LEN];
732	unsigned int pos, i;
733	unsigned int len;
734
735	len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
736				linebuf, sizeof(linebuf));
737	if (len == 0)
738		return NULL;
739
740	*total_time = 0;
741	pos = 0;
742	for (i = 0; i < len; i++) {
743		if (i == strlen(linebuf) || linebuf[i] == '\n')	{
744			if (i - pos < 2)
745				continue;
746			if ((i - pos) >= SYSFS_PATH_MAX)
747				goto error_out;
748			if (current) {
749				current->next = malloc(sizeof(*current));
750				if (!current->next)
751					goto error_out;
752				current = current->next;
753			} else {
754				first = malloc(sizeof(*first));
755				if (!first)
756					return NULL;
757				current = first;
758			}
759			current->first = first;
760			current->next = NULL;
761
762			memcpy(one_value, linebuf + pos, i - pos);
763			one_value[i - pos] = '\0';
764			if (sscanf(one_value, "%lu %llu",
765					&current->frequency,
766					&current->time_in_state) != 2)
767				goto error_out;
768
769			*total_time = *total_time + current->time_in_state;
770			pos = i + 1;
771		}
772	}
773
774	return first;
775
776 error_out:
777	while (first) {
778		current = first->next;
779		free(first);
780		first = current;
781	}
782	return NULL;
783}
784
785void cpufreq_put_stats(struct cpufreq_stats *any)
786{
787	struct cpufreq_stats *tmp, *next;
788
789	if (!any)
790		return;
791
792	tmp = any->first;
793	while (tmp) {
794		next = tmp->next;
795		free(tmp);
796		tmp = next;
797	}
798}
799
800unsigned long cpufreq_get_transitions(unsigned int cpu)
801{
802	return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
803}
~/ Read-only file preview.