diff options
Diffstat (limited to 'tools/power/cpupower/utils/helpers')
| -rw-r--r-- | tools/power/cpupower/utils/helpers/amd.c | 135 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/bitmask.c | 292 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/bitmask.h | 33 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/cpuid.c | 178 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/helpers.h | 195 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/misc.c | 27 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/msr.c | 115 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/pci.c | 55 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/sysfs.c | 472 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/sysfs.h | 38 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/topology.c | 116 | 
11 files changed, 1656 insertions, 0 deletions
diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c new file mode 100644 index 00000000000..6437ef39aee --- /dev/null +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -0,0 +1,135 @@ +#if defined(__i386__) || defined(__x86_64__) +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <stdint.h> + +#include <pci/pci.h> + +#include "helpers/helpers.h" + +#define MSR_AMD_PSTATE_STATUS	0xc0010063 +#define MSR_AMD_PSTATE		0xc0010064 +#define MSR_AMD_PSTATE_LIMIT	0xc0010061 + +union msr_pstate { +	struct { +		unsigned fid:6; +		unsigned did:3; +		unsigned vid:7; +		unsigned res1:6; +		unsigned nbdid:1; +		unsigned res2:2; +		unsigned nbvid:7; +		unsigned iddval:8; +		unsigned idddiv:2; +		unsigned res3:21; +		unsigned en:1; +	} bits; +	unsigned long long val; +}; + +static int get_did(int family, union msr_pstate pstate) +{ +	int t; + +	if (family == 0x12) +		t = pstate.val & 0xf; +	else +		t = pstate.bits.did; + +	return t; +} + +static int get_cof(int family, union msr_pstate pstate) +{ +	int t; +	int fid, did; + +	did = get_did(family, pstate); + +	t = 0x10; +	fid = pstate.bits.fid; +	if (family == 0x11) +		t = 0x8; + +	return (100 * (fid + t)) >> did; +} + +/* Needs: + * cpu          -> the cpu that gets evaluated + * cpu_family   -> The cpu's family (0x10, 0x12,...) + * boots_states -> how much boost states the machines support + * + * Fills up: + * pstates -> a pointer to an array of size MAX_HW_PSTATES + *            must be initialized with zeros. + *            All available  HW pstates (including boost states) + * no      -> amount of pstates above array got filled up with + * + * returns zero on success, -1 on failure + */ +int decode_pstates(unsigned int cpu, unsigned int cpu_family, +		   int boost_states, unsigned long *pstates, int *no) +{ +	int i, psmax, pscur; +	union msr_pstate pstate; +	unsigned long long val; + +	/* Only read out frequencies from HW when CPU might be boostable +	   to keep the code as short and clean as possible. +	   Otherwise frequencies are exported via ACPI tables. +	*/ +	if (cpu_family < 0x10 || cpu_family == 0x14) +		return -1; + +	if (read_msr(cpu, MSR_AMD_PSTATE_LIMIT, &val)) +		return -1; + +	psmax = (val >> 4) & 0x7; + +	if (read_msr(cpu, MSR_AMD_PSTATE_STATUS, &val)) +		return -1; + +	pscur = val & 0x7; + +	pscur += boost_states; +	psmax += boost_states; +	for (i = 0; i <= psmax; i++) { +		if (i >= MAX_HW_PSTATES) { +			fprintf(stderr, "HW pstates [%d] exceeding max [%d]\n", +				psmax, MAX_HW_PSTATES); +			return -1; +		} +		if (read_msr(cpu, MSR_AMD_PSTATE + i, &pstate.val)) +			return -1; +		pstates[i] = get_cof(cpu_family, pstate); +	} +	*no = i; +	return 0; +} + +int amd_pci_get_num_boost_states(int *active, int *states) +{ +	struct pci_access *pci_acc; +	struct pci_dev *device; +	uint8_t val = 0; + +	*active = *states = 0; + +	device = pci_slot_func_init(&pci_acc, 0x18, 4); + +	if (device == NULL) +		return -ENODEV; + +	val = pci_read_byte(device, 0x15c); +	if (val & 3) +		*active = 1; +	else +		*active = 0; +	*states = (val >> 2) & 7; + +	pci_cleanup(pci_acc); +	return 0; +} +#endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/tools/power/cpupower/utils/helpers/bitmask.c b/tools/power/cpupower/utils/helpers/bitmask.c new file mode 100644 index 00000000000..5c074c60f90 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/bitmask.c @@ -0,0 +1,292 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <helpers/bitmask.h> + +/* How many bits in an unsigned long */ +#define bitsperlong (8 * sizeof(unsigned long)) + +/* howmany(a,b) : how many elements of size b needed to hold all of a */ +#define howmany(x, y) (((x)+((y)-1))/(y)) + +/* How many longs in mask of n bits */ +#define longsperbits(n) howmany(n, bitsperlong) + +#define max(a, b) ((a) > (b) ? (a) : (b)) + +/* + * Allocate and free `struct bitmask *` + */ + +/* Allocate a new `struct bitmask` with a size of n bits */ +struct bitmask *bitmask_alloc(unsigned int n) +{ +	struct bitmask *bmp; + +	bmp = malloc(sizeof(*bmp)); +	if (bmp == 0) +		return 0; +	bmp->size = n; +	bmp->maskp = calloc(longsperbits(n), sizeof(unsigned long)); +	if (bmp->maskp == 0) { +		free(bmp); +		return 0; +	} +	return bmp; +} + +/* Free `struct bitmask` */ +void bitmask_free(struct bitmask *bmp) +{ +	if (bmp == 0) +		return; +	free(bmp->maskp); +	bmp->maskp = (unsigned long *)0xdeadcdef;  /* double free tripwire */ +	free(bmp); +} + +/* + * The routines _getbit() and _setbit() are the only + * routines that actually understand the layout of bmp->maskp[]. + * + * On little endian architectures, this could simply be an array of + * bytes.  But the kernel layout of bitmasks _is_ visible to userspace + * via the sched_(set/get)affinity calls in Linux 2.6, and on big + * endian architectures, it is painfully obvious that this is an + * array of unsigned longs. + */ + +/* Return the value (0 or 1) of bit n in bitmask bmp */ +static unsigned int _getbit(const struct bitmask *bmp, unsigned int n) +{ +	if (n < bmp->size) +		return (bmp->maskp[n/bitsperlong] >> (n % bitsperlong)) & 1; +	else +		return 0; +} + +/* Set bit n in bitmask bmp to value v (0 or 1) */ +static void _setbit(struct bitmask *bmp, unsigned int n, unsigned int v) +{ +	if (n < bmp->size) { +		if (v) +			bmp->maskp[n/bitsperlong] |= 1UL << (n % bitsperlong); +		else +			bmp->maskp[n/bitsperlong] &= +				~(1UL << (n % bitsperlong)); +	} +} + +/* + * When parsing bitmask lists, only allow numbers, separated by one + * of the allowed next characters. + * + * The parameter 'sret' is the return from a sscanf "%u%c".  It is + * -1 if the sscanf input string was empty.  It is 0 if the first + * character in the sscanf input string was not a decimal number. + * It is 1 if the unsigned number matching the "%u" was the end of the + * input string.  It is 2 if one or more additional characters followed + * the matched unsigned number.  If it is 2, then 'nextc' is the first + * character following the number.  The parameter 'ok_next_chars' + * is the nul-terminated list of allowed next characters. + * + * The mask term just scanned was ok if and only if either the numbers + * matching the %u were all of the input or if the next character in + * the input past the numbers was one of the allowed next characters. + */ +static int scan_was_ok(int sret, char nextc, const char *ok_next_chars) +{ +	return sret == 1 || +		(sret == 2 && strchr(ok_next_chars, nextc) != NULL); +} + +static const char *nexttoken(const char *q,  int sep) +{ +	if (q) +		q = strchr(q, sep); +	if (q) +		q++; +	return q; +} + +/* Set a single bit i in bitmask */ +struct bitmask *bitmask_setbit(struct bitmask *bmp, unsigned int i) +{ +	_setbit(bmp, i, 1); +	return bmp; +} + +/* Set all bits in bitmask: bmp = ~0 */ +struct bitmask *bitmask_setall(struct bitmask *bmp) +{ +	unsigned int i; +	for (i = 0; i < bmp->size; i++) +		_setbit(bmp, i, 1); +	return bmp; +} + +/* Clear all bits in bitmask: bmp = 0 */ +struct bitmask *bitmask_clearall(struct bitmask *bmp) +{ +	unsigned int i; +	for (i = 0; i < bmp->size; i++) +		_setbit(bmp, i, 0); +	return bmp; +} + +/* True if all bits are clear */ +int bitmask_isallclear(const struct bitmask *bmp) +{ +	unsigned int i; +	for (i = 0; i < bmp->size; i++) +		if (_getbit(bmp, i)) +			return 0; +	return 1; +} + +/* True if specified bit i is set */ +int bitmask_isbitset(const struct bitmask *bmp, unsigned int i) +{ +	return _getbit(bmp, i); +} + +/* Number of lowest set bit (min) */ +unsigned int bitmask_first(const struct bitmask *bmp) +{ +	return bitmask_next(bmp, 0); +} + +/* Number of highest set bit (max) */ +unsigned int bitmask_last(const struct bitmask *bmp) +{ +	unsigned int i; +	unsigned int m = bmp->size; +	for (i = 0; i < bmp->size; i++) +		if (_getbit(bmp, i)) +			m = i; +	return m; +} + +/* Number of next set bit at or above given bit i */ +unsigned int bitmask_next(const struct bitmask *bmp, unsigned int i) +{ +	unsigned int n; +	for (n = i; n < bmp->size; n++) +		if (_getbit(bmp, n)) +			break; +	return n; +} + +/* + * Parses a comma-separated list of numbers and ranges of numbers, + * with optional ':%u' strides modifying ranges, into provided bitmask. + * Some examples of input lists and their equivalent simple list: + *	Input		Equivalent to + *	0-3		0,1,2,3 + *	0-7:2		0,2,4,6 + *	1,3,5-7		1,3,5,6,7 + *	0-3:2,8-15:4	0,2,8,12 + */ +int bitmask_parselist(const char *buf, struct bitmask *bmp) +{ +	const char *p, *q; + +	bitmask_clearall(bmp); + +	q = buf; +	while (p = q, q = nexttoken(q, ','), p) { +		unsigned int a;		/* begin of range */ +		unsigned int b;		/* end of range */ +		unsigned int s;		/* stride */ +		const char *c1, *c2;	/* next tokens after '-' or ',' */ +		char nextc;		/* char after sscanf %u match */ +		int sret;		/* sscanf return (number of matches) */ + +		sret = sscanf(p, "%u%c", &a, &nextc); +		if (!scan_was_ok(sret, nextc, ",-")) +			goto err; +		b = a; +		s = 1; +		c1 = nexttoken(p, '-'); +		c2 = nexttoken(p, ','); +		if (c1 != NULL && (c2 == NULL || c1 < c2)) { +			sret = sscanf(c1, "%u%c", &b, &nextc); +			if (!scan_was_ok(sret, nextc, ",:")) +				goto err; +			c1 = nexttoken(c1, ':'); +			if (c1 != NULL && (c2 == NULL || c1 < c2)) { +				sret = sscanf(c1, "%u%c", &s, &nextc); +				if (!scan_was_ok(sret, nextc, ",")) +					goto err; +			} +		} +		if (!(a <= b)) +			goto err; +		if (b >= bmp->size) +			goto err; +		while (a <= b) { +			_setbit(bmp, a, 1); +			a += s; +		} +	} +	return 0; +err: +	bitmask_clearall(bmp); +	return -1; +} + +/* + * emit(buf, buflen, rbot, rtop, len) + * + * Helper routine for bitmask_displaylist().  Write decimal number + * or range to buf+len, suppressing output past buf+buflen, with optional + * comma-prefix.  Return len of what would be written to buf, if it + * all fit. + */ + +static inline int emit(char *buf, int buflen, int rbot, int rtop, int len) +{ +	if (len > 0) +		len += snprintf(buf + len, max(buflen - len, 0), ","); +	if (rbot == rtop) +		len += snprintf(buf + len, max(buflen - len, 0), "%d", rbot); +	else +		len += snprintf(buf + len, max(buflen - len, 0), "%d-%d", +				rbot, rtop); +	return len; +} + +/* + * Write decimal list representation of bmp to buf. + * + * Output format is a comma-separated list of decimal numbers and + * ranges.  Consecutively set bits are shown as two hyphen-separated + * decimal numbers, the smallest and largest bit numbers set in + * the range.  Output format is compatible with the format + * accepted as input by bitmap_parselist(). + * + * The return value is the number of characters which would be + * generated for the given input, excluding the trailing '\0', as + * per ISO C99. + */ + +int bitmask_displaylist(char *buf, int buflen, const struct bitmask *bmp) +{ +	int len = 0; +	/* current bit is 'cur', most recently seen range is [rbot, rtop] */ +	unsigned int cur, rbot, rtop; + +	if (buflen > 0) +		*buf = 0; +	rbot = cur = bitmask_first(bmp); +	while (cur < bmp->size) { +		rtop = cur; +		cur = bitmask_next(bmp, cur+1); +		if (cur >= bmp->size || cur > rtop + 1) { +			len = emit(buf, buflen, rbot, rtop, len); +			rbot = cur; +		} +	} +	return len; +} diff --git a/tools/power/cpupower/utils/helpers/bitmask.h b/tools/power/cpupower/utils/helpers/bitmask.h new file mode 100644 index 00000000000..eb289df4105 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/bitmask.h @@ -0,0 +1,33 @@ +#ifndef __CPUPOWER_BITMASK__ +#define __CPUPOWER_BITMASK__ + +/* Taken over from libbitmask, a project initiated from sgi: + * Url:            http://oss.sgi.com/projects/cpusets/ + * Unfortunately it's not very widespread, therefore relevant parts are + * pasted here. + */ + +struct bitmask { +	unsigned int size; +	unsigned long *maskp; +}; + +struct bitmask *bitmask_alloc(unsigned int n); +void bitmask_free(struct bitmask *bmp); + +struct bitmask *bitmask_setbit(struct bitmask *bmp, unsigned int i); +struct bitmask *bitmask_setall(struct bitmask *bmp); +struct bitmask *bitmask_clearall(struct bitmask *bmp); + +unsigned int bitmask_first(const struct bitmask *bmp); +unsigned int bitmask_next(const struct bitmask *bmp, unsigned int i); +unsigned int bitmask_last(const struct bitmask *bmp); +int bitmask_isallclear(const struct bitmask *bmp); +int bitmask_isbitset(const struct bitmask *bmp, unsigned int i); + +int bitmask_parselist(const char *buf, struct bitmask *bmp); +int bitmask_displaylist(char *buf, int len, const struct bitmask *bmp); + + + +#endif /*__CPUPOWER_BITMASK__ */ diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c new file mode 100644 index 00000000000..93b0aa74ca0 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/cpuid.c @@ -0,0 +1,178 @@ +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#include "helpers/helpers.h" + +static const char *cpu_vendor_table[X86_VENDOR_MAX] = { +	"Unknown", "GenuineIntel", "AuthenticAMD", +}; + +#if defined(__i386__) || defined(__x86_64__) + +/* from gcc */ +#include <cpuid.h> + +/* + * CPUID functions returning a single datum + * + * Define unsigned int cpuid_e[abcd]x(unsigned int op) + */ +#define cpuid_func(reg)					\ +	unsigned int cpuid_##reg(unsigned int op)	\ +	{						\ +	unsigned int eax, ebx, ecx, edx;		\ +	__cpuid(op, eax, ebx, ecx, edx);		\ +	return reg;					\ +	} +cpuid_func(eax); +cpuid_func(ebx); +cpuid_func(ecx); +cpuid_func(edx); + +#endif /* defined(__i386__) || defined(__x86_64__) */ + +/* get_cpu_info + * + * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo + * + * Returns 0 on success or a negativ error code + * + * TBD: Should there be a cpuid alternative for this if /proc is not mounted? + */ +int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info) +{ +	FILE *fp; +	char value[64]; +	unsigned int proc, x; +	unsigned int unknown = 0xffffff; +	unsigned int cpuid_level, ext_cpuid_level; + +	int ret = -EINVAL; + +	cpu_info->vendor		= X86_VENDOR_UNKNOWN; +	cpu_info->family		= unknown; +	cpu_info->model			= unknown; +	cpu_info->stepping		= unknown; +	cpu_info->caps			= 0; + +	fp = fopen("/proc/cpuinfo", "r"); +	if (!fp) +		return -EIO; + +	while (!feof(fp)) { +		if (!fgets(value, 64, fp)) +			continue; +		value[63 - 1] = '\0'; + +		if (!strncmp(value, "processor\t: ", 12)) +			sscanf(value, "processor\t: %u", &proc); + +		if (proc != cpu) +			continue; + +		/* Get CPU vendor */ +		if (!strncmp(value, "vendor_id", 9)) { +			for (x = 1; x < X86_VENDOR_MAX; x++) { +				if (strstr(value, cpu_vendor_table[x])) +					cpu_info->vendor = x; +			} +		/* Get CPU family, etc. */ +		} else if (!strncmp(value, "cpu family\t: ", 13)) { +			sscanf(value, "cpu family\t: %u", +			       &cpu_info->family); +		} else if (!strncmp(value, "model\t\t: ", 9)) { +			sscanf(value, "model\t\t: %u", +			       &cpu_info->model); +		} else if (!strncmp(value, "stepping\t: ", 10)) { +			sscanf(value, "stepping\t: %u", +			       &cpu_info->stepping); + +			/* Exit -> all values must have been set */ +			if (cpu_info->vendor == X86_VENDOR_UNKNOWN || +			    cpu_info->family == unknown || +			    cpu_info->model == unknown || +			    cpu_info->stepping == unknown) { +				ret = -EINVAL; +				goto out; +			} + +			ret = 0; +			goto out; +		} +	} +	ret = -ENODEV; +out: +	fclose(fp); +	/* Get some useful CPU capabilities from cpuid */ +	if (cpu_info->vendor != X86_VENDOR_AMD && +	    cpu_info->vendor != X86_VENDOR_INTEL) +		return ret; + +	cpuid_level	= cpuid_eax(0); +	ext_cpuid_level	= cpuid_eax(0x80000000); + +	/* Invariant TSC */ +	if (ext_cpuid_level >= 0x80000007 && +	    (cpuid_edx(0x80000007) & (1 << 8))) +		cpu_info->caps |= CPUPOWER_CAP_INV_TSC; + +	/* Aperf/Mperf registers support */ +	if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1)) +		cpu_info->caps |= CPUPOWER_CAP_APERF; + +	/* AMD Boost state enable/disable register */ +	if (cpu_info->vendor == X86_VENDOR_AMD) { +		if (ext_cpuid_level >= 0x80000007 && +		    (cpuid_edx(0x80000007) & (1 << 9))) +			cpu_info->caps |= CPUPOWER_CAP_AMD_CBP; +	} + +	if (cpu_info->vendor == X86_VENDOR_INTEL) { +		if (cpuid_level >= 6 && +		    (cpuid_eax(6) & (1 << 1))) +			cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; +	} + +	if (cpu_info->vendor == X86_VENDOR_INTEL) { +		/* Intel's perf-bias MSR support */ +		if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) +			cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; + +		/* Intel's Turbo Ratio Limit support */ +		if (cpu_info->family == 6) { +			switch (cpu_info->model) { +			case 0x1A:	/* Core i7, Xeon 5500 series +					 * Bloomfield, Gainstown NHM-EP +					 */ +			case 0x1E:	/* Core i7 and i5 Processor +					 * Clarksfield, Lynnfield, Jasper Forest +					 */ +			case 0x1F:	/* Core i7 and i5 Processor - Nehalem */ +			case 0x25:	/* Westmere Client +					 * Clarkdale, Arrandale +					 */ +			case 0x2C:	/* Westmere EP - Gulftown */ +				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; +			case 0x2A:	/* SNB */ +			case 0x2D:	/* SNB Xeon */ +			case 0x3A:	/* IVB */ +			case 0x3E:	/* IVB Xeon */ +				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; +				cpu_info->caps |= CPUPOWER_CAP_IS_SNB; +				break; +			case 0x2E:	/* Nehalem-EX Xeon - Beckton */ +			case 0x2F:	/* Westmere-EX Xeon - Eagleton */ +			default: +				break; +			} +		} +	} + +	/*	printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", +		cpuid_level, ext_cpuid_level, cpu_info->caps); +	*/ +	return ret; +} diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h new file mode 100644 index 00000000000..aa9e95486a2 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -0,0 +1,195 @@ +/* + *  (C) 2010,2011       Thomas Renninger <trenn@suse.de>, Novell Inc. + * + *  Licensed under the terms of the GNU GPL License version 2. + * + * Miscellaneous helpers which do not fit or are worth + * to put into separate headers + */ + +#ifndef __CPUPOWERUTILS_HELPERS__ +#define __CPUPOWERUTILS_HELPERS__ + +#include <libintl.h> +#include <locale.h> + +#include "helpers/bitmask.h" + +/* Internationalization ****************************/ +#ifdef NLS + +#define _(String) gettext(String) +#ifndef gettext_noop +#define gettext_noop(String) String +#endif +#define N_(String) gettext_noop(String) + +#else /* !NLS */ + +#define _(String) String +#define N_(String) String + +#endif +/* Internationalization ****************************/ + +extern int run_as_root; +extern struct bitmask *cpus_chosen; + +/* Global verbose (-d) stuff *********************************/ +/* + * define DEBUG via global Makefile variable + * Debug output is sent to stderr, do: + * cpupower monitor 2>/tmp/debug + * to split debug output away from normal output +*/ +#ifdef DEBUG +extern int be_verbose; + +#define dprint(fmt, ...) {					\ +		if (be_verbose) {				\ +			fprintf(stderr, "%s: " fmt,		\ +				__func__, ##__VA_ARGS__);	\ +		}						\ +	} +#else +static inline void dprint(const char *fmt, ...) { } +#endif +extern int be_verbose; +/* Global verbose (-v) stuff *********************************/ + +/* cpuid and cpuinfo helpers  **************************/ +enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL, +			  X86_VENDOR_AMD, X86_VENDOR_MAX}; + +#define CPUPOWER_CAP_INV_TSC		0x00000001 +#define CPUPOWER_CAP_APERF		0x00000002 +#define CPUPOWER_CAP_AMD_CBP		0x00000004 +#define CPUPOWER_CAP_PERF_BIAS		0x00000008 +#define CPUPOWER_CAP_HAS_TURBO_RATIO	0x00000010 +#define CPUPOWER_CAP_IS_SNB		0x00000020 +#define CPUPOWER_CAP_INTEL_IDA		0x00000040 + +#define MAX_HW_PSTATES 10 + +struct cpupower_cpu_info { +	enum cpupower_cpu_vendor vendor; +	unsigned int family; +	unsigned int model; +	unsigned int stepping; +	/* CPU capabilities read out from cpuid */ +	unsigned long long caps; +}; + +/* get_cpu_info + * + * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo + * + * Returns 0 on success or a negativ error code + * Only used on x86, below global's struct values are zero/unknown on + * other archs + */ +extern int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info); +extern struct cpupower_cpu_info cpupower_cpu_info; +/* cpuid and cpuinfo helpers  **************************/ + +struct cpuid_core_info { +	int pkg; +	int core; +	int cpu; + +	/* flags */ +	unsigned int is_online:1; +}; + +/* CPU topology/hierarchy parsing ******************/ +struct cpupower_topology { +	/* Amount of CPU cores, packages and threads per core in the system */ +	unsigned int cores; +	unsigned int pkgs; +	unsigned int threads; /* per core */ + +	/* Array gets mallocated with cores entries, holding per core info */ +	struct cpuid_core_info *core_info; +}; + +extern int get_cpu_topology(struct cpupower_topology *cpu_top); +extern void cpu_topology_release(struct cpupower_topology cpu_top); + +/* CPU topology/hierarchy parsing ******************/ + +/* X86 ONLY ****************************************/ +#if defined(__i386__) || defined(__x86_64__) + +#include <pci/pci.h> + +/* Read/Write msr ****************************/ +extern int read_msr(int cpu, unsigned int idx, unsigned long long *val); +extern int write_msr(int cpu, unsigned int idx, unsigned long long val); + +extern int msr_intel_set_perf_bias(unsigned int cpu, unsigned int val); +extern int msr_intel_get_perf_bias(unsigned int cpu); +extern unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu); + +/* Read/Write msr ****************************/ + +/* PCI stuff ****************************/ +extern int amd_pci_get_num_boost_states(int *active, int *states); +extern struct pci_dev *pci_acc_init(struct pci_access **pacc, int domain, +				    int bus, int slot, int func, int vendor, +				    int dev); +extern struct pci_dev *pci_slot_func_init(struct pci_access **pacc, +					      int slot, int func); + +/* PCI stuff ****************************/ + +/* AMD HW pstate decoding **************************/ + +extern int decode_pstates(unsigned int cpu, unsigned int cpu_family, +			  int boost_states, unsigned long *pstates, int *no); + +/* AMD HW pstate decoding **************************/ + +extern int cpufreq_has_boost_support(unsigned int cpu, int *support, +				     int *active, int * states); +/* + * CPUID functions returning a single datum + */ +unsigned int cpuid_eax(unsigned int op); +unsigned int cpuid_ebx(unsigned int op); +unsigned int cpuid_ecx(unsigned int op); +unsigned int cpuid_edx(unsigned int op); + +/* cpuid and cpuinfo helpers  **************************/ +/* X86 ONLY ********************************************/ +#else +static inline int decode_pstates(unsigned int cpu, unsigned int cpu_family, +				 int boost_states, unsigned long *pstates, +				 int *no) +{ return -1; }; + +static inline int read_msr(int cpu, unsigned int idx, unsigned long long *val) +{ return -1; }; +static inline int write_msr(int cpu, unsigned int idx, unsigned long long val) +{ return -1; }; +static inline int msr_intel_set_perf_bias(unsigned int cpu, unsigned int val) +{ return -1; }; +static inline int msr_intel_get_perf_bias(unsigned int cpu) +{ return -1; }; +static inline unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu) +{ return 0; }; + +/* Read/Write msr ****************************/ + +static inline int cpufreq_has_boost_support(unsigned int cpu, int *support, +					    int *active, int * states) +{ return -1; } + +/* cpuid and cpuinfo helpers  **************************/ + +static inline unsigned int cpuid_eax(unsigned int op) { return 0; }; +static inline unsigned int cpuid_ebx(unsigned int op) { return 0; }; +static inline unsigned int cpuid_ecx(unsigned int op) { return 0; }; +static inline unsigned int cpuid_edx(unsigned int op) { return 0; }; +#endif /* defined(__i386__) || defined(__x86_64__) */ + +#endif /* __CPUPOWERUTILS_HELPERS__ */ diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c new file mode 100644 index 00000000000..1609243f5c6 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -0,0 +1,27 @@ +#if defined(__i386__) || defined(__x86_64__) + +#include "helpers/helpers.h" + +int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, +			int *states) +{ +	struct cpupower_cpu_info cpu_info; +	int ret; + +	*support = *active = *states = 0; + +	ret = get_cpu_info(0, &cpu_info); +	if (ret) +		return ret; + +	if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_CBP) { +		*support = 1; +		amd_pci_get_num_boost_states(active, states); +		if (ret <= 0) +			return ret; +		*support = 1; +	} else if (cpupower_cpu_info.caps & CPUPOWER_CAP_INTEL_IDA) +		*support = *active = 1; +	return 0; +} +#endif /* #if defined(__i386__) || defined(__x86_64__) */ diff --git a/tools/power/cpupower/utils/helpers/msr.c b/tools/power/cpupower/utils/helpers/msr.c new file mode 100644 index 00000000000..31a4b24a8bc --- /dev/null +++ b/tools/power/cpupower/utils/helpers/msr.c @@ -0,0 +1,115 @@ +#if defined(__i386__) || defined(__x86_64__) + +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> + +#include "helpers/helpers.h" + +/* Intel specific MSRs */ +#define MSR_IA32_PERF_STATUS		0x198 +#define MSR_IA32_MISC_ENABLES		0x1a0 +#define MSR_IA32_ENERGY_PERF_BIAS	0x1b0 +#define MSR_NEHALEM_TURBO_RATIO_LIMIT	0x1ad + +/* + * read_msr + * + * Will return 0 on success and -1 on failure. + * Possible errno values could be: + * EFAULT -If the read/write did not fully complete + * EIO    -If the CPU does not support MSRs + * ENXIO  -If the CPU does not exist + */ + +int read_msr(int cpu, unsigned int idx, unsigned long long *val) +{ +	int fd; +	char msr_file_name[64]; + +	sprintf(msr_file_name, "/dev/cpu/%d/msr", cpu); +	fd = open(msr_file_name, O_RDONLY); +	if (fd < 0) +		return -1; +	if (lseek(fd, idx, SEEK_CUR) == -1) +		goto err; +	if (read(fd, val, sizeof *val) != sizeof *val) +		goto err; +	close(fd); +	return 0; + err: +	close(fd); +	return -1; +} + +/* + * write_msr + * + * Will return 0 on success and -1 on failure. + * Possible errno values could be: + * EFAULT -If the read/write did not fully complete + * EIO    -If the CPU does not support MSRs + * ENXIO  -If the CPU does not exist + */ +int write_msr(int cpu, unsigned int idx, unsigned long long val) +{ +	int fd; +	char msr_file_name[64]; + +	sprintf(msr_file_name, "/dev/cpu/%d/msr", cpu); +	fd = open(msr_file_name, O_WRONLY); +	if (fd < 0) +		return -1; +	if (lseek(fd, idx, SEEK_CUR) == -1) +		goto err; +	if (write(fd, &val, sizeof val) != sizeof val) +		goto err; +	close(fd); +	return 0; + err: +	close(fd); +	return -1; +} + +int msr_intel_get_perf_bias(unsigned int cpu) +{ +	unsigned long long val; +	int ret; + +	if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS)) +		return -1; + +	ret = read_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, &val); +	if (ret) +		return ret; +	return val; +} + +int msr_intel_set_perf_bias(unsigned int cpu, unsigned int val) +{ +	int ret; + +	if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS)) +		return -1; + +	ret = write_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, val); +	if (ret) +		return ret; +	return 0; +} + +unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu) +{ +	unsigned long long val; +	int ret; + +	if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_HAS_TURBO_RATIO)) +		return -1; + +	ret = read_msr(cpu, MSR_NEHALEM_TURBO_RATIO_LIMIT, &val); +	if (ret) +		return ret; +	return val; +} +#endif diff --git a/tools/power/cpupower/utils/helpers/pci.c b/tools/power/cpupower/utils/helpers/pci.c new file mode 100644 index 00000000000..9690798e644 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/pci.c @@ -0,0 +1,55 @@ +#if defined(__i386__) || defined(__x86_64__) + +#include <helpers/helpers.h> + +/* + * pci_acc_init + * + * PCI access helper function depending on libpci + * + * **pacc : if a valid pci_dev is returned + *         *pacc must be passed to pci_acc_cleanup to free it + * + * domain: domain + * bus:    bus + * slot:   slot + * func:   func + * vendor: vendor + * device: device + * Pass -1 for one of the six above to match any + * + * Returns : + * struct pci_dev which can be used with pci_{read,write}_* functions + *                to access the PCI config space of matching pci devices + */ +struct pci_dev *pci_acc_init(struct pci_access **pacc, int domain, int bus, +			     int slot, int func, int vendor, int dev) +{ +	struct pci_filter filter_nb_link = { domain, bus, slot, func, +					     vendor, dev }; +	struct pci_dev *device; + +	*pacc = pci_alloc(); +	if (*pacc == NULL) +		return NULL; + +	pci_init(*pacc); +	pci_scan_bus(*pacc); + +	for (device = (*pacc)->devices; device; device = device->next) { +		if (pci_filter_match(&filter_nb_link, device)) +			return device; +	} +	pci_cleanup(*pacc); +	return NULL; +} + +/* Typically one wants to get a specific slot(device)/func of the root domain +   and bus */ +struct pci_dev *pci_slot_func_init(struct pci_access **pacc, int slot, +				       int func) +{ +	return pci_acc_init(pacc, 0, 0, slot, func, -1, -1); +} + +#endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c new file mode 100644 index 00000000000..851c7a16ca4 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/sysfs.c @@ -0,0 +1,472 @@ +/* + *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de> + *  (C) 2011       Thomas Renninger <trenn@novell.com> Novell Inc. + * + *  Licensed under the terms of the GNU GPL License version 2. + */ + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "helpers/sysfs.h" + +unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) +{ +	int fd; +	ssize_t numread; + +	fd = open(path, O_RDONLY); +	if (fd == -1) +		return 0; + +	numread = read(fd, buf, buflen - 1); +	if (numread < 1) { +		close(fd); +		return 0; +	} + +	buf[numread] = '\0'; +	close(fd); + +	return (unsigned int) numread; +} + +/* + * Detect whether a CPU is online + * + * Returns: + *     1 -> if CPU is online + *     0 -> if CPU is offline + *     negative errno values in error case + */ +int sysfs_is_cpu_online(unsigned int cpu) +{ +	char path[SYSFS_PATH_MAX]; +	int fd; +	ssize_t numread; +	unsigned long long value; +	char linebuf[MAX_LINE_LEN]; +	char *endp; +	struct stat statbuf; + +	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu); + +	if (stat(path, &statbuf) != 0) +		return 0; + +	/* +	 * kernel without CONFIG_HOTPLUG_CPU +	 * -> cpuX directory exists, but not cpuX/online file +	 */ +	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu); +	if (stat(path, &statbuf) != 0) +		return 1; + +	fd = open(path, O_RDONLY); +	if (fd == -1) +		return -errno; + +	numread = read(fd, linebuf, MAX_LINE_LEN - 1); +	if (numread < 1) { +		close(fd); +		return -EIO; +	} +	linebuf[numread] = '\0'; +	close(fd); + +	value = strtoull(linebuf, &endp, 0); +	if (value > 1 || value < 0) +		return -EINVAL; + +	return value; +} + +/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ + + +/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ + +/* + * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir + * exists. + * For example the functionality to disable c-states was introduced in later + * kernel versions, this function can be used to explicitly check for this + * feature. + * + * returns 1 if the file exists, 0 otherwise. + */ +unsigned int sysfs_idlestate_file_exists(unsigned int cpu, +					 unsigned int idlestate, +					 const char *fname) +{ +	char path[SYSFS_PATH_MAX]; +	struct stat statbuf; + + +	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", +		 cpu, idlestate, fname); +	if (stat(path, &statbuf) != 0) +		return 0; +	return 1; +} + +/* + * helper function to read file from /sys into given buffer + * fname is a relative path under "cpuX/cpuidle/stateX/" dir + * cstates starting with 0, C0 is not counted as cstate. + * This means if you want C1 info, pass 0 as idlestate param + */ +unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate, +			     const char *fname, char *buf, size_t buflen) +{ +	char path[SYSFS_PATH_MAX]; +	int fd; +	ssize_t numread; + +	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", +		 cpu, idlestate, fname); + +	fd = open(path, O_RDONLY); +	if (fd == -1) +		return 0; + +	numread = read(fd, buf, buflen - 1); +	if (numread < 1) { +		close(fd); +		return 0; +	} + +	buf[numread] = '\0'; +	close(fd); + +	return (unsigned int) numread; +} + +/*  + * helper function to write a new value to a /sys file + * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir + * + * Returns the number of bytes written or 0 on error + */ +static +unsigned int sysfs_idlestate_write_file(unsigned int cpu, +					unsigned int idlestate, +					const char *fname, +					const char *value, size_t len) +{ +	char path[SYSFS_PATH_MAX]; +	int fd; +	ssize_t numwrite; + +	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", +		 cpu, idlestate, fname); + +	fd = open(path, O_WRONLY); +	if (fd == -1) +		return 0; + +	numwrite = write(fd, value, len); +	if (numwrite < 1) { +		close(fd); +		return 0; +	} + +	close(fd); + +	return (unsigned int) numwrite; +} + +/* read access to files which contain one numeric value */ + +enum idlestate_value { +	IDLESTATE_USAGE, +	IDLESTATE_POWER, +	IDLESTATE_LATENCY, +	IDLESTATE_TIME, +	IDLESTATE_DISABLE, +	MAX_IDLESTATE_VALUE_FILES +}; + +static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = { +	[IDLESTATE_USAGE] = "usage", +	[IDLESTATE_POWER] = "power", +	[IDLESTATE_LATENCY] = "latency", +	[IDLESTATE_TIME]  = "time", +	[IDLESTATE_DISABLE]  = "disable", +}; + +static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu, +						     unsigned int idlestate, +						     enum idlestate_value which) +{ +	unsigned long long value; +	unsigned int len; +	char linebuf[MAX_LINE_LEN]; +	char *endp; + +	if (which >= MAX_IDLESTATE_VALUE_FILES) +		return 0; + +	len = sysfs_idlestate_read_file(cpu, idlestate, +					idlestate_value_files[which], +					linebuf, sizeof(linebuf)); +	if (len == 0) +		return 0; + +	value = strtoull(linebuf, &endp, 0); + +	if (endp == linebuf || errno == ERANGE) +		return 0; + +	return value; +} + +/* read access to files which contain one string */ + +enum idlestate_string { +	IDLESTATE_DESC, +	IDLESTATE_NAME, +	MAX_IDLESTATE_STRING_FILES +}; + +static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { +	[IDLESTATE_DESC] = "desc", +	[IDLESTATE_NAME] = "name", +}; + + +static char *sysfs_idlestate_get_one_string(unsigned int cpu, +					unsigned int idlestate, +					enum idlestate_string which) +{ +	char linebuf[MAX_LINE_LEN]; +	char *result; +	unsigned int len; + +	if (which >= MAX_IDLESTATE_STRING_FILES) +		return NULL; + +	len = sysfs_idlestate_read_file(cpu, idlestate, +					idlestate_string_files[which], +					linebuf, sizeof(linebuf)); +	if (len == 0) +		return NULL; + +	result = strdup(linebuf); +	if (result == NULL) +		return NULL; + +	if (result[strlen(result) - 1] == '\n') +		result[strlen(result) - 1] = '\0'; + +	return result; +} + +/* + * Returns: + *    1  if disabled + *    0  if enabled + *    -1 if idlestate is not available + *    -2 if disabling is not supported by the kernel + */ +int sysfs_is_idlestate_disabled(unsigned int cpu, +				unsigned int idlestate) +{ +	if (sysfs_get_idlestate_count(cpu) <= idlestate) +		return -1; + +	if (!sysfs_idlestate_file_exists(cpu, idlestate, +				 idlestate_value_files[IDLESTATE_DISABLE])) +		return -2; +	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); +} + +/* + * Pass 1 as last argument to disable or 0 to enable the state + * Returns: + *    0  on success + *    negative values on error, for example: + *      -1 if idlestate is not available + *      -2 if disabling is not supported by the kernel + *      -3 No write access to disable/enable C-states + */ +int sysfs_idlestate_disable(unsigned int cpu, +			    unsigned int idlestate, +			    unsigned int disable) +{ +	char value[SYSFS_PATH_MAX]; +	int bytes_written; + +	if (sysfs_get_idlestate_count(cpu) <= idlestate) +		return -1; + +	if (!sysfs_idlestate_file_exists(cpu, idlestate, +				 idlestate_value_files[IDLESTATE_DISABLE])) +		return -2; + +	snprintf(value, SYSFS_PATH_MAX, "%u", disable); + +	bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable", +						   value, sizeof(disable)); +	if (bytes_written) +		return 0; +	return -3; +} + +unsigned long sysfs_get_idlestate_latency(unsigned int cpu, +					  unsigned int idlestate) +{ +	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); +} + +unsigned long sysfs_get_idlestate_usage(unsigned int cpu, +					unsigned int idlestate) +{ +	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE); +} + +unsigned long long sysfs_get_idlestate_time(unsigned int cpu, +					unsigned int idlestate) +{ +	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME); +} + +char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate) +{ +	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME); +} + +char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate) +{ +	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC); +} + +/* + * Returns number of supported C-states of CPU core cpu + * Negativ in error case + * Zero if cpuidle does not export any C-states + */ +unsigned int sysfs_get_idlestate_count(unsigned int cpu) +{ +	char file[SYSFS_PATH_MAX]; +	struct stat statbuf; +	int idlestates = 1; + + +	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); +	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) +		return -ENODEV; + +	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); +	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) +		return 0; + +	while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { +		snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU +			 "cpu%u/cpuidle/state%d", cpu, idlestates); +		idlestates++; +	} +	idlestates--; +	return idlestates; +} + +/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ + +/* + * helper function to read file from /sys into given buffer + * fname is a relative path under "cpu/cpuidle/" dir + */ +static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, +					    size_t buflen) +{ +	char path[SYSFS_PATH_MAX]; + +	snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); + +	return sysfs_read_file(path, buf, buflen); +} + + + +/* read access to files which contain one string */ + +enum cpuidle_string { +	CPUIDLE_GOVERNOR, +	CPUIDLE_GOVERNOR_RO, +	CPUIDLE_DRIVER, +	MAX_CPUIDLE_STRING_FILES +}; + +static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { +	[CPUIDLE_GOVERNOR]	= "current_governor", +	[CPUIDLE_GOVERNOR_RO]	= "current_governor_ro", +	[CPUIDLE_DRIVER]	= "current_driver", +}; + + +static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which) +{ +	char linebuf[MAX_LINE_LEN]; +	char *result; +	unsigned int len; + +	if (which >= MAX_CPUIDLE_STRING_FILES) +		return NULL; + +	len = sysfs_cpuidle_read_file(cpuidle_string_files[which], +				linebuf, sizeof(linebuf)); +	if (len == 0) +		return NULL; + +	result = strdup(linebuf); +	if (result == NULL) +		return NULL; + +	if (result[strlen(result) - 1] == '\n') +		result[strlen(result) - 1] = '\0'; + +	return result; +} + +char *sysfs_get_cpuidle_governor(void) +{ +	char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); +	if (!tmp) +		return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); +	else +		return tmp; +} + +char *sysfs_get_cpuidle_driver(void) +{ +	return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); +} +/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ + +/* + * Get sched_mc or sched_smt settings + * Pass "mc" or "smt" as argument + * + * Returns negative value on failure + */ +int sysfs_get_sched(const char *smt_mc) +{ +	return -ENODEV; +} + +/* + * Get sched_mc or sched_smt settings + * Pass "mc" or "smt" as argument + * + * Returns negative value on failure + */ +int sysfs_set_sched(const char *smt_mc, int val) +{ +	return -ENODEV; +} diff --git a/tools/power/cpupower/utils/helpers/sysfs.h b/tools/power/cpupower/utils/helpers/sysfs.h new file mode 100644 index 00000000000..d28f11fedbd --- /dev/null +++ b/tools/power/cpupower/utils/helpers/sysfs.h @@ -0,0 +1,38 @@ +#ifndef __CPUPOWER_HELPERS_SYSFS_H__ +#define __CPUPOWER_HELPERS_SYSFS_H__ + +#define PATH_TO_CPU "/sys/devices/system/cpu/" +#define MAX_LINE_LEN 255 +#define SYSFS_PATH_MAX 255 + +extern unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen); + +extern unsigned int sysfs_idlestate_file_exists(unsigned int cpu, +						unsigned int idlestate, +						const char *fname); + +extern int sysfs_is_cpu_online(unsigned int cpu); + +extern int sysfs_is_idlestate_disabled(unsigned int cpu, +				       unsigned int idlestate); +extern int sysfs_idlestate_disable(unsigned int cpu, unsigned int idlestate, +				   unsigned int disable); +extern unsigned long sysfs_get_idlestate_latency(unsigned int cpu, +						unsigned int idlestate); +extern unsigned long sysfs_get_idlestate_usage(unsigned int cpu, +					unsigned int idlestate); +extern unsigned long long sysfs_get_idlestate_time(unsigned int cpu, +						unsigned int idlestate); +extern char *sysfs_get_idlestate_name(unsigned int cpu, +				unsigned int idlestate); +extern char *sysfs_get_idlestate_desc(unsigned int cpu, +				unsigned int idlestate); +extern unsigned int sysfs_get_idlestate_count(unsigned int cpu); + +extern char *sysfs_get_cpuidle_governor(void); +extern char *sysfs_get_cpuidle_driver(void); + +extern int sysfs_get_sched(const char *smt_mc); +extern int sysfs_set_sched(const char *smt_mc, int val); + +#endif /* __CPUPOWER_HELPERS_SYSFS_H__ */ diff --git a/tools/power/cpupower/utils/helpers/topology.c b/tools/power/cpupower/utils/helpers/topology.c new file mode 100644 index 00000000000..c13120af519 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/topology.c @@ -0,0 +1,116 @@ +/* + *  (C) 2010,2011       Thomas Renninger <trenn@suse.de>, Novell Inc. + * + *  Licensed under the terms of the GNU GPL License version 2. + * + * ToDo: Needs to be done more properly for AMD/Intel specifics + */ + +/* Helper struct for qsort, must be in sync with cpupower_topology.cpu_info */ +/* Be careful: Need to pass unsigned to the sort, so that offlined cores are +   in the end, but double check for -1 for offlined cpus at other places */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include <helpers/helpers.h> +#include <helpers/sysfs.h> + +/* returns -1 on failure, 0 on success */ +static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result) +{ +	char linebuf[MAX_LINE_LEN]; +	char *endp; +	char path[SYSFS_PATH_MAX]; + +	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s", +			 cpu, fname); +	if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0) +		return -1; +	*result = strtol(linebuf, &endp, 0); +	if (endp == linebuf || errno == ERANGE) +		return -1; +	return 0; +} + +static int __compare(const void *t1, const void *t2) +{ +	struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1; +	struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2; +	if (top1->pkg < top2->pkg) +		return -1; +	else if (top1->pkg > top2->pkg) +		return 1; +	else if (top1->core < top2->core) +		return -1; +	else if (top1->core > top2->core) +		return 1; +	else if (top1->cpu < top2->cpu) +		return -1; +	else if (top1->cpu > top2->cpu) +		return 1; +	else +		return 0; +} + +/* + * Returns amount of cpus, negative on error, cpu_top must be + * passed to cpu_topology_release to free resources + * + * Array is sorted after ->pkg, ->core, then ->cpu + */ +int get_cpu_topology(struct cpupower_topology *cpu_top) +{ +	int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF); + +	cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus); +	if (cpu_top->core_info == NULL) +		return -ENOMEM; +	cpu_top->pkgs = cpu_top->cores = 0; +	for (cpu = 0; cpu < cpus; cpu++) { +		cpu_top->core_info[cpu].cpu = cpu; +		cpu_top->core_info[cpu].is_online = sysfs_is_cpu_online(cpu); +		if(sysfs_topology_read_file( +			cpu, +			"physical_package_id", +			&(cpu_top->core_info[cpu].pkg)) < 0) +			return -1; +		if(sysfs_topology_read_file( +			cpu, +			"core_id", +			&(cpu_top->core_info[cpu].core)) < 0) +			return -1; +	} + +	qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), +	      __compare); + +	/* Count the number of distinct pkgs values. This works +	   because the primary sort of the core_info struct was just +	   done by pkg value. */ +	last_pkg = cpu_top->core_info[0].pkg; +	for(cpu = 1; cpu < cpus; cpu++) { +		if(cpu_top->core_info[cpu].pkg != last_pkg) { +			last_pkg = cpu_top->core_info[cpu].pkg; +			cpu_top->pkgs++; +		} +	} +	cpu_top->pkgs++; + +	/* Intel's cores count is not consecutively numbered, there may +	 * be a core_id of 3, but none of 2. Assume there always is 0 +	 * Get amount of cores by counting duplicates in a package +	for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) { +		if (cpu_top->core_info[cpu].core == 0) +	cpu_top->cores++; +	*/ +	return cpus; +} + +void cpu_topology_release(struct cpupower_topology cpu_top) +{ +	free(cpu_top.core_info); +}  | 
