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 
34 unsigned int verbose;		/* set with -v */
35 unsigned int read_only;		/* set with -r */
36 char *progname;
37 unsigned long long new_bias;
38 int 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  */
64 void 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 
78 void 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  */
137 void 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 
187 unsigned 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 
212 unsigned 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 
245 void print_msr(int cpu)
246 {
247 	printf("cpu%d: 0x%016llx\n",
248 		cpu, get_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS));
249 }
250 
251 void 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 
264 char *proc_stat = "/proc/stat";
265 /*
266  * run func() on every cpu in /dev/cpu
267  */
268 void 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 
299 int 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