Re: [PATCH 1/2] Support PCU power metrics in turbostat

From: Len Brown
Date: Tue Mar 24 2015 - 17:20:27 EST


<cc: linux-pm list>

On Thu, Nov 13, 2014 at 6:19 PM, Andi Kleen <andi@xxxxxxxxxxxxxx> wrote:
> From: Andi Kleen <ak@xxxxxxxxxxxxxxx>
>
> Add support for reading PCU power metrics on Sandy Bridge / Ivy Bridge EP
> and Haswell Server in turbostat. This is done using the perf ABI,
> using the perf uncore driver. This requires the kernel to
> have uncore perf driver support.

What happens if kernel doesn't include that support?

> The PCU has a large number of events, but only allows to monitor
> four of them at the same time. We always need the PCU cycles
> event, so this leaves three events per event group.
>
> The user has to specify the event group using a new -x option. All
> more sensible option characters were already taken. When -x is
> not specified no behavior changes.

I'm concerned that turbostat cmdline is getting too complicated,
and this makes that more the case.

would need EXAMPLES in turbostat.8 to really be useful.

> perf in principle supports time based multi-plexing, which
> allows monitoring multiple group at the same time, but support for
> that is not implemented in the tool so far. It would
> also require enabling an potentially idle-disturbing timer.
> So right now we don't multiplex.
>
> I modeled the event groups after the proven ones in
> pcm-power in the PCM (Intel Performance Counter Monitor) tool.
> That is where the numbers come from.
>
> However unlike PCM this uses the perf interfaces, instead
> of directly accessing the hardware.
>
> The current groups are:
>
> 0: This should 3 monitor frequency bands. It could give more
> accurate information than the average frequency from turbostat,
> as it can keep multiple buckets.
>
> However this currently runs into a problem with the uncore
> driver that only makes us able to monitor a single band.
> Disabled until this is fixed.
>
> 1: C-state residencies. Already covered in turbostat and not
> implemented.
>
> 2/3/4: Various reasons for frequency limits.
>
> 5: Number of frequency transitions and time of PCU transition duration.
> (note this is not the full time of the transition)
>
> Other power metrics could be added later using the same frame work.
> For example it would be possible to implement the memory power saving
> metrics from pcm-power, which would output power state statistics per
> channel. This would definitely need a new output format,
> as it won't fit into any terminal with the current one.

More columns are no longer an issue.
The latest turbostat has two modes -- default is just topology & frequency.
The --debug option adds all metrics, and output is generally
re-directed to a file.

>
> Custom user metrics would be also possible.
>
> The event resolution code is derived from the jevents library
> (parts of pmu-tools, http://github.com/andikleen/pmu-tools)
> and is BSD licensed.

can we put BSD licensed code into utilities that are in the linux
kernel git tree?

I sort of like a single source file, but if the code really is
unrelated, I guess 2nd file is okay.

thanks,
-Len


> Signed-off-by: Andi Kleen <ak@xxxxxxxxxxxxxxx>
> ---
> tools/power/x86/turbostat/Makefile | 11 +-
> tools/power/x86/turbostat/resolve.c | 233 ++++++++++++++++++++++++++++++++++
> tools/power/x86/turbostat/resolve.h | 2 +
> tools/power/x86/turbostat/turbostat.8 | 5 +
> tools/power/x86/turbostat/turbostat.c | 206 +++++++++++++++++++++++++++++-
> 5 files changed, 449 insertions(+), 8 deletions(-)
> create mode 100644 tools/power/x86/turbostat/resolve.c
> create mode 100644 tools/power/x86/turbostat/resolve.h
>
> diff --git a/tools/power/x86/turbostat/Makefile b/tools/power/x86/turbostat/Makefile
> index d1b3a36..745af06 100644
> --- a/tools/power/x86/turbostat/Makefile
> +++ b/tools/power/x86/turbostat/Makefile
> @@ -3,17 +3,20 @@ BUILD_OUTPUT := $(PWD)
> PREFIX := /usr
> DESTDIR :=
>
> -turbostat : turbostat.c
> +turbostat : turbostat.o resolve.o
> + @mkdir -p $(BUILD_OUTPUT)
> + $(CC) $(LDFLAGS) $^ -o $(BUILD_OUTPUT)/$@
> +
> CFLAGS += -Wall
> CFLAGS += -DMSRHEADER='"../../../../arch/x86/include/uapi/asm/msr-index.h"'
>
> -%: %.c
> +%.o: %.c
> @mkdir -p $(BUILD_OUTPUT)
> - $(CC) $(CFLAGS) $< -o $(BUILD_OUTPUT)/$@
> + $(CC) $(CFLAGS) $< -c -o $(BUILD_OUTPUT)/$@
>
> .PHONY : clean
> clean :
> - @rm -f $(BUILD_OUTPUT)/turbostat
> + @rm -f $(BUILD_OUTPUT)/turbostat turbostat.o resolve.o
>
> install : turbostat
> install -d $(DESTDIR)$(PREFIX)/bin
> diff --git a/tools/power/x86/turbostat/resolve.c b/tools/power/x86/turbostat/resolve.c
> new file mode 100644
> index 0000000..ad53159
> --- /dev/null
> +++ b/tools/power/x86/turbostat/resolve.c
> @@ -0,0 +1,233 @@
> +/* Resolve perf style event descriptions to attr */
> +/*
> + * Copyright (c) 2014, Intel Corporation
> + * Author: Andi Kleen
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are met:
> + *
> + * 1. Redistributions of source code must retain the above copyright notice,
> + * this list of conditions and the following disclaimer.
> + *
> + * 2. Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the distribution.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> +*/
> +
> +#define _GNU_SOURCE 1
> +#include "resolve.h"
> +#include <linux/perf_event.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdarg.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +#include <sys/fcntl.h>
> +#include <err.h>
> +
> +#define MAXFILE 4096
> +
> +static int read_file(char **val, const char *fmt, ...)
> +{
> + char *fn;
> + va_list ap;
> + int fd;
> + int ret = -1;
> + int len;
> +
> + *val = malloc(MAXFILE);
> + if (!*val)
> + err(1, "out of memory");
> + va_start(ap, fmt);
> + vasprintf(&fn, fmt, ap);
> + va_end(ap);
> + fd = open(fn, O_RDONLY);
> + free(fn);
> + if (fd >= 0) {
> + if ((len = read(fd, *val, MAXFILE - 1)) > 0) {
> + ret = 0;
> + (*val)[len] = 0;
> + }
> + close(fd);
> + }
> + if (ret < 0) {
> + free(*val);
> + *val = NULL;
> + }
> + return ret;
> +}
> +
> +#define BITS(x) ((1U << (x)) - 1)
> +
> +static bool try_parse(char *format, char *fmt, __u64 val, __u64 *config)
> +{
> + int start, end;
> + int n = sscanf(format, fmt, &start, &end);
> + if (n == 1)
> + end = start + 1;
> + if (n == 0)
> + return false;
> + *config |= (val & BITS(end - start + 1)) << start;
> + return true;
> +}
> +
> +static int read_qual(char *qual, struct perf_event_attr *attr)
> +{
> + while (*qual) {
> + switch (*qual) {
> + case 'p':
> + attr->precise_ip++;
> + break;
> + case 'k':
> + attr->exclude_user = 1;
> + break;
> + case 'u':
> + attr->exclude_kernel = 1;
> + break;
> + case 'h':
> + attr->exclude_guest = 1;
> + break;
> + /* XXX more */
> + default:
> + fprintf(stderr, "Unknown modifier %c at end\n", *qual);
> + return -1;
> + }
> + qual++;
> + }
> + return 0;
> +}
> +
> +static bool special_attr(char *name, int val, struct perf_event_attr *attr)
> +{
> + if (!strcmp(name, "period")) {
> + attr->sample_period = val;
> + return true;
> + }
> + if (!strcmp(name, "freq")) {
> + attr->sample_freq = val;
> + attr->freq = 1;
> + return true;
> + }
> + return false;
> +}
> +
> +static int parse_terms(char *pmu, char *config, struct perf_event_attr *attr, int recur)
> +{
> + char *format = NULL;
> + char *term;
> +
> + char *newl = strchr(config, '\n');
> + if (newl)
> + *newl = 0;
> +
> + while ((term = strsep(&config, ",")) != NULL) {
> + char name[30];
> + int n, val = 1;
> +
> + n = sscanf(term, "%30[^=]=%i", name, &val);
> + if (n < 1)
> + break;
> + if (special_attr(name, val, attr))
> + continue;
> + free(format);
> + if (read_file(&format, "/sys/devices/%s/format/%s", pmu, name) < 0) {
> + char *alias = NULL;
> +
> + if (recur == 0 &&
> + read_file(&alias, "/sys/devices/%s/events/%s", pmu, name) == 0) {
> + if (parse_terms(pmu, alias, attr, 1) < 0) {
> + free(alias);
> + fprintf(stderr, "Cannot parse kernel event alias %s\n", name);
> + break;
> + }
> + free(alias);
> + continue;
> + }
> + fprintf(stderr, "Cannot parse qualifier %s\n", name);
> + break;
> + }
> + bool ok = try_parse(format, "config:%d-%d", val, &attr->config) ||
> + try_parse(format, "config:%d", val, &attr->config) ||
> + try_parse(format, "config1:%d-%d", val, &attr->config1) ||
> + try_parse(format, "config1:%d", val, &attr->config1) ||
> + try_parse(format, "config2:%d-%d", val, &attr->config2) ||
> + try_parse(format, "config2:%d", val, &attr->config2);
> + if (!ok) {
> + fprintf(stderr, "Cannot parse kernel format %s: %s\n",
> + name, format);
> + break;
> + }
> + }
> + free(format);
> + if (term)
> + return -1;
> + return 0;
> +}
> +
> +
> +/* Resolve perf new style event descriptor to perf ATTR. User must initialize
> + * attr->sample_type and attr->read_format as needed after this call,
> + * and possibly other fields.
> + */
> +int tjevent_name_to_attr(char *str, struct perf_event_attr *attr)
> +{
> + char pmu[30], config[200];
> + int qual_off;
> +
> + memset(attr, 0, sizeof(struct perf_event_attr));
> + attr->size = PERF_ATTR_SIZE_VER1;
> +
> + if (sscanf(str, "%30[^/]/%200[^/]/%n", pmu, config, &qual_off) < 2)
> + return -1;
> + char *type = NULL;
> + if (read_file(&type, "/sys/devices/%s/type", pmu) < 0)
> + return -1;
> + attr->type = atoi(type);
> + free(type);
> + if (parse_terms(pmu, config, attr, 0) < 0)
> + return -1;
> + if (read_qual(str + qual_off, attr) < 0)
> + return -1;
> + return 0;
> +}
> +
> +#ifdef TEST
> +#include <asm/unistd.h>
> +int main(int ac, char **av)
> +{
> + struct perf_event_attr attr = { 0 };
> + int ret = 1;
> +
> + if (!av[1]) {
> + printf("Usage: ... perf-event-to-parse\n");
> + exit(1);
> + }
> + while (*++av) {
> + if (jevent_name_to_attr(*av, &attr) < 0)
> + printf("cannot parse %s\n", *av);
> + printf("config %llx config1 %llx\n", attr.config, attr.config1);
> + int fd;
> + if ((fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0)) < 0)
> + perror("perf_event_open");
> + else
> + ret = 0;
> + close(fd);
> + }
> + return ret;
> +}
> +#endif
> diff --git a/tools/power/x86/turbostat/resolve.h b/tools/power/x86/turbostat/resolve.h
> new file mode 100644
> index 0000000..286e798
> --- /dev/null
> +++ b/tools/power/x86/turbostat/resolve.h
> @@ -0,0 +1,2 @@
> +struct perf_event_attr;
> +int tjevent_name_to_attr(char *str, struct perf_event_attr *attr);
> diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8
> index 56bfb52..e91d837 100644
> --- a/tools/power/x86/turbostat/turbostat.8
> +++ b/tools/power/x86/turbostat/turbostat.8
> @@ -42,6 +42,11 @@ The \fB-M MSR#\fP option includes the the specified 64-bit MSR value.
> The \fB-i interval_sec\fP option prints statistics every \fiinterval_sec\fP seconds.
> The default is 5 seconds.
> .PP
> +The \fB-x GROUP\fP option enables PCU event group monitoring. Valid groups are
> +2 for thermal frequency limits, 3 and 4 for other frequency limits,
> +5 for frequency transitions statistics. Requires the kernel
> +to support the perf uncore driver for this platform.
> +.PP
> The \fBcommand\fP parameter forks \fBcommand\fP and upon its exit,
> displays the statistics gathered since it was forked.
> .PP
> diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
> index 5b1b807..5c23f00 100644
> --- a/tools/power/x86/turbostat/turbostat.c
> +++ b/tools/power/x86/turbostat/turbostat.c
> @@ -38,6 +38,9 @@
> #include <ctype.h>
> #include <sched.h>
> #include <cpuid.h>
> +#include <sys/syscall.h>
> +#include <linux/perf_event.h>
> +#include "resolve.h"
>
> char *proc_stat = "/proc/stat";
> unsigned int interval_sec = 5; /* set with -i interval_sec */
> @@ -81,6 +84,11 @@ unsigned int tcc_activation_temp;
> unsigned int tcc_activation_temp_override;
> double rapl_power_units, rapl_energy_units, rapl_time_units;
> double rapl_joule_counter_range;
> +int do_pcu_group = -1;
> +int is_jkt;
> +int is_ivt;
> +int is_hsx;
> +int max_package_id;
>
> #define RAPL_PKG (1 << 0)
> /* 0x610 MSR_PKG_POWER_LIMIT */
> @@ -159,7 +167,7 @@ struct pkg_data {
> unsigned int rapl_pkg_perf_status; /* MSR_PKG_PERF_STATUS */
> unsigned int rapl_dram_perf_status; /* MSR_DRAM_PERF_STATUS */
> unsigned int pkg_temp_c;
> -
> + unsigned long long pcu0, pcu1, pcu2, pcu3;
> } *package_even, *package_odd;
>
> #define ODD_COUNTERS thread_odd, core_odd, package_odd
> @@ -264,6 +272,8 @@ int get_msr(int cpu, off_t offset, unsigned long long *msr)
> return 0;
> }
>
> +static char *pcu_group_titles[][3];
> +
> /*
> * Example Format w/ field column widths:
> *
> @@ -353,6 +363,12 @@ void print_header(void)
> outp += sprintf(outp, " time");
>
> }
> + if (do_pcu_group >= 0) {
> + outp += sprintf(outp, " %-12s", pcu_group_titles[do_pcu_group][0]);
> + outp += sprintf(outp, " %-12s", pcu_group_titles[do_pcu_group][1]);
> + if (pcu_group_titles[do_pcu_group][2][0])
> + outp += sprintf(outp, " %-8s", pcu_group_titles[do_pcu_group][2]);
> + }
> outp += sprintf(outp, "\n");
> }
>
> @@ -406,6 +422,11 @@ int dump_counters(struct thread_data *t, struct core_data *c,
> outp += sprintf(outp, "Throttle RAM: %0X\n",
> p->rapl_dram_perf_status);
> outp += sprintf(outp, "PTM: %dC\n", p->pkg_temp_c);
> +
> + outp += sprintf(outp, "PCU0: %0llX\n", p->pcu0);
> + outp += sprintf(outp, "PCU1: %0llX\n", p->pcu1);
> + outp += sprintf(outp, "PCU2: %0llX\n", p->pcu2);
> + outp += sprintf(outp, "PCU3: %0llX\n", p->pcu3);
> }
>
> outp += sprintf(outp, "\n");
> @@ -413,6 +434,13 @@ int dump_counters(struct thread_data *t, struct core_data *c,
> return 0;
> }
>
> +static int add_percent(char *outp, unsigned long long val, double cycles)
> +{
> + if (val == 0)
> + return sprintf(outp, "%12s", "");
> + return sprintf(outp, " %12.2f", 100.0 * (val / cycles));
> +}
> +
> /*
> * column formatting convention & formats
> */
> @@ -581,6 +609,17 @@ int format_counters(struct thread_data *t, struct core_data *c,
> outp += sprintf(outp, fmt8, interval_float);
>
> }
> +
> + if (do_pcu_group >= 0) {
> + double pcu_cycles = p->pcu0;
> +
> + if (do_pcu_group == 5)
> + outp += sprintf(outp, " %12llu", p->pcu1);
> + else
> + outp += add_percent(outp, p->pcu2, pcu_cycles);
> + outp += add_percent(outp, p->pcu2, pcu_cycles);
> + outp += add_percent(outp, p->pcu3, pcu_cycles);
> + }
> done:
> outp += sprintf(outp, "\n");
>
> @@ -635,6 +674,10 @@ delta_package(struct pkg_data *new, struct pkg_data *old)
> old->pc9 = new->pc9 - old->pc9;
> old->pc10 = new->pc10 - old->pc10;
> old->pkg_temp_c = new->pkg_temp_c;
> + old->pcu0 = new->pcu0;
> + old->pcu1 = new->pcu1;
> + old->pcu2 = new->pcu2;
> + old->pcu3 = new->pcu3;
>
> DELTA_WRAP32(new->energy_pkg, old->energy_pkg);
> DELTA_WRAP32(new->energy_cores, old->energy_cores);
> @@ -783,6 +826,10 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data
> p->rapl_pkg_perf_status = 0;
> p->rapl_dram_perf_status = 0;
> p->pkg_temp_c = 0;
> + p->pcu0 = 0;
> + p->pcu1 = 0;
> + p->pcu2 = 0;
> + p->pcu3 = 0;
> }
> int sum_counters(struct thread_data *t, struct core_data *c,
> struct pkg_data *p)
> @@ -826,6 +873,11 @@ int sum_counters(struct thread_data *t, struct core_data *c,
>
> average.packages.rapl_pkg_perf_status += p->rapl_pkg_perf_status;
> average.packages.rapl_dram_perf_status += p->rapl_dram_perf_status;
> +
> + average.packages.pcu0 += p->pcu0;
> + average.packages.pcu1 += p->pcu1;
> + average.packages.pcu2 += p->pcu2;
> + average.packages.pcu3 += p->pcu3;
> return 0;
> }
> /*
> @@ -872,6 +924,137 @@ static unsigned long long rdtsc(void)
> return low | ((unsigned long long)high) << 32;
> }
>
> +/* Get PCU statistics:
> +
> + We can only measure three events at a time
> + (4 counters in the PCU, and one for the clock ticks event)
> +
> + To generate new events use the ucevent tool in pmu-tools
> + FORCECPU=cpu ucevent.py --resolve EVENT-NAME */
> +
> +static char *pcu_groups[][5] = {
> + /* Runs into problems with the uncore driver with the filters for now. */
> + [0] = { NULL },
> + /* group 1 is covered already */
> + [1] = { NULL },
> + [2] = { "uncore_pcu/event=0x0/", /* PCU.CLOCKTICKS */
> + "uncore_pcu/event=0x9/", /* PCU.PROCHOT_EXTERNAL_CYCLES */
> + "uncore_pcu/event=0xa/", /* PCU.PROCHOT_INTERNAL_CYCLES */
> + "uncore_pcu/event=0x4/", /* PCU.FREQ_MAX_LIMIT_THERMAL_CYCLES */
> + NULL },
> + [3] = { "uncore_pcu/event=0x0/", /* PCU.CLOCKTICKS */
> + "uncore_pcu/event=0x4/", /* PCU.FREQ_MAX_LIMIT_THERMAL_CYCLES */
> + "uncore_pcu/event=0x5/", /* PCU.FREQ_MAX_POWER_CYCLES */
> + "uncore_pcu/event=0x7/", /* PCU.FREQ_MAX_CURRENT_CYCLES */
> + NULL },
> + [4] = { "uncore_pcu/event=0x0/", /* PCU.CLOCKTICKS */
> + "uncore_pcu/event=0x6/", /* PCU.FREQ_MAX_OS_CYCLES */
> + "uncore_pcu/event=0x5/", /* PCU.FREQ_MAX_POWER_CYCLES */
> + "uncore_pcu/event=0x7/", /* PCU.FREQ_MAX_CURRENT_CYCLES */
> + NULL },
> + [5] = { "uncore_pcu/event=0x0/", /* PCU.CLOCKTICKS */
> + "uncore_pcu/event=0x60,edge=1/", /* PCU.FREQ_TRANS_CYCLES,edge */
> + "uncore_pcu/event=0x60/", /* PCU.FREQ_TRANS_CYCLES */
> + NULL },
> +};
> +
> +static char *pcu_group_titles[][3] = {
> + [0] = { "", "", "" },
> + [1] = { "", "", "" },
> + [2] = { "ProcHot-ext%", "ProcHot-int%", "Therm-Lim%" },
> + [3] = { "Therm-Lim%", "Power-Lim%", "Current-Lim%" },
> + [4] = { "OS-Limit%", "Power-Limit%", "Current-Lim%" },
> + [5] = { "Num-Freq-Trans", "Freq-Trans%", "" }
> +};
> +
> +#define NUM_COUNTER 4
> +
> +static void pcu_fixup_tables(void)
> +{
> + /* Table is for IVT. Fix up deltas to other CPUs */
> + if (is_hsx) {
> + pcu_groups[4][3] = NULL; /* No PCU.FREQ_MAX_CURRENT_CYCLES */
> + pcu_groups[3][3] = NULL;
> + pcu_group_titles[4][3] = NULL;
> + pcu_group_titles[3][3] = NULL;
> + } else if (is_jkt) {
> + pcu_groups[5][1] = "uncore_pcu/event=0x200000,edge=1/"; /* PCU.FREQ_TRANS_CYCLES */
> + pcu_groups[5][2] = "uncore_pcu/event=0x200000/";
> + }
> +}
> +
> +static int pcu_perf_init(int group, int cpu, int *pcu_fd)
> +{
> + int i;
> + char **evnames = pcu_groups[group];
> + pcu_fd[0] = -1;
> +
> + for (i = 0; evnames[i]; i++) {
> + struct perf_event_attr attr;
> + char *ev = evnames[i];
> +
> + if (tjevent_name_to_attr(ev, &attr) < 0) {
> + fprintf(stderr, "Cannot resolve %s\n", ev);
> + goto fallback;
> + }
> + attr.read_format = PERF_FORMAT_GROUP;
> + pcu_fd[i] = syscall(__NR_perf_event_open,
> + &attr,
> + -1,
> + cpu,
> + pcu_fd[0],
> + i == 0 ? PERF_FLAG_FD_OUTPUT : 0);
> + if (pcu_fd[i] < 0) {
> + fprintf(stderr, "cannot open perf event %s\n", ev);
> + goto fallback;
> + }
> + if (ev != evnames[i])
> + free(ev);
> + }
> + return 0;
> +fallback:
> + /* Don't error out */
> + do_pcu_group = -1;
> + while (--i >= 0) {
> + close(pcu_fd[i]);
> + pcu_fd[i] = -1;
> + }
> + return -1;
> +}
> +
> +int get_pcu_data(struct pkg_data *p)
> +{
> + static int **pcu_fds;
> + int *pcu_fd;
> + unsigned long long val[NUM_COUNTER + 3];
> +
> + if (!pcu_fds) {
> + pcu_fds = calloc(max_package_id + 1, sizeof(void *));
> + if (!pcu_fds)
> + err(1, "no memory");
> + pcu_fds[p->package_id] = calloc(NUM_COUNTER, sizeof(void *));
> + if (!pcu_fds[p->package_id])
> + err(1, "no memory");
> + }
> + pcu_fd = pcu_fds[p->package_id];
> + if (!pcu_fd[0]) {
> + if (pcu_perf_init(do_pcu_group, p->package_id, pcu_fd) < 0)
> + return 0;
> + if (!pcu_fd[0])
> + return 0;
> + }
> +
> + memset(val, 0, sizeof(val));
> + read(pcu_fd[0], val, sizeof(val));
> +
> + /* XXX scale by run time for multiplexing */
> + p->pcu0 = val[1 + 0];
> + p->pcu1 = val[1 + 1];
> + p->pcu2 = val[1 + 2];
> + p->pcu3 = val[1 + 3];
> +
> + return 0;
> +}
>
> /*
> * get_counters(...)
> @@ -1011,6 +1194,10 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
> return -17;
> p->pkg_temp_c = tcc_activation_temp - ((msr >> 16) & 0x7F);
> }
> + if (do_pcu_group >= 0) {
> + if (get_pcu_data(p))
> + return -18;
> + }
> return 0;
> }
>
> @@ -2070,6 +2257,9 @@ void check_cpuid()
> do_c8_c9_c10 = has_c8_c9_c10(family, model);
> do_slm_cstates = is_slm(family, model);
> bclk = discover_bclk(family, model);
> + is_jkt = genuine_intel && model == 45;
> + is_ivt = genuine_intel && model == 62;
> + is_hsx = genuine_intel && model == 63;
>
> do_nehalem_turbo_ratio_limit = has_nehalem_turbo_ratio_limit(family, model);
> do_ivt_turbo_ratio_limit = has_ivt_turbo_ratio_limit(family, model);
> @@ -2081,7 +2271,7 @@ void check_cpuid()
>
> void usage()
> {
> - errx(1, "%s: [-v][-R][-T][-p|-P|-S][-c MSR#][-C MSR#][-m MSR#][-M MSR#][-i interval_sec | command ...]\n",
> + errx(1, "%s: [-v][-R][-T][-p|-P|-S][-c MSR#][-C MSR#][-m MSR#][-M MSR#] [-xPCUGROUP] [-i interval_sec | command ...]\n",
> progname);
> }
>
> @@ -2107,7 +2297,6 @@ void topology_probe()
> {
> int i;
> int max_core_id = 0;
> - int max_package_id = 0;
> int max_siblings = 0;
> struct cpu_topology {
> int core_id;
> @@ -2319,6 +2508,10 @@ void turbostat_init()
>
> if (verbose)
> for_all_cpus(print_thermal, ODD_COUNTERS);
> +
> + pcu_fixup_tables();
> + if (!is_ivt && !is_jkt && is_hsx)
> + do_pcu_group = -1;
> }
>
> int fork_it(char **argv)
> @@ -2388,7 +2581,7 @@ void cmdline(int argc, char **argv)
>
> progname = argv[0];
>
> - while ((opt = getopt(argc, argv, "+pPsSvi:c:C:m:M:RJT:")) != -1) {
> + while ((opt = getopt(argc, argv, "+pPsSvi:c:C:m:M:RJT:x:")) != -1) {
> switch (opt) {
> case 'p':
> show_core_only++;
> @@ -2429,6 +2622,11 @@ void cmdline(int argc, char **argv)
> case 'J':
> rapl_joules++;
> break;
> + case 'x':
> + sscanf(optarg, "%d", &do_pcu_group);
> + if (do_pcu_group < 0 || do_pcu_group > 5)
> + usage();
> + break;
>
> default:
> usage();
> --
> 1.9.3
>



--
Len Brown, Intel Open Source Technology Center
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/