diff options
author | Dominik Brodowski <linux@dominikbrodowski.net> | 2011-03-30 18:30:11 +0400 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2011-07-29 20:35:36 +0400 |
commit | 7fe2f6399a84760a9af8896ac152728250f82adb (patch) | |
tree | fa4bf236359b8d6d9f8d6ff823ddd3e839da5768 /tools/power/cpupower/utils/helpers | |
parent | 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe (diff) | |
download | linux-7fe2f6399a84760a9af8896ac152728250f82adb.tar.xz |
cpupowerutils - cpufrequtils extended with quite some features
CPU power consumption vs performance tuning is no longer
limited to CPU frequency switching anymore: deep sleep states,
traditional dynamic frequency scaling and hidden turbo/boost
frequencies are tied close together and depend on each other.
The first two exist on different architectures like PPC, Itanium and
ARM, the latter (so far) only on X86. On X86 the APU (CPU+GPU) will
only run most efficiently if CPU and GPU has proper power management
in place.
Users and Developers want to have *one* tool to get an overview what
their system supports and to monitor and debug CPU power management
in detail. The tool should compile and work on as many architectures
as possible.
Once this tool stabilizes a bit, it is intended to replace the
Intel-specific tools in tools/power/x86
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'tools/power/cpupower/utils/helpers')
-rw-r--r-- | tools/power/cpupower/utils/helpers/amd.c | 137 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/bitmask.c | 290 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/bitmask.h | 33 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/cpuid.c | 145 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/helpers.h | 180 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/misc.c | 34 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/msr.c | 122 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/pci.c | 44 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/sysfs.c | 350 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/sysfs.h | 23 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/topology.c | 108 |
11 files changed, 1466 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 000000000000..5e44e31fc7f9 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -0,0 +1,137 @@ +#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; + int vendor_id = 0x1022; + int boost_dev_ids[4] = {0x1204, 0x1604, 0x1704, 0}; + struct pci_dev *device; + uint8_t val = 0; + + *active = *states = 0; + + device = pci_acc_init(&pci_acc, vendor_id, boost_dev_ids); + + 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 000000000000..60f4d69bb20d --- /dev/null +++ b/tools/power/cpupower/utils/helpers/bitmask.c @@ -0,0 +1,290 @@ +#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 000000000000..eb289df41053 --- /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 000000000000..71021f3bb69d --- /dev/null +++ b/tools/power/cpupower/utils/helpers/cpuid.c @@ -0,0 +1,145 @@ +#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; + } + + /* Intel's perf-bias MSR support */ + if (cpu_info->vendor == X86_VENDOR_INTEL) { + if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) + cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; + } + + /* 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 000000000000..a487dadb4cf0 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -0,0 +1,180 @@ +/* + * (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 ****************************/ +#define _(String) gettext(String) +#ifndef gettext_noop +#define gettext_noop(String) String +#endif +#define N_(String) gettext_noop (String) +/* 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, \ + __FUNCTION__, ##__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 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 **************************/ + + +/* 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 { + int pkg; + int core; + int cpu; + } *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 int msr_intel_has_boost_support(unsigned int cpu); +extern int msr_intel_boost_is_active(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 vendor_id, + int *dev_ids); + +/* 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 int msr_intel_has_boost_support(unsigned int cpu) +{ return -1; }; +static inline int msr_intel_boost_is_active(unsigned int cpu) +{ return -1; }; + +/* 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 000000000000..c1566e93e0ec --- /dev/null +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -0,0 +1,34 @@ +#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.vendor == X86_VENDOR_INTEL) { + ret = msr_intel_has_boost_support(cpu); + if (ret <= 0) + return ret; + *support = ret; + ret = msr_intel_boost_is_active(cpu); + if (ret <= 0) + return ret; + *active = ret; + } + 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 000000000000..93d48bd56e57 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/msr.c @@ -0,0 +1,122 @@ +#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 + +/* + * 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_has_boost_support(unsigned int cpu) +{ + unsigned long long misc_enables; + int ret; + + ret = read_msr(cpu, MSR_IA32_MISC_ENABLES, &misc_enables); + if (ret) + return ret; + return (misc_enables >> 38) & 0x1; +} + +int msr_intel_boost_is_active(unsigned int cpu) +{ + unsigned long long perf_status; + int ret; + + ret = read_msr(cpu, MSR_IA32_PERF_STATUS, &perf_status); + if (ret) + return ret; + return (perf_status >> 32) & 0x1; +} + +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; +} +#endif diff --git a/tools/power/cpupower/utils/helpers/pci.c b/tools/power/cpupower/utils/helpers/pci.c new file mode 100644 index 000000000000..8dcc93813716 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/pci.c @@ -0,0 +1,44 @@ +#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 + * + * vendor_id : the pci vendor id matching the pci device to access + * dev_ids : device ids matching the pci device to access + * + * 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 vendor_id, + int *dev_ids) +{ + struct pci_filter filter_nb_link = { -1, -1, -1, -1, vendor_id, 0}; + struct pci_dev *device; + unsigned int i; + + *pacc = pci_alloc(); + if (*pacc == NULL) + return NULL; + + pci_init(*pacc); + pci_scan_bus(*pacc); + + for (i = 0; dev_ids[i] != 0; i++) { + filter_nb_link.device = dev_ids[i]; + for (device=(*pacc)->devices; device; device = device->next) { + if (pci_filter_match(&filter_nb_link, device)) + return device; + } + } + pci_cleanup(*pacc); + return NULL; +} +#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 000000000000..0c534e79652b --- /dev/null +++ b/tools/power/cpupower/utils/helpers/sysfs.c @@ -0,0 +1,350 @@ +/* + * (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; + size_t numread; + + if ( ( fd = open(path, O_RDONLY) ) == -1 ) + return 0; + + numread = read(fd, buf, buflen - 1); + if ( numread < 1 ) + { + close(fd); + return 0; + } + + buf[numread] = '\0'; + close(fd); + + return numread; +} + +static unsigned int sysfs_write_file(const char *path, + const char *value, size_t len) +{ + int fd; + size_t numwrite; + + if ( ( fd = open(path, O_WRONLY) ) == -1 ) + return 0; + + numwrite = write(fd, value, len); + if ( numwrite < 1 ) + { + close(fd); + return 0; + } + close(fd); + return numwrite; +} + +/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ + +/* + * 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; + size_t numread; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", + cpu, idlestate, fname); + + if ( ( fd = open(path, O_RDONLY) ) == -1 ) + return 0; + + numread = read(fd, buf, buflen - 1); + if ( numread < 1 ) + { + close(fd); + return 0; + } + + buf[numread] = '\0'; + close(fd); + + return numread; +} + +/* read access to files which contain one numeric value */ + +enum idlestate_value { + IDLESTATE_USAGE, + IDLESTATE_POWER, + IDLESTATE_LATENCY, + IDLESTATE_TIME, + 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", +}; + +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; + + if ( ( len = sysfs_idlestate_read_file(cpu, idlestate, + idlestate_value_files[which], + linebuf, sizeof(linebuf))) == 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; + + if ( ( len = sysfs_idlestate_read_file(cpu, idlestate, + idlestate_string_files[which], + linebuf, sizeof(linebuf))) == 0 ) + return NULL; + + if ( ( result = strdup(linebuf) ) == NULL ) + return NULL; + + if (result[strlen(result) - 1] == '\n') + result[strlen(result) - 1] = '\0'; + + return result; +} + +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 + */ +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; + + if ( ( len = sysfs_cpuidle_read_file(cpuidle_string_files[which], + linebuf, sizeof(linebuf))) == 0 ) + return NULL; + + if ( ( result = strdup(linebuf) ) == 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) +{ + unsigned long value; + char linebuf[MAX_LINE_LEN]; + char *endp; + char path[SYSFS_PATH_MAX]; + + if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc)) + return -EINVAL; + + snprintf(path, sizeof(path), PATH_TO_CPU "sched_%s_power_savings", smt_mc); + if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0 ) + return -1; + value = strtoul(linebuf, &endp, 0); + if ( endp == linebuf || errno == ERANGE ) + return -1; + return value; +} + +/* + * 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) +{ + char linebuf[MAX_LINE_LEN]; + char path[SYSFS_PATH_MAX]; + struct stat statbuf; + + if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc)) + return -EINVAL; + + snprintf(path, sizeof(path), PATH_TO_CPU "sched_%s_power_savings", smt_mc); + sprintf(linebuf, "%d", val); + + if ( stat(path, &statbuf) != 0 ) + return -ENODEV; + + if (sysfs_write_file(path, linebuf, MAX_LINE_LEN) == 0 ) + return -1; + return 0; +} diff --git a/tools/power/cpupower/utils/helpers/sysfs.h b/tools/power/cpupower/utils/helpers/sysfs.h new file mode 100644 index 000000000000..5d02d2fc70e1 --- /dev/null +++ b/tools/power/cpupower/utils/helpers/sysfs.h @@ -0,0 +1,23 @@ +#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 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 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 000000000000..5ad842b956bb --- /dev/null +++ b/tools/power/cpupower/utils/helpers/topology.c @@ -0,0 +1,108 @@ +/* + * (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 */ +int sysfs_topology_read_file(unsigned int cpu, const char *fname) +{ + unsigned long value; + 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; + value = strtoul(linebuf, &endp, 0); + if ( endp == linebuf || errno == ERANGE ) + return -1; + return value; +} + +struct cpuid_core_info { + unsigned int pkg; + unsigned int thread; + unsigned int cpu; +}; + +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->thread < top2->thread) + return -1; + else if (top1->thread > top2->thread) + 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, cpus = sysconf(_SC_NPROCESSORS_CONF); + + cpu_top->core_info = malloc(sizeof(struct cpupower_topology) * 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].pkg = + sysfs_topology_read_file(cpu, "physical_package_id"); + if ((int)cpu_top->core_info[cpu].pkg != -1 && + cpu_top->core_info[cpu].pkg > cpu_top->pkgs) + cpu_top->pkgs = cpu_top->core_info[cpu].pkg; + cpu_top->core_info[cpu].core = + sysfs_topology_read_file(cpu, "core_id"); + cpu_top->core_info[cpu].cpu = cpu; + } + cpu_top->pkgs++; + + qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), + __compare); + + /* 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); +} |