linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/4] perf/x86: add Intel RAPL PMU support
@ 2013-10-31 14:59 Stephane Eranian
  2013-10-31 14:59 ` [PATCH v4 1/4] perf: add active_entry list head to struct perf_event Stephane Eranian
                   ` (3 more replies)
  0 siblings, 4 replies; 15+ messages in thread
From: Stephane Eranian @ 2013-10-31 14:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: peterz, mingo, ak, acme, jolsa, zheng.z.yan, bp, maria.n.dimakopoulou

This patch adds a new uncore PMU to expose the Intel
RAPL (Running Average Power Limit) energy consumption counters.
Up to 3 counters, each counting a particular RAPL event are exposed.

The RAPL counters are available on Intel SandyBridge,
IvyBridge, Haswell. The server skus add a 3rd counter to measure
DRAM power consumption.

The following events are available nd exposed in sysfs:
- power/energy-cores: power consumption of all cores on socket
- power/energy-pkg: power consumption of all cores + LLc cache
- power/energy-dram: power consumption of DRAM (server skus only)

The RAPL PMU is uncore by nature and is implemented such
that it only works in system-wide mode. Measuring only
one CPU per socket is sufficient. The /sys/devices/rapl/cpumask
is exported and can be used by tools to figure out which CPU
to monitor by default. For instance, on a 2-socket system, 2 CPUs
(one on each socket) will be shown.

The counters all count in the same unit. The perf_events API
exposes all RAPL counters as 64-bit integers counting in unit
of 1/2^32 Joules (or 0.23 nJ). User level tools must convert
the counts by multiplying them by 0.23 and divide 10^9 to
obtain Joules.  The reason for this is that the kernel avoids
doing floating point math whenever possible because it is
expensive (user floating-point state must be saved). The method
used avoids kernel floating-point and minimizes the loss of
precision (bits). Thanks to PeterZ for suggesting this approach.

To convert the raw count in Watt: W = C * 0.23 / (1e9 * time)

The kernel exposes both the scaling factor (0.23 nJ) and the
unit (Joules) in sysfs:
$ ls -1 /sys/devices/power/events/energy-*
/sys/devices/power/events/energy-cores
/sys/devices/power/events/energy-cores.scale
/sys/devices/power/events/energy-cores.unit
/sys/devices/power/events/energy-pkg
/sys/devices/power/events/energy-pkg.scale
/sys/devices/power/events/energy-pkg.unit

$ cat /sys/devices/power/events/energy-cores.scale
2.3e-10

$ cat cat /sys/devices/power/events/energy-cores.unit
Joules

RAPL PMU is a new standalone PMU which registers with the
perf_event core subsystem. The PMU type (attr->type) is
dynamically allocated and is available from /sys/device/rapl/type.

Sampling is not supported by the RAPL PMU. There is no
privilege level filtering either.

The PMU exports a cpumask in /sys/devices/power/cpumask. It
is used by perf to ensure only one instance of each RAPL event
is measured per processor socket. Hotplug CPU is also supported.

The perf stat infrasrtructure is modified to now show event
unit. It also applies the scaling factor. As such it will print
RAPL events in Joules (and not increments on 0.23 nJ):

 # perf stat -a -e power/energy-pkg/,power/energy-cores/,cycles -I 1000 sleep 1000
 #          time             counts   unit events
     1.000282860               2.51 Joules power/energy-pkg/         [100.00%]
     1.000282860               0.31 Joules power/energy-cores/      
     1.000282860           37765378 ?      cycles                    [100.00%]

The patch adds a hrtimer to poll the counters given that
they do no interrupt on overflow. Hardware counters are 32-bit
wide.

In v2, we add the locking necesarry to protect the rapl_pmu
struct. We also add a description at the top of the file.
We check for Intel only processor. We improved the data
layout of the rapl_pmu struct. We also lifted the restriction
of the number of instances of RAPL counters that can be active
at the same time. RAPL is free running counters, so ought to be
able to measure events as many times as necessary in parallel
via multiple tools. There is never multiplexing among RAPL events.

In v3, we have renamed the event to be more generic power/* instead
of rapl/*. We have modified perf stat to print the event with the
unit and scaling factors.

In v4, we integrate the feedback from Jiri and rebase to 3.12-rc7+
from tip.git.

Supported CPUs: SandyBridge, IvyBridge, Haswell.

Signed-off-by: Stephane Eranian <eranian@google.com>

Stephane Eranian (4):
  perf: add active_entry list head to struct perf_event
  perf stat: add event unit and scale support
  perf,x86: add Intel RAPL PMU support
  perf,x86: add RAPL hrtimer support

 arch/x86/kernel/cpu/Makefile                |    2 +-
 arch/x86/kernel/cpu/perf_event_intel_rapl.c |  720 +++++++++++++++++++++++++++
 include/linux/perf_event.h                  |    5 +-
 kernel/events/core.c                        |    1 +
 tools/perf/builtin-stat.c                   |   72 ++-
 tools/perf/util/evsel.c                     |    2 +
 tools/perf/util/evsel.h                     |    3 +
 tools/perf/util/parse-events.c              |    1 +
 tools/perf/util/pmu.c                       |  170 ++++++-
 tools/perf/util/pmu.h                       |    3 +
 10 files changed, 956 insertions(+), 23 deletions(-)
 create mode 100644 arch/x86/kernel/cpu/perf_event_intel_rapl.c

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v4 1/4] perf: add active_entry list head to struct perf_event
  2013-10-31 14:59 [PATCH v4 0/4] perf/x86: add Intel RAPL PMU support Stephane Eranian
@ 2013-10-31 14:59 ` Stephane Eranian
  2013-10-31 14:59 ` [PATCH v4 2/4] perf stat: add event unit and scale support Stephane Eranian
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 15+ messages in thread
From: Stephane Eranian @ 2013-10-31 14:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: peterz, mingo, ak, acme, jolsa, zheng.z.yan, bp, maria.n.dimakopoulou

This patch adds a new field to the struct perf_event.
It is intended to be used to chain events which are
active (enabled). It helps in the hardware layer
for PMUs which do not have actual counter restrictions, i.e.,
free running read-only counters. Active events are chained
as opposed to being tracked via the counter they use.

To save space we use a union with hlist_entry as both
are mutually exclusive (suggested by Jiri Olsa).

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 include/linux/perf_event.h |    5 ++++-
 kernel/events/core.c       |    1 +
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 2e069d1..8f4a70f 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -319,7 +319,10 @@ struct perf_event {
 	 */
 	struct list_head		migrate_entry;
 
-	struct hlist_node		hlist_entry;
+	union {
+		struct hlist_node	hlist_entry;
+		struct list_head	active_entry;
+	};
 	int				nr_siblings;
 	int				group_flags;
 	struct perf_event		*group_leader;
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 17b3c6c..2de509b 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -6663,6 +6663,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
 	INIT_LIST_HEAD(&event->event_entry);
 	INIT_LIST_HEAD(&event->sibling_list);
 	INIT_LIST_HEAD(&event->rb_entry);
+	INIT_LIST_HEAD(&event->active_entry);
 
 	init_waitqueue_head(&event->waitq);
 	init_irq_work(&event->pending, perf_pending_event);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-10-31 14:59 [PATCH v4 0/4] perf/x86: add Intel RAPL PMU support Stephane Eranian
  2013-10-31 14:59 ` [PATCH v4 1/4] perf: add active_entry list head to struct perf_event Stephane Eranian
@ 2013-10-31 14:59 ` Stephane Eranian
  2013-11-01 10:57   ` Jiri Olsa
  2013-10-31 14:59 ` [PATCH v4 3/4] perf/x86: add Intel RAPL PMU support Stephane Eranian
  2013-10-31 14:59 ` [PATCH v4 4/4] perf,x86: add RAPL hrtimer support Stephane Eranian
  3 siblings, 1 reply; 15+ messages in thread
From: Stephane Eranian @ 2013-10-31 14:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: peterz, mingo, ak, acme, jolsa, zheng.z.yan, bp, maria.n.dimakopoulou

This patch adds perf stat support fo rhandling event units and
scales as exported by the kernel.

The kernel can export PMU events actual unit and scaling factor
via sysfs:
$ ls -1 /sys/devices/power/events/energy-*
/sys/devices/power/events/energy-cores
/sys/devices/power/events/energy-cores.scale
/sys/devices/power/events/energy-cores.unit
/sys/devices/power/events/energy-pkg
/sys/devices/power/events/energy-pkg.scale
/sys/devices/power/events/energy-pkg.unit
$ cat /sys/devices/power/events/energy-cores.scale
2.3e-10
$ cat cat /sys/devices/power/events/energy-cores.unit
Joules

This patch modifies the pmu event alias code to check
for the presence of the .unit and .scale files to load
the corresponding values. They are then used by perf stat
transparentely:

 # perf stat -a -e power/energy-pkg/,power/energy-cores/,cycles -I 1000 sleep 1000
 #          time             counts   unit events
     1.000214717               3.07 Joules power/energy-pkg/         [100.00%]
     1.000214717               0.53 Joules power/energy-cores/
     1.000214717           12965028      ? cycles                    [100.00%]
     2.000749289               3.01 Joules power/energy-pkg/
     2.000749289               0.52 Joules power/energy-cores/
     2.000749289           15817043      ? cycles

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 tools/perf/builtin-stat.c      |   72 ++++++++++++-----
 tools/perf/util/evsel.c        |    2 +
 tools/perf/util/evsel.h        |    3 +
 tools/perf/util/parse-events.c |    1 +
 tools/perf/util/pmu.c          |  170 +++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/pmu.h          |    3 +
 6 files changed, 230 insertions(+), 21 deletions(-)

diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 1a9c95d..43dea3b 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -138,6 +138,7 @@ static const char		*post_cmd			= NULL;
 static bool			sync_run			= false;
 static unsigned int		interval			= 0;
 static unsigned int		initial_delay			= 0;
+static unsigned int		unit_width			= 4; /* strlen("unit") */
 static bool			forever				= false;
 static struct timespec		ref_time;
 static struct cpu_map		*aggr_map;
@@ -462,17 +463,17 @@ static void print_interval(void)
 	if (num_print_interval == 0 && !csv_output) {
 		switch (aggr_mode) {
 		case AGGR_SOCKET:
-			fprintf(output, "#           time socket cpus             counts events\n");
+			fprintf(output, "#           time socket cpus             counts  %*s events\n", unit_width, "unit");
 			break;
 		case AGGR_CORE:
-			fprintf(output, "#           time core         cpus             counts events\n");
+			fprintf(output, "#           time core         cpus             counts %*s events\n", unit_width, "unit");
 			break;
 		case AGGR_NONE:
-			fprintf(output, "#           time CPU                 counts events\n");
+			fprintf(output, "#           time CPU                counts %*s events\n", unit_width, "unit");
 			break;
 		case AGGR_GLOBAL:
 		default:
-			fprintf(output, "#           time             counts events\n");
+			fprintf(output, "#           time             counts %*s events\n", unit_width, "unit");
 		}
 	}
 
@@ -517,6 +518,7 @@ static int __run_perf_stat(int argc, const char **argv)
 	unsigned long long t0, t1;
 	struct perf_evsel *counter;
 	struct timespec ts;
+	size_t l;
 	int status = 0;
 	const bool forks = (argc > 0);
 
@@ -566,6 +568,10 @@ static int __run_perf_stat(int argc, const char **argv)
 			return -1;
 		}
 		counter->supported = true;
+
+		l = strlen(counter->unit);
+		if (l > unit_width)
+			unit_width = l;
 	}
 
 	if (perf_evlist__apply_filters(evsel_list)) {
@@ -911,19 +917,32 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
 	double total, ratio = 0.0, total2;
 	const char *fmt;
 
-	if (csv_output)
-		fmt = "%.0f%s%s";
-	else if (big_num)
-		fmt = "%'18.0f%s%-25s";
-	else
-		fmt = "%18.0f%s%-25s";
+	if (csv_output) {
+		if (evsel->scale != 1.0)
+			fmt = "%.2f%s%s%s%s";
+		else
+			fmt = "%.0f%s%s%s%s";
+	} else if (big_num)
+		if (evsel->scale != 1.0)
+			fmt = "%'18.2f%s%-*s%s%-25s";
+		else
+			fmt = "%'18.0f%s%-*s%s%-25s";
+	else {
+		if (evsel->scale != 1.0)
+			fmt = "%18.2f%s%-*s%s%-25s";
+		else
+			fmt = "%18.0f%s%-*s%s%-25s";
+	}
 
 	aggr_printout(evsel, cpu, nr);
 
 	if (aggr_mode == AGGR_GLOBAL)
 		cpu = 0;
 
-	fprintf(output, fmt, avg, csv_sep, perf_evsel__name(evsel));
+	if (csv_output)
+		fprintf(output, fmt, avg, csv_sep, evsel->unit, csv_sep, perf_evsel__name(evsel));
+	else
+		fprintf(output, fmt, avg, csv_sep, unit_width, evsel->unit, csv_sep, perf_evsel__name(evsel));
 
 	if (evsel->cgrp)
 		fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
@@ -1062,6 +1081,7 @@ static void print_aggr(char *prefix)
 {
 	struct perf_evsel *counter;
 	int cpu, cpu2, s, s2, id, nr;
+	double uval;
 	u64 ena, run, val;
 
 	if (!(aggr_map || aggr_get_id))
@@ -1088,9 +1108,13 @@ static void print_aggr(char *prefix)
 			if (run == 0 || ena == 0) {
 				aggr_printout(counter, id, nr);
 
-				fprintf(output, "%*s%s%*s",
+				fprintf(output, "%*s%s%*s%s%*s",
 					csv_output ? 0 : 18,
 					counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
+
+					csv_sep,
+					csv_output ? 0 : -10,
+					counter->unit,
 					csv_sep,
 					csv_output ? 0 : -24,
 					perf_evsel__name(counter));
@@ -1102,11 +1126,12 @@ static void print_aggr(char *prefix)
 				fputc('\n', output);
 				continue;
 			}
+			uval = val * counter->scale;
 
 			if (nsec_counter(counter))
-				nsec_printout(id, nr, counter, val);
+				nsec_printout(id, nr, counter, uval);
 			else
-				abs_printout(id, nr, counter, val);
+				abs_printout(id, nr, counter, uval);
 
 			if (!csv_output) {
 				print_noise(counter, 1.0);
@@ -1129,6 +1154,7 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
 	struct perf_stat *ps = counter->priv;
 	double avg = avg_stats(&ps->res_stats[0]);
 	int scaled = counter->counts->scaled;
+	double uval;
 
 	if (prefix)
 		fprintf(output, "%s", prefix);
@@ -1148,10 +1174,12 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
 		return;
 	}
 
+	uval = avg * counter->scale;
+
 	if (nsec_counter(counter))
-		nsec_printout(-1, 0, counter, avg);
+		nsec_printout(-1, 0, counter, uval);
 	else
-		abs_printout(-1, 0, counter, avg);
+		abs_printout(-1, 0, counter, uval);
 
 	print_noise(counter, avg);
 
@@ -1178,6 +1206,7 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
 static void print_counter(struct perf_evsel *counter, char *prefix)
 {
 	u64 ena, run, val;
+	double uval;
 	int cpu;
 
 	for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
@@ -1189,12 +1218,15 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
 			fprintf(output, "%s", prefix);
 
 		if (run == 0 || ena == 0) {
-			fprintf(output, "CPU%*d%s%*s%s%*s",
+			fprintf(output, "CPU%*d%s%*s%s%*s%s%*s",
 				csv_output ? 0 : -4,
 				perf_evsel__cpus(counter)->map[cpu], csv_sep,
 				csv_output ? 0 : 18,
 				counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
 				csv_sep,
+				csv_output ? 0 : -10,
+				counter->unit,
+				csv_sep,
 				csv_output ? 0 : -24,
 				perf_evsel__name(counter));
 
@@ -1206,10 +1238,12 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
 			continue;
 		}
 
+		uval = val * counter->scale;
+
 		if (nsec_counter(counter))
-			nsec_printout(cpu, 0, counter, val);
+			nsec_printout(cpu, 0, counter, uval);
 		else
-			abs_printout(cpu, 0, counter, val);
+			abs_printout(cpu, 0, counter, uval);
 
 		if (!csv_output) {
 			print_noise(counter, 1.0);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 3a334f0..867971b 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -162,6 +162,8 @@ void perf_evsel__init(struct perf_evsel *evsel,
 	evsel->idx	   = idx;
 	evsel->attr	   = *attr;
 	evsel->leader	   = evsel;
+	evsel->unit	   = "?";
+	evsel->scale	   = 1.0;
 	INIT_LIST_HEAD(&evsel->node);
 	hists__init(&evsel->hists);
 	evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 5aa68cd..d5c6606 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -68,6 +68,8 @@ struct perf_evsel {
 	u32			ids;
 	struct hists		hists;
 	char			*name;
+	double			scale;
+	const char		*unit;
 	struct event_format	*tp_format;
 	union {
 		void		*priv;
@@ -130,6 +132,7 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
 int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
 					    char *bf, size_t size);
 const char *perf_evsel__name(struct perf_evsel *evsel);
+
 const char *perf_evsel__group_name(struct perf_evsel *evsel);
 int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
 
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index c90e55c..be7eba8 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -838,6 +838,7 @@ int parse_events_name(struct list_head *list, char *name)
 	list_for_each_entry(evsel, list, node) {
 		if (!evsel->name)
 			evsel->name = strdup(name);
+		pmu_get_event_unit_scale(evsel);
 	}
 
 	return 0;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 64362fe..e18ef87 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -4,6 +4,7 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <dirent.h>
+#include <locale.h>
 #include "sysfs.h"
 #include "util.h"
 #include "pmu.h"
@@ -14,6 +15,8 @@ struct perf_pmu_alias {
 	char *name;
 	struct list_head terms;
 	struct list_head list;
+	char *unit;
+	double scale;
 };
 
 struct perf_pmu_format {
@@ -95,7 +98,89 @@ static int pmu_format(const char *name, struct list_head *format)
 	return 0;
 }
 
-static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
+static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name)
+{
+	struct stat st;
+	ssize_t sret;
+	char scale[128];
+	int fd, ret = -1;
+	char path[PATH_MAX];
+	char *lc;
+
+	snprintf(path, PATH_MAX, "%s/%s.scale", dir, name);
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return -1;
+
+	if (fstat(fd, &st) < 0)
+		goto error;
+
+	sret = read(fd, scale, sizeof(scale)-1);
+	if (sret < 0)
+		goto error;
+
+	scale[sret] = '\0';
+	/*
+	 * save current locale
+	 */
+	lc = setlocale(LC_NUMERIC, NULL);
+
+	/*
+	 * force to C locale to ensure kernel
+	 * scale string is converted correctly.
+	 * kernel uses default C locale.
+	 */
+	setlocale(LC_NUMERIC, "C");
+
+	alias->scale = strtod(scale, NULL);
+
+	/* restore locale */
+	setlocale(LC_NUMERIC, lc);
+
+	ret = 0;
+error:
+	close(fd);
+	return ret;
+}
+
+static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name)
+{
+	struct stat st;
+	ssize_t sret;
+	int fd;
+	char path[PATH_MAX];
+
+	snprintf(path, PATH_MAX, "%s/%s.unit", dir, name);
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return -1;
+
+	if (fstat(fd, &st) < 0)
+		goto error;
+
+	alias->unit = malloc(st.st_size + 1);
+	if (!alias->unit)
+		goto error;
+
+	sret = read(fd, alias->unit, st.st_size);
+	if (sret < 0)
+		goto error;
+
+	close(fd);
+
+	alias->unit[sret] = '\0';
+
+	return 0;
+error:
+	close(fd);
+	free(alias->unit);
+	alias->unit = NULL;
+	return -1;
+}
+
+static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
 {
 	struct perf_pmu_alias *alias;
 	char buf[256];
@@ -111,6 +196,9 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
 		return -ENOMEM;
 
 	INIT_LIST_HEAD(&alias->terms);
+	alias->scale = 1.0;
+	alias->unit = NULL;
+
 	ret = parse_events_terms(&alias->terms, buf);
 	if (ret) {
 		free(alias);
@@ -118,7 +206,14 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
 	}
 
 	alias->name = strdup(name);
+	/*
+	 * load unit name and scale if available
+	 */
+	perf_pmu__parse_unit(alias, dir, name);
+	perf_pmu__parse_scale(alias, dir, name);
+
 	list_add_tail(&alias->list, list);
+
 	return 0;
 }
 
@@ -130,6 +225,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
 {
 	struct dirent *evt_ent;
 	DIR *event_dir;
+	size_t len;
 	int ret = 0;
 
 	event_dir = opendir(dir);
@@ -144,13 +240,24 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
 		if (!strcmp(name, ".") || !strcmp(name, ".."))
 			continue;
 
+		/*
+		 * skip .unit and .scale info files
+		 * parsed in perf_pmu__new_alias()
+		 */
+		len = strlen(name);
+		if (len > 5 && !strcmp(name + len - 5, ".unit"))
+			continue;
+		if (len > 6 && !strcmp(name + len - 6, ".scale"))
+			continue;
+
 		snprintf(path, PATH_MAX, "%s/%s", dir, name);
 
 		ret = -EINVAL;
 		file = fopen(path, "r");
 		if (!file)
 			break;
-		ret = perf_pmu__new_alias(head, name, file);
+
+		ret = perf_pmu__new_alias(head, dir, name, file);
 		fclose(file);
 	}
 
@@ -653,3 +760,62 @@ bool pmu_have_event(const char *pname, const char *name)
 	}
 	return false;
 }
+
+static const char *pmu_event_unit(struct perf_pmu *pmu, const char *name)
+{
+	struct perf_pmu_alias *alias;
+	char buf[1024];
+	char *fname;
+	const char *unit = "?";
+
+	if (!name)
+		return unit;
+
+	list_for_each_entry(alias, &pmu->aliases, list) {
+		fname = format_alias(buf, sizeof(buf), pmu, alias);
+		if (!strcmp(fname, name)) {
+			unit = alias->unit;
+			break;
+		}
+	}
+	return unit;
+}
+
+static double pmu_event_scale(struct perf_pmu *pmu, const char *name)
+{
+	struct perf_pmu_alias *alias;
+	char buf[1024];
+	char *fname;
+	double scale = 1.0;
+
+	if (!name)
+		return 1.0;
+
+	list_for_each_entry(alias, &pmu->aliases, list) {
+		fname = format_alias(buf, sizeof(buf), pmu, alias);
+		if (!strcmp(fname, name)) {
+			scale = alias->scale;
+			break;
+		}
+	}
+	return scale;
+}
+
+int pmu_get_event_unit_scale(struct perf_evsel *evsel)
+{
+	__u32 type = evsel->attr.type;
+	struct perf_pmu *pmu;
+
+	if (!evsel->name)
+		return -1;
+
+	list_for_each_entry(pmu, &pmus, list) {
+		if (pmu->type == type)
+			goto found;
+	}
+	return -1;
+found:
+	evsel->unit  = pmu_event_unit(pmu, evsel->name);
+	evsel->scale = pmu_event_scale(pmu, evsel->name);
+	return 0;
+}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 1179b26..6bf23b2 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -4,6 +4,7 @@
 #include <linux/bitops.h>
 #include <linux/perf_event.h>
 #include <stdbool.h>
+#include "util/evsel.h"
 
 enum {
 	PERF_PMU_FORMAT_VALUE_CONFIG,
@@ -45,4 +46,6 @@ void print_pmu_events(const char *event_glob, bool name_only);
 bool pmu_have_event(const char *pname, const char *name);
 
 int perf_pmu__test(void);
+
+int pmu_get_event_unit_scale(struct perf_evsel *evsel);
 #endif /* __PMU_H */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v4 3/4] perf/x86: add Intel RAPL PMU support
  2013-10-31 14:59 [PATCH v4 0/4] perf/x86: add Intel RAPL PMU support Stephane Eranian
  2013-10-31 14:59 ` [PATCH v4 1/4] perf: add active_entry list head to struct perf_event Stephane Eranian
  2013-10-31 14:59 ` [PATCH v4 2/4] perf stat: add event unit and scale support Stephane Eranian
@ 2013-10-31 14:59 ` Stephane Eranian
  2013-10-31 15:14   ` Peter Zijlstra
  2013-10-31 14:59 ` [PATCH v4 4/4] perf,x86: add RAPL hrtimer support Stephane Eranian
  3 siblings, 1 reply; 15+ messages in thread
From: Stephane Eranian @ 2013-10-31 14:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: peterz, mingo, ak, acme, jolsa, zheng.z.yan, bp, maria.n.dimakopoulou

This patch adds a new uncore PMU to expose the Intel
RAPL energy consumption counters. Up to 3 counters,
each counting a particular RAPL event are exposed.

The RAPL counters are available on Intel SandyBridge,
IvyBridge, Haswell. The server skus add a 3rd counter.

The following events are available and exposed in sysfs:
- power/energy-cores: power consumption of all cores on socket
- power/energy-pkg: power consumption of all cores + LLc cache
- power/energy-dram: power consumption of DRAM (servers only)

For each event both the unit (Joules) and scale (0.23 nJ)
is exposed in sysfs for use by perf stat and other tools.
Files are:
	/sys/devices/power/events/energy-*.unit
	/sys/devices/power/events/energy-*.scale

The RAPL PMU is uncore by nature and is implemented such
that it only works in system-wide mode. Measuring only
one CPU per socket is sufficient. The /sys/devices/power/cpumask
file can be used by tools to figure out which CPUs to monitor
by default. For instance, on a 2-socket system, 2 CPUs
(one on each socket) will be shown.

All the counters measure in the same unit (exposed via sysfs).
The perf_events API exposes all RAPL counters as 64-bit integers
counting in unit of 1/2^32 Joules (or 0.23 nJ). User level tools
must convert the counts by multiplying them by 0.23 and divide 10^9
to obtain Joules. The reason for this is that the kernel avoids
doing floating point math whenever possible because it is
expensive (user floating-point state must be saved). The method
used avoids kernel floating-point usage. There is no loss of
precision. Thanks to PeterZ for suggesting this approach.

To convert the raw count in Watt:
   W = C * 2.3 / (1e10 * time)
or ldexp(C, -32).

RAPL PMU is a new standalone PMU which registers with the
perf_event core subsystem. The PMU type (attr->type) is
dynamically allocated and is available from /sys/device/power/type.

Sampling is not supported by the RAPL PMU. There is no
privilege level filtering either.

Thanks to Maria Dimakopoulou for her contributions to this patch
especially on the math aspects.

Signed-off-by: Stephane Eranian <eranian@google.com>
Signed-off-by: Maria Dimakopoulou <maria.n.dimakopoulou@gmail.com>
---
 arch/x86/kernel/cpu/Makefile                |    2 +-
 arch/x86/kernel/cpu/perf_event_intel_rapl.c |  655 +++++++++++++++++++++++++++
 2 files changed, 656 insertions(+), 1 deletion(-)
 create mode 100644 arch/x86/kernel/cpu/perf_event_intel_rapl.c

diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 47b56a7..6359506 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -36,7 +36,7 @@ obj-$(CONFIG_CPU_SUP_AMD)		+= perf_event_amd_iommu.o
 endif
 obj-$(CONFIG_CPU_SUP_INTEL)		+= perf_event_p6.o perf_event_knc.o perf_event_p4.o
 obj-$(CONFIG_CPU_SUP_INTEL)		+= perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o
-obj-$(CONFIG_CPU_SUP_INTEL)		+= perf_event_intel_uncore.o
+obj-$(CONFIG_CPU_SUP_INTEL)		+= perf_event_intel_uncore.o perf_event_intel_rapl.o
 endif
 
 
diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
new file mode 100644
index 0000000..080d494
--- /dev/null
+++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
@@ -0,0 +1,655 @@
+/*
+ * perf_event_intel_rapl.c: support Intel RAPL energy consumption counters
+ * Copyright (C) 2013 Google, Inc., Stephane Eranian
+ *
+ * Intel RAPL interface is specified in the IA-32 Manual Vol3b
+ * section 14.7.1 (September 2013)
+ *
+ * RAPL provides more controls than just reporting energy consumption
+ * however here we only expose the 3 energy consumption free running
+ * counters (pp0, pkg, dram).
+ *
+ * Each of those counters increments in a power unit defined by the
+ * RAPL_POWER_UNIT MSR. On SandyBridge, this unit is 1/(2^16) Joules
+ * but it can vary.
+ *
+ * Counter to rapl events mappings:
+ *
+ *  pp0 counter: consumption of all physical cores (power plane 0)
+ * 	  event: rapl_energy_cores
+ *    perf code: 0x1
+ *
+ *  pkg counter: consumption of the whole processor package
+ *	  event: rapl_energy_pkg
+ *    perf code: 0x2
+ *
+ * dram counter: consumption of the dram domain (servers only)
+ *	  event: rapl_energy_dram
+ *    perf code: 0x3
+ *
+ * We manage those counters as free running (read-only). They may be
+ * use simultaneously by other tools, such as turbostat.
+ *
+ * The events only support system-wide mode counting. There is no
+ * sampling support because it does not make sense and is not
+ * supported by the RAPL hardware.
+ *
+ * Because we want to avoid floating-point operations in the kernel,
+ * the events are all reported in fixed point arithmetic (32.32).
+ * Tools must adjust the counts to convert them to Watts using
+ * the duration of the measurement. Tools may use a function such as
+ * ldexp(raw_count, -32);
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/perf_event.h>
+#include <asm/cpu_device_id.h>
+#include "perf_event.h"
+
+/*
+ * RAPL energy status counters
+ */
+#define RAPL_IDX_PP0_NRG_STAT	0	/* all cores */
+#define INTEL_RAPL_PP0		0x1	/* pseudo-encoding */
+#define RAPL_IDX_PKG_NRG_STAT	1	/* entire package */
+#define INTEL_RAPL_PKG		0x2	/* pseudo-encoding */
+#define RAPL_IDX_RAM_NRG_STAT	2	/* DRAM */
+#define INTEL_RAPL_RAM		0x3	/* pseudo-encoding */
+
+/* Clients have PP0, PKG */
+#define RAPL_IDX_CLN	(1<<RAPL_IDX_PP0_NRG_STAT|\
+			 1<<RAPL_IDX_PKG_NRG_STAT)
+
+/* Servers have PP0, PKG, RAM */
+#define RAPL_IDX_SRV	(1<<RAPL_IDX_PP0_NRG_STAT|\
+			 1<<RAPL_IDX_PKG_NRG_STAT|\
+			 1<<RAPL_IDX_RAM_NRG_STAT)
+
+/*
+ * event code: LSB 8 bits, passed in attr->config
+ * any other bit is reserved
+ */
+#define RAPL_EVENT_MASK	0xFFULL
+
+#define DEFINE_RAPL_FORMAT_ATTR(_var, _name, _format)		\
+static ssize_t __rapl_##_var##_show(struct kobject *kobj,	\
+				struct kobj_attribute *attr,	\
+				char *page)			\
+{								\
+	BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE);		\
+	return sprintf(page, _format "\n");			\
+}								\
+static struct kobj_attribute format_attr_##_var =		\
+	__ATTR(_name, 0444, __rapl_##_var##_show, NULL)
+
+#define RAPL_EVENT_DESC(_name, _config)				\
+{								\
+	.attr	= __ATTR(_name, 0444, rapl_event_show, NULL),	\
+	.config	= _config,					\
+}
+
+#define RAPL_CNTR_WIDTH 32 /* 32-bit rapl counters */
+
+struct rapl_pmu {
+	spinlock_t	lock;
+	atomic_t	refcnt;
+	int		hw_unit;  /* 1/2^hw_unit Joule */
+	int		phys_id;
+	int		n_active; /* number of active events */
+	struct list_head active_list;
+};
+
+static struct pmu rapl_pmu_class;
+static cpumask_t rapl_cpu_mask;
+static int rapl_cntr_mask;
+
+static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu);
+static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu_kfree);
+
+static DEFINE_SPINLOCK(rapl_hotplug_lock);
+
+static inline u64 rapl_read_counter(struct perf_event *event)
+{
+	u64 raw;
+	rdmsrl(event->hw.event_base, raw);
+	return raw;
+}
+
+static inline u64 rapl_scale(u64 v)
+{
+	/*
+	 * scale delta to smallest unit (1/2^32)
+	 * users must then scale back: count * 1/(1e9*2^32) to get Joules
+	 * or use ldexp(count, -32).
+	 * Watts = Joules/Time delta
+	 */
+	return v << (32 - __get_cpu_var(rapl_pmu)->hw_unit);
+}
+
+static u64 rapl_event_update(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	u64 prev_raw_count, new_raw_count;
+	s64 delta, sdelta;
+	int shift = RAPL_CNTR_WIDTH;
+
+again:
+	prev_raw_count = local64_read(&hwc->prev_count);
+	rdmsrl(event->hw.event_base, new_raw_count);
+
+	if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+			    new_raw_count) != prev_raw_count) {
+		cpu_relax();
+		goto again;
+	}
+
+	/*
+	 * Now we have the new raw value and have updated the prev
+	 * timestamp already. We can now calculate the elapsed delta
+	 * (event-)time and add that to the generic event.
+	 *
+	 * Careful, not all hw sign-extends above the physical width
+	 * of the count.
+	 */
+	delta = (new_raw_count << shift) - (prev_raw_count << shift);
+	delta >>= shift;
+
+	sdelta = rapl_scale(delta);
+
+	local64_add(sdelta, &event->count);
+
+	return new_raw_count;
+}
+
+static void __rapl_pmu_event_start(struct rapl_pmu *pmu,
+				   struct perf_event *event)
+{
+	if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
+		return;
+
+	event->hw.state = 0;
+
+	list_add_tail(&event->active_entry, &pmu->active_list);
+
+	local64_set(&event->hw.prev_count, rapl_read_counter(event));
+
+	pmu->n_active++;
+}
+
+static void rapl_pmu_event_start(struct perf_event *event, int mode)
+{
+	struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu);
+	unsigned long flags;
+
+	spin_lock_irqsave(&pmu->lock, flags);
+	__rapl_pmu_event_start(pmu, event);
+	spin_unlock_irqrestore(&pmu->lock, flags);
+}
+
+static void rapl_pmu_event_stop(struct perf_event *event, int mode)
+{
+	struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pmu->lock, flags);
+
+	/* mark event as deactivated and stopped */
+	if (!(hwc->state & PERF_HES_STOPPED)) {
+		WARN_ON_ONCE(pmu->n_active <= 0);
+		pmu->n_active--;
+
+		list_del(&event->active_entry);
+
+		WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
+		hwc->state |= PERF_HES_STOPPED;
+	}
+
+	/* check if update of sw counter is necessary */
+	if ((mode & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+		/*
+		 * Drain the remaining delta count out of a event
+		 * that we are disabling:
+		 */
+		rapl_event_update(event);
+		hwc->state |= PERF_HES_UPTODATE;
+	}
+
+	spin_unlock_irqrestore(&pmu->lock, flags);
+}
+
+static int rapl_pmu_event_add(struct perf_event *event, int mode)
+{
+	struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pmu->lock, flags);
+
+	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+	if (mode & PERF_EF_START)
+		__rapl_pmu_event_start(pmu, event);
+
+	spin_unlock_irqrestore(&pmu->lock, flags);
+
+	return 0;
+}
+
+static void rapl_pmu_event_del(struct perf_event *event, int flags)
+{
+	rapl_pmu_event_stop(event, PERF_EF_UPDATE);
+}
+
+static int rapl_pmu_event_init(struct perf_event *event)
+{
+	u64 cfg = event->attr.config & RAPL_EVENT_MASK;
+	int bit, msr, ret = 0;
+
+	/* only look at RAPL events */
+	if (event->attr.type != rapl_pmu_class.type)
+		return -ENOENT;
+
+	/* check only supported bits are set */
+	if (event->attr.config & ~RAPL_EVENT_MASK)
+		return -EINVAL;
+
+	/*
+	 * check event is known (determines counter)
+	 */
+	switch (cfg) {
+	case INTEL_RAPL_PP0:
+		bit = RAPL_IDX_PP0_NRG_STAT;
+		msr = MSR_PP0_ENERGY_STATUS;
+		break;
+	case INTEL_RAPL_PKG:
+		bit = RAPL_IDX_PKG_NRG_STAT;
+		msr = MSR_PKG_ENERGY_STATUS;
+		break;
+	case INTEL_RAPL_RAM:
+		bit = RAPL_IDX_RAM_NRG_STAT;
+		msr = MSR_DRAM_ENERGY_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* check event supported */
+	if (!(rapl_cntr_mask & (1 << bit)))
+		return -EINVAL;
+
+	/* unsupported modes and filters */
+	if (event->attr.exclude_user   ||
+	    event->attr.exclude_kernel ||
+	    event->attr.exclude_hv     ||
+	    event->attr.exclude_idle   ||
+	    event->attr.exclude_host   ||
+	    event->attr.exclude_guest  ||
+	    event->attr.sample_period) /* no sampling */
+		return -EINVAL;
+
+	/* must be done before validate_group */
+	event->hw.event_base = msr;
+	event->hw.config = cfg;
+	event->hw.idx = bit;
+
+	return ret;
+}
+
+static void rapl_pmu_event_read(struct perf_event *event)
+{
+	rapl_event_update(event);
+}
+
+static ssize_t rapl_get_attr_cpumask(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &rapl_cpu_mask);
+
+	buf[n++] = '\n';
+	buf[n] = '\0';
+	return n;
+}
+
+static DEVICE_ATTR(cpumask, S_IRUGO, rapl_get_attr_cpumask, NULL);
+
+static struct attribute *rapl_pmu_attrs[] = {
+	&dev_attr_cpumask.attr,
+	NULL,
+};
+
+static struct attribute_group rapl_pmu_attr_group = {
+	.attrs = rapl_pmu_attrs,
+};
+
+EVENT_ATTR_STR(energy-cores, rapl_cores, "event=0x01");
+EVENT_ATTR_STR(energy-pkg  , rapl_pkg, "event=0x02");
+EVENT_ATTR_STR(energy-ram  , rapl_ram, "event=0x03");
+
+EVENT_ATTR_STR(energy-cores.unit, rapl_cores_unit, "Joules");
+EVENT_ATTR_STR(energy-pkg.unit  , rapl_pkg_unit, "Joules");
+EVENT_ATTR_STR(energy-ram.unit  , rapl_ram_unit, "Joules");
+
+/*
+ * we compute in 0.23 nJ increments regardless of MSR
+ */
+EVENT_ATTR_STR(energy-cores.scale, rapl_cores_scale, "2.3e-10");
+EVENT_ATTR_STR(energy-pkg.scale, rapl_pkg_scale, "2.3e-10");
+EVENT_ATTR_STR(energy-ram.scale, rapl_ram_scale, "2.3e-10");
+
+static struct attribute *rapl_events_srv_attr[] = {
+	EVENT_PTR(rapl_cores),
+	EVENT_PTR(rapl_pkg),
+	EVENT_PTR(rapl_ram),
+
+	EVENT_PTR(rapl_cores_unit),
+	EVENT_PTR(rapl_pkg_unit),
+	EVENT_PTR(rapl_ram_unit),
+
+	EVENT_PTR(rapl_cores_scale),
+	EVENT_PTR(rapl_pkg_scale),
+	EVENT_PTR(rapl_ram_scale),
+	NULL,
+};
+
+static struct attribute *rapl_events_cln_attr[] = {
+	EVENT_PTR(rapl_cores),
+	EVENT_PTR(rapl_pkg),
+
+	EVENT_PTR(rapl_cores_unit),
+	EVENT_PTR(rapl_pkg_unit),
+
+	EVENT_PTR(rapl_cores_scale),
+	EVENT_PTR(rapl_pkg_scale),
+	NULL,
+};
+
+static struct attribute_group rapl_pmu_events_group = {
+	.name = "events",
+	.attrs = NULL, /* patched at runtime */
+};
+
+DEFINE_RAPL_FORMAT_ATTR(event, event, "config:0-7");
+static struct attribute *rapl_formats_attr[] = {
+	&format_attr_event.attr,
+	NULL,
+};
+
+static struct attribute_group rapl_pmu_format_group = {
+	.name = "format",
+	.attrs = rapl_formats_attr,
+};
+
+const struct attribute_group *rapl_attr_groups[] = {
+	&rapl_pmu_attr_group,
+	&rapl_pmu_format_group,
+	&rapl_pmu_events_group,
+	NULL,
+};
+
+static struct pmu rapl_pmu_class = {
+	.attr_groups	= rapl_attr_groups,
+	.task_ctx_nr	= perf_invalid_context, /* system-wide only */
+	.event_init	= rapl_pmu_event_init,
+	.add		= rapl_pmu_event_add, /* must have */
+	.del		= rapl_pmu_event_del, /* must have */
+	.start		= rapl_pmu_event_start,
+	.stop		= rapl_pmu_event_stop,
+	.read		= rapl_pmu_event_read,
+};
+
+static void rapl_exit_cpu(int cpu)
+{
+	int i, phys_id = topology_physical_package_id(cpu);
+
+	spin_lock(&rapl_hotplug_lock);
+
+	/* if CPU not in RAPL mask, nothing to do */
+	if (!cpumask_test_and_clear_cpu(cpu, &rapl_cpu_mask))
+		goto skip;
+
+	/* find a new cpu on same package */
+	for_each_online_cpu(i) {
+		if (i == cpu || i == 0)
+			continue;
+		if (phys_id == topology_physical_package_id(i)) {
+			cpumask_set_cpu(i, &rapl_cpu_mask);
+			break;
+		}
+	}
+
+	WARN_ON(cpumask_empty(&rapl_cpu_mask));
+skip:
+	spin_unlock(&rapl_hotplug_lock);
+}
+
+static void rapl_init_cpu(int cpu)
+{
+	int i, phys_id = topology_physical_package_id(cpu);
+
+	spin_lock(&rapl_hotplug_lock);
+
+	/* check if phys_is is already covered */
+	for_each_cpu(i, &rapl_cpu_mask) {
+		if (phys_id == topology_physical_package_id(i))
+			goto skip;
+	}
+	/* was not found, so add it */
+	cpumask_set_cpu(cpu, &rapl_cpu_mask);
+skip:
+	spin_unlock(&rapl_hotplug_lock);
+}
+
+static int rapl_cpu_prepare(int cpu)
+{
+	struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
+	int phys_id = topology_physical_package_id(cpu);
+
+	if (pmu)
+		return 0;
+
+	if (phys_id < 0)
+		return -1;
+
+	pmu = kzalloc_node(sizeof(*pmu), GFP_KERNEL, cpu_to_node(cpu));
+	if (!pmu)
+		return -1;
+
+	spin_lock_init(&pmu->lock);
+	atomic_set(&pmu->refcnt, 1);
+
+	INIT_LIST_HEAD(&pmu->active_list);
+
+	pmu->phys_id = phys_id;
+	/*
+	 * grab power unit as: 1/2^unit Joules
+	 *
+	 * we cache in local PMU instance
+	 */
+	rdmsrl(MSR_RAPL_POWER_UNIT, pmu->hw_unit);
+	pmu->hw_unit = (pmu->hw_unit >> 8) & 0x1FULL;
+
+	/* set RAPL pmu for this cpu for now */
+	per_cpu(rapl_pmu_kfree, cpu) = NULL;
+	per_cpu(rapl_pmu, cpu) = pmu;
+
+	return 0;
+}
+
+static int rapl_cpu_starting(int cpu)
+{
+	struct rapl_pmu *pmu2;
+	struct rapl_pmu *pmu1 = per_cpu(rapl_pmu, cpu);
+	int i, phys_id = topology_physical_package_id(cpu);
+
+	if (pmu1)
+		return 0;
+
+	spin_lock(&rapl_hotplug_lock);
+
+	for_each_online_cpu(i) {
+		pmu2 = per_cpu(rapl_pmu, i);
+
+		if (!pmu2 || i == cpu)
+			continue;
+
+		if (pmu2->phys_id == phys_id) {
+			per_cpu(rapl_pmu, cpu) = pmu2;
+			per_cpu(rapl_pmu_kfree, cpu) = pmu1;
+			atomic_inc(&pmu2->refcnt);
+			break;
+		}
+	}
+	spin_unlock(&rapl_hotplug_lock);
+	return 0;
+}
+
+static int rapl_cpu_dying(int cpu)
+{
+	struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
+	struct perf_event *event, *tmp;
+
+	if (!pmu)
+		return 0;
+
+	spin_lock(&rapl_hotplug_lock);
+
+	/*
+	 * stop all syswide RAPL events on that  CPU
+	 * as a consequence also stops the hrtimer
+	 */
+	list_for_each_entry_safe(event, tmp, &pmu->active_list, active_entry) {
+		rapl_pmu_event_stop(event, PERF_EF_UPDATE);
+	}
+
+	per_cpu(rapl_pmu, cpu) = NULL;
+
+	if (atomic_dec_and_test(&pmu->refcnt))
+		kfree(pmu);
+
+	spin_unlock(&rapl_hotplug_lock);
+	return 0;
+}
+
+static int rapl_cpu_notifier(struct notifier_block *self,
+			     unsigned long action, void *hcpu)
+{
+	unsigned int cpu = (long)hcpu;
+
+	/* allocate/free data structure for uncore box */
+	switch (action & ~CPU_TASKS_FROZEN) {
+	case CPU_UP_PREPARE:
+		rapl_cpu_prepare(cpu);
+		break;
+	case CPU_STARTING:
+		rapl_cpu_starting(cpu);
+		break;
+	case CPU_UP_CANCELED:
+	case CPU_DYING:
+		rapl_cpu_dying(cpu);
+		break;
+	case CPU_ONLINE:
+		kfree(per_cpu(rapl_pmu_kfree, cpu));
+		per_cpu(rapl_pmu_kfree, cpu) = NULL;
+		break;
+	case CPU_DEAD:
+		per_cpu(rapl_pmu, cpu) = NULL;
+		break;
+	default:
+		break;
+	}
+
+	/* select the cpu that collects uncore events */
+	switch (action & ~CPU_TASKS_FROZEN) {
+	case CPU_DOWN_FAILED:
+	case CPU_STARTING:
+		rapl_init_cpu(cpu);
+		break;
+	case CPU_DOWN_PREPARE:
+		rapl_exit_cpu(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static const struct x86_cpu_id rapl_cpu_match[] = {
+	[0] = { .vendor = X86_VENDOR_INTEL, .family = 6 },
+	[1] = {},
+};
+static int __init rapl_pmu_init(void)
+{
+	struct rapl_pmu *pmu;
+	int i, cpu, ret;
+
+	/*
+	 * check for Intel processor family 6
+	 */
+	if (!x86_match_cpu(rapl_cpu_match))
+		return 0;
+
+	/* check supported CPU */
+	switch (boot_cpu_data.x86_model) {
+	case 42: /* Sandy Bridge */
+	case 58: /* Ivy Bridge */
+	case 60: /* Haswell */
+		rapl_cntr_mask = RAPL_IDX_CLN;
+		rapl_pmu_events_group.attrs = rapl_events_cln_attr;
+		break;
+	case 45: /* Sandy Bridge-EP */
+	case 62: /* IvyTown */
+		rapl_cntr_mask = RAPL_IDX_SRV;
+		rapl_pmu_events_group.attrs = rapl_events_srv_attr;
+		break;
+
+	default:
+		/* unsupported */
+		return 0;
+	}
+	get_online_cpus();
+
+	for_each_online_cpu(cpu) {
+		int phys_id = topology_physical_package_id(cpu);
+
+		/* save on prepare by only calling prepare for new phys_id */
+		for_each_cpu(i, &rapl_cpu_mask) {
+			if (phys_id == topology_physical_package_id(i)) {
+				phys_id = -1;
+				break;
+			}
+		}
+		if (phys_id < 0) {
+			pmu = per_cpu(rapl_pmu, i);
+			if (pmu) {
+				per_cpu(rapl_pmu, cpu) = pmu;
+				atomic_inc(&pmu->refcnt);
+			}
+			continue;
+		}
+		rapl_cpu_prepare(cpu);
+		cpumask_set_cpu(cpu, &rapl_cpu_mask);
+	}
+
+	perf_cpu_notifier(rapl_cpu_notifier);
+
+	ret = perf_pmu_register(&rapl_pmu_class, "power", -1);
+	if (WARN_ON(ret)) {
+		pr_info("RAPL PMU detected, registration failed (%d), RAPL PMU disabled\n", ret);
+		put_online_cpus();
+		return -1;
+	}
+
+	pmu = __get_cpu_var(rapl_pmu);
+
+	pr_info("RAPL PMU detected, hw unit 2^-%d Joules,"
+		" API unit is 2^-32 Joules,"
+		" %d fixed counters\n",
+		pmu->hw_unit,
+		hweight32(rapl_cntr_mask));
+
+	put_online_cpus();
+
+	return 0;
+}
+device_initcall(rapl_pmu_init);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v4 4/4] perf,x86: add RAPL hrtimer support
  2013-10-31 14:59 [PATCH v4 0/4] perf/x86: add Intel RAPL PMU support Stephane Eranian
                   ` (2 preceding siblings ...)
  2013-10-31 14:59 ` [PATCH v4 3/4] perf/x86: add Intel RAPL PMU support Stephane Eranian
@ 2013-10-31 14:59 ` Stephane Eranian
  3 siblings, 0 replies; 15+ messages in thread
From: Stephane Eranian @ 2013-10-31 14:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: peterz, mingo, ak, acme, jolsa, zheng.z.yan, bp, maria.n.dimakopoulou

The RAPL PMU counters do not interrupt on overflow.
Therefore, the kernel needs to poll the counters
to avoid missing an overflow. This patch adds
the hrtimer code to do this.

The timer internval is calculated at boot time
based on the power unit used by the HW.

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 arch/x86/kernel/cpu/perf_event_intel_rapl.c |   75 +++++++++++++++++++++++++--
 1 file changed, 70 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
index e2b719b..23f2aff 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_rapl.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
@@ -92,11 +92,13 @@ static struct kobj_attribute format_attr_##_var =		\
 
 struct rapl_pmu {
 	spinlock_t	lock;
-	atomic_t	refcnt;
 	int		hw_unit;  /* 1/2^hw_unit Joule */
-	int		phys_id;
-	int		n_active; /* number of active events */
+	struct hrtimer	hrtimer;
 	struct list_head active_list;
+	ktime_t		timer_interval; /* in ktime_t unit */
+	int		n_active; /* number of active events */
+	int		phys_id;
+	atomic_t	refcnt;
 };
 
 static struct pmu rapl_pmu_class;
@@ -161,6 +163,47 @@ static u64 rapl_event_update(struct perf_event *event)
 	return new_raw_count;
 }
 
+static void rapl_start_hrtimer(struct rapl_pmu *pmu)
+{
+	__hrtimer_start_range_ns(&pmu->hrtimer,
+			pmu->timer_interval, 0,
+			HRTIMER_MODE_REL_PINNED, 0);
+}
+
+static void rapl_stop_hrtimer(struct rapl_pmu *pmu)
+{
+	hrtimer_cancel(&pmu->hrtimer);
+}
+
+static enum hrtimer_restart rapl_hrtimer_handle(struct hrtimer *hrtimer)
+{
+	struct rapl_pmu *pmu = container_of(hrtimer, struct rapl_pmu, hrtimer);
+	struct perf_event *event;
+	unsigned long flags;
+
+	if (!pmu->n_active)
+		return HRTIMER_NORESTART;
+
+	spin_lock_irqsave(&pmu->lock, flags);
+
+	list_for_each_entry(event, &pmu->active_list, active_entry) {
+		rapl_event_update(event);
+	}
+
+	spin_unlock_irqrestore(&pmu->lock, flags);
+
+	hrtimer_forward_now(&pmu->hrtimer, pmu->timer_interval);
+
+	return HRTIMER_RESTART;
+}
+
+static void rapl_hrtimer_init(struct rapl_pmu *pmu)
+{
+	hrtimer_init(&pmu->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	pmu->hrtimer.function = rapl_hrtimer_handle;
+}
+
+
 static void __rapl_pmu_event_start(struct rapl_pmu *pmu,
 				   struct perf_event *event)
 {
@@ -174,6 +217,8 @@ static void __rapl_pmu_event_start(struct rapl_pmu *pmu,
 	local64_set(&event->hw.prev_count, rapl_read_counter(event));
 
 	pmu->n_active++;
+	if (pmu->n_active == 1)
+		rapl_start_hrtimer(pmu);
 }
 
 static void rapl_pmu_event_start(struct perf_event *event, int mode)
@@ -198,6 +243,8 @@ static void rapl_pmu_event_stop(struct perf_event *event, int mode)
 	if (!(hwc->state & PERF_HES_STOPPED)) {
 		WARN_ON_ONCE(pmu->n_active <= 0);
 		pmu->n_active--;
+		if (pmu->n_active == 0)
+			rapl_stop_hrtimer(pmu);
 
 		list_del(&event->active_entry);
 
@@ -439,6 +486,7 @@ static int rapl_cpu_prepare(int cpu)
 {
 	struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
 	int phys_id = topology_physical_package_id(cpu);
+	u64 ms;
 
 	if (pmu)
 		return 0;
@@ -464,6 +512,20 @@ static int rapl_cpu_prepare(int cpu)
 	rdmsrl(MSR_RAPL_POWER_UNIT, pmu->hw_unit);
 	pmu->hw_unit = (pmu->hw_unit >> 8) & 0x1FULL;
 
+	/*
+	 * use reference of 200W for scaling the timeout
+	 * to avoid missing counter overflows.
+	 * 200W = 200 Joules/sec
+	 * divide interval by 2 to avoid lockstep (2 * 100)
+	 * if hw unit is 32, then we use 2 ms 1/200/2
+	 */
+	if (pmu->hw_unit < 32)
+		ms = 1000 * (1ULL << (32 - pmu->hw_unit - 1)) / (2 * 100);
+	else
+		ms = 2;
+
+	pmu->timer_interval = ms_to_ktime(ms);
+
 	/* set RAPL pmu for this cpu for now */
 	per_cpu(rapl_pmu_kfree, cpu) = NULL;
 	per_cpu(rapl_pmu, cpu) = pmu;
@@ -625,6 +687,7 @@ static int __init rapl_pmu_init(void)
 		}
 		rapl_cpu_prepare(cpu);
 		cpumask_set_cpu(cpu, &rapl_cpu_mask);
+		rapl_hrtimer_init(per_cpu(rapl_pmu, cpu));
 	}
 
 	perf_cpu_notifier(rapl_cpu_notifier);
@@ -640,9 +703,11 @@ static int __init rapl_pmu_init(void)
 
 	pr_info("RAPL PMU detected, hw unit 2^-%d Joules,"
 		" API unit is 2^-32 Joules,"
-		" %d fixed counters\n",
+		" %d fixed counters"
+		" %llu ms ovfl timer\n",
 		pmu->hw_unit,
-		hweight32(rapl_cntr_mask));
+		hweight32(rapl_cntr_mask),
+		ktime_to_ms(pmu->timer_interval));
 
 	put_online_cpus();
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 3/4] perf/x86: add Intel RAPL PMU support
  2013-10-31 14:59 ` [PATCH v4 3/4] perf/x86: add Intel RAPL PMU support Stephane Eranian
@ 2013-10-31 15:14   ` Peter Zijlstra
  0 siblings, 0 replies; 15+ messages in thread
From: Peter Zijlstra @ 2013-10-31 15:14 UTC (permalink / raw)
  To: Stephane Eranian
  Cc: linux-kernel, mingo, ak, acme, jolsa, zheng.z.yan, bp,
	maria.n.dimakopoulou

On Thu, Oct 31, 2013 at 03:59:41PM +0100, Stephane Eranian wrote:
> +/*
> + * we compute in 0.23 nJ increments regardless of MSR

We don't in fact use 0.23 nJ, we use 2^-32 J.

> + */
> +EVENT_ATTR_STR(energy-cores.scale, rapl_cores_scale, "2.3e-10");
> +EVENT_ATTR_STR(energy-pkg.scale, rapl_pkg_scale, "2.3e-10");
> +EVENT_ATTR_STR(energy-ram.scale, rapl_ram_scale, "2.3e-10");

Am I weird for proposing we use:

  "2.3283064365386962890625e-10"

instead? Its not like anybody needs to go type this by hand.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-10-31 14:59 ` [PATCH v4 2/4] perf stat: add event unit and scale support Stephane Eranian
@ 2013-11-01 10:57   ` Jiri Olsa
  2013-11-04 14:47     ` Stephane Eranian
  0 siblings, 1 reply; 15+ messages in thread
From: Jiri Olsa @ 2013-11-01 10:57 UTC (permalink / raw)
  To: Stephane Eranian
  Cc: linux-kernel, peterz, mingo, ak, acme, zheng.z.yan, bp,
	maria.n.dimakopoulou

On Thu, Oct 31, 2013 at 03:59:40PM +0100, Stephane Eranian wrote:
> This patch adds perf stat support fo rhandling event units and
> scales as exported by the kernel.
> 
> The kernel can export PMU events actual unit and scaling factor
> via sysfs:
> $ ls -1 /sys/devices/power/events/energy-*
> /sys/devices/power/events/energy-cores
> /sys/devices/power/events/energy-cores.scale
> /sys/devices/power/events/energy-cores.unit
> /sys/devices/power/events/energy-pkg
> /sys/devices/power/events/energy-pkg.scale
> /sys/devices/power/events/energy-pkg.unit
> $ cat /sys/devices/power/events/energy-cores.scale
> 2.3e-10
> $ cat cat /sys/devices/power/events/energy-cores.unit
> Joules
> 
> This patch modifies the pmu event alias code to check
> for the presence of the .unit and .scale files to load
> the corresponding values. They are then used by perf stat
> transparentely:
>

wrt our previous conversation, attached patch is what
I meant (based on your changes)

you could get rid of following functions:
  pmu_event_unit
  pmu_event_scale
  pmu_get_event_unit_scale

and have the unit/scale processing within the alias code

the name that matters is the term name which is available
in the parse_events_add_pmu as one of the terms

jirka
---
 tools/perf/util/parse-events.c | 29 ++++++++-----
 tools/perf/util/pmu.c          | 92 +++++++++++++++---------------------------
 tools/perf/util/pmu.h          |  6 +--
 3 files changed, 53 insertions(+), 74 deletions(-)

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index be7eba8..90ae328 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -269,9 +269,10 @@ const char *event_type(int type)
 
 
 
-static int __add_event(struct list_head *list, int *idx,
-		       struct perf_event_attr *attr,
-		       char *name, struct cpu_map *cpus)
+static struct perf_evsel *
+__add_event(struct list_head *list, int *idx,
+	    struct perf_event_attr *attr,
+	    char *name, struct cpu_map *cpus)
 {
 	struct perf_evsel *evsel;
 
@@ -279,19 +280,19 @@ static int __add_event(struct list_head *list, int *idx,
 
 	evsel = perf_evsel__new(attr, (*idx)++);
 	if (!evsel)
-		return -ENOMEM;
+		return NULL;
 
 	evsel->cpus = cpus;
 	if (name)
 		evsel->name = strdup(name);
 	list_add_tail(&evsel->node, list);
-	return 0;
+	return evsel;
 }
 
 static int add_event(struct list_head *list, int *idx,
 		     struct perf_event_attr *attr, char *name)
 {
-	return __add_event(list, idx, attr, name, NULL);
+	return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM;
 }
 
 static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
@@ -633,6 +634,9 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
 {
 	struct perf_event_attr attr;
 	struct perf_pmu *pmu;
+	struct perf_evsel *evsel;
+	char *unit;
+	double scale;
 
 	pmu = perf_pmu__find(name);
 	if (!pmu)
@@ -640,7 +644,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
 
 	memset(&attr, 0, sizeof(attr));
 
-	if (perf_pmu__check_alias(pmu, head_config))
+	if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))
 		return -EINVAL;
 
 	/*
@@ -652,8 +656,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
 	if (perf_pmu__config(pmu, &attr, head_config))
 		return -EINVAL;
 
-	return __add_event(list, idx, &attr, pmu_event_name(head_config),
-			   pmu->cpus);
+	evsel = __add_event(list, idx, &attr, pmu_event_name(head_config),
+			    pmu->cpus);
+	if (evsel) {
+		evsel->unit = unit;
+		evsel->scale = scale;
+	}
+
+	return evsel ? 0 : -ENOMEM;
 }
 
 int parse_events__modifier_group(struct list_head *list,
@@ -838,7 +848,6 @@ int parse_events_name(struct list_head *list, char *name)
 	list_for_each_entry(evsel, list, node) {
 		if (!evsel->name)
 			evsel->name = strdup(name);
-		pmu_get_event_unit_scale(evsel);
 	}
 
 	return 0;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index e18ef87..df68d43 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -620,16 +620,42 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
 	return NULL;
 }
 
+
+static int check_unit_scale(struct perf_pmu_alias *alias,
+			    char **unit, double *scale)
+{
+	/*
+	 * Only one term in event definition can
+	 * define unit and scale, fail if there's
+	 * more than one.
+	 */
+	if ((*unit && alias->unit) ||
+	    (*scale && alias->scale))
+		return -EINVAL;
+
+	if (alias->unit)
+		*unit = alias->unit;
+
+	if (alias->scale)
+		*scale = alias->scale;
+
+	return 0;
+}
+
 /*
  * Find alias in the terms list and replace it with the terms
  * defined for the alias
  */
-int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
+int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
+			  char **unit, double *scale)
 {
 	struct parse_events_term *term, *h;
 	struct perf_pmu_alias *alias;
 	int ret;
 
+	*unit   = NULL;
+	*scale  = 0;
+
 	list_for_each_entry_safe(term, h, head_terms, list) {
 		alias = pmu_find_alias(pmu, term);
 		if (!alias)
@@ -637,6 +663,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
 		ret = pmu_alias_terms(alias, &term->list);
 		if (ret)
 			return ret;
+
+		ret = check_unit_scale(alias, unit, scale);
+		if (ret)
+			return ret;
+
 		list_del(&term->list);
 		free(term);
 	}
@@ -760,62 +791,3 @@ bool pmu_have_event(const char *pname, const char *name)
 	}
 	return false;
 }
-
-static const char *pmu_event_unit(struct perf_pmu *pmu, const char *name)
-{
-	struct perf_pmu_alias *alias;
-	char buf[1024];
-	char *fname;
-	const char *unit = "?";
-
-	if (!name)
-		return unit;
-
-	list_for_each_entry(alias, &pmu->aliases, list) {
-		fname = format_alias(buf, sizeof(buf), pmu, alias);
-		if (!strcmp(fname, name)) {
-			unit = alias->unit;
-			break;
-		}
-	}
-	return unit;
-}
-
-static double pmu_event_scale(struct perf_pmu *pmu, const char *name)
-{
-	struct perf_pmu_alias *alias;
-	char buf[1024];
-	char *fname;
-	double scale = 1.0;
-
-	if (!name)
-		return 1.0;
-
-	list_for_each_entry(alias, &pmu->aliases, list) {
-		fname = format_alias(buf, sizeof(buf), pmu, alias);
-		if (!strcmp(fname, name)) {
-			scale = alias->scale;
-			break;
-		}
-	}
-	return scale;
-}
-
-int pmu_get_event_unit_scale(struct perf_evsel *evsel)
-{
-	__u32 type = evsel->attr.type;
-	struct perf_pmu *pmu;
-
-	if (!evsel->name)
-		return -1;
-
-	list_for_each_entry(pmu, &pmus, list) {
-		if (pmu->type == type)
-			goto found;
-	}
-	return -1;
-found:
-	evsel->unit  = pmu_event_unit(pmu, evsel->name);
-	evsel->scale = pmu_event_scale(pmu, evsel->name);
-	return 0;
-}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 6bf23b2..9183380 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -4,7 +4,6 @@
 #include <linux/bitops.h>
 #include <linux/perf_event.h>
 #include <stdbool.h>
-#include "util/evsel.h"
 
 enum {
 	PERF_PMU_FORMAT_VALUE_CONFIG,
@@ -29,7 +28,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
 int perf_pmu__config_terms(struct list_head *formats,
 			   struct perf_event_attr *attr,
 			   struct list_head *head_terms);
-int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms);
+int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
+			  char **unit, double *scale);
 struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
 				  struct list_head *head_terms);
 int perf_pmu_wrap(void);
@@ -46,6 +46,4 @@ void print_pmu_events(const char *event_glob, bool name_only);
 bool pmu_have_event(const char *pname, const char *name);
 
 int perf_pmu__test(void);
-
-int pmu_get_event_unit_scale(struct perf_evsel *evsel);
 #endif /* __PMU_H */
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-11-01 10:57   ` Jiri Olsa
@ 2013-11-04 14:47     ` Stephane Eranian
  2013-11-04 14:51       ` Jiri Olsa
  0 siblings, 1 reply; 15+ messages in thread
From: Stephane Eranian @ 2013-11-04 14:47 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: LKML, Peter Zijlstra, mingo, ak, Arnaldo Carvalho de Melo, Yan,
	Zheng, Borislav Petkov, Maria Dimakopoulou

Jiri,


Thanks for the patch. I tested it and it works fine.
I will merge it with my "unit and scale" and add
your Signed-off-by. Is that okay with you?



On Fri, Nov 1, 2013 at 11:57 AM, Jiri Olsa <jolsa@redhat.com> wrote:
> On Thu, Oct 31, 2013 at 03:59:40PM +0100, Stephane Eranian wrote:
>> This patch adds perf stat support fo rhandling event units and
>> scales as exported by the kernel.
>>
>> The kernel can export PMU events actual unit and scaling factor
>> via sysfs:
>> $ ls -1 /sys/devices/power/events/energy-*
>> /sys/devices/power/events/energy-cores
>> /sys/devices/power/events/energy-cores.scale
>> /sys/devices/power/events/energy-cores.unit
>> /sys/devices/power/events/energy-pkg
>> /sys/devices/power/events/energy-pkg.scale
>> /sys/devices/power/events/energy-pkg.unit
>> $ cat /sys/devices/power/events/energy-cores.scale
>> 2.3e-10
>> $ cat cat /sys/devices/power/events/energy-cores.unit
>> Joules
>>
>> This patch modifies the pmu event alias code to check
>> for the presence of the .unit and .scale files to load
>> the corresponding values. They are then used by perf stat
>> transparentely:
>>
>
> wrt our previous conversation, attached patch is what
> I meant (based on your changes)
>
> you could get rid of following functions:
>   pmu_event_unit
>   pmu_event_scale
>   pmu_get_event_unit_scale
>
> and have the unit/scale processing within the alias code
>
> the name that matters is the term name which is available
> in the parse_events_add_pmu as one of the terms
>
> jirka
> ---
>  tools/perf/util/parse-events.c | 29 ++++++++-----
>  tools/perf/util/pmu.c          | 92 +++++++++++++++---------------------------
>  tools/perf/util/pmu.h          |  6 +--
>  3 files changed, 53 insertions(+), 74 deletions(-)
>
> diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
> index be7eba8..90ae328 100644
> --- a/tools/perf/util/parse-events.c
> +++ b/tools/perf/util/parse-events.c
> @@ -269,9 +269,10 @@ const char *event_type(int type)
>
>
>
> -static int __add_event(struct list_head *list, int *idx,
> -                      struct perf_event_attr *attr,
> -                      char *name, struct cpu_map *cpus)
> +static struct perf_evsel *
> +__add_event(struct list_head *list, int *idx,
> +           struct perf_event_attr *attr,
> +           char *name, struct cpu_map *cpus)
>  {
>         struct perf_evsel *evsel;
>
> @@ -279,19 +280,19 @@ static int __add_event(struct list_head *list, int *idx,
>
>         evsel = perf_evsel__new(attr, (*idx)++);
>         if (!evsel)
> -               return -ENOMEM;
> +               return NULL;
>
>         evsel->cpus = cpus;
>         if (name)
>                 evsel->name = strdup(name);
>         list_add_tail(&evsel->node, list);
> -       return 0;
> +       return evsel;
>  }
>
>  static int add_event(struct list_head *list, int *idx,
>                      struct perf_event_attr *attr, char *name)
>  {
> -       return __add_event(list, idx, attr, name, NULL);
> +       return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM;
>  }
>
>  static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
> @@ -633,6 +634,9 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
>  {
>         struct perf_event_attr attr;
>         struct perf_pmu *pmu;
> +       struct perf_evsel *evsel;
> +       char *unit;
> +       double scale;
>
>         pmu = perf_pmu__find(name);
>         if (!pmu)
> @@ -640,7 +644,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
>
>         memset(&attr, 0, sizeof(attr));
>
> -       if (perf_pmu__check_alias(pmu, head_config))
> +       if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))
>                 return -EINVAL;
>
>         /*
> @@ -652,8 +656,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
>         if (perf_pmu__config(pmu, &attr, head_config))
>                 return -EINVAL;
>
> -       return __add_event(list, idx, &attr, pmu_event_name(head_config),
> -                          pmu->cpus);
> +       evsel = __add_event(list, idx, &attr, pmu_event_name(head_config),
> +                           pmu->cpus);
> +       if (evsel) {
> +               evsel->unit = unit;
> +               evsel->scale = scale;
> +       }
> +
> +       return evsel ? 0 : -ENOMEM;
>  }
>
>  int parse_events__modifier_group(struct list_head *list,
> @@ -838,7 +848,6 @@ int parse_events_name(struct list_head *list, char *name)
>         list_for_each_entry(evsel, list, node) {
>                 if (!evsel->name)
>                         evsel->name = strdup(name);
> -               pmu_get_event_unit_scale(evsel);
>         }
>
>         return 0;
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index e18ef87..df68d43 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -620,16 +620,42 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
>         return NULL;
>  }
>
> +
> +static int check_unit_scale(struct perf_pmu_alias *alias,
> +                           char **unit, double *scale)
> +{
> +       /*
> +        * Only one term in event definition can
> +        * define unit and scale, fail if there's
> +        * more than one.
> +        */
> +       if ((*unit && alias->unit) ||
> +           (*scale && alias->scale))
> +               return -EINVAL;
> +
> +       if (alias->unit)
> +               *unit = alias->unit;
> +
> +       if (alias->scale)
> +               *scale = alias->scale;
> +
> +       return 0;
> +}
> +
>  /*
>   * Find alias in the terms list and replace it with the terms
>   * defined for the alias
>   */
> -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
> +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
> +                         char **unit, double *scale)
>  {
>         struct parse_events_term *term, *h;
>         struct perf_pmu_alias *alias;
>         int ret;
>
> +       *unit   = NULL;
> +       *scale  = 0;
> +
>         list_for_each_entry_safe(term, h, head_terms, list) {
>                 alias = pmu_find_alias(pmu, term);
>                 if (!alias)
> @@ -637,6 +663,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
>                 ret = pmu_alias_terms(alias, &term->list);
>                 if (ret)
>                         return ret;
> +
> +               ret = check_unit_scale(alias, unit, scale);
> +               if (ret)
> +                       return ret;
> +
>                 list_del(&term->list);
>                 free(term);
>         }
> @@ -760,62 +791,3 @@ bool pmu_have_event(const char *pname, const char *name)
>         }
>         return false;
>  }
> -
> -static const char *pmu_event_unit(struct perf_pmu *pmu, const char *name)
> -{
> -       struct perf_pmu_alias *alias;
> -       char buf[1024];
> -       char *fname;
> -       const char *unit = "?";
> -
> -       if (!name)
> -               return unit;
> -
> -       list_for_each_entry(alias, &pmu->aliases, list) {
> -               fname = format_alias(buf, sizeof(buf), pmu, alias);
> -               if (!strcmp(fname, name)) {
> -                       unit = alias->unit;
> -                       break;
> -               }
> -       }
> -       return unit;
> -}
> -
> -static double pmu_event_scale(struct perf_pmu *pmu, const char *name)
> -{
> -       struct perf_pmu_alias *alias;
> -       char buf[1024];
> -       char *fname;
> -       double scale = 1.0;
> -
> -       if (!name)
> -               return 1.0;
> -
> -       list_for_each_entry(alias, &pmu->aliases, list) {
> -               fname = format_alias(buf, sizeof(buf), pmu, alias);
> -               if (!strcmp(fname, name)) {
> -                       scale = alias->scale;
> -                       break;
> -               }
> -       }
> -       return scale;
> -}
> -
> -int pmu_get_event_unit_scale(struct perf_evsel *evsel)
> -{
> -       __u32 type = evsel->attr.type;
> -       struct perf_pmu *pmu;
> -
> -       if (!evsel->name)
> -               return -1;
> -
> -       list_for_each_entry(pmu, &pmus, list) {
> -               if (pmu->type == type)
> -                       goto found;
> -       }
> -       return -1;
> -found:
> -       evsel->unit  = pmu_event_unit(pmu, evsel->name);
> -       evsel->scale = pmu_event_scale(pmu, evsel->name);
> -       return 0;
> -}
> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> index 6bf23b2..9183380 100644
> --- a/tools/perf/util/pmu.h
> +++ b/tools/perf/util/pmu.h
> @@ -4,7 +4,6 @@
>  #include <linux/bitops.h>
>  #include <linux/perf_event.h>
>  #include <stdbool.h>
> -#include "util/evsel.h"
>
>  enum {
>         PERF_PMU_FORMAT_VALUE_CONFIG,
> @@ -29,7 +28,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
>  int perf_pmu__config_terms(struct list_head *formats,
>                            struct perf_event_attr *attr,
>                            struct list_head *head_terms);
> -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms);
> +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
> +                         char **unit, double *scale);
>  struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
>                                   struct list_head *head_terms);
>  int perf_pmu_wrap(void);
> @@ -46,6 +46,4 @@ void print_pmu_events(const char *event_glob, bool name_only);
>  bool pmu_have_event(const char *pname, const char *name);
>
>  int perf_pmu__test(void);
> -
> -int pmu_get_event_unit_scale(struct perf_evsel *evsel);
>  #endif /* __PMU_H */
> --
> 1.7.11.7
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-11-04 14:47     ` Stephane Eranian
@ 2013-11-04 14:51       ` Jiri Olsa
  2013-11-05 13:34         ` Stephane Eranian
  0 siblings, 1 reply; 15+ messages in thread
From: Jiri Olsa @ 2013-11-04 14:51 UTC (permalink / raw)
  To: Stephane Eranian
  Cc: LKML, Peter Zijlstra, mingo, ak, Arnaldo Carvalho de Melo, Yan,
	Zheng, Borislav Petkov, Maria Dimakopoulou

On Mon, Nov 04, 2013 at 03:47:53PM +0100, Stephane Eranian wrote:
> Jiri,
> 
> 
> Thanks for the patch. I tested it and it works fine.
> I will merge it with my "unit and scale" and add
> your Signed-off-by. Is that okay with you?

sure, np

jirka

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-11-04 14:51       ` Jiri Olsa
@ 2013-11-05 13:34         ` Stephane Eranian
  2013-11-05 14:00           ` Jiri Olsa
  2013-11-05 14:16           ` Andi Kleen
  0 siblings, 2 replies; 15+ messages in thread
From: Stephane Eranian @ 2013-11-05 13:34 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: LKML, Peter Zijlstra, mingo, ak, Arnaldo Carvalho de Melo, Yan,
	Zheng, Borislav Petkov, Maria Dimakopoulou

Hi,

One thing I realized while testing is that we cannot simply add the
unit printout like that.
This may break all the scripts people may have written to parse the
output of perf stat.
I think we need to make the display of the unit optional. If I do:
   $ perf stat -e cycles ls

The output should remain as it was before and not show:
$ perf stat -e cycles ls
      22782847475 ? cycles

So I think we need a --show-unit option. It would be off by default.
Of course doing this causes a mess with the current code because
of all the various printf() in builtin-stat.c but I think it is better for
the end user.

Any opinion?

On Mon, Nov 4, 2013 at 3:51 PM, Jiri Olsa <jolsa@redhat.com> wrote:
> On Mon, Nov 04, 2013 at 03:47:53PM +0100, Stephane Eranian wrote:
>> Jiri,
>>
>>
>> Thanks for the patch. I tested it and it works fine.
>> I will merge it with my "unit and scale" and add
>> your Signed-off-by. Is that okay with you?
>
> sure, np
>
> jirka

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-11-05 13:34         ` Stephane Eranian
@ 2013-11-05 14:00           ` Jiri Olsa
  2013-11-05 14:04             ` Stephane Eranian
  2013-11-05 14:16           ` Andi Kleen
  1 sibling, 1 reply; 15+ messages in thread
From: Jiri Olsa @ 2013-11-05 14:00 UTC (permalink / raw)
  To: Stephane Eranian
  Cc: LKML, Peter Zijlstra, mingo, ak, Arnaldo Carvalho de Melo, Yan,
	Zheng, Borislav Petkov, Maria Dimakopoulou

On Tue, Nov 05, 2013 at 02:34:45PM +0100, Stephane Eranian wrote:
> Hi,
> 
> One thing I realized while testing is that we cannot simply add the
> unit printout like that.
> This may break all the scripts people may have written to parse the
> output of perf stat.

isn't it what the -x output is meant for?

  perf stat -x, ...

  1.738605,task-clock
  367,context-switches
  0,cpu-migrations
  272,page-faults
  6722006,cycles
  2592661,stalled-cycles-frontend
  1935855,stalled-cycles-backend
  4324013,instructions
  823229,branches
  11192,branch-misses

> I think we need to make the display of the unit optional. If I do:
>    $ perf stat -e cycles ls
> 
> The output should remain as it was before and not show:
> $ perf stat -e cycles ls
>       22782847475 ? cycles

maybe we should display just space ' ' instead
of the '?' ...seems confusing

> 
> So I think we need a --show-unit option. It would be off by default.
> Of course doing this causes a mess with the current code because
> of all the various printf() in builtin-stat.c but I think it is better for
> the end user.
> 
> Any opinion?

I haven't checked by I think we changed the default perf stat
output in the past without any fuzz

maybe just keep the -x output or add the unit
to the end of the line

jirka

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-11-05 14:00           ` Jiri Olsa
@ 2013-11-05 14:04             ` Stephane Eranian
  0 siblings, 0 replies; 15+ messages in thread
From: Stephane Eranian @ 2013-11-05 14:04 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: LKML, Peter Zijlstra, mingo, ak, Arnaldo Carvalho de Melo, Yan,
	Zheng, Borislav Petkov, Maria Dimakopoulou

On Tue, Nov 5, 2013 at 3:00 PM, Jiri Olsa <jolsa@redhat.com> wrote:
> On Tue, Nov 05, 2013 at 02:34:45PM +0100, Stephane Eranian wrote:
>> Hi,
>>
>> One thing I realized while testing is that we cannot simply add the
>> unit printout like that.
>> This may break all the scripts people may have written to parse the
>> output of perf stat.
>
> isn't it what the -x output is meant for?
>
>   perf stat -x, ...
>
Somewhat. It is used to make parsing easy for the perf stat output.
But if you have a script that was expecting 2222267,cycles and now
is getting 222267,?,cycles. Things will break.


>   1.738605,task-clock
>   367,context-switches
>   0,cpu-migrations
>   272,page-faults
>   6722006,cycles
>   2592661,stalled-cycles-frontend
>   1935855,stalled-cycles-backend
>   4324013,instructions
>   823229,branches
>   11192,branch-misses
>
>> I think we need to make the display of the unit optional. If I do:
>>    $ perf stat -e cycles ls
>>
>> The output should remain as it was before and not show:
>> $ perf stat -e cycles ls
>>       22782847475 ? cycles
>
> maybe we should display just space ' ' instead
> of the '?' ...seems confusing
>
But then, it is not so good for the -x option to get ' ' in the csv output.

>>
>> So I think we need a --show-unit option. It would be off by default.
>> Of course doing this causes a mess with the current code because
>> of all the various printf() in builtin-stat.c but I think it is better for
>> the end user.
>>
>> Any opinion?
>
> I haven't checked by I think we changed the default perf stat
> output in the past without any fuzz
>
> maybe just keep the -x output or add the unit
> to the end of the line
>
Well, that's what I had initially. But then people thought it was more
natural to have in before the event. I agreed with that.

I almost have the --show-unit option working now. I think we should
go with that.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-11-05 13:34         ` Stephane Eranian
  2013-11-05 14:00           ` Jiri Olsa
@ 2013-11-05 14:16           ` Andi Kleen
  2013-11-05 14:17             ` Stephane Eranian
  1 sibling, 1 reply; 15+ messages in thread
From: Andi Kleen @ 2013-11-05 14:16 UTC (permalink / raw)
  To: Stephane Eranian
  Cc: Jiri Olsa, LKML, Peter Zijlstra, mingo, Arnaldo Carvalho de Melo,
	Yan, Zheng, Borislav Petkov, Maria Dimakopoulou

> Any opinion?

Just don't show the unit if there is no unit.
That is what I asked for earlier.

-Andi

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-11-05 14:16           ` Andi Kleen
@ 2013-11-05 14:17             ` Stephane Eranian
  2013-11-05 15:03               ` Stephane Eranian
  0 siblings, 1 reply; 15+ messages in thread
From: Stephane Eranian @ 2013-11-05 14:17 UTC (permalink / raw)
  To: Andi Kleen
  Cc: Jiri Olsa, LKML, Peter Zijlstra, mingo, Arnaldo Carvalho de Melo,
	Yan, Zheng, Borislav Petkov, Maria Dimakopoulou

On Tue, Nov 5, 2013 at 3:16 PM, Andi Kleen <ak@linux.intel.com> wrote:
>> Any opinion?
>
> Just don't show the unit if there is no unit.
> That is what I asked for earlier.
>
You said, print ? if no unit.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v4 2/4] perf stat: add event unit and scale support
  2013-11-05 14:17             ` Stephane Eranian
@ 2013-11-05 15:03               ` Stephane Eranian
  0 siblings, 0 replies; 15+ messages in thread
From: Stephane Eranian @ 2013-11-05 15:03 UTC (permalink / raw)
  To: Andi Kleen
  Cc: Jiri Olsa, LKML, Peter Zijlstra, mingo, Arnaldo Carvalho de Melo,
	Yan, Zheng, Borislav Petkov, Maria Dimakopoulou

On Tue, Nov 5, 2013 at 3:17 PM, Stephane Eranian <eranian@google.com> wrote:
> On Tue, Nov 5, 2013 at 3:16 PM, Andi Kleen <ak@linux.intel.com> wrote:
>>> Any opinion?
>>
>> Just don't show the unit if there is no unit.
>> That is what I asked for earlier.
>>
> You said, print ? if no unit.

Okay, I now print nothing when there is no unit. Fixed up a couple of
printf alignment issues.
It's all good now. Posting the series momentarily.

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2013-11-05 15:03 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-31 14:59 [PATCH v4 0/4] perf/x86: add Intel RAPL PMU support Stephane Eranian
2013-10-31 14:59 ` [PATCH v4 1/4] perf: add active_entry list head to struct perf_event Stephane Eranian
2013-10-31 14:59 ` [PATCH v4 2/4] perf stat: add event unit and scale support Stephane Eranian
2013-11-01 10:57   ` Jiri Olsa
2013-11-04 14:47     ` Stephane Eranian
2013-11-04 14:51       ` Jiri Olsa
2013-11-05 13:34         ` Stephane Eranian
2013-11-05 14:00           ` Jiri Olsa
2013-11-05 14:04             ` Stephane Eranian
2013-11-05 14:16           ` Andi Kleen
2013-11-05 14:17             ` Stephane Eranian
2013-11-05 15:03               ` Stephane Eranian
2013-10-31 14:59 ` [PATCH v4 3/4] perf/x86: add Intel RAPL PMU support Stephane Eranian
2013-10-31 15:14   ` Peter Zijlstra
2013-10-31 14:59 ` [PATCH v4 4/4] perf,x86: add RAPL hrtimer support Stephane Eranian

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).