cpufreq-set.c (7294B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> 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 23static 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 32static 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 43struct freq_units { 44 char *str_unit; 45 int power_of_ten; 46}; 47 48const 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 57static void print_unknown_arg(void) 58{ 59 printf(_("invalid or unknown argument\n")); 60} 61 62static 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 144static 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 171static 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 196int 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}