linux/tools/power/cpupower/utils/cpufreq-set.c
<<
>>
Prefs
   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++, power--;
 104        }
 105        if (power >= -1)        /* not enough => pad */
 106                pad = power + 1;
 107        else                    /* to much => strip */
 108                pad = 0, cp += power + 1;
 109        /* check bounds */
 110        if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1)
 111                return 0;
 112
 113        /* copy digits */
 114        for (i = 0; i < cp; i++, str++) {
 115                if (*str == '.')
 116                        str++;
 117                normalized[i] = *str;
 118        }
 119        /* and pad */
 120        for (; i < cp + pad; i++)
 121                normalized[i] = '0';
 122
 123        /* round up, down ? */
 124        match_count = (normalized[i-1] >= '5');
 125        /* and drop the decimal part */
 126        normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */
 127
 128        /* final conversion (and applying rounding) */
 129        errno = 0;
 130        freq = strtoul(normalized, &end, 10);
 131        if (errno)
 132                return 0;
 133        else {
 134                if (match_count && freq != ULONG_MAX)
 135                        freq++;
 136                return freq;
 137        }
 138}
 139
 140static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol)
 141{
 142        struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);
 143        int ret;
 144
 145        if (!cur_pol) {
 146                printf(_("wrong, unknown or unhandled CPU?\n"));
 147                return -EINVAL;
 148        }
 149
 150        if (!new_pol->min)
 151                new_pol->min = cur_pol->min;
 152
 153        if (!new_pol->max)
 154                new_pol->max = cur_pol->max;
 155
 156        if (!new_pol->governor)
 157                new_pol->governor = cur_pol->governor;
 158
 159        ret = cpufreq_set_policy(cpu, new_pol);
 160
 161        cpufreq_put_policy(cur_pol);
 162
 163        return ret;
 164}
 165
 166
 167static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol,
 168                unsigned long freq, unsigned int pc)
 169{
 170        switch (pc) {
 171        case 0:
 172                return cpufreq_set_frequency(cpu, freq);
 173
 174        case 1:
 175                /* if only one value of a policy is to be changed, we can
 176                 * use a "fast path".
 177                 */
 178                if (new_pol->min)
 179                        return cpufreq_modify_policy_min(cpu, new_pol->min);
 180                else if (new_pol->max)
 181                        return cpufreq_modify_policy_max(cpu, new_pol->max);
 182                else if (new_pol->governor)
 183                        return cpufreq_modify_policy_governor(cpu,
 184                                                        new_pol->governor);
 185
 186        default:
 187                /* slow path */
 188                return do_new_policy(cpu, new_pol);
 189        }
 190}
 191
 192int cmd_freq_set(int argc, char **argv)
 193{
 194        extern char *optarg;
 195        extern int optind, opterr, optopt;
 196        int ret = 0, cont = 1;
 197        int double_parm = 0, related = 0, policychange = 0;
 198        unsigned long freq = 0;
 199        char gov[20];
 200        unsigned int cpu;
 201
 202        struct cpufreq_policy new_pol = {
 203                .min = 0,
 204                .max = 0,
 205                .governor = NULL,
 206        };
 207
 208        /* parameter parsing */
 209        do {
 210                ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);
 211                switch (ret) {
 212                case '?':
 213                        print_unknown_arg();
 214                        return -EINVAL;
 215                case -1:
 216                        cont = 0;
 217                        break;
 218                case 'r':
 219                        if (related)
 220                                double_parm++;
 221                        related++;
 222                        break;
 223                case 'd':
 224                        if (new_pol.min)
 225                                double_parm++;
 226                        policychange++;
 227                        new_pol.min = string_to_frequency(optarg);
 228                        if (new_pol.min == 0) {
 229                                print_unknown_arg();
 230                                return -EINVAL;
 231                        }
 232                        break;
 233                case 'u':
 234                        if (new_pol.max)
 235                                double_parm++;
 236                        policychange++;
 237                        new_pol.max = string_to_frequency(optarg);
 238                        if (new_pol.max == 0) {
 239                                print_unknown_arg();
 240                                return -EINVAL;
 241                        }
 242                        break;
 243                case 'f':
 244                        if (freq)
 245                                double_parm++;
 246                        freq = string_to_frequency(optarg);
 247                        if (freq == 0) {
 248                                print_unknown_arg();
 249                                return -EINVAL;
 250                        }
 251                        break;
 252                case 'g':
 253                        if (new_pol.governor)
 254                                double_parm++;
 255                        policychange++;
 256                        if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {
 257                                print_unknown_arg();
 258                                return -EINVAL;
 259                        }
 260                        if ((sscanf(optarg, "%19s", gov)) != 1) {
 261                                print_unknown_arg();
 262                                return -EINVAL;
 263                        }
 264                        new_pol.governor = gov;
 265                        break;
 266                }
 267        } while (cont);
 268
 269        /* parameter checking */
 270        if (double_parm) {
 271                printf("the same parameter was passed more than once\n");
 272                return -EINVAL;
 273        }
 274
 275        if (freq && policychange) {
 276                printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"
 277                                "-g/--governor parameters\n"));
 278                return -EINVAL;
 279        }
 280
 281        if (!freq && !policychange) {
 282                printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"
 283                                "-g/--governor must be passed\n"));
 284                return -EINVAL;
 285        }
 286
 287        /* Default is: set all CPUs */
 288        if (bitmask_isallclear(cpus_chosen))
 289                bitmask_setall(cpus_chosen);
 290
 291        /* Also set frequency settings for related CPUs if -r is passed */
 292        if (related) {
 293                for (cpu = bitmask_first(cpus_chosen);
 294                     cpu <= bitmask_last(cpus_chosen); cpu++) {
 295                        struct cpufreq_affected_cpus *cpus;
 296
 297                        if (!bitmask_isbitset(cpus_chosen, cpu) ||
 298                            cpupower_is_cpu_online(cpu) != 1)
 299                                continue;
 300
 301                        cpus = cpufreq_get_related_cpus(cpu);
 302                        if (!cpus)
 303                                break;
 304                        while (cpus->next) {
 305                                bitmask_setbit(cpus_chosen, cpus->cpu);
 306                                cpus = cpus->next;
 307                        }
 308                        /* Set the last cpu in related cpus list */
 309                        bitmask_setbit(cpus_chosen, cpus->cpu);
 310                        cpufreq_put_related_cpus(cpus);
 311                }
 312        }
 313
 314
 315        /* loop over CPUs */
 316        for (cpu = bitmask_first(cpus_chosen);
 317             cpu <= bitmask_last(cpus_chosen); cpu++) {
 318
 319                if (!bitmask_isbitset(cpus_chosen, cpu) ||
 320                    cpupower_is_cpu_online(cpu) != 1)
 321                        continue;
 322
 323                printf(_("Setting cpu: %d\n"), cpu);
 324                ret = do_one_cpu(cpu, &new_pol, freq, policychange);
 325                if (ret) {
 326                        print_error();
 327                        return ret;
 328                }
 329        }
 330
 331        return 0;
 332}
 333