Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/power/cpupower/utils/cpufreq-set.c
26295 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* (C) 2004-2009 Dominik Brodowski <[email protected]>
4
*/
5
6
7
#include <unistd.h>
8
#include <stdio.h>
9
#include <errno.h>
10
#include <stdlib.h>
11
#include <limits.h>
12
#include <string.h>
13
#include <ctype.h>
14
15
#include <getopt.h>
16
17
#include "cpufreq.h"
18
#include "cpuidle.h"
19
#include "helpers/helpers.h"
20
21
#define NORM_FREQ_LEN 32
22
23
static struct option set_opts[] = {
24
{"min", required_argument, NULL, 'd'},
25
{"max", required_argument, NULL, 'u'},
26
{"governor", required_argument, NULL, 'g'},
27
{"freq", required_argument, NULL, 'f'},
28
{"related", no_argument, NULL, 'r'},
29
{ },
30
};
31
32
static void print_error(void)
33
{
34
printf(_("Error setting new values. Common errors:\n"
35
"- Do you have proper administration rights? (super-user?)\n"
36
"- Is the governor you requested available and modprobed?\n"
37
"- Trying to set an invalid policy?\n"
38
"- Trying to set a specific frequency, but userspace governor is not available,\n"
39
" for example because of hardware which cannot be set to a specific frequency\n"
40
" or because the userspace governor isn't loaded?\n"));
41
};
42
43
struct freq_units {
44
char *str_unit;
45
int power_of_ten;
46
};
47
48
const struct freq_units def_units[] = {
49
{"hz", -3},
50
{"khz", 0}, /* default */
51
{"mhz", 3},
52
{"ghz", 6},
53
{"thz", 9},
54
{NULL, 0}
55
};
56
57
static void print_unknown_arg(void)
58
{
59
printf(_("invalid or unknown argument\n"));
60
}
61
62
static unsigned long string_to_frequency(const char *str)
63
{
64
char normalized[NORM_FREQ_LEN];
65
const struct freq_units *unit;
66
const char *scan;
67
char *end;
68
unsigned long freq;
69
int power = 0, match_count = 0, i, cp, pad;
70
71
while (*str == '0')
72
str++;
73
74
for (scan = str; isdigit(*scan) || *scan == '.'; scan++) {
75
if (*scan == '.' && match_count == 0)
76
match_count = 1;
77
else if (*scan == '.' && match_count == 1)
78
return 0;
79
}
80
81
if (*scan) {
82
match_count = 0;
83
for (unit = def_units; unit->str_unit; unit++) {
84
for (i = 0;
85
scan[i] && tolower(scan[i]) == unit->str_unit[i];
86
++i)
87
continue;
88
if (scan[i])
89
continue;
90
match_count++;
91
power = unit->power_of_ten;
92
}
93
if (match_count != 1)
94
return 0;
95
}
96
97
/* count the number of digits to be copied */
98
for (cp = 0; isdigit(str[cp]); cp++)
99
continue;
100
101
if (str[cp] == '.') {
102
while (power > -1 && isdigit(str[cp+1])) {
103
cp++;
104
power--;
105
}
106
}
107
if (power >= -1) { /* not enough => pad */
108
pad = power + 1;
109
} else { /* too much => strip */
110
pad = 0;
111
cp += power + 1;
112
}
113
/* check bounds */
114
if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1)
115
return 0;
116
117
/* copy digits */
118
for (i = 0; i < cp; i++, str++) {
119
if (*str == '.')
120
str++;
121
normalized[i] = *str;
122
}
123
/* and pad */
124
for (; i < cp + pad; i++)
125
normalized[i] = '0';
126
127
/* round up, down ? */
128
match_count = (normalized[i-1] >= '5');
129
/* and drop the decimal part */
130
normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */
131
132
/* final conversion (and applying rounding) */
133
errno = 0;
134
freq = strtoul(normalized, &end, 10);
135
if (errno)
136
return 0;
137
else {
138
if (match_count && freq != ULONG_MAX)
139
freq++;
140
return freq;
141
}
142
}
143
144
static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol)
145
{
146
struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);
147
int ret;
148
149
if (!cur_pol) {
150
printf(_("wrong, unknown or unhandled CPU?\n"));
151
return -EINVAL;
152
}
153
154
if (!new_pol->min)
155
new_pol->min = cur_pol->min;
156
157
if (!new_pol->max)
158
new_pol->max = cur_pol->max;
159
160
if (!new_pol->governor)
161
new_pol->governor = cur_pol->governor;
162
163
ret = cpufreq_set_policy(cpu, new_pol);
164
165
cpufreq_put_policy(cur_pol);
166
167
return ret;
168
}
169
170
171
static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol,
172
unsigned long freq, unsigned int pc)
173
{
174
switch (pc) {
175
case 0:
176
return cpufreq_set_frequency(cpu, freq);
177
178
case 1:
179
/* if only one value of a policy is to be changed, we can
180
* use a "fast path".
181
*/
182
if (new_pol->min)
183
return cpufreq_modify_policy_min(cpu, new_pol->min);
184
else if (new_pol->max)
185
return cpufreq_modify_policy_max(cpu, new_pol->max);
186
else if (new_pol->governor)
187
return cpufreq_modify_policy_governor(cpu,
188
new_pol->governor);
189
190
default:
191
/* slow path */
192
return do_new_policy(cpu, new_pol);
193
}
194
}
195
196
int cmd_freq_set(int argc, char **argv)
197
{
198
extern char *optarg;
199
extern int optind, opterr, optopt;
200
int ret = 0, cont = 1;
201
int double_parm = 0, related = 0, policychange = 0;
202
unsigned long freq = 0;
203
char gov[20];
204
unsigned int cpu;
205
206
struct cpufreq_policy new_pol = {
207
.min = 0,
208
.max = 0,
209
.governor = NULL,
210
};
211
212
/* parameter parsing */
213
do {
214
ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);
215
switch (ret) {
216
case '?':
217
print_unknown_arg();
218
return -EINVAL;
219
case -1:
220
cont = 0;
221
break;
222
case 'r':
223
if (related)
224
double_parm++;
225
related++;
226
break;
227
case 'd':
228
if (new_pol.min)
229
double_parm++;
230
policychange++;
231
new_pol.min = string_to_frequency(optarg);
232
if (new_pol.min == 0) {
233
print_unknown_arg();
234
return -EINVAL;
235
}
236
break;
237
case 'u':
238
if (new_pol.max)
239
double_parm++;
240
policychange++;
241
new_pol.max = string_to_frequency(optarg);
242
if (new_pol.max == 0) {
243
print_unknown_arg();
244
return -EINVAL;
245
}
246
break;
247
case 'f':
248
if (freq)
249
double_parm++;
250
freq = string_to_frequency(optarg);
251
if (freq == 0) {
252
print_unknown_arg();
253
return -EINVAL;
254
}
255
break;
256
case 'g':
257
if (new_pol.governor)
258
double_parm++;
259
policychange++;
260
if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {
261
print_unknown_arg();
262
return -EINVAL;
263
}
264
if ((sscanf(optarg, "%19s", gov)) != 1) {
265
print_unknown_arg();
266
return -EINVAL;
267
}
268
new_pol.governor = gov;
269
break;
270
}
271
} while (cont);
272
273
/* parameter checking */
274
if (double_parm) {
275
printf("the same parameter was passed more than once\n");
276
return -EINVAL;
277
}
278
279
if (freq && policychange) {
280
printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"
281
"-g/--governor parameters\n"));
282
return -EINVAL;
283
}
284
285
if (!freq && !policychange) {
286
printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"
287
"-g/--governor must be passed\n"));
288
return -EINVAL;
289
}
290
291
/* Default is: set all CPUs */
292
if (bitmask_isallclear(cpus_chosen))
293
bitmask_setall(cpus_chosen);
294
295
/* Also set frequency settings for related CPUs if -r is passed */
296
if (related) {
297
for (cpu = bitmask_first(cpus_chosen);
298
cpu <= bitmask_last(cpus_chosen); cpu++) {
299
struct cpufreq_affected_cpus *cpus;
300
301
if (!bitmask_isbitset(cpus_chosen, cpu) ||
302
cpupower_is_cpu_online(cpu) != 1)
303
continue;
304
305
cpus = cpufreq_get_related_cpus(cpu);
306
if (!cpus)
307
break;
308
while (cpus->next) {
309
bitmask_setbit(cpus_chosen, cpus->cpu);
310
cpus = cpus->next;
311
}
312
/* Set the last cpu in related cpus list */
313
bitmask_setbit(cpus_chosen, cpus->cpu);
314
cpufreq_put_related_cpus(cpus);
315
}
316
}
317
318
get_cpustate();
319
320
/* loop over CPUs */
321
for (cpu = bitmask_first(cpus_chosen);
322
cpu <= bitmask_last(cpus_chosen); cpu++) {
323
324
if (!bitmask_isbitset(cpus_chosen, cpu) ||
325
cpupower_is_cpu_online(cpu) != 1)
326
continue;
327
328
printf(_("Setting cpu: %d\n"), cpu);
329
ret = do_one_cpu(cpu, &new_pol, freq, policychange);
330
if (ret) {
331
print_error();
332
return ret;
333
}
334
}
335
336
print_offline_cpus();
337
338
return 0;
339
}
340
341