Linux Audio

Check our new training course

Loading...
  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			return;
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}