Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/power/cpupower/lib/cpufreq.c
26288 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* (C) 2004-2009 Dominik Brodowski <[email protected]>
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 */
23
static 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 */
35
static 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
63
enum 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
75
static 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
86
unsigned 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
117
static 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
127
enum cpufreq_string {
128
SCALING_DRIVER,
129
SCALING_GOVERNOR,
130
ENERGY_PERFORMANCE_PREFERENCE,
131
MAX_CPUFREQ_STRING_FILES
132
};
133
134
static 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
141
static 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
168
enum 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
176
static 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
183
static 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
197
unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
198
{
199
return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
200
}
201
202
unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
203
{
204
return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
205
}
206
207
unsigned long cpufreq_get_transition_latency(unsigned int cpu)
208
{
209
return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
210
}
211
212
char *cpufreq_get_energy_performance_preference(unsigned int cpu)
213
{
214
return sysfs_cpufreq_get_one_string(cpu, ENERGY_PERFORMANCE_PREFERENCE);
215
}
216
217
void cpufreq_put_energy_performance_preference(char *ptr)
218
{
219
if (!ptr)
220
return;
221
free(ptr);
222
}
223
224
int 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
242
char *cpufreq_get_driver(unsigned int cpu)
243
{
244
return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
245
}
246
247
void cpufreq_put_driver(char *ptr)
248
{
249
if (!ptr)
250
return;
251
free(ptr);
252
}
253
254
struct 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
278
void 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
288
struct 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
344
void 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
362
struct 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
418
struct 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
474
void 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
489
void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any)
490
{
491
cpufreq_put_available_frequencies(any);
492
}
493
494
static 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
550
struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
551
{
552
return sysfs_get_cpu_list(cpu, "affected_cpus");
553
}
554
555
void 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
571
struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
572
{
573
return sysfs_get_cpu_list(cpu, "related_cpus");
574
}
575
576
void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
577
{
578
cpufreq_put_affected_cpus(any);
579
}
580
581
static 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
616
int 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
664
int 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
675
int 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
685
int 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
699
int 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
725
struct 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
785
void 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
800
unsigned long cpufreq_get_transitions(unsigned int cpu)
801
{
802
return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
803
}
804
805