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