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