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++;
 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}
 340