linux/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c
<<
>>
Prefs
   1/*
   2 * x86_energy_perf_policy -- set the energy versus performance
   3 * policy preference bias on recent X86 processors.
   4 */
   5/*
   6 * Copyright (c) 2010, Intel Corporation.
   7 * Len Brown <len.brown@intel.com>
   8 *
   9 * This program is free software; you can redistribute it and/or modify it
  10 * under the terms and conditions of the GNU General Public License,
  11 * version 2, as published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope it will be useful, but WITHOUT
  14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  16 * more details.
  17 *
  18 * You should have received a copy of the GNU General Public License along with
  19 * this program; if not, write to the Free Software Foundation, Inc.,
  20 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  21 */
  22
  23#include <stdio.h>
  24#include <unistd.h>
  25#include <sys/types.h>
  26#include <sys/stat.h>
  27#include <sys/resource.h>
  28#include <fcntl.h>
  29#include <signal.h>
  30#include <sys/time.h>
  31#include <stdlib.h>
  32#include <string.h>
  33
  34unsigned int verbose;           /* set with -v */
  35unsigned int read_only;         /* set with -r */
  36char *progname;
  37unsigned long long new_bias;
  38int cpu = -1;
  39
  40/*
  41 * Usage:
  42 *
  43 * -c cpu: limit action to a single CPU (default is all CPUs)
  44 * -v: verbose output (can invoke more than once)
  45 * -r: read-only, don't change any settings
  46 *
  47 *  performance
  48 *      Performance is paramount.
  49 *      Unwilling to sacrifice any performance
  50 *      for the sake of energy saving. (hardware default)
  51 *
  52 *  normal
  53 *      Can tolerate minor performance compromise
  54 *      for potentially significant energy savings.
  55 *      (reasonable default for most desktops and servers)
  56 *
  57 *  powersave
  58 *      Can tolerate significant performance hit
  59 *      to maximize energy savings.
  60 *
  61 * n
  62 *      a numerical value to write to the underlying MSR.
  63 */
  64void usage(void)
  65{
  66        printf("%s: [-c cpu] [-v] "
  67                "(-r | 'performance' | 'normal' | 'powersave' | n)\n",
  68                progname);
  69        exit(1);
  70}
  71
  72#define MSR_IA32_ENERGY_PERF_BIAS       0x000001b0
  73
  74#define BIAS_PERFORMANCE                0
  75#define BIAS_BALANCE                    6
  76#define BIAS_POWERSAVE                  15
  77
  78void cmdline(int argc, char **argv)
  79{
  80        int opt;
  81
  82        progname = argv[0];
  83
  84        while ((opt = getopt(argc, argv, "+rvc:")) != -1) {
  85                switch (opt) {
  86                case 'c':
  87                        cpu = atoi(optarg);
  88                        break;
  89                case 'r':
  90                        read_only = 1;
  91                        break;
  92                case 'v':
  93                        verbose++;
  94                        break;
  95                default:
  96                        usage();
  97                }
  98        }
  99        /* if -r, then should be no additional optind */
 100        if (read_only && (argc > optind))
 101                usage();
 102
 103        /*
 104         * if no -r , then must be one additional optind
 105         */
 106        if (!read_only) {
 107
 108                if (argc != optind + 1) {
 109                        printf("must supply -r or policy param\n");
 110                        usage();
 111                        }
 112
 113                if (!strcmp("performance", argv[optind])) {
 114                        new_bias = BIAS_PERFORMANCE;
 115                } else if (!strcmp("normal", argv[optind])) {
 116                        new_bias = BIAS_BALANCE;
 117                } else if (!strcmp("powersave", argv[optind])) {
 118                        new_bias = BIAS_POWERSAVE;
 119                } else {
 120                        char *endptr;
 121
 122                        new_bias = strtoull(argv[optind], &endptr, 0);
 123                        if (endptr == argv[optind] ||
 124                                new_bias > BIAS_POWERSAVE) {
 125                                        fprintf(stderr, "invalid value: %s\n",
 126                                                argv[optind]);
 127                                usage();
 128                        }
 129                }
 130        }
 131}
 132
 133/*
 134 * validate_cpuid()
 135 * returns on success, quietly exits on failure (make verbose with -v)
 136 */
 137void validate_cpuid(void)
 138{
 139        unsigned int eax, ebx, ecx, edx, max_level;
 140        unsigned int fms, family, model, stepping;
 141
 142        eax = ebx = ecx = edx = 0;
 143
 144        asm("cpuid" : "=a" (max_level), "=b" (ebx), "=c" (ecx),
 145                "=d" (edx) : "a" (0));
 146
 147        if (ebx != 0x756e6547 || edx != 0x49656e69 || ecx != 0x6c65746e) {
 148                if (verbose)
 149                        fprintf(stderr, "%.4s%.4s%.4s != GenuineIntel",
 150                                (char *)&ebx, (char *)&edx, (char *)&ecx);
 151                exit(1);
 152        }
 153
 154        asm("cpuid" : "=a" (fms), "=c" (ecx), "=d" (edx) : "a" (1) : "ebx");
 155        family = (fms >> 8) & 0xf;
 156        model = (fms >> 4) & 0xf;
 157        stepping = fms & 0xf;
 158        if (family == 6 || family == 0xf)
 159                model += ((fms >> 16) & 0xf) << 4;
 160
 161        if (verbose > 1)
 162                printf("CPUID %d levels family:model:stepping "
 163                        "0x%x:%x:%x (%d:%d:%d)\n", max_level,
 164                        family, model, stepping, family, model, stepping);
 165
 166        if (!(edx & (1 << 5))) {
 167                if (verbose)
 168                        printf("CPUID: no MSR\n");
 169                exit(1);
 170        }
 171
 172        /*
 173         * Support for MSR_IA32_ENERGY_PERF_BIAS
 174         * is indicated by CPUID.06H.ECX.bit3
 175         */
 176        asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (6));
 177        if (verbose)
 178                printf("CPUID.06H.ECX: 0x%x\n", ecx);
 179        if (!(ecx & (1 << 3))) {
 180                if (verbose)
 181                        printf("CPUID: No MSR_IA32_ENERGY_PERF_BIAS\n");
 182                exit(1);
 183        }
 184        return; /* success */
 185}
 186
 187unsigned long long get_msr(int cpu, int offset)
 188{
 189        unsigned long long msr;
 190        char msr_path[32];
 191        int retval;
 192        int fd;
 193
 194        sprintf(msr_path, "/dev/cpu/%d/msr", cpu);
 195        fd = open(msr_path, O_RDONLY);
 196        if (fd < 0) {
 197                printf("Try \"# modprobe msr\"\n");
 198                perror(msr_path);
 199                exit(1);
 200        }
 201
 202        retval = pread(fd, &msr, sizeof msr, offset);
 203
 204        if (retval != sizeof msr) {
 205                printf("pread cpu%d 0x%x = %d\n", cpu, offset, retval);
 206                exit(-2);
 207        }
 208        close(fd);
 209        return msr;
 210}
 211
 212unsigned long long  put_msr(int cpu, unsigned long long new_msr, int offset)
 213{
 214        unsigned long long old_msr;
 215        char msr_path[32];
 216        int retval;
 217        int fd;
 218
 219        sprintf(msr_path, "/dev/cpu/%d/msr", cpu);
 220        fd = open(msr_path, O_RDWR);
 221        if (fd < 0) {
 222                perror(msr_path);
 223                exit(1);
 224        }
 225
 226        retval = pread(fd, &old_msr, sizeof old_msr, offset);
 227        if (retval != sizeof old_msr) {
 228                perror("pwrite");
 229                printf("pread cpu%d 0x%x = %d\n", cpu, offset, retval);
 230                exit(-2);
 231        }
 232
 233        retval = pwrite(fd, &new_msr, sizeof new_msr, offset);
 234        if (retval != sizeof new_msr) {
 235                perror("pwrite");
 236                printf("pwrite cpu%d 0x%x = %d\n", cpu, offset, retval);
 237                exit(-2);
 238        }
 239
 240        close(fd);
 241
 242        return old_msr;
 243}
 244
 245void print_msr(int cpu)
 246{
 247        printf("cpu%d: 0x%016llx\n",
 248                cpu, get_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS));
 249}
 250
 251void update_msr(int cpu)
 252{
 253        unsigned long long previous_msr;
 254
 255        previous_msr = put_msr(cpu, new_bias, MSR_IA32_ENERGY_PERF_BIAS);
 256
 257        if (verbose)
 258                printf("cpu%d  msr0x%x 0x%016llx -> 0x%016llx\n",
 259                        cpu, MSR_IA32_ENERGY_PERF_BIAS, previous_msr, new_bias);
 260
 261        return;
 262}
 263
 264char *proc_stat = "/proc/stat";
 265/*
 266 * run func() on every cpu in /dev/cpu
 267 */
 268void for_every_cpu(void (func)(int))
 269{
 270        FILE *fp;
 271        int retval;
 272
 273        fp = fopen(proc_stat, "r");
 274        if (fp == NULL) {
 275                perror(proc_stat);
 276                exit(1);
 277        }
 278
 279        retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n");
 280        if (retval != 0) {
 281                perror("/proc/stat format");
 282                exit(1);
 283        }
 284
 285        while (1) {
 286                int cpu;
 287
 288                retval = fscanf(fp,
 289                        "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n",
 290                        &cpu);
 291                if (retval != 1)
 292                        break;
 293
 294                func(cpu);
 295        }
 296        fclose(fp);
 297}
 298
 299int main(int argc, char **argv)
 300{
 301        cmdline(argc, argv);
 302
 303        if (verbose > 1)
 304                printf("x86_energy_perf_policy Nov 24, 2010"
 305                                " - Len Brown <lenb@kernel.org>\n");
 306        if (verbose > 1 && !read_only)
 307                printf("new_bias %lld\n", new_bias);
 308
 309        validate_cpuid();
 310
 311        if (cpu != -1) {
 312                if (read_only)
 313                        print_msr(cpu);
 314                else
 315                        update_msr(cpu);
 316        } else {
 317                if (read_only)
 318                        for_every_cpu(print_msr);
 319                else
 320                        for_every_cpu(update_msr);
 321        }
 322
 323        return 0;
 324}
 325