All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V8 00/23] Coresight integration with perf
@ 2016-01-14 21:45 ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

This patchset aims to integrate configuration and control of
the Coresight tracers with the perf sub-system.

The goal is to use PMUs to represent tracers and the auxiliary
buffer enhancement to collect processor traces.  As such a lot
of work is done to move the current Coresight sysFS oriented
configuration and control commands to perf's AUX API.

For the time being the work concentrates on ETMv3 and ETB1.0
sink buffer.  Work on ETMv4 and other type of sink buffers
will follow once a foundation has been established.

This patchset is based on [1] and assumes [2] has been applied.

Best regards,
Mathieu

Changes since V7:
* Moved coresight_release_path() a workqueue context, preventing
  pm_runtime_put_sync() from being called from an invalid
  (i.e interrupt) context.
* Fixed wrong 'buf_ptr' type in function etb_update_buffer().
* In function auxtrace_record__init(), no longer setting the 'err'
  pointer to '-EINVAL'.
* Removing erroneous call to kfree() in function etm_disable_sysfs(). 

[1]. https://git.linaro.org/kernel/coresight.git/shortlog/refs/heads/next 
[2]. http://www.spinics.net/lists/arm-kernel/msg474573.html 

Changes since V6:
* Rebased to 4.4-rc5.
* Thanks to [1], all tracer configuration now happens
  when events are scheduled.

[1]. https://lwn.net/Articles/667573/

Changes since V5:
* Addressed Arnaldo's comment about using a struct perf_evlist rather
  than a struct perf_session.
* Addressed kbuild test robot's anomaly report [1].
* Moved perf_aux_output_{begin|end} to etm_event_{start|stop} in
  accordance with [2].

[1]. https://lkml.org/lkml/2015/11/30/46
[2]. http://thread.gmane.org/gmane.linux.kernel/2099328/focus=2099343

Changes since V4:
* Reworked the way event configuration information is kept, fixing
  a few corner cases when doing per CPU tracing.
* Rebased on 4.4-rc1.
* Added 'Cc' to specific perf related patches in the hope of suscitating
  interest from reviewers, i.e perf maintainers.

Changes since V3:
* Rebased to GKH's char-misc-next tree (e2d8680741ed).

Changes since V2:
* Rebased to v4.3.
* Used the -M option to "git format-patch".
* Decoupled tracers and path - both are now completely independent.
* Moved path enablement/release to Perf's add/del functions.
* Moved PM runtime operations to the coresight core rather than
  individual drivers.
* Enhanced mutual exclusion scheme between perf and sysFS.
* Removed architecture specific define in perf cmd line tool.
* Got rid of unused fields in structure 'cs_buffers'.
* Enhanced changelog for patch 16/26.

Changes since V1:
 * Fixed typos in typographical error in documentation.
 * Moved to a multi session support scheme.
 * Split static and dynamic tracer configuration.
 * Fixed configuration for user and kernel space tracing.
 * Using WARN_ON_ONCE() rather than WARN_ON().
 * Implemented strategy to prevent tracers from being used simultaneously.
 * Changed sink_ops::unset_buffer() to sink_ops::reset_buffer().
 * Moves ETM's sysFS interface from driver core to dedicated file.
 * Removed spinlock in "etm_cpu_id()".
 * Aggregated PMU driver pieces in a single patch.
 * Added user space changes and rebased everything to v4.3-rc5.

Mathieu Poirier (23):
  coresight: associating path with session rather than tracer
  coresight: add API to get sink from path
  coresight: moving PM runtime operations to core framework
  coresight: etm3x: moving etm_readl/writel to header file
  coresight: etm3x: moving sysFS entries to dedicated file
  coresight: etm3x: unlocking tracers in default arch init
  coresight: etm3x: splitting struct etm_drvdata
  coresight: etm3x: adding operation mode for etm_enable()
  coresight: etm3x: set progbit to stop trace collection
  coresight: etm3x: changing default trace configuration
  coresight: etm3x: consolidating initial config
  coresight: etm3x: implementing user/kernel mode tracing
  coresight: etm3x: implementing perf_enable/disable() API
  coresight: etb10: moving to local atomic operations
  coresight: etb10: adding operation mode for sink->enable()
  coresight: etb10: implementing AUX API
  coresight: updating documentation to reflect integration with perf
  coresight: etm-perf: new PMU driver for ETM tracers
  coresight: introducing a global trace ID function
  perf tools: making function set_max_cpu_num() non static
  perf tools: adding perf_evlist to *info_priv_size()
  perf tools: making coresight PMU listable
  perf tools: adding coresight etm PMU record capabilities

 Documentation/trace/coresight.txt                  |  138 +-
 MAINTAINERS                                        |    4 +
 drivers/hwtracing/coresight/Kconfig                |    1 +
 drivers/hwtracing/coresight/Makefile               |    4 +-
 drivers/hwtracing/coresight/coresight-etb10.c      |  276 ++-
 drivers/hwtracing/coresight/coresight-etm-perf.c   |  398 +++++
 drivers/hwtracing/coresight/coresight-etm-perf.h   |   32 +
 drivers/hwtracing/coresight/coresight-etm.h        |  142 +-
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    | 1272 ++++++++++++++
 drivers/hwtracing/coresight/coresight-etm3x.c      | 1816 +++++---------------
 drivers/hwtracing/coresight/coresight-etm4x.c      |   10 +-
 drivers/hwtracing/coresight/coresight-funnel.c     |    2 -
 drivers/hwtracing/coresight/coresight-priv.h       |   15 +
 .../coresight/coresight-replicator-qcom.c          |    4 -
 drivers/hwtracing/coresight/coresight-replicator.c |    2 -
 drivers/hwtracing/coresight/coresight-tmc.c        |    7 +-
 drivers/hwtracing/coresight/coresight-tpiu.c       |    4 +-
 drivers/hwtracing/coresight/coresight.c            |  326 ++--
 include/linux/coresight-pmu.h                      |   39 +
 include/linux/coresight.h                          |   31 +-
 tools/perf/arch/arm/util/Build                     |    2 +
 tools/perf/arch/arm/util/auxtrace.c                |   54 +
 tools/perf/arch/arm/util/cs_etm.c                  |  466 +++++
 tools/perf/arch/arm/util/cs_etm.h                  |   44 +
 tools/perf/arch/arm/util/pmu.c                     |   34 +
 tools/perf/arch/x86/util/intel-bts.c               |    4 +-
 tools/perf/arch/x86/util/intel-pt.c                |    4 +-
 tools/perf/config/Makefile                         |   17 +-
 tools/perf/util/auxtrace.c                         |    8 +-
 tools/perf/util/auxtrace.h                         |    7 +-
 tools/perf/util/cpumap.c                           |    2 +-
 tools/perf/util/cpumap.h                           |    1 +
 32 files changed, 3522 insertions(+), 1644 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.h
 create mode 100644 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
 create mode 100644 include/linux/coresight-pmu.h
 create mode 100644 tools/perf/arch/arm/util/auxtrace.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.h
 create mode 100644 tools/perf/arch/arm/util/pmu.c

-- 
2.1.4

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

* [PATCH V8 00/23] Coresight integration with perf
@ 2016-01-14 21:45 ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset aims to integrate configuration and control of
the Coresight tracers with the perf sub-system.

The goal is to use PMUs to represent tracers and the auxiliary
buffer enhancement to collect processor traces.  As such a lot
of work is done to move the current Coresight sysFS oriented
configuration and control commands to perf's AUX API.

For the time being the work concentrates on ETMv3 and ETB1.0
sink buffer.  Work on ETMv4 and other type of sink buffers
will follow once a foundation has been established.

This patchset is based on [1] and assumes [2] has been applied.

Best regards,
Mathieu

Changes since V7:
* Moved coresight_release_path() a workqueue context, preventing
  pm_runtime_put_sync() from being called from an invalid
  (i.e interrupt) context.
* Fixed wrong 'buf_ptr' type in function etb_update_buffer().
* In function auxtrace_record__init(), no longer setting the 'err'
  pointer to '-EINVAL'.
* Removing erroneous call to kfree() in function etm_disable_sysfs(). 

[1]. https://git.linaro.org/kernel/coresight.git/shortlog/refs/heads/next 
[2]. http://www.spinics.net/lists/arm-kernel/msg474573.html 

Changes since V6:
* Rebased to 4.4-rc5.
* Thanks to [1], all tracer configuration now happens
  when events are scheduled.

[1]. https://lwn.net/Articles/667573/

Changes since V5:
* Addressed Arnaldo's comment about using a struct perf_evlist rather
  than a struct perf_session.
* Addressed kbuild test robot's anomaly report [1].
* Moved perf_aux_output_{begin|end} to etm_event_{start|stop} in
  accordance with [2].

[1]. https://lkml.org/lkml/2015/11/30/46
[2]. http://thread.gmane.org/gmane.linux.kernel/2099328/focus=2099343

Changes since V4:
* Reworked the way event configuration information is kept, fixing
  a few corner cases when doing per CPU tracing.
* Rebased on 4.4-rc1.
* Added 'Cc' to specific perf related patches in the hope of suscitating
  interest from reviewers, i.e perf maintainers.

Changes since V3:
* Rebased to GKH's char-misc-next tree (e2d8680741ed).

Changes since V2:
* Rebased to v4.3.
* Used the -M option to "git format-patch".
* Decoupled tracers and path - both are now completely independent.
* Moved path enablement/release to Perf's add/del functions.
* Moved PM runtime operations to the coresight core rather than
  individual drivers.
* Enhanced mutual exclusion scheme between perf and sysFS.
* Removed architecture specific define in perf cmd line tool.
* Got rid of unused fields in structure 'cs_buffers'.
* Enhanced changelog for patch 16/26.

Changes since V1:
 * Fixed typos in typographical error in documentation.
 * Moved to a multi session support scheme.
 * Split static and dynamic tracer configuration.
 * Fixed configuration for user and kernel space tracing.
 * Using WARN_ON_ONCE() rather than WARN_ON().
 * Implemented strategy to prevent tracers from being used simultaneously.
 * Changed sink_ops::unset_buffer() to sink_ops::reset_buffer().
 * Moves ETM's sysFS interface from driver core to dedicated file.
 * Removed spinlock in "etm_cpu_id()".
 * Aggregated PMU driver pieces in a single patch.
 * Added user space changes and rebased everything to v4.3-rc5.

Mathieu Poirier (23):
  coresight: associating path with session rather than tracer
  coresight: add API to get sink from path
  coresight: moving PM runtime operations to core framework
  coresight: etm3x: moving etm_readl/writel to header file
  coresight: etm3x: moving sysFS entries to dedicated file
  coresight: etm3x: unlocking tracers in default arch init
  coresight: etm3x: splitting struct etm_drvdata
  coresight: etm3x: adding operation mode for etm_enable()
  coresight: etm3x: set progbit to stop trace collection
  coresight: etm3x: changing default trace configuration
  coresight: etm3x: consolidating initial config
  coresight: etm3x: implementing user/kernel mode tracing
  coresight: etm3x: implementing perf_enable/disable() API
  coresight: etb10: moving to local atomic operations
  coresight: etb10: adding operation mode for sink->enable()
  coresight: etb10: implementing AUX API
  coresight: updating documentation to reflect integration with perf
  coresight: etm-perf: new PMU driver for ETM tracers
  coresight: introducing a global trace ID function
  perf tools: making function set_max_cpu_num() non static
  perf tools: adding perf_evlist to *info_priv_size()
  perf tools: making coresight PMU listable
  perf tools: adding coresight etm PMU record capabilities

 Documentation/trace/coresight.txt                  |  138 +-
 MAINTAINERS                                        |    4 +
 drivers/hwtracing/coresight/Kconfig                |    1 +
 drivers/hwtracing/coresight/Makefile               |    4 +-
 drivers/hwtracing/coresight/coresight-etb10.c      |  276 ++-
 drivers/hwtracing/coresight/coresight-etm-perf.c   |  398 +++++
 drivers/hwtracing/coresight/coresight-etm-perf.h   |   32 +
 drivers/hwtracing/coresight/coresight-etm.h        |  142 +-
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    | 1272 ++++++++++++++
 drivers/hwtracing/coresight/coresight-etm3x.c      | 1816 +++++---------------
 drivers/hwtracing/coresight/coresight-etm4x.c      |   10 +-
 drivers/hwtracing/coresight/coresight-funnel.c     |    2 -
 drivers/hwtracing/coresight/coresight-priv.h       |   15 +
 .../coresight/coresight-replicator-qcom.c          |    4 -
 drivers/hwtracing/coresight/coresight-replicator.c |    2 -
 drivers/hwtracing/coresight/coresight-tmc.c        |    7 +-
 drivers/hwtracing/coresight/coresight-tpiu.c       |    4 +-
 drivers/hwtracing/coresight/coresight.c            |  326 ++--
 include/linux/coresight-pmu.h                      |   39 +
 include/linux/coresight.h                          |   31 +-
 tools/perf/arch/arm/util/Build                     |    2 +
 tools/perf/arch/arm/util/auxtrace.c                |   54 +
 tools/perf/arch/arm/util/cs_etm.c                  |  466 +++++
 tools/perf/arch/arm/util/cs_etm.h                  |   44 +
 tools/perf/arch/arm/util/pmu.c                     |   34 +
 tools/perf/arch/x86/util/intel-bts.c               |    4 +-
 tools/perf/arch/x86/util/intel-pt.c                |    4 +-
 tools/perf/config/Makefile                         |   17 +-
 tools/perf/util/auxtrace.c                         |    8 +-
 tools/perf/util/auxtrace.h                         |    7 +-
 tools/perf/util/cpumap.c                           |    2 +-
 tools/perf/util/cpumap.h                           |    1 +
 32 files changed, 3522 insertions(+), 1644 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.h
 create mode 100644 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
 create mode 100644 include/linux/coresight-pmu.h
 create mode 100644 tools/perf/arch/arm/util/auxtrace.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.h
 create mode 100644 tools/perf/arch/arm/util/pmu.c

-- 
2.1.4

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

* [PATCH V8 01/23] coresight: associating path with session rather than tracer
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:45   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

When using the Coresight framework from the sysFS interface a
tracer is always handling a single session and as such, a path
can be associated with a tracer.  But when supporting multiple
session per tracer there is no guarantee that sessions will always
have the same path from source to sink.

This patch is removing the automatic association between path and
tracers.  The building of a path and enablement of the components
in the path are decoupled, allowing for the association of a path
with a session rather than a tracer.

To keep backward functionality with the current sysFS access methods
a per-cpu place holder is used to keep a handle on the path built when
tracers are enabled.  Lastly APIs to build paths and enable tracers are
made public so that other subsystem can interact with the Coresight
framework.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-priv.h |   5 +
 drivers/hwtracing/coresight/coresight.c      | 296 ++++++++++++++++++---------
 include/linux/coresight.h                    |   2 -
 3 files changed, 206 insertions(+), 97 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 62fcd98cc7cf..7b193a34d709 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -52,6 +52,11 @@ static inline void CS_UNLOCK(void __iomem *addr)
 	} while (0);
 }
 
+void coresight_disable_path(struct list_head *path);
+int coresight_enable_path(struct list_head *path);
+struct list_head *coresight_build_path(struct coresight_device *csdev);
+void coresight_release_path(struct list_head *path);
+
 #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
 extern int etm_readl_cp14(u32 off, unsigned int *val);
 extern int etm_writel_cp14(u32 off, u32 val);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 7e6e9ff27dd1..f26589effb70 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -29,6 +29,22 @@
 
 static DEFINE_MUTEX(coresight_mutex);
 
+/**
+ * struct coresight_node - elements of a path, from source to sink
+ * @csdev:	Address of an element.
+ * @link:	hook to the list.
+ */
+struct coresight_node {
+	struct coresight_device *csdev;
+	struct list_head link;
+};
+
+/*
+ * When operating Coresight drivers from the sysFS interface, only a single
+ * path can exist from a tracer (associated to a CPU) to a sink.
+ */
+static DEFINE_PER_CPU(struct list_head *, sysfs_path);
+
 static int coresight_id_match(struct device *dev, void *data)
 {
 	int trace_id, i_trace_id;
@@ -68,15 +84,12 @@ static int coresight_source_is_unique(struct coresight_device *csdev)
 				 csdev, coresight_id_match);
 }
 
-static int coresight_find_link_inport(struct coresight_device *csdev)
+static int coresight_find_link_inport(struct coresight_device *csdev,
+				      struct coresight_device *parent)
 {
 	int i;
-	struct coresight_device *parent;
 	struct coresight_connection *conn;
 
-	parent = container_of(csdev->path_link.next,
-			      struct coresight_device, path_link);
-
 	for (i = 0; i < parent->nr_outport; i++) {
 		conn = &parent->conns[i];
 		if (conn->child_dev == csdev)
@@ -89,15 +102,12 @@ static int coresight_find_link_inport(struct coresight_device *csdev)
 	return 0;
 }
 
-static int coresight_find_link_outport(struct coresight_device *csdev)
+static int coresight_find_link_outport(struct coresight_device *csdev,
+				       struct coresight_device *child)
 {
 	int i;
-	struct coresight_device *child;
 	struct coresight_connection *conn;
 
-	child = container_of(csdev->path_link.prev,
-			     struct coresight_device, path_link);
-
 	for (i = 0; i < csdev->nr_outport; i++) {
 		conn = &csdev->conns[i];
 		if (conn->child_dev == child)
@@ -138,14 +148,19 @@ static void coresight_disable_sink(struct coresight_device *csdev)
 	}
 }
 
-static int coresight_enable_link(struct coresight_device *csdev)
+static int coresight_enable_link(struct coresight_device *csdev,
+				 struct coresight_device *parent,
+				 struct coresight_device *child)
 {
 	int ret;
 	int link_subtype;
 	int refport, inport, outport;
 
-	inport = coresight_find_link_inport(csdev);
-	outport = coresight_find_link_outport(csdev);
+	if (!parent || !child)
+		return -EINVAL;
+
+	inport = coresight_find_link_inport(csdev, parent);
+	outport = coresight_find_link_outport(csdev, child);
 	link_subtype = csdev->subtype.link_subtype;
 
 	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
@@ -168,14 +183,19 @@ static int coresight_enable_link(struct coresight_device *csdev)
 	return 0;
 }
 
-static void coresight_disable_link(struct coresight_device *csdev)
+static void coresight_disable_link(struct coresight_device *csdev,
+				   struct coresight_device *parent,
+				   struct coresight_device *child)
 {
 	int i, nr_conns;
 	int link_subtype;
 	int refport, inport, outport;
 
-	inport = coresight_find_link_inport(csdev);
-	outport = coresight_find_link_outport(csdev);
+	if (!parent || !child)
+		return;
+
+	inport = coresight_find_link_inport(csdev, parent);
+	outport = coresight_find_link_outport(csdev, child);
 	link_subtype = csdev->subtype.link_subtype;
 
 	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
@@ -235,109 +255,167 @@ static void coresight_disable_source(struct coresight_device *csdev)
 	}
 }
 
-static int coresight_enable_path(struct list_head *path)
+void coresight_disable_path(struct list_head *path)
 {
-	int ret = 0;
-	struct coresight_device *cd;
+	struct coresight_node *nd;
+	struct coresight_device *csdev, *parent, *child;
 
-	/*
-	 * At this point we have a full @path, from source to sink.  The
-	 * sink is the first entry and the source the last one.  Go through
-	 * all the components and enable them one by one.
-	 */
-	list_for_each_entry(cd, path, path_link) {
-		if (cd == list_first_entry(path, struct coresight_device,
-					   path_link)) {
-			ret = coresight_enable_sink(cd);
-		} else if (list_is_last(&cd->path_link, path)) {
-			/*
-			 * Don't enable the source just yet - this needs to
-			 * happen at the very end when all links and sink
-			 * along the path have been configured properly.
-			 */
-			;
-		} else {
-			ret = coresight_enable_link(cd);
+	list_for_each_entry(nd, path, link) {
+		csdev = nd->csdev;
+
+		switch (csdev->type) {
+		case CORESIGHT_DEV_TYPE_SINK:
+		case CORESIGHT_DEV_TYPE_LINKSINK:
+			coresight_disable_sink(csdev);
+			break;
+		case CORESIGHT_DEV_TYPE_SOURCE:
+			/* sources are disabled from either sysFS or Perf */
+			break;
+		case CORESIGHT_DEV_TYPE_LINK:
+			parent = list_prev_entry(nd, link)->csdev;
+			child = list_next_entry(nd, link)->csdev;
+			coresight_disable_link(csdev, parent, child);
+			break;
+		default:
+			break;
 		}
-		if (ret)
-			goto err;
 	}
+}
 
-	return 0;
-err:
-	list_for_each_entry_continue_reverse(cd, path, path_link) {
-		if (cd == list_first_entry(path, struct coresight_device,
-					   path_link)) {
-			coresight_disable_sink(cd);
-		} else if (list_is_last(&cd->path_link, path)) {
-			;
-		} else {
-			coresight_disable_link(cd);
+int coresight_enable_path(struct list_head *path)
+{
+
+	int ret = 0;
+	struct coresight_node *nd;
+	struct coresight_device *csdev, *parent, *child;
+
+	list_for_each_entry_reverse(nd, path, link) {
+		csdev = nd->csdev;
+
+		switch (csdev->type) {
+		case CORESIGHT_DEV_TYPE_SINK:
+		case CORESIGHT_DEV_TYPE_LINKSINK:
+			ret = coresight_enable_sink(csdev);
+			if (ret)
+				goto err;
+			break;
+		case CORESIGHT_DEV_TYPE_SOURCE:
+			/* sources are enabled from either sysFS or Perf */
+			break;
+		case CORESIGHT_DEV_TYPE_LINK:
+			parent = list_prev_entry(nd, link)->csdev;
+			child = list_next_entry(nd, link)->csdev;
+			ret = coresight_enable_link(csdev, parent, child);
+			if (ret)
+				goto err;
+			break;
+		default:
+			goto err;
 		}
 	}
 
+out:
 	return ret;
+err:
+	coresight_disable_path(path);
+	goto out;
 }
 
-static int coresight_disable_path(struct list_head *path)
+/**
+ * _coresight_build_path - recursively build a path from a @csdev to a sink.
+ * @csdev:	The device to start from.
+ * @path:	The list to add devices to.
+ *
+ * The tree of Coresight device is traversed until an activated sink is
+ * found.  From there the sink is added to the list along with all the
+ * devices that led to that point - the end result is a list from source
+ * to sink. In that list the source is the first device and the sink the
+ * last one.
+ */
+static int _coresight_build_path(struct coresight_device *csdev,
+				 struct list_head *path)
 {
-	struct coresight_device *cd;
+	int i;
+	bool found = false;
+	struct coresight_node *node;
+	struct coresight_connection *conn;
 
-	list_for_each_entry_reverse(cd, path, path_link) {
-		if (cd == list_first_entry(path, struct coresight_device,
-					   path_link)) {
-			coresight_disable_sink(cd);
-		} else if (list_is_last(&cd->path_link, path)) {
-			/*
-			 * The source has already been stopped, no need
-			 * to do it again here.
-			 */
-			;
-		} else {
-			coresight_disable_link(cd);
+	/* An activated sink has been found.  Enqueue the element */
+	if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
+	     csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && csdev->activated)
+		goto out;
+
+	/* Not a sink - recursively explore each port found on this element */
+	for (i = 0; i < csdev->nr_outport; i++) {
+		conn = &csdev->conns[i];
+		if (_coresight_build_path(conn->child_dev, path) == 0) {
+			found = true;
+			break;
 		}
 	}
 
+	if (!found)
+		return -ENODEV;
+
+out:
+	/*
+	 * A path from this element to a sink has been found.  The elements
+	 * leading to the sink are already enqueued, all that is left to do
+	 * is add a node for this element.
+	 */
+	node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+
+	node->csdev = csdev;
+	list_add(&node->link, path);
+
 	return 0;
 }
 
-static int coresight_build_paths(struct coresight_device *csdev,
-				 struct list_head *path,
-				 bool enable)
+struct list_head *coresight_build_path(struct coresight_device *csdev)
 {
-	int i, ret = -EINVAL;
-	struct coresight_connection *conn;
+	struct list_head *path;
 
-	list_add(&csdev->path_link, path);
+	path = kzalloc(sizeof(struct list_head), GFP_KERNEL);
+	if (!path)
+		return NULL;
 
-	if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
-	    csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
-	    csdev->activated) {
-		if (enable)
-			ret = coresight_enable_path(path);
-		else
-			ret = coresight_disable_path(path);
-	} else {
-		for (i = 0; i < csdev->nr_outport; i++) {
-			conn = &csdev->conns[i];
-			if (coresight_build_paths(conn->child_dev,
-						    path, enable) == 0)
-				ret = 0;
-		}
+	INIT_LIST_HEAD(path);
+
+	if (_coresight_build_path(csdev, path)) {
+		kfree(path);
+		path = NULL;
 	}
 
-	if (list_first_entry(path, struct coresight_device, path_link) != csdev)
-		dev_err(&csdev->dev, "wrong device in %s\n", __func__);
+	return path;
+}
 
-	list_del(&csdev->path_link);
+/**
+ * coresight_release_path - release a previously built path.
+ * @path:	the path to release.
+ *
+ * Go through all the elements of a path and 1) removed it from the list and
+ * 2) free the memory allocated for each node.
+ */
+void coresight_release_path(struct list_head *path)
+{
+	struct coresight_node *nd, *next;
 
-	return ret;
+	list_for_each_entry_safe(nd, next, path, link) {
+		list_del(&nd->link);
+		kfree(nd);
+	}
+
+	kfree(path);
+	path = NULL;
 }
 
 int coresight_enable(struct coresight_device *csdev)
 {
 	int ret = 0;
-	LIST_HEAD(path);
+	int cpu;
+	struct list_head *path;
 
 	mutex_lock(&coresight_mutex);
 	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
@@ -348,22 +426,47 @@ int coresight_enable(struct coresight_device *csdev)
 	if (csdev->enable)
 		goto out;
 
-	if (coresight_build_paths(csdev, &path, true)) {
-		dev_err(&csdev->dev, "building path(s) failed\n");
+	path = coresight_build_path(csdev);
+	if (!path) {
+		pr_err("building path(s) failed\n");
 		goto out;
 	}
 
-	if (coresight_enable_source(csdev))
-		dev_err(&csdev->dev, "source enable failed\n");
+	ret = coresight_enable_path(path);
+	if (ret)
+		goto err_path;
+
+	ret = coresight_enable_source(csdev);
+	if (ret)
+		goto err_source;
+
+	/*
+	 * When working from sysFS it is important to keep track
+	 * of the paths that were created so that they can be
+	 * undone in 'coresight_disable()'.  Since there can only
+	 * be a single session per tracer (when working from sysFS)
+	 * a per-cpu variable will do just fine.
+	 */
+	cpu = source_ops(csdev)->cpu_id(csdev);
+	per_cpu(sysfs_path, cpu) = path;
+
 out:
 	mutex_unlock(&coresight_mutex);
 	return ret;
+
+err_source:
+	coresight_disable_path(path);
+
+err_path:
+	coresight_release_path(path);
+	goto out;
 }
 EXPORT_SYMBOL_GPL(coresight_enable);
 
 void coresight_disable(struct coresight_device *csdev)
 {
-	LIST_HEAD(path);
+	int cpu;
+	struct list_head *path;
 
 	mutex_lock(&coresight_mutex);
 	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
@@ -373,9 +476,12 @@ void coresight_disable(struct coresight_device *csdev)
 	if (!csdev->enable)
 		goto out;
 
+	cpu = source_ops(csdev)->cpu_id(csdev);
+	path = per_cpu(sysfs_path, cpu);
 	coresight_disable_source(csdev);
-	if (coresight_build_paths(csdev, &path, false))
-		dev_err(&csdev->dev, "releasing path(s) failed\n");
+	coresight_disable_path(path);
+	coresight_release_path(path);
+	per_cpu(sysfs_path, cpu) = NULL;
 
 out:
 	mutex_unlock(&coresight_mutex);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index bf62b265bf52..851ecb22397e 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -152,7 +152,6 @@ struct coresight_connection {
 		by @coresight_ops.
  * @dev:	The device entity associated to this component.
  * @refcnt:	keep track of what is in use.
- * @path_link:	link of current component into the path being enabled.
  * @orphan:	true if the component has connections that haven't been linked.
  * @enable:	'true' if component is currently part of an active path.
  * @activated:	'true' only if a _sink_ has been activated.  A sink can be
@@ -168,7 +167,6 @@ struct coresight_device {
 	const struct coresight_ops *ops;
 	struct device dev;
 	atomic_t *refcnt;
-	struct list_head path_link;
 	bool orphan;
 	bool enable;	/* true only if configured as part of a path */
 	bool activated;	/* true only if a sink is part of a path */
-- 
2.1.4

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

* [PATCH V8 01/23] coresight: associating path with session rather than tracer
@ 2016-01-14 21:45   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel

When using the Coresight framework from the sysFS interface a
tracer is always handling a single session and as such, a path
can be associated with a tracer.  But when supporting multiple
session per tracer there is no guarantee that sessions will always
have the same path from source to sink.

This patch is removing the automatic association between path and
tracers.  The building of a path and enablement of the components
in the path are decoupled, allowing for the association of a path
with a session rather than a tracer.

To keep backward functionality with the current sysFS access methods
a per-cpu place holder is used to keep a handle on the path built when
tracers are enabled.  Lastly APIs to build paths and enable tracers are
made public so that other subsystem can interact with the Coresight
framework.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-priv.h |   5 +
 drivers/hwtracing/coresight/coresight.c      | 296 ++++++++++++++++++---------
 include/linux/coresight.h                    |   2 -
 3 files changed, 206 insertions(+), 97 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 62fcd98cc7cf..7b193a34d709 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -52,6 +52,11 @@ static inline void CS_UNLOCK(void __iomem *addr)
 	} while (0);
 }
 
+void coresight_disable_path(struct list_head *path);
+int coresight_enable_path(struct list_head *path);
+struct list_head *coresight_build_path(struct coresight_device *csdev);
+void coresight_release_path(struct list_head *path);
+
 #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
 extern int etm_readl_cp14(u32 off, unsigned int *val);
 extern int etm_writel_cp14(u32 off, u32 val);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 7e6e9ff27dd1..f26589effb70 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -29,6 +29,22 @@
 
 static DEFINE_MUTEX(coresight_mutex);
 
+/**
+ * struct coresight_node - elements of a path, from source to sink
+ * @csdev:	Address of an element.
+ * @link:	hook to the list.
+ */
+struct coresight_node {
+	struct coresight_device *csdev;
+	struct list_head link;
+};
+
+/*
+ * When operating Coresight drivers from the sysFS interface, only a single
+ * path can exist from a tracer (associated to a CPU) to a sink.
+ */
+static DEFINE_PER_CPU(struct list_head *, sysfs_path);
+
 static int coresight_id_match(struct device *dev, void *data)
 {
 	int trace_id, i_trace_id;
@@ -68,15 +84,12 @@ static int coresight_source_is_unique(struct coresight_device *csdev)
 				 csdev, coresight_id_match);
 }
 
-static int coresight_find_link_inport(struct coresight_device *csdev)
+static int coresight_find_link_inport(struct coresight_device *csdev,
+				      struct coresight_device *parent)
 {
 	int i;
-	struct coresight_device *parent;
 	struct coresight_connection *conn;
 
-	parent = container_of(csdev->path_link.next,
-			      struct coresight_device, path_link);
-
 	for (i = 0; i < parent->nr_outport; i++) {
 		conn = &parent->conns[i];
 		if (conn->child_dev == csdev)
@@ -89,15 +102,12 @@ static int coresight_find_link_inport(struct coresight_device *csdev)
 	return 0;
 }
 
-static int coresight_find_link_outport(struct coresight_device *csdev)
+static int coresight_find_link_outport(struct coresight_device *csdev,
+				       struct coresight_device *child)
 {
 	int i;
-	struct coresight_device *child;
 	struct coresight_connection *conn;
 
-	child = container_of(csdev->path_link.prev,
-			     struct coresight_device, path_link);
-
 	for (i = 0; i < csdev->nr_outport; i++) {
 		conn = &csdev->conns[i];
 		if (conn->child_dev == child)
@@ -138,14 +148,19 @@ static void coresight_disable_sink(struct coresight_device *csdev)
 	}
 }
 
-static int coresight_enable_link(struct coresight_device *csdev)
+static int coresight_enable_link(struct coresight_device *csdev,
+				 struct coresight_device *parent,
+				 struct coresight_device *child)
 {
 	int ret;
 	int link_subtype;
 	int refport, inport, outport;
 
-	inport = coresight_find_link_inport(csdev);
-	outport = coresight_find_link_outport(csdev);
+	if (!parent || !child)
+		return -EINVAL;
+
+	inport = coresight_find_link_inport(csdev, parent);
+	outport = coresight_find_link_outport(csdev, child);
 	link_subtype = csdev->subtype.link_subtype;
 
 	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
@@ -168,14 +183,19 @@ static int coresight_enable_link(struct coresight_device *csdev)
 	return 0;
 }
 
-static void coresight_disable_link(struct coresight_device *csdev)
+static void coresight_disable_link(struct coresight_device *csdev,
+				   struct coresight_device *parent,
+				   struct coresight_device *child)
 {
 	int i, nr_conns;
 	int link_subtype;
 	int refport, inport, outport;
 
-	inport = coresight_find_link_inport(csdev);
-	outport = coresight_find_link_outport(csdev);
+	if (!parent || !child)
+		return;
+
+	inport = coresight_find_link_inport(csdev, parent);
+	outport = coresight_find_link_outport(csdev, child);
 	link_subtype = csdev->subtype.link_subtype;
 
 	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
@@ -235,109 +255,167 @@ static void coresight_disable_source(struct coresight_device *csdev)
 	}
 }
 
-static int coresight_enable_path(struct list_head *path)
+void coresight_disable_path(struct list_head *path)
 {
-	int ret = 0;
-	struct coresight_device *cd;
+	struct coresight_node *nd;
+	struct coresight_device *csdev, *parent, *child;
 
-	/*
-	 * At this point we have a full @path, from source to sink.  The
-	 * sink is the first entry and the source the last one.  Go through
-	 * all the components and enable them one by one.
-	 */
-	list_for_each_entry(cd, path, path_link) {
-		if (cd == list_first_entry(path, struct coresight_device,
-					   path_link)) {
-			ret = coresight_enable_sink(cd);
-		} else if (list_is_last(&cd->path_link, path)) {
-			/*
-			 * Don't enable the source just yet - this needs to
-			 * happen at the very end when all links and sink
-			 * along the path have been configured properly.
-			 */
-			;
-		} else {
-			ret = coresight_enable_link(cd);
+	list_for_each_entry(nd, path, link) {
+		csdev = nd->csdev;
+
+		switch (csdev->type) {
+		case CORESIGHT_DEV_TYPE_SINK:
+		case CORESIGHT_DEV_TYPE_LINKSINK:
+			coresight_disable_sink(csdev);
+			break;
+		case CORESIGHT_DEV_TYPE_SOURCE:
+			/* sources are disabled from either sysFS or Perf */
+			break;
+		case CORESIGHT_DEV_TYPE_LINK:
+			parent = list_prev_entry(nd, link)->csdev;
+			child = list_next_entry(nd, link)->csdev;
+			coresight_disable_link(csdev, parent, child);
+			break;
+		default:
+			break;
 		}
-		if (ret)
-			goto err;
 	}
+}
 
-	return 0;
-err:
-	list_for_each_entry_continue_reverse(cd, path, path_link) {
-		if (cd == list_first_entry(path, struct coresight_device,
-					   path_link)) {
-			coresight_disable_sink(cd);
-		} else if (list_is_last(&cd->path_link, path)) {
-			;
-		} else {
-			coresight_disable_link(cd);
+int coresight_enable_path(struct list_head *path)
+{
+
+	int ret = 0;
+	struct coresight_node *nd;
+	struct coresight_device *csdev, *parent, *child;
+
+	list_for_each_entry_reverse(nd, path, link) {
+		csdev = nd->csdev;
+
+		switch (csdev->type) {
+		case CORESIGHT_DEV_TYPE_SINK:
+		case CORESIGHT_DEV_TYPE_LINKSINK:
+			ret = coresight_enable_sink(csdev);
+			if (ret)
+				goto err;
+			break;
+		case CORESIGHT_DEV_TYPE_SOURCE:
+			/* sources are enabled from either sysFS or Perf */
+			break;
+		case CORESIGHT_DEV_TYPE_LINK:
+			parent = list_prev_entry(nd, link)->csdev;
+			child = list_next_entry(nd, link)->csdev;
+			ret = coresight_enable_link(csdev, parent, child);
+			if (ret)
+				goto err;
+			break;
+		default:
+			goto err;
 		}
 	}
 
+out:
 	return ret;
+err:
+	coresight_disable_path(path);
+	goto out;
 }
 
-static int coresight_disable_path(struct list_head *path)
+/**
+ * _coresight_build_path - recursively build a path from a @csdev to a sink.
+ * @csdev:	The device to start from.
+ * @path:	The list to add devices to.
+ *
+ * The tree of Coresight device is traversed until an activated sink is
+ * found.  From there the sink is added to the list along with all the
+ * devices that led to that point - the end result is a list from source
+ * to sink. In that list the source is the first device and the sink the
+ * last one.
+ */
+static int _coresight_build_path(struct coresight_device *csdev,
+				 struct list_head *path)
 {
-	struct coresight_device *cd;
+	int i;
+	bool found = false;
+	struct coresight_node *node;
+	struct coresight_connection *conn;
 
-	list_for_each_entry_reverse(cd, path, path_link) {
-		if (cd == list_first_entry(path, struct coresight_device,
-					   path_link)) {
-			coresight_disable_sink(cd);
-		} else if (list_is_last(&cd->path_link, path)) {
-			/*
-			 * The source has already been stopped, no need
-			 * to do it again here.
-			 */
-			;
-		} else {
-			coresight_disable_link(cd);
+	/* An activated sink has been found.  Enqueue the element */
+	if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
+	     csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && csdev->activated)
+		goto out;
+
+	/* Not a sink - recursively explore each port found on this element */
+	for (i = 0; i < csdev->nr_outport; i++) {
+		conn = &csdev->conns[i];
+		if (_coresight_build_path(conn->child_dev, path) == 0) {
+			found = true;
+			break;
 		}
 	}
 
+	if (!found)
+		return -ENODEV;
+
+out:
+	/*
+	 * A path from this element to a sink has been found.  The elements
+	 * leading to the sink are already enqueued, all that is left to do
+	 * is add a node for this element.
+	 */
+	node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+
+	node->csdev = csdev;
+	list_add(&node->link, path);
+
 	return 0;
 }
 
-static int coresight_build_paths(struct coresight_device *csdev,
-				 struct list_head *path,
-				 bool enable)
+struct list_head *coresight_build_path(struct coresight_device *csdev)
 {
-	int i, ret = -EINVAL;
-	struct coresight_connection *conn;
+	struct list_head *path;
 
-	list_add(&csdev->path_link, path);
+	path = kzalloc(sizeof(struct list_head), GFP_KERNEL);
+	if (!path)
+		return NULL;
 
-	if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
-	    csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
-	    csdev->activated) {
-		if (enable)
-			ret = coresight_enable_path(path);
-		else
-			ret = coresight_disable_path(path);
-	} else {
-		for (i = 0; i < csdev->nr_outport; i++) {
-			conn = &csdev->conns[i];
-			if (coresight_build_paths(conn->child_dev,
-						    path, enable) == 0)
-				ret = 0;
-		}
+	INIT_LIST_HEAD(path);
+
+	if (_coresight_build_path(csdev, path)) {
+		kfree(path);
+		path = NULL;
 	}
 
-	if (list_first_entry(path, struct coresight_device, path_link) != csdev)
-		dev_err(&csdev->dev, "wrong device in %s\n", __func__);
+	return path;
+}
 
-	list_del(&csdev->path_link);
+/**
+ * coresight_release_path - release a previously built path.
+ * @path:	the path to release.
+ *
+ * Go through all the elements of a path and 1) removed it from the list and
+ * 2) free the memory allocated for each node.
+ */
+void coresight_release_path(struct list_head *path)
+{
+	struct coresight_node *nd, *next;
 
-	return ret;
+	list_for_each_entry_safe(nd, next, path, link) {
+		list_del(&nd->link);
+		kfree(nd);
+	}
+
+	kfree(path);
+	path = NULL;
 }
 
 int coresight_enable(struct coresight_device *csdev)
 {
 	int ret = 0;
-	LIST_HEAD(path);
+	int cpu;
+	struct list_head *path;
 
 	mutex_lock(&coresight_mutex);
 	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
@@ -348,22 +426,47 @@ int coresight_enable(struct coresight_device *csdev)
 	if (csdev->enable)
 		goto out;
 
-	if (coresight_build_paths(csdev, &path, true)) {
-		dev_err(&csdev->dev, "building path(s) failed\n");
+	path = coresight_build_path(csdev);
+	if (!path) {
+		pr_err("building path(s) failed\n");
 		goto out;
 	}
 
-	if (coresight_enable_source(csdev))
-		dev_err(&csdev->dev, "source enable failed\n");
+	ret = coresight_enable_path(path);
+	if (ret)
+		goto err_path;
+
+	ret = coresight_enable_source(csdev);
+	if (ret)
+		goto err_source;
+
+	/*
+	 * When working from sysFS it is important to keep track
+	 * of the paths that were created so that they can be
+	 * undone in 'coresight_disable()'.  Since there can only
+	 * be a single session per tracer (when working from sysFS)
+	 * a per-cpu variable will do just fine.
+	 */
+	cpu = source_ops(csdev)->cpu_id(csdev);
+	per_cpu(sysfs_path, cpu) = path;
+
 out:
 	mutex_unlock(&coresight_mutex);
 	return ret;
+
+err_source:
+	coresight_disable_path(path);
+
+err_path:
+	coresight_release_path(path);
+	goto out;
 }
 EXPORT_SYMBOL_GPL(coresight_enable);
 
 void coresight_disable(struct coresight_device *csdev)
 {
-	LIST_HEAD(path);
+	int cpu;
+	struct list_head *path;
 
 	mutex_lock(&coresight_mutex);
 	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
@@ -373,9 +476,12 @@ void coresight_disable(struct coresight_device *csdev)
 	if (!csdev->enable)
 		goto out;
 
+	cpu = source_ops(csdev)->cpu_id(csdev);
+	path = per_cpu(sysfs_path, cpu);
 	coresight_disable_source(csdev);
-	if (coresight_build_paths(csdev, &path, false))
-		dev_err(&csdev->dev, "releasing path(s) failed\n");
+	coresight_disable_path(path);
+	coresight_release_path(path);
+	per_cpu(sysfs_path, cpu) = NULL;
 
 out:
 	mutex_unlock(&coresight_mutex);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index bf62b265bf52..851ecb22397e 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -152,7 +152,6 @@ struct coresight_connection {
 		by @coresight_ops.
  * @dev:	The device entity associated to this component.
  * @refcnt:	keep track of what is in use.
- * @path_link:	link of current component into the path being enabled.
  * @orphan:	true if the component has connections that haven't been linked.
  * @enable:	'true' if component is currently part of an active path.
  * @activated:	'true' only if a _sink_ has been activated.  A sink can be
@@ -168,7 +167,6 @@ struct coresight_device {
 	const struct coresight_ops *ops;
 	struct device dev;
 	atomic_t *refcnt;
-	struct list_head path_link;
 	bool orphan;
 	bool enable;	/* true only if configured as part of a path */
 	bool activated;	/* true only if a sink is part of a path */
-- 
2.1.4

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

* [PATCH V8 02/23] coresight: add API to get sink from path
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:45   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Add an API allowing external code to quickly get a handle on the
sink within a path.  The sink is always last, but adding an API allows
to keep the path's node structure private and remove redundant checks.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-priv.h |  1 +
 drivers/hwtracing/coresight/coresight.c      | 15 +++++++++++++++
 2 files changed, 16 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 7b193a34d709..14f245a2018d 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -54,6 +54,7 @@ static inline void CS_UNLOCK(void __iomem *addr)
 
 void coresight_disable_path(struct list_head *path);
 int coresight_enable_path(struct list_head *path);
+struct coresight_device *coresight_get_sink(struct list_head *path);
 struct list_head *coresight_build_path(struct coresight_device *csdev);
 void coresight_release_path(struct list_head *path);
 
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index f26589effb70..2ed48545df26 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -321,6 +321,21 @@ err:
 	goto out;
 }
 
+struct coresight_device *coresight_get_sink(struct list_head *path)
+{
+	struct coresight_device *csdev;
+
+	if (!path)
+		return NULL;
+
+	csdev = list_last_entry(path, struct coresight_node, link)->csdev;
+	if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
+	    csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
+		return NULL;
+
+	return csdev;
+}
+
 /**
  * _coresight_build_path - recursively build a path from a @csdev to a sink.
  * @csdev:	The device to start from.
-- 
2.1.4

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

* [PATCH V8 02/23] coresight: add API to get sink from path
@ 2016-01-14 21:45   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel

Add an API allowing external code to quickly get a handle on the
sink within a path.  The sink is always last, but adding an API allows
to keep the path's node structure private and remove redundant checks.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-priv.h |  1 +
 drivers/hwtracing/coresight/coresight.c      | 15 +++++++++++++++
 2 files changed, 16 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 7b193a34d709..14f245a2018d 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -54,6 +54,7 @@ static inline void CS_UNLOCK(void __iomem *addr)
 
 void coresight_disable_path(struct list_head *path);
 int coresight_enable_path(struct list_head *path);
+struct coresight_device *coresight_get_sink(struct list_head *path);
 struct list_head *coresight_build_path(struct coresight_device *csdev);
 void coresight_release_path(struct list_head *path);
 
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index f26589effb70..2ed48545df26 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -321,6 +321,21 @@ err:
 	goto out;
 }
 
+struct coresight_device *coresight_get_sink(struct list_head *path)
+{
+	struct coresight_device *csdev;
+
+	if (!path)
+		return NULL;
+
+	csdev = list_last_entry(path, struct coresight_node, link)->csdev;
+	if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
+	    csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
+		return NULL;
+
+	return csdev;
+}
+
 /**
  * _coresight_build_path - recursively build a path from a @csdev to a sink.
  * @csdev:	The device to start from.
-- 
2.1.4

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

* [PATCH V8 03/23] coresight: moving PM runtime operations to core framework
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:45   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Moving PM runtime operations in Coresight devices enable() and
disable() API to the framework core when a path is setup.  That
way the runtime core doesn't have to be involved everytime a
path is enabled.  It also avoids calling runtime PM operations
in IRQ context.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c           | 4 ----
 drivers/hwtracing/coresight/coresight-etm3x.c           | 3 ---
 drivers/hwtracing/coresight/coresight-etm4x.c           | 6 ------
 drivers/hwtracing/coresight/coresight-funnel.c          | 2 --
 drivers/hwtracing/coresight/coresight-replicator-qcom.c | 4 ----
 drivers/hwtracing/coresight/coresight-replicator.c      | 2 --
 drivers/hwtracing/coresight/coresight-tmc.c             | 5 -----
 drivers/hwtracing/coresight/coresight-tpiu.c            | 2 --
 drivers/hwtracing/coresight/coresight.c                 | 9 ++++++++-
 9 files changed, 8 insertions(+), 29 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 77d0f9c1118d..1301edc44629 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -137,8 +137,6 @@ static int etb_enable(struct coresight_device *csdev)
 	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	unsigned long flags;
 
-	pm_runtime_get_sync(drvdata->dev);
-
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	etb_enable_hw(drvdata);
 	drvdata->enable = true;
@@ -247,8 +245,6 @@ static void etb_disable(struct coresight_device *csdev)
 	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
-	pm_runtime_put(drvdata->dev);
-
 	dev_info(drvdata->dev, "ETB disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 755f6f4d6d79..fae66cb45424 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -349,7 +349,6 @@ static int etm_enable(struct coresight_device *csdev)
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
 
-	pm_runtime_get_sync(csdev->dev.parent);
 	spin_lock(&drvdata->spinlock);
 
 	/*
@@ -373,7 +372,6 @@ static int etm_enable(struct coresight_device *csdev)
 	return 0;
 err:
 	spin_unlock(&drvdata->spinlock);
-	pm_runtime_put(csdev->dev.parent);
 	return ret;
 }
 
@@ -422,7 +420,6 @@ static void etm_disable(struct coresight_device *csdev)
 
 	spin_unlock(&drvdata->spinlock);
 	put_online_cpus();
-	pm_runtime_put(csdev->dev.parent);
 
 	dev_info(drvdata->dev, "ETM tracing disabled\n");
 }
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index a6707642bb23..1c6e32dd6e49 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -72,7 +72,6 @@ static int etm4_trace_id(struct coresight_device *csdev)
 	if (!drvdata->enable)
 		return drvdata->trcid;
 
-	pm_runtime_get_sync(drvdata->dev);
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
 	CS_UNLOCK(drvdata->base);
@@ -81,7 +80,6 @@ static int etm4_trace_id(struct coresight_device *csdev)
 	CS_LOCK(drvdata->base);
 
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
 
 	return trace_id;
 }
@@ -187,7 +185,6 @@ static int etm4_enable(struct coresight_device *csdev)
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
 
-	pm_runtime_get_sync(drvdata->dev);
 	spin_lock(&drvdata->spinlock);
 
 	/*
@@ -207,7 +204,6 @@ static int etm4_enable(struct coresight_device *csdev)
 	return 0;
 err:
 	spin_unlock(&drvdata->spinlock);
-	pm_runtime_put(drvdata->dev);
 	return ret;
 }
 
@@ -256,8 +252,6 @@ static void etm4_disable(struct coresight_device *csdev)
 	spin_unlock(&drvdata->spinlock);
 	put_online_cpus();
 
-	pm_runtime_put(drvdata->dev);
-
 	dev_info(drvdata->dev, "ETM tracing disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
index 2e36bde7fcb4..a47bba361833 100644
--- a/drivers/hwtracing/coresight/coresight-funnel.c
+++ b/drivers/hwtracing/coresight/coresight-funnel.c
@@ -69,7 +69,6 @@ static int funnel_enable(struct coresight_device *csdev, int inport,
 {
 	struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_get_sync(drvdata->dev);
 	funnel_enable_hw(drvdata, inport);
 
 	dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
@@ -95,7 +94,6 @@ static void funnel_disable(struct coresight_device *csdev, int inport,
 	struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
 	funnel_disable_hw(drvdata, inport);
-	pm_runtime_put(drvdata->dev);
 
 	dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport);
 }
diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
index 584059e9e866..8149087e8966 100644
--- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c
+++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
@@ -48,8 +48,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport,
 {
 	struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_get_sync(drvdata->dev);
-
 	CS_UNLOCK(drvdata->base);
 
 	/*
@@ -86,8 +84,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport,
 
 	CS_LOCK(drvdata->base);
 
-	pm_runtime_put(drvdata->dev);
-
 	dev_info(drvdata->dev, "REPLICATOR disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c
index 963ac197c253..a0fbb2e05389 100644
--- a/drivers/hwtracing/coresight/coresight-replicator.c
+++ b/drivers/hwtracing/coresight/coresight-replicator.c
@@ -41,7 +41,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport,
 {
 	struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_get_sync(drvdata->dev);
 	dev_info(drvdata->dev, "REPLICATOR enabled\n");
 	return 0;
 }
@@ -51,7 +50,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport,
 {
 	struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_put(drvdata->dev);
 	dev_info(drvdata->dev, "REPLICATOR disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index a57c7ec1661f..5e2a71767870 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -242,12 +242,9 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
 {
 	unsigned long flags;
 
-	pm_runtime_get_sync(drvdata->dev);
-
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
-		pm_runtime_put(drvdata->dev);
 		return -EBUSY;
 	}
 
@@ -381,8 +378,6 @@ out:
 	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
-	pm_runtime_put(drvdata->dev);
-
 	dev_info(drvdata->dev, "TMC disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index 7214efd10db5..e19b86e61c38 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -74,7 +74,6 @@ static int tpiu_enable(struct coresight_device *csdev)
 {
 	struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_get_sync(csdev->dev.parent);
 	tpiu_enable_hw(drvdata);
 
 	dev_info(drvdata->dev, "TPIU enabled\n");
@@ -98,7 +97,6 @@ static void tpiu_disable(struct coresight_device *csdev)
 	struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
 	tpiu_disable_hw(drvdata);
-	pm_runtime_put(csdev->dev.parent);
 
 	dev_info(drvdata->dev, "TPIU disabled\n");
 }
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 2ed48545df26..6b44928c1076 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -24,6 +24,7 @@
 #include <linux/coresight.h>
 #include <linux/of_platform.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 
 #include "coresight-priv.h"
 
@@ -376,7 +377,8 @@ out:
 	/*
 	 * A path from this element to a sink has been found.  The elements
 	 * leading to the sink are already enqueued, all that is left to do
-	 * is add a node for this element.
+	 * is tell the PM runtime core we need this element and add a node
+	 * for it.
 	 */
 	node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL);
 	if (!node)
@@ -384,6 +386,7 @@ out:
 
 	node->csdev = csdev;
 	list_add(&node->link, path);
+	pm_runtime_get_sync(csdev->dev.parent);
 
 	return 0;
 }
@@ -415,9 +418,13 @@ struct list_head *coresight_build_path(struct coresight_device *csdev)
  */
 void coresight_release_path(struct list_head *path)
 {
+	struct coresight_device *csdev;
 	struct coresight_node *nd, *next;
 
 	list_for_each_entry_safe(nd, next, path, link) {
+		csdev = nd->csdev;
+
+		pm_runtime_put_sync(csdev->dev.parent);
 		list_del(&nd->link);
 		kfree(nd);
 	}
-- 
2.1.4

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

* [PATCH V8 03/23] coresight: moving PM runtime operations to core framework
@ 2016-01-14 21:45   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel

Moving PM runtime operations in Coresight devices enable() and
disable() API to the framework core when a path is setup.  That
way the runtime core doesn't have to be involved everytime a
path is enabled.  It also avoids calling runtime PM operations
in IRQ context.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c           | 4 ----
 drivers/hwtracing/coresight/coresight-etm3x.c           | 3 ---
 drivers/hwtracing/coresight/coresight-etm4x.c           | 6 ------
 drivers/hwtracing/coresight/coresight-funnel.c          | 2 --
 drivers/hwtracing/coresight/coresight-replicator-qcom.c | 4 ----
 drivers/hwtracing/coresight/coresight-replicator.c      | 2 --
 drivers/hwtracing/coresight/coresight-tmc.c             | 5 -----
 drivers/hwtracing/coresight/coresight-tpiu.c            | 2 --
 drivers/hwtracing/coresight/coresight.c                 | 9 ++++++++-
 9 files changed, 8 insertions(+), 29 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 77d0f9c1118d..1301edc44629 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -137,8 +137,6 @@ static int etb_enable(struct coresight_device *csdev)
 	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	unsigned long flags;
 
-	pm_runtime_get_sync(drvdata->dev);
-
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	etb_enable_hw(drvdata);
 	drvdata->enable = true;
@@ -247,8 +245,6 @@ static void etb_disable(struct coresight_device *csdev)
 	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
-	pm_runtime_put(drvdata->dev);
-
 	dev_info(drvdata->dev, "ETB disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 755f6f4d6d79..fae66cb45424 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -349,7 +349,6 @@ static int etm_enable(struct coresight_device *csdev)
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
 
-	pm_runtime_get_sync(csdev->dev.parent);
 	spin_lock(&drvdata->spinlock);
 
 	/*
@@ -373,7 +372,6 @@ static int etm_enable(struct coresight_device *csdev)
 	return 0;
 err:
 	spin_unlock(&drvdata->spinlock);
-	pm_runtime_put(csdev->dev.parent);
 	return ret;
 }
 
@@ -422,7 +420,6 @@ static void etm_disable(struct coresight_device *csdev)
 
 	spin_unlock(&drvdata->spinlock);
 	put_online_cpus();
-	pm_runtime_put(csdev->dev.parent);
 
 	dev_info(drvdata->dev, "ETM tracing disabled\n");
 }
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index a6707642bb23..1c6e32dd6e49 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -72,7 +72,6 @@ static int etm4_trace_id(struct coresight_device *csdev)
 	if (!drvdata->enable)
 		return drvdata->trcid;
 
-	pm_runtime_get_sync(drvdata->dev);
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
 	CS_UNLOCK(drvdata->base);
@@ -81,7 +80,6 @@ static int etm4_trace_id(struct coresight_device *csdev)
 	CS_LOCK(drvdata->base);
 
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
 
 	return trace_id;
 }
@@ -187,7 +185,6 @@ static int etm4_enable(struct coresight_device *csdev)
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
 
-	pm_runtime_get_sync(drvdata->dev);
 	spin_lock(&drvdata->spinlock);
 
 	/*
@@ -207,7 +204,6 @@ static int etm4_enable(struct coresight_device *csdev)
 	return 0;
 err:
 	spin_unlock(&drvdata->spinlock);
-	pm_runtime_put(drvdata->dev);
 	return ret;
 }
 
@@ -256,8 +252,6 @@ static void etm4_disable(struct coresight_device *csdev)
 	spin_unlock(&drvdata->spinlock);
 	put_online_cpus();
 
-	pm_runtime_put(drvdata->dev);
-
 	dev_info(drvdata->dev, "ETM tracing disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
index 2e36bde7fcb4..a47bba361833 100644
--- a/drivers/hwtracing/coresight/coresight-funnel.c
+++ b/drivers/hwtracing/coresight/coresight-funnel.c
@@ -69,7 +69,6 @@ static int funnel_enable(struct coresight_device *csdev, int inport,
 {
 	struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_get_sync(drvdata->dev);
 	funnel_enable_hw(drvdata, inport);
 
 	dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
@@ -95,7 +94,6 @@ static void funnel_disable(struct coresight_device *csdev, int inport,
 	struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
 	funnel_disable_hw(drvdata, inport);
-	pm_runtime_put(drvdata->dev);
 
 	dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport);
 }
diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
index 584059e9e866..8149087e8966 100644
--- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c
+++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
@@ -48,8 +48,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport,
 {
 	struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_get_sync(drvdata->dev);
-
 	CS_UNLOCK(drvdata->base);
 
 	/*
@@ -86,8 +84,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport,
 
 	CS_LOCK(drvdata->base);
 
-	pm_runtime_put(drvdata->dev);
-
 	dev_info(drvdata->dev, "REPLICATOR disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c
index 963ac197c253..a0fbb2e05389 100644
--- a/drivers/hwtracing/coresight/coresight-replicator.c
+++ b/drivers/hwtracing/coresight/coresight-replicator.c
@@ -41,7 +41,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport,
 {
 	struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_get_sync(drvdata->dev);
 	dev_info(drvdata->dev, "REPLICATOR enabled\n");
 	return 0;
 }
@@ -51,7 +50,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport,
 {
 	struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_put(drvdata->dev);
 	dev_info(drvdata->dev, "REPLICATOR disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index a57c7ec1661f..5e2a71767870 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -242,12 +242,9 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
 {
 	unsigned long flags;
 
-	pm_runtime_get_sync(drvdata->dev);
-
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
-		pm_runtime_put(drvdata->dev);
 		return -EBUSY;
 	}
 
@@ -381,8 +378,6 @@ out:
 	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
-	pm_runtime_put(drvdata->dev);
-
 	dev_info(drvdata->dev, "TMC disabled\n");
 }
 
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index 7214efd10db5..e19b86e61c38 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -74,7 +74,6 @@ static int tpiu_enable(struct coresight_device *csdev)
 {
 	struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	pm_runtime_get_sync(csdev->dev.parent);
 	tpiu_enable_hw(drvdata);
 
 	dev_info(drvdata->dev, "TPIU enabled\n");
@@ -98,7 +97,6 @@ static void tpiu_disable(struct coresight_device *csdev)
 	struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
 	tpiu_disable_hw(drvdata);
-	pm_runtime_put(csdev->dev.parent);
 
 	dev_info(drvdata->dev, "TPIU disabled\n");
 }
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 2ed48545df26..6b44928c1076 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -24,6 +24,7 @@
 #include <linux/coresight.h>
 #include <linux/of_platform.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 
 #include "coresight-priv.h"
 
@@ -376,7 +377,8 @@ out:
 	/*
 	 * A path from this element to a sink has been found.  The elements
 	 * leading to the sink are already enqueued, all that is left to do
-	 * is add a node for this element.
+	 * is tell the PM runtime core we need this element and add a node
+	 * for it.
 	 */
 	node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL);
 	if (!node)
@@ -384,6 +386,7 @@ out:
 
 	node->csdev = csdev;
 	list_add(&node->link, path);
+	pm_runtime_get_sync(csdev->dev.parent);
 
 	return 0;
 }
@@ -415,9 +418,13 @@ struct list_head *coresight_build_path(struct coresight_device *csdev)
  */
 void coresight_release_path(struct list_head *path)
 {
+	struct coresight_device *csdev;
 	struct coresight_node *nd, *next;
 
 	list_for_each_entry_safe(nd, next, path, link) {
+		csdev = nd->csdev;
+
+		pm_runtime_put_sync(csdev->dev.parent);
 		list_del(&nd->link);
 		kfree(nd);
 	}
-- 
2.1.4

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

* [PATCH V8 04/23] coresight: etm3x: moving etm_readl/writel to header file
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:45   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Moving functions etm_readl/writel to file "coresight-etm.h"
so that the main ETM3x driver can be split in more than one
file.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h   | 29 +++++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm3x.c | 29 ---------------------------
 2 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index b4481eb29304..34f7db881fa7 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -251,4 +251,33 @@ enum etm_addr_type {
 	ETM_ADDR_TYPE_START,
 	ETM_ADDR_TYPE_STOP,
 };
+
+static inline void etm_writel(struct etm_drvdata *drvdata,
+			      u32 val, u32 off)
+{
+	if (drvdata->use_cp14) {
+		if (etm_writel_cp14(off, val)) {
+			dev_err(drvdata->dev,
+				"invalid CP14 access to ETM reg: %#x", off);
+		}
+	} else {
+		writel_relaxed(val, drvdata->base + off);
+	}
+}
+
+static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
+{
+	u32 val;
+
+	if (drvdata->use_cp14) {
+		if (etm_readl_cp14(off, &val)) {
+			dev_err(drvdata->dev,
+				"invalid CP14 access to ETM reg: %#x", off);
+		}
+	} else {
+		val = readl_relaxed(drvdata->base + off);
+	}
+
+	return val;
+}
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index fae66cb45424..3be1f14da44c 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -42,35 +42,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 static int etm_count;
 static struct etm_drvdata *etmdrvdata[NR_CPUS];
 
-static inline void etm_writel(struct etm_drvdata *drvdata,
-			      u32 val, u32 off)
-{
-	if (drvdata->use_cp14) {
-		if (etm_writel_cp14(off, val)) {
-			dev_err(drvdata->dev,
-				"invalid CP14 access to ETM reg: %#x", off);
-		}
-	} else {
-		writel_relaxed(val, drvdata->base + off);
-	}
-}
-
-static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
-{
-	u32 val;
-
-	if (drvdata->use_cp14) {
-		if (etm_readl_cp14(off, &val)) {
-			dev_err(drvdata->dev,
-				"invalid CP14 access to ETM reg: %#x", off);
-		}
-	} else {
-		val = readl_relaxed(drvdata->base + off);
-	}
-
-	return val;
-}
-
 /*
  * Memory mapped writes to clear os lock are not supported on some processors
  * and OS lock must be unlocked before any memory mapped access on such
-- 
2.1.4

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

* [PATCH V8 04/23] coresight: etm3x: moving etm_readl/writel to header file
@ 2016-01-14 21:45   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel

Moving functions etm_readl/writel to file "coresight-etm.h"
so that the main ETM3x driver can be split in more than one
file.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h   | 29 +++++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm3x.c | 29 ---------------------------
 2 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index b4481eb29304..34f7db881fa7 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -251,4 +251,33 @@ enum etm_addr_type {
 	ETM_ADDR_TYPE_START,
 	ETM_ADDR_TYPE_STOP,
 };
+
+static inline void etm_writel(struct etm_drvdata *drvdata,
+			      u32 val, u32 off)
+{
+	if (drvdata->use_cp14) {
+		if (etm_writel_cp14(off, val)) {
+			dev_err(drvdata->dev,
+				"invalid CP14 access to ETM reg: %#x", off);
+		}
+	} else {
+		writel_relaxed(val, drvdata->base + off);
+	}
+}
+
+static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
+{
+	u32 val;
+
+	if (drvdata->use_cp14) {
+		if (etm_readl_cp14(off, &val)) {
+			dev_err(drvdata->dev,
+				"invalid CP14 access to ETM reg: %#x", off);
+		}
+	} else {
+		val = readl_relaxed(drvdata->base + off);
+	}
+
+	return val;
+}
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index fae66cb45424..3be1f14da44c 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -42,35 +42,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 static int etm_count;
 static struct etm_drvdata *etmdrvdata[NR_CPUS];
 
-static inline void etm_writel(struct etm_drvdata *drvdata,
-			      u32 val, u32 off)
-{
-	if (drvdata->use_cp14) {
-		if (etm_writel_cp14(off, val)) {
-			dev_err(drvdata->dev,
-				"invalid CP14 access to ETM reg: %#x", off);
-		}
-	} else {
-		writel_relaxed(val, drvdata->base + off);
-	}
-}
-
-static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
-{
-	u32 val;
-
-	if (drvdata->use_cp14) {
-		if (etm_readl_cp14(off, &val)) {
-			dev_err(drvdata->dev,
-				"invalid CP14 access to ETM reg: %#x", off);
-		}
-	} else {
-		val = readl_relaxed(drvdata->base + off);
-	}
-
-	return val;
-}
-
 /*
  * Memory mapped writes to clear os lock are not supported on some processors
  * and OS lock must be unlocked before any memory mapped access on such
-- 
2.1.4

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

* [PATCH V8 05/23] coresight: etm3x: moving sysFS entries to dedicated file
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:45   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

SysFS entries are big enough to justify their own file.
As such moving all sysFS related declarations to a dedicated
location.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Makefile               |    3 +-
 drivers/hwtracing/coresight/coresight-etm.h        |    4 +
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    | 1218 +++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm3x.c      | 1234 +-------------------
 4 files changed, 1241 insertions(+), 1218 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 99f8e5f6256e..233d66cf22d3 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o
 obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
 obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
 					   coresight-replicator.o
-obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o
+obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \
+					coresight-etm3x-sysfs.o
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
 obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 34f7db881fa7..9a30aa392ed9 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -280,4 +280,8 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
 
 	return val;
 }
+
+extern const struct attribute_group *coresight_etm_groups[];
+int etm_get_trace_id(struct etm_drvdata *drvdata);
+void etm_set_default(struct etm_drvdata *drvdata);
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
new file mode 100644
index 000000000000..f409f5a88e95
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -0,0 +1,1218 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/sysfs.h>
+#include "coresight-etm.h"
+
+static ssize_t nr_addr_cmp_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->nr_addr_cmp;
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(nr_addr_cmp);
+
+static ssize_t nr_cntr_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->nr_cntr;
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(nr_cntr);
+
+static ssize_t nr_ctxid_cmp_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->nr_ctxid_cmp;
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(nr_ctxid_cmp);
+
+static ssize_t etmsr_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	unsigned long flags, val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	pm_runtime_get_sync(drvdata->dev);
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	CS_UNLOCK(drvdata->base);
+
+	val = etm_readl(drvdata, ETMSR);
+
+	CS_LOCK(drvdata->base);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	pm_runtime_put(drvdata->dev);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(etmsr);
+
+static ssize_t reset_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t size)
+{
+	int i, ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val) {
+		spin_lock(&drvdata->spinlock);
+		drvdata->mode = ETM_MODE_EXCLUDE;
+		drvdata->ctrl = 0x0;
+		drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
+		drvdata->startstop_ctrl = 0x0;
+		drvdata->addr_idx = 0x0;
+		for (i = 0; i < drvdata->nr_addr_cmp; i++) {
+			drvdata->addr_val[i] = 0x0;
+			drvdata->addr_acctype[i] = 0x0;
+			drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
+		}
+		drvdata->cntr_idx = 0x0;
+
+		etm_set_default(drvdata);
+		spin_unlock(&drvdata->spinlock);
+	}
+
+	return size;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t mode_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->mode;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t mode_store(struct device *dev,
+			  struct device_attribute *attr,
+			  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->mode = val & ETM_MODE_ALL;
+
+	if (drvdata->mode & ETM_MODE_EXCLUDE)
+		drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC;
+	else
+		drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
+
+	if (drvdata->mode & ETM_MODE_CYCACC)
+		drvdata->ctrl |= ETMCR_CYC_ACC;
+	else
+		drvdata->ctrl &= ~ETMCR_CYC_ACC;
+
+	if (drvdata->mode & ETM_MODE_STALL) {
+		if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
+			dev_warn(drvdata->dev, "stall mode not supported\n");
+			ret = -EINVAL;
+			goto err_unlock;
+		}
+		drvdata->ctrl |= ETMCR_STALL_MODE;
+	 } else
+		drvdata->ctrl &= ~ETMCR_STALL_MODE;
+
+	if (drvdata->mode & ETM_MODE_TIMESTAMP) {
+		if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
+			dev_warn(drvdata->dev, "timestamp not supported\n");
+			ret = -EINVAL;
+			goto err_unlock;
+		}
+		drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
+	} else
+		drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN;
+
+	if (drvdata->mode & ETM_MODE_CTXID)
+		drvdata->ctrl |= ETMCR_CTXID_SIZE;
+	else
+		drvdata->ctrl &= ~ETMCR_CTXID_SIZE;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+
+err_unlock:
+	spin_unlock(&drvdata->spinlock);
+	return ret;
+}
+static DEVICE_ATTR_RW(mode);
+
+static ssize_t trigger_event_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->trigger_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t trigger_event_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->trigger_event = val & ETM_EVENT_MASK;
+
+	return size;
+}
+static DEVICE_ATTR_RW(trigger_event);
+
+static ssize_t enable_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->enable_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t enable_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->enable_event = val & ETM_EVENT_MASK;
+
+	return size;
+}
+static DEVICE_ATTR_RW(enable_event);
+
+static ssize_t fifofull_level_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->fifofull_level;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t fifofull_level_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->fifofull_level = val;
+
+	return size;
+}
+static DEVICE_ATTR_RW(fifofull_level);
+
+static ssize_t addr_idx_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->addr_idx;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_idx_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val >= drvdata->nr_addr_cmp)
+		return -EINVAL;
+
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
+	 */
+	spin_lock(&drvdata->spinlock);
+	drvdata->addr_idx = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_idx);
+
+static ssize_t addr_single_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EINVAL;
+	}
+
+	val = drvdata->addr_val[idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_single_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t size)
+{
+	u8 idx;
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EINVAL;
+	}
+
+	drvdata->addr_val[idx] = val;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_single);
+
+static ssize_t addr_range_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val1, val2;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (idx % 2 != 0) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	val1 = drvdata->addr_val[idx];
+	val2 = drvdata->addr_val[idx + 1];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx %#lx\n", val1, val2);
+}
+
+static ssize_t addr_range_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	u8 idx;
+	unsigned long val1, val2;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
+		return -EINVAL;
+	/* Lower address comparator cannot have a higher address value */
+	if (val1 > val2)
+		return -EINVAL;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (idx % 2 != 0) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	drvdata->addr_val[idx] = val1;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
+	drvdata->addr_val[idx + 1] = val2;
+	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
+	drvdata->enable_ctrl1 |= (1 << (idx/2));
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_range);
+
+static ssize_t addr_start_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	val = drvdata->addr_val[idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_start_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	u8 idx;
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	drvdata->addr_val[idx] = val;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
+	drvdata->startstop_ctrl |= (1 << idx);
+	drvdata->enable_ctrl1 |= BIT(25);
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_start);
+
+static ssize_t addr_stop_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	val = drvdata->addr_val[idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_stop_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	u8 idx;
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	drvdata->addr_val[idx] = val;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
+	drvdata->startstop_ctrl |= (1 << (idx + 16));
+	drvdata->enable_ctrl1 |= ETMTECR1_START_STOP;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_stop);
+
+static ssize_t addr_acctype_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->addr_acctype[drvdata->addr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_acctype_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->addr_acctype[drvdata->addr_idx] = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_acctype);
+
+static ssize_t cntr_idx_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->cntr_idx;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_idx_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val >= drvdata->nr_cntr)
+		return -EINVAL;
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
+	 */
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_idx = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_idx);
+
+static ssize_t cntr_rld_val_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_rld_val_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_rld_val);
+
+static ssize_t cntr_event_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->cntr_event[drvdata->cntr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_event_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_event);
+
+static ssize_t cntr_rld_event_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_rld_event_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_rld_event);
+
+static ssize_t cntr_val_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	int i, ret = 0;
+	u32 val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!drvdata->enable) {
+		spin_lock(&drvdata->spinlock);
+		for (i = 0; i < drvdata->nr_cntr; i++)
+			ret += sprintf(buf, "counter %d: %x\n",
+				       i, drvdata->cntr_val[i]);
+		spin_unlock(&drvdata->spinlock);
+		return ret;
+	}
+
+	for (i = 0; i < drvdata->nr_cntr; i++) {
+		val = etm_readl(drvdata, ETMCNTVRn(i));
+		ret += sprintf(buf, "counter %d: %x\n", i, val);
+	}
+
+	return ret;
+}
+
+static ssize_t cntr_val_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_val[drvdata->cntr_idx] = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_val);
+
+static ssize_t seq_12_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_12_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_12_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_12_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_12_event);
+
+static ssize_t seq_21_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_21_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_21_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_21_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_21_event);
+
+static ssize_t seq_23_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_23_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_23_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_23_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_23_event);
+
+static ssize_t seq_31_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_31_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_31_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_31_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_31_event);
+
+static ssize_t seq_32_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_32_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_32_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_32_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_32_event);
+
+static ssize_t seq_13_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_13_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_13_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_13_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_13_event);
+
+static ssize_t seq_curr_state_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	unsigned long val, flags;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!drvdata->enable) {
+		val = drvdata->seq_curr_state;
+		goto out;
+	}
+
+	pm_runtime_get_sync(drvdata->dev);
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	CS_UNLOCK(drvdata->base);
+	val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
+	CS_LOCK(drvdata->base);
+
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	pm_runtime_put(drvdata->dev);
+out:
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_curr_state_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val > ETM_SEQ_STATE_MAX_VAL)
+		return -EINVAL;
+
+	drvdata->seq_curr_state = val;
+
+	return size;
+}
+static DEVICE_ATTR_RW(seq_curr_state);
+
+static ssize_t ctxid_idx_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->ctxid_idx;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t ctxid_idx_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val >= drvdata->nr_ctxid_cmp)
+		return -EINVAL;
+
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
+	 */
+	spin_lock(&drvdata->spinlock);
+	drvdata->ctxid_idx = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(ctxid_idx);
+
+static ssize_t ctxid_pid_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->ctxid_vpid[drvdata->ctxid_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t ctxid_pid_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	int ret;
+	unsigned long vpid, pid;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &vpid);
+	if (ret)
+		return ret;
+
+	pid = coresight_vpid_to_pid(vpid);
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->ctxid_pid[drvdata->ctxid_idx] = pid;
+	drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(ctxid_pid);
+
+static ssize_t ctxid_mask_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->ctxid_mask;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t ctxid_mask_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->ctxid_mask = val;
+	return size;
+}
+static DEVICE_ATTR_RW(ctxid_mask);
+
+static ssize_t sync_freq_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->sync_freq;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t sync_freq_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->sync_freq = val & ETM_SYNC_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(sync_freq);
+
+static ssize_t timestamp_event_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->timestamp_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t timestamp_event_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->timestamp_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(timestamp_event);
+
+static ssize_t cpu_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->cpu;
+	return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+
+}
+static DEVICE_ATTR_RO(cpu);
+
+static ssize_t traceid_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = etm_get_trace_id(drvdata);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t traceid_store(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->traceid = val & ETM_TRACEID_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(traceid);
+
+static struct attribute *coresight_etm_attrs[] = {
+	&dev_attr_nr_addr_cmp.attr,
+	&dev_attr_nr_cntr.attr,
+	&dev_attr_nr_ctxid_cmp.attr,
+	&dev_attr_etmsr.attr,
+	&dev_attr_reset.attr,
+	&dev_attr_mode.attr,
+	&dev_attr_trigger_event.attr,
+	&dev_attr_enable_event.attr,
+	&dev_attr_fifofull_level.attr,
+	&dev_attr_addr_idx.attr,
+	&dev_attr_addr_single.attr,
+	&dev_attr_addr_range.attr,
+	&dev_attr_addr_start.attr,
+	&dev_attr_addr_stop.attr,
+	&dev_attr_addr_acctype.attr,
+	&dev_attr_cntr_idx.attr,
+	&dev_attr_cntr_rld_val.attr,
+	&dev_attr_cntr_event.attr,
+	&dev_attr_cntr_rld_event.attr,
+	&dev_attr_cntr_val.attr,
+	&dev_attr_seq_12_event.attr,
+	&dev_attr_seq_21_event.attr,
+	&dev_attr_seq_23_event.attr,
+	&dev_attr_seq_31_event.attr,
+	&dev_attr_seq_32_event.attr,
+	&dev_attr_seq_13_event.attr,
+	&dev_attr_seq_curr_state.attr,
+	&dev_attr_ctxid_idx.attr,
+	&dev_attr_ctxid_pid.attr,
+	&dev_attr_ctxid_mask.attr,
+	&dev_attr_sync_freq.attr,
+	&dev_attr_timestamp_event.attr,
+	&dev_attr_traceid.attr,
+	&dev_attr_cpu.attr,
+	NULL,
+};
+
+#define coresight_simple_func(name, offset)                             \
+static ssize_t name##_show(struct device *_dev,                         \
+			   struct device_attribute *attr, char *buf)    \
+{                                                                       \
+	struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent);    \
+	return scnprintf(buf, PAGE_SIZE, "0x%x\n",                      \
+			 readl_relaxed(drvdata->base + offset));        \
+}                                                                       \
+DEVICE_ATTR_RO(name)
+
+coresight_simple_func(etmccr, ETMCCR);
+coresight_simple_func(etmccer, ETMCCER);
+coresight_simple_func(etmscr, ETMSCR);
+coresight_simple_func(etmidr, ETMIDR);
+coresight_simple_func(etmcr, ETMCR);
+coresight_simple_func(etmtraceidr, ETMTRACEIDR);
+coresight_simple_func(etmteevr, ETMTEEVR);
+coresight_simple_func(etmtssvr, ETMTSSCR);
+coresight_simple_func(etmtecr1, ETMTECR1);
+coresight_simple_func(etmtecr2, ETMTECR2);
+
+static struct attribute *coresight_etm_mgmt_attrs[] = {
+	&dev_attr_etmccr.attr,
+	&dev_attr_etmccer.attr,
+	&dev_attr_etmscr.attr,
+	&dev_attr_etmidr.attr,
+	&dev_attr_etmcr.attr,
+	&dev_attr_etmtraceidr.attr,
+	&dev_attr_etmteevr.attr,
+	&dev_attr_etmtssvr.attr,
+	&dev_attr_etmtecr1.attr,
+	&dev_attr_etmtecr2.attr,
+	NULL,
+};
+
+static const struct attribute_group coresight_etm_group = {
+	.attrs = coresight_etm_attrs,
+};
+
+static const struct attribute_group coresight_etm_mgmt_group = {
+	.attrs = coresight_etm_mgmt_attrs,
+	.name = "mgmt",
+};
+
+const struct attribute_group *coresight_etm_groups[] = {
+	&coresight_etm_group,
+	&coresight_etm_mgmt_group,
+	NULL,
+};
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 3be1f14da44c..042b88ecae92 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -186,7 +186,7 @@ static void etm_clr_prog(struct etm_drvdata *drvdata)
 	}
 }
 
-static void etm_set_default(struct etm_drvdata *drvdata)
+void etm_set_default(struct etm_drvdata *drvdata)
 {
 	int i;
 
@@ -293,15 +293,18 @@ static int etm_cpu_id(struct coresight_device *csdev)
 	return drvdata->cpu;
 }
 
-static int etm_trace_id(struct coresight_device *csdev)
+int etm_get_trace_id(struct etm_drvdata *drvdata)
 {
-	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	unsigned long flags;
 	int trace_id = -1;
 
+	if (!drvdata)
+		goto out;
+
 	if (!drvdata->enable)
 		return drvdata->traceid;
-	pm_runtime_get_sync(csdev->dev.parent);
+
+	pm_runtime_get_sync(drvdata->dev);
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
@@ -310,9 +313,18 @@ static int etm_trace_id(struct coresight_device *csdev)
 	CS_LOCK(drvdata->base);
 
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(csdev->dev.parent);
+	pm_runtime_put(drvdata->dev);
 
+out:
 	return trace_id;
+
+}
+
+static int etm_trace_id(struct coresight_device *csdev)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	return etm_get_trace_id(drvdata);
 }
 
 static int etm_enable(struct coresight_device *csdev)
@@ -406,1218 +418,6 @@ static const struct coresight_ops etm_cs_ops = {
 	.source_ops	= &etm_source_ops,
 };
 
-static ssize_t nr_addr_cmp_show(struct device *dev,
-				struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->nr_addr_cmp;
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(nr_addr_cmp);
-
-static ssize_t nr_cntr_show(struct device *dev,
-			    struct device_attribute *attr, char *buf)
-{	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->nr_cntr;
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(nr_cntr);
-
-static ssize_t nr_ctxid_cmp_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->nr_ctxid_cmp;
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(nr_ctxid_cmp);
-
-static ssize_t etmsr_show(struct device *dev,
-			  struct device_attribute *attr, char *buf)
-{
-	unsigned long flags, val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	pm_runtime_get_sync(drvdata->dev);
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	CS_UNLOCK(drvdata->base);
-
-	val = etm_readl(drvdata, ETMSR);
-
-	CS_LOCK(drvdata->base);
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(etmsr);
-
-static ssize_t reset_store(struct device *dev,
-			   struct device_attribute *attr,
-			   const char *buf, size_t size)
-{
-	int i, ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val) {
-		spin_lock(&drvdata->spinlock);
-		drvdata->mode = ETM_MODE_EXCLUDE;
-		drvdata->ctrl = 0x0;
-		drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
-		drvdata->startstop_ctrl = 0x0;
-		drvdata->addr_idx = 0x0;
-		for (i = 0; i < drvdata->nr_addr_cmp; i++) {
-			drvdata->addr_val[i] = 0x0;
-			drvdata->addr_acctype[i] = 0x0;
-			drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
-		}
-		drvdata->cntr_idx = 0x0;
-
-		etm_set_default(drvdata);
-		spin_unlock(&drvdata->spinlock);
-	}
-
-	return size;
-}
-static DEVICE_ATTR_WO(reset);
-
-static ssize_t mode_show(struct device *dev,
-			 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->mode;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t mode_store(struct device *dev,
-			  struct device_attribute *attr,
-			  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->mode = val & ETM_MODE_ALL;
-
-	if (drvdata->mode & ETM_MODE_EXCLUDE)
-		drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC;
-	else
-		drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
-
-	if (drvdata->mode & ETM_MODE_CYCACC)
-		drvdata->ctrl |= ETMCR_CYC_ACC;
-	else
-		drvdata->ctrl &= ~ETMCR_CYC_ACC;
-
-	if (drvdata->mode & ETM_MODE_STALL) {
-		if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
-			dev_warn(drvdata->dev, "stall mode not supported\n");
-			ret = -EINVAL;
-			goto err_unlock;
-		}
-		drvdata->ctrl |= ETMCR_STALL_MODE;
-	 } else
-		drvdata->ctrl &= ~ETMCR_STALL_MODE;
-
-	if (drvdata->mode & ETM_MODE_TIMESTAMP) {
-		if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
-			dev_warn(drvdata->dev, "timestamp not supported\n");
-			ret = -EINVAL;
-			goto err_unlock;
-		}
-		drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
-	} else
-		drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN;
-
-	if (drvdata->mode & ETM_MODE_CTXID)
-		drvdata->ctrl |= ETMCR_CTXID_SIZE;
-	else
-		drvdata->ctrl &= ~ETMCR_CTXID_SIZE;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-
-err_unlock:
-	spin_unlock(&drvdata->spinlock);
-	return ret;
-}
-static DEVICE_ATTR_RW(mode);
-
-static ssize_t trigger_event_show(struct device *dev,
-				  struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->trigger_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t trigger_event_store(struct device *dev,
-				   struct device_attribute *attr,
-				   const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->trigger_event = val & ETM_EVENT_MASK;
-
-	return size;
-}
-static DEVICE_ATTR_RW(trigger_event);
-
-static ssize_t enable_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->enable_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t enable_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->enable_event = val & ETM_EVENT_MASK;
-
-	return size;
-}
-static DEVICE_ATTR_RW(enable_event);
-
-static ssize_t fifofull_level_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->fifofull_level;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t fifofull_level_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->fifofull_level = val;
-
-	return size;
-}
-static DEVICE_ATTR_RW(fifofull_level);
-
-static ssize_t addr_idx_show(struct device *dev,
-			     struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->addr_idx;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_idx_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val >= drvdata->nr_addr_cmp)
-		return -EINVAL;
-
-	/*
-	 * Use spinlock to ensure index doesn't change while it gets
-	 * dereferenced multiple times within a spinlock block elsewhere.
-	 */
-	spin_lock(&drvdata->spinlock);
-	drvdata->addr_idx = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_idx);
-
-static ssize_t addr_single_show(struct device *dev,
-				struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EINVAL;
-	}
-
-	val = drvdata->addr_val[idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_single_store(struct device *dev,
-				 struct device_attribute *attr,
-				 const char *buf, size_t size)
-{
-	u8 idx;
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EINVAL;
-	}
-
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_single);
-
-static ssize_t addr_range_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val1, val2;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (idx % 2 != 0) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	val1 = drvdata->addr_val[idx];
-	val2 = drvdata->addr_val[idx + 1];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx %#lx\n", val1, val2);
-}
-
-static ssize_t addr_range_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	u8 idx;
-	unsigned long val1, val2;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
-		return -EINVAL;
-	/* Lower address comparator cannot have a higher address value */
-	if (val1 > val2)
-		return -EINVAL;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (idx % 2 != 0) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	drvdata->addr_val[idx] = val1;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
-	drvdata->addr_val[idx + 1] = val2;
-	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
-	drvdata->enable_ctrl1 |= (1 << (idx/2));
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_range);
-
-static ssize_t addr_start_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	val = drvdata->addr_val[idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_start_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	u8 idx;
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
-	drvdata->startstop_ctrl |= (1 << idx);
-	drvdata->enable_ctrl1 |= BIT(25);
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_start);
-
-static ssize_t addr_stop_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	val = drvdata->addr_val[idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_stop_store(struct device *dev,
-			       struct device_attribute *attr,
-			       const char *buf, size_t size)
-{
-	u8 idx;
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
-	drvdata->startstop_ctrl |= (1 << (idx + 16));
-	drvdata->enable_ctrl1 |= ETMTECR1_START_STOP;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_stop);
-
-static ssize_t addr_acctype_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->addr_acctype[drvdata->addr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_acctype_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->addr_acctype[drvdata->addr_idx] = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_acctype);
-
-static ssize_t cntr_idx_show(struct device *dev,
-			     struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->cntr_idx;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_idx_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val >= drvdata->nr_cntr)
-		return -EINVAL;
-	/*
-	 * Use spinlock to ensure index doesn't change while it gets
-	 * dereferenced multiple times within a spinlock block elsewhere.
-	 */
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_idx = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_idx);
-
-static ssize_t cntr_rld_val_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_rld_val_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_rld_val);
-
-static ssize_t cntr_event_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_event[drvdata->cntr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_event_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_event);
-
-static ssize_t cntr_rld_event_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_rld_event_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_rld_event);
-
-static ssize_t cntr_val_show(struct device *dev,
-			     struct device_attribute *attr, char *buf)
-{
-	int i, ret = 0;
-	u32 val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (!drvdata->enable) {
-		spin_lock(&drvdata->spinlock);
-		for (i = 0; i < drvdata->nr_cntr; i++)
-			ret += sprintf(buf, "counter %d: %x\n",
-				       i, drvdata->cntr_val[i]);
-		spin_unlock(&drvdata->spinlock);
-		return ret;
-	}
-
-	for (i = 0; i < drvdata->nr_cntr; i++) {
-		val = etm_readl(drvdata, ETMCNTVRn(i));
-		ret += sprintf(buf, "counter %d: %x\n", i, val);
-	}
-
-	return ret;
-}
-
-static ssize_t cntr_val_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_val[drvdata->cntr_idx] = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_val);
-
-static ssize_t seq_12_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_12_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_12_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_12_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_12_event);
-
-static ssize_t seq_21_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_21_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_21_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_21_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_21_event);
-
-static ssize_t seq_23_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_23_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_23_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_23_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_23_event);
-
-static ssize_t seq_31_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_31_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_31_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_31_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_31_event);
-
-static ssize_t seq_32_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_32_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_32_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_32_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_32_event);
-
-static ssize_t seq_13_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_13_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_13_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_13_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_13_event);
-
-static ssize_t seq_curr_state_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	unsigned long val, flags;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (!drvdata->enable) {
-		val = drvdata->seq_curr_state;
-		goto out;
-	}
-
-	pm_runtime_get_sync(drvdata->dev);
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-
-	CS_UNLOCK(drvdata->base);
-	val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
-	CS_LOCK(drvdata->base);
-
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
-out:
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_curr_state_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val > ETM_SEQ_STATE_MAX_VAL)
-		return -EINVAL;
-
-	drvdata->seq_curr_state = val;
-
-	return size;
-}
-static DEVICE_ATTR_RW(seq_curr_state);
-
-static ssize_t ctxid_idx_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->ctxid_idx;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t ctxid_idx_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val >= drvdata->nr_ctxid_cmp)
-		return -EINVAL;
-
-	/*
-	 * Use spinlock to ensure index doesn't change while it gets
-	 * dereferenced multiple times within a spinlock block elsewhere.
-	 */
-	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_idx = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(ctxid_idx);
-
-static ssize_t ctxid_pid_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->ctxid_vpid[drvdata->ctxid_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t ctxid_pid_store(struct device *dev,
-			       struct device_attribute *attr,
-			       const char *buf, size_t size)
-{
-	int ret;
-	unsigned long vpid, pid;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &vpid);
-	if (ret)
-		return ret;
-
-	pid = coresight_vpid_to_pid(vpid);
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_pid[drvdata->ctxid_idx] = pid;
-	drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(ctxid_pid);
-
-static ssize_t ctxid_mask_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->ctxid_mask;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t ctxid_mask_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->ctxid_mask = val;
-	return size;
-}
-static DEVICE_ATTR_RW(ctxid_mask);
-
-static ssize_t sync_freq_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->sync_freq;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t sync_freq_store(struct device *dev,
-			       struct device_attribute *attr,
-			       const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->sync_freq = val & ETM_SYNC_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(sync_freq);
-
-static ssize_t timestamp_event_show(struct device *dev,
-				    struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->timestamp_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t timestamp_event_store(struct device *dev,
-				     struct device_attribute *attr,
-				     const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->timestamp_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(timestamp_event);
-
-static ssize_t cpu_show(struct device *dev,
-			struct device_attribute *attr, char *buf)
-{
-	int val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->cpu;
-	return scnprintf(buf, PAGE_SIZE, "%d\n", val);
-
-}
-static DEVICE_ATTR_RO(cpu);
-
-static ssize_t traceid_show(struct device *dev,
-			    struct device_attribute *attr, char *buf)
-{
-	unsigned long val, flags;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (!drvdata->enable) {
-		val = drvdata->traceid;
-		goto out;
-	}
-
-	pm_runtime_get_sync(drvdata->dev);
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	CS_UNLOCK(drvdata->base);
-
-	val = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK);
-
-	CS_LOCK(drvdata->base);
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
-out:
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t traceid_store(struct device *dev,
-			     struct device_attribute *attr,
-			     const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->traceid = val & ETM_TRACEID_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(traceid);
-
-static struct attribute *coresight_etm_attrs[] = {
-	&dev_attr_nr_addr_cmp.attr,
-	&dev_attr_nr_cntr.attr,
-	&dev_attr_nr_ctxid_cmp.attr,
-	&dev_attr_etmsr.attr,
-	&dev_attr_reset.attr,
-	&dev_attr_mode.attr,
-	&dev_attr_trigger_event.attr,
-	&dev_attr_enable_event.attr,
-	&dev_attr_fifofull_level.attr,
-	&dev_attr_addr_idx.attr,
-	&dev_attr_addr_single.attr,
-	&dev_attr_addr_range.attr,
-	&dev_attr_addr_start.attr,
-	&dev_attr_addr_stop.attr,
-	&dev_attr_addr_acctype.attr,
-	&dev_attr_cntr_idx.attr,
-	&dev_attr_cntr_rld_val.attr,
-	&dev_attr_cntr_event.attr,
-	&dev_attr_cntr_rld_event.attr,
-	&dev_attr_cntr_val.attr,
-	&dev_attr_seq_12_event.attr,
-	&dev_attr_seq_21_event.attr,
-	&dev_attr_seq_23_event.attr,
-	&dev_attr_seq_31_event.attr,
-	&dev_attr_seq_32_event.attr,
-	&dev_attr_seq_13_event.attr,
-	&dev_attr_seq_curr_state.attr,
-	&dev_attr_ctxid_idx.attr,
-	&dev_attr_ctxid_pid.attr,
-	&dev_attr_ctxid_mask.attr,
-	&dev_attr_sync_freq.attr,
-	&dev_attr_timestamp_event.attr,
-	&dev_attr_traceid.attr,
-	&dev_attr_cpu.attr,
-	NULL,
-};
-
-#define coresight_simple_func(name, offset)                             \
-static ssize_t name##_show(struct device *_dev,                         \
-			   struct device_attribute *attr, char *buf)    \
-{                                                                       \
-	struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent);    \
-	return scnprintf(buf, PAGE_SIZE, "0x%x\n",                      \
-			 readl_relaxed(drvdata->base + offset));        \
-}                                                                       \
-DEVICE_ATTR_RO(name)
-
-coresight_simple_func(etmccr, ETMCCR);
-coresight_simple_func(etmccer, ETMCCER);
-coresight_simple_func(etmscr, ETMSCR);
-coresight_simple_func(etmidr, ETMIDR);
-coresight_simple_func(etmcr, ETMCR);
-coresight_simple_func(etmtraceidr, ETMTRACEIDR);
-coresight_simple_func(etmteevr, ETMTEEVR);
-coresight_simple_func(etmtssvr, ETMTSSCR);
-coresight_simple_func(etmtecr1, ETMTECR1);
-coresight_simple_func(etmtecr2, ETMTECR2);
-
-static struct attribute *coresight_etm_mgmt_attrs[] = {
-	&dev_attr_etmccr.attr,
-	&dev_attr_etmccer.attr,
-	&dev_attr_etmscr.attr,
-	&dev_attr_etmidr.attr,
-	&dev_attr_etmcr.attr,
-	&dev_attr_etmtraceidr.attr,
-	&dev_attr_etmteevr.attr,
-	&dev_attr_etmtssvr.attr,
-	&dev_attr_etmtecr1.attr,
-	&dev_attr_etmtecr2.attr,
-	NULL,
-};
-
-static const struct attribute_group coresight_etm_group = {
-	.attrs = coresight_etm_attrs,
-};
-
-
-static const struct attribute_group coresight_etm_mgmt_group = {
-	.attrs = coresight_etm_mgmt_attrs,
-	.name = "mgmt",
-};
-
-static const struct attribute_group *coresight_etm_groups[] = {
-	&coresight_etm_group,
-	&coresight_etm_mgmt_group,
-	NULL,
-};
-
 static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 			    void *hcpu)
 {
-- 
2.1.4

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

* [PATCH V8 05/23] coresight: etm3x: moving sysFS entries to dedicated file
@ 2016-01-14 21:45   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:45 UTC (permalink / raw)
  To: linux-arm-kernel

SysFS entries are big enough to justify their own file.
As such moving all sysFS related declarations to a dedicated
location.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Makefile               |    3 +-
 drivers/hwtracing/coresight/coresight-etm.h        |    4 +
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    | 1218 +++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm3x.c      | 1234 +-------------------
 4 files changed, 1241 insertions(+), 1218 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 99f8e5f6256e..233d66cf22d3 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o
 obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
 obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
 					   coresight-replicator.o
-obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o
+obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \
+					coresight-etm3x-sysfs.o
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
 obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 34f7db881fa7..9a30aa392ed9 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -280,4 +280,8 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
 
 	return val;
 }
+
+extern const struct attribute_group *coresight_etm_groups[];
+int etm_get_trace_id(struct etm_drvdata *drvdata);
+void etm_set_default(struct etm_drvdata *drvdata);
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
new file mode 100644
index 000000000000..f409f5a88e95
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -0,0 +1,1218 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/sysfs.h>
+#include "coresight-etm.h"
+
+static ssize_t nr_addr_cmp_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->nr_addr_cmp;
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(nr_addr_cmp);
+
+static ssize_t nr_cntr_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->nr_cntr;
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(nr_cntr);
+
+static ssize_t nr_ctxid_cmp_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->nr_ctxid_cmp;
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(nr_ctxid_cmp);
+
+static ssize_t etmsr_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	unsigned long flags, val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	pm_runtime_get_sync(drvdata->dev);
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	CS_UNLOCK(drvdata->base);
+
+	val = etm_readl(drvdata, ETMSR);
+
+	CS_LOCK(drvdata->base);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	pm_runtime_put(drvdata->dev);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(etmsr);
+
+static ssize_t reset_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t size)
+{
+	int i, ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val) {
+		spin_lock(&drvdata->spinlock);
+		drvdata->mode = ETM_MODE_EXCLUDE;
+		drvdata->ctrl = 0x0;
+		drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
+		drvdata->startstop_ctrl = 0x0;
+		drvdata->addr_idx = 0x0;
+		for (i = 0; i < drvdata->nr_addr_cmp; i++) {
+			drvdata->addr_val[i] = 0x0;
+			drvdata->addr_acctype[i] = 0x0;
+			drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
+		}
+		drvdata->cntr_idx = 0x0;
+
+		etm_set_default(drvdata);
+		spin_unlock(&drvdata->spinlock);
+	}
+
+	return size;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t mode_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->mode;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t mode_store(struct device *dev,
+			  struct device_attribute *attr,
+			  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->mode = val & ETM_MODE_ALL;
+
+	if (drvdata->mode & ETM_MODE_EXCLUDE)
+		drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC;
+	else
+		drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
+
+	if (drvdata->mode & ETM_MODE_CYCACC)
+		drvdata->ctrl |= ETMCR_CYC_ACC;
+	else
+		drvdata->ctrl &= ~ETMCR_CYC_ACC;
+
+	if (drvdata->mode & ETM_MODE_STALL) {
+		if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
+			dev_warn(drvdata->dev, "stall mode not supported\n");
+			ret = -EINVAL;
+			goto err_unlock;
+		}
+		drvdata->ctrl |= ETMCR_STALL_MODE;
+	 } else
+		drvdata->ctrl &= ~ETMCR_STALL_MODE;
+
+	if (drvdata->mode & ETM_MODE_TIMESTAMP) {
+		if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
+			dev_warn(drvdata->dev, "timestamp not supported\n");
+			ret = -EINVAL;
+			goto err_unlock;
+		}
+		drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
+	} else
+		drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN;
+
+	if (drvdata->mode & ETM_MODE_CTXID)
+		drvdata->ctrl |= ETMCR_CTXID_SIZE;
+	else
+		drvdata->ctrl &= ~ETMCR_CTXID_SIZE;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+
+err_unlock:
+	spin_unlock(&drvdata->spinlock);
+	return ret;
+}
+static DEVICE_ATTR_RW(mode);
+
+static ssize_t trigger_event_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->trigger_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t trigger_event_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->trigger_event = val & ETM_EVENT_MASK;
+
+	return size;
+}
+static DEVICE_ATTR_RW(trigger_event);
+
+static ssize_t enable_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->enable_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t enable_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->enable_event = val & ETM_EVENT_MASK;
+
+	return size;
+}
+static DEVICE_ATTR_RW(enable_event);
+
+static ssize_t fifofull_level_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->fifofull_level;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t fifofull_level_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->fifofull_level = val;
+
+	return size;
+}
+static DEVICE_ATTR_RW(fifofull_level);
+
+static ssize_t addr_idx_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->addr_idx;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_idx_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val >= drvdata->nr_addr_cmp)
+		return -EINVAL;
+
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
+	 */
+	spin_lock(&drvdata->spinlock);
+	drvdata->addr_idx = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_idx);
+
+static ssize_t addr_single_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EINVAL;
+	}
+
+	val = drvdata->addr_val[idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_single_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t size)
+{
+	u8 idx;
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EINVAL;
+	}
+
+	drvdata->addr_val[idx] = val;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_single);
+
+static ssize_t addr_range_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val1, val2;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (idx % 2 != 0) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	val1 = drvdata->addr_val[idx];
+	val2 = drvdata->addr_val[idx + 1];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx %#lx\n", val1, val2);
+}
+
+static ssize_t addr_range_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	u8 idx;
+	unsigned long val1, val2;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
+		return -EINVAL;
+	/* Lower address comparator cannot have a higher address value */
+	if (val1 > val2)
+		return -EINVAL;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (idx % 2 != 0) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	drvdata->addr_val[idx] = val1;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
+	drvdata->addr_val[idx + 1] = val2;
+	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
+	drvdata->enable_ctrl1 |= (1 << (idx/2));
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_range);
+
+static ssize_t addr_start_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	val = drvdata->addr_val[idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_start_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	u8 idx;
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	drvdata->addr_val[idx] = val;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
+	drvdata->startstop_ctrl |= (1 << idx);
+	drvdata->enable_ctrl1 |= BIT(25);
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_start);
+
+static ssize_t addr_stop_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	val = drvdata->addr_val[idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_stop_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	u8 idx;
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	drvdata->addr_val[idx] = val;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
+	drvdata->startstop_ctrl |= (1 << (idx + 16));
+	drvdata->enable_ctrl1 |= ETMTECR1_START_STOP;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_stop);
+
+static ssize_t addr_acctype_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->addr_acctype[drvdata->addr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_acctype_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->addr_acctype[drvdata->addr_idx] = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_acctype);
+
+static ssize_t cntr_idx_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->cntr_idx;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_idx_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val >= drvdata->nr_cntr)
+		return -EINVAL;
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
+	 */
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_idx = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_idx);
+
+static ssize_t cntr_rld_val_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_rld_val_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_rld_val);
+
+static ssize_t cntr_event_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->cntr_event[drvdata->cntr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_event_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_event);
+
+static ssize_t cntr_rld_event_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_rld_event_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_rld_event);
+
+static ssize_t cntr_val_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	int i, ret = 0;
+	u32 val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!drvdata->enable) {
+		spin_lock(&drvdata->spinlock);
+		for (i = 0; i < drvdata->nr_cntr; i++)
+			ret += sprintf(buf, "counter %d: %x\n",
+				       i, drvdata->cntr_val[i]);
+		spin_unlock(&drvdata->spinlock);
+		return ret;
+	}
+
+	for (i = 0; i < drvdata->nr_cntr; i++) {
+		val = etm_readl(drvdata, ETMCNTVRn(i));
+		ret += sprintf(buf, "counter %d: %x\n", i, val);
+	}
+
+	return ret;
+}
+
+static ssize_t cntr_val_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_val[drvdata->cntr_idx] = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_val);
+
+static ssize_t seq_12_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_12_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_12_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_12_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_12_event);
+
+static ssize_t seq_21_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_21_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_21_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_21_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_21_event);
+
+static ssize_t seq_23_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_23_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_23_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_23_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_23_event);
+
+static ssize_t seq_31_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_31_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_31_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_31_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_31_event);
+
+static ssize_t seq_32_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_32_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_32_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_32_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_32_event);
+
+static ssize_t seq_13_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_13_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_13_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_13_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_13_event);
+
+static ssize_t seq_curr_state_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	unsigned long val, flags;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!drvdata->enable) {
+		val = drvdata->seq_curr_state;
+		goto out;
+	}
+
+	pm_runtime_get_sync(drvdata->dev);
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	CS_UNLOCK(drvdata->base);
+	val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
+	CS_LOCK(drvdata->base);
+
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	pm_runtime_put(drvdata->dev);
+out:
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_curr_state_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val > ETM_SEQ_STATE_MAX_VAL)
+		return -EINVAL;
+
+	drvdata->seq_curr_state = val;
+
+	return size;
+}
+static DEVICE_ATTR_RW(seq_curr_state);
+
+static ssize_t ctxid_idx_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->ctxid_idx;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t ctxid_idx_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val >= drvdata->nr_ctxid_cmp)
+		return -EINVAL;
+
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
+	 */
+	spin_lock(&drvdata->spinlock);
+	drvdata->ctxid_idx = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(ctxid_idx);
+
+static ssize_t ctxid_pid_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->ctxid_vpid[drvdata->ctxid_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t ctxid_pid_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	int ret;
+	unsigned long vpid, pid;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &vpid);
+	if (ret)
+		return ret;
+
+	pid = coresight_vpid_to_pid(vpid);
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->ctxid_pid[drvdata->ctxid_idx] = pid;
+	drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(ctxid_pid);
+
+static ssize_t ctxid_mask_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->ctxid_mask;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t ctxid_mask_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->ctxid_mask = val;
+	return size;
+}
+static DEVICE_ATTR_RW(ctxid_mask);
+
+static ssize_t sync_freq_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->sync_freq;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t sync_freq_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->sync_freq = val & ETM_SYNC_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(sync_freq);
+
+static ssize_t timestamp_event_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->timestamp_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t timestamp_event_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->timestamp_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(timestamp_event);
+
+static ssize_t cpu_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->cpu;
+	return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+
+}
+static DEVICE_ATTR_RO(cpu);
+
+static ssize_t traceid_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = etm_get_trace_id(drvdata);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t traceid_store(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->traceid = val & ETM_TRACEID_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(traceid);
+
+static struct attribute *coresight_etm_attrs[] = {
+	&dev_attr_nr_addr_cmp.attr,
+	&dev_attr_nr_cntr.attr,
+	&dev_attr_nr_ctxid_cmp.attr,
+	&dev_attr_etmsr.attr,
+	&dev_attr_reset.attr,
+	&dev_attr_mode.attr,
+	&dev_attr_trigger_event.attr,
+	&dev_attr_enable_event.attr,
+	&dev_attr_fifofull_level.attr,
+	&dev_attr_addr_idx.attr,
+	&dev_attr_addr_single.attr,
+	&dev_attr_addr_range.attr,
+	&dev_attr_addr_start.attr,
+	&dev_attr_addr_stop.attr,
+	&dev_attr_addr_acctype.attr,
+	&dev_attr_cntr_idx.attr,
+	&dev_attr_cntr_rld_val.attr,
+	&dev_attr_cntr_event.attr,
+	&dev_attr_cntr_rld_event.attr,
+	&dev_attr_cntr_val.attr,
+	&dev_attr_seq_12_event.attr,
+	&dev_attr_seq_21_event.attr,
+	&dev_attr_seq_23_event.attr,
+	&dev_attr_seq_31_event.attr,
+	&dev_attr_seq_32_event.attr,
+	&dev_attr_seq_13_event.attr,
+	&dev_attr_seq_curr_state.attr,
+	&dev_attr_ctxid_idx.attr,
+	&dev_attr_ctxid_pid.attr,
+	&dev_attr_ctxid_mask.attr,
+	&dev_attr_sync_freq.attr,
+	&dev_attr_timestamp_event.attr,
+	&dev_attr_traceid.attr,
+	&dev_attr_cpu.attr,
+	NULL,
+};
+
+#define coresight_simple_func(name, offset)                             \
+static ssize_t name##_show(struct device *_dev,                         \
+			   struct device_attribute *attr, char *buf)    \
+{                                                                       \
+	struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent);    \
+	return scnprintf(buf, PAGE_SIZE, "0x%x\n",                      \
+			 readl_relaxed(drvdata->base + offset));        \
+}                                                                       \
+DEVICE_ATTR_RO(name)
+
+coresight_simple_func(etmccr, ETMCCR);
+coresight_simple_func(etmccer, ETMCCER);
+coresight_simple_func(etmscr, ETMSCR);
+coresight_simple_func(etmidr, ETMIDR);
+coresight_simple_func(etmcr, ETMCR);
+coresight_simple_func(etmtraceidr, ETMTRACEIDR);
+coresight_simple_func(etmteevr, ETMTEEVR);
+coresight_simple_func(etmtssvr, ETMTSSCR);
+coresight_simple_func(etmtecr1, ETMTECR1);
+coresight_simple_func(etmtecr2, ETMTECR2);
+
+static struct attribute *coresight_etm_mgmt_attrs[] = {
+	&dev_attr_etmccr.attr,
+	&dev_attr_etmccer.attr,
+	&dev_attr_etmscr.attr,
+	&dev_attr_etmidr.attr,
+	&dev_attr_etmcr.attr,
+	&dev_attr_etmtraceidr.attr,
+	&dev_attr_etmteevr.attr,
+	&dev_attr_etmtssvr.attr,
+	&dev_attr_etmtecr1.attr,
+	&dev_attr_etmtecr2.attr,
+	NULL,
+};
+
+static const struct attribute_group coresight_etm_group = {
+	.attrs = coresight_etm_attrs,
+};
+
+static const struct attribute_group coresight_etm_mgmt_group = {
+	.attrs = coresight_etm_mgmt_attrs,
+	.name = "mgmt",
+};
+
+const struct attribute_group *coresight_etm_groups[] = {
+	&coresight_etm_group,
+	&coresight_etm_mgmt_group,
+	NULL,
+};
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 3be1f14da44c..042b88ecae92 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -186,7 +186,7 @@ static void etm_clr_prog(struct etm_drvdata *drvdata)
 	}
 }
 
-static void etm_set_default(struct etm_drvdata *drvdata)
+void etm_set_default(struct etm_drvdata *drvdata)
 {
 	int i;
 
@@ -293,15 +293,18 @@ static int etm_cpu_id(struct coresight_device *csdev)
 	return drvdata->cpu;
 }
 
-static int etm_trace_id(struct coresight_device *csdev)
+int etm_get_trace_id(struct etm_drvdata *drvdata)
 {
-	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	unsigned long flags;
 	int trace_id = -1;
 
+	if (!drvdata)
+		goto out;
+
 	if (!drvdata->enable)
 		return drvdata->traceid;
-	pm_runtime_get_sync(csdev->dev.parent);
+
+	pm_runtime_get_sync(drvdata->dev);
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
@@ -310,9 +313,18 @@ static int etm_trace_id(struct coresight_device *csdev)
 	CS_LOCK(drvdata->base);
 
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(csdev->dev.parent);
+	pm_runtime_put(drvdata->dev);
 
+out:
 	return trace_id;
+
+}
+
+static int etm_trace_id(struct coresight_device *csdev)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	return etm_get_trace_id(drvdata);
 }
 
 static int etm_enable(struct coresight_device *csdev)
@@ -406,1218 +418,6 @@ static const struct coresight_ops etm_cs_ops = {
 	.source_ops	= &etm_source_ops,
 };
 
-static ssize_t nr_addr_cmp_show(struct device *dev,
-				struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->nr_addr_cmp;
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(nr_addr_cmp);
-
-static ssize_t nr_cntr_show(struct device *dev,
-			    struct device_attribute *attr, char *buf)
-{	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->nr_cntr;
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(nr_cntr);
-
-static ssize_t nr_ctxid_cmp_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->nr_ctxid_cmp;
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(nr_ctxid_cmp);
-
-static ssize_t etmsr_show(struct device *dev,
-			  struct device_attribute *attr, char *buf)
-{
-	unsigned long flags, val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	pm_runtime_get_sync(drvdata->dev);
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	CS_UNLOCK(drvdata->base);
-
-	val = etm_readl(drvdata, ETMSR);
-
-	CS_LOCK(drvdata->base);
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(etmsr);
-
-static ssize_t reset_store(struct device *dev,
-			   struct device_attribute *attr,
-			   const char *buf, size_t size)
-{
-	int i, ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val) {
-		spin_lock(&drvdata->spinlock);
-		drvdata->mode = ETM_MODE_EXCLUDE;
-		drvdata->ctrl = 0x0;
-		drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
-		drvdata->startstop_ctrl = 0x0;
-		drvdata->addr_idx = 0x0;
-		for (i = 0; i < drvdata->nr_addr_cmp; i++) {
-			drvdata->addr_val[i] = 0x0;
-			drvdata->addr_acctype[i] = 0x0;
-			drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
-		}
-		drvdata->cntr_idx = 0x0;
-
-		etm_set_default(drvdata);
-		spin_unlock(&drvdata->spinlock);
-	}
-
-	return size;
-}
-static DEVICE_ATTR_WO(reset);
-
-static ssize_t mode_show(struct device *dev,
-			 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->mode;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t mode_store(struct device *dev,
-			  struct device_attribute *attr,
-			  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->mode = val & ETM_MODE_ALL;
-
-	if (drvdata->mode & ETM_MODE_EXCLUDE)
-		drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC;
-	else
-		drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
-
-	if (drvdata->mode & ETM_MODE_CYCACC)
-		drvdata->ctrl |= ETMCR_CYC_ACC;
-	else
-		drvdata->ctrl &= ~ETMCR_CYC_ACC;
-
-	if (drvdata->mode & ETM_MODE_STALL) {
-		if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
-			dev_warn(drvdata->dev, "stall mode not supported\n");
-			ret = -EINVAL;
-			goto err_unlock;
-		}
-		drvdata->ctrl |= ETMCR_STALL_MODE;
-	 } else
-		drvdata->ctrl &= ~ETMCR_STALL_MODE;
-
-	if (drvdata->mode & ETM_MODE_TIMESTAMP) {
-		if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
-			dev_warn(drvdata->dev, "timestamp not supported\n");
-			ret = -EINVAL;
-			goto err_unlock;
-		}
-		drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
-	} else
-		drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN;
-
-	if (drvdata->mode & ETM_MODE_CTXID)
-		drvdata->ctrl |= ETMCR_CTXID_SIZE;
-	else
-		drvdata->ctrl &= ~ETMCR_CTXID_SIZE;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-
-err_unlock:
-	spin_unlock(&drvdata->spinlock);
-	return ret;
-}
-static DEVICE_ATTR_RW(mode);
-
-static ssize_t trigger_event_show(struct device *dev,
-				  struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->trigger_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t trigger_event_store(struct device *dev,
-				   struct device_attribute *attr,
-				   const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->trigger_event = val & ETM_EVENT_MASK;
-
-	return size;
-}
-static DEVICE_ATTR_RW(trigger_event);
-
-static ssize_t enable_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->enable_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t enable_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->enable_event = val & ETM_EVENT_MASK;
-
-	return size;
-}
-static DEVICE_ATTR_RW(enable_event);
-
-static ssize_t fifofull_level_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->fifofull_level;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t fifofull_level_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->fifofull_level = val;
-
-	return size;
-}
-static DEVICE_ATTR_RW(fifofull_level);
-
-static ssize_t addr_idx_show(struct device *dev,
-			     struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->addr_idx;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_idx_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val >= drvdata->nr_addr_cmp)
-		return -EINVAL;
-
-	/*
-	 * Use spinlock to ensure index doesn't change while it gets
-	 * dereferenced multiple times within a spinlock block elsewhere.
-	 */
-	spin_lock(&drvdata->spinlock);
-	drvdata->addr_idx = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_idx);
-
-static ssize_t addr_single_show(struct device *dev,
-				struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EINVAL;
-	}
-
-	val = drvdata->addr_val[idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_single_store(struct device *dev,
-				 struct device_attribute *attr,
-				 const char *buf, size_t size)
-{
-	u8 idx;
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EINVAL;
-	}
-
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_single);
-
-static ssize_t addr_range_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val1, val2;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (idx % 2 != 0) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	val1 = drvdata->addr_val[idx];
-	val2 = drvdata->addr_val[idx + 1];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx %#lx\n", val1, val2);
-}
-
-static ssize_t addr_range_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	u8 idx;
-	unsigned long val1, val2;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
-		return -EINVAL;
-	/* Lower address comparator cannot have a higher address value */
-	if (val1 > val2)
-		return -EINVAL;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (idx % 2 != 0) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	drvdata->addr_val[idx] = val1;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
-	drvdata->addr_val[idx + 1] = val2;
-	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
-	drvdata->enable_ctrl1 |= (1 << (idx/2));
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_range);
-
-static ssize_t addr_start_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	val = drvdata->addr_val[idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_start_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	u8 idx;
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
-	drvdata->startstop_ctrl |= (1 << idx);
-	drvdata->enable_ctrl1 |= BIT(25);
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_start);
-
-static ssize_t addr_stop_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	val = drvdata->addr_val[idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_stop_store(struct device *dev,
-			       struct device_attribute *attr,
-			       const char *buf, size_t size)
-{
-	u8 idx;
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
-	drvdata->startstop_ctrl |= (1 << (idx + 16));
-	drvdata->enable_ctrl1 |= ETMTECR1_START_STOP;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_stop);
-
-static ssize_t addr_acctype_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->addr_acctype[drvdata->addr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_acctype_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->addr_acctype[drvdata->addr_idx] = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_acctype);
-
-static ssize_t cntr_idx_show(struct device *dev,
-			     struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->cntr_idx;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_idx_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val >= drvdata->nr_cntr)
-		return -EINVAL;
-	/*
-	 * Use spinlock to ensure index doesn't change while it gets
-	 * dereferenced multiple times within a spinlock block elsewhere.
-	 */
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_idx = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_idx);
-
-static ssize_t cntr_rld_val_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_rld_val_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_rld_val);
-
-static ssize_t cntr_event_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_event[drvdata->cntr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_event_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_event);
-
-static ssize_t cntr_rld_event_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_rld_event_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_rld_event);
-
-static ssize_t cntr_val_show(struct device *dev,
-			     struct device_attribute *attr, char *buf)
-{
-	int i, ret = 0;
-	u32 val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (!drvdata->enable) {
-		spin_lock(&drvdata->spinlock);
-		for (i = 0; i < drvdata->nr_cntr; i++)
-			ret += sprintf(buf, "counter %d: %x\n",
-				       i, drvdata->cntr_val[i]);
-		spin_unlock(&drvdata->spinlock);
-		return ret;
-	}
-
-	for (i = 0; i < drvdata->nr_cntr; i++) {
-		val = etm_readl(drvdata, ETMCNTVRn(i));
-		ret += sprintf(buf, "counter %d: %x\n", i, val);
-	}
-
-	return ret;
-}
-
-static ssize_t cntr_val_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_val[drvdata->cntr_idx] = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_val);
-
-static ssize_t seq_12_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_12_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_12_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_12_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_12_event);
-
-static ssize_t seq_21_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_21_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_21_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_21_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_21_event);
-
-static ssize_t seq_23_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_23_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_23_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_23_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_23_event);
-
-static ssize_t seq_31_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_31_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_31_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_31_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_31_event);
-
-static ssize_t seq_32_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_32_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_32_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_32_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_32_event);
-
-static ssize_t seq_13_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_13_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_13_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_13_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_13_event);
-
-static ssize_t seq_curr_state_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	unsigned long val, flags;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (!drvdata->enable) {
-		val = drvdata->seq_curr_state;
-		goto out;
-	}
-
-	pm_runtime_get_sync(drvdata->dev);
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-
-	CS_UNLOCK(drvdata->base);
-	val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
-	CS_LOCK(drvdata->base);
-
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
-out:
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_curr_state_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val > ETM_SEQ_STATE_MAX_VAL)
-		return -EINVAL;
-
-	drvdata->seq_curr_state = val;
-
-	return size;
-}
-static DEVICE_ATTR_RW(seq_curr_state);
-
-static ssize_t ctxid_idx_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->ctxid_idx;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t ctxid_idx_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val >= drvdata->nr_ctxid_cmp)
-		return -EINVAL;
-
-	/*
-	 * Use spinlock to ensure index doesn't change while it gets
-	 * dereferenced multiple times within a spinlock block elsewhere.
-	 */
-	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_idx = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(ctxid_idx);
-
-static ssize_t ctxid_pid_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->ctxid_vpid[drvdata->ctxid_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t ctxid_pid_store(struct device *dev,
-			       struct device_attribute *attr,
-			       const char *buf, size_t size)
-{
-	int ret;
-	unsigned long vpid, pid;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &vpid);
-	if (ret)
-		return ret;
-
-	pid = coresight_vpid_to_pid(vpid);
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_pid[drvdata->ctxid_idx] = pid;
-	drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(ctxid_pid);
-
-static ssize_t ctxid_mask_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->ctxid_mask;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t ctxid_mask_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->ctxid_mask = val;
-	return size;
-}
-static DEVICE_ATTR_RW(ctxid_mask);
-
-static ssize_t sync_freq_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->sync_freq;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t sync_freq_store(struct device *dev,
-			       struct device_attribute *attr,
-			       const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->sync_freq = val & ETM_SYNC_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(sync_freq);
-
-static ssize_t timestamp_event_show(struct device *dev,
-				    struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->timestamp_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t timestamp_event_store(struct device *dev,
-				     struct device_attribute *attr,
-				     const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->timestamp_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(timestamp_event);
-
-static ssize_t cpu_show(struct device *dev,
-			struct device_attribute *attr, char *buf)
-{
-	int val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->cpu;
-	return scnprintf(buf, PAGE_SIZE, "%d\n", val);
-
-}
-static DEVICE_ATTR_RO(cpu);
-
-static ssize_t traceid_show(struct device *dev,
-			    struct device_attribute *attr, char *buf)
-{
-	unsigned long val, flags;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (!drvdata->enable) {
-		val = drvdata->traceid;
-		goto out;
-	}
-
-	pm_runtime_get_sync(drvdata->dev);
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	CS_UNLOCK(drvdata->base);
-
-	val = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK);
-
-	CS_LOCK(drvdata->base);
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
-out:
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t traceid_store(struct device *dev,
-			     struct device_attribute *attr,
-			     const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->traceid = val & ETM_TRACEID_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(traceid);
-
-static struct attribute *coresight_etm_attrs[] = {
-	&dev_attr_nr_addr_cmp.attr,
-	&dev_attr_nr_cntr.attr,
-	&dev_attr_nr_ctxid_cmp.attr,
-	&dev_attr_etmsr.attr,
-	&dev_attr_reset.attr,
-	&dev_attr_mode.attr,
-	&dev_attr_trigger_event.attr,
-	&dev_attr_enable_event.attr,
-	&dev_attr_fifofull_level.attr,
-	&dev_attr_addr_idx.attr,
-	&dev_attr_addr_single.attr,
-	&dev_attr_addr_range.attr,
-	&dev_attr_addr_start.attr,
-	&dev_attr_addr_stop.attr,
-	&dev_attr_addr_acctype.attr,
-	&dev_attr_cntr_idx.attr,
-	&dev_attr_cntr_rld_val.attr,
-	&dev_attr_cntr_event.attr,
-	&dev_attr_cntr_rld_event.attr,
-	&dev_attr_cntr_val.attr,
-	&dev_attr_seq_12_event.attr,
-	&dev_attr_seq_21_event.attr,
-	&dev_attr_seq_23_event.attr,
-	&dev_attr_seq_31_event.attr,
-	&dev_attr_seq_32_event.attr,
-	&dev_attr_seq_13_event.attr,
-	&dev_attr_seq_curr_state.attr,
-	&dev_attr_ctxid_idx.attr,
-	&dev_attr_ctxid_pid.attr,
-	&dev_attr_ctxid_mask.attr,
-	&dev_attr_sync_freq.attr,
-	&dev_attr_timestamp_event.attr,
-	&dev_attr_traceid.attr,
-	&dev_attr_cpu.attr,
-	NULL,
-};
-
-#define coresight_simple_func(name, offset)                             \
-static ssize_t name##_show(struct device *_dev,                         \
-			   struct device_attribute *attr, char *buf)    \
-{                                                                       \
-	struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent);    \
-	return scnprintf(buf, PAGE_SIZE, "0x%x\n",                      \
-			 readl_relaxed(drvdata->base + offset));        \
-}                                                                       \
-DEVICE_ATTR_RO(name)
-
-coresight_simple_func(etmccr, ETMCCR);
-coresight_simple_func(etmccer, ETMCCER);
-coresight_simple_func(etmscr, ETMSCR);
-coresight_simple_func(etmidr, ETMIDR);
-coresight_simple_func(etmcr, ETMCR);
-coresight_simple_func(etmtraceidr, ETMTRACEIDR);
-coresight_simple_func(etmteevr, ETMTEEVR);
-coresight_simple_func(etmtssvr, ETMTSSCR);
-coresight_simple_func(etmtecr1, ETMTECR1);
-coresight_simple_func(etmtecr2, ETMTECR2);
-
-static struct attribute *coresight_etm_mgmt_attrs[] = {
-	&dev_attr_etmccr.attr,
-	&dev_attr_etmccer.attr,
-	&dev_attr_etmscr.attr,
-	&dev_attr_etmidr.attr,
-	&dev_attr_etmcr.attr,
-	&dev_attr_etmtraceidr.attr,
-	&dev_attr_etmteevr.attr,
-	&dev_attr_etmtssvr.attr,
-	&dev_attr_etmtecr1.attr,
-	&dev_attr_etmtecr2.attr,
-	NULL,
-};
-
-static const struct attribute_group coresight_etm_group = {
-	.attrs = coresight_etm_attrs,
-};
-
-
-static const struct attribute_group coresight_etm_mgmt_group = {
-	.attrs = coresight_etm_mgmt_attrs,
-	.name = "mgmt",
-};
-
-static const struct attribute_group *coresight_etm_groups[] = {
-	&coresight_etm_group,
-	&coresight_etm_mgmt_group,
-	NULL,
-};
-
 static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 			    void *hcpu)
 {
-- 
2.1.4

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

* [PATCH V8 06/23] coresight: etm3x: unlocking tracers in default arch init
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Calling function 'smp_call_function_single()' to unlock a
tracer and calling it again right after to perform the
default initialisation doesn't make sense.

Moving 'etm_os_unlock()' just before making the default
initialisation results in the same outcome while saving
one call to 'smp_call_function_single()'.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 042b88ecae92..c383d218d921 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -47,11 +47,11 @@ static struct etm_drvdata *etmdrvdata[NR_CPUS];
  * and OS lock must be unlocked before any memory mapped access on such
  * processors, otherwise memory mapped reads/writes will be invalid.
  */
-static void etm_os_unlock(void *info)
+static void etm_os_unlock(struct etm_drvdata *drvdata)
 {
-	struct etm_drvdata *drvdata = (struct etm_drvdata *)info;
 	/* Writing any value to ETMOSLAR unlocks the trace registers */
 	etm_writel(drvdata, 0x0, ETMOSLAR);
+	drvdata->os_unlock = true;
 	isb();
 }
 
@@ -483,6 +483,9 @@ static void etm_init_arch_data(void *info)
 	u32 etmccr;
 	struct etm_drvdata *drvdata = info;
 
+	/* Make sure all registers are accessible */
+	etm_os_unlock(drvdata);
+
 	CS_UNLOCK(drvdata->base);
 
 	/* First dummy read */
@@ -607,9 +610,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 	get_online_cpus();
 	etmdrvdata[drvdata->cpu] = drvdata;
 
-	if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1))
-		drvdata->os_unlock = true;
-
 	if (smp_call_function_single(drvdata->cpu,
 				     etm_init_arch_data,  drvdata, 1))
 		dev_err(dev, "ETM arch init failed\n");
-- 
2.1.4

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

* [PATCH V8 06/23] coresight: etm3x: unlocking tracers in default arch init
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Calling function 'smp_call_function_single()' to unlock a
tracer and calling it again right after to perform the
default initialisation doesn't make sense.

Moving 'etm_os_unlock()' just before making the default
initialisation results in the same outcome while saving
one call to 'smp_call_function_single()'.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 042b88ecae92..c383d218d921 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -47,11 +47,11 @@ static struct etm_drvdata *etmdrvdata[NR_CPUS];
  * and OS lock must be unlocked before any memory mapped access on such
  * processors, otherwise memory mapped reads/writes will be invalid.
  */
-static void etm_os_unlock(void *info)
+static void etm_os_unlock(struct etm_drvdata *drvdata)
 {
-	struct etm_drvdata *drvdata = (struct etm_drvdata *)info;
 	/* Writing any value to ETMOSLAR unlocks the trace registers */
 	etm_writel(drvdata, 0x0, ETMOSLAR);
+	drvdata->os_unlock = true;
 	isb();
 }
 
@@ -483,6 +483,9 @@ static void etm_init_arch_data(void *info)
 	u32 etmccr;
 	struct etm_drvdata *drvdata = info;
 
+	/* Make sure all registers are accessible */
+	etm_os_unlock(drvdata);
+
 	CS_UNLOCK(drvdata->base);
 
 	/* First dummy read */
@@ -607,9 +610,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 	get_online_cpus();
 	etmdrvdata[drvdata->cpu] = drvdata;
 
-	if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1))
-		drvdata->os_unlock = true;
-
 	if (smp_call_function_single(drvdata->cpu,
 				     etm_init_arch_data,  drvdata, 1))
 		dev_err(dev, "ETM arch init failed\n");
-- 
2.1.4

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

* [PATCH V8 07/23] coresight: etm3x: splitting struct etm_drvdata
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Splitting "etm_drvdata" in two sections, one for the HW specific
data and another for user configuration.

That way it is easier to manipulate and zero out the configuration
data when more than one concurrent tracing session configuration
is active.

Also taking care of up-lifting all the code affected by this new
arrangement.  No loss or gain of functionality (other than what is
mentioned above) is introduced by this patch.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h        | 102 ++++----
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    | 290 ++++++++++++---------
 drivers/hwtracing/coresight/coresight-etm3x.c      | 153 +++++------
 3 files changed, 305 insertions(+), 240 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 9a30aa392ed9..371fb7d2e829 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -136,29 +136,9 @@
 #define ETM_DEFAULT_EVENT_VAL	(ETM_HARD_WIRE_RES_A	|	\
 				 ETM_ADD_COMP_0		|	\
 				 ETM_EVENT_NOT_A)
+
 /**
- * struct etm_drvdata - specifics associated to an ETM component
- * @base:	memory mapped base address for this component.
- * @dev:	the device entity associated to this component.
- * @atclk:	optional clock for the core parts of the ETM.
- * @csdev:	component vitals needed by the framework.
- * @spinlock:	only one at a time pls.
- * @cpu:	the cpu this component is affined to.
- * @port_size:	port size as reported by ETMCR bit 4-6 and 21.
- * @arch:	ETM/PTM version number.
- * @use_cpu14:	true if management registers need to be accessed via CP14.
- * @enable:	is this ETM/PTM currently tracing.
- * @sticky_enable: true if ETM base configuration has been done.
- * @boot_enable:true if we should start tracing at boot time.
- * @os_unlock:	true if access to management registers is allowed.
- * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR.
- * @nr_cntr:	Number of counters as found in ETMCCR bit 13-15.
- * @nr_ext_inp:	Number of external input as found in ETMCCR bit 17-19.
- * @nr_ext_out:	Number of external output as found in ETMCCR bit 20-22.
- * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25.
- * @etmccr:	value of register ETMCCR.
- * @etmccer:	value of register ETMCCER.
- * @traceid:	value of the current ID for this component.
+ * struct etm_config - configuration information related to an ETM
  * @mode:	controls various modes supported by this ETM/PTM.
  * @ctrl:	used in conjunction with @mode.
  * @trigger_event: setting for register ETMTRIGGER.
@@ -189,30 +169,9 @@
  * @ctxid_mask: mask applicable to all the context IDs.
  * @sync_freq:	Synchronisation frequency.
  * @timestamp_event: Defines an event that requests the insertion
-		     of a timestamp into the trace stream.
+ *		     of a timestamp into the trace stream.
  */
-struct etm_drvdata {
-	void __iomem			*base;
-	struct device			*dev;
-	struct clk			*atclk;
-	struct coresight_device		*csdev;
-	spinlock_t			spinlock;
-	int				cpu;
-	int				port_size;
-	u8				arch;
-	bool				use_cp14;
-	bool				enable;
-	bool				sticky_enable;
-	bool				boot_enable;
-	bool				os_unlock;
-	u8				nr_addr_cmp;
-	u8				nr_cntr;
-	u8				nr_ext_inp;
-	u8				nr_ext_out;
-	u8				nr_ctxid_cmp;
-	u32				etmccr;
-	u32				etmccer;
-	u32				traceid;
+struct etm_config {
 	u32				mode;
 	u32				ctrl;
 	u32				trigger_event;
@@ -244,6 +203,56 @@ struct etm_drvdata {
 	u32				timestamp_event;
 };
 
+/**
+ * struct etm_drvdata - specifics associated to an ETM component
+ * @base:	memory mapped base address for this component.
+ * @dev:	the device entity associated to this component.
+ * @atclk:	optional clock for the core parts of the ETM.
+ * @csdev:	component vitals needed by the framework.
+ * @spinlock:	only one at a time pls.
+ * @cpu:	the cpu this component is affined to.
+ * @port_size:	port size as reported by ETMCR bit 4-6 and 21.
+ * @arch:	ETM/PTM version number.
+ * @use_cpu14:	true if management registers need to be accessed via CP14.
+ * @enable:	is this ETM/PTM currently tracing.
+ * @sticky_enable: true if ETM base configuration has been done.
+ * @boot_enable:true if we should start tracing at boot time.
+ * @os_unlock:	true if access to management registers is allowed.
+ * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR.
+ * @nr_cntr:	Number of counters as found in ETMCCR bit 13-15.
+ * @nr_ext_inp:	Number of external input as found in ETMCCR bit 17-19.
+ * @nr_ext_out:	Number of external output as found in ETMCCR bit 20-22.
+ * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25.
+ * @etmccr:	value of register ETMCCR.
+ * @etmccer:	value of register ETMCCER.
+ * @traceid:	value of the current ID for this component.
+ * @config:	structure holding configuration parameters.
+ */
+struct etm_drvdata {
+	void __iomem			*base;
+	struct device			*dev;
+	struct clk			*atclk;
+	struct coresight_device		*csdev;
+	spinlock_t			spinlock;
+	int				cpu;
+	int				port_size;
+	u8				arch;
+	bool				use_cp14;
+	bool				enable;
+	bool				sticky_enable;
+	bool				boot_enable;
+	bool				os_unlock;
+	u8				nr_addr_cmp;
+	u8				nr_cntr;
+	u8				nr_ext_inp;
+	u8				nr_ext_out;
+	u8				nr_ctxid_cmp;
+	u32				etmccr;
+	u32				etmccer;
+	u32				traceid;
+	struct etm_config		config;
+};
+
 enum etm_addr_type {
 	ETM_ADDR_TYPE_NONE,
 	ETM_ADDR_TYPE_SINGLE,
@@ -283,5 +292,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
 
 extern const struct attribute_group *coresight_etm_groups[];
 int etm_get_trace_id(struct etm_drvdata *drvdata);
-void etm_set_default(struct etm_drvdata *drvdata);
+void etm_set_default(struct etm_config *config);
+struct etm_config *get_etm_config(struct etm_drvdata *drvdata);
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index f409f5a88e95..456df2378a6f 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -78,6 +78,7 @@ static ssize_t reset_store(struct device *dev,
 	int i, ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -85,19 +86,14 @@ static ssize_t reset_store(struct device *dev,
 
 	if (val) {
 		spin_lock(&drvdata->spinlock);
-		drvdata->mode = ETM_MODE_EXCLUDE;
-		drvdata->ctrl = 0x0;
-		drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
-		drvdata->startstop_ctrl = 0x0;
-		drvdata->addr_idx = 0x0;
+		memset(config, 0, sizeof(struct etm_config));
+		config->mode = ETM_MODE_EXCLUDE;
+		config->trigger_event = ETM_DEFAULT_EVENT_VAL;
 		for (i = 0; i < drvdata->nr_addr_cmp; i++) {
-			drvdata->addr_val[i] = 0x0;
-			drvdata->addr_acctype[i] = 0x0;
-			drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
+			config->addr_type[i] = ETM_ADDR_TYPE_NONE;
 		}
-		drvdata->cntr_idx = 0x0;
 
-		etm_set_default(drvdata);
+		etm_set_default(config);
 		spin_unlock(&drvdata->spinlock);
 	}
 
@@ -110,8 +106,9 @@ static ssize_t mode_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->mode;
+	val = config->mode;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -122,48 +119,49 @@ static ssize_t mode_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->mode = val & ETM_MODE_ALL;
+	config->mode = val & ETM_MODE_ALL;
 
-	if (drvdata->mode & ETM_MODE_EXCLUDE)
-		drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC;
+	if (config->mode & ETM_MODE_EXCLUDE)
+		config->enable_ctrl1 |= ETMTECR1_INC_EXC;
 	else
-		drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
+		config->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
 
-	if (drvdata->mode & ETM_MODE_CYCACC)
-		drvdata->ctrl |= ETMCR_CYC_ACC;
+	if (config->mode & ETM_MODE_CYCACC)
+		config->ctrl |= ETMCR_CYC_ACC;
 	else
-		drvdata->ctrl &= ~ETMCR_CYC_ACC;
+		config->ctrl &= ~ETMCR_CYC_ACC;
 
-	if (drvdata->mode & ETM_MODE_STALL) {
+	if (config->mode & ETM_MODE_STALL) {
 		if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
 			dev_warn(drvdata->dev, "stall mode not supported\n");
 			ret = -EINVAL;
 			goto err_unlock;
 		}
-		drvdata->ctrl |= ETMCR_STALL_MODE;
+		config->ctrl |= ETMCR_STALL_MODE;
 	 } else
-		drvdata->ctrl &= ~ETMCR_STALL_MODE;
+		config->ctrl &= ~ETMCR_STALL_MODE;
 
-	if (drvdata->mode & ETM_MODE_TIMESTAMP) {
+	if (config->mode & ETM_MODE_TIMESTAMP) {
 		if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
 			dev_warn(drvdata->dev, "timestamp not supported\n");
 			ret = -EINVAL;
 			goto err_unlock;
 		}
-		drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
+		config->ctrl |= ETMCR_TIMESTAMP_EN;
 	} else
-		drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN;
+		config->ctrl &= ~ETMCR_TIMESTAMP_EN;
 
-	if (drvdata->mode & ETM_MODE_CTXID)
-		drvdata->ctrl |= ETMCR_CTXID_SIZE;
+	if (config->mode & ETM_MODE_CTXID)
+		config->ctrl |= ETMCR_CTXID_SIZE;
 	else
-		drvdata->ctrl &= ~ETMCR_CTXID_SIZE;
+		config->ctrl &= ~ETMCR_CTXID_SIZE;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -179,8 +177,9 @@ static ssize_t trigger_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->trigger_event;
+	val = config->trigger_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -191,12 +190,13 @@ static ssize_t trigger_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->trigger_event = val & ETM_EVENT_MASK;
+	config->trigger_event = val & ETM_EVENT_MASK;
 
 	return size;
 }
@@ -207,8 +207,9 @@ static ssize_t enable_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->enable_event;
+	val = config->enable_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -219,12 +220,13 @@ static ssize_t enable_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->enable_event = val & ETM_EVENT_MASK;
+	config->enable_event = val & ETM_EVENT_MASK;
 
 	return size;
 }
@@ -235,8 +237,9 @@ static ssize_t fifofull_level_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->fifofull_level;
+	val = config->fifofull_level;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -247,12 +250,13 @@ static ssize_t fifofull_level_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->fifofull_level = val;
+	config->fifofull_level = val;
 
 	return size;
 }
@@ -263,8 +267,9 @@ static ssize_t addr_idx_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->addr_idx;
+	val = config->addr_idx;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -275,6 +280,7 @@ static ssize_t addr_idx_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -288,7 +294,7 @@ static ssize_t addr_idx_store(struct device *dev,
 	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
 	spin_lock(&drvdata->spinlock);
-	drvdata->addr_idx = val;
+	config->addr_idx = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -301,16 +307,17 @@ static ssize_t addr_single_show(struct device *dev,
 	u8 idx;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EINVAL;
 	}
 
-	val = drvdata->addr_val[idx];
+	val = config->addr_val[idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -324,21 +331,22 @@ static ssize_t addr_single_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EINVAL;
 	}
 
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
+	config->addr_val[idx] = val;
+	config->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -351,23 +359,24 @@ static ssize_t addr_range_show(struct device *dev,
 	u8 idx;
 	unsigned long val1, val2;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
+	idx = config->addr_idx;
 	if (idx % 2 != 0) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+	if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	val1 = drvdata->addr_val[idx];
-	val2 = drvdata->addr_val[idx + 1];
+	val1 = config->addr_val[idx];
+	val2 = config->addr_val[idx + 1];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx %#lx\n", val1, val2);
@@ -380,6 +389,7 @@ static ssize_t addr_range_store(struct device *dev,
 	u8 idx;
 	unsigned long val1, val2;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
 		return -EINVAL;
@@ -388,24 +398,24 @@ static ssize_t addr_range_store(struct device *dev,
 		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
+	idx = config->addr_idx;
 	if (idx % 2 != 0) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+	if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	drvdata->addr_val[idx] = val1;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
-	drvdata->addr_val[idx + 1] = val2;
-	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
-	drvdata->enable_ctrl1 |= (1 << (idx/2));
+	config->addr_val[idx] = val1;
+	config->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
+	config->addr_val[idx + 1] = val2;
+	config->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
+	config->enable_ctrl1 |= (1 << (idx/2));
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -418,16 +428,17 @@ static ssize_t addr_start_show(struct device *dev,
 	u8 idx;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_START)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	val = drvdata->addr_val[idx];
+	val = config->addr_val[idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -441,23 +452,24 @@ static ssize_t addr_start_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_START)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
-	drvdata->startstop_ctrl |= (1 << idx);
-	drvdata->enable_ctrl1 |= BIT(25);
+	config->addr_val[idx] = val;
+	config->addr_type[idx] = ETM_ADDR_TYPE_START;
+	config->startstop_ctrl |= (1 << idx);
+	config->enable_ctrl1 |= BIT(25);
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -470,16 +482,17 @@ static ssize_t addr_stop_show(struct device *dev,
 	u8 idx;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	val = drvdata->addr_val[idx];
+	val = config->addr_val[idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -493,23 +506,24 @@ static ssize_t addr_stop_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
-	drvdata->startstop_ctrl |= (1 << (idx + 16));
-	drvdata->enable_ctrl1 |= ETMTECR1_START_STOP;
+	config->addr_val[idx] = val;
+	config->addr_type[idx] = ETM_ADDR_TYPE_STOP;
+	config->startstop_ctrl |= (1 << (idx + 16));
+	config->enable_ctrl1 |= ETMTECR1_START_STOP;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -521,9 +535,10 @@ static ssize_t addr_acctype_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->addr_acctype[drvdata->addr_idx];
+	val = config->addr_acctype[config->addr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -536,13 +551,14 @@ static ssize_t addr_acctype_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->addr_acctype[drvdata->addr_idx] = val;
+	config->addr_acctype[config->addr_idx] = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -554,8 +570,9 @@ static ssize_t cntr_idx_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->cntr_idx;
+	val = config->cntr_idx;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -566,6 +583,7 @@ static ssize_t cntr_idx_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -578,7 +596,7 @@ static ssize_t cntr_idx_store(struct device *dev,
 	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_idx = val;
+	config->cntr_idx = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -590,9 +608,10 @@ static ssize_t cntr_rld_val_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
+	val = config->cntr_rld_val[config->cntr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -605,13 +624,14 @@ static ssize_t cntr_rld_val_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
+	config->cntr_rld_val[config->cntr_idx] = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -623,9 +643,10 @@ static ssize_t cntr_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_event[drvdata->cntr_idx];
+	val = config->cntr_event[config->cntr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -638,13 +659,14 @@ static ssize_t cntr_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	config->cntr_event[config->cntr_idx] = val & ETM_EVENT_MASK;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -656,9 +678,10 @@ static ssize_t cntr_rld_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
+	val = config->cntr_rld_event[config->cntr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -671,13 +694,14 @@ static ssize_t cntr_rld_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	config->cntr_rld_event[config->cntr_idx] = val & ETM_EVENT_MASK;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -690,12 +714,13 @@ static ssize_t cntr_val_show(struct device *dev,
 	int i, ret = 0;
 	u32 val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	if (!drvdata->enable) {
 		spin_lock(&drvdata->spinlock);
 		for (i = 0; i < drvdata->nr_cntr; i++)
 			ret += sprintf(buf, "counter %d: %x\n",
-				       i, drvdata->cntr_val[i]);
+				       i, config->cntr_val[i]);
 		spin_unlock(&drvdata->spinlock);
 		return ret;
 	}
@@ -715,13 +740,14 @@ static ssize_t cntr_val_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_val[drvdata->cntr_idx] = val;
+	config->cntr_val[config->cntr_idx] = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -733,8 +759,9 @@ static ssize_t seq_12_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_12_event;
+	val = config->seq_12_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -745,12 +772,13 @@ static ssize_t seq_12_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_12_event = val & ETM_EVENT_MASK;
+	config->seq_12_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_12_event);
@@ -760,8 +788,9 @@ static ssize_t seq_21_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_21_event;
+	val = config->seq_21_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -772,12 +801,13 @@ static ssize_t seq_21_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_21_event = val & ETM_EVENT_MASK;
+	config->seq_21_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_21_event);
@@ -787,8 +817,9 @@ static ssize_t seq_23_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_23_event;
+	val = config->seq_23_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -799,12 +830,13 @@ static ssize_t seq_23_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_23_event = val & ETM_EVENT_MASK;
+	config->seq_23_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_23_event);
@@ -814,8 +846,9 @@ static ssize_t seq_31_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_31_event;
+	val = config->seq_31_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -826,12 +859,13 @@ static ssize_t seq_31_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_31_event = val & ETM_EVENT_MASK;
+	config->seq_31_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_31_event);
@@ -841,8 +875,9 @@ static ssize_t seq_32_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_32_event;
+	val = config->seq_32_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -853,12 +888,13 @@ static ssize_t seq_32_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_32_event = val & ETM_EVENT_MASK;
+	config->seq_32_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_32_event);
@@ -868,8 +904,9 @@ static ssize_t seq_13_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_13_event;
+	val = config->seq_13_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -880,12 +917,13 @@ static ssize_t seq_13_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_13_event = val & ETM_EVENT_MASK;
+	config->seq_13_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_13_event);
@@ -895,9 +933,10 @@ static ssize_t seq_curr_state_show(struct device *dev,
 {
 	unsigned long val, flags;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	if (!drvdata->enable) {
-		val = drvdata->seq_curr_state;
+		val = config->seq_curr_state;
 		goto out;
 	}
 
@@ -921,6 +960,7 @@ static ssize_t seq_curr_state_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -929,7 +969,7 @@ static ssize_t seq_curr_state_store(struct device *dev,
 	if (val > ETM_SEQ_STATE_MAX_VAL)
 		return -EINVAL;
 
-	drvdata->seq_curr_state = val;
+	config->seq_curr_state = val;
 
 	return size;
 }
@@ -940,8 +980,9 @@ static ssize_t ctxid_idx_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->ctxid_idx;
+	val = config->ctxid_idx;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -952,6 +993,7 @@ static ssize_t ctxid_idx_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -965,7 +1007,7 @@ static ssize_t ctxid_idx_store(struct device *dev,
 	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
 	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_idx = val;
+	config->ctxid_idx = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -977,9 +1019,10 @@ static ssize_t ctxid_pid_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->ctxid_vpid[drvdata->ctxid_idx];
+	val = config->ctxid_vpid[config->ctxid_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -992,6 +1035,7 @@ static ssize_t ctxid_pid_store(struct device *dev,
 	int ret;
 	unsigned long vpid, pid;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &vpid);
 	if (ret)
@@ -1000,8 +1044,8 @@ static ssize_t ctxid_pid_store(struct device *dev,
 	pid = coresight_vpid_to_pid(vpid);
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_pid[drvdata->ctxid_idx] = pid;
-	drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid;
+	config->ctxid_pid[config->ctxid_idx] = pid;
+	config->ctxid_vpid[config->ctxid_idx] = vpid;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -1013,8 +1057,9 @@ static ssize_t ctxid_mask_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->ctxid_mask;
+	val = config->ctxid_mask;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -1025,12 +1070,13 @@ static ssize_t ctxid_mask_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->ctxid_mask = val;
+	config->ctxid_mask = val;
 	return size;
 }
 static DEVICE_ATTR_RW(ctxid_mask);
@@ -1040,8 +1086,9 @@ static ssize_t sync_freq_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->sync_freq;
+	val = config->sync_freq;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -1052,12 +1099,13 @@ static ssize_t sync_freq_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->sync_freq = val & ETM_SYNC_MASK;
+	config->sync_freq = val & ETM_SYNC_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(sync_freq);
@@ -1067,8 +1115,9 @@ static ssize_t timestamp_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->timestamp_event;
+	val = config->timestamp_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -1079,12 +1128,13 @@ static ssize_t timestamp_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->timestamp_event = val & ETM_EVENT_MASK;
+	config->timestamp_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(timestamp_event);
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index c383d218d921..11e4325f02a1 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -41,6 +41,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 /* The number of ETM/PTM currently registered */
 static int etm_count;
 static struct etm_drvdata *etmdrvdata[NR_CPUS];
+static void etm_init_default_data(struct etm_config *config);
 
 /*
  * Memory mapped writes to clear os lock are not supported on some processors
@@ -186,36 +187,39 @@ static void etm_clr_prog(struct etm_drvdata *drvdata)
 	}
 }
 
-void etm_set_default(struct etm_drvdata *drvdata)
+void etm_set_default(struct etm_config *config)
 {
 	int i;
 
-	drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->enable_event = ETM_HARD_WIRE_RES_A;
-
-	drvdata->seq_12_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_21_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_23_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_31_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_32_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_13_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->timestamp_event = ETM_DEFAULT_EVENT_VAL;
-
-	for (i = 0; i < drvdata->nr_cntr; i++) {
-		drvdata->cntr_rld_val[i] = 0x0;
-		drvdata->cntr_event[i] = ETM_DEFAULT_EVENT_VAL;
-		drvdata->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL;
-		drvdata->cntr_val[i] = 0x0;
+	if (WARN_ON_ONCE(!config))
+		return;
+
+	config->trigger_event = ETM_DEFAULT_EVENT_VAL;
+	config->enable_event = ETM_HARD_WIRE_RES_A;
+
+	config->seq_12_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_21_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_23_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_31_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_32_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_13_event = ETM_DEFAULT_EVENT_VAL;
+	config->timestamp_event = ETM_DEFAULT_EVENT_VAL;
+
+	for (i = 0; i < ETM_MAX_CNTR; i++) {
+		config->cntr_rld_val[i] = 0x0;
+		config->cntr_event[i] = ETM_DEFAULT_EVENT_VAL;
+		config->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL;
+		config->cntr_val[i] = 0x0;
 	}
 
-	drvdata->seq_curr_state = 0x0;
-	drvdata->ctxid_idx = 0x0;
-	for (i = 0; i < drvdata->nr_ctxid_cmp; i++) {
-		drvdata->ctxid_pid[i] = 0x0;
-		drvdata->ctxid_vpid[i] = 0x0;
+	config->seq_curr_state = 0x0;
+	config->ctxid_idx = 0x0;
+	for (i = 0; i < ETM_MAX_CTXID_CMP; i++) {
+		config->ctxid_pid[i] = 0x0;
+		config->ctxid_vpid[i] = 0x0;
 	}
 
-	drvdata->ctxid_mask = 0x0;
+	config->ctxid_mask = 0x0;
 }
 
 static void etm_enable_hw(void *info)
@@ -223,6 +227,7 @@ static void etm_enable_hw(void *info)
 	int i;
 	u32 etmcr;
 	struct etm_drvdata *drvdata = info;
+	struct etm_config *config = &drvdata->config;
 
 	CS_UNLOCK(drvdata->base);
 
@@ -238,39 +243,39 @@ static void etm_enable_hw(void *info)
 	etmcr = etm_readl(drvdata, ETMCR);
 	etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG);
 	etmcr |= drvdata->port_size;
-	etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR);
-	etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER);
-	etm_writel(drvdata, drvdata->startstop_ctrl, ETMTSSCR);
-	etm_writel(drvdata, drvdata->enable_event, ETMTEEVR);
-	etm_writel(drvdata, drvdata->enable_ctrl1, ETMTECR1);
-	etm_writel(drvdata, drvdata->fifofull_level, ETMFFLR);
+	etm_writel(drvdata, config->ctrl | etmcr, ETMCR);
+	etm_writel(drvdata, config->trigger_event, ETMTRIGGER);
+	etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR);
+	etm_writel(drvdata, config->enable_event, ETMTEEVR);
+	etm_writel(drvdata, config->enable_ctrl1, ETMTECR1);
+	etm_writel(drvdata, config->fifofull_level, ETMFFLR);
 	for (i = 0; i < drvdata->nr_addr_cmp; i++) {
-		etm_writel(drvdata, drvdata->addr_val[i], ETMACVRn(i));
-		etm_writel(drvdata, drvdata->addr_acctype[i], ETMACTRn(i));
+		etm_writel(drvdata, config->addr_val[i], ETMACVRn(i));
+		etm_writel(drvdata, config->addr_acctype[i], ETMACTRn(i));
 	}
 	for (i = 0; i < drvdata->nr_cntr; i++) {
-		etm_writel(drvdata, drvdata->cntr_rld_val[i], ETMCNTRLDVRn(i));
-		etm_writel(drvdata, drvdata->cntr_event[i], ETMCNTENRn(i));
-		etm_writel(drvdata, drvdata->cntr_rld_event[i],
+		etm_writel(drvdata, config->cntr_rld_val[i], ETMCNTRLDVRn(i));
+		etm_writel(drvdata, config->cntr_event[i], ETMCNTENRn(i));
+		etm_writel(drvdata, config->cntr_rld_event[i],
 			   ETMCNTRLDEVRn(i));
-		etm_writel(drvdata, drvdata->cntr_val[i], ETMCNTVRn(i));
+		etm_writel(drvdata, config->cntr_val[i], ETMCNTVRn(i));
 	}
-	etm_writel(drvdata, drvdata->seq_12_event, ETMSQ12EVR);
-	etm_writel(drvdata, drvdata->seq_21_event, ETMSQ21EVR);
-	etm_writel(drvdata, drvdata->seq_23_event, ETMSQ23EVR);
-	etm_writel(drvdata, drvdata->seq_31_event, ETMSQ31EVR);
-	etm_writel(drvdata, drvdata->seq_32_event, ETMSQ32EVR);
-	etm_writel(drvdata, drvdata->seq_13_event, ETMSQ13EVR);
-	etm_writel(drvdata, drvdata->seq_curr_state, ETMSQR);
+	etm_writel(drvdata, config->seq_12_event, ETMSQ12EVR);
+	etm_writel(drvdata, config->seq_21_event, ETMSQ21EVR);
+	etm_writel(drvdata, config->seq_23_event, ETMSQ23EVR);
+	etm_writel(drvdata, config->seq_31_event, ETMSQ31EVR);
+	etm_writel(drvdata, config->seq_32_event, ETMSQ32EVR);
+	etm_writel(drvdata, config->seq_13_event, ETMSQ13EVR);
+	etm_writel(drvdata, config->seq_curr_state, ETMSQR);
 	for (i = 0; i < drvdata->nr_ext_out; i++)
 		etm_writel(drvdata, ETM_DEFAULT_EVENT_VAL, ETMEXTOUTEVRn(i));
 	for (i = 0; i < drvdata->nr_ctxid_cmp; i++)
-		etm_writel(drvdata, drvdata->ctxid_pid[i], ETMCIDCVRn(i));
-	etm_writel(drvdata, drvdata->ctxid_mask, ETMCIDCMR);
-	etm_writel(drvdata, drvdata->sync_freq, ETMSYNCFR);
+		etm_writel(drvdata, config->ctxid_pid[i], ETMCIDCVRn(i));
+	etm_writel(drvdata, config->ctxid_mask, ETMCIDCMR);
+	etm_writel(drvdata, config->sync_freq, ETMSYNCFR);
 	/* No external input selected */
 	etm_writel(drvdata, 0x0, ETMEXTINSELR);
-	etm_writel(drvdata, drvdata->timestamp_event, ETMTSEVR);
+	etm_writel(drvdata, config->timestamp_event, ETMTSEVR);
 	/* No auxiliary control selected */
 	etm_writel(drvdata, 0x0, ETMAUXCR);
 	etm_writel(drvdata, drvdata->traceid, ETMTRACEIDR);
@@ -278,7 +283,7 @@ static void etm_enable_hw(void *info)
 	etm_writel(drvdata, 0x0, ETMVMIDCVR);
 
 	/* Ensures trace output is enabled from this ETM */
-	etm_writel(drvdata, drvdata->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
+	etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
 
 	etm_clr_prog(drvdata);
 	CS_LOCK(drvdata->base);
@@ -362,6 +367,7 @@ static void etm_disable_hw(void *info)
 {
 	int i;
 	struct etm_drvdata *drvdata = info;
+	struct etm_config *config = &drvdata->config;
 
 	CS_UNLOCK(drvdata->base);
 	etm_set_prog(drvdata);
@@ -370,10 +376,10 @@ static void etm_disable_hw(void *info)
 	etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR);
 
 	/* Read back sequencer and counters for post trace analysis */
-	drvdata->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
+	config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
 
 	for (i = 0; i < drvdata->nr_cntr; i++)
-		drvdata->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i));
+		config->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i));
 
 	etm_set_pwrdwn(drvdata);
 	CS_LOCK(drvdata->base);
@@ -522,14 +528,8 @@ static void etm_init_arch_data(void *info)
 	CS_LOCK(drvdata->base);
 }
 
-static void etm_init_default_data(struct etm_drvdata *drvdata)
+static void etm_init_default_data(struct etm_config *config)
 {
-	/*
-	 * A trace ID of value 0 is invalid, so let's start at some
-	 * random value that fits in 7 bits and will be just as good.
-	 */
-	static int etm3x_traceid = 0x10;
-
 	u32 flags = (1 << 0 | /* instruction execute*/
 		     3 << 3 | /* ARM instruction */
 		     0 << 5 | /* No data value comparison */
@@ -537,25 +537,28 @@ static void etm_init_default_data(struct etm_drvdata *drvdata)
 		     0 << 8 | /* Ignore context ID */
 		     0 << 10); /* Security ignored */
 
+	if (WARN_ON_ONCE(!config))
+		return;
+
+	config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN);
+	config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
+	config->addr_val[0] = (u32) _stext;
+	config->addr_val[1] = (u32) _etext;
+	config->addr_acctype[0] = flags;
+	config->addr_acctype[1] = flags;
+	config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
+	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
+
+	etm_set_default(config);
+}
+
+static void etm_init_trace_id(struct etm_drvdata *drvdata)
+{
 	/*
-	 * Initial configuration only - guarantees sources handled by
-	 * this driver have a unique ID at startup time but not between
-	 * all other types of sources.  For that we lean on the core
-	 * framework.
+	 * A trace ID of value 0 is invalid, so let's start at some
+	 * random value that fits in 7 bits and go from there.
 	 */
-	drvdata->traceid = etm3x_traceid++;
-	drvdata->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN);
-	drvdata->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
-	if (drvdata->nr_addr_cmp >= 2) {
-		drvdata->addr_val[0] = (u32) _stext;
-		drvdata->addr_val[1] = (u32) _etext;
-		drvdata->addr_acctype[0] = flags;
-		drvdata->addr_acctype[1] = flags;
-		drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE;
-		drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE;
-	}
-
-	etm_set_default(drvdata);
+	drvdata->traceid = 0x10 + drvdata->cpu;
 }
 
 static int etm_probe(struct amba_device *adev, const struct amba_id *id)
@@ -623,7 +626,9 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 		ret = -EINVAL;
 		goto err_arch_supported;
 	}
-	etm_init_default_data(drvdata);
+
+	etm_init_trace_id(drvdata);
+	etm_init_default_data(&drvdata->config);
 
 	desc->type = CORESIGHT_DEV_TYPE_SOURCE;
 	desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
-- 
2.1.4

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

* [PATCH V8 07/23] coresight: etm3x: splitting struct etm_drvdata
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Splitting "etm_drvdata" in two sections, one for the HW specific
data and another for user configuration.

That way it is easier to manipulate and zero out the configuration
data when more than one concurrent tracing session configuration
is active.

Also taking care of up-lifting all the code affected by this new
arrangement.  No loss or gain of functionality (other than what is
mentioned above) is introduced by this patch.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h        | 102 ++++----
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    | 290 ++++++++++++---------
 drivers/hwtracing/coresight/coresight-etm3x.c      | 153 +++++------
 3 files changed, 305 insertions(+), 240 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 9a30aa392ed9..371fb7d2e829 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -136,29 +136,9 @@
 #define ETM_DEFAULT_EVENT_VAL	(ETM_HARD_WIRE_RES_A	|	\
 				 ETM_ADD_COMP_0		|	\
 				 ETM_EVENT_NOT_A)
+
 /**
- * struct etm_drvdata - specifics associated to an ETM component
- * @base:	memory mapped base address for this component.
- * @dev:	the device entity associated to this component.
- * @atclk:	optional clock for the core parts of the ETM.
- * @csdev:	component vitals needed by the framework.
- * @spinlock:	only one at a time pls.
- * @cpu:	the cpu this component is affined to.
- * @port_size:	port size as reported by ETMCR bit 4-6 and 21.
- * @arch:	ETM/PTM version number.
- * @use_cpu14:	true if management registers need to be accessed via CP14.
- * @enable:	is this ETM/PTM currently tracing.
- * @sticky_enable: true if ETM base configuration has been done.
- * @boot_enable:true if we should start tracing at boot time.
- * @os_unlock:	true if access to management registers is allowed.
- * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR.
- * @nr_cntr:	Number of counters as found in ETMCCR bit 13-15.
- * @nr_ext_inp:	Number of external input as found in ETMCCR bit 17-19.
- * @nr_ext_out:	Number of external output as found in ETMCCR bit 20-22.
- * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25.
- * @etmccr:	value of register ETMCCR.
- * @etmccer:	value of register ETMCCER.
- * @traceid:	value of the current ID for this component.
+ * struct etm_config - configuration information related to an ETM
  * @mode:	controls various modes supported by this ETM/PTM.
  * @ctrl:	used in conjunction with @mode.
  * @trigger_event: setting for register ETMTRIGGER.
@@ -189,30 +169,9 @@
  * @ctxid_mask: mask applicable to all the context IDs.
  * @sync_freq:	Synchronisation frequency.
  * @timestamp_event: Defines an event that requests the insertion
-		     of a timestamp into the trace stream.
+ *		     of a timestamp into the trace stream.
  */
-struct etm_drvdata {
-	void __iomem			*base;
-	struct device			*dev;
-	struct clk			*atclk;
-	struct coresight_device		*csdev;
-	spinlock_t			spinlock;
-	int				cpu;
-	int				port_size;
-	u8				arch;
-	bool				use_cp14;
-	bool				enable;
-	bool				sticky_enable;
-	bool				boot_enable;
-	bool				os_unlock;
-	u8				nr_addr_cmp;
-	u8				nr_cntr;
-	u8				nr_ext_inp;
-	u8				nr_ext_out;
-	u8				nr_ctxid_cmp;
-	u32				etmccr;
-	u32				etmccer;
-	u32				traceid;
+struct etm_config {
 	u32				mode;
 	u32				ctrl;
 	u32				trigger_event;
@@ -244,6 +203,56 @@ struct etm_drvdata {
 	u32				timestamp_event;
 };
 
+/**
+ * struct etm_drvdata - specifics associated to an ETM component
+ * @base:	memory mapped base address for this component.
+ * @dev:	the device entity associated to this component.
+ * @atclk:	optional clock for the core parts of the ETM.
+ * @csdev:	component vitals needed by the framework.
+ * @spinlock:	only one at a time pls.
+ * @cpu:	the cpu this component is affined to.
+ * @port_size:	port size as reported by ETMCR bit 4-6 and 21.
+ * @arch:	ETM/PTM version number.
+ * @use_cpu14:	true if management registers need to be accessed via CP14.
+ * @enable:	is this ETM/PTM currently tracing.
+ * @sticky_enable: true if ETM base configuration has been done.
+ * @boot_enable:true if we should start tracing at boot time.
+ * @os_unlock:	true if access to management registers is allowed.
+ * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR.
+ * @nr_cntr:	Number of counters as found in ETMCCR bit 13-15.
+ * @nr_ext_inp:	Number of external input as found in ETMCCR bit 17-19.
+ * @nr_ext_out:	Number of external output as found in ETMCCR bit 20-22.
+ * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25.
+ * @etmccr:	value of register ETMCCR.
+ * @etmccer:	value of register ETMCCER.
+ * @traceid:	value of the current ID for this component.
+ * @config:	structure holding configuration parameters.
+ */
+struct etm_drvdata {
+	void __iomem			*base;
+	struct device			*dev;
+	struct clk			*atclk;
+	struct coresight_device		*csdev;
+	spinlock_t			spinlock;
+	int				cpu;
+	int				port_size;
+	u8				arch;
+	bool				use_cp14;
+	bool				enable;
+	bool				sticky_enable;
+	bool				boot_enable;
+	bool				os_unlock;
+	u8				nr_addr_cmp;
+	u8				nr_cntr;
+	u8				nr_ext_inp;
+	u8				nr_ext_out;
+	u8				nr_ctxid_cmp;
+	u32				etmccr;
+	u32				etmccer;
+	u32				traceid;
+	struct etm_config		config;
+};
+
 enum etm_addr_type {
 	ETM_ADDR_TYPE_NONE,
 	ETM_ADDR_TYPE_SINGLE,
@@ -283,5 +292,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
 
 extern const struct attribute_group *coresight_etm_groups[];
 int etm_get_trace_id(struct etm_drvdata *drvdata);
-void etm_set_default(struct etm_drvdata *drvdata);
+void etm_set_default(struct etm_config *config);
+struct etm_config *get_etm_config(struct etm_drvdata *drvdata);
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index f409f5a88e95..456df2378a6f 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -78,6 +78,7 @@ static ssize_t reset_store(struct device *dev,
 	int i, ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -85,19 +86,14 @@ static ssize_t reset_store(struct device *dev,
 
 	if (val) {
 		spin_lock(&drvdata->spinlock);
-		drvdata->mode = ETM_MODE_EXCLUDE;
-		drvdata->ctrl = 0x0;
-		drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
-		drvdata->startstop_ctrl = 0x0;
-		drvdata->addr_idx = 0x0;
+		memset(config, 0, sizeof(struct etm_config));
+		config->mode = ETM_MODE_EXCLUDE;
+		config->trigger_event = ETM_DEFAULT_EVENT_VAL;
 		for (i = 0; i < drvdata->nr_addr_cmp; i++) {
-			drvdata->addr_val[i] = 0x0;
-			drvdata->addr_acctype[i] = 0x0;
-			drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
+			config->addr_type[i] = ETM_ADDR_TYPE_NONE;
 		}
-		drvdata->cntr_idx = 0x0;
 
-		etm_set_default(drvdata);
+		etm_set_default(config);
 		spin_unlock(&drvdata->spinlock);
 	}
 
@@ -110,8 +106,9 @@ static ssize_t mode_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->mode;
+	val = config->mode;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -122,48 +119,49 @@ static ssize_t mode_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->mode = val & ETM_MODE_ALL;
+	config->mode = val & ETM_MODE_ALL;
 
-	if (drvdata->mode & ETM_MODE_EXCLUDE)
-		drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC;
+	if (config->mode & ETM_MODE_EXCLUDE)
+		config->enable_ctrl1 |= ETMTECR1_INC_EXC;
 	else
-		drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
+		config->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
 
-	if (drvdata->mode & ETM_MODE_CYCACC)
-		drvdata->ctrl |= ETMCR_CYC_ACC;
+	if (config->mode & ETM_MODE_CYCACC)
+		config->ctrl |= ETMCR_CYC_ACC;
 	else
-		drvdata->ctrl &= ~ETMCR_CYC_ACC;
+		config->ctrl &= ~ETMCR_CYC_ACC;
 
-	if (drvdata->mode & ETM_MODE_STALL) {
+	if (config->mode & ETM_MODE_STALL) {
 		if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
 			dev_warn(drvdata->dev, "stall mode not supported\n");
 			ret = -EINVAL;
 			goto err_unlock;
 		}
-		drvdata->ctrl |= ETMCR_STALL_MODE;
+		config->ctrl |= ETMCR_STALL_MODE;
 	 } else
-		drvdata->ctrl &= ~ETMCR_STALL_MODE;
+		config->ctrl &= ~ETMCR_STALL_MODE;
 
-	if (drvdata->mode & ETM_MODE_TIMESTAMP) {
+	if (config->mode & ETM_MODE_TIMESTAMP) {
 		if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
 			dev_warn(drvdata->dev, "timestamp not supported\n");
 			ret = -EINVAL;
 			goto err_unlock;
 		}
-		drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
+		config->ctrl |= ETMCR_TIMESTAMP_EN;
 	} else
-		drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN;
+		config->ctrl &= ~ETMCR_TIMESTAMP_EN;
 
-	if (drvdata->mode & ETM_MODE_CTXID)
-		drvdata->ctrl |= ETMCR_CTXID_SIZE;
+	if (config->mode & ETM_MODE_CTXID)
+		config->ctrl |= ETMCR_CTXID_SIZE;
 	else
-		drvdata->ctrl &= ~ETMCR_CTXID_SIZE;
+		config->ctrl &= ~ETMCR_CTXID_SIZE;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -179,8 +177,9 @@ static ssize_t trigger_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->trigger_event;
+	val = config->trigger_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -191,12 +190,13 @@ static ssize_t trigger_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->trigger_event = val & ETM_EVENT_MASK;
+	config->trigger_event = val & ETM_EVENT_MASK;
 
 	return size;
 }
@@ -207,8 +207,9 @@ static ssize_t enable_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->enable_event;
+	val = config->enable_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -219,12 +220,13 @@ static ssize_t enable_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->enable_event = val & ETM_EVENT_MASK;
+	config->enable_event = val & ETM_EVENT_MASK;
 
 	return size;
 }
@@ -235,8 +237,9 @@ static ssize_t fifofull_level_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->fifofull_level;
+	val = config->fifofull_level;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -247,12 +250,13 @@ static ssize_t fifofull_level_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->fifofull_level = val;
+	config->fifofull_level = val;
 
 	return size;
 }
@@ -263,8 +267,9 @@ static ssize_t addr_idx_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->addr_idx;
+	val = config->addr_idx;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -275,6 +280,7 @@ static ssize_t addr_idx_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -288,7 +294,7 @@ static ssize_t addr_idx_store(struct device *dev,
 	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
 	spin_lock(&drvdata->spinlock);
-	drvdata->addr_idx = val;
+	config->addr_idx = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -301,16 +307,17 @@ static ssize_t addr_single_show(struct device *dev,
 	u8 idx;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EINVAL;
 	}
 
-	val = drvdata->addr_val[idx];
+	val = config->addr_val[idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -324,21 +331,22 @@ static ssize_t addr_single_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EINVAL;
 	}
 
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
+	config->addr_val[idx] = val;
+	config->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -351,23 +359,24 @@ static ssize_t addr_range_show(struct device *dev,
 	u8 idx;
 	unsigned long val1, val2;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
+	idx = config->addr_idx;
 	if (idx % 2 != 0) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+	if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	val1 = drvdata->addr_val[idx];
-	val2 = drvdata->addr_val[idx + 1];
+	val1 = config->addr_val[idx];
+	val2 = config->addr_val[idx + 1];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx %#lx\n", val1, val2);
@@ -380,6 +389,7 @@ static ssize_t addr_range_store(struct device *dev,
 	u8 idx;
 	unsigned long val1, val2;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
 		return -EINVAL;
@@ -388,24 +398,24 @@ static ssize_t addr_range_store(struct device *dev,
 		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
+	idx = config->addr_idx;
 	if (idx % 2 != 0) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+	if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	drvdata->addr_val[idx] = val1;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
-	drvdata->addr_val[idx + 1] = val2;
-	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
-	drvdata->enable_ctrl1 |= (1 << (idx/2));
+	config->addr_val[idx] = val1;
+	config->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
+	config->addr_val[idx + 1] = val2;
+	config->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
+	config->enable_ctrl1 |= (1 << (idx/2));
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -418,16 +428,17 @@ static ssize_t addr_start_show(struct device *dev,
 	u8 idx;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_START)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	val = drvdata->addr_val[idx];
+	val = config->addr_val[idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -441,23 +452,24 @@ static ssize_t addr_start_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_START)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
-	drvdata->startstop_ctrl |= (1 << idx);
-	drvdata->enable_ctrl1 |= BIT(25);
+	config->addr_val[idx] = val;
+	config->addr_type[idx] = ETM_ADDR_TYPE_START;
+	config->startstop_ctrl |= (1 << idx);
+	config->enable_ctrl1 |= BIT(25);
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -470,16 +482,17 @@ static ssize_t addr_stop_show(struct device *dev,
 	u8 idx;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	val = drvdata->addr_val[idx];
+	val = config->addr_val[idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -493,23 +506,24 @@ static ssize_t addr_stop_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
-	drvdata->startstop_ctrl |= (1 << (idx + 16));
-	drvdata->enable_ctrl1 |= ETMTECR1_START_STOP;
+	config->addr_val[idx] = val;
+	config->addr_type[idx] = ETM_ADDR_TYPE_STOP;
+	config->startstop_ctrl |= (1 << (idx + 16));
+	config->enable_ctrl1 |= ETMTECR1_START_STOP;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -521,9 +535,10 @@ static ssize_t addr_acctype_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->addr_acctype[drvdata->addr_idx];
+	val = config->addr_acctype[config->addr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -536,13 +551,14 @@ static ssize_t addr_acctype_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->addr_acctype[drvdata->addr_idx] = val;
+	config->addr_acctype[config->addr_idx] = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -554,8 +570,9 @@ static ssize_t cntr_idx_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->cntr_idx;
+	val = config->cntr_idx;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -566,6 +583,7 @@ static ssize_t cntr_idx_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -578,7 +596,7 @@ static ssize_t cntr_idx_store(struct device *dev,
 	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_idx = val;
+	config->cntr_idx = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -590,9 +608,10 @@ static ssize_t cntr_rld_val_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
+	val = config->cntr_rld_val[config->cntr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -605,13 +624,14 @@ static ssize_t cntr_rld_val_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
+	config->cntr_rld_val[config->cntr_idx] = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -623,9 +643,10 @@ static ssize_t cntr_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_event[drvdata->cntr_idx];
+	val = config->cntr_event[config->cntr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -638,13 +659,14 @@ static ssize_t cntr_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	config->cntr_event[config->cntr_idx] = val & ETM_EVENT_MASK;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -656,9 +678,10 @@ static ssize_t cntr_rld_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
+	val = config->cntr_rld_event[config->cntr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -671,13 +694,14 @@ static ssize_t cntr_rld_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	config->cntr_rld_event[config->cntr_idx] = val & ETM_EVENT_MASK;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -690,12 +714,13 @@ static ssize_t cntr_val_show(struct device *dev,
 	int i, ret = 0;
 	u32 val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	if (!drvdata->enable) {
 		spin_lock(&drvdata->spinlock);
 		for (i = 0; i < drvdata->nr_cntr; i++)
 			ret += sprintf(buf, "counter %d: %x\n",
-				       i, drvdata->cntr_val[i]);
+				       i, config->cntr_val[i]);
 		spin_unlock(&drvdata->spinlock);
 		return ret;
 	}
@@ -715,13 +740,14 @@ static ssize_t cntr_val_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_val[drvdata->cntr_idx] = val;
+	config->cntr_val[config->cntr_idx] = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -733,8 +759,9 @@ static ssize_t seq_12_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_12_event;
+	val = config->seq_12_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -745,12 +772,13 @@ static ssize_t seq_12_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_12_event = val & ETM_EVENT_MASK;
+	config->seq_12_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_12_event);
@@ -760,8 +788,9 @@ static ssize_t seq_21_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_21_event;
+	val = config->seq_21_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -772,12 +801,13 @@ static ssize_t seq_21_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_21_event = val & ETM_EVENT_MASK;
+	config->seq_21_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_21_event);
@@ -787,8 +817,9 @@ static ssize_t seq_23_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_23_event;
+	val = config->seq_23_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -799,12 +830,13 @@ static ssize_t seq_23_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_23_event = val & ETM_EVENT_MASK;
+	config->seq_23_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_23_event);
@@ -814,8 +846,9 @@ static ssize_t seq_31_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_31_event;
+	val = config->seq_31_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -826,12 +859,13 @@ static ssize_t seq_31_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_31_event = val & ETM_EVENT_MASK;
+	config->seq_31_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_31_event);
@@ -841,8 +875,9 @@ static ssize_t seq_32_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_32_event;
+	val = config->seq_32_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -853,12 +888,13 @@ static ssize_t seq_32_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_32_event = val & ETM_EVENT_MASK;
+	config->seq_32_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_32_event);
@@ -868,8 +904,9 @@ static ssize_t seq_13_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->seq_13_event;
+	val = config->seq_13_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -880,12 +917,13 @@ static ssize_t seq_13_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_13_event = val & ETM_EVENT_MASK;
+	config->seq_13_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_13_event);
@@ -895,9 +933,10 @@ static ssize_t seq_curr_state_show(struct device *dev,
 {
 	unsigned long val, flags;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	if (!drvdata->enable) {
-		val = drvdata->seq_curr_state;
+		val = config->seq_curr_state;
 		goto out;
 	}
 
@@ -921,6 +960,7 @@ static ssize_t seq_curr_state_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -929,7 +969,7 @@ static ssize_t seq_curr_state_store(struct device *dev,
 	if (val > ETM_SEQ_STATE_MAX_VAL)
 		return -EINVAL;
 
-	drvdata->seq_curr_state = val;
+	config->seq_curr_state = val;
 
 	return size;
 }
@@ -940,8 +980,9 @@ static ssize_t ctxid_idx_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->ctxid_idx;
+	val = config->ctxid_idx;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -952,6 +993,7 @@ static ssize_t ctxid_idx_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -965,7 +1007,7 @@ static ssize_t ctxid_idx_store(struct device *dev,
 	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
 	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_idx = val;
+	config->ctxid_idx = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -977,9 +1019,10 @@ static ssize_t ctxid_pid_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->ctxid_vpid[drvdata->ctxid_idx];
+	val = config->ctxid_vpid[config->ctxid_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -992,6 +1035,7 @@ static ssize_t ctxid_pid_store(struct device *dev,
 	int ret;
 	unsigned long vpid, pid;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &vpid);
 	if (ret)
@@ -1000,8 +1044,8 @@ static ssize_t ctxid_pid_store(struct device *dev,
 	pid = coresight_vpid_to_pid(vpid);
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_pid[drvdata->ctxid_idx] = pid;
-	drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid;
+	config->ctxid_pid[config->ctxid_idx] = pid;
+	config->ctxid_vpid[config->ctxid_idx] = vpid;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -1013,8 +1057,9 @@ static ssize_t ctxid_mask_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->ctxid_mask;
+	val = config->ctxid_mask;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -1025,12 +1070,13 @@ static ssize_t ctxid_mask_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->ctxid_mask = val;
+	config->ctxid_mask = val;
 	return size;
 }
 static DEVICE_ATTR_RW(ctxid_mask);
@@ -1040,8 +1086,9 @@ static ssize_t sync_freq_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->sync_freq;
+	val = config->sync_freq;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -1052,12 +1099,13 @@ static ssize_t sync_freq_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->sync_freq = val & ETM_SYNC_MASK;
+	config->sync_freq = val & ETM_SYNC_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(sync_freq);
@@ -1067,8 +1115,9 @@ static ssize_t timestamp_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
-	val = drvdata->timestamp_event;
+	val = config->timestamp_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -1079,12 +1128,13 @@ static ssize_t timestamp_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = &drvdata->config;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->timestamp_event = val & ETM_EVENT_MASK;
+	config->timestamp_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(timestamp_event);
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index c383d218d921..11e4325f02a1 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -41,6 +41,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 /* The number of ETM/PTM currently registered */
 static int etm_count;
 static struct etm_drvdata *etmdrvdata[NR_CPUS];
+static void etm_init_default_data(struct etm_config *config);
 
 /*
  * Memory mapped writes to clear os lock are not supported on some processors
@@ -186,36 +187,39 @@ static void etm_clr_prog(struct etm_drvdata *drvdata)
 	}
 }
 
-void etm_set_default(struct etm_drvdata *drvdata)
+void etm_set_default(struct etm_config *config)
 {
 	int i;
 
-	drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->enable_event = ETM_HARD_WIRE_RES_A;
-
-	drvdata->seq_12_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_21_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_23_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_31_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_32_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_13_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->timestamp_event = ETM_DEFAULT_EVENT_VAL;
-
-	for (i = 0; i < drvdata->nr_cntr; i++) {
-		drvdata->cntr_rld_val[i] = 0x0;
-		drvdata->cntr_event[i] = ETM_DEFAULT_EVENT_VAL;
-		drvdata->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL;
-		drvdata->cntr_val[i] = 0x0;
+	if (WARN_ON_ONCE(!config))
+		return;
+
+	config->trigger_event = ETM_DEFAULT_EVENT_VAL;
+	config->enable_event = ETM_HARD_WIRE_RES_A;
+
+	config->seq_12_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_21_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_23_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_31_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_32_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_13_event = ETM_DEFAULT_EVENT_VAL;
+	config->timestamp_event = ETM_DEFAULT_EVENT_VAL;
+
+	for (i = 0; i < ETM_MAX_CNTR; i++) {
+		config->cntr_rld_val[i] = 0x0;
+		config->cntr_event[i] = ETM_DEFAULT_EVENT_VAL;
+		config->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL;
+		config->cntr_val[i] = 0x0;
 	}
 
-	drvdata->seq_curr_state = 0x0;
-	drvdata->ctxid_idx = 0x0;
-	for (i = 0; i < drvdata->nr_ctxid_cmp; i++) {
-		drvdata->ctxid_pid[i] = 0x0;
-		drvdata->ctxid_vpid[i] = 0x0;
+	config->seq_curr_state = 0x0;
+	config->ctxid_idx = 0x0;
+	for (i = 0; i < ETM_MAX_CTXID_CMP; i++) {
+		config->ctxid_pid[i] = 0x0;
+		config->ctxid_vpid[i] = 0x0;
 	}
 
-	drvdata->ctxid_mask = 0x0;
+	config->ctxid_mask = 0x0;
 }
 
 static void etm_enable_hw(void *info)
@@ -223,6 +227,7 @@ static void etm_enable_hw(void *info)
 	int i;
 	u32 etmcr;
 	struct etm_drvdata *drvdata = info;
+	struct etm_config *config = &drvdata->config;
 
 	CS_UNLOCK(drvdata->base);
 
@@ -238,39 +243,39 @@ static void etm_enable_hw(void *info)
 	etmcr = etm_readl(drvdata, ETMCR);
 	etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG);
 	etmcr |= drvdata->port_size;
-	etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR);
-	etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER);
-	etm_writel(drvdata, drvdata->startstop_ctrl, ETMTSSCR);
-	etm_writel(drvdata, drvdata->enable_event, ETMTEEVR);
-	etm_writel(drvdata, drvdata->enable_ctrl1, ETMTECR1);
-	etm_writel(drvdata, drvdata->fifofull_level, ETMFFLR);
+	etm_writel(drvdata, config->ctrl | etmcr, ETMCR);
+	etm_writel(drvdata, config->trigger_event, ETMTRIGGER);
+	etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR);
+	etm_writel(drvdata, config->enable_event, ETMTEEVR);
+	etm_writel(drvdata, config->enable_ctrl1, ETMTECR1);
+	etm_writel(drvdata, config->fifofull_level, ETMFFLR);
 	for (i = 0; i < drvdata->nr_addr_cmp; i++) {
-		etm_writel(drvdata, drvdata->addr_val[i], ETMACVRn(i));
-		etm_writel(drvdata, drvdata->addr_acctype[i], ETMACTRn(i));
+		etm_writel(drvdata, config->addr_val[i], ETMACVRn(i));
+		etm_writel(drvdata, config->addr_acctype[i], ETMACTRn(i));
 	}
 	for (i = 0; i < drvdata->nr_cntr; i++) {
-		etm_writel(drvdata, drvdata->cntr_rld_val[i], ETMCNTRLDVRn(i));
-		etm_writel(drvdata, drvdata->cntr_event[i], ETMCNTENRn(i));
-		etm_writel(drvdata, drvdata->cntr_rld_event[i],
+		etm_writel(drvdata, config->cntr_rld_val[i], ETMCNTRLDVRn(i));
+		etm_writel(drvdata, config->cntr_event[i], ETMCNTENRn(i));
+		etm_writel(drvdata, config->cntr_rld_event[i],
 			   ETMCNTRLDEVRn(i));
-		etm_writel(drvdata, drvdata->cntr_val[i], ETMCNTVRn(i));
+		etm_writel(drvdata, config->cntr_val[i], ETMCNTVRn(i));
 	}
-	etm_writel(drvdata, drvdata->seq_12_event, ETMSQ12EVR);
-	etm_writel(drvdata, drvdata->seq_21_event, ETMSQ21EVR);
-	etm_writel(drvdata, drvdata->seq_23_event, ETMSQ23EVR);
-	etm_writel(drvdata, drvdata->seq_31_event, ETMSQ31EVR);
-	etm_writel(drvdata, drvdata->seq_32_event, ETMSQ32EVR);
-	etm_writel(drvdata, drvdata->seq_13_event, ETMSQ13EVR);
-	etm_writel(drvdata, drvdata->seq_curr_state, ETMSQR);
+	etm_writel(drvdata, config->seq_12_event, ETMSQ12EVR);
+	etm_writel(drvdata, config->seq_21_event, ETMSQ21EVR);
+	etm_writel(drvdata, config->seq_23_event, ETMSQ23EVR);
+	etm_writel(drvdata, config->seq_31_event, ETMSQ31EVR);
+	etm_writel(drvdata, config->seq_32_event, ETMSQ32EVR);
+	etm_writel(drvdata, config->seq_13_event, ETMSQ13EVR);
+	etm_writel(drvdata, config->seq_curr_state, ETMSQR);
 	for (i = 0; i < drvdata->nr_ext_out; i++)
 		etm_writel(drvdata, ETM_DEFAULT_EVENT_VAL, ETMEXTOUTEVRn(i));
 	for (i = 0; i < drvdata->nr_ctxid_cmp; i++)
-		etm_writel(drvdata, drvdata->ctxid_pid[i], ETMCIDCVRn(i));
-	etm_writel(drvdata, drvdata->ctxid_mask, ETMCIDCMR);
-	etm_writel(drvdata, drvdata->sync_freq, ETMSYNCFR);
+		etm_writel(drvdata, config->ctxid_pid[i], ETMCIDCVRn(i));
+	etm_writel(drvdata, config->ctxid_mask, ETMCIDCMR);
+	etm_writel(drvdata, config->sync_freq, ETMSYNCFR);
 	/* No external input selected */
 	etm_writel(drvdata, 0x0, ETMEXTINSELR);
-	etm_writel(drvdata, drvdata->timestamp_event, ETMTSEVR);
+	etm_writel(drvdata, config->timestamp_event, ETMTSEVR);
 	/* No auxiliary control selected */
 	etm_writel(drvdata, 0x0, ETMAUXCR);
 	etm_writel(drvdata, drvdata->traceid, ETMTRACEIDR);
@@ -278,7 +283,7 @@ static void etm_enable_hw(void *info)
 	etm_writel(drvdata, 0x0, ETMVMIDCVR);
 
 	/* Ensures trace output is enabled from this ETM */
-	etm_writel(drvdata, drvdata->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
+	etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
 
 	etm_clr_prog(drvdata);
 	CS_LOCK(drvdata->base);
@@ -362,6 +367,7 @@ static void etm_disable_hw(void *info)
 {
 	int i;
 	struct etm_drvdata *drvdata = info;
+	struct etm_config *config = &drvdata->config;
 
 	CS_UNLOCK(drvdata->base);
 	etm_set_prog(drvdata);
@@ -370,10 +376,10 @@ static void etm_disable_hw(void *info)
 	etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR);
 
 	/* Read back sequencer and counters for post trace analysis */
-	drvdata->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
+	config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
 
 	for (i = 0; i < drvdata->nr_cntr; i++)
-		drvdata->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i));
+		config->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i));
 
 	etm_set_pwrdwn(drvdata);
 	CS_LOCK(drvdata->base);
@@ -522,14 +528,8 @@ static void etm_init_arch_data(void *info)
 	CS_LOCK(drvdata->base);
 }
 
-static void etm_init_default_data(struct etm_drvdata *drvdata)
+static void etm_init_default_data(struct etm_config *config)
 {
-	/*
-	 * A trace ID of value 0 is invalid, so let's start at some
-	 * random value that fits in 7 bits and will be just as good.
-	 */
-	static int etm3x_traceid = 0x10;
-
 	u32 flags = (1 << 0 | /* instruction execute*/
 		     3 << 3 | /* ARM instruction */
 		     0 << 5 | /* No data value comparison */
@@ -537,25 +537,28 @@ static void etm_init_default_data(struct etm_drvdata *drvdata)
 		     0 << 8 | /* Ignore context ID */
 		     0 << 10); /* Security ignored */
 
+	if (WARN_ON_ONCE(!config))
+		return;
+
+	config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN);
+	config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
+	config->addr_val[0] = (u32) _stext;
+	config->addr_val[1] = (u32) _etext;
+	config->addr_acctype[0] = flags;
+	config->addr_acctype[1] = flags;
+	config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
+	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
+
+	etm_set_default(config);
+}
+
+static void etm_init_trace_id(struct etm_drvdata *drvdata)
+{
 	/*
-	 * Initial configuration only - guarantees sources handled by
-	 * this driver have a unique ID at startup time but not between
-	 * all other types of sources.  For that we lean on the core
-	 * framework.
+	 * A trace ID of value 0 is invalid, so let's start at some
+	 * random value that fits in 7 bits and go from there.
 	 */
-	drvdata->traceid = etm3x_traceid++;
-	drvdata->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN);
-	drvdata->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
-	if (drvdata->nr_addr_cmp >= 2) {
-		drvdata->addr_val[0] = (u32) _stext;
-		drvdata->addr_val[1] = (u32) _etext;
-		drvdata->addr_acctype[0] = flags;
-		drvdata->addr_acctype[1] = flags;
-		drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE;
-		drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE;
-	}
-
-	etm_set_default(drvdata);
+	drvdata->traceid = 0x10 + drvdata->cpu;
 }
 
 static int etm_probe(struct amba_device *adev, const struct amba_id *id)
@@ -623,7 +626,9 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 		ret = -EINVAL;
 		goto err_arch_supported;
 	}
-	etm_init_default_data(drvdata);
+
+	etm_init_trace_id(drvdata);
+	etm_init_default_data(&drvdata->config);
 
 	desc->type = CORESIGHT_DEV_TYPE_SOURCE;
 	desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
-- 
2.1.4

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

* [PATCH V8 08/23] coresight: etm3x: adding operation mode for etm_enable()
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Adding a new mode to source API enable() in order to
distinguish where the request comes from.  That way it is
possible to perform different operations based on where
the request was issued from.

The ETM4x driver is also modified to keep in sync with the
new interface.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h        |  5 +-
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    |  4 +-
 drivers/hwtracing/coresight/coresight-etm3x.c      | 68 +++++++++++++++++++---
 drivers/hwtracing/coresight/coresight-etm4x.c      |  2 +-
 drivers/hwtracing/coresight/coresight-priv.h       |  6 ++
 drivers/hwtracing/coresight/coresight.c            |  6 +-
 include/linux/coresight.h                          |  2 +-
 7 files changed, 76 insertions(+), 17 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 371fb7d2e829..5b29d5540fe5 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -13,6 +13,7 @@
 #ifndef _CORESIGHT_CORESIGHT_ETM_H
 #define _CORESIGHT_CORESIGHT_ETM_H
 
+#include <asm/local.h>
 #include <linux/spinlock.h>
 #include "coresight-priv.h"
 
@@ -214,7 +215,7 @@ struct etm_config {
  * @port_size:	port size as reported by ETMCR bit 4-6 and 21.
  * @arch:	ETM/PTM version number.
  * @use_cpu14:	true if management registers need to be accessed via CP14.
- * @enable:	is this ETM/PTM currently tracing.
+ * @mode:	this tracer's mode, i.e sysFS, Perf or disabled.
  * @sticky_enable: true if ETM base configuration has been done.
  * @boot_enable:true if we should start tracing at boot time.
  * @os_unlock:	true if access to management registers is allowed.
@@ -238,7 +239,7 @@ struct etm_drvdata {
 	int				port_size;
 	u8				arch;
 	bool				use_cp14;
-	bool				enable;
+	local_t				mode;
 	bool				sticky_enable;
 	bool				boot_enable;
 	bool				os_unlock;
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index 456df2378a6f..387c79fd9d5e 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -716,7 +716,7 @@ static ssize_t cntr_val_show(struct device *dev,
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	struct etm_config *config = &drvdata->config;
 
-	if (!drvdata->enable) {
+	if (!local_read(&drvdata->mode)) {
 		spin_lock(&drvdata->spinlock);
 		for (i = 0; i < drvdata->nr_cntr; i++)
 			ret += sprintf(buf, "counter %d: %x\n",
@@ -935,7 +935,7 @@ static ssize_t seq_curr_state_show(struct device *dev,
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	struct etm_config *config = &drvdata->config;
 
-	if (!drvdata->enable) {
+	if (!local_read(&drvdata->mode)) {
 		val = config->seq_curr_state;
 		goto out;
 	}
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 11e4325f02a1..4db5147680f6 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -306,7 +306,7 @@ int etm_get_trace_id(struct etm_drvdata *drvdata)
 	if (!drvdata)
 		goto out;
 
-	if (!drvdata->enable)
+	if (!local_read(&drvdata->mode))
 		return drvdata->traceid;
 
 	pm_runtime_get_sync(drvdata->dev);
@@ -332,7 +332,7 @@ static int etm_trace_id(struct coresight_device *csdev)
 	return etm_get_trace_id(drvdata);
 }
 
-static int etm_enable(struct coresight_device *csdev)
+static int etm_enable_sysfs(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
@@ -351,18 +351,44 @@ static int etm_enable(struct coresight_device *csdev)
 			goto err;
 	}
 
-	drvdata->enable = true;
 	drvdata->sticky_enable = true;
-
 	spin_unlock(&drvdata->spinlock);
 
 	dev_info(drvdata->dev, "ETM tracing enabled\n");
 	return 0;
+
 err:
 	spin_unlock(&drvdata->spinlock);
 	return ret;
 }
 
+static int etm_enable(struct coresight_device *csdev, u32 mode)
+{
+	int ret;
+	u32 val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+
+	/* Someone is already using the tracer */
+	if (val)
+		return -EBUSY;
+
+	switch (mode) {
+	case CS_MODE_SYSFS:
+		ret = etm_enable_sysfs(csdev);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	/* The tracer didn't start */
+	if (ret)
+		local_set(&drvdata->mode, CS_MODE_DISABLED);
+
+	return ret;
+}
+
 static void etm_disable_hw(void *info)
 {
 	int i;
@@ -387,7 +413,7 @@ static void etm_disable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
 }
 
-static void etm_disable(struct coresight_device *csdev)
+static void etm_disable_sysfs(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -405,7 +431,6 @@ static void etm_disable(struct coresight_device *csdev)
 	 * ensures that register writes occur when cpu is powered.
 	 */
 	smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1);
-	drvdata->enable = false;
 
 	spin_unlock(&drvdata->spinlock);
 	put_online_cpus();
@@ -413,6 +438,33 @@ static void etm_disable(struct coresight_device *csdev)
 	dev_info(drvdata->dev, "ETM tracing disabled\n");
 }
 
+static void etm_disable(struct coresight_device *csdev)
+{
+	u32 mode;
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	/*
+	 * For as long as the tracer isn't disabled another entity can't
+	 * change its status.  As such we can read the status here without
+	 * fearing it will change under us.
+	 */
+	mode = local_read(&drvdata->mode);
+
+	switch (mode) {
+	case CS_MODE_DISABLED:
+		break;
+	case CS_MODE_SYSFS:
+		etm_disable_sysfs(csdev);
+		break;
+	default:
+		WARN_ON_ONCE(mode);
+		return;
+	}
+
+	if (mode)
+		local_set(&drvdata->mode, CS_MODE_DISABLED);
+}
+
 static const struct coresight_ops_source etm_source_ops = {
 	.cpu_id		= etm_cpu_id,
 	.trace_id	= etm_trace_id,
@@ -440,7 +492,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 			etmdrvdata[cpu]->os_unlock = true;
 		}
 
-		if (etmdrvdata[cpu]->enable)
+		if (local_read(&etmdrvdata[cpu]->mode))
 			etm_enable_hw(etmdrvdata[cpu]);
 		spin_unlock(&etmdrvdata[cpu]->spinlock);
 		break;
@@ -453,7 +505,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 
 	case CPU_DYING:
 		spin_lock(&etmdrvdata[cpu]->spinlock);
-		if (etmdrvdata[cpu]->enable)
+		if (local_read(&etmdrvdata[cpu]->mode))
 			etm_disable_hw(etmdrvdata[cpu]);
 		spin_unlock(&etmdrvdata[cpu]->spinlock);
 		break;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 1c6e32dd6e49..4ab291b3a6c8 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -180,7 +180,7 @@ static void etm4_enable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
 }
 
-static int etm4_enable(struct coresight_device *csdev)
+static int etm4_enable(struct coresight_device *csdev, u32 mode)
 {
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 14f245a2018d..ed116b303e87 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -34,6 +34,12 @@
 #define TIMEOUT_US		100
 #define BMVAL(val, lsb, msb)	((val & GENMASK(msb, lsb)) >> lsb)
 
+enum cs_mode {
+	CS_MODE_DISABLED,
+	CS_MODE_SYSFS,
+	CS_MODE_PERF,
+};
+
 static inline void CS_LOCK(void __iomem *addr)
 {
 	do {
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 6b44928c1076..b20afb709141 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -222,7 +222,7 @@ static void coresight_disable_link(struct coresight_device *csdev,
 	csdev->enable = false;
 }
 
-static int coresight_enable_source(struct coresight_device *csdev)
+static int coresight_enable_source(struct coresight_device *csdev, u32 mode)
 {
 	int ret;
 
@@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev)
 
 	if (!csdev->enable) {
 		if (source_ops(csdev)->enable) {
-			ret = source_ops(csdev)->enable(csdev);
+			ret = source_ops(csdev)->enable(csdev, mode);
 			if (ret)
 				return ret;
 		}
@@ -458,7 +458,7 @@ int coresight_enable(struct coresight_device *csdev)
 	if (ret)
 		goto err_path;
 
-	ret = coresight_enable_source(csdev);
+	ret = coresight_enable_source(csdev, CS_MODE_SYSFS);
 	if (ret)
 		goto err_source;
 
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 851ecb22397e..61dfb8d511ea 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -213,7 +213,7 @@ struct coresight_ops_link {
 struct coresight_ops_source {
 	int (*cpu_id)(struct coresight_device *csdev);
 	int (*trace_id)(struct coresight_device *csdev);
-	int (*enable)(struct coresight_device *csdev);
+	int (*enable)(struct coresight_device *csdev, u32 mode);
 	void (*disable)(struct coresight_device *csdev);
 };
 
-- 
2.1.4

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

* [PATCH V8 08/23] coresight: etm3x: adding operation mode for etm_enable()
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Adding a new mode to source API enable() in order to
distinguish where the request comes from.  That way it is
possible to perform different operations based on where
the request was issued from.

The ETM4x driver is also modified to keep in sync with the
new interface.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h        |  5 +-
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    |  4 +-
 drivers/hwtracing/coresight/coresight-etm3x.c      | 68 +++++++++++++++++++---
 drivers/hwtracing/coresight/coresight-etm4x.c      |  2 +-
 drivers/hwtracing/coresight/coresight-priv.h       |  6 ++
 drivers/hwtracing/coresight/coresight.c            |  6 +-
 include/linux/coresight.h                          |  2 +-
 7 files changed, 76 insertions(+), 17 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 371fb7d2e829..5b29d5540fe5 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -13,6 +13,7 @@
 #ifndef _CORESIGHT_CORESIGHT_ETM_H
 #define _CORESIGHT_CORESIGHT_ETM_H
 
+#include <asm/local.h>
 #include <linux/spinlock.h>
 #include "coresight-priv.h"
 
@@ -214,7 +215,7 @@ struct etm_config {
  * @port_size:	port size as reported by ETMCR bit 4-6 and 21.
  * @arch:	ETM/PTM version number.
  * @use_cpu14:	true if management registers need to be accessed via CP14.
- * @enable:	is this ETM/PTM currently tracing.
+ * @mode:	this tracer's mode, i.e sysFS, Perf or disabled.
  * @sticky_enable: true if ETM base configuration has been done.
  * @boot_enable:true if we should start tracing at boot time.
  * @os_unlock:	true if access to management registers is allowed.
@@ -238,7 +239,7 @@ struct etm_drvdata {
 	int				port_size;
 	u8				arch;
 	bool				use_cp14;
-	bool				enable;
+	local_t				mode;
 	bool				sticky_enable;
 	bool				boot_enable;
 	bool				os_unlock;
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index 456df2378a6f..387c79fd9d5e 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -716,7 +716,7 @@ static ssize_t cntr_val_show(struct device *dev,
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	struct etm_config *config = &drvdata->config;
 
-	if (!drvdata->enable) {
+	if (!local_read(&drvdata->mode)) {
 		spin_lock(&drvdata->spinlock);
 		for (i = 0; i < drvdata->nr_cntr; i++)
 			ret += sprintf(buf, "counter %d: %x\n",
@@ -935,7 +935,7 @@ static ssize_t seq_curr_state_show(struct device *dev,
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	struct etm_config *config = &drvdata->config;
 
-	if (!drvdata->enable) {
+	if (!local_read(&drvdata->mode)) {
 		val = config->seq_curr_state;
 		goto out;
 	}
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 11e4325f02a1..4db5147680f6 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -306,7 +306,7 @@ int etm_get_trace_id(struct etm_drvdata *drvdata)
 	if (!drvdata)
 		goto out;
 
-	if (!drvdata->enable)
+	if (!local_read(&drvdata->mode))
 		return drvdata->traceid;
 
 	pm_runtime_get_sync(drvdata->dev);
@@ -332,7 +332,7 @@ static int etm_trace_id(struct coresight_device *csdev)
 	return etm_get_trace_id(drvdata);
 }
 
-static int etm_enable(struct coresight_device *csdev)
+static int etm_enable_sysfs(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
@@ -351,18 +351,44 @@ static int etm_enable(struct coresight_device *csdev)
 			goto err;
 	}
 
-	drvdata->enable = true;
 	drvdata->sticky_enable = true;
-
 	spin_unlock(&drvdata->spinlock);
 
 	dev_info(drvdata->dev, "ETM tracing enabled\n");
 	return 0;
+
 err:
 	spin_unlock(&drvdata->spinlock);
 	return ret;
 }
 
+static int etm_enable(struct coresight_device *csdev, u32 mode)
+{
+	int ret;
+	u32 val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+
+	/* Someone is already using the tracer */
+	if (val)
+		return -EBUSY;
+
+	switch (mode) {
+	case CS_MODE_SYSFS:
+		ret = etm_enable_sysfs(csdev);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	/* The tracer didn't start */
+	if (ret)
+		local_set(&drvdata->mode, CS_MODE_DISABLED);
+
+	return ret;
+}
+
 static void etm_disable_hw(void *info)
 {
 	int i;
@@ -387,7 +413,7 @@ static void etm_disable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
 }
 
-static void etm_disable(struct coresight_device *csdev)
+static void etm_disable_sysfs(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -405,7 +431,6 @@ static void etm_disable(struct coresight_device *csdev)
 	 * ensures that register writes occur when cpu is powered.
 	 */
 	smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1);
-	drvdata->enable = false;
 
 	spin_unlock(&drvdata->spinlock);
 	put_online_cpus();
@@ -413,6 +438,33 @@ static void etm_disable(struct coresight_device *csdev)
 	dev_info(drvdata->dev, "ETM tracing disabled\n");
 }
 
+static void etm_disable(struct coresight_device *csdev)
+{
+	u32 mode;
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	/*
+	 * For as long as the tracer isn't disabled another entity can't
+	 * change its status.  As such we can read the status here without
+	 * fearing it will change under us.
+	 */
+	mode = local_read(&drvdata->mode);
+
+	switch (mode) {
+	case CS_MODE_DISABLED:
+		break;
+	case CS_MODE_SYSFS:
+		etm_disable_sysfs(csdev);
+		break;
+	default:
+		WARN_ON_ONCE(mode);
+		return;
+	}
+
+	if (mode)
+		local_set(&drvdata->mode, CS_MODE_DISABLED);
+}
+
 static const struct coresight_ops_source etm_source_ops = {
 	.cpu_id		= etm_cpu_id,
 	.trace_id	= etm_trace_id,
@@ -440,7 +492,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 			etmdrvdata[cpu]->os_unlock = true;
 		}
 
-		if (etmdrvdata[cpu]->enable)
+		if (local_read(&etmdrvdata[cpu]->mode))
 			etm_enable_hw(etmdrvdata[cpu]);
 		spin_unlock(&etmdrvdata[cpu]->spinlock);
 		break;
@@ -453,7 +505,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 
 	case CPU_DYING:
 		spin_lock(&etmdrvdata[cpu]->spinlock);
-		if (etmdrvdata[cpu]->enable)
+		if (local_read(&etmdrvdata[cpu]->mode))
 			etm_disable_hw(etmdrvdata[cpu]);
 		spin_unlock(&etmdrvdata[cpu]->spinlock);
 		break;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 1c6e32dd6e49..4ab291b3a6c8 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -180,7 +180,7 @@ static void etm4_enable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
 }
 
-static int etm4_enable(struct coresight_device *csdev)
+static int etm4_enable(struct coresight_device *csdev, u32 mode)
 {
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 14f245a2018d..ed116b303e87 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -34,6 +34,12 @@
 #define TIMEOUT_US		100
 #define BMVAL(val, lsb, msb)	((val & GENMASK(msb, lsb)) >> lsb)
 
+enum cs_mode {
+	CS_MODE_DISABLED,
+	CS_MODE_SYSFS,
+	CS_MODE_PERF,
+};
+
 static inline void CS_LOCK(void __iomem *addr)
 {
 	do {
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 6b44928c1076..b20afb709141 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -222,7 +222,7 @@ static void coresight_disable_link(struct coresight_device *csdev,
 	csdev->enable = false;
 }
 
-static int coresight_enable_source(struct coresight_device *csdev)
+static int coresight_enable_source(struct coresight_device *csdev, u32 mode)
 {
 	int ret;
 
@@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev)
 
 	if (!csdev->enable) {
 		if (source_ops(csdev)->enable) {
-			ret = source_ops(csdev)->enable(csdev);
+			ret = source_ops(csdev)->enable(csdev, mode);
 			if (ret)
 				return ret;
 		}
@@ -458,7 +458,7 @@ int coresight_enable(struct coresight_device *csdev)
 	if (ret)
 		goto err_path;
 
-	ret = coresight_enable_source(csdev);
+	ret = coresight_enable_source(csdev, CS_MODE_SYSFS);
 	if (ret)
 		goto err_source;
 
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 851ecb22397e..61dfb8d511ea 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -213,7 +213,7 @@ struct coresight_ops_link {
 struct coresight_ops_source {
 	int (*cpu_id)(struct coresight_device *csdev);
 	int (*trace_id)(struct coresight_device *csdev);
-	int (*enable)(struct coresight_device *csdev);
+	int (*enable)(struct coresight_device *csdev, u32 mode);
 	void (*disable)(struct coresight_device *csdev);
 };
 
-- 
2.1.4

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

* [PATCH V8 09/23] coresight: etm3x: set progbit to stop trace collection
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

There is no need to use the event enable's "always false" event to
stop trace collection.  For that purpose setting the programming bit
(ETMCR:10) is enough.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 4db5147680f6..82d1557f4ecf 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -398,9 +398,6 @@ static void etm_disable_hw(void *info)
 	CS_UNLOCK(drvdata->base);
 	etm_set_prog(drvdata);
 
-	/* Program trace enable to low by using always false event */
-	etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR);
-
 	/* Read back sequencer and counters for post trace analysis */
 	config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
 
-- 
2.1.4

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

* [PATCH V8 09/23] coresight: etm3x: set progbit to stop trace collection
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

There is no need to use the event enable's "always false" event to
stop trace collection.  For that purpose setting the programming bit
(ETMCR:10) is enough.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 4db5147680f6..82d1557f4ecf 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -398,9 +398,6 @@ static void etm_disable_hw(void *info)
 	CS_UNLOCK(drvdata->base);
 	etm_set_prog(drvdata);
 
-	/* Program trace enable to low by using always false event */
-	etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR);
-
 	/* Read back sequencer and counters for post trace analysis */
 	config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
 
-- 
2.1.4

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

* [PATCH V8 10/23] coresight: etm3x: changing default trace configuration
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Changing default configuration to include the entire address
range rather than just the kernel.  That way traces are more
inclusive and it is easier to narrow down if needed.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h   |  2 ++
 drivers/hwtracing/coresight/coresight-etm3x.c | 29 ++++++++++++---------------
 2 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 5b29d5540fe5..44585d4adb26 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -146,6 +146,7 @@
  * @startstop_ctrl: setting for register ETMTSSCR.
  * @enable_event: setting for register ETMTEEVR.
  * @enable_ctrl1: setting for register ETMTECR1.
+ * @enable_ctrl2: setting for register ETMTECR2.
  * @fifofull_level: setting for register ETMFFLR.
  * @addr_idx:	index for the address comparator selection.
  * @addr_val:	value for address comparator register.
@@ -179,6 +180,7 @@ struct etm_config {
 	u32				startstop_ctrl;
 	u32				enable_event;
 	u32				enable_ctrl1;
+	u32				enable_ctrl2;
 	u32				fifofull_level;
 	u8				addr_idx;
 	u32				addr_val[ETM_MAX_ADDR_CMP];
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 82d1557f4ecf..6469d7a83aa1 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -579,26 +579,23 @@ static void etm_init_arch_data(void *info)
 
 static void etm_init_default_data(struct etm_config *config)
 {
-	u32 flags = (1 << 0 | /* instruction execute*/
-		     3 << 3 | /* ARM instruction */
-		     0 << 5 | /* No data value comparison */
-		     0 << 7 | /* No exact mach */
-		     0 << 8 | /* Ignore context ID */
-		     0 << 10); /* Security ignored */
-
 	if (WARN_ON_ONCE(!config))
 		return;
 
-	config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN);
-	config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
-	config->addr_val[0] = (u32) _stext;
-	config->addr_val[1] = (u32) _etext;
-	config->addr_acctype[0] = flags;
-	config->addr_acctype[1] = flags;
-	config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
-	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
-
 	etm_set_default(config);
+
+	/*
+	 * Taken verbatim from the TRM:
+	 *
+	 * To trace all memory:
+	 *  set bit [24] in register 0x009, the ETMTECR1, to 1
+	 *  set all other bits in register 0x009, the ETMTECR1, to 0
+	 *  set all bits in register 0x007, the ETMTECR2, to 0
+	 *  set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
+	 */
+	config->enable_ctrl1 = BIT(24);
+	config->enable_ctrl2 = 0x0;
+	config->enable_event = ETM_HARD_WIRE_RES_A;
 }
 
 static void etm_init_trace_id(struct etm_drvdata *drvdata)
-- 
2.1.4

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

* [PATCH V8 10/23] coresight: etm3x: changing default trace configuration
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Changing default configuration to include the entire address
range rather than just the kernel.  That way traces are more
inclusive and it is easier to narrow down if needed.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h   |  2 ++
 drivers/hwtracing/coresight/coresight-etm3x.c | 29 ++++++++++++---------------
 2 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 5b29d5540fe5..44585d4adb26 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -146,6 +146,7 @@
  * @startstop_ctrl: setting for register ETMTSSCR.
  * @enable_event: setting for register ETMTEEVR.
  * @enable_ctrl1: setting for register ETMTECR1.
+ * @enable_ctrl2: setting for register ETMTECR2.
  * @fifofull_level: setting for register ETMFFLR.
  * @addr_idx:	index for the address comparator selection.
  * @addr_val:	value for address comparator register.
@@ -179,6 +180,7 @@ struct etm_config {
 	u32				startstop_ctrl;
 	u32				enable_event;
 	u32				enable_ctrl1;
+	u32				enable_ctrl2;
 	u32				fifofull_level;
 	u8				addr_idx;
 	u32				addr_val[ETM_MAX_ADDR_CMP];
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 82d1557f4ecf..6469d7a83aa1 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -579,26 +579,23 @@ static void etm_init_arch_data(void *info)
 
 static void etm_init_default_data(struct etm_config *config)
 {
-	u32 flags = (1 << 0 | /* instruction execute*/
-		     3 << 3 | /* ARM instruction */
-		     0 << 5 | /* No data value comparison */
-		     0 << 7 | /* No exact mach */
-		     0 << 8 | /* Ignore context ID */
-		     0 << 10); /* Security ignored */
-
 	if (WARN_ON_ONCE(!config))
 		return;
 
-	config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN);
-	config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
-	config->addr_val[0] = (u32) _stext;
-	config->addr_val[1] = (u32) _etext;
-	config->addr_acctype[0] = flags;
-	config->addr_acctype[1] = flags;
-	config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
-	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
-
 	etm_set_default(config);
+
+	/*
+	 * Taken verbatim from the TRM:
+	 *
+	 * To trace all memory:
+	 *  set bit [24] in register 0x009, the ETMTECR1, to 1
+	 *  set all other bits in register 0x009, the ETMTECR1, to 0
+	 *  set all bits in register 0x007, the ETMTECR2, to 0
+	 *  set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
+	 */
+	config->enable_ctrl1 = BIT(24);
+	config->enable_ctrl2 = 0x0;
+	config->enable_event = ETM_HARD_WIRE_RES_A;
 }
 
 static void etm_init_trace_id(struct etm_drvdata *drvdata)
-- 
2.1.4

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

* [PATCH V8 11/23] coresight: etm3x: consolidating initial config
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

There is really no point in having two functions to take care
of doing the initial tracer configuration.  As such moving
everything to 'etm_set_default()'.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 37 ++++++++++-----------------
 1 file changed, 14 insertions(+), 23 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 6469d7a83aa1..d0f2a55f9b16 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -41,7 +41,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 /* The number of ETM/PTM currently registered */
 static int etm_count;
 static struct etm_drvdata *etmdrvdata[NR_CPUS];
-static void etm_init_default_data(struct etm_config *config);
 
 /*
  * Memory mapped writes to clear os lock are not supported on some processors
@@ -194,6 +193,19 @@ void etm_set_default(struct etm_config *config)
 	if (WARN_ON_ONCE(!config))
 		return;
 
+	/*
+	 * Taken verbatim from the TRM:
+	 *
+	 * To trace all memory:
+	 *  set bit [24] in register 0x009, the ETMTECR1, to 1
+	 *  set all other bits in register 0x009, the ETMTECR1, to 0
+	 *  set all bits in register 0x007, the ETMTECR2, to 0
+	 *  set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
+	 */
+	config->enable_ctrl1 = BIT(24);
+	config->enable_ctrl2 = 0x0;
+	config->enable_event = ETM_HARD_WIRE_RES_A;
+
 	config->trigger_event = ETM_DEFAULT_EVENT_VAL;
 	config->enable_event = ETM_HARD_WIRE_RES_A;
 
@@ -577,27 +589,6 @@ static void etm_init_arch_data(void *info)
 	CS_LOCK(drvdata->base);
 }
 
-static void etm_init_default_data(struct etm_config *config)
-{
-	if (WARN_ON_ONCE(!config))
-		return;
-
-	etm_set_default(config);
-
-	/*
-	 * Taken verbatim from the TRM:
-	 *
-	 * To trace all memory:
-	 *  set bit [24] in register 0x009, the ETMTECR1, to 1
-	 *  set all other bits in register 0x009, the ETMTECR1, to 0
-	 *  set all bits in register 0x007, the ETMTECR2, to 0
-	 *  set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
-	 */
-	config->enable_ctrl1 = BIT(24);
-	config->enable_ctrl2 = 0x0;
-	config->enable_event = ETM_HARD_WIRE_RES_A;
-}
-
 static void etm_init_trace_id(struct etm_drvdata *drvdata)
 {
 	/*
@@ -674,7 +665,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 	}
 
 	etm_init_trace_id(drvdata);
-	etm_init_default_data(&drvdata->config);
+	etm_set_default(&drvdata->config);
 
 	desc->type = CORESIGHT_DEV_TYPE_SOURCE;
 	desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
-- 
2.1.4

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

* [PATCH V8 11/23] coresight: etm3x: consolidating initial config
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

There is really no point in having two functions to take care
of doing the initial tracer configuration.  As such moving
everything to 'etm_set_default()'.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 37 ++++++++++-----------------
 1 file changed, 14 insertions(+), 23 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 6469d7a83aa1..d0f2a55f9b16 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -41,7 +41,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 /* The number of ETM/PTM currently registered */
 static int etm_count;
 static struct etm_drvdata *etmdrvdata[NR_CPUS];
-static void etm_init_default_data(struct etm_config *config);
 
 /*
  * Memory mapped writes to clear os lock are not supported on some processors
@@ -194,6 +193,19 @@ void etm_set_default(struct etm_config *config)
 	if (WARN_ON_ONCE(!config))
 		return;
 
+	/*
+	 * Taken verbatim from the TRM:
+	 *
+	 * To trace all memory:
+	 *  set bit [24] in register 0x009, the ETMTECR1, to 1
+	 *  set all other bits in register 0x009, the ETMTECR1, to 0
+	 *  set all bits in register 0x007, the ETMTECR2, to 0
+	 *  set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
+	 */
+	config->enable_ctrl1 = BIT(24);
+	config->enable_ctrl2 = 0x0;
+	config->enable_event = ETM_HARD_WIRE_RES_A;
+
 	config->trigger_event = ETM_DEFAULT_EVENT_VAL;
 	config->enable_event = ETM_HARD_WIRE_RES_A;
 
@@ -577,27 +589,6 @@ static void etm_init_arch_data(void *info)
 	CS_LOCK(drvdata->base);
 }
 
-static void etm_init_default_data(struct etm_config *config)
-{
-	if (WARN_ON_ONCE(!config))
-		return;
-
-	etm_set_default(config);
-
-	/*
-	 * Taken verbatim from the TRM:
-	 *
-	 * To trace all memory:
-	 *  set bit [24] in register 0x009, the ETMTECR1, to 1
-	 *  set all other bits in register 0x009, the ETMTECR1, to 0
-	 *  set all bits in register 0x007, the ETMTECR2, to 0
-	 *  set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
-	 */
-	config->enable_ctrl1 = BIT(24);
-	config->enable_ctrl2 = 0x0;
-	config->enable_event = ETM_HARD_WIRE_RES_A;
-}
-
 static void etm_init_trace_id(struct etm_drvdata *drvdata)
 {
 	/*
@@ -674,7 +665,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 	}
 
 	etm_init_trace_id(drvdata);
-	etm_init_default_data(&drvdata->config);
+	etm_set_default(&drvdata->config);
 
 	desc->type = CORESIGHT_DEV_TYPE_SOURCE;
 	desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
-- 
2.1.4

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

* [PATCH V8 12/23] coresight: etm3x: implementing user/kernel mode tracing
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Adding new mode to limit tracing to kernel or user space.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h        |  6 ++-
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    |  4 ++
 drivers/hwtracing/coresight/coresight-etm3x.c      | 63 ++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-priv.h       |  3 ++
 4 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 44585d4adb26..51597cb2c08a 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -110,7 +110,10 @@
 #define ETM_MODE_STALL		BIT(2)
 #define ETM_MODE_TIMESTAMP	BIT(3)
 #define ETM_MODE_CTXID		BIT(4)
-#define ETM_MODE_ALL		0x1f
+#define ETM_MODE_ALL		(ETM_MODE_EXCLUDE | ETM_MODE_CYCACC | \
+				 ETM_MODE_STALL | ETM_MODE_TIMESTAMP | \
+				 ETM_MODE_CTXID | ETM_MODE_EXCL_KERN | \
+				 ETM_MODE_EXCL_USER)
 
 #define ETM_SQR_MASK		0x3
 #define ETM_TRACEID_MASK	0x3f
@@ -296,5 +299,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
 extern const struct attribute_group *coresight_etm_groups[];
 int etm_get_trace_id(struct etm_drvdata *drvdata);
 void etm_set_default(struct etm_config *config);
+void etm_config_trace_mode(struct etm_config *config);
 struct etm_config *get_etm_config(struct etm_drvdata *drvdata);
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index 387c79fd9d5e..cbb4046c1070 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -162,6 +162,10 @@ static ssize_t mode_store(struct device *dev,
 		config->ctrl |= ETMCR_CTXID_SIZE;
 	else
 		config->ctrl &= ~ETMCR_CTXID_SIZE;
+
+	if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER))
+		etm_config_trace_mode(config);
+
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index d0f2a55f9b16..c683b267ecfc 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -234,6 +234,69 @@ void etm_set_default(struct etm_config *config)
 	config->ctxid_mask = 0x0;
 }
 
+void etm_config_trace_mode(struct etm_config *config)
+{
+	u32 flags, mode;
+
+	mode = config->mode;
+
+	mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER);
+
+	/* excluding kernel AND user space doesn't make sense */
+	if (mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER))
+		return;
+
+	/* nothing to do if neither flags are set */
+	if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER))
+		return;
+
+	flags = (1 << 0 |	/* instruction execute */
+		 3 << 3 |	/* ARM instruction */
+		 0 << 5 |	/* No data value comparison */
+		 0 << 7 |	/* No exact mach */
+		 0 << 8);	/* Ignore context ID */
+
+	/* No need to worry about single address comparators. */
+	config->enable_ctrl2 = 0x0;
+
+	/* Bit 0 is address range comparator 1 */
+	config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
+
+	/*
+	 * On ETMv3.5:
+	 * ETMACTRn[13,11] == Non-secure state comparison control
+	 * ETMACTRn[12,10] == Secure state comparison control
+	 *
+	 * b00 == Match in all modes in this state
+	 * b01 == Do not match in any more in this state
+	 * b10 == Match in all modes excepts user mode in this state
+	 * b11 == Match only in user mode in this state
+	 */
+
+	/* Tracing in secure mode is not supported at this time */
+	flags |= (0 << 12 | 1 << 10);
+
+	if (mode & ETM_MODE_EXCL_USER) {
+		/* exclude user, match all modes except user mode */
+		flags |= (1 << 13 | 0 << 11);
+	} else {
+		/* exclude kernel, match only in user mode */
+		flags |= (1 << 13 | 1 << 11);
+	}
+
+	/*
+	 * The ETMEEVR register is already set to "hard wire A".  As such
+	 * all there is to do is setup an address comparator that spans
+	 * the entire address range and configure the state and mode bits.
+	 */
+	config->addr_val[0] = (u32) 0x0;
+	config->addr_val[1] = (u32) ~0x0;
+	config->addr_acctype[0] = flags;
+	config->addr_acctype[1] = flags;
+	config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
+	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
+}
+
 static void etm_enable_hw(void *info)
 {
 	int i;
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index ed116b303e87..932f34a84d96 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -34,6 +34,9 @@
 #define TIMEOUT_US		100
 #define BMVAL(val, lsb, msb)	((val & GENMASK(msb, lsb)) >> lsb)
 
+#define ETM_MODE_EXCL_KERN	BIT(30)
+#define ETM_MODE_EXCL_USER	BIT(31)
+
 enum cs_mode {
 	CS_MODE_DISABLED,
 	CS_MODE_SYSFS,
-- 
2.1.4

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

* [PATCH V8 12/23] coresight: etm3x: implementing user/kernel mode tracing
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Adding new mode to limit tracing to kernel or user space.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h        |  6 ++-
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    |  4 ++
 drivers/hwtracing/coresight/coresight-etm3x.c      | 63 ++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-priv.h       |  3 ++
 4 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 44585d4adb26..51597cb2c08a 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -110,7 +110,10 @@
 #define ETM_MODE_STALL		BIT(2)
 #define ETM_MODE_TIMESTAMP	BIT(3)
 #define ETM_MODE_CTXID		BIT(4)
-#define ETM_MODE_ALL		0x1f
+#define ETM_MODE_ALL		(ETM_MODE_EXCLUDE | ETM_MODE_CYCACC | \
+				 ETM_MODE_STALL | ETM_MODE_TIMESTAMP | \
+				 ETM_MODE_CTXID | ETM_MODE_EXCL_KERN | \
+				 ETM_MODE_EXCL_USER)
 
 #define ETM_SQR_MASK		0x3
 #define ETM_TRACEID_MASK	0x3f
@@ -296,5 +299,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
 extern const struct attribute_group *coresight_etm_groups[];
 int etm_get_trace_id(struct etm_drvdata *drvdata);
 void etm_set_default(struct etm_config *config);
+void etm_config_trace_mode(struct etm_config *config);
 struct etm_config *get_etm_config(struct etm_drvdata *drvdata);
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index 387c79fd9d5e..cbb4046c1070 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -162,6 +162,10 @@ static ssize_t mode_store(struct device *dev,
 		config->ctrl |= ETMCR_CTXID_SIZE;
 	else
 		config->ctrl &= ~ETMCR_CTXID_SIZE;
+
+	if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER))
+		etm_config_trace_mode(config);
+
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index d0f2a55f9b16..c683b267ecfc 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -234,6 +234,69 @@ void etm_set_default(struct etm_config *config)
 	config->ctxid_mask = 0x0;
 }
 
+void etm_config_trace_mode(struct etm_config *config)
+{
+	u32 flags, mode;
+
+	mode = config->mode;
+
+	mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER);
+
+	/* excluding kernel AND user space doesn't make sense */
+	if (mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER))
+		return;
+
+	/* nothing to do if neither flags are set */
+	if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER))
+		return;
+
+	flags = (1 << 0 |	/* instruction execute */
+		 3 << 3 |	/* ARM instruction */
+		 0 << 5 |	/* No data value comparison */
+		 0 << 7 |	/* No exact mach */
+		 0 << 8);	/* Ignore context ID */
+
+	/* No need to worry about single address comparators. */
+	config->enable_ctrl2 = 0x0;
+
+	/* Bit 0 is address range comparator 1 */
+	config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
+
+	/*
+	 * On ETMv3.5:
+	 * ETMACTRn[13,11] == Non-secure state comparison control
+	 * ETMACTRn[12,10] == Secure state comparison control
+	 *
+	 * b00 == Match in all modes in this state
+	 * b01 == Do not match in any more in this state
+	 * b10 == Match in all modes excepts user mode in this state
+	 * b11 == Match only in user mode in this state
+	 */
+
+	/* Tracing in secure mode is not supported at this time */
+	flags |= (0 << 12 | 1 << 10);
+
+	if (mode & ETM_MODE_EXCL_USER) {
+		/* exclude user, match all modes except user mode */
+		flags |= (1 << 13 | 0 << 11);
+	} else {
+		/* exclude kernel, match only in user mode */
+		flags |= (1 << 13 | 1 << 11);
+	}
+
+	/*
+	 * The ETMEEVR register is already set to "hard wire A".  As such
+	 * all there is to do is setup an address comparator that spans
+	 * the entire address range and configure the state and mode bits.
+	 */
+	config->addr_val[0] = (u32) 0x0;
+	config->addr_val[1] = (u32) ~0x0;
+	config->addr_acctype[0] = flags;
+	config->addr_acctype[1] = flags;
+	config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
+	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
+}
+
 static void etm_enable_hw(void *info)
 {
 	int i;
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index ed116b303e87..932f34a84d96 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -34,6 +34,9 @@
 #define TIMEOUT_US		100
 #define BMVAL(val, lsb, msb)	((val & GENMASK(msb, lsb)) >> lsb)
 
+#define ETM_MODE_EXCL_KERN	BIT(30)
+#define ETM_MODE_EXCL_USER	BIT(31)
+
 enum cs_mode {
 	CS_MODE_DISABLED,
 	CS_MODE_SYSFS,
-- 
2.1.4

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

* [PATCH V8 13/23] coresight: etm3x: implementing perf_enable/disable() API
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

That way traces can be enabled and disabled automatically
from the Perf subystem using the PMU abstraction.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Kconfig           |  1 +
 drivers/hwtracing/coresight/coresight-etm3x.c | 95 +++++++++++++++++++++++++--
 drivers/hwtracing/coresight/coresight-etm4x.c |  4 +-
 drivers/hwtracing/coresight/coresight.c       |  2 +-
 include/linux/coresight.h                     |  6 +-
 5 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index c85935f3525a..db0541031c72 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -4,6 +4,7 @@
 menuconfig CORESIGHT
 	bool "CoreSight Tracing Support"
 	select ARM_AMBA
+	select PERF_EVENTS
 	help
 	  This framework provides a kernel interface for the CoreSight debug
 	  and trace drivers to register themselves with. It's intended to build
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index c683b267ecfc..a96129a0e89f 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -31,6 +31,7 @@
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
 #include <linux/clk.h>
+#include <linux/perf_event.h>
 #include <asm/sections.h>
 
 #include "coresight-etm.h"
@@ -297,6 +298,47 @@ void etm_config_trace_mode(struct etm_config *config)
 	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
 }
 
+#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN)
+
+static int etm_parse_event_config(struct etm_drvdata *drvdata,
+				  struct perf_event_attr *attr)
+{
+	struct etm_config *config = &drvdata->config;
+
+	if (!attr)
+		return -EINVAL;
+
+	/* Clear configuration from previous run */
+	memset(config, 0, sizeof(struct etm_config));
+
+	if (attr->exclude_kernel)
+		config->mode = ETM_MODE_EXCL_KERN;
+
+	if (attr->exclude_user)
+		config->mode = ETM_MODE_EXCL_USER;
+
+	/* Always start from the default config */
+	etm_set_default(config);
+
+	/*
+	 * By default the tracers are configured to trace the whole address
+	 * range.  Narrow the field only if requested by user space.
+	 */
+	if (config->mode)
+		etm_config_trace_mode(config);
+
+	/*
+	 * At this time only cycle accurate and timestamp options are
+	 * available.
+	 */
+	if (attr->config & ~ETM3X_SUPPORTED_OPTIONS)
+		return -EINVAL;
+
+	config->ctrl = attr->config;
+
+	return 0;
+}
+
 static void etm_enable_hw(void *info)
 {
 	int i;
@@ -316,8 +358,10 @@ static void etm_enable_hw(void *info)
 	etm_set_prog(drvdata);
 
 	etmcr = etm_readl(drvdata, ETMCR);
-	etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG);
+	/* Clear setting from a previous run if need be */
+	etmcr &= ~ETM3X_SUPPORTED_OPTIONS;
 	etmcr |= drvdata->port_size;
+	etmcr |= ETMCR_ETM_EN;
 	etm_writel(drvdata, config->ctrl | etmcr, ETMCR);
 	etm_writel(drvdata, config->trigger_event, ETMTRIGGER);
 	etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR);
@@ -357,9 +401,6 @@ static void etm_enable_hw(void *info)
 	/* No VMID comparator value selected */
 	etm_writel(drvdata, 0x0, ETMVMIDCVR);
 
-	/* Ensures trace output is enabled from this ETM */
-	etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
-
 	etm_clr_prog(drvdata);
 	CS_LOCK(drvdata->base);
 
@@ -407,6 +448,22 @@ static int etm_trace_id(struct coresight_device *csdev)
 	return etm_get_trace_id(drvdata);
 }
 
+static int etm_enable_perf(struct coresight_device *csdev,
+			   struct perf_event_attr *attr)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
+		return -EINVAL;
+
+	/* Configure the tracer based on the session's specifics */
+	etm_parse_event_config(drvdata, attr);
+	/* And enable it */
+	etm_enable_hw(drvdata);
+
+	return 0;
+}
+
 static int etm_enable_sysfs(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -437,7 +494,8 @@ err:
 	return ret;
 }
 
-static int etm_enable(struct coresight_device *csdev, u32 mode)
+static int etm_enable(struct coresight_device *csdev,
+		      struct perf_event_attr *attr, u32 mode)
 {
 	int ret;
 	u32 val;
@@ -453,6 +511,9 @@ static int etm_enable(struct coresight_device *csdev, u32 mode)
 	case CS_MODE_SYSFS:
 		ret = etm_enable_sysfs(csdev);
 		break;
+	case CS_MODE_PERF:
+		ret = etm_enable_perf(csdev, attr);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -485,6 +546,27 @@ static void etm_disable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
 }
 
+static void etm_disable_perf(struct coresight_device *csdev)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
+		return;
+
+	CS_UNLOCK(drvdata->base);
+
+	/* Setting the prog bit disables tracing immediately */
+	etm_set_prog(drvdata);
+
+	/*
+	 * There is no way to know when the tracer will be used again so
+	 * power down the tracer.
+	 */
+	etm_set_pwrdwn(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
 static void etm_disable_sysfs(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -528,6 +610,9 @@ static void etm_disable(struct coresight_device *csdev)
 	case CS_MODE_SYSFS:
 		etm_disable_sysfs(csdev);
 		break;
+	case CS_MODE_PERF:
+		etm_disable_perf(csdev);
+		break;
 	default:
 		WARN_ON_ONCE(mode);
 		return;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 4ab291b3a6c8..2f7523caf9d6 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -32,6 +32,7 @@
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
 #include <linux/pm_runtime.h>
+#include <linux/perf_event.h>
 #include <asm/sections.h>
 
 #include "coresight-etm4x.h"
@@ -180,7 +181,8 @@ static void etm4_enable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
 }
 
-static int etm4_enable(struct coresight_device *csdev, u32 mode)
+static int etm4_enable(struct coresight_device *csdev,
+		       struct perf_event_attr *attr, u32 mode)
 {
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index b20afb709141..95cccb179763 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode)
 
 	if (!csdev->enable) {
 		if (source_ops(csdev)->enable) {
-			ret = source_ops(csdev)->enable(csdev, mode);
+			ret = source_ops(csdev)->enable(csdev, NULL, mode);
 			if (ret)
 				return ret;
 		}
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 61dfb8d511ea..6801dd64ee5d 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -14,6 +14,7 @@
 #define _LINUX_CORESIGHT_H
 
 #include <linux/device.h>
+#include <linux/perf_event.h>
 #include <linux/sched.h>
 
 /* Peripheral id registers (0xFD0-0xFEC) */
@@ -206,14 +207,15 @@ struct coresight_ops_link {
  * @cpu_id:	returns the value of the CPU number this component
  *		is associated to.
  * @trace_id:	returns the value of the component's trace ID as known
-		to the HW.
+ *		to the HW.
  * @enable:	enables tracing for a source.
  * @disable:	disables tracing for a source.
  */
 struct coresight_ops_source {
 	int (*cpu_id)(struct coresight_device *csdev);
 	int (*trace_id)(struct coresight_device *csdev);
-	int (*enable)(struct coresight_device *csdev, u32 mode);
+	int (*enable)(struct coresight_device *csdev,
+		      struct perf_event_attr *attr,  u32 mode);
 	void (*disable)(struct coresight_device *csdev);
 };
 
-- 
2.1.4

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

* [PATCH V8 13/23] coresight: etm3x: implementing perf_enable/disable() API
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

That way traces can be enabled and disabled automatically
from the Perf subystem using the PMU abstraction.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Kconfig           |  1 +
 drivers/hwtracing/coresight/coresight-etm3x.c | 95 +++++++++++++++++++++++++--
 drivers/hwtracing/coresight/coresight-etm4x.c |  4 +-
 drivers/hwtracing/coresight/coresight.c       |  2 +-
 include/linux/coresight.h                     |  6 +-
 5 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index c85935f3525a..db0541031c72 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -4,6 +4,7 @@
 menuconfig CORESIGHT
 	bool "CoreSight Tracing Support"
 	select ARM_AMBA
+	select PERF_EVENTS
 	help
 	  This framework provides a kernel interface for the CoreSight debug
 	  and trace drivers to register themselves with. It's intended to build
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index c683b267ecfc..a96129a0e89f 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -31,6 +31,7 @@
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
 #include <linux/clk.h>
+#include <linux/perf_event.h>
 #include <asm/sections.h>
 
 #include "coresight-etm.h"
@@ -297,6 +298,47 @@ void etm_config_trace_mode(struct etm_config *config)
 	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
 }
 
+#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN)
+
+static int etm_parse_event_config(struct etm_drvdata *drvdata,
+				  struct perf_event_attr *attr)
+{
+	struct etm_config *config = &drvdata->config;
+
+	if (!attr)
+		return -EINVAL;
+
+	/* Clear configuration from previous run */
+	memset(config, 0, sizeof(struct etm_config));
+
+	if (attr->exclude_kernel)
+		config->mode = ETM_MODE_EXCL_KERN;
+
+	if (attr->exclude_user)
+		config->mode = ETM_MODE_EXCL_USER;
+
+	/* Always start from the default config */
+	etm_set_default(config);
+
+	/*
+	 * By default the tracers are configured to trace the whole address
+	 * range.  Narrow the field only if requested by user space.
+	 */
+	if (config->mode)
+		etm_config_trace_mode(config);
+
+	/*
+	 * At this time only cycle accurate and timestamp options are
+	 * available.
+	 */
+	if (attr->config & ~ETM3X_SUPPORTED_OPTIONS)
+		return -EINVAL;
+
+	config->ctrl = attr->config;
+
+	return 0;
+}
+
 static void etm_enable_hw(void *info)
 {
 	int i;
@@ -316,8 +358,10 @@ static void etm_enable_hw(void *info)
 	etm_set_prog(drvdata);
 
 	etmcr = etm_readl(drvdata, ETMCR);
-	etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG);
+	/* Clear setting from a previous run if need be */
+	etmcr &= ~ETM3X_SUPPORTED_OPTIONS;
 	etmcr |= drvdata->port_size;
+	etmcr |= ETMCR_ETM_EN;
 	etm_writel(drvdata, config->ctrl | etmcr, ETMCR);
 	etm_writel(drvdata, config->trigger_event, ETMTRIGGER);
 	etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR);
@@ -357,9 +401,6 @@ static void etm_enable_hw(void *info)
 	/* No VMID comparator value selected */
 	etm_writel(drvdata, 0x0, ETMVMIDCVR);
 
-	/* Ensures trace output is enabled from this ETM */
-	etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
-
 	etm_clr_prog(drvdata);
 	CS_LOCK(drvdata->base);
 
@@ -407,6 +448,22 @@ static int etm_trace_id(struct coresight_device *csdev)
 	return etm_get_trace_id(drvdata);
 }
 
+static int etm_enable_perf(struct coresight_device *csdev,
+			   struct perf_event_attr *attr)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
+		return -EINVAL;
+
+	/* Configure the tracer based on the session's specifics */
+	etm_parse_event_config(drvdata, attr);
+	/* And enable it */
+	etm_enable_hw(drvdata);
+
+	return 0;
+}
+
 static int etm_enable_sysfs(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -437,7 +494,8 @@ err:
 	return ret;
 }
 
-static int etm_enable(struct coresight_device *csdev, u32 mode)
+static int etm_enable(struct coresight_device *csdev,
+		      struct perf_event_attr *attr, u32 mode)
 {
 	int ret;
 	u32 val;
@@ -453,6 +511,9 @@ static int etm_enable(struct coresight_device *csdev, u32 mode)
 	case CS_MODE_SYSFS:
 		ret = etm_enable_sysfs(csdev);
 		break;
+	case CS_MODE_PERF:
+		ret = etm_enable_perf(csdev, attr);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -485,6 +546,27 @@ static void etm_disable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
 }
 
+static void etm_disable_perf(struct coresight_device *csdev)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
+		return;
+
+	CS_UNLOCK(drvdata->base);
+
+	/* Setting the prog bit disables tracing immediately */
+	etm_set_prog(drvdata);
+
+	/*
+	 * There is no way to know when the tracer will be used again so
+	 * power down the tracer.
+	 */
+	etm_set_pwrdwn(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
 static void etm_disable_sysfs(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -528,6 +610,9 @@ static void etm_disable(struct coresight_device *csdev)
 	case CS_MODE_SYSFS:
 		etm_disable_sysfs(csdev);
 		break;
+	case CS_MODE_PERF:
+		etm_disable_perf(csdev);
+		break;
 	default:
 		WARN_ON_ONCE(mode);
 		return;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 4ab291b3a6c8..2f7523caf9d6 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -32,6 +32,7 @@
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
 #include <linux/pm_runtime.h>
+#include <linux/perf_event.h>
 #include <asm/sections.h>
 
 #include "coresight-etm4x.h"
@@ -180,7 +181,8 @@ static void etm4_enable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
 }
 
-static int etm4_enable(struct coresight_device *csdev, u32 mode)
+static int etm4_enable(struct coresight_device *csdev,
+		       struct perf_event_attr *attr, u32 mode)
 {
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index b20afb709141..95cccb179763 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode)
 
 	if (!csdev->enable) {
 		if (source_ops(csdev)->enable) {
-			ret = source_ops(csdev)->enable(csdev, mode);
+			ret = source_ops(csdev)->enable(csdev, NULL, mode);
 			if (ret)
 				return ret;
 		}
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 61dfb8d511ea..6801dd64ee5d 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -14,6 +14,7 @@
 #define _LINUX_CORESIGHT_H
 
 #include <linux/device.h>
+#include <linux/perf_event.h>
 #include <linux/sched.h>
 
 /* Peripheral id registers (0xFD0-0xFEC) */
@@ -206,14 +207,15 @@ struct coresight_ops_link {
  * @cpu_id:	returns the value of the CPU number this component
  *		is associated to.
  * @trace_id:	returns the value of the component's trace ID as known
-		to the HW.
+ *		to the HW.
  * @enable:	enables tracing for a source.
  * @disable:	disables tracing for a source.
  */
 struct coresight_ops_source {
 	int (*cpu_id)(struct coresight_device *csdev);
 	int (*trace_id)(struct coresight_device *csdev);
-	int (*enable)(struct coresight_device *csdev, u32 mode);
+	int (*enable)(struct coresight_device *csdev,
+		      struct perf_event_attr *attr,  u32 mode);
 	void (*disable)(struct coresight_device *csdev);
 };
 
-- 
2.1.4

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

* [PATCH V8 14/23] coresight: etb10: moving to local atomic operations
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Moving to use local atomic operations to take advantage of the
lockless implementation, something that will come handy when
the ETB is accessed from the Perf subsystem. Also changing the
name of the variable to something more meaningful.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 1301edc44629..09e306361e4f 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  */
 
+#include <asm/local.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -71,7 +72,7 @@
  * @csdev:	component vitals needed by the framework.
  * @miscdev:	specifics to handle "/dev/xyz.etb" entry.
  * @spinlock:	only one at a time pls.
- * @in_use:	synchronise user space access to etb buffer.
+ * @reading:	synchronise user space access to etb buffer.
  * @buf:	area of memory where ETB buffer content gets sent.
  * @buffer_depth: size of @buf.
  * @enable:	this ETB is being used.
@@ -84,7 +85,7 @@ struct etb_drvdata {
 	struct coresight_device	*csdev;
 	struct miscdevice	miscdev;
 	spinlock_t		spinlock;
-	atomic_t		in_use;
+	local_t			reading;
 	u8			*buf;
 	u32			buffer_depth;
 	bool			enable;
@@ -277,7 +278,7 @@ static int etb_open(struct inode *inode, struct file *file)
 	struct etb_drvdata *drvdata = container_of(file->private_data,
 						   struct etb_drvdata, miscdev);
 
-	if (atomic_cmpxchg(&drvdata->in_use, 0, 1))
+	if (local_cmpxchg(&drvdata->reading, 0, 1))
 		return -EBUSY;
 
 	dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
@@ -313,7 +314,7 @@ static int etb_release(struct inode *inode, struct file *file)
 {
 	struct etb_drvdata *drvdata = container_of(file->private_data,
 						   struct etb_drvdata, miscdev);
-	atomic_set(&drvdata->in_use, 0);
+	local_set(&drvdata->reading, 0);
 
 	dev_dbg(drvdata->dev, "%s: released\n", __func__);
 	return 0;
-- 
2.1.4

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

* [PATCH V8 14/23] coresight: etb10: moving to local atomic operations
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Moving to use local atomic operations to take advantage of the
lockless implementation, something that will come handy when
the ETB is accessed from the Perf subsystem. Also changing the
name of the variable to something more meaningful.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 1301edc44629..09e306361e4f 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  */
 
+#include <asm/local.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -71,7 +72,7 @@
  * @csdev:	component vitals needed by the framework.
  * @miscdev:	specifics to handle "/dev/xyz.etb" entry.
  * @spinlock:	only one at a time pls.
- * @in_use:	synchronise user space access to etb buffer.
+ * @reading:	synchronise user space access to etb buffer.
  * @buf:	area of memory where ETB buffer content gets sent.
  * @buffer_depth: size of @buf.
  * @enable:	this ETB is being used.
@@ -84,7 +85,7 @@ struct etb_drvdata {
 	struct coresight_device	*csdev;
 	struct miscdevice	miscdev;
 	spinlock_t		spinlock;
-	atomic_t		in_use;
+	local_t			reading;
 	u8			*buf;
 	u32			buffer_depth;
 	bool			enable;
@@ -277,7 +278,7 @@ static int etb_open(struct inode *inode, struct file *file)
 	struct etb_drvdata *drvdata = container_of(file->private_data,
 						   struct etb_drvdata, miscdev);
 
-	if (atomic_cmpxchg(&drvdata->in_use, 0, 1))
+	if (local_cmpxchg(&drvdata->reading, 0, 1))
 		return -EBUSY;
 
 	dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
@@ -313,7 +314,7 @@ static int etb_release(struct inode *inode, struct file *file)
 {
 	struct etb_drvdata *drvdata = container_of(file->private_data,
 						   struct etb_drvdata, miscdev);
-	atomic_set(&drvdata->in_use, 0);
+	local_set(&drvdata->reading, 0);
 
 	dev_dbg(drvdata->dev, "%s: released\n", __func__);
 	return 0;
-- 
2.1.4

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

* [PATCH V8 15/23] coresight: etb10: adding operation mode for sink->enable()
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Adding an operation mode to the sink->enable() API in order
to prevent simultaneous access from different callers.

TPIU and TMC won't be supplemented with the AUX area
API immediately and as such ignore the new mode.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 30 ++++++++++++++++++++-------
 drivers/hwtracing/coresight/coresight-priv.h  |  2 +-
 drivers/hwtracing/coresight/coresight-tmc.c   |  2 +-
 drivers/hwtracing/coresight/coresight-tpiu.c  |  2 +-
 drivers/hwtracing/coresight/coresight.c       | 10 ++++-----
 include/linux/coresight.h                     |  2 +-
 6 files changed, 32 insertions(+), 16 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 09e306361e4f..1581dcea6f03 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -73,9 +73,9 @@
  * @miscdev:	specifics to handle "/dev/xyz.etb" entry.
  * @spinlock:	only one at a time pls.
  * @reading:	synchronise user space access to etb buffer.
+ * @mode:	this ETB is being used.
  * @buf:	area of memory where ETB buffer content gets sent.
  * @buffer_depth: size of @buf.
- * @enable:	this ETB is being used.
  * @trigger_cntr: amount of words to store after a trigger.
  */
 struct etb_drvdata {
@@ -86,9 +86,9 @@ struct etb_drvdata {
 	struct miscdevice	miscdev;
 	spinlock_t		spinlock;
 	local_t			reading;
+	local_t			mode;
 	u8			*buf;
 	u32			buffer_depth;
-	bool			enable;
 	u32			trigger_cntr;
 };
 
@@ -133,16 +133,31 @@ static void etb_enable_hw(struct etb_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static int etb_enable(struct coresight_device *csdev)
+static int etb_enable(struct coresight_device *csdev, u32 mode)
 {
-	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	u32 val;
 	unsigned long flags;
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	val = local_cmpxchg(&drvdata->mode,
+			    CS_MODE_DISABLED, mode);
+	/*
+	 * When accessing from Perf, a HW buffer can be handled
+	 * by a single trace entity.  In sysFS mode many tracers
+	 * can be logging to the same HW buffer.
+	 */
+	if (val == CS_MODE_PERF)
+		return -EBUSY;
+
+	/* Nothing to do, the tracer is already enabled. */
+	if (val == CS_MODE_SYSFS)
+		goto out;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	etb_enable_hw(drvdata);
-	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+out:
 	dev_info(drvdata->dev, "ETB enabled\n");
 	return 0;
 }
@@ -243,9 +258,10 @@ static void etb_disable(struct coresight_device *csdev)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	etb_disable_hw(drvdata);
 	etb_dump_hw(drvdata);
-	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+	local_set(&drvdata->mode, CS_MODE_DISABLED);
+
 	dev_info(drvdata->dev, "ETB disabled\n");
 }
 
@@ -263,7 +279,7 @@ static void etb_dump(struct etb_drvdata *drvdata)
 	unsigned long flags;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
 		etb_disable_hw(drvdata);
 		etb_dump_hw(drvdata);
 		etb_enable_hw(drvdata);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 932f34a84d96..333eddaed339 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -62,7 +62,7 @@ static inline void CS_UNLOCK(void __iomem *addr)
 }
 
 void coresight_disable_path(struct list_head *path);
-int coresight_enable_path(struct list_head *path);
+int coresight_enable_path(struct list_head *path, u32 mode);
 struct coresight_device *coresight_get_sink(struct list_head *path);
 struct list_head *coresight_build_path(struct coresight_device *csdev);
 void coresight_release_path(struct list_head *path);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 5e2a71767870..d7e83da7a621 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -265,7 +265,7 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
 	return 0;
 }
 
-static int tmc_enable_sink(struct coresight_device *csdev)
+static int tmc_enable_sink(struct coresight_device *csdev, u32 mode)
 {
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index e19b86e61c38..0a09135382f6 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -70,7 +70,7 @@ static void tpiu_enable_hw(struct tpiu_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static int tpiu_enable(struct coresight_device *csdev)
+static int tpiu_enable(struct coresight_device *csdev, u32 mode)
 {
 	struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 95cccb179763..6ec2b66af9ee 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -121,13 +121,13 @@ static int coresight_find_link_outport(struct coresight_device *csdev,
 	return 0;
 }
 
-static int coresight_enable_sink(struct coresight_device *csdev)
+static int coresight_enable_sink(struct coresight_device *csdev, u32 mode)
 {
 	int ret;
 
 	if (!csdev->enable) {
 		if (sink_ops(csdev)->enable) {
-			ret = sink_ops(csdev)->enable(csdev);
+			ret = sink_ops(csdev)->enable(csdev, mode);
 			if (ret)
 				return ret;
 		}
@@ -283,7 +283,7 @@ void coresight_disable_path(struct list_head *path)
 	}
 }
 
-int coresight_enable_path(struct list_head *path)
+int coresight_enable_path(struct list_head *path, u32 mode)
 {
 
 	int ret = 0;
@@ -296,7 +296,7 @@ int coresight_enable_path(struct list_head *path)
 		switch (csdev->type) {
 		case CORESIGHT_DEV_TYPE_SINK:
 		case CORESIGHT_DEV_TYPE_LINKSINK:
-			ret = coresight_enable_sink(csdev);
+			ret = coresight_enable_sink(csdev, mode);
 			if (ret)
 				goto err;
 			break;
@@ -454,7 +454,7 @@ int coresight_enable(struct coresight_device *csdev)
 		goto out;
 	}
 
-	ret = coresight_enable_path(path);
+	ret = coresight_enable_path(path, CS_MODE_SYSFS);
 	if (ret)
 		goto err_path;
 
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 6801dd64ee5d..9fa92dcdd2ea 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -186,7 +186,7 @@ struct coresight_device {
  * @disable:	disables the sink.
  */
 struct coresight_ops_sink {
-	int (*enable)(struct coresight_device *csdev);
+	int (*enable)(struct coresight_device *csdev, u32 mode);
 	void (*disable)(struct coresight_device *csdev);
 };
 
-- 
2.1.4

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

* [PATCH V8 15/23] coresight: etb10: adding operation mode for sink->enable()
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Adding an operation mode to the sink->enable() API in order
to prevent simultaneous access from different callers.

TPIU and TMC won't be supplemented with the AUX area
API immediately and as such ignore the new mode.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 30 ++++++++++++++++++++-------
 drivers/hwtracing/coresight/coresight-priv.h  |  2 +-
 drivers/hwtracing/coresight/coresight-tmc.c   |  2 +-
 drivers/hwtracing/coresight/coresight-tpiu.c  |  2 +-
 drivers/hwtracing/coresight/coresight.c       | 10 ++++-----
 include/linux/coresight.h                     |  2 +-
 6 files changed, 32 insertions(+), 16 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 09e306361e4f..1581dcea6f03 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -73,9 +73,9 @@
  * @miscdev:	specifics to handle "/dev/xyz.etb" entry.
  * @spinlock:	only one at a time pls.
  * @reading:	synchronise user space access to etb buffer.
+ * @mode:	this ETB is being used.
  * @buf:	area of memory where ETB buffer content gets sent.
  * @buffer_depth: size of @buf.
- * @enable:	this ETB is being used.
  * @trigger_cntr: amount of words to store after a trigger.
  */
 struct etb_drvdata {
@@ -86,9 +86,9 @@ struct etb_drvdata {
 	struct miscdevice	miscdev;
 	spinlock_t		spinlock;
 	local_t			reading;
+	local_t			mode;
 	u8			*buf;
 	u32			buffer_depth;
-	bool			enable;
 	u32			trigger_cntr;
 };
 
@@ -133,16 +133,31 @@ static void etb_enable_hw(struct etb_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static int etb_enable(struct coresight_device *csdev)
+static int etb_enable(struct coresight_device *csdev, u32 mode)
 {
-	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	u32 val;
 	unsigned long flags;
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	val = local_cmpxchg(&drvdata->mode,
+			    CS_MODE_DISABLED, mode);
+	/*
+	 * When accessing from Perf, a HW buffer can be handled
+	 * by a single trace entity.  In sysFS mode many tracers
+	 * can be logging to the same HW buffer.
+	 */
+	if (val == CS_MODE_PERF)
+		return -EBUSY;
+
+	/* Nothing to do, the tracer is already enabled. */
+	if (val == CS_MODE_SYSFS)
+		goto out;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	etb_enable_hw(drvdata);
-	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+out:
 	dev_info(drvdata->dev, "ETB enabled\n");
 	return 0;
 }
@@ -243,9 +258,10 @@ static void etb_disable(struct coresight_device *csdev)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	etb_disable_hw(drvdata);
 	etb_dump_hw(drvdata);
-	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+	local_set(&drvdata->mode, CS_MODE_DISABLED);
+
 	dev_info(drvdata->dev, "ETB disabled\n");
 }
 
@@ -263,7 +279,7 @@ static void etb_dump(struct etb_drvdata *drvdata)
 	unsigned long flags;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
 		etb_disable_hw(drvdata);
 		etb_dump_hw(drvdata);
 		etb_enable_hw(drvdata);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 932f34a84d96..333eddaed339 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -62,7 +62,7 @@ static inline void CS_UNLOCK(void __iomem *addr)
 }
 
 void coresight_disable_path(struct list_head *path);
-int coresight_enable_path(struct list_head *path);
+int coresight_enable_path(struct list_head *path, u32 mode);
 struct coresight_device *coresight_get_sink(struct list_head *path);
 struct list_head *coresight_build_path(struct coresight_device *csdev);
 void coresight_release_path(struct list_head *path);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 5e2a71767870..d7e83da7a621 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -265,7 +265,7 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
 	return 0;
 }
 
-static int tmc_enable_sink(struct coresight_device *csdev)
+static int tmc_enable_sink(struct coresight_device *csdev, u32 mode)
 {
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index e19b86e61c38..0a09135382f6 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -70,7 +70,7 @@ static void tpiu_enable_hw(struct tpiu_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static int tpiu_enable(struct coresight_device *csdev)
+static int tpiu_enable(struct coresight_device *csdev, u32 mode)
 {
 	struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 95cccb179763..6ec2b66af9ee 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -121,13 +121,13 @@ static int coresight_find_link_outport(struct coresight_device *csdev,
 	return 0;
 }
 
-static int coresight_enable_sink(struct coresight_device *csdev)
+static int coresight_enable_sink(struct coresight_device *csdev, u32 mode)
 {
 	int ret;
 
 	if (!csdev->enable) {
 		if (sink_ops(csdev)->enable) {
-			ret = sink_ops(csdev)->enable(csdev);
+			ret = sink_ops(csdev)->enable(csdev, mode);
 			if (ret)
 				return ret;
 		}
@@ -283,7 +283,7 @@ void coresight_disable_path(struct list_head *path)
 	}
 }
 
-int coresight_enable_path(struct list_head *path)
+int coresight_enable_path(struct list_head *path, u32 mode)
 {
 
 	int ret = 0;
@@ -296,7 +296,7 @@ int coresight_enable_path(struct list_head *path)
 		switch (csdev->type) {
 		case CORESIGHT_DEV_TYPE_SINK:
 		case CORESIGHT_DEV_TYPE_LINKSINK:
-			ret = coresight_enable_sink(csdev);
+			ret = coresight_enable_sink(csdev, mode);
 			if (ret)
 				goto err;
 			break;
@@ -454,7 +454,7 @@ int coresight_enable(struct coresight_device *csdev)
 		goto out;
 	}
 
-	ret = coresight_enable_path(path);
+	ret = coresight_enable_path(path, CS_MODE_SYSFS);
 	if (ret)
 		goto err_path;
 
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 6801dd64ee5d..9fa92dcdd2ea 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -186,7 +186,7 @@ struct coresight_device {
  * @disable:	disables the sink.
  */
 struct coresight_ops_sink {
-	int (*enable)(struct coresight_device *csdev);
+	int (*enable)(struct coresight_device *csdev, u32 mode);
 	void (*disable)(struct coresight_device *csdev);
 };
 
-- 
2.1.4

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

* [PATCH V8 16/23] coresight: etb10: implementing AUX API
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Adding an ETB10 specific AUX area operations to be used
by the perf framework when events are initialised.

Part of this operation involves modeling the mmap'ed area
based on the specific ways a sink buffer gathers information.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 237 ++++++++++++++++++++++++++
 include/linux/coresight.h                     |  21 ++-
 2 files changed, 256 insertions(+), 2 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 1581dcea6f03..36f99cd7c4ff 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -28,6 +28,11 @@
 #include <linux/coresight.h>
 #include <linux/amba/bus.h>
 #include <linux/clk.h>
+#include <linux/circ_buf.h>
+#include <linux/mm.h>
+#include <linux/perf_event.h>
+
+#include <asm/local.h>
 
 #include "coresight-priv.h"
 
@@ -65,6 +70,26 @@
 #define ETB_FRAME_SIZE_WORDS	4
 
 /**
+ * struct cs_buffer - keep track of a recording session' specifics
+ * @cur:	index of the current buffer
+ * @nr_pages:	max number of pages granted to us
+ * @offset:	offset within the current buffer
+ * @data_size:	how much we collected in this run
+ * @lost:	other than zero if we had a HW buffer wrap around
+ * @snapshot:	is this run in snapshot mode
+ * @data_pages:	a handle the ring buffer
+ */
+struct cs_buffers {
+	unsigned int		cur;
+	unsigned int		nr_pages;
+	unsigned long		offset;
+	local_t			data_size;
+	local_t			lost;
+	bool			snapshot;
+	void			**data_pages;
+};
+
+/**
  * struct etb_drvdata - specifics associated to an ETB component
  * @base:	memory mapped base address for this component.
  * @dev:	the device entity associated to this component.
@@ -265,9 +290,221 @@ static void etb_disable(struct coresight_device *csdev)
 	dev_info(drvdata->dev, "ETB disabled\n");
 }
 
+static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu,
+			      void **pages, int nr_pages, bool overwrite)
+{
+	int node;
+	struct cs_buffers *buf;
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node);
+	if (!buf)
+		return NULL;
+
+	buf->snapshot = overwrite;
+	buf->nr_pages = nr_pages;
+	buf->data_pages = pages;
+
+	return buf;
+}
+
+static void etb_free_buffer(void *config)
+{
+	struct cs_buffers *buf = config;
+
+	kfree(buf);
+}
+
+static int etb_set_buffer(struct coresight_device *csdev,
+			  struct perf_output_handle *handle,
+			  void *sink_config)
+{
+	int ret = 0;
+	unsigned long head;
+	struct cs_buffers *buf = sink_config;
+
+	/* wrap head around to the amount of space we have */
+	head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1);
+
+	/* find the page to write to */
+	buf->cur = head / PAGE_SIZE;
+
+	/* and offset within that page */
+	buf->offset = head % PAGE_SIZE;
+
+	local_set(&buf->data_size, 0);
+
+	return ret;
+}
+
+static unsigned long etb_reset_buffer(struct coresight_device *csdev,
+				      struct perf_output_handle *handle,
+				      void *sink_config, bool *lost)
+{
+	unsigned long size = 0;
+	struct cs_buffers *buf = sink_config;
+
+	if (buf) {
+		/*
+		 * In snapshot mode ->data_size holds the new address of the
+		 * ring buffer's head.  The size itself is the whole address
+		 * range since we want the latest information.
+		 */
+		if (buf->snapshot)
+			handle->head = local_xchg(&buf->data_size,
+						  buf->nr_pages << PAGE_SHIFT);
+
+		/*
+		 * Tell the tracer PMU how much we got in this run and if
+		 * something went wrong along the way.  Nobody else can use
+		 * this cs_buffers instance until we are done.  As such
+		 * resetting parameters here and squaring off with the ring
+		 * buffer API in the tracer PMU is fine.
+		 */
+		*lost = local_xchg(&buf->lost, 0);
+		size = local_xchg(&buf->data_size, 0);
+	}
+
+	return size;
+}
+
+static void etb_update_buffer(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config)
+{
+	int i, cur;
+	u8 *buf_ptr;
+	u32 read_ptr, write_ptr, capacity;
+	u32 status, read_data, to_read;
+	unsigned long flags, offset;
+	struct cs_buffers *buf = sink_config;
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (!buf)
+		return;
+
+	capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	CS_UNLOCK(drvdata->base);
+	etb_disable_hw(drvdata);
+
+	/* unit is in words, not bytes */
+	read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER);
+	write_ptr = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER);
+
+	/*
+	 * Entries should be aligned to the frame size.  If they are not
+	 * go back to the last alignement point to give decoding tools a
+	 * chance to fix things.
+	 */
+	if (write_ptr % ETB_FRAME_SIZE_WORDS) {
+		dev_err(drvdata->dev,
+			"write_ptr: %lu not aligned to formatter frame size\n",
+			(unsigned long)write_ptr);
+
+		write_ptr &= ~(ETB_FRAME_SIZE_WORDS - 1);
+		local_inc(&buf->lost);
+	}
+
+	/*
+	 * Get a hold of the status register and see if a wrap around
+	 * has occurred.  If so adjust things accordingly.  Otherwise
+	 * start at the beginning and go until the write pointer has
+	 * been reached.
+	 */
+	status = readl_relaxed(drvdata->base + ETB_STATUS_REG);
+	if (status & ETB_STATUS_RAM_FULL) {
+		local_inc(&buf->lost);
+		to_read = capacity;
+		read_ptr = write_ptr;
+	} else {
+		to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->buffer_depth);
+		to_read *= ETB_FRAME_SIZE_WORDS;
+	}
+
+	/*
+	 * Make sure we don't overwrite data that hasn't been consumed yet.
+	 * It is entirely possible that the HW buffer has more data than the
+	 * ring buffer can currently handle.  If so adjust the start address
+	 * to take only the last traces.
+	 *
+	 * In snapshot mode we are looking to get the latest traces only and as
+	 * such, we don't care about not overwriting data that hasn't been
+	 * processed by user space.
+	 */
+	if (!buf->snapshot && to_read > handle->size) {
+		u32 mask = ~(ETB_FRAME_SIZE_WORDS - 1);
+
+		/* The new read pointer must be frame size aligned */
+		to_read -= handle->size & mask;
+		/*
+		 * Move the RAM read pointer up, keeping in mind that
+		 * everything is in frame size units.
+		 */
+		read_ptr = (write_ptr + drvdata->buffer_depth) -
+					to_read / ETB_FRAME_SIZE_WORDS;
+		/* Wrap around if need be*/
+		read_ptr &= ~(drvdata->buffer_depth - 1);
+		/* let the decoder know we've skipped ahead */
+		local_inc(&buf->lost);
+	}
+
+	/* finally tell HW where we want to start reading from */
+	writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER);
+
+	cur = buf->cur;
+	offset = buf->offset;
+	for (i = 0; i < to_read; i += 4) {
+		buf_ptr = buf->data_pages[cur] + offset;
+		read_data = readl_relaxed(drvdata->base +
+					  ETB_RAM_READ_DATA_REG);
+		*buf_ptr++ = read_data >> 0;
+		*buf_ptr++ = read_data >> 8;
+		*buf_ptr++ = read_data >> 16;
+		*buf_ptr++ = read_data >> 24;
+
+		offset += 4;
+		if (offset >= PAGE_SIZE) {
+			offset = 0;
+			cur++;
+			/* wrap around at the end of the buffer */
+			cur &= buf->nr_pages - 1;
+		}
+	}
+
+	/* reset ETB buffer for next run */
+	writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER);
+	writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER);
+
+	/*
+	 * In snapshot mode all we have to do is communicate to
+	 * perf_aux_output_end() the address of the current head.  In full
+	 * trace mode the same function expects a size to move rb->aux_head
+	 * forward.
+	 */
+	if (buf->snapshot)
+		local_set(&buf->data_size, (cur * PAGE_SIZE) + offset);
+	else
+		local_add(to_read, &buf->data_size);
+
+	etb_enable_hw(drvdata);
+	CS_LOCK(drvdata->base);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+}
+
 static const struct coresight_ops_sink etb_sink_ops = {
 	.enable		= etb_enable,
 	.disable	= etb_disable,
+	.alloc_buffer	= etb_alloc_buffer,
+	.free_buffer	= etb_free_buffer,
+	.set_buffer	= etb_set_buffer,
+	.reset_buffer	= etb_reset_buffer,
+	.update_buffer	= etb_update_buffer,
 };
 
 static const struct coresight_ops etb_cs_ops = {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 9fa92dcdd2ea..385d62e64abb 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -182,12 +182,29 @@ struct coresight_device {
 /**
  * struct coresight_ops_sink - basic operations for a sink
  * Operations available for sinks
- * @enable:	enables the sink.
- * @disable:	disables the sink.
+ * @enable:		enables the sink.
+ * @disable:		disables the sink.
+ * @alloc_buffer:	initialises perf's ring buffer for trace collection.
+ * @free_buffer:	release memory allocated in @get_config.
+ * @set_buffer:		initialises buffer mechanic before a trace session.
+ * @reset_buffer:	finalises buffer mechanic after a trace session.
+ * @update_buffer:	update buffer pointers after a trace session.
  */
 struct coresight_ops_sink {
 	int (*enable)(struct coresight_device *csdev, u32 mode);
 	void (*disable)(struct coresight_device *csdev);
+	void *(*alloc_buffer)(struct coresight_device *csdev, int cpu,
+			      void **pages, int nr_pages, bool overwrite);
+	void (*free_buffer)(void *config);
+	int (*set_buffer)(struct coresight_device *csdev,
+			  struct perf_output_handle *handle,
+			  void *sink_config);
+	unsigned long (*reset_buffer)(struct coresight_device *csdev,
+				      struct perf_output_handle *handle,
+				      void *sink_config, bool *lost);
+	void (*update_buffer)(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config);
 };
 
 /**
-- 
2.1.4

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

* [PATCH V8 16/23] coresight: etb10: implementing AUX API
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Adding an ETB10 specific AUX area operations to be used
by the perf framework when events are initialised.

Part of this operation involves modeling the mmap'ed area
based on the specific ways a sink buffer gathers information.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 237 ++++++++++++++++++++++++++
 include/linux/coresight.h                     |  21 ++-
 2 files changed, 256 insertions(+), 2 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 1581dcea6f03..36f99cd7c4ff 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -28,6 +28,11 @@
 #include <linux/coresight.h>
 #include <linux/amba/bus.h>
 #include <linux/clk.h>
+#include <linux/circ_buf.h>
+#include <linux/mm.h>
+#include <linux/perf_event.h>
+
+#include <asm/local.h>
 
 #include "coresight-priv.h"
 
@@ -65,6 +70,26 @@
 #define ETB_FRAME_SIZE_WORDS	4
 
 /**
+ * struct cs_buffer - keep track of a recording session' specifics
+ * @cur:	index of the current buffer
+ * @nr_pages:	max number of pages granted to us
+ * @offset:	offset within the current buffer
+ * @data_size:	how much we collected in this run
+ * @lost:	other than zero if we had a HW buffer wrap around
+ * @snapshot:	is this run in snapshot mode
+ * @data_pages:	a handle the ring buffer
+ */
+struct cs_buffers {
+	unsigned int		cur;
+	unsigned int		nr_pages;
+	unsigned long		offset;
+	local_t			data_size;
+	local_t			lost;
+	bool			snapshot;
+	void			**data_pages;
+};
+
+/**
  * struct etb_drvdata - specifics associated to an ETB component
  * @base:	memory mapped base address for this component.
  * @dev:	the device entity associated to this component.
@@ -265,9 +290,221 @@ static void etb_disable(struct coresight_device *csdev)
 	dev_info(drvdata->dev, "ETB disabled\n");
 }
 
+static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu,
+			      void **pages, int nr_pages, bool overwrite)
+{
+	int node;
+	struct cs_buffers *buf;
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node);
+	if (!buf)
+		return NULL;
+
+	buf->snapshot = overwrite;
+	buf->nr_pages = nr_pages;
+	buf->data_pages = pages;
+
+	return buf;
+}
+
+static void etb_free_buffer(void *config)
+{
+	struct cs_buffers *buf = config;
+
+	kfree(buf);
+}
+
+static int etb_set_buffer(struct coresight_device *csdev,
+			  struct perf_output_handle *handle,
+			  void *sink_config)
+{
+	int ret = 0;
+	unsigned long head;
+	struct cs_buffers *buf = sink_config;
+
+	/* wrap head around to the amount of space we have */
+	head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1);
+
+	/* find the page to write to */
+	buf->cur = head / PAGE_SIZE;
+
+	/* and offset within that page */
+	buf->offset = head % PAGE_SIZE;
+
+	local_set(&buf->data_size, 0);
+
+	return ret;
+}
+
+static unsigned long etb_reset_buffer(struct coresight_device *csdev,
+				      struct perf_output_handle *handle,
+				      void *sink_config, bool *lost)
+{
+	unsigned long size = 0;
+	struct cs_buffers *buf = sink_config;
+
+	if (buf) {
+		/*
+		 * In snapshot mode ->data_size holds the new address of the
+		 * ring buffer's head.  The size itself is the whole address
+		 * range since we want the latest information.
+		 */
+		if (buf->snapshot)
+			handle->head = local_xchg(&buf->data_size,
+						  buf->nr_pages << PAGE_SHIFT);
+
+		/*
+		 * Tell the tracer PMU how much we got in this run and if
+		 * something went wrong along the way.  Nobody else can use
+		 * this cs_buffers instance until we are done.  As such
+		 * resetting parameters here and squaring off with the ring
+		 * buffer API in the tracer PMU is fine.
+		 */
+		*lost = local_xchg(&buf->lost, 0);
+		size = local_xchg(&buf->data_size, 0);
+	}
+
+	return size;
+}
+
+static void etb_update_buffer(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config)
+{
+	int i, cur;
+	u8 *buf_ptr;
+	u32 read_ptr, write_ptr, capacity;
+	u32 status, read_data, to_read;
+	unsigned long flags, offset;
+	struct cs_buffers *buf = sink_config;
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (!buf)
+		return;
+
+	capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	CS_UNLOCK(drvdata->base);
+	etb_disable_hw(drvdata);
+
+	/* unit is in words, not bytes */
+	read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER);
+	write_ptr = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER);
+
+	/*
+	 * Entries should be aligned to the frame size.  If they are not
+	 * go back to the last alignement point to give decoding tools a
+	 * chance to fix things.
+	 */
+	if (write_ptr % ETB_FRAME_SIZE_WORDS) {
+		dev_err(drvdata->dev,
+			"write_ptr: %lu not aligned to formatter frame size\n",
+			(unsigned long)write_ptr);
+
+		write_ptr &= ~(ETB_FRAME_SIZE_WORDS - 1);
+		local_inc(&buf->lost);
+	}
+
+	/*
+	 * Get a hold of the status register and see if a wrap around
+	 * has occurred.  If so adjust things accordingly.  Otherwise
+	 * start at the beginning and go until the write pointer has
+	 * been reached.
+	 */
+	status = readl_relaxed(drvdata->base + ETB_STATUS_REG);
+	if (status & ETB_STATUS_RAM_FULL) {
+		local_inc(&buf->lost);
+		to_read = capacity;
+		read_ptr = write_ptr;
+	} else {
+		to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->buffer_depth);
+		to_read *= ETB_FRAME_SIZE_WORDS;
+	}
+
+	/*
+	 * Make sure we don't overwrite data that hasn't been consumed yet.
+	 * It is entirely possible that the HW buffer has more data than the
+	 * ring buffer can currently handle.  If so adjust the start address
+	 * to take only the last traces.
+	 *
+	 * In snapshot mode we are looking to get the latest traces only and as
+	 * such, we don't care about not overwriting data that hasn't been
+	 * processed by user space.
+	 */
+	if (!buf->snapshot && to_read > handle->size) {
+		u32 mask = ~(ETB_FRAME_SIZE_WORDS - 1);
+
+		/* The new read pointer must be frame size aligned */
+		to_read -= handle->size & mask;
+		/*
+		 * Move the RAM read pointer up, keeping in mind that
+		 * everything is in frame size units.
+		 */
+		read_ptr = (write_ptr + drvdata->buffer_depth) -
+					to_read / ETB_FRAME_SIZE_WORDS;
+		/* Wrap around if need be*/
+		read_ptr &= ~(drvdata->buffer_depth - 1);
+		/* let the decoder know we've skipped ahead */
+		local_inc(&buf->lost);
+	}
+
+	/* finally tell HW where we want to start reading from */
+	writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER);
+
+	cur = buf->cur;
+	offset = buf->offset;
+	for (i = 0; i < to_read; i += 4) {
+		buf_ptr = buf->data_pages[cur] + offset;
+		read_data = readl_relaxed(drvdata->base +
+					  ETB_RAM_READ_DATA_REG);
+		*buf_ptr++ = read_data >> 0;
+		*buf_ptr++ = read_data >> 8;
+		*buf_ptr++ = read_data >> 16;
+		*buf_ptr++ = read_data >> 24;
+
+		offset += 4;
+		if (offset >= PAGE_SIZE) {
+			offset = 0;
+			cur++;
+			/* wrap around at the end of the buffer */
+			cur &= buf->nr_pages - 1;
+		}
+	}
+
+	/* reset ETB buffer for next run */
+	writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER);
+	writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER);
+
+	/*
+	 * In snapshot mode all we have to do is communicate to
+	 * perf_aux_output_end() the address of the current head.  In full
+	 * trace mode the same function expects a size to move rb->aux_head
+	 * forward.
+	 */
+	if (buf->snapshot)
+		local_set(&buf->data_size, (cur * PAGE_SIZE) + offset);
+	else
+		local_add(to_read, &buf->data_size);
+
+	etb_enable_hw(drvdata);
+	CS_LOCK(drvdata->base);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+}
+
 static const struct coresight_ops_sink etb_sink_ops = {
 	.enable		= etb_enable,
 	.disable	= etb_disable,
+	.alloc_buffer	= etb_alloc_buffer,
+	.free_buffer	= etb_free_buffer,
+	.set_buffer	= etb_set_buffer,
+	.reset_buffer	= etb_reset_buffer,
+	.update_buffer	= etb_update_buffer,
 };
 
 static const struct coresight_ops etb_cs_ops = {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 9fa92dcdd2ea..385d62e64abb 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -182,12 +182,29 @@ struct coresight_device {
 /**
  * struct coresight_ops_sink - basic operations for a sink
  * Operations available for sinks
- * @enable:	enables the sink.
- * @disable:	disables the sink.
+ * @enable:		enables the sink.
+ * @disable:		disables the sink.
+ * @alloc_buffer:	initialises perf's ring buffer for trace collection.
+ * @free_buffer:	release memory allocated in @get_config.
+ * @set_buffer:		initialises buffer mechanic before a trace session.
+ * @reset_buffer:	finalises buffer mechanic after a trace session.
+ * @update_buffer:	update buffer pointers after a trace session.
  */
 struct coresight_ops_sink {
 	int (*enable)(struct coresight_device *csdev, u32 mode);
 	void (*disable)(struct coresight_device *csdev);
+	void *(*alloc_buffer)(struct coresight_device *csdev, int cpu,
+			      void **pages, int nr_pages, bool overwrite);
+	void (*free_buffer)(void *config);
+	int (*set_buffer)(struct coresight_device *csdev,
+			  struct perf_output_handle *handle,
+			  void *sink_config);
+	unsigned long (*reset_buffer)(struct coresight_device *csdev,
+				      struct perf_output_handle *handle,
+				      void *sink_config, bool *lost);
+	void (*update_buffer)(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config);
 };
 
 /**
-- 
2.1.4

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

* [PATCH V8 17/23] coresight: updating documentation to reflect integration with perf
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Adding a new section giving information on how coresight has been
integrated with the perf subsystem along with a general idea of how
to control tracing from the perf tool cmd line.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 Documentation/trace/coresight.txt | 138 ++++++++++++++++++++++++++++++++++----
 1 file changed, 124 insertions(+), 14 deletions(-)

diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt
index 0a5c3290e732..9515206b4b1a 100644
--- a/Documentation/trace/coresight.txt
+++ b/Documentation/trace/coresight.txt
@@ -20,13 +20,13 @@ Components are generally categorised as source, link and sinks and are
 
 "Sources" generate a compressed stream representing the processor instruction
 path based on tracing scenarios as configured by users.  From there the stream
-flows through the coresight system (via ATB bus) using links that are connecting
-the emanating source to a sink(s).  Sinks serve as endpoints to the coresight
+flows through the Coresight system (via ATB bus) using links that are connecting
+the emanating source to a sink(s).  Sinks serve as endpoints to the Coresight
 implementation, either storing the compressed stream in a memory buffer or
 creating an interface to the outside world where data can be transferred to a
-host without fear of filling up the onboard coresight memory buffer.
+host without fear of filling up the onboard Coresight memory buffer.
 
-At typical coresight system would look like this:
+At typical Coresight system would look like this:
 
   *****************************************************************
  **************************** AMBA AXI  ****************************===||
@@ -83,8 +83,8 @@ While on target configuration of the components is done via the APB bus,
 all trace data are carried out-of-band on the ATB bus.  The CTM provides
 a way to aggregate and distribute signals between CoreSight components.
 
-The coresight framework provides a central point to represent, configure and
-manage coresight devices on a platform.  This first implementation centers on
+The Coresight framework provides a central point to represent, configure and
+manage Coresight devices on a platform.  This first implementation centers on
 the basic tracing functionality, enabling components such ETM/PTM, funnel,
 replicator, TMC, TPIU and ETB.  Future work will enable more
 intricate IP blocks such as STM and CTI.
@@ -129,11 +129,11 @@ expected to be added as the solution matures.
 Framework and implementation
 ----------------------------
 
-The coresight framework provides a central point to represent, configure and
-manage coresight devices on a platform.  Any coresight compliant device can
+The Coresight framework provides a central point to represent, configure and
+manage Coresight devices on a platform.  Any Coresight compliant device can
 register with the framework for as long as they use the right APIs:
 
-struct coresight_device *coresight_register(struct coresight_desc *desc);
+struct Coresight_device *coresight_register(struct coresight_desc *desc);
 void coresight_unregister(struct coresight_device *csdev);
 
 The registering function is taking a "struct coresight_device *csdev" and
@@ -193,10 +193,120 @@ the information carried in "THIS_MODULE".
 How to use
 ----------
 
-Before trace collection can start, a coresight sink needs to be identify.
-There is no limit on the amount of sinks (nor sources) that can be enabled at
-any given moment.  As a generic operation, all device pertaining to the sink
-class will have an "active" entry in sysfs:
+There is two ways to use the Coresight framework: 1) using the perf cmd line
+tool and 2) interacting directly with the Coresight devices using the sysFS
+interface.  The latter will slowly be faded out as more functionality become
+available from the perf cmd line tool but for the time being both are still
+supported.  The following sections provide details on using both methods.
+
+1) Using perf framework:
+
+Coresight tracers like ETM and PTM are represented using the Perf framework's
+Performance Monitoring Unit (PMU).  As such the perf framework takes charge of
+controlling when tracing happens based on when the process(es) of interest are
+scheduled.  When configure in a system, Coresight PMUs will be listed when
+queried by the perf command line tool:
+
+linaro@linaro-nano:~$ ./perf list pmu
+
+List of pre-defined events (to be used in -e):
+
+  cs_etm//                                           [Kernel PMU event]
+
+linaro@linaro-nano:~$
+
+Regardless of the amount ETM/PTM IP block in a system (usually equal to the
+amount of processor core), the "cs_etm" PMU will be listed only once.
+
+Before a trace can be configured and started a Coresight sink needs to be
+selected using the sysFS method (see below).  This is only temporary until
+sink selection can be made from the command line tool.
+
+linaro@linaro-nano:~$ ls /sys/bus/coresight/devices
+20010000.etb  20030000.tpiu  20040000.funnel  2201c000.ptm
+2201d000.ptm  2203c000.etm  2203d000.etm  2203e000.etm  replicator
+
+linaro@linaro-nano:~$ echo 1 > /sys/bus/coresight/devices/20010000.etb/enable_sink
+
+Once a sink has been selected configuring a Coresight PMU works the same way as
+any other PMU.  As such tracing can happen for a single CPU, a group of CPU, per
+thread or a combination of those:
+
+linaro@linaro-nano:~$ perf record -e cs_etm// --per-thread <command>
+
+linaro@linaro-nano:~$ perf record -C 0,2-3 -e cs_etm// <command>
+
+Tracing limited to user and kernel space can also be used to narrow the amount
+of collected traces:
+
+linaro@linaro-nano:~$ perf record -e cs_etm//u --per-thread <command>
+
+linaro@linaro-nano:~$ perf record -C 0,2-3 -e cs_etm//k <command>
+
+As of this writing two ETM/PTM specific options have are available: cycle
+accurate and timestamp (please refer to the Embedded Trace Macrocell reference
+manual for details on these options).  By default both are disabled but using
+the "cycacc" and "timestamp" mnemonic within the double '/' will see those
+options configure for the upcoming trace run:
+
+linaro@linaro-nano:~$ perf record -e cs_etm/cycacc/ --per-thread <command>
+
+linaro@linaro-nano:~$ perf record -C 0,2-3 -e cs_etm/cycacc,timestamp/ <command>
+
+The Coresight PMUs can be configured to work in "full trace" or "snapshot" mode.
+In full trace mode trace acquisition is enabled from beginning to end with trace
+data being recorded continuously:
+
+linaro@linaro-nano:~$ perf record -e cs_etm// dd if=/dev/random of=./test.txt bs=1k count=1000
+
+Since this can lead to a significant amount of data and because some devices are
+limited in disk space snapshot mode can be used instead.  In snapshot mode
+traces are still collected in the ring buffer but not communicated to user
+space.  The ring buffer is allowed to wrap around, providing the latest
+information before an event of interest happens.  Significant events are
+communicated by sending a USR2 signal to the user space command line tool.
+From there the tool will stop trace collection and harvest data from the ring
+buffer before re-enabling traces.  Snapshot mode can be invoked using '-S' when
+launching a trace collection:
+
+linaro@linaro-nano:~$ perf record -S -e cs_etm// dd if=/dev/random of=./test.txt bs=1k count=1000
+
+Trace data collected during trace runs ends up in the "perf.data" file.  Trace
+configuration information necessary for trace decoding is also embedded in the
+"perf.data" file.  Two new headers, 'PERF_RECORD_AUXTRACE_INFO' and
+'PERF_RECORD_AUXTRACE' have been added to the list of event types in order to
+find out where the different sections start.
+
+It is worth noting that a set of metadata information exists for each tracer
+that participated in a trace run.  As such if 5 processors have been engaged,
+5 sets of metadata will be found in the perf.data file.  This is to ensure that
+tracer decompression tools have all the information they need in order to
+process the trace data.
+
+Metadata information is collected directly from the ETM/PTM management registers
+using the sysFS interface.  Since there is no way for the perf command line
+tool to associate a CPU with a tracer, a symbolic link has been created between
+the cs_etm sysFS event directory and each Coresight tracer:
+
+linaro@linaro-nano:~$ ls /sys/bus/event_source/devices/cs_etm
+cpu0  cpu1  cpu2  cpu3  cpu4  format  perf_event_mux_interval_ms
+power  subsystem  type  uevent
+
+linaro@linaro-nano:~$ ls /sys/bus/event_source/devices/cs_etm/cpu0/mgmt/
+etmccer  etmccr  etmcr  etmidr  etmscr  etmtecr1  etmtecr2
+etmteevr  etmtraceidr  etmtssvr
+
+2) Using the sysFS interface:
+
+Most, if not all, configuration registers are made available to users via the
+sysFS interface.  Until all Coresight ETM drivers have been converted to perf,
+it will also be possible to start and stop traces from sysFS.
+
+As with the perf method described above, a Coresight sink needs to be identify
+before trace collection can commence.  Using the sysFS method _only_, there is
+no limit on the amount of sinks (nor sources) that can be enabled at
+any given moment.  As a generic operation, all devices pertaining to the sink
+class will have an "enable_sink" entry in sysfs:
 
 root:/sys/bus/coresight/devices# ls
 replicator  20030000.tpiu    2201c000.ptm  2203c000.etm  2203e000.etm
@@ -246,7 +356,7 @@ The file cstrace.bin can be decompressed using "ptm2human", DS-5 or Trace32.
 
 Following is a DS-5 output of an experimental loop that increments a variable up
 to a certain value.  The example is simple and yet provides a glimpse of the
-wealth of possibilities that coresight provides.
+wealth of possibilities that Coresight provides.
 
 Info                                    Tracing enabled
 Instruction     106378866       0x8026B53C      E52DE004        false   PUSH     {lr}
-- 
2.1.4

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

* [PATCH V8 17/23] coresight: updating documentation to reflect integration with perf
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Adding a new section giving information on how coresight has been
integrated with the perf subsystem along with a general idea of how
to control tracing from the perf tool cmd line.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 Documentation/trace/coresight.txt | 138 ++++++++++++++++++++++++++++++++++----
 1 file changed, 124 insertions(+), 14 deletions(-)

diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt
index 0a5c3290e732..9515206b4b1a 100644
--- a/Documentation/trace/coresight.txt
+++ b/Documentation/trace/coresight.txt
@@ -20,13 +20,13 @@ Components are generally categorised as source, link and sinks and are
 
 "Sources" generate a compressed stream representing the processor instruction
 path based on tracing scenarios as configured by users.  From there the stream
-flows through the coresight system (via ATB bus) using links that are connecting
-the emanating source to a sink(s).  Sinks serve as endpoints to the coresight
+flows through the Coresight system (via ATB bus) using links that are connecting
+the emanating source to a sink(s).  Sinks serve as endpoints to the Coresight
 implementation, either storing the compressed stream in a memory buffer or
 creating an interface to the outside world where data can be transferred to a
-host without fear of filling up the onboard coresight memory buffer.
+host without fear of filling up the onboard Coresight memory buffer.
 
-At typical coresight system would look like this:
+At typical Coresight system would look like this:
 
   *****************************************************************
  **************************** AMBA AXI  ****************************===||
@@ -83,8 +83,8 @@ While on target configuration of the components is done via the APB bus,
 all trace data are carried out-of-band on the ATB bus.  The CTM provides
 a way to aggregate and distribute signals between CoreSight components.
 
-The coresight framework provides a central point to represent, configure and
-manage coresight devices on a platform.  This first implementation centers on
+The Coresight framework provides a central point to represent, configure and
+manage Coresight devices on a platform.  This first implementation centers on
 the basic tracing functionality, enabling components such ETM/PTM, funnel,
 replicator, TMC, TPIU and ETB.  Future work will enable more
 intricate IP blocks such as STM and CTI.
@@ -129,11 +129,11 @@ expected to be added as the solution matures.
 Framework and implementation
 ----------------------------
 
-The coresight framework provides a central point to represent, configure and
-manage coresight devices on a platform.  Any coresight compliant device can
+The Coresight framework provides a central point to represent, configure and
+manage Coresight devices on a platform.  Any Coresight compliant device can
 register with the framework for as long as they use the right APIs:
 
-struct coresight_device *coresight_register(struct coresight_desc *desc);
+struct Coresight_device *coresight_register(struct coresight_desc *desc);
 void coresight_unregister(struct coresight_device *csdev);
 
 The registering function is taking a "struct coresight_device *csdev" and
@@ -193,10 +193,120 @@ the information carried in "THIS_MODULE".
 How to use
 ----------
 
-Before trace collection can start, a coresight sink needs to be identify.
-There is no limit on the amount of sinks (nor sources) that can be enabled at
-any given moment.  As a generic operation, all device pertaining to the sink
-class will have an "active" entry in sysfs:
+There is two ways to use the Coresight framework: 1) using the perf cmd line
+tool and 2) interacting directly with the Coresight devices using the sysFS
+interface.  The latter will slowly be faded out as more functionality become
+available from the perf cmd line tool but for the time being both are still
+supported.  The following sections provide details on using both methods.
+
+1) Using perf framework:
+
+Coresight tracers like ETM and PTM are represented using the Perf framework's
+Performance Monitoring Unit (PMU).  As such the perf framework takes charge of
+controlling when tracing happens based on when the process(es) of interest are
+scheduled.  When configure in a system, Coresight PMUs will be listed when
+queried by the perf command line tool:
+
+linaro at linaro-nano:~$ ./perf list pmu
+
+List of pre-defined events (to be used in -e):
+
+  cs_etm//                                           [Kernel PMU event]
+
+linaro at linaro-nano:~$
+
+Regardless of the amount ETM/PTM IP block in a system (usually equal to the
+amount of processor core), the "cs_etm" PMU will be listed only once.
+
+Before a trace can be configured and started a Coresight sink needs to be
+selected using the sysFS method (see below).  This is only temporary until
+sink selection can be made from the command line tool.
+
+linaro at linaro-nano:~$ ls /sys/bus/coresight/devices
+20010000.etb  20030000.tpiu  20040000.funnel  2201c000.ptm
+2201d000.ptm  2203c000.etm  2203d000.etm  2203e000.etm  replicator
+
+linaro at linaro-nano:~$ echo 1 > /sys/bus/coresight/devices/20010000.etb/enable_sink
+
+Once a sink has been selected configuring a Coresight PMU works the same way as
+any other PMU.  As such tracing can happen for a single CPU, a group of CPU, per
+thread or a combination of those:
+
+linaro at linaro-nano:~$ perf record -e cs_etm// --per-thread <command>
+
+linaro at linaro-nano:~$ perf record -C 0,2-3 -e cs_etm// <command>
+
+Tracing limited to user and kernel space can also be used to narrow the amount
+of collected traces:
+
+linaro at linaro-nano:~$ perf record -e cs_etm//u --per-thread <command>
+
+linaro at linaro-nano:~$ perf record -C 0,2-3 -e cs_etm//k <command>
+
+As of this writing two ETM/PTM specific options have are available: cycle
+accurate and timestamp (please refer to the Embedded Trace Macrocell reference
+manual for details on these options).  By default both are disabled but using
+the "cycacc" and "timestamp" mnemonic within the double '/' will see those
+options configure for the upcoming trace run:
+
+linaro at linaro-nano:~$ perf record -e cs_etm/cycacc/ --per-thread <command>
+
+linaro at linaro-nano:~$ perf record -C 0,2-3 -e cs_etm/cycacc,timestamp/ <command>
+
+The Coresight PMUs can be configured to work in "full trace" or "snapshot" mode.
+In full trace mode trace acquisition is enabled from beginning to end with trace
+data being recorded continuously:
+
+linaro at linaro-nano:~$ perf record -e cs_etm// dd if=/dev/random of=./test.txt bs=1k count=1000
+
+Since this can lead to a significant amount of data and because some devices are
+limited in disk space snapshot mode can be used instead.  In snapshot mode
+traces are still collected in the ring buffer but not communicated to user
+space.  The ring buffer is allowed to wrap around, providing the latest
+information before an event of interest happens.  Significant events are
+communicated by sending a USR2 signal to the user space command line tool.
+From there the tool will stop trace collection and harvest data from the ring
+buffer before re-enabling traces.  Snapshot mode can be invoked using '-S' when
+launching a trace collection:
+
+linaro at linaro-nano:~$ perf record -S -e cs_etm// dd if=/dev/random of=./test.txt bs=1k count=1000
+
+Trace data collected during trace runs ends up in the "perf.data" file.  Trace
+configuration information necessary for trace decoding is also embedded in the
+"perf.data" file.  Two new headers, 'PERF_RECORD_AUXTRACE_INFO' and
+'PERF_RECORD_AUXTRACE' have been added to the list of event types in order to
+find out where the different sections start.
+
+It is worth noting that a set of metadata information exists for each tracer
+that participated in a trace run.  As such if 5 processors have been engaged,
+5 sets of metadata will be found in the perf.data file.  This is to ensure that
+tracer decompression tools have all the information they need in order to
+process the trace data.
+
+Metadata information is collected directly from the ETM/PTM management registers
+using the sysFS interface.  Since there is no way for the perf command line
+tool to associate a CPU with a tracer, a symbolic link has been created between
+the cs_etm sysFS event directory and each Coresight tracer:
+
+linaro at linaro-nano:~$ ls /sys/bus/event_source/devices/cs_etm
+cpu0  cpu1  cpu2  cpu3  cpu4  format  perf_event_mux_interval_ms
+power  subsystem  type  uevent
+
+linaro at linaro-nano:~$ ls /sys/bus/event_source/devices/cs_etm/cpu0/mgmt/
+etmccer  etmccr  etmcr  etmidr  etmscr  etmtecr1  etmtecr2
+etmteevr  etmtraceidr  etmtssvr
+
+2) Using the sysFS interface:
+
+Most, if not all, configuration registers are made available to users via the
+sysFS interface.  Until all Coresight ETM drivers have been converted to perf,
+it will also be possible to start and stop traces from sysFS.
+
+As with the perf method described above, a Coresight sink needs to be identify
+before trace collection can commence.  Using the sysFS method _only_, there is
+no limit on the amount of sinks (nor sources) that can be enabled at
+any given moment.  As a generic operation, all devices pertaining to the sink
+class will have an "enable_sink" entry in sysfs:
 
 root:/sys/bus/coresight/devices# ls
 replicator  20030000.tpiu    2201c000.ptm  2203c000.etm  2203e000.etm
@@ -246,7 +356,7 @@ The file cstrace.bin can be decompressed using "ptm2human", DS-5 or Trace32.
 
 Following is a DS-5 output of an experimental loop that increments a variable up
 to a certain value.  The example is simple and yet provides a glimpse of the
-wealth of possibilities that coresight provides.
+wealth of possibilities that Coresight provides.
 
 Info                                    Tracing enabled
 Instruction     106378866       0x8026B53C      E52DE004        false   PUSH     {lr}
-- 
2.1.4

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

* [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier, Alexander Shishkin

Perf is a well known and used tool for performance monitoring
and much more. A such it is an ideal candidate for integration
with coresight based HW tracing.

This patch introduces a PMU that represent a coresight tracer to
the Perf core.

Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Makefile             |   3 +-
 drivers/hwtracing/coresight/coresight-etm-perf.c | 398 +++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm-perf.h |  32 ++
 drivers/hwtracing/coresight/coresight-etm3x.c    |   7 +
 include/linux/coresight-pmu.h                    |  27 ++
 5 files changed, 466 insertions(+), 1 deletion(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.h
 create mode 100644 include/linux/coresight-pmu.h

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 233d66cf22d3..cf8c6d689747 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
 obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
 					   coresight-replicator.o
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \
-					coresight-etm3x-sysfs.o
+					coresight-etm3x-sysfs.o \
+					coresight-etm-perf.o
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
 obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
new file mode 100644
index 000000000000..f5833b64b448
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/coresight.h>
+#include <linux/coresight-pmu.h>
+#include <linux/cpumask.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/perf_event.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "coresight-priv.h"
+
+static struct pmu etm_pmu;
+static bool etm_perf_up;
+
+/**
+ * struct etm_event_data - Coresight specifics associated to an event
+ * @work:		Handle to free allocated memory outside IRQ context.
+ * @mask:		Hold the CPU(s) this event was set for.
+ * @snk_config:		The sink configuration.
+ * @path:		An array of path, each slot for one CPU.
+ */
+struct etm_event_data {
+	struct work_struct work;
+	cpumask_t mask;
+	void *snk_config;
+	struct list_head **path;
+};
+
+static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle);
+static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
+
+/* ETMv3.5/PTM's ETMCR is 'config' */
+PMU_FORMAT_ATTR(cycacc,		"config:" __stringify(ETM_OPT_CYCACC));
+PMU_FORMAT_ATTR(timestamp,	"config:" __stringify(ETM_OPT_TS));
+
+static struct attribute *etm_config_formats_attr[] = {
+	&format_attr_cycacc.attr,
+	&format_attr_timestamp.attr,
+	NULL,
+};
+
+static struct attribute_group etm_pmu_format_group = {
+	.name   = "format",
+	.attrs  = etm_config_formats_attr,
+};
+
+static const struct attribute_group *etm_pmu_attr_groups[] = {
+	&etm_pmu_format_group,
+	NULL,
+};
+
+static void etm_event_read(struct perf_event *event) {}
+
+static int etm_event_init(struct perf_event *event)
+{
+	if (event->attr.type != etm_pmu.type)
+		return -ENOENT;
+
+	if (event->cpu >= nr_cpu_ids)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void free_event_data(struct work_struct *work)
+{
+	int cpu;
+	cpumask_t *mask;
+	struct etm_event_data *event_data;
+	struct coresight_device *sink;
+
+	event_data = container_of(work, struct etm_event_data, work);
+	mask = &event_data->mask;
+	/*
+	 * First deal with the sink configuration.  See comment in
+	 * etm_setup_aux() about why we take the first available path.
+	 */
+	if (event_data->snk_config) {
+		cpu = cpumask_first(mask);
+		sink = coresight_get_sink(event_data->path[cpu]);
+		if (sink_ops(sink)->free_buffer)
+			sink_ops(sink)->free_buffer(event_data->snk_config);
+	}
+
+	for_each_cpu(cpu, mask) {
+		if (event_data->path[cpu])
+			coresight_release_path(event_data->path[cpu]);
+	}
+
+	kfree(event_data->path);
+	kfree(event_data);
+	pr_err("freed event data\n");
+}
+
+static void *alloc_event_data(int cpu)
+{
+	int size;
+	cpumask_t *mask;
+	struct etm_event_data *event_data;
+
+	/* First get memory for the session's data */
+	event_data = kzalloc(sizeof(struct etm_event_data), GFP_KERNEL);
+	if (!event_data)
+		return NULL;
+
+	/* Make sure nothing disappears under us */
+	get_online_cpus();
+	size = num_online_cpus();
+
+	mask = &event_data->mask;
+	if (cpu != -1)
+		cpumask_set_cpu(cpu, mask);
+	else
+		cpumask_copy(mask, cpu_online_mask);
+	put_online_cpus();
+
+	/*
+	 * Each CPU has a single path between source and destination.  As such
+	 * allocate an array using CPU numbers as indexes.  That way a path
+	 * for any CPU can easily be accessed at any given time.  We proceed
+	 * the same way for sessions involving a single CPU.  The cost of
+	 * unused memory when dealing with single CPU trace scenarios is small
+	 * compared to the cost of searching through an optimized array.
+	 */
+	event_data->path = kcalloc(size,
+				   sizeof(struct list_head *), GFP_KERNEL);
+	if (!event_data->path) {
+		kfree(event_data);
+		return NULL;
+	}
+
+	return event_data;
+}
+
+static void etm_free_aux(void *data)
+{
+	struct etm_event_data *event_data = data;
+
+	pr_err("Queing work\n");
+	schedule_work(&event_data->work);
+}
+
+static void *etm_setup_aux(int event_cpu, void **pages,
+			   int nr_pages, bool overwrite)
+{
+	int cpu;
+	cpumask_t *mask;
+	struct coresight_device *sink;
+	struct etm_event_data *event_data = NULL;
+
+	event_data = alloc_event_data(event_cpu);
+	if (!event_data)
+		return NULL;
+
+	INIT_WORK(&event_data->work, free_event_data);
+
+	mask = &event_data->mask;
+
+	/* Setup the path for each CPU in a trace session */
+	for_each_cpu(cpu, mask) {
+		struct coresight_device *csdev;
+
+		csdev = per_cpu(csdev_src, cpu);
+		if (!csdev)
+			goto err;
+
+		/*
+		 * Building a path doesn't enable it, it simply builds a
+		 * list of devices from source to sink that can be
+		 * referenced later when the path is actually needed.
+		 */
+		event_data->path[cpu] = coresight_build_path(csdev);
+		if (!event_data->path[cpu])
+			goto err;
+	}
+
+	/*
+	 * In theory nothing prevent tracers in a trace session from being
+	 * associated with different sinks, nor having a sink per tracer.  But
+	 * until we have HW with this kind of topology and a way to convey
+	 * sink assignement from the perf cmd line we need to assume tracers
+	 * in a trace session are using the same sink.  Therefore pick the sink
+	 * found at the end of the first available path.
+	 */
+	cpu = cpumask_first(mask);
+	/* Grab the sink at the end of the path */
+	sink = coresight_get_sink(event_data->path[cpu]);
+	if (!sink)
+		goto err;
+
+	if (!sink_ops(sink)->alloc_buffer)
+		goto err;
+
+	/* Get the AUX specific data from the sink buffer */
+	event_data->snk_config =
+			sink_ops(sink)->alloc_buffer(sink, cpu, pages,
+						     nr_pages, overwrite);
+	if (!event_data->snk_config)
+		goto err;
+
+out:
+	return event_data;
+
+err:
+	etm_free_aux(event_data);
+	event_data = NULL;
+	goto out;
+}
+
+static void etm_event_start(struct perf_event *event, int flags)
+{
+	int cpu = smp_processor_id();
+	struct etm_event_data *event_data;
+	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
+	struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
+
+	if (!csdev)
+		goto fail;
+
+	/*
+	 * Deal with the ring buffer API and get a handle on the
+	 * session's information.
+	 */
+	event_data = perf_aux_output_begin(handle, event);
+	if (WARN_ON_ONCE(!event_data))
+		goto fail;
+
+	/* We need a sink, no need to continue without one */
+	sink = coresight_get_sink(event_data->path[cpu]);
+	if (!sink || !sink_ops(sink)->set_buffer)
+		goto fail_end_stop;
+
+	/* Configure the sink */
+	if (sink_ops(sink)->set_buffer(sink, handle,
+				       event_data->snk_config))
+		goto fail_end_stop;
+
+	/* Nothing will happen without a path */
+	if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF))
+		goto fail_end_stop;
+
+	/* Tell the perf core the event is alive */
+	event->hw.state = 0;
+
+	/* Finally enable the tracer */
+	if (source_ops(csdev)->enable(csdev, &event->attr, CS_MODE_PERF))
+		goto fail_end_stop;
+
+out:
+	return;
+
+fail_end_stop:
+	perf_aux_output_end(handle, 0, true);
+fail:
+	event->hw.state = PERF_HES_STOPPED;
+	goto out;
+}
+
+static void etm_event_stop(struct perf_event *event, int mode)
+{
+	bool lost;
+	int cpu = smp_processor_id();
+	unsigned long size;
+	struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
+	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
+	struct etm_event_data *event_data = perf_get_aux(handle);
+
+	if (event->hw.state == PERF_HES_STOPPED)
+		return;
+
+	if (!csdev)
+		return;
+
+	sink = coresight_get_sink(event_data->path[cpu]);
+	if (!sink)
+		return;
+
+	/* stop tracer */
+	source_ops(csdev)->disable(csdev);
+
+	/* tell the core */
+	event->hw.state = PERF_HES_STOPPED;
+
+	if (mode & PERF_EF_UPDATE) {
+		if (WARN_ON_ONCE(handle->event != event))
+			return;
+
+		/* update trace information */
+		if (!sink_ops(sink)->update_buffer)
+			return;
+
+		sink_ops(sink)->update_buffer(sink, handle,
+					      event_data->snk_config);
+
+		if (!sink_ops(sink)->reset_buffer)
+			return;
+
+		size = sink_ops(sink)->reset_buffer(sink, handle,
+						    event_data->snk_config,
+						    &lost);
+
+		perf_aux_output_end(handle, size, lost);
+	}
+
+	/* Disabling the path make its elements available to other sessions */
+	coresight_disable_path(event_data->path[cpu]);
+}
+
+static int etm_event_add(struct perf_event *event, int mode)
+{
+	int ret = 0;
+	struct hw_perf_event *hwc = &event->hw;
+
+	if (mode & PERF_EF_START) {
+		etm_event_start(event, 0);
+		if (hwc->state & PERF_HES_STOPPED)
+			ret = -EINVAL;
+	} else {
+		hwc->state = PERF_HES_STOPPED;
+	}
+
+	return ret;
+}
+
+static void etm_event_del(struct perf_event *event, int mode)
+{
+	etm_event_stop(event, PERF_EF_UPDATE);
+}
+
+int etm_perf_symlink(struct coresight_device *csdev, bool link)
+{
+	char entry[sizeof("cpu9999999")];
+	int ret = 0, cpu = source_ops(csdev)->cpu_id(csdev);
+	struct device *pmu_dev = etm_pmu.dev;
+	struct device *cs_dev = &csdev->dev;
+
+	sprintf(entry, "cpu%d", cpu);
+
+	if (!etm_perf_up)
+		return -EPROBE_DEFER;
+
+	if (link) {
+		ret = sysfs_create_link(&pmu_dev->kobj, &cs_dev->kobj, entry);
+		if (ret)
+			return ret;
+		per_cpu(csdev_src, cpu) = csdev;
+	} else {
+		sysfs_remove_link(&pmu_dev->kobj, entry);
+		per_cpu(csdev_src, cpu) = NULL;
+	}
+
+	return 0;
+}
+
+static int __init etm_perf_init(void)
+{
+	int ret;
+
+	etm_pmu.capabilities	= PERF_PMU_CAP_EXCLUSIVE;
+
+	etm_pmu.attr_groups	= etm_pmu_attr_groups;
+	etm_pmu.task_ctx_nr	= perf_sw_context;
+	etm_pmu.read		= etm_event_read;
+	etm_pmu.event_init	= etm_event_init;
+	etm_pmu.setup_aux	= etm_setup_aux;
+	etm_pmu.free_aux	= etm_free_aux;
+	etm_pmu.start		= etm_event_start;
+	etm_pmu.stop		= etm_event_stop;
+	etm_pmu.add		= etm_event_add;
+	etm_pmu.del		= etm_event_del;
+
+	ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
+	if (ret == 0)
+		etm_perf_up = true;
+
+	return ret;
+}
+module_init(etm_perf_init);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h
new file mode 100644
index 000000000000..87f5a134eb6f
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CORESIGHT_ETM_PERF_H
+#define _CORESIGHT_ETM_PERF_H
+
+struct coresight_device;
+
+#ifdef CONFIG_CORESIGHT
+int etm_perf_symlink(struct coresight_device *csdev, bool link);
+
+#else
+static inline int etm_perf_symlink(struct coresight_device *csdev, bool link)
+{ return -EINVAL; }
+
+#endif /* CONFIG_CORESIGHT */
+
+#endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index a96129a0e89f..8055b2d246ad 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -35,6 +35,7 @@
 #include <asm/sections.h>
 
 #include "coresight-etm.h"
+#include "coresight-etm-perf.h"
 
 static int boot_enable;
 module_param_named(boot_enable, boot_enable, int, S_IRUGO);
@@ -827,6 +828,12 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 		goto err_arch_supported;
 	}
 
+	ret = etm_perf_symlink(drvdata->csdev, true);
+	if (ret) {
+		coresight_unregister(drvdata->csdev);
+		goto err_arch_supported;
+	}
+
 	pm_runtime_put(&adev->dev);
 	dev_info(dev, "%s initialized\n", (char *)id->data);
 
diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h
new file mode 100644
index 000000000000..6c5386b23b10
--- /dev/null
+++ b/include/linux/coresight-pmu.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LINUX_CORESIGHT_PMU_H
+#define _LINUX_CORESIGHT_PMU_H
+
+#define CORESIGHT_ETM_PMU_NAME "cs_etm"
+
+/* ETMv3.5/PTM's ETMCR config bit */
+#define ETM_OPT_CYCACC  12
+#define ETM_OPT_TS      28
+
+#endif
-- 
2.1.4

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

* [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Perf is a well known and used tool for performance monitoring
and much more. A such it is an ideal candidate for integration
with coresight based HW tracing.

This patch introduces a PMU that represent a coresight tracer to
the Perf core.

Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Makefile             |   3 +-
 drivers/hwtracing/coresight/coresight-etm-perf.c | 398 +++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm-perf.h |  32 ++
 drivers/hwtracing/coresight/coresight-etm3x.c    |   7 +
 include/linux/coresight-pmu.h                    |  27 ++
 5 files changed, 466 insertions(+), 1 deletion(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.h
 create mode 100644 include/linux/coresight-pmu.h

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 233d66cf22d3..cf8c6d689747 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
 obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
 					   coresight-replicator.o
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \
-					coresight-etm3x-sysfs.o
+					coresight-etm3x-sysfs.o \
+					coresight-etm-perf.o
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
 obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
new file mode 100644
index 000000000000..f5833b64b448
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/coresight.h>
+#include <linux/coresight-pmu.h>
+#include <linux/cpumask.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/perf_event.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "coresight-priv.h"
+
+static struct pmu etm_pmu;
+static bool etm_perf_up;
+
+/**
+ * struct etm_event_data - Coresight specifics associated to an event
+ * @work:		Handle to free allocated memory outside IRQ context.
+ * @mask:		Hold the CPU(s) this event was set for.
+ * @snk_config:		The sink configuration.
+ * @path:		An array of path, each slot for one CPU.
+ */
+struct etm_event_data {
+	struct work_struct work;
+	cpumask_t mask;
+	void *snk_config;
+	struct list_head **path;
+};
+
+static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle);
+static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
+
+/* ETMv3.5/PTM's ETMCR is 'config' */
+PMU_FORMAT_ATTR(cycacc,		"config:" __stringify(ETM_OPT_CYCACC));
+PMU_FORMAT_ATTR(timestamp,	"config:" __stringify(ETM_OPT_TS));
+
+static struct attribute *etm_config_formats_attr[] = {
+	&format_attr_cycacc.attr,
+	&format_attr_timestamp.attr,
+	NULL,
+};
+
+static struct attribute_group etm_pmu_format_group = {
+	.name   = "format",
+	.attrs  = etm_config_formats_attr,
+};
+
+static const struct attribute_group *etm_pmu_attr_groups[] = {
+	&etm_pmu_format_group,
+	NULL,
+};
+
+static void etm_event_read(struct perf_event *event) {}
+
+static int etm_event_init(struct perf_event *event)
+{
+	if (event->attr.type != etm_pmu.type)
+		return -ENOENT;
+
+	if (event->cpu >= nr_cpu_ids)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void free_event_data(struct work_struct *work)
+{
+	int cpu;
+	cpumask_t *mask;
+	struct etm_event_data *event_data;
+	struct coresight_device *sink;
+
+	event_data = container_of(work, struct etm_event_data, work);
+	mask = &event_data->mask;
+	/*
+	 * First deal with the sink configuration.  See comment in
+	 * etm_setup_aux() about why we take the first available path.
+	 */
+	if (event_data->snk_config) {
+		cpu = cpumask_first(mask);
+		sink = coresight_get_sink(event_data->path[cpu]);
+		if (sink_ops(sink)->free_buffer)
+			sink_ops(sink)->free_buffer(event_data->snk_config);
+	}
+
+	for_each_cpu(cpu, mask) {
+		if (event_data->path[cpu])
+			coresight_release_path(event_data->path[cpu]);
+	}
+
+	kfree(event_data->path);
+	kfree(event_data);
+	pr_err("freed event data\n");
+}
+
+static void *alloc_event_data(int cpu)
+{
+	int size;
+	cpumask_t *mask;
+	struct etm_event_data *event_data;
+
+	/* First get memory for the session's data */
+	event_data = kzalloc(sizeof(struct etm_event_data), GFP_KERNEL);
+	if (!event_data)
+		return NULL;
+
+	/* Make sure nothing disappears under us */
+	get_online_cpus();
+	size = num_online_cpus();
+
+	mask = &event_data->mask;
+	if (cpu != -1)
+		cpumask_set_cpu(cpu, mask);
+	else
+		cpumask_copy(mask, cpu_online_mask);
+	put_online_cpus();
+
+	/*
+	 * Each CPU has a single path between source and destination.  As such
+	 * allocate an array using CPU numbers as indexes.  That way a path
+	 * for any CPU can easily be accessed at any given time.  We proceed
+	 * the same way for sessions involving a single CPU.  The cost of
+	 * unused memory when dealing with single CPU trace scenarios is small
+	 * compared to the cost of searching through an optimized array.
+	 */
+	event_data->path = kcalloc(size,
+				   sizeof(struct list_head *), GFP_KERNEL);
+	if (!event_data->path) {
+		kfree(event_data);
+		return NULL;
+	}
+
+	return event_data;
+}
+
+static void etm_free_aux(void *data)
+{
+	struct etm_event_data *event_data = data;
+
+	pr_err("Queing work\n");
+	schedule_work(&event_data->work);
+}
+
+static void *etm_setup_aux(int event_cpu, void **pages,
+			   int nr_pages, bool overwrite)
+{
+	int cpu;
+	cpumask_t *mask;
+	struct coresight_device *sink;
+	struct etm_event_data *event_data = NULL;
+
+	event_data = alloc_event_data(event_cpu);
+	if (!event_data)
+		return NULL;
+
+	INIT_WORK(&event_data->work, free_event_data);
+
+	mask = &event_data->mask;
+
+	/* Setup the path for each CPU in a trace session */
+	for_each_cpu(cpu, mask) {
+		struct coresight_device *csdev;
+
+		csdev = per_cpu(csdev_src, cpu);
+		if (!csdev)
+			goto err;
+
+		/*
+		 * Building a path doesn't enable it, it simply builds a
+		 * list of devices from source to sink that can be
+		 * referenced later when the path is actually needed.
+		 */
+		event_data->path[cpu] = coresight_build_path(csdev);
+		if (!event_data->path[cpu])
+			goto err;
+	}
+
+	/*
+	 * In theory nothing prevent tracers in a trace session from being
+	 * associated with different sinks, nor having a sink per tracer.  But
+	 * until we have HW with this kind of topology and a way to convey
+	 * sink assignement from the perf cmd line we need to assume tracers
+	 * in a trace session are using the same sink.  Therefore pick the sink
+	 * found at the end of the first available path.
+	 */
+	cpu = cpumask_first(mask);
+	/* Grab the sink at the end of the path */
+	sink = coresight_get_sink(event_data->path[cpu]);
+	if (!sink)
+		goto err;
+
+	if (!sink_ops(sink)->alloc_buffer)
+		goto err;
+
+	/* Get the AUX specific data from the sink buffer */
+	event_data->snk_config =
+			sink_ops(sink)->alloc_buffer(sink, cpu, pages,
+						     nr_pages, overwrite);
+	if (!event_data->snk_config)
+		goto err;
+
+out:
+	return event_data;
+
+err:
+	etm_free_aux(event_data);
+	event_data = NULL;
+	goto out;
+}
+
+static void etm_event_start(struct perf_event *event, int flags)
+{
+	int cpu = smp_processor_id();
+	struct etm_event_data *event_data;
+	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
+	struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
+
+	if (!csdev)
+		goto fail;
+
+	/*
+	 * Deal with the ring buffer API and get a handle on the
+	 * session's information.
+	 */
+	event_data = perf_aux_output_begin(handle, event);
+	if (WARN_ON_ONCE(!event_data))
+		goto fail;
+
+	/* We need a sink, no need to continue without one */
+	sink = coresight_get_sink(event_data->path[cpu]);
+	if (!sink || !sink_ops(sink)->set_buffer)
+		goto fail_end_stop;
+
+	/* Configure the sink */
+	if (sink_ops(sink)->set_buffer(sink, handle,
+				       event_data->snk_config))
+		goto fail_end_stop;
+
+	/* Nothing will happen without a path */
+	if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF))
+		goto fail_end_stop;
+
+	/* Tell the perf core the event is alive */
+	event->hw.state = 0;
+
+	/* Finally enable the tracer */
+	if (source_ops(csdev)->enable(csdev, &event->attr, CS_MODE_PERF))
+		goto fail_end_stop;
+
+out:
+	return;
+
+fail_end_stop:
+	perf_aux_output_end(handle, 0, true);
+fail:
+	event->hw.state = PERF_HES_STOPPED;
+	goto out;
+}
+
+static void etm_event_stop(struct perf_event *event, int mode)
+{
+	bool lost;
+	int cpu = smp_processor_id();
+	unsigned long size;
+	struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
+	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
+	struct etm_event_data *event_data = perf_get_aux(handle);
+
+	if (event->hw.state == PERF_HES_STOPPED)
+		return;
+
+	if (!csdev)
+		return;
+
+	sink = coresight_get_sink(event_data->path[cpu]);
+	if (!sink)
+		return;
+
+	/* stop tracer */
+	source_ops(csdev)->disable(csdev);
+
+	/* tell the core */
+	event->hw.state = PERF_HES_STOPPED;
+
+	if (mode & PERF_EF_UPDATE) {
+		if (WARN_ON_ONCE(handle->event != event))
+			return;
+
+		/* update trace information */
+		if (!sink_ops(sink)->update_buffer)
+			return;
+
+		sink_ops(sink)->update_buffer(sink, handle,
+					      event_data->snk_config);
+
+		if (!sink_ops(sink)->reset_buffer)
+			return;
+
+		size = sink_ops(sink)->reset_buffer(sink, handle,
+						    event_data->snk_config,
+						    &lost);
+
+		perf_aux_output_end(handle, size, lost);
+	}
+
+	/* Disabling the path make its elements available to other sessions */
+	coresight_disable_path(event_data->path[cpu]);
+}
+
+static int etm_event_add(struct perf_event *event, int mode)
+{
+	int ret = 0;
+	struct hw_perf_event *hwc = &event->hw;
+
+	if (mode & PERF_EF_START) {
+		etm_event_start(event, 0);
+		if (hwc->state & PERF_HES_STOPPED)
+			ret = -EINVAL;
+	} else {
+		hwc->state = PERF_HES_STOPPED;
+	}
+
+	return ret;
+}
+
+static void etm_event_del(struct perf_event *event, int mode)
+{
+	etm_event_stop(event, PERF_EF_UPDATE);
+}
+
+int etm_perf_symlink(struct coresight_device *csdev, bool link)
+{
+	char entry[sizeof("cpu9999999")];
+	int ret = 0, cpu = source_ops(csdev)->cpu_id(csdev);
+	struct device *pmu_dev = etm_pmu.dev;
+	struct device *cs_dev = &csdev->dev;
+
+	sprintf(entry, "cpu%d", cpu);
+
+	if (!etm_perf_up)
+		return -EPROBE_DEFER;
+
+	if (link) {
+		ret = sysfs_create_link(&pmu_dev->kobj, &cs_dev->kobj, entry);
+		if (ret)
+			return ret;
+		per_cpu(csdev_src, cpu) = csdev;
+	} else {
+		sysfs_remove_link(&pmu_dev->kobj, entry);
+		per_cpu(csdev_src, cpu) = NULL;
+	}
+
+	return 0;
+}
+
+static int __init etm_perf_init(void)
+{
+	int ret;
+
+	etm_pmu.capabilities	= PERF_PMU_CAP_EXCLUSIVE;
+
+	etm_pmu.attr_groups	= etm_pmu_attr_groups;
+	etm_pmu.task_ctx_nr	= perf_sw_context;
+	etm_pmu.read		= etm_event_read;
+	etm_pmu.event_init	= etm_event_init;
+	etm_pmu.setup_aux	= etm_setup_aux;
+	etm_pmu.free_aux	= etm_free_aux;
+	etm_pmu.start		= etm_event_start;
+	etm_pmu.stop		= etm_event_stop;
+	etm_pmu.add		= etm_event_add;
+	etm_pmu.del		= etm_event_del;
+
+	ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
+	if (ret == 0)
+		etm_perf_up = true;
+
+	return ret;
+}
+module_init(etm_perf_init);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h
new file mode 100644
index 000000000000..87f5a134eb6f
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CORESIGHT_ETM_PERF_H
+#define _CORESIGHT_ETM_PERF_H
+
+struct coresight_device;
+
+#ifdef CONFIG_CORESIGHT
+int etm_perf_symlink(struct coresight_device *csdev, bool link);
+
+#else
+static inline int etm_perf_symlink(struct coresight_device *csdev, bool link)
+{ return -EINVAL; }
+
+#endif /* CONFIG_CORESIGHT */
+
+#endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index a96129a0e89f..8055b2d246ad 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -35,6 +35,7 @@
 #include <asm/sections.h>
 
 #include "coresight-etm.h"
+#include "coresight-etm-perf.h"
 
 static int boot_enable;
 module_param_named(boot_enable, boot_enable, int, S_IRUGO);
@@ -827,6 +828,12 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 		goto err_arch_supported;
 	}
 
+	ret = etm_perf_symlink(drvdata->csdev, true);
+	if (ret) {
+		coresight_unregister(drvdata->csdev);
+		goto err_arch_supported;
+	}
+
 	pm_runtime_put(&adev->dev);
 	dev_info(dev, "%s initialized\n", (char *)id->data);
 
diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h
new file mode 100644
index 000000000000..6c5386b23b10
--- /dev/null
+++ b/include/linux/coresight-pmu.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LINUX_CORESIGHT_PMU_H
+#define _LINUX_CORESIGHT_PMU_H
+
+#define CORESIGHT_ETM_PMU_NAME "cs_etm"
+
+/* ETMv3.5/PTM's ETMCR config bit */
+#define ETM_OPT_CYCACC  12
+#define ETM_OPT_TS      28
+
+#endif
-- 
2.1.4

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

* [PATCH V8 19/23] coresight: introducing a global trace ID function
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

TraceID values have to be unique for all tracers and
consistent between drivers and user space.  As such
introducing a central function to be used whenever a
traceID value is required.

The patch also account for data traceIDs, which are usually
I(N) + 1.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c |  7 ++-----
 include/linux/coresight-pmu.h                 | 12 ++++++++++++
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 8055b2d246ad..70146865ff0d 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -27,6 +27,7 @@
 #include <linux/cpu.h>
 #include <linux/of.h>
 #include <linux/coresight.h>
+#include <linux/coresight-pmu.h>
 #include <linux/amba/bus.h>
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
@@ -740,11 +741,7 @@ static void etm_init_arch_data(void *info)
 
 static void etm_init_trace_id(struct etm_drvdata *drvdata)
 {
-	/*
-	 * A trace ID of value 0 is invalid, so let's start at some
-	 * random value that fits in 7 bits and go from there.
-	 */
-	drvdata->traceid = 0x10 + drvdata->cpu;
+	drvdata->traceid = coresight_get_trace_id(drvdata->cpu);
 }
 
 static int etm_probe(struct amba_device *adev, const struct amba_id *id)
diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h
index 6c5386b23b10..7d410260661b 100644
--- a/include/linux/coresight-pmu.h
+++ b/include/linux/coresight-pmu.h
@@ -19,9 +19,21 @@
 #define _LINUX_CORESIGHT_PMU_H
 
 #define CORESIGHT_ETM_PMU_NAME "cs_etm"
+#define CORESIGHT_ETM_PMU_SEED  0x10
 
 /* ETMv3.5/PTM's ETMCR config bit */
 #define ETM_OPT_CYCACC  12
 #define ETM_OPT_TS      28
 
+static inline int coresight_get_trace_id(int cpu)
+{
+	/*
+	 * A trace ID of value 0 is invalid, so let's start at some
+	 * random value that fits in 7 bits and go from there.  Since
+	 * the common convention is to have data trace IDs be I(N) + 1,
+	 * set instruction trace IDs as a function of the CPU number.
+	 */
+	return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
+}
+
 #endif
-- 
2.1.4

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

* [PATCH V8 19/23] coresight: introducing a global trace ID function
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

TraceID values have to be unique for all tracers and
consistent between drivers and user space.  As such
introducing a central function to be used whenever a
traceID value is required.

The patch also account for data traceIDs, which are usually
I(N) + 1.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c |  7 ++-----
 include/linux/coresight-pmu.h                 | 12 ++++++++++++
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 8055b2d246ad..70146865ff0d 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -27,6 +27,7 @@
 #include <linux/cpu.h>
 #include <linux/of.h>
 #include <linux/coresight.h>
+#include <linux/coresight-pmu.h>
 #include <linux/amba/bus.h>
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
@@ -740,11 +741,7 @@ static void etm_init_arch_data(void *info)
 
 static void etm_init_trace_id(struct etm_drvdata *drvdata)
 {
-	/*
-	 * A trace ID of value 0 is invalid, so let's start at some
-	 * random value that fits in 7 bits and go from there.
-	 */
-	drvdata->traceid = 0x10 + drvdata->cpu;
+	drvdata->traceid = coresight_get_trace_id(drvdata->cpu);
 }
 
 static int etm_probe(struct amba_device *adev, const struct amba_id *id)
diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h
index 6c5386b23b10..7d410260661b 100644
--- a/include/linux/coresight-pmu.h
+++ b/include/linux/coresight-pmu.h
@@ -19,9 +19,21 @@
 #define _LINUX_CORESIGHT_PMU_H
 
 #define CORESIGHT_ETM_PMU_NAME "cs_etm"
+#define CORESIGHT_ETM_PMU_SEED  0x10
 
 /* ETMv3.5/PTM's ETMCR config bit */
 #define ETM_OPT_CYCACC  12
 #define ETM_OPT_TS      28
 
+static inline int coresight_get_trace_id(int cpu)
+{
+	/*
+	 * A trace ID of value 0 is invalid, so let's start at some
+	 * random value that fits in 7 bits and go from there.  Since
+	 * the common convention is to have data trace IDs be I(N) + 1,
+	 * set instruction trace IDs as a function of the CPU number.
+	 */
+	return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
+}
+
 #endif
-- 
2.1.4

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

* [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo

For memory allocation purposes, code located in other places
then util/cpumap.c may want to know how many CPUs the system has.

This patch is making function set_max_cpu_num() available to
other parts of the perf tool so that global variable
'max_cpu_num' gets the right value when referenced by cpu__max_cpu().

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/util/cpumap.c | 2 +-
 tools/perf/util/cpumap.h | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 10af1e7524fb..ae179320c0c0 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -380,7 +380,7 @@ out:
 }
 
 /* Determine highest possible cpu in the system for sparse allocation */
-static void set_max_cpu_num(void)
+void set_max_cpu_num(void)
 {
 	const char *mnt;
 	char path[PATH_MAX];
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 85f7772457fa..45fa963345eb 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -14,6 +14,7 @@ struct cpu_map {
 	int map[];
 };
 
+void set_max_cpu_num(void);
 struct cpu_map *cpu_map__new(const char *cpu_list);
 struct cpu_map *cpu_map__empty_new(int nr);
 struct cpu_map *cpu_map__dummy_new(void);
-- 
2.1.4

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

* [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

For memory allocation purposes, code located in other places
then util/cpumap.c may want to know how many CPUs the system has.

This patch is making function set_max_cpu_num() available to
other parts of the perf tool so that global variable
'max_cpu_num' gets the right value when referenced by cpu__max_cpu().

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/util/cpumap.c | 2 +-
 tools/perf/util/cpumap.h | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 10af1e7524fb..ae179320c0c0 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -380,7 +380,7 @@ out:
 }
 
 /* Determine highest possible cpu in the system for sparse allocation */
-static void set_max_cpu_num(void)
+void set_max_cpu_num(void)
 {
 	const char *mnt;
 	char path[PATH_MAX];
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 85f7772457fa..45fa963345eb 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -14,6 +14,7 @@ struct cpu_map {
 	int map[];
 };
 
+void set_max_cpu_num(void);
 struct cpu_map *cpu_map__new(const char *cpu_list);
 struct cpu_map *cpu_map__empty_new(int nr);
 struct cpu_map *cpu_map__dummy_new(void);
-- 
2.1.4

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

* [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo

On some architecture the size of the private header may
be dependent on the number of tracers used in the session.  As
such adding a "struct perf_evlist *" parameter, which should
contain all the required information.

Also adjusting the existing client of the interface to take
the new parameter into account.

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/arch/x86/util/intel-bts.c | 4 +++-
 tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
 tools/perf/util/auxtrace.c           | 7 ++++---
 tools/perf/util/auxtrace.h           | 6 ++++--
 4 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
index 9b94ce520917..4685a40777cc 100644
--- a/tools/perf/arch/x86/util/intel-bts.c
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -60,7 +60,9 @@ struct branch {
 	u64 misc;
 };
 
-static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
+static size_t
+intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+			 struct perf_evlist *evlist __maybe_unused)
 {
 	return INTEL_BTS_AUXTRACE_PRIV_SIZE;
 }
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index b02af064f0f9..e5c1f2e21f87 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
 	return attr;
 }
 
-static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
+static size_t
+intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+			struct perf_evlist *evlist __maybe_unused)
 {
 	return INTEL_PT_AUXTRACE_PRIV_SIZE;
 }
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 7f10430af39c..cc1c9ce5cc56 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
 			 heap_array[last].ordinal);
 }
 
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+				       struct perf_evlist *evlist)
 {
 	if (itr)
-		return itr->info_priv_size(itr);
+		return itr->info_priv_size(itr, evlist);
 	return 0;
 }
 
@@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
 	int err;
 
 	pr_debug2("Synthesizing auxtrace information\n");
-	priv_size = auxtrace_record__info_priv_size(itr);
+	priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
 	ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
 	if (!ev)
 		return -ENOMEM;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index b86f90db1352..e5a8e2d4f2af 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -293,7 +293,8 @@ struct auxtrace_record {
 	int (*recording_options)(struct auxtrace_record *itr,
 				 struct perf_evlist *evlist,
 				 struct record_opts *opts);
-	size_t (*info_priv_size)(struct auxtrace_record *itr);
+	size_t (*info_priv_size)(struct auxtrace_record *itr,
+				 struct perf_evlist *evlist);
 	int (*info_fill)(struct auxtrace_record *itr,
 			 struct perf_session *session,
 			 struct auxtrace_info_event *auxtrace_info,
@@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
 int auxtrace_record__options(struct auxtrace_record *itr,
 			     struct perf_evlist *evlist,
 			     struct record_opts *opts);
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+				       struct perf_evlist *evlist);
 int auxtrace_record__info_fill(struct auxtrace_record *itr,
 			       struct perf_session *session,
 			       struct auxtrace_info_event *auxtrace_info,
-- 
2.1.4

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

* [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

On some architecture the size of the private header may
be dependent on the number of tracers used in the session.  As
such adding a "struct perf_evlist *" parameter, which should
contain all the required information.

Also adjusting the existing client of the interface to take
the new parameter into account.

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/arch/x86/util/intel-bts.c | 4 +++-
 tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
 tools/perf/util/auxtrace.c           | 7 ++++---
 tools/perf/util/auxtrace.h           | 6 ++++--
 4 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
index 9b94ce520917..4685a40777cc 100644
--- a/tools/perf/arch/x86/util/intel-bts.c
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -60,7 +60,9 @@ struct branch {
 	u64 misc;
 };
 
-static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
+static size_t
+intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+			 struct perf_evlist *evlist __maybe_unused)
 {
 	return INTEL_BTS_AUXTRACE_PRIV_SIZE;
 }
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index b02af064f0f9..e5c1f2e21f87 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
 	return attr;
 }
 
-static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
+static size_t
+intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+			struct perf_evlist *evlist __maybe_unused)
 {
 	return INTEL_PT_AUXTRACE_PRIV_SIZE;
 }
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 7f10430af39c..cc1c9ce5cc56 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
 			 heap_array[last].ordinal);
 }
 
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+				       struct perf_evlist *evlist)
 {
 	if (itr)
-		return itr->info_priv_size(itr);
+		return itr->info_priv_size(itr, evlist);
 	return 0;
 }
 
@@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
 	int err;
 
 	pr_debug2("Synthesizing auxtrace information\n");
-	priv_size = auxtrace_record__info_priv_size(itr);
+	priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
 	ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
 	if (!ev)
 		return -ENOMEM;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index b86f90db1352..e5a8e2d4f2af 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -293,7 +293,8 @@ struct auxtrace_record {
 	int (*recording_options)(struct auxtrace_record *itr,
 				 struct perf_evlist *evlist,
 				 struct record_opts *opts);
-	size_t (*info_priv_size)(struct auxtrace_record *itr);
+	size_t (*info_priv_size)(struct auxtrace_record *itr,
+				 struct perf_evlist *evlist);
 	int (*info_fill)(struct auxtrace_record *itr,
 			 struct perf_session *session,
 			 struct auxtrace_info_event *auxtrace_info,
@@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
 int auxtrace_record__options(struct auxtrace_record *itr,
 			     struct perf_evlist *evlist,
 			     struct record_opts *opts);
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+				       struct perf_evlist *evlist);
 int auxtrace_record__info_fill(struct auxtrace_record *itr,
 			       struct perf_session *session,
 			       struct auxtrace_info_event *auxtrace_info,
-- 
2.1.4

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

* [PATCH V8 22/23] perf tools: making coresight PMU listable
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo

Adding the required mechanic allowing 'perf list pmu' to
discover coresight ETM/PTM tracers.

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 MAINTAINERS                    |  1 +
 tools/perf/arch/arm/util/Build |  2 ++
 tools/perf/arch/arm/util/pmu.c | 34 ++++++++++++++++++++++++++++++++++
 tools/perf/config/Makefile     | 17 ++++++++++++-----
 4 files changed, 49 insertions(+), 5 deletions(-)
 create mode 100644 tools/perf/arch/arm/util/pmu.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 233f83464814..b2a92245eece 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1007,6 +1007,7 @@ F:	drivers/hwtracing/coresight/*
 F:	Documentation/trace/coresight.txt
 F:	Documentation/devicetree/bindings/arm/coresight.txt
 F:	Documentation/ABI/testing/sysfs-bus-coresight-devices-*
+F:	tools/perf/arch/arm/util/pmu.c
 
 ARM/CORGI MACHINE SUPPORT
 M:	Richard Purdie <rpurdie@rpsys.net>
diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index d22e3d07de3d..66ab0b05549c 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -2,3 +2,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
 
 libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
 libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
+
+libperf-$(CONFIG_AUXTRACE) += pmu.o
diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c
new file mode 100644
index 000000000000..af9fb666b44f
--- /dev/null
+++ b/tools/perf/arch/arm/util/pmu.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <linux/coresight-pmu.h>
+#include <linux/perf_event.h>
+
+#include "../../util/pmu.h"
+
+struct perf_event_attr
+*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
+{
+#ifdef HAVE_AUXTRACE_SUPPORT
+	if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
+		/* add ETM default config here */
+		pmu->selectable = true;
+	}
+#endif
+	return NULL;
+}
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index de89ec574361..905faabff8b1 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -647,12 +647,19 @@ ifdef LIBBABELTRACE
 endif
 
 ifndef NO_AUXTRACE
-  ifeq ($(feature-get_cpuid), 0)
-    msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
-    NO_AUXTRACE := 1
+  ifeq ($(ARCH),x86)
+    ifeq ($(feature-get_cpuid), 0)
+      msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
+      NO_AUXTRACE := 1
+    else
+      $(call detected,CONFIG_AUXTRACE)
+      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
+    endif
   else
-    $(call detected,CONFIG_AUXTRACE)
-    CFLAGS += -DHAVE_AUXTRACE_SUPPORT
+    ifeq ($(ARCH),$(filter $(ARCH), arm arm64))
+      $(call detected,CONFIG_AUXTRACE)
+      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
+    endif
   endif
 endif
 
-- 
2.1.4

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

* [PATCH V8 22/23] perf tools: making coresight PMU listable
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Adding the required mechanic allowing 'perf list pmu' to
discover coresight ETM/PTM tracers.

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 MAINTAINERS                    |  1 +
 tools/perf/arch/arm/util/Build |  2 ++
 tools/perf/arch/arm/util/pmu.c | 34 ++++++++++++++++++++++++++++++++++
 tools/perf/config/Makefile     | 17 ++++++++++++-----
 4 files changed, 49 insertions(+), 5 deletions(-)
 create mode 100644 tools/perf/arch/arm/util/pmu.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 233f83464814..b2a92245eece 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1007,6 +1007,7 @@ F:	drivers/hwtracing/coresight/*
 F:	Documentation/trace/coresight.txt
 F:	Documentation/devicetree/bindings/arm/coresight.txt
 F:	Documentation/ABI/testing/sysfs-bus-coresight-devices-*
+F:	tools/perf/arch/arm/util/pmu.c
 
 ARM/CORGI MACHINE SUPPORT
 M:	Richard Purdie <rpurdie@rpsys.net>
diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index d22e3d07de3d..66ab0b05549c 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -2,3 +2,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
 
 libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
 libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
+
+libperf-$(CONFIG_AUXTRACE) += pmu.o
diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c
new file mode 100644
index 000000000000..af9fb666b44f
--- /dev/null
+++ b/tools/perf/arch/arm/util/pmu.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <linux/coresight-pmu.h>
+#include <linux/perf_event.h>
+
+#include "../../util/pmu.h"
+
+struct perf_event_attr
+*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
+{
+#ifdef HAVE_AUXTRACE_SUPPORT
+	if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
+		/* add ETM default config here */
+		pmu->selectable = true;
+	}
+#endif
+	return NULL;
+}
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index de89ec574361..905faabff8b1 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -647,12 +647,19 @@ ifdef LIBBABELTRACE
 endif
 
 ifndef NO_AUXTRACE
-  ifeq ($(feature-get_cpuid), 0)
-    msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
-    NO_AUXTRACE := 1
+  ifeq ($(ARCH),x86)
+    ifeq ($(feature-get_cpuid), 0)
+      msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
+      NO_AUXTRACE := 1
+    else
+      $(call detected,CONFIG_AUXTRACE)
+      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
+    endif
   else
-    $(call detected,CONFIG_AUXTRACE)
-    CFLAGS += -DHAVE_AUXTRACE_SUPPORT
+    ifeq ($(ARCH),$(filter $(ARCH), arm arm64))
+      $(call detected,CONFIG_AUXTRACE)
+      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
+    endif
   endif
 endif
 
-- 
2.1.4

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-01-14 21:45 ` Mathieu Poirier
@ 2016-01-14 21:46   ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo

Coresight ETMs are IP blocks used to perform HW assisted tracing
on a CPU core.  This patch introduce the required auxiliary API
functions allowing the perf core to interact with a tracer.

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 MAINTAINERS                         |   3 +
 tools/perf/arch/arm/util/Build      |   2 +-
 tools/perf/arch/arm/util/auxtrace.c |  54 +++++
 tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
 tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
 tools/perf/util/auxtrace.c          |   1 +
 tools/perf/util/auxtrace.h          |   1 +
 7 files changed, 570 insertions(+), 1 deletion(-)
 create mode 100644 tools/perf/arch/arm/util/auxtrace.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.h

diff --git a/MAINTAINERS b/MAINTAINERS
index b2a92245eece..a81b2737ebc3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1008,6 +1008,9 @@ F:	Documentation/trace/coresight.txt
 F:	Documentation/devicetree/bindings/arm/coresight.txt
 F:	Documentation/ABI/testing/sysfs-bus-coresight-devices-*
 F:	tools/perf/arch/arm/util/pmu.c
+F:	tools/perf/arch/arm/util/auxtrace.c
+F:	tools/perf/arch/arm/util/cs_etm.c
+F:	tools/perf/arch/arm/util/cs_etm.h
 
 ARM/CORGI MACHINE SUPPORT
 M:	Richard Purdie <rpurdie@rpsys.net>
diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index 66ab0b05549c..0a25a1248f42 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
 libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
 libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
 
-libperf-$(CONFIG_AUXTRACE) += pmu.o
+libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
new file mode 100644
index 000000000000..d327316f0e8a
--- /dev/null
+++ b/tools/perf/arch/arm/util/auxtrace.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <linux/coresight-pmu.h>
+
+#include "../../util/auxtrace.h"
+#include "../../util/evlist.h"
+#include "../../util/pmu.h"
+#include "cs_etm.h"
+
+struct auxtrace_record
+*auxtrace_record__init(struct perf_evlist *evlist, int *err)
+{
+	struct perf_pmu	*cs_etm_pmu;
+	struct perf_evsel *evsel;
+	bool found_etm = false;
+
+	cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
+
+	 if (evlist) {
+		evlist__for_each(evlist, evsel) {
+			if (cs_etm_pmu &&
+			    evsel->attr.type == cs_etm_pmu->type)
+				found_etm = true;
+		}
+	}
+
+	if (found_etm)
+		return cs_etm_record_init(err);
+
+	/*
+	 * Clear 'err' even if we haven't found a cs_etm event - that way perf
+	 * record can still be used even if tracers aren't present.  The NULL
+	 * return value will take care of telling the infrastructure HW tracing
+	 * isn't available.
+	 */
+	*err = 0;
+	return NULL;
+}
diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
new file mode 100644
index 000000000000..5710b90e23d5
--- /dev/null
+++ b/tools/perf/arch/arm/util/cs_etm.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <api/fs/fs.h>
+#include <linux/bitops.h>
+#include <linux/coresight-pmu.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/types.h>
+
+#include "../../perf.h"
+#include "../../util/auxtrace.h"
+#include "../../util/cpumap.h"
+#include "../../util/evlist.h"
+#include "../../util/pmu.h"
+#include "../../util/thread_map.h"
+#include "cs_etm.h"
+
+#include <stdlib.h>
+
+#define KiB(x) ((x) * 1024)
+#define MiB(x) ((x) * 1024 * 1024)
+
+struct cs_etm_recording {
+	struct auxtrace_record	itr;
+	struct perf_pmu		*cs_etm_pmu;
+	struct perf_evlist	*evlist;
+	bool			snapshot_mode;
+	size_t			snapshot_size;
+};
+
+static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
+					 struct record_opts *opts,
+					 const char *str)
+{
+	struct cs_etm_recording *ptr =
+				container_of(itr, struct cs_etm_recording, itr);
+	unsigned long long snapshot_size = 0;
+	char *endptr;
+
+	if (str) {
+		snapshot_size = strtoull(str, &endptr, 0);
+		if (*endptr || snapshot_size > SIZE_MAX)
+			return -1;
+	}
+
+	opts->auxtrace_snapshot_mode = true;
+	opts->auxtrace_snapshot_size = snapshot_size;
+	ptr->snapshot_size = snapshot_size;
+
+	return 0;
+}
+
+static int cs_etm_recording_options(struct auxtrace_record *itr,
+				    struct perf_evlist *evlist,
+				    struct record_opts *opts)
+{
+	struct cs_etm_recording *ptr =
+				container_of(itr, struct cs_etm_recording, itr);
+	struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
+	struct perf_evsel *evsel, *cs_etm_evsel = NULL;
+	const struct cpu_map *cpus = evlist->cpus;
+	bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
+
+	ptr->evlist = evlist;
+	ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
+
+	evlist__for_each(evlist, evsel) {
+		if (evsel->attr.type == cs_etm_pmu->type) {
+			if (cs_etm_evsel) {
+				pr_err("There may be only one %s event\n",
+				       CORESIGHT_ETM_PMU_NAME);
+				return -EINVAL;
+			}
+			evsel->attr.freq = 0;
+			evsel->attr.sample_period = 1;
+			cs_etm_evsel = evsel;
+			opts->full_auxtrace = true;
+		}
+	}
+
+	/* no need to continue if at least one event of interest was found */
+	if (!cs_etm_evsel)
+		return 0;
+
+	if (opts->use_clockid) {
+		pr_err("Cannot use clockid (-k option) with %s\n",
+		       CORESIGHT_ETM_PMU_NAME);
+		return -EINVAL;
+	}
+
+	/* we are in snapshot mode */
+	if (opts->auxtrace_snapshot_mode) {
+		/*
+		 * No size were given to '-S' or '-m,', so go with
+		 * the default
+		 */
+		if (!opts->auxtrace_snapshot_size &&
+		    !opts->auxtrace_mmap_pages) {
+			if (privileged) {
+				opts->auxtrace_mmap_pages = MiB(4) / page_size;
+			} else {
+				opts->auxtrace_mmap_pages =
+							KiB(128) / page_size;
+				if (opts->mmap_pages == UINT_MAX)
+					opts->mmap_pages = KiB(256) / page_size;
+			}
+		} else if (!opts->auxtrace_mmap_pages && !privileged &&
+						opts->mmap_pages == UINT_MAX) {
+			opts->mmap_pages = KiB(256) / page_size;
+		}
+
+		/*
+		 * '-m,xyz' was specified but no snapshot size, so make the
+		 * snapshot size as big as the auxtrace mmap area.
+		 */
+		if (!opts->auxtrace_snapshot_size) {
+			opts->auxtrace_snapshot_size =
+				opts->auxtrace_mmap_pages * (size_t)page_size;
+		}
+
+		/*
+		 * -Sxyz was specified but no auxtrace mmap area, so make the
+		 * auxtrace mmap area big enough to fit the requested snapshot
+		 * size.
+		 */
+		if (!opts->auxtrace_mmap_pages) {
+			size_t sz = opts->auxtrace_snapshot_size;
+
+			sz = round_up(sz, page_size) / page_size;
+			opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
+		}
+
+		/* Snapshost size can't be bigger than the auxtrace area */
+		if (opts->auxtrace_snapshot_size >
+				opts->auxtrace_mmap_pages * (size_t)page_size) {
+			pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
+			       opts->auxtrace_snapshot_size,
+			       opts->auxtrace_mmap_pages * (size_t)page_size);
+			return -EINVAL;
+		}
+
+		/* Something went wrong somewhere - this shouldn't happen */
+		if (!opts->auxtrace_snapshot_size ||
+		    !opts->auxtrace_mmap_pages) {
+			pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
+			return -EINVAL;
+		}
+	}
+
+	/* We are in full trace mode but '-m,xyz' wasn't specified */
+	 if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
+		if (privileged) {
+			opts->auxtrace_mmap_pages = MiB(4) / page_size;
+		} else {
+			opts->auxtrace_mmap_pages = KiB(128) / page_size;
+			if (opts->mmap_pages == UINT_MAX)
+				opts->mmap_pages = KiB(256) / page_size;
+		}
+
+	}
+
+	/* Validate auxtrace_mmap_pages provided by user */
+	if (opts->auxtrace_mmap_pages) {
+		unsigned int max_page = (KiB(128) / page_size);
+		size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
+
+		if (!privileged &&
+		    opts->auxtrace_mmap_pages > max_page) {
+			opts->auxtrace_mmap_pages = max_page;
+			pr_err("auxtrace too big, truncating to %d\n",
+			       max_page);
+		}
+
+		if (!is_power_of_2(sz)) {
+			pr_err("Invalid mmap size for %s: must be a power of 2\n",
+			       CORESIGHT_ETM_PMU_NAME);
+			return -EINVAL;
+		}
+	}
+
+	if (opts->auxtrace_snapshot_mode)
+		pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
+			  opts->auxtrace_snapshot_size);
+
+	if (cs_etm_evsel) {
+		/*
+		 * To obtain the auxtrace buffer file descriptor, the auxtrace
+		 * event must come first.
+		 */
+		perf_evlist__to_front(evlist, cs_etm_evsel);
+		/*
+		 * In the case of per-cpu mmaps, we need the CPU on the
+		 * AUX event.
+		 */
+		if (!cpu_map__empty(cpus))
+			perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
+	}
+
+	/* Add dummy event to keep tracking */
+	if (opts->full_auxtrace) {
+		struct perf_evsel *tracking_evsel;
+		int err;
+
+		err = parse_events(evlist, "dummy:u", NULL);
+		if (err)
+			return err;
+
+		tracking_evsel = perf_evlist__last(evlist);
+		perf_evlist__set_tracking_event(evlist, tracking_evsel);
+
+		tracking_evsel->attr.freq = 0;
+		tracking_evsel->attr.sample_period = 1;
+
+		/* In per-cpu case, always need the time of mmap events etc */
+		if (!cpu_map__empty(cpus))
+			perf_evsel__set_sample_bit(tracking_evsel, TIME);
+	}
+
+	return 0;
+}
+
+static u64 cs_etm_get_config(struct auxtrace_record *itr)
+{
+	u64 config = 0;
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
+	struct perf_evlist *evlist = ptr->evlist;
+	struct perf_evsel *evsel;
+
+	evlist__for_each(evlist, evsel) {
+		if (evsel->attr.type == cs_etm_pmu->type) {
+			/*
+			 * Variable perf_event_attr::config is assigned to
+			 * ETMv3/PTM.  The bit fields have been made to match
+			 * the ETMv3.5 ETRMCR register specification.  See the
+			 * PMU_FORMAT_ATTR() declarations in
+			 * drivers/hwtracing/coresight/coresight-perf.c for
+			 * details.
+			 */
+			config = evsel->attr.config;
+			break;
+		}
+	}
+
+	return config;
+}
+
+static size_t
+cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+		      struct perf_evlist *evlist __maybe_unused)
+{
+	int records;
+	const struct cpu_map *cpus = evlist->cpus;
+
+	if (!cpu_map__empty(cpus)) {
+		records = cpu_map__nr(cpus);
+		goto out;
+	}
+
+	set_max_cpu_num();
+	records = cpu__max_cpu();
+out:
+	return records * CS_ETM_PRIV_SIZE;
+}
+
+static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
+	[CS_ETM_ETMCCER]	= "mgmt/etmccer",
+	[CS_ETM_ETMIDR]		= "mgmt/etmidr",
+};
+
+static int cs_etm_get_metadata(int cpu, int index,
+			       struct auxtrace_record *itr,
+			       struct auxtrace_info_event *info)
+{
+	char path[PATH_MAX];
+	int offset = 0, ret = 0;
+	int i, scan;
+	unsigned int val;
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
+
+	offset = index * CS_ETM_PRIV_MAX;
+
+	/* Build generic header portion */
+	info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
+	info->priv[offset + CS_ETM_CPU] = cpu;
+	info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
+
+	/* Get user configurables from the session */
+	info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
+	info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
+
+	/* Get RO metadata from sysfs */
+	for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
+		snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
+
+		scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
+		if (scan != 1) {
+			ret = -EINVAL;
+			break;
+		}
+
+		info->priv[offset + i] = val;
+	}
+
+	return ret;
+}
+
+static int cs_etm_info_fill(struct auxtrace_record *itr,
+			    struct perf_session *session,
+			    struct auxtrace_info_event *auxtrace_info,
+			    size_t priv_size)
+{
+	int i, nr_cpu, ret = 0;
+	const struct cpu_map *cpus = session->evlist->cpus;
+
+	if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
+		return -EINVAL;
+
+	if (!session->evlist->nr_mmaps)
+		return -EINVAL;
+
+	auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
+
+	/* cpu map is not empty, we have specific CPUs to work with */
+	if (!cpu_map__empty(cpus)) {
+		for (i = 0; i < cpu_map__nr(cpus); i++) {
+			ret = cs_etm_get_metadata(cpus->map[i], i,
+						  itr, auxtrace_info);
+			if (ret)
+				goto out;
+		}
+	} else {
+		/* get configuration for all CPUs in the system */
+		nr_cpu = cpu__max_cpu();
+		for (i = 0; i < nr_cpu; i++) {
+			ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
+			if (ret)
+				goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
+				int idx, struct auxtrace_mmap *mm,
+				unsigned char *data __maybe_unused,
+				u64 *head, u64 *old)
+{
+	pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
+		  __func__, idx, (size_t)*old, (size_t)*head, mm->len);
+
+	*old = *head;
+	*head += mm->len;
+
+	return 0;
+}
+
+static int cs_etm_snapshot_start(struct auxtrace_record *itr)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_evsel *evsel;
+
+	evlist__for_each(ptr->evlist, evsel) {
+		if (evsel->attr.type == ptr->cs_etm_pmu->type)
+			return perf_evlist__disable_event(ptr->evlist, evsel);
+	}
+	return -EINVAL;
+}
+
+static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_evsel *evsel;
+
+	evlist__for_each(ptr->evlist, evsel) {
+		if (evsel->attr.type == ptr->cs_etm_pmu->type)
+			return perf_evlist__enable_event(ptr->evlist, evsel);
+	}
+	return -EINVAL;
+}
+
+static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
+{
+	return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
+		(((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
+}
+
+static void cs_etm_recording_free(struct auxtrace_record *itr)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	free(ptr);
+}
+
+static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_evsel *evsel;
+
+	evlist__for_each(ptr->evlist, evsel) {
+		if (evsel->attr.type == ptr->cs_etm_pmu->type)
+			return perf_evlist__enable_event_idx(ptr->evlist,
+							     evsel, idx);
+	}
+
+	return -EINVAL;
+}
+
+struct auxtrace_record *cs_etm_record_init(int *err)
+{
+	struct perf_pmu *cs_etm_pmu;
+	struct cs_etm_recording *ptr;
+
+	cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
+
+	if (!cs_etm_pmu) {
+		*err = -EINVAL;
+		goto out;
+	}
+
+	ptr = zalloc(sizeof(struct cs_etm_recording));
+	if (!ptr) {
+		*err = -ENOMEM;
+		goto out;
+	}
+
+	ptr->cs_etm_pmu			= cs_etm_pmu;
+	ptr->itr.parse_snapshot_options	= cs_etm_parse_snapshot_options;
+	ptr->itr.recording_options	= cs_etm_recording_options;
+	ptr->itr.info_priv_size		= cs_etm_info_priv_size;
+	ptr->itr.info_fill		= cs_etm_info_fill;
+	ptr->itr.find_snapshot		= cs_etm_find_snapshot;
+	ptr->itr.snapshot_start		= cs_etm_snapshot_start;
+	ptr->itr.snapshot_finish	= cs_etm_snapshot_finish;
+	ptr->itr.reference		= cs_etm_reference;
+	ptr->itr.free			= cs_etm_recording_free;
+	ptr->itr.read_finish		= cs_etm_read_finish;
+
+	*err = 0;
+	return &ptr->itr;
+out:
+	return NULL;
+}
diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
new file mode 100644
index 000000000000..7e85c1b43598
--- /dev/null
+++ b/tools/perf/arch/arm/util/cs_etm.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDE__PERF_CS_ETM_H__
+#define INCLUDE__PERF_CS_ETM_H__
+
+/* Beginning of header common to both ETMv3 and V4 */
+enum {
+	CS_ETM_MAGIC,
+	CS_ETM_CPU,
+	CS_ETM_SNAPSHOT,
+};
+
+/* ETMv3/PTM metadata */
+enum {
+	/* Dynamic, configurable parameters */
+	CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
+	CS_ETM_ETMTRACEIDR,
+	/* RO, taken from sysFS */
+	CS_ETM_ETMCCER,
+	CS_ETM_ETMIDR,
+	CS_ETM_PRIV_MAX,
+};
+
+static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
+#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
+
+struct auxtrace_record *cs_etm_record_init(int *err);
+
+#endif
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index cc1c9ce5cc56..a6f291dbc4d9 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
 		return intel_pt_process_auxtrace_info(event, session);
 	case PERF_AUXTRACE_INTEL_BTS:
 		return intel_bts_process_auxtrace_info(event, session);
+	case PERF_AUXTRACE_CS_ETM:
 	case PERF_AUXTRACE_UNKNOWN:
 	default:
 		return -EINVAL;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index e5a8e2d4f2af..adb53e7bcabf 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -41,6 +41,7 @@ enum auxtrace_type {
 	PERF_AUXTRACE_UNKNOWN,
 	PERF_AUXTRACE_INTEL_PT,
 	PERF_AUXTRACE_INTEL_BTS,
+	PERF_AUXTRACE_CS_ETM,
 };
 
 enum itrace_period_type {
-- 
2.1.4

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-01-14 21:46   ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-14 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Coresight ETMs are IP blocks used to perform HW assisted tracing
on a CPU core.  This patch introduce the required auxiliary API
functions allowing the perf core to interact with a tracer.

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 MAINTAINERS                         |   3 +
 tools/perf/arch/arm/util/Build      |   2 +-
 tools/perf/arch/arm/util/auxtrace.c |  54 +++++
 tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
 tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
 tools/perf/util/auxtrace.c          |   1 +
 tools/perf/util/auxtrace.h          |   1 +
 7 files changed, 570 insertions(+), 1 deletion(-)
 create mode 100644 tools/perf/arch/arm/util/auxtrace.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.h

diff --git a/MAINTAINERS b/MAINTAINERS
index b2a92245eece..a81b2737ebc3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1008,6 +1008,9 @@ F:	Documentation/trace/coresight.txt
 F:	Documentation/devicetree/bindings/arm/coresight.txt
 F:	Documentation/ABI/testing/sysfs-bus-coresight-devices-*
 F:	tools/perf/arch/arm/util/pmu.c
+F:	tools/perf/arch/arm/util/auxtrace.c
+F:	tools/perf/arch/arm/util/cs_etm.c
+F:	tools/perf/arch/arm/util/cs_etm.h
 
 ARM/CORGI MACHINE SUPPORT
 M:	Richard Purdie <rpurdie@rpsys.net>
diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index 66ab0b05549c..0a25a1248f42 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
 libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
 libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
 
-libperf-$(CONFIG_AUXTRACE) += pmu.o
+libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
new file mode 100644
index 000000000000..d327316f0e8a
--- /dev/null
+++ b/tools/perf/arch/arm/util/auxtrace.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <linux/coresight-pmu.h>
+
+#include "../../util/auxtrace.h"
+#include "../../util/evlist.h"
+#include "../../util/pmu.h"
+#include "cs_etm.h"
+
+struct auxtrace_record
+*auxtrace_record__init(struct perf_evlist *evlist, int *err)
+{
+	struct perf_pmu	*cs_etm_pmu;
+	struct perf_evsel *evsel;
+	bool found_etm = false;
+
+	cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
+
+	 if (evlist) {
+		evlist__for_each(evlist, evsel) {
+			if (cs_etm_pmu &&
+			    evsel->attr.type == cs_etm_pmu->type)
+				found_etm = true;
+		}
+	}
+
+	if (found_etm)
+		return cs_etm_record_init(err);
+
+	/*
+	 * Clear 'err' even if we haven't found a cs_etm event - that way perf
+	 * record can still be used even if tracers aren't present.  The NULL
+	 * return value will take care of telling the infrastructure HW tracing
+	 * isn't available.
+	 */
+	*err = 0;
+	return NULL;
+}
diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
new file mode 100644
index 000000000000..5710b90e23d5
--- /dev/null
+++ b/tools/perf/arch/arm/util/cs_etm.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <api/fs/fs.h>
+#include <linux/bitops.h>
+#include <linux/coresight-pmu.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/types.h>
+
+#include "../../perf.h"
+#include "../../util/auxtrace.h"
+#include "../../util/cpumap.h"
+#include "../../util/evlist.h"
+#include "../../util/pmu.h"
+#include "../../util/thread_map.h"
+#include "cs_etm.h"
+
+#include <stdlib.h>
+
+#define KiB(x) ((x) * 1024)
+#define MiB(x) ((x) * 1024 * 1024)
+
+struct cs_etm_recording {
+	struct auxtrace_record	itr;
+	struct perf_pmu		*cs_etm_pmu;
+	struct perf_evlist	*evlist;
+	bool			snapshot_mode;
+	size_t			snapshot_size;
+};
+
+static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
+					 struct record_opts *opts,
+					 const char *str)
+{
+	struct cs_etm_recording *ptr =
+				container_of(itr, struct cs_etm_recording, itr);
+	unsigned long long snapshot_size = 0;
+	char *endptr;
+
+	if (str) {
+		snapshot_size = strtoull(str, &endptr, 0);
+		if (*endptr || snapshot_size > SIZE_MAX)
+			return -1;
+	}
+
+	opts->auxtrace_snapshot_mode = true;
+	opts->auxtrace_snapshot_size = snapshot_size;
+	ptr->snapshot_size = snapshot_size;
+
+	return 0;
+}
+
+static int cs_etm_recording_options(struct auxtrace_record *itr,
+				    struct perf_evlist *evlist,
+				    struct record_opts *opts)
+{
+	struct cs_etm_recording *ptr =
+				container_of(itr, struct cs_etm_recording, itr);
+	struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
+	struct perf_evsel *evsel, *cs_etm_evsel = NULL;
+	const struct cpu_map *cpus = evlist->cpus;
+	bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
+
+	ptr->evlist = evlist;
+	ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
+
+	evlist__for_each(evlist, evsel) {
+		if (evsel->attr.type == cs_etm_pmu->type) {
+			if (cs_etm_evsel) {
+				pr_err("There may be only one %s event\n",
+				       CORESIGHT_ETM_PMU_NAME);
+				return -EINVAL;
+			}
+			evsel->attr.freq = 0;
+			evsel->attr.sample_period = 1;
+			cs_etm_evsel = evsel;
+			opts->full_auxtrace = true;
+		}
+	}
+
+	/* no need to continue if at least one event of interest was found */
+	if (!cs_etm_evsel)
+		return 0;
+
+	if (opts->use_clockid) {
+		pr_err("Cannot use clockid (-k option) with %s\n",
+		       CORESIGHT_ETM_PMU_NAME);
+		return -EINVAL;
+	}
+
+	/* we are in snapshot mode */
+	if (opts->auxtrace_snapshot_mode) {
+		/*
+		 * No size were given to '-S' or '-m,', so go with
+		 * the default
+		 */
+		if (!opts->auxtrace_snapshot_size &&
+		    !opts->auxtrace_mmap_pages) {
+			if (privileged) {
+				opts->auxtrace_mmap_pages = MiB(4) / page_size;
+			} else {
+				opts->auxtrace_mmap_pages =
+							KiB(128) / page_size;
+				if (opts->mmap_pages == UINT_MAX)
+					opts->mmap_pages = KiB(256) / page_size;
+			}
+		} else if (!opts->auxtrace_mmap_pages && !privileged &&
+						opts->mmap_pages == UINT_MAX) {
+			opts->mmap_pages = KiB(256) / page_size;
+		}
+
+		/*
+		 * '-m,xyz' was specified but no snapshot size, so make the
+		 * snapshot size as big as the auxtrace mmap area.
+		 */
+		if (!opts->auxtrace_snapshot_size) {
+			opts->auxtrace_snapshot_size =
+				opts->auxtrace_mmap_pages * (size_t)page_size;
+		}
+
+		/*
+		 * -Sxyz was specified but no auxtrace mmap area, so make the
+		 * auxtrace mmap area big enough to fit the requested snapshot
+		 * size.
+		 */
+		if (!opts->auxtrace_mmap_pages) {
+			size_t sz = opts->auxtrace_snapshot_size;
+
+			sz = round_up(sz, page_size) / page_size;
+			opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
+		}
+
+		/* Snapshost size can't be bigger than the auxtrace area */
+		if (opts->auxtrace_snapshot_size >
+				opts->auxtrace_mmap_pages * (size_t)page_size) {
+			pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
+			       opts->auxtrace_snapshot_size,
+			       opts->auxtrace_mmap_pages * (size_t)page_size);
+			return -EINVAL;
+		}
+
+		/* Something went wrong somewhere - this shouldn't happen */
+		if (!opts->auxtrace_snapshot_size ||
+		    !opts->auxtrace_mmap_pages) {
+			pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
+			return -EINVAL;
+		}
+	}
+
+	/* We are in full trace mode but '-m,xyz' wasn't specified */
+	 if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
+		if (privileged) {
+			opts->auxtrace_mmap_pages = MiB(4) / page_size;
+		} else {
+			opts->auxtrace_mmap_pages = KiB(128) / page_size;
+			if (opts->mmap_pages == UINT_MAX)
+				opts->mmap_pages = KiB(256) / page_size;
+		}
+
+	}
+
+	/* Validate auxtrace_mmap_pages provided by user */
+	if (opts->auxtrace_mmap_pages) {
+		unsigned int max_page = (KiB(128) / page_size);
+		size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
+
+		if (!privileged &&
+		    opts->auxtrace_mmap_pages > max_page) {
+			opts->auxtrace_mmap_pages = max_page;
+			pr_err("auxtrace too big, truncating to %d\n",
+			       max_page);
+		}
+
+		if (!is_power_of_2(sz)) {
+			pr_err("Invalid mmap size for %s: must be a power of 2\n",
+			       CORESIGHT_ETM_PMU_NAME);
+			return -EINVAL;
+		}
+	}
+
+	if (opts->auxtrace_snapshot_mode)
+		pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
+			  opts->auxtrace_snapshot_size);
+
+	if (cs_etm_evsel) {
+		/*
+		 * To obtain the auxtrace buffer file descriptor, the auxtrace
+		 * event must come first.
+		 */
+		perf_evlist__to_front(evlist, cs_etm_evsel);
+		/*
+		 * In the case of per-cpu mmaps, we need the CPU on the
+		 * AUX event.
+		 */
+		if (!cpu_map__empty(cpus))
+			perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
+	}
+
+	/* Add dummy event to keep tracking */
+	if (opts->full_auxtrace) {
+		struct perf_evsel *tracking_evsel;
+		int err;
+
+		err = parse_events(evlist, "dummy:u", NULL);
+		if (err)
+			return err;
+
+		tracking_evsel = perf_evlist__last(evlist);
+		perf_evlist__set_tracking_event(evlist, tracking_evsel);
+
+		tracking_evsel->attr.freq = 0;
+		tracking_evsel->attr.sample_period = 1;
+
+		/* In per-cpu case, always need the time of mmap events etc */
+		if (!cpu_map__empty(cpus))
+			perf_evsel__set_sample_bit(tracking_evsel, TIME);
+	}
+
+	return 0;
+}
+
+static u64 cs_etm_get_config(struct auxtrace_record *itr)
+{
+	u64 config = 0;
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
+	struct perf_evlist *evlist = ptr->evlist;
+	struct perf_evsel *evsel;
+
+	evlist__for_each(evlist, evsel) {
+		if (evsel->attr.type == cs_etm_pmu->type) {
+			/*
+			 * Variable perf_event_attr::config is assigned to
+			 * ETMv3/PTM.  The bit fields have been made to match
+			 * the ETMv3.5 ETRMCR register specification.  See the
+			 * PMU_FORMAT_ATTR() declarations in
+			 * drivers/hwtracing/coresight/coresight-perf.c for
+			 * details.
+			 */
+			config = evsel->attr.config;
+			break;
+		}
+	}
+
+	return config;
+}
+
+static size_t
+cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+		      struct perf_evlist *evlist __maybe_unused)
+{
+	int records;
+	const struct cpu_map *cpus = evlist->cpus;
+
+	if (!cpu_map__empty(cpus)) {
+		records = cpu_map__nr(cpus);
+		goto out;
+	}
+
+	set_max_cpu_num();
+	records = cpu__max_cpu();
+out:
+	return records * CS_ETM_PRIV_SIZE;
+}
+
+static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
+	[CS_ETM_ETMCCER]	= "mgmt/etmccer",
+	[CS_ETM_ETMIDR]		= "mgmt/etmidr",
+};
+
+static int cs_etm_get_metadata(int cpu, int index,
+			       struct auxtrace_record *itr,
+			       struct auxtrace_info_event *info)
+{
+	char path[PATH_MAX];
+	int offset = 0, ret = 0;
+	int i, scan;
+	unsigned int val;
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
+
+	offset = index * CS_ETM_PRIV_MAX;
+
+	/* Build generic header portion */
+	info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
+	info->priv[offset + CS_ETM_CPU] = cpu;
+	info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
+
+	/* Get user configurables from the session */
+	info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
+	info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
+
+	/* Get RO metadata from sysfs */
+	for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
+		snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
+
+		scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
+		if (scan != 1) {
+			ret = -EINVAL;
+			break;
+		}
+
+		info->priv[offset + i] = val;
+	}
+
+	return ret;
+}
+
+static int cs_etm_info_fill(struct auxtrace_record *itr,
+			    struct perf_session *session,
+			    struct auxtrace_info_event *auxtrace_info,
+			    size_t priv_size)
+{
+	int i, nr_cpu, ret = 0;
+	const struct cpu_map *cpus = session->evlist->cpus;
+
+	if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
+		return -EINVAL;
+
+	if (!session->evlist->nr_mmaps)
+		return -EINVAL;
+
+	auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
+
+	/* cpu map is not empty, we have specific CPUs to work with */
+	if (!cpu_map__empty(cpus)) {
+		for (i = 0; i < cpu_map__nr(cpus); i++) {
+			ret = cs_etm_get_metadata(cpus->map[i], i,
+						  itr, auxtrace_info);
+			if (ret)
+				goto out;
+		}
+	} else {
+		/* get configuration for all CPUs in the system */
+		nr_cpu = cpu__max_cpu();
+		for (i = 0; i < nr_cpu; i++) {
+			ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
+			if (ret)
+				goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
+				int idx, struct auxtrace_mmap *mm,
+				unsigned char *data __maybe_unused,
+				u64 *head, u64 *old)
+{
+	pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
+		  __func__, idx, (size_t)*old, (size_t)*head, mm->len);
+
+	*old = *head;
+	*head += mm->len;
+
+	return 0;
+}
+
+static int cs_etm_snapshot_start(struct auxtrace_record *itr)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_evsel *evsel;
+
+	evlist__for_each(ptr->evlist, evsel) {
+		if (evsel->attr.type == ptr->cs_etm_pmu->type)
+			return perf_evlist__disable_event(ptr->evlist, evsel);
+	}
+	return -EINVAL;
+}
+
+static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_evsel *evsel;
+
+	evlist__for_each(ptr->evlist, evsel) {
+		if (evsel->attr.type == ptr->cs_etm_pmu->type)
+			return perf_evlist__enable_event(ptr->evlist, evsel);
+	}
+	return -EINVAL;
+}
+
+static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
+{
+	return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
+		(((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
+}
+
+static void cs_etm_recording_free(struct auxtrace_record *itr)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	free(ptr);
+}
+
+static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_evsel *evsel;
+
+	evlist__for_each(ptr->evlist, evsel) {
+		if (evsel->attr.type == ptr->cs_etm_pmu->type)
+			return perf_evlist__enable_event_idx(ptr->evlist,
+							     evsel, idx);
+	}
+
+	return -EINVAL;
+}
+
+struct auxtrace_record *cs_etm_record_init(int *err)
+{
+	struct perf_pmu *cs_etm_pmu;
+	struct cs_etm_recording *ptr;
+
+	cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
+
+	if (!cs_etm_pmu) {
+		*err = -EINVAL;
+		goto out;
+	}
+
+	ptr = zalloc(sizeof(struct cs_etm_recording));
+	if (!ptr) {
+		*err = -ENOMEM;
+		goto out;
+	}
+
+	ptr->cs_etm_pmu			= cs_etm_pmu;
+	ptr->itr.parse_snapshot_options	= cs_etm_parse_snapshot_options;
+	ptr->itr.recording_options	= cs_etm_recording_options;
+	ptr->itr.info_priv_size		= cs_etm_info_priv_size;
+	ptr->itr.info_fill		= cs_etm_info_fill;
+	ptr->itr.find_snapshot		= cs_etm_find_snapshot;
+	ptr->itr.snapshot_start		= cs_etm_snapshot_start;
+	ptr->itr.snapshot_finish	= cs_etm_snapshot_finish;
+	ptr->itr.reference		= cs_etm_reference;
+	ptr->itr.free			= cs_etm_recording_free;
+	ptr->itr.read_finish		= cs_etm_read_finish;
+
+	*err = 0;
+	return &ptr->itr;
+out:
+	return NULL;
+}
diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
new file mode 100644
index 000000000000..7e85c1b43598
--- /dev/null
+++ b/tools/perf/arch/arm/util/cs_etm.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDE__PERF_CS_ETM_H__
+#define INCLUDE__PERF_CS_ETM_H__
+
+/* Beginning of header common to both ETMv3 and V4 */
+enum {
+	CS_ETM_MAGIC,
+	CS_ETM_CPU,
+	CS_ETM_SNAPSHOT,
+};
+
+/* ETMv3/PTM metadata */
+enum {
+	/* Dynamic, configurable parameters */
+	CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
+	CS_ETM_ETMTRACEIDR,
+	/* RO, taken from sysFS */
+	CS_ETM_ETMCCER,
+	CS_ETM_ETMIDR,
+	CS_ETM_PRIV_MAX,
+};
+
+static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
+#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
+
+struct auxtrace_record *cs_etm_record_init(int *err);
+
+#endif
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index cc1c9ce5cc56..a6f291dbc4d9 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
 		return intel_pt_process_auxtrace_info(event, session);
 	case PERF_AUXTRACE_INTEL_BTS:
 		return intel_bts_process_auxtrace_info(event, session);
+	case PERF_AUXTRACE_CS_ETM:
 	case PERF_AUXTRACE_UNKNOWN:
 	default:
 		return -EINVAL;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index e5a8e2d4f2af..adb53e7bcabf 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -41,6 +41,7 @@ enum auxtrace_type {
 	PERF_AUXTRACE_UNKNOWN,
 	PERF_AUXTRACE_INTEL_PT,
 	PERF_AUXTRACE_INTEL_BTS,
+	PERF_AUXTRACE_CS_ETM,
 };
 
 enum itrace_period_type {
-- 
2.1.4

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

* Re: [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
  2016-01-14 21:46   ` Mathieu Poirier
@ 2016-01-25 20:46     ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-25 20:46 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: linux-doc, Mathieu Poirier, Ingo Molnar, linux-arm-kernel,
	linux-kernel, Peter Zijlstra

On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> For memory allocation purposes, code located in other places
> then util/cpumap.c may want to know how many CPUs the system has.
>
> This patch is making function set_max_cpu_num() available to
> other parts of the perf tool so that global variable
> 'max_cpu_num' gets the right value when referenced by cpu__max_cpu().
>
> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  tools/perf/util/cpumap.c | 2 +-
>  tools/perf/util/cpumap.h | 1 +
>  2 files changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
> index 10af1e7524fb..ae179320c0c0 100644
> --- a/tools/perf/util/cpumap.c
> +++ b/tools/perf/util/cpumap.c
> @@ -380,7 +380,7 @@ out:
>  }
>
>  /* Determine highest possible cpu in the system for sparse allocation */
> -static void set_max_cpu_num(void)
> +void set_max_cpu_num(void)
>  {
>         const char *mnt;
>         char path[PATH_MAX];
> diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
> index 85f7772457fa..45fa963345eb 100644
> --- a/tools/perf/util/cpumap.h
> +++ b/tools/perf/util/cpumap.h
> @@ -14,6 +14,7 @@ struct cpu_map {
>         int map[];
>  };
>
> +void set_max_cpu_num(void);
>  struct cpu_map *cpu_map__new(const char *cpu_list);
>  struct cpu_map *cpu_map__empty_new(int nr);
>  struct cpu_map *cpu_map__dummy_new(void);
> --
> 2.1.4
>

Arnaldo,

I can't queue this patch for 4.6 without at least a reviewed by from you.

It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
be happy to use another baseline should that be more adequate for you.
The same goes for [2]

Thanks,
Mathieu

[1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
@ 2016-01-25 20:46     ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-25 20:46 UTC (permalink / raw)
  To: linux-arm-kernel

On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> For memory allocation purposes, code located in other places
> then util/cpumap.c may want to know how many CPUs the system has.
>
> This patch is making function set_max_cpu_num() available to
> other parts of the perf tool so that global variable
> 'max_cpu_num' gets the right value when referenced by cpu__max_cpu().
>
> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  tools/perf/util/cpumap.c | 2 +-
>  tools/perf/util/cpumap.h | 1 +
>  2 files changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
> index 10af1e7524fb..ae179320c0c0 100644
> --- a/tools/perf/util/cpumap.c
> +++ b/tools/perf/util/cpumap.c
> @@ -380,7 +380,7 @@ out:
>  }
>
>  /* Determine highest possible cpu in the system for sparse allocation */
> -static void set_max_cpu_num(void)
> +void set_max_cpu_num(void)
>  {
>         const char *mnt;
>         char path[PATH_MAX];
> diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
> index 85f7772457fa..45fa963345eb 100644
> --- a/tools/perf/util/cpumap.h
> +++ b/tools/perf/util/cpumap.h
> @@ -14,6 +14,7 @@ struct cpu_map {
>         int map[];
>  };
>
> +void set_max_cpu_num(void);
>  struct cpu_map *cpu_map__new(const char *cpu_list);
>  struct cpu_map *cpu_map__empty_new(int nr);
>  struct cpu_map *cpu_map__dummy_new(void);
> --
> 2.1.4
>

Arnaldo,

I can't queue this patch for 4.6 without at least a reviewed by from you.

It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
be happy to use another baseline should that be more adequate for you.
The same goes for [2]

Thanks,
Mathieu

[1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* Re: [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
  2016-01-14 21:46   ` Mathieu Poirier
@ 2016-01-25 20:48     ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-25 20:48 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: linux-arm-kernel, Mathieu Poirier, Peter Zijlstra, linux-kernel,
	Ingo Molnar

On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> On some architecture the size of the private header may
> be dependent on the number of tracers used in the session.  As
> such adding a "struct perf_evlist *" parameter, which should
> contain all the required information.
>
> Also adjusting the existing client of the interface to take
> the new parameter into account.
>
> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  tools/perf/arch/x86/util/intel-bts.c | 4 +++-
>  tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
>  tools/perf/util/auxtrace.c           | 7 ++++---
>  tools/perf/util/auxtrace.h           | 6 ++++--
>  4 files changed, 14 insertions(+), 7 deletions(-)
>
> diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
> index 9b94ce520917..4685a40777cc 100644
> --- a/tools/perf/arch/x86/util/intel-bts.c
> +++ b/tools/perf/arch/x86/util/intel-bts.c
> @@ -60,7 +60,9 @@ struct branch {
>         u64 misc;
>  };
>
> -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
> +static size_t
> +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> +                        struct perf_evlist *evlist __maybe_unused)
>  {
>         return INTEL_BTS_AUXTRACE_PRIV_SIZE;
>  }
> diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
> index b02af064f0f9..e5c1f2e21f87 100644
> --- a/tools/perf/arch/x86/util/intel-pt.c
> +++ b/tools/perf/arch/x86/util/intel-pt.c
> @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
>         return attr;
>  }
>
> -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
> +static size_t
> +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> +                       struct perf_evlist *evlist __maybe_unused)
>  {
>         return INTEL_PT_AUXTRACE_PRIV_SIZE;
>  }
> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> index 7f10430af39c..cc1c9ce5cc56 100644
> --- a/tools/perf/util/auxtrace.c
> +++ b/tools/perf/util/auxtrace.c
> @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
>                          heap_array[last].ordinal);
>  }
>
> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
> +                                      struct perf_evlist *evlist)
>  {
>         if (itr)
> -               return itr->info_priv_size(itr);
> +               return itr->info_priv_size(itr, evlist);
>         return 0;
>  }
>
> @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
>         int err;
>
>         pr_debug2("Synthesizing auxtrace information\n");
> -       priv_size = auxtrace_record__info_priv_size(itr);
> +       priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
>         ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
>         if (!ev)
>                 return -ENOMEM;
> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> index b86f90db1352..e5a8e2d4f2af 100644
> --- a/tools/perf/util/auxtrace.h
> +++ b/tools/perf/util/auxtrace.h
> @@ -293,7 +293,8 @@ struct auxtrace_record {
>         int (*recording_options)(struct auxtrace_record *itr,
>                                  struct perf_evlist *evlist,
>                                  struct record_opts *opts);
> -       size_t (*info_priv_size)(struct auxtrace_record *itr);
> +       size_t (*info_priv_size)(struct auxtrace_record *itr,
> +                                struct perf_evlist *evlist);
>         int (*info_fill)(struct auxtrace_record *itr,
>                          struct perf_session *session,
>                          struct auxtrace_info_event *auxtrace_info,
> @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
>  int auxtrace_record__options(struct auxtrace_record *itr,
>                              struct perf_evlist *evlist,
>                              struct record_opts *opts);
> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
> +                                      struct perf_evlist *evlist);
>  int auxtrace_record__info_fill(struct auxtrace_record *itr,
>                                struct perf_session *session,
>                                struct auxtrace_info_event *auxtrace_info,
> --
> 2.1.4
>

Arnaldo,

As with my previous email, I can't queue this patch for 4.6 without at
least a reviewed by from you.

It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
be happy to use another baseline should that be more adequate for you.

Thanks,
Mathieu

[1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
@ 2016-01-25 20:48     ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-25 20:48 UTC (permalink / raw)
  To: linux-arm-kernel

On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> On some architecture the size of the private header may
> be dependent on the number of tracers used in the session.  As
> such adding a "struct perf_evlist *" parameter, which should
> contain all the required information.
>
> Also adjusting the existing client of the interface to take
> the new parameter into account.
>
> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  tools/perf/arch/x86/util/intel-bts.c | 4 +++-
>  tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
>  tools/perf/util/auxtrace.c           | 7 ++++---
>  tools/perf/util/auxtrace.h           | 6 ++++--
>  4 files changed, 14 insertions(+), 7 deletions(-)
>
> diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
> index 9b94ce520917..4685a40777cc 100644
> --- a/tools/perf/arch/x86/util/intel-bts.c
> +++ b/tools/perf/arch/x86/util/intel-bts.c
> @@ -60,7 +60,9 @@ struct branch {
>         u64 misc;
>  };
>
> -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
> +static size_t
> +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> +                        struct perf_evlist *evlist __maybe_unused)
>  {
>         return INTEL_BTS_AUXTRACE_PRIV_SIZE;
>  }
> diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
> index b02af064f0f9..e5c1f2e21f87 100644
> --- a/tools/perf/arch/x86/util/intel-pt.c
> +++ b/tools/perf/arch/x86/util/intel-pt.c
> @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
>         return attr;
>  }
>
> -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
> +static size_t
> +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> +                       struct perf_evlist *evlist __maybe_unused)
>  {
>         return INTEL_PT_AUXTRACE_PRIV_SIZE;
>  }
> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> index 7f10430af39c..cc1c9ce5cc56 100644
> --- a/tools/perf/util/auxtrace.c
> +++ b/tools/perf/util/auxtrace.c
> @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
>                          heap_array[last].ordinal);
>  }
>
> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
> +                                      struct perf_evlist *evlist)
>  {
>         if (itr)
> -               return itr->info_priv_size(itr);
> +               return itr->info_priv_size(itr, evlist);
>         return 0;
>  }
>
> @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
>         int err;
>
>         pr_debug2("Synthesizing auxtrace information\n");
> -       priv_size = auxtrace_record__info_priv_size(itr);
> +       priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
>         ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
>         if (!ev)
>                 return -ENOMEM;
> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> index b86f90db1352..e5a8e2d4f2af 100644
> --- a/tools/perf/util/auxtrace.h
> +++ b/tools/perf/util/auxtrace.h
> @@ -293,7 +293,8 @@ struct auxtrace_record {
>         int (*recording_options)(struct auxtrace_record *itr,
>                                  struct perf_evlist *evlist,
>                                  struct record_opts *opts);
> -       size_t (*info_priv_size)(struct auxtrace_record *itr);
> +       size_t (*info_priv_size)(struct auxtrace_record *itr,
> +                                struct perf_evlist *evlist);
>         int (*info_fill)(struct auxtrace_record *itr,
>                          struct perf_session *session,
>                          struct auxtrace_info_event *auxtrace_info,
> @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
>  int auxtrace_record__options(struct auxtrace_record *itr,
>                              struct perf_evlist *evlist,
>                              struct record_opts *opts);
> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
> +                                      struct perf_evlist *evlist);
>  int auxtrace_record__info_fill(struct auxtrace_record *itr,
>                                struct perf_session *session,
>                                struct auxtrace_info_event *auxtrace_info,
> --
> 2.1.4
>

Arnaldo,

As with my previous email, I can't queue this patch for 4.6 without at
least a reviewed by from you.

It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
be happy to use another baseline should that be more adequate for you.

Thanks,
Mathieu

[1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* Re: [PATCH V8 22/23] perf tools: making coresight PMU listable
  2016-01-14 21:46   ` Mathieu Poirier
@ 2016-01-25 20:49     ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-25 20:49 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: linux-arm-kernel, linux-kernel, Peter Zijlstra, Ingo Molnar

On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> Adding the required mechanic allowing 'perf list pmu' to
> discover coresight ETM/PTM tracers.
>
> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  MAINTAINERS                    |  1 +
>  tools/perf/arch/arm/util/Build |  2 ++
>  tools/perf/arch/arm/util/pmu.c | 34 ++++++++++++++++++++++++++++++++++
>  tools/perf/config/Makefile     | 17 ++++++++++++-----
>  4 files changed, 49 insertions(+), 5 deletions(-)
>  create mode 100644 tools/perf/arch/arm/util/pmu.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 233f83464814..b2a92245eece 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1007,6 +1007,7 @@ F:        drivers/hwtracing/coresight/*
>  F:     Documentation/trace/coresight.txt
>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
> +F:     tools/perf/arch/arm/util/pmu.c
>
>  ARM/CORGI MACHINE SUPPORT
>  M:     Richard Purdie <rpurdie@rpsys.net>
> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> index d22e3d07de3d..66ab0b05549c 100644
> --- a/tools/perf/arch/arm/util/Build
> +++ b/tools/perf/arch/arm/util/Build
> @@ -2,3 +2,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>
>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> +
> +libperf-$(CONFIG_AUXTRACE) += pmu.o
> diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c
> new file mode 100644
> index 000000000000..af9fb666b44f
> --- /dev/null
> +++ b/tools/perf/arch/arm/util/pmu.c
> @@ -0,0 +1,34 @@
> +/*
> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <string.h>
> +#include <linux/coresight-pmu.h>
> +#include <linux/perf_event.h>
> +
> +#include "../../util/pmu.h"
> +
> +struct perf_event_attr
> +*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
> +{
> +#ifdef HAVE_AUXTRACE_SUPPORT
> +       if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
> +               /* add ETM default config here */
> +               pmu->selectable = true;
> +       }
> +#endif
> +       return NULL;
> +}
> diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
> index de89ec574361..905faabff8b1 100644
> --- a/tools/perf/config/Makefile
> +++ b/tools/perf/config/Makefile
> @@ -647,12 +647,19 @@ ifdef LIBBABELTRACE
>  endif
>
>  ifndef NO_AUXTRACE
> -  ifeq ($(feature-get_cpuid), 0)
> -    msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
> -    NO_AUXTRACE := 1
> +  ifeq ($(ARCH),x86)
> +    ifeq ($(feature-get_cpuid), 0)
> +      msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
> +      NO_AUXTRACE := 1
> +    else
> +      $(call detected,CONFIG_AUXTRACE)
> +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> +    endif
>    else
> -    $(call detected,CONFIG_AUXTRACE)
> -    CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> +    ifeq ($(ARCH),$(filter $(ARCH), arm arm64))
> +      $(call detected,CONFIG_AUXTRACE)
> +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> +    endif
>    endif
>  endif
>
> --
> 2.1.4
>

Arnaldo,

This is the third (of four) patch that I can't queue without a
reviewed by from you.

It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
be happy to use another baseline should that be more adequate for you.

Thanks,
Mathieu

[1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* [PATCH V8 22/23] perf tools: making coresight PMU listable
@ 2016-01-25 20:49     ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-25 20:49 UTC (permalink / raw)
  To: linux-arm-kernel

On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> Adding the required mechanic allowing 'perf list pmu' to
> discover coresight ETM/PTM tracers.
>
> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  MAINTAINERS                    |  1 +
>  tools/perf/arch/arm/util/Build |  2 ++
>  tools/perf/arch/arm/util/pmu.c | 34 ++++++++++++++++++++++++++++++++++
>  tools/perf/config/Makefile     | 17 ++++++++++++-----
>  4 files changed, 49 insertions(+), 5 deletions(-)
>  create mode 100644 tools/perf/arch/arm/util/pmu.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 233f83464814..b2a92245eece 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1007,6 +1007,7 @@ F:        drivers/hwtracing/coresight/*
>  F:     Documentation/trace/coresight.txt
>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
> +F:     tools/perf/arch/arm/util/pmu.c
>
>  ARM/CORGI MACHINE SUPPORT
>  M:     Richard Purdie <rpurdie@rpsys.net>
> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> index d22e3d07de3d..66ab0b05549c 100644
> --- a/tools/perf/arch/arm/util/Build
> +++ b/tools/perf/arch/arm/util/Build
> @@ -2,3 +2,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>
>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> +
> +libperf-$(CONFIG_AUXTRACE) += pmu.o
> diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c
> new file mode 100644
> index 000000000000..af9fb666b44f
> --- /dev/null
> +++ b/tools/perf/arch/arm/util/pmu.c
> @@ -0,0 +1,34 @@
> +/*
> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <string.h>
> +#include <linux/coresight-pmu.h>
> +#include <linux/perf_event.h>
> +
> +#include "../../util/pmu.h"
> +
> +struct perf_event_attr
> +*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
> +{
> +#ifdef HAVE_AUXTRACE_SUPPORT
> +       if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
> +               /* add ETM default config here */
> +               pmu->selectable = true;
> +       }
> +#endif
> +       return NULL;
> +}
> diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
> index de89ec574361..905faabff8b1 100644
> --- a/tools/perf/config/Makefile
> +++ b/tools/perf/config/Makefile
> @@ -647,12 +647,19 @@ ifdef LIBBABELTRACE
>  endif
>
>  ifndef NO_AUXTRACE
> -  ifeq ($(feature-get_cpuid), 0)
> -    msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
> -    NO_AUXTRACE := 1
> +  ifeq ($(ARCH),x86)
> +    ifeq ($(feature-get_cpuid), 0)
> +      msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
> +      NO_AUXTRACE := 1
> +    else
> +      $(call detected,CONFIG_AUXTRACE)
> +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> +    endif
>    else
> -    $(call detected,CONFIG_AUXTRACE)
> -    CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> +    ifeq ($(ARCH),$(filter $(ARCH), arm arm64))
> +      $(call detected,CONFIG_AUXTRACE)
> +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> +    endif
>    endif
>  endif
>
> --
> 2.1.4
>

Arnaldo,

This is the third (of four) patch that I can't queue without a
reviewed by from you.

It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
be happy to use another baseline should that be more adequate for you.

Thanks,
Mathieu

[1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* Re: [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-01-14 21:46   ` Mathieu Poirier
@ 2016-01-25 20:51     ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-25 20:51 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: linux-kernel, linux-arm-kernel, Peter Zijlstra, Ingo Molnar

On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> Coresight ETMs are IP blocks used to perform HW assisted tracing
> on a CPU core.  This patch introduce the required auxiliary API
> functions allowing the perf core to interact with a tracer.
>
> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  MAINTAINERS                         |   3 +
>  tools/perf/arch/arm/util/Build      |   2 +-
>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
>  tools/perf/util/auxtrace.c          |   1 +
>  tools/perf/util/auxtrace.h          |   1 +
>  7 files changed, 570 insertions(+), 1 deletion(-)
>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index b2a92245eece..a81b2737ebc3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>  F:     tools/perf/arch/arm/util/pmu.c
> +F:     tools/perf/arch/arm/util/auxtrace.c
> +F:     tools/perf/arch/arm/util/cs_etm.c
> +F:     tools/perf/arch/arm/util/cs_etm.h
>
>  ARM/CORGI MACHINE SUPPORT
>  M:     Richard Purdie <rpurdie@rpsys.net>
> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> index 66ab0b05549c..0a25a1248f42 100644
> --- a/tools/perf/arch/arm/util/Build
> +++ b/tools/perf/arch/arm/util/Build
> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>
> -libperf-$(CONFIG_AUXTRACE) += pmu.o
> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
> new file mode 100644
> index 000000000000..d327316f0e8a
> --- /dev/null
> +++ b/tools/perf/arch/arm/util/auxtrace.c
> @@ -0,0 +1,54 @@
> +/*
> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <stdbool.h>
> +#include <linux/coresight-pmu.h>
> +
> +#include "../../util/auxtrace.h"
> +#include "../../util/evlist.h"
> +#include "../../util/pmu.h"
> +#include "cs_etm.h"
> +
> +struct auxtrace_record
> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
> +{
> +       struct perf_pmu *cs_etm_pmu;
> +       struct perf_evsel *evsel;
> +       bool found_etm = false;
> +
> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> +
> +        if (evlist) {
> +               evlist__for_each(evlist, evsel) {
> +                       if (cs_etm_pmu &&
> +                           evsel->attr.type == cs_etm_pmu->type)
> +                               found_etm = true;
> +               }
> +       }
> +
> +       if (found_etm)
> +               return cs_etm_record_init(err);
> +
> +       /*
> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
> +        * record can still be used even if tracers aren't present.  The NULL
> +        * return value will take care of telling the infrastructure HW tracing
> +        * isn't available.
> +        */
> +       *err = 0;
> +       return NULL;
> +}
> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
> new file mode 100644
> index 000000000000..5710b90e23d5
> --- /dev/null
> +++ b/tools/perf/arch/arm/util/cs_etm.c
> @@ -0,0 +1,466 @@
> +/*
> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <api/fs/fs.h>
> +#include <linux/bitops.h>
> +#include <linux/coresight-pmu.h>
> +#include <linux/kernel.h>
> +#include <linux/log2.h>
> +#include <linux/types.h>
> +
> +#include "../../perf.h"
> +#include "../../util/auxtrace.h"
> +#include "../../util/cpumap.h"
> +#include "../../util/evlist.h"
> +#include "../../util/pmu.h"
> +#include "../../util/thread_map.h"
> +#include "cs_etm.h"
> +
> +#include <stdlib.h>
> +
> +#define KiB(x) ((x) * 1024)
> +#define MiB(x) ((x) * 1024 * 1024)
> +
> +struct cs_etm_recording {
> +       struct auxtrace_record  itr;
> +       struct perf_pmu         *cs_etm_pmu;
> +       struct perf_evlist      *evlist;
> +       bool                    snapshot_mode;
> +       size_t                  snapshot_size;
> +};
> +
> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
> +                                        struct record_opts *opts,
> +                                        const char *str)
> +{
> +       struct cs_etm_recording *ptr =
> +                               container_of(itr, struct cs_etm_recording, itr);
> +       unsigned long long snapshot_size = 0;
> +       char *endptr;
> +
> +       if (str) {
> +               snapshot_size = strtoull(str, &endptr, 0);
> +               if (*endptr || snapshot_size > SIZE_MAX)
> +                       return -1;
> +       }
> +
> +       opts->auxtrace_snapshot_mode = true;
> +       opts->auxtrace_snapshot_size = snapshot_size;
> +       ptr->snapshot_size = snapshot_size;
> +
> +       return 0;
> +}
> +
> +static int cs_etm_recording_options(struct auxtrace_record *itr,
> +                                   struct perf_evlist *evlist,
> +                                   struct record_opts *opts)
> +{
> +       struct cs_etm_recording *ptr =
> +                               container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
> +       const struct cpu_map *cpus = evlist->cpus;
> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
> +
> +       ptr->evlist = evlist;
> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
> +
> +       evlist__for_each(evlist, evsel) {
> +               if (evsel->attr.type == cs_etm_pmu->type) {
> +                       if (cs_etm_evsel) {
> +                               pr_err("There may be only one %s event\n",
> +                                      CORESIGHT_ETM_PMU_NAME);
> +                               return -EINVAL;
> +                       }
> +                       evsel->attr.freq = 0;
> +                       evsel->attr.sample_period = 1;
> +                       cs_etm_evsel = evsel;
> +                       opts->full_auxtrace = true;
> +               }
> +       }
> +
> +       /* no need to continue if at least one event of interest was found */
> +       if (!cs_etm_evsel)
> +               return 0;
> +
> +       if (opts->use_clockid) {
> +               pr_err("Cannot use clockid (-k option) with %s\n",
> +                      CORESIGHT_ETM_PMU_NAME);
> +               return -EINVAL;
> +       }
> +
> +       /* we are in snapshot mode */
> +       if (opts->auxtrace_snapshot_mode) {
> +               /*
> +                * No size were given to '-S' or '-m,', so go with
> +                * the default
> +                */
> +               if (!opts->auxtrace_snapshot_size &&
> +                   !opts->auxtrace_mmap_pages) {
> +                       if (privileged) {
> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
> +                       } else {
> +                               opts->auxtrace_mmap_pages =
> +                                                       KiB(128) / page_size;
> +                               if (opts->mmap_pages == UINT_MAX)
> +                                       opts->mmap_pages = KiB(256) / page_size;
> +                       }
> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
> +                                               opts->mmap_pages == UINT_MAX) {
> +                       opts->mmap_pages = KiB(256) / page_size;
> +               }
> +
> +               /*
> +                * '-m,xyz' was specified but no snapshot size, so make the
> +                * snapshot size as big as the auxtrace mmap area.
> +                */
> +               if (!opts->auxtrace_snapshot_size) {
> +                       opts->auxtrace_snapshot_size =
> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
> +               }
> +
> +               /*
> +                * -Sxyz was specified but no auxtrace mmap area, so make the
> +                * auxtrace mmap area big enough to fit the requested snapshot
> +                * size.
> +                */
> +               if (!opts->auxtrace_mmap_pages) {
> +                       size_t sz = opts->auxtrace_snapshot_size;
> +
> +                       sz = round_up(sz, page_size) / page_size;
> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
> +               }
> +
> +               /* Snapshost size can't be bigger than the auxtrace area */
> +               if (opts->auxtrace_snapshot_size >
> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
> +                              opts->auxtrace_snapshot_size,
> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
> +                       return -EINVAL;
> +               }
> +
> +               /* Something went wrong somewhere - this shouldn't happen */
> +               if (!opts->auxtrace_snapshot_size ||
> +                   !opts->auxtrace_mmap_pages) {
> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
> +               if (privileged) {
> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
> +               } else {
> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
> +                       if (opts->mmap_pages == UINT_MAX)
> +                               opts->mmap_pages = KiB(256) / page_size;
> +               }
> +
> +       }
> +
> +       /* Validate auxtrace_mmap_pages provided by user */
> +       if (opts->auxtrace_mmap_pages) {
> +               unsigned int max_page = (KiB(128) / page_size);
> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
> +
> +               if (!privileged &&
> +                   opts->auxtrace_mmap_pages > max_page) {
> +                       opts->auxtrace_mmap_pages = max_page;
> +                       pr_err("auxtrace too big, truncating to %d\n",
> +                              max_page);
> +               }
> +
> +               if (!is_power_of_2(sz)) {
> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
> +                              CORESIGHT_ETM_PMU_NAME);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       if (opts->auxtrace_snapshot_mode)
> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
> +                         opts->auxtrace_snapshot_size);
> +
> +       if (cs_etm_evsel) {
> +               /*
> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
> +                * event must come first.
> +                */
> +               perf_evlist__to_front(evlist, cs_etm_evsel);
> +               /*
> +                * In the case of per-cpu mmaps, we need the CPU on the
> +                * AUX event.
> +                */
> +               if (!cpu_map__empty(cpus))
> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
> +       }
> +
> +       /* Add dummy event to keep tracking */
> +       if (opts->full_auxtrace) {
> +               struct perf_evsel *tracking_evsel;
> +               int err;
> +
> +               err = parse_events(evlist, "dummy:u", NULL);
> +               if (err)
> +                       return err;
> +
> +               tracking_evsel = perf_evlist__last(evlist);
> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
> +
> +               tracking_evsel->attr.freq = 0;
> +               tracking_evsel->attr.sample_period = 1;
> +
> +               /* In per-cpu case, always need the time of mmap events etc */
> +               if (!cpu_map__empty(cpus))
> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
> +       }
> +
> +       return 0;
> +}
> +
> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
> +{
> +       u64 config = 0;
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> +       struct perf_evlist *evlist = ptr->evlist;
> +       struct perf_evsel *evsel;
> +
> +       evlist__for_each(evlist, evsel) {
> +               if (evsel->attr.type == cs_etm_pmu->type) {
> +                       /*
> +                        * Variable perf_event_attr::config is assigned to
> +                        * ETMv3/PTM.  The bit fields have been made to match
> +                        * the ETMv3.5 ETRMCR register specification.  See the
> +                        * PMU_FORMAT_ATTR() declarations in
> +                        * drivers/hwtracing/coresight/coresight-perf.c for
> +                        * details.
> +                        */
> +                       config = evsel->attr.config;
> +                       break;
> +               }
> +       }
> +
> +       return config;
> +}
> +
> +static size_t
> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> +                     struct perf_evlist *evlist __maybe_unused)
> +{
> +       int records;
> +       const struct cpu_map *cpus = evlist->cpus;
> +
> +       if (!cpu_map__empty(cpus)) {
> +               records = cpu_map__nr(cpus);
> +               goto out;
> +       }
> +
> +       set_max_cpu_num();
> +       records = cpu__max_cpu();
> +out:
> +       return records * CS_ETM_PRIV_SIZE;
> +}
> +
> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
> +};
> +
> +static int cs_etm_get_metadata(int cpu, int index,
> +                              struct auxtrace_record *itr,
> +                              struct auxtrace_info_event *info)
> +{
> +       char path[PATH_MAX];
> +       int offset = 0, ret = 0;
> +       int i, scan;
> +       unsigned int val;
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> +
> +       offset = index * CS_ETM_PRIV_MAX;
> +
> +       /* Build generic header portion */
> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
> +       info->priv[offset + CS_ETM_CPU] = cpu;
> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
> +
> +       /* Get user configurables from the session */
> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
> +
> +       /* Get RO metadata from sysfs */
> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
> +
> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
> +               if (scan != 1) {
> +                       ret = -EINVAL;
> +                       break;
> +               }
> +
> +               info->priv[offset + i] = val;
> +       }
> +
> +       return ret;
> +}
> +
> +static int cs_etm_info_fill(struct auxtrace_record *itr,
> +                           struct perf_session *session,
> +                           struct auxtrace_info_event *auxtrace_info,
> +                           size_t priv_size)
> +{
> +       int i, nr_cpu, ret = 0;
> +       const struct cpu_map *cpus = session->evlist->cpus;
> +
> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
> +               return -EINVAL;
> +
> +       if (!session->evlist->nr_mmaps)
> +               return -EINVAL;
> +
> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
> +
> +       /* cpu map is not empty, we have specific CPUs to work with */
> +       if (!cpu_map__empty(cpus)) {
> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
> +                                                 itr, auxtrace_info);
> +                       if (ret)
> +                               goto out;
> +               }
> +       } else {
> +               /* get configuration for all CPUs in the system */
> +               nr_cpu = cpu__max_cpu();
> +               for (i = 0; i < nr_cpu; i++) {
> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
> +                       if (ret)
> +                               goto out;
> +               }
> +       }
> +
> +out:
> +       return ret;
> +}
> +
> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
> +                               int idx, struct auxtrace_mmap *mm,
> +                               unsigned char *data __maybe_unused,
> +                               u64 *head, u64 *old)
> +{
> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
> +
> +       *old = *head;
> +       *head += mm->len;
> +
> +       return 0;
> +}
> +
> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
> +{
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_evsel *evsel;
> +
> +       evlist__for_each(ptr->evlist, evsel) {
> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
> +       }
> +       return -EINVAL;
> +}
> +
> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
> +{
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_evsel *evsel;
> +
> +       evlist__for_each(ptr->evlist, evsel) {
> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
> +       }
> +       return -EINVAL;
> +}
> +
> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
> +{
> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
> +}
> +
> +static void cs_etm_recording_free(struct auxtrace_record *itr)
> +{
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       free(ptr);
> +}
> +
> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
> +{
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_evsel *evsel;
> +
> +       evlist__for_each(ptr->evlist, evsel) {
> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> +                       return perf_evlist__enable_event_idx(ptr->evlist,
> +                                                            evsel, idx);
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +struct auxtrace_record *cs_etm_record_init(int *err)
> +{
> +       struct perf_pmu *cs_etm_pmu;
> +       struct cs_etm_recording *ptr;
> +
> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> +
> +       if (!cs_etm_pmu) {
> +               *err = -EINVAL;
> +               goto out;
> +       }
> +
> +       ptr = zalloc(sizeof(struct cs_etm_recording));
> +       if (!ptr) {
> +               *err = -ENOMEM;
> +               goto out;
> +       }
> +
> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
> +       ptr->itr.recording_options      = cs_etm_recording_options;
> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
> +       ptr->itr.info_fill              = cs_etm_info_fill;
> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
> +       ptr->itr.reference              = cs_etm_reference;
> +       ptr->itr.free                   = cs_etm_recording_free;
> +       ptr->itr.read_finish            = cs_etm_read_finish;
> +
> +       *err = 0;
> +       return &ptr->itr;
> +out:
> +       return NULL;
> +}
> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
> new file mode 100644
> index 000000000000..7e85c1b43598
> --- /dev/null
> +++ b/tools/perf/arch/arm/util/cs_etm.h
> @@ -0,0 +1,44 @@
> +/*
> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef INCLUDE__PERF_CS_ETM_H__
> +#define INCLUDE__PERF_CS_ETM_H__
> +
> +/* Beginning of header common to both ETMv3 and V4 */
> +enum {
> +       CS_ETM_MAGIC,
> +       CS_ETM_CPU,
> +       CS_ETM_SNAPSHOT,
> +};
> +
> +/* ETMv3/PTM metadata */
> +enum {
> +       /* Dynamic, configurable parameters */
> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
> +       CS_ETM_ETMTRACEIDR,
> +       /* RO, taken from sysFS */
> +       CS_ETM_ETMCCER,
> +       CS_ETM_ETMIDR,
> +       CS_ETM_PRIV_MAX,
> +};
> +
> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
> +
> +struct auxtrace_record *cs_etm_record_init(int *err);
> +
> +#endif
> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> index cc1c9ce5cc56..a6f291dbc4d9 100644
> --- a/tools/perf/util/auxtrace.c
> +++ b/tools/perf/util/auxtrace.c
> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
>                 return intel_pt_process_auxtrace_info(event, session);
>         case PERF_AUXTRACE_INTEL_BTS:
>                 return intel_bts_process_auxtrace_info(event, session);
> +       case PERF_AUXTRACE_CS_ETM:
>         case PERF_AUXTRACE_UNKNOWN:
>         default:
>                 return -EINVAL;
> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> index e5a8e2d4f2af..adb53e7bcabf 100644
> --- a/tools/perf/util/auxtrace.h
> +++ b/tools/perf/util/auxtrace.h
> @@ -41,6 +41,7 @@ enum auxtrace_type {
>         PERF_AUXTRACE_UNKNOWN,
>         PERF_AUXTRACE_INTEL_PT,
>         PERF_AUXTRACE_INTEL_BTS,
> +       PERF_AUXTRACE_CS_ETM,
>  };
>
>  enum itrace_period_type {
> --
> 2.1.4
>

Arnaldo,

Last but not least, this is the final patch that I would like you to
review before queing.

It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
be happy to use another baseline should that be more adequate for you.

Thanks,
Mathieu

[1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-01-25 20:51     ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-25 20:51 UTC (permalink / raw)
  To: linux-arm-kernel

On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> Coresight ETMs are IP blocks used to perform HW assisted tracing
> on a CPU core.  This patch introduce the required auxiliary API
> functions allowing the perf core to interact with a tracer.
>
> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  MAINTAINERS                         |   3 +
>  tools/perf/arch/arm/util/Build      |   2 +-
>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
>  tools/perf/util/auxtrace.c          |   1 +
>  tools/perf/util/auxtrace.h          |   1 +
>  7 files changed, 570 insertions(+), 1 deletion(-)
>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index b2a92245eece..a81b2737ebc3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>  F:     tools/perf/arch/arm/util/pmu.c
> +F:     tools/perf/arch/arm/util/auxtrace.c
> +F:     tools/perf/arch/arm/util/cs_etm.c
> +F:     tools/perf/arch/arm/util/cs_etm.h
>
>  ARM/CORGI MACHINE SUPPORT
>  M:     Richard Purdie <rpurdie@rpsys.net>
> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> index 66ab0b05549c..0a25a1248f42 100644
> --- a/tools/perf/arch/arm/util/Build
> +++ b/tools/perf/arch/arm/util/Build
> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>
> -libperf-$(CONFIG_AUXTRACE) += pmu.o
> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
> new file mode 100644
> index 000000000000..d327316f0e8a
> --- /dev/null
> +++ b/tools/perf/arch/arm/util/auxtrace.c
> @@ -0,0 +1,54 @@
> +/*
> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <stdbool.h>
> +#include <linux/coresight-pmu.h>
> +
> +#include "../../util/auxtrace.h"
> +#include "../../util/evlist.h"
> +#include "../../util/pmu.h"
> +#include "cs_etm.h"
> +
> +struct auxtrace_record
> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
> +{
> +       struct perf_pmu *cs_etm_pmu;
> +       struct perf_evsel *evsel;
> +       bool found_etm = false;
> +
> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> +
> +        if (evlist) {
> +               evlist__for_each(evlist, evsel) {
> +                       if (cs_etm_pmu &&
> +                           evsel->attr.type == cs_etm_pmu->type)
> +                               found_etm = true;
> +               }
> +       }
> +
> +       if (found_etm)
> +               return cs_etm_record_init(err);
> +
> +       /*
> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
> +        * record can still be used even if tracers aren't present.  The NULL
> +        * return value will take care of telling the infrastructure HW tracing
> +        * isn't available.
> +        */
> +       *err = 0;
> +       return NULL;
> +}
> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
> new file mode 100644
> index 000000000000..5710b90e23d5
> --- /dev/null
> +++ b/tools/perf/arch/arm/util/cs_etm.c
> @@ -0,0 +1,466 @@
> +/*
> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <api/fs/fs.h>
> +#include <linux/bitops.h>
> +#include <linux/coresight-pmu.h>
> +#include <linux/kernel.h>
> +#include <linux/log2.h>
> +#include <linux/types.h>
> +
> +#include "../../perf.h"
> +#include "../../util/auxtrace.h"
> +#include "../../util/cpumap.h"
> +#include "../../util/evlist.h"
> +#include "../../util/pmu.h"
> +#include "../../util/thread_map.h"
> +#include "cs_etm.h"
> +
> +#include <stdlib.h>
> +
> +#define KiB(x) ((x) * 1024)
> +#define MiB(x) ((x) * 1024 * 1024)
> +
> +struct cs_etm_recording {
> +       struct auxtrace_record  itr;
> +       struct perf_pmu         *cs_etm_pmu;
> +       struct perf_evlist      *evlist;
> +       bool                    snapshot_mode;
> +       size_t                  snapshot_size;
> +};
> +
> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
> +                                        struct record_opts *opts,
> +                                        const char *str)
> +{
> +       struct cs_etm_recording *ptr =
> +                               container_of(itr, struct cs_etm_recording, itr);
> +       unsigned long long snapshot_size = 0;
> +       char *endptr;
> +
> +       if (str) {
> +               snapshot_size = strtoull(str, &endptr, 0);
> +               if (*endptr || snapshot_size > SIZE_MAX)
> +                       return -1;
> +       }
> +
> +       opts->auxtrace_snapshot_mode = true;
> +       opts->auxtrace_snapshot_size = snapshot_size;
> +       ptr->snapshot_size = snapshot_size;
> +
> +       return 0;
> +}
> +
> +static int cs_etm_recording_options(struct auxtrace_record *itr,
> +                                   struct perf_evlist *evlist,
> +                                   struct record_opts *opts)
> +{
> +       struct cs_etm_recording *ptr =
> +                               container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
> +       const struct cpu_map *cpus = evlist->cpus;
> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
> +
> +       ptr->evlist = evlist;
> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
> +
> +       evlist__for_each(evlist, evsel) {
> +               if (evsel->attr.type == cs_etm_pmu->type) {
> +                       if (cs_etm_evsel) {
> +                               pr_err("There may be only one %s event\n",
> +                                      CORESIGHT_ETM_PMU_NAME);
> +                               return -EINVAL;
> +                       }
> +                       evsel->attr.freq = 0;
> +                       evsel->attr.sample_period = 1;
> +                       cs_etm_evsel = evsel;
> +                       opts->full_auxtrace = true;
> +               }
> +       }
> +
> +       /* no need to continue if at least one event of interest was found */
> +       if (!cs_etm_evsel)
> +               return 0;
> +
> +       if (opts->use_clockid) {
> +               pr_err("Cannot use clockid (-k option) with %s\n",
> +                      CORESIGHT_ETM_PMU_NAME);
> +               return -EINVAL;
> +       }
> +
> +       /* we are in snapshot mode */
> +       if (opts->auxtrace_snapshot_mode) {
> +               /*
> +                * No size were given to '-S' or '-m,', so go with
> +                * the default
> +                */
> +               if (!opts->auxtrace_snapshot_size &&
> +                   !opts->auxtrace_mmap_pages) {
> +                       if (privileged) {
> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
> +                       } else {
> +                               opts->auxtrace_mmap_pages =
> +                                                       KiB(128) / page_size;
> +                               if (opts->mmap_pages == UINT_MAX)
> +                                       opts->mmap_pages = KiB(256) / page_size;
> +                       }
> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
> +                                               opts->mmap_pages == UINT_MAX) {
> +                       opts->mmap_pages = KiB(256) / page_size;
> +               }
> +
> +               /*
> +                * '-m,xyz' was specified but no snapshot size, so make the
> +                * snapshot size as big as the auxtrace mmap area.
> +                */
> +               if (!opts->auxtrace_snapshot_size) {
> +                       opts->auxtrace_snapshot_size =
> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
> +               }
> +
> +               /*
> +                * -Sxyz was specified but no auxtrace mmap area, so make the
> +                * auxtrace mmap area big enough to fit the requested snapshot
> +                * size.
> +                */
> +               if (!opts->auxtrace_mmap_pages) {
> +                       size_t sz = opts->auxtrace_snapshot_size;
> +
> +                       sz = round_up(sz, page_size) / page_size;
> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
> +               }
> +
> +               /* Snapshost size can't be bigger than the auxtrace area */
> +               if (opts->auxtrace_snapshot_size >
> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
> +                              opts->auxtrace_snapshot_size,
> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
> +                       return -EINVAL;
> +               }
> +
> +               /* Something went wrong somewhere - this shouldn't happen */
> +               if (!opts->auxtrace_snapshot_size ||
> +                   !opts->auxtrace_mmap_pages) {
> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
> +               if (privileged) {
> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
> +               } else {
> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
> +                       if (opts->mmap_pages == UINT_MAX)
> +                               opts->mmap_pages = KiB(256) / page_size;
> +               }
> +
> +       }
> +
> +       /* Validate auxtrace_mmap_pages provided by user */
> +       if (opts->auxtrace_mmap_pages) {
> +               unsigned int max_page = (KiB(128) / page_size);
> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
> +
> +               if (!privileged &&
> +                   opts->auxtrace_mmap_pages > max_page) {
> +                       opts->auxtrace_mmap_pages = max_page;
> +                       pr_err("auxtrace too big, truncating to %d\n",
> +                              max_page);
> +               }
> +
> +               if (!is_power_of_2(sz)) {
> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
> +                              CORESIGHT_ETM_PMU_NAME);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       if (opts->auxtrace_snapshot_mode)
> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
> +                         opts->auxtrace_snapshot_size);
> +
> +       if (cs_etm_evsel) {
> +               /*
> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
> +                * event must come first.
> +                */
> +               perf_evlist__to_front(evlist, cs_etm_evsel);
> +               /*
> +                * In the case of per-cpu mmaps, we need the CPU on the
> +                * AUX event.
> +                */
> +               if (!cpu_map__empty(cpus))
> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
> +       }
> +
> +       /* Add dummy event to keep tracking */
> +       if (opts->full_auxtrace) {
> +               struct perf_evsel *tracking_evsel;
> +               int err;
> +
> +               err = parse_events(evlist, "dummy:u", NULL);
> +               if (err)
> +                       return err;
> +
> +               tracking_evsel = perf_evlist__last(evlist);
> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
> +
> +               tracking_evsel->attr.freq = 0;
> +               tracking_evsel->attr.sample_period = 1;
> +
> +               /* In per-cpu case, always need the time of mmap events etc */
> +               if (!cpu_map__empty(cpus))
> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
> +       }
> +
> +       return 0;
> +}
> +
> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
> +{
> +       u64 config = 0;
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> +       struct perf_evlist *evlist = ptr->evlist;
> +       struct perf_evsel *evsel;
> +
> +       evlist__for_each(evlist, evsel) {
> +               if (evsel->attr.type == cs_etm_pmu->type) {
> +                       /*
> +                        * Variable perf_event_attr::config is assigned to
> +                        * ETMv3/PTM.  The bit fields have been made to match
> +                        * the ETMv3.5 ETRMCR register specification.  See the
> +                        * PMU_FORMAT_ATTR() declarations in
> +                        * drivers/hwtracing/coresight/coresight-perf.c for
> +                        * details.
> +                        */
> +                       config = evsel->attr.config;
> +                       break;
> +               }
> +       }
> +
> +       return config;
> +}
> +
> +static size_t
> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> +                     struct perf_evlist *evlist __maybe_unused)
> +{
> +       int records;
> +       const struct cpu_map *cpus = evlist->cpus;
> +
> +       if (!cpu_map__empty(cpus)) {
> +               records = cpu_map__nr(cpus);
> +               goto out;
> +       }
> +
> +       set_max_cpu_num();
> +       records = cpu__max_cpu();
> +out:
> +       return records * CS_ETM_PRIV_SIZE;
> +}
> +
> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
> +};
> +
> +static int cs_etm_get_metadata(int cpu, int index,
> +                              struct auxtrace_record *itr,
> +                              struct auxtrace_info_event *info)
> +{
> +       char path[PATH_MAX];
> +       int offset = 0, ret = 0;
> +       int i, scan;
> +       unsigned int val;
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> +
> +       offset = index * CS_ETM_PRIV_MAX;
> +
> +       /* Build generic header portion */
> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
> +       info->priv[offset + CS_ETM_CPU] = cpu;
> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
> +
> +       /* Get user configurables from the session */
> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
> +
> +       /* Get RO metadata from sysfs */
> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
> +
> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
> +               if (scan != 1) {
> +                       ret = -EINVAL;
> +                       break;
> +               }
> +
> +               info->priv[offset + i] = val;
> +       }
> +
> +       return ret;
> +}
> +
> +static int cs_etm_info_fill(struct auxtrace_record *itr,
> +                           struct perf_session *session,
> +                           struct auxtrace_info_event *auxtrace_info,
> +                           size_t priv_size)
> +{
> +       int i, nr_cpu, ret = 0;
> +       const struct cpu_map *cpus = session->evlist->cpus;
> +
> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
> +               return -EINVAL;
> +
> +       if (!session->evlist->nr_mmaps)
> +               return -EINVAL;
> +
> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
> +
> +       /* cpu map is not empty, we have specific CPUs to work with */
> +       if (!cpu_map__empty(cpus)) {
> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
> +                                                 itr, auxtrace_info);
> +                       if (ret)
> +                               goto out;
> +               }
> +       } else {
> +               /* get configuration for all CPUs in the system */
> +               nr_cpu = cpu__max_cpu();
> +               for (i = 0; i < nr_cpu; i++) {
> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
> +                       if (ret)
> +                               goto out;
> +               }
> +       }
> +
> +out:
> +       return ret;
> +}
> +
> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
> +                               int idx, struct auxtrace_mmap *mm,
> +                               unsigned char *data __maybe_unused,
> +                               u64 *head, u64 *old)
> +{
> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
> +
> +       *old = *head;
> +       *head += mm->len;
> +
> +       return 0;
> +}
> +
> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
> +{
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_evsel *evsel;
> +
> +       evlist__for_each(ptr->evlist, evsel) {
> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
> +       }
> +       return -EINVAL;
> +}
> +
> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
> +{
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_evsel *evsel;
> +
> +       evlist__for_each(ptr->evlist, evsel) {
> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
> +       }
> +       return -EINVAL;
> +}
> +
> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
> +{
> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
> +}
> +
> +static void cs_etm_recording_free(struct auxtrace_record *itr)
> +{
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       free(ptr);
> +}
> +
> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
> +{
> +       struct cs_etm_recording *ptr =
> +                       container_of(itr, struct cs_etm_recording, itr);
> +       struct perf_evsel *evsel;
> +
> +       evlist__for_each(ptr->evlist, evsel) {
> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> +                       return perf_evlist__enable_event_idx(ptr->evlist,
> +                                                            evsel, idx);
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +struct auxtrace_record *cs_etm_record_init(int *err)
> +{
> +       struct perf_pmu *cs_etm_pmu;
> +       struct cs_etm_recording *ptr;
> +
> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> +
> +       if (!cs_etm_pmu) {
> +               *err = -EINVAL;
> +               goto out;
> +       }
> +
> +       ptr = zalloc(sizeof(struct cs_etm_recording));
> +       if (!ptr) {
> +               *err = -ENOMEM;
> +               goto out;
> +       }
> +
> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
> +       ptr->itr.recording_options      = cs_etm_recording_options;
> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
> +       ptr->itr.info_fill              = cs_etm_info_fill;
> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
> +       ptr->itr.reference              = cs_etm_reference;
> +       ptr->itr.free                   = cs_etm_recording_free;
> +       ptr->itr.read_finish            = cs_etm_read_finish;
> +
> +       *err = 0;
> +       return &ptr->itr;
> +out:
> +       return NULL;
> +}
> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
> new file mode 100644
> index 000000000000..7e85c1b43598
> --- /dev/null
> +++ b/tools/perf/arch/arm/util/cs_etm.h
> @@ -0,0 +1,44 @@
> +/*
> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef INCLUDE__PERF_CS_ETM_H__
> +#define INCLUDE__PERF_CS_ETM_H__
> +
> +/* Beginning of header common to both ETMv3 and V4 */
> +enum {
> +       CS_ETM_MAGIC,
> +       CS_ETM_CPU,
> +       CS_ETM_SNAPSHOT,
> +};
> +
> +/* ETMv3/PTM metadata */
> +enum {
> +       /* Dynamic, configurable parameters */
> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
> +       CS_ETM_ETMTRACEIDR,
> +       /* RO, taken from sysFS */
> +       CS_ETM_ETMCCER,
> +       CS_ETM_ETMIDR,
> +       CS_ETM_PRIV_MAX,
> +};
> +
> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
> +
> +struct auxtrace_record *cs_etm_record_init(int *err);
> +
> +#endif
> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> index cc1c9ce5cc56..a6f291dbc4d9 100644
> --- a/tools/perf/util/auxtrace.c
> +++ b/tools/perf/util/auxtrace.c
> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
>                 return intel_pt_process_auxtrace_info(event, session);
>         case PERF_AUXTRACE_INTEL_BTS:
>                 return intel_bts_process_auxtrace_info(event, session);
> +       case PERF_AUXTRACE_CS_ETM:
>         case PERF_AUXTRACE_UNKNOWN:
>         default:
>                 return -EINVAL;
> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> index e5a8e2d4f2af..adb53e7bcabf 100644
> --- a/tools/perf/util/auxtrace.h
> +++ b/tools/perf/util/auxtrace.h
> @@ -41,6 +41,7 @@ enum auxtrace_type {
>         PERF_AUXTRACE_UNKNOWN,
>         PERF_AUXTRACE_INTEL_PT,
>         PERF_AUXTRACE_INTEL_BTS,
> +       PERF_AUXTRACE_CS_ETM,
>  };
>
>  enum itrace_period_type {
> --
> 2.1.4
>

Arnaldo,

Last but not least, this is the final patch that I would like you to
review before queing.

It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
be happy to use another baseline should that be more adequate for you.

Thanks,
Mathieu

[1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* Re: [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
  2016-01-25 20:48     ` Mathieu Poirier
@ 2016-01-25 21:08       ` Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:08 UTC (permalink / raw)
  To: Adrian Hunter, Mathieu Poirier
  Cc: linux-arm-kernel, Peter Zijlstra, linux-kernel, Ingo Molnar

Em Mon, Jan 25, 2016 at 01:48:28PM -0700, Mathieu Poirier escreveu:
> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > On some architecture the size of the private header may
> > be dependent on the number of tracers used in the session.  As
> > such adding a "struct perf_evlist *" parameter, which should
> > contain all the required information.
> >
> > Also adjusting the existing client of the interface to take
> > the new parameter into account.
> >
> > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> >  tools/perf/arch/x86/util/intel-bts.c | 4 +++-
> >  tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
> >  tools/perf/util/auxtrace.c           | 7 ++++---
> >  tools/perf/util/auxtrace.h           | 6 ++++--
> >  4 files changed, 14 insertions(+), 7 deletions(-)
> >
> > diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
> > index 9b94ce520917..4685a40777cc 100644
> > --- a/tools/perf/arch/x86/util/intel-bts.c
> > +++ b/tools/perf/arch/x86/util/intel-bts.c
> > @@ -60,7 +60,9 @@ struct branch {
> >         u64 misc;
> >  };
> >
> > -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
> > +static size_t
> > +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> > +                        struct perf_evlist *evlist __maybe_unused)
> >  {
> >         return INTEL_BTS_AUXTRACE_PRIV_SIZE;
> >  }
> > diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
> > index b02af064f0f9..e5c1f2e21f87 100644
> > --- a/tools/perf/arch/x86/util/intel-pt.c
> > +++ b/tools/perf/arch/x86/util/intel-pt.c
> > @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
> >         return attr;
> >  }
> >
> > -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
> > +static size_t
> > +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> > +                       struct perf_evlist *evlist __maybe_unused)
> >  {
> >         return INTEL_PT_AUXTRACE_PRIV_SIZE;
> >  }
> > diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> > index 7f10430af39c..cc1c9ce5cc56 100644
> > --- a/tools/perf/util/auxtrace.c
> > +++ b/tools/perf/util/auxtrace.c
> > @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
> >                          heap_array[last].ordinal);
> >  }
> >
> > -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
> > +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
> > +                                      struct perf_evlist *evlist)
> >  {
> >         if (itr)
> > -               return itr->info_priv_size(itr);
> > +               return itr->info_priv_size(itr, evlist);
> >         return 0;
> >  }
> >
> > @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
> >         int err;
> >
> >         pr_debug2("Synthesizing auxtrace information\n");
> > -       priv_size = auxtrace_record__info_priv_size(itr);
> > +       priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
> >         ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
> >         if (!ev)
> >                 return -ENOMEM;
> > diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> > index b86f90db1352..e5a8e2d4f2af 100644
> > --- a/tools/perf/util/auxtrace.h
> > +++ b/tools/perf/util/auxtrace.h
> > @@ -293,7 +293,8 @@ struct auxtrace_record {
> >         int (*recording_options)(struct auxtrace_record *itr,
> >                                  struct perf_evlist *evlist,
> >                                  struct record_opts *opts);
> > -       size_t (*info_priv_size)(struct auxtrace_record *itr);
> > +       size_t (*info_priv_size)(struct auxtrace_record *itr,
> > +                                struct perf_evlist *evlist);
> >         int (*info_fill)(struct auxtrace_record *itr,
> >                          struct perf_session *session,
> >                          struct auxtrace_info_event *auxtrace_info,
> > @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
> >  int auxtrace_record__options(struct auxtrace_record *itr,
> >                              struct perf_evlist *evlist,
> >                              struct record_opts *opts);
> > -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
> > +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
> > +                                      struct perf_evlist *evlist);
> >  int auxtrace_record__info_fill(struct auxtrace_record *itr,
> >                                struct perf_session *session,
> >                                struct auxtrace_info_event *auxtrace_info,
> > --
> > 2.1.4
> >
> 
> Arnaldo,
> 
> As with my previous email, I can't queue this patch for 4.6 without at
> least a reviewed by from you.
> 
> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> be happy to use another baseline should that be more adequate for you.

Adrian, are you ok with this?

- Arnaldo

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

* [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
@ 2016-01-25 21:08       ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:08 UTC (permalink / raw)
  To: linux-arm-kernel

Em Mon, Jan 25, 2016 at 01:48:28PM -0700, Mathieu Poirier escreveu:
> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > On some architecture the size of the private header may
> > be dependent on the number of tracers used in the session.  As
> > such adding a "struct perf_evlist *" parameter, which should
> > contain all the required information.
> >
> > Also adjusting the existing client of the interface to take
> > the new parameter into account.
> >
> > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> >  tools/perf/arch/x86/util/intel-bts.c | 4 +++-
> >  tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
> >  tools/perf/util/auxtrace.c           | 7 ++++---
> >  tools/perf/util/auxtrace.h           | 6 ++++--
> >  4 files changed, 14 insertions(+), 7 deletions(-)
> >
> > diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
> > index 9b94ce520917..4685a40777cc 100644
> > --- a/tools/perf/arch/x86/util/intel-bts.c
> > +++ b/tools/perf/arch/x86/util/intel-bts.c
> > @@ -60,7 +60,9 @@ struct branch {
> >         u64 misc;
> >  };
> >
> > -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
> > +static size_t
> > +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> > +                        struct perf_evlist *evlist __maybe_unused)
> >  {
> >         return INTEL_BTS_AUXTRACE_PRIV_SIZE;
> >  }
> > diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
> > index b02af064f0f9..e5c1f2e21f87 100644
> > --- a/tools/perf/arch/x86/util/intel-pt.c
> > +++ b/tools/perf/arch/x86/util/intel-pt.c
> > @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
> >         return attr;
> >  }
> >
> > -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
> > +static size_t
> > +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> > +                       struct perf_evlist *evlist __maybe_unused)
> >  {
> >         return INTEL_PT_AUXTRACE_PRIV_SIZE;
> >  }
> > diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> > index 7f10430af39c..cc1c9ce5cc56 100644
> > --- a/tools/perf/util/auxtrace.c
> > +++ b/tools/perf/util/auxtrace.c
> > @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
> >                          heap_array[last].ordinal);
> >  }
> >
> > -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
> > +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
> > +                                      struct perf_evlist *evlist)
> >  {
> >         if (itr)
> > -               return itr->info_priv_size(itr);
> > +               return itr->info_priv_size(itr, evlist);
> >         return 0;
> >  }
> >
> > @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
> >         int err;
> >
> >         pr_debug2("Synthesizing auxtrace information\n");
> > -       priv_size = auxtrace_record__info_priv_size(itr);
> > +       priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
> >         ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
> >         if (!ev)
> >                 return -ENOMEM;
> > diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> > index b86f90db1352..e5a8e2d4f2af 100644
> > --- a/tools/perf/util/auxtrace.h
> > +++ b/tools/perf/util/auxtrace.h
> > @@ -293,7 +293,8 @@ struct auxtrace_record {
> >         int (*recording_options)(struct auxtrace_record *itr,
> >                                  struct perf_evlist *evlist,
> >                                  struct record_opts *opts);
> > -       size_t (*info_priv_size)(struct auxtrace_record *itr);
> > +       size_t (*info_priv_size)(struct auxtrace_record *itr,
> > +                                struct perf_evlist *evlist);
> >         int (*info_fill)(struct auxtrace_record *itr,
> >                          struct perf_session *session,
> >                          struct auxtrace_info_event *auxtrace_info,
> > @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
> >  int auxtrace_record__options(struct auxtrace_record *itr,
> >                              struct perf_evlist *evlist,
> >                              struct record_opts *opts);
> > -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
> > +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
> > +                                      struct perf_evlist *evlist);
> >  int auxtrace_record__info_fill(struct auxtrace_record *itr,
> >                                struct perf_session *session,
> >                                struct auxtrace_info_event *auxtrace_info,
> > --
> > 2.1.4
> >
> 
> Arnaldo,
> 
> As with my previous email, I can't queue this patch for 4.6 without at
> least a reviewed by from you.
> 
> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> be happy to use another baseline should that be more adequate for you.

Adrian, are you ok with this?

- Arnaldo

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

* Re: [PATCH V8 22/23] perf tools: making coresight PMU listable
  2016-01-25 20:49     ` Mathieu Poirier
@ 2016-01-25 21:10       ` Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:10 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Mathieu Poirier, linux-arm-kernel, linux-kernel, Peter Zijlstra,
	Ingo Molnar

Em Mon, Jan 25, 2016 at 01:49:47PM -0700, Mathieu Poirier escreveu:
> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > Adding the required mechanic allowing 'perf list pmu' to
> > discover coresight ETM/PTM tracers.
> >
> > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> >  MAINTAINERS                    |  1 +
> >  tools/perf/arch/arm/util/Build |  2 ++
> >  tools/perf/arch/arm/util/pmu.c | 34 ++++++++++++++++++++++++++++++++++
> >  tools/perf/config/Makefile     | 17 ++++++++++++-----
> >  4 files changed, 49 insertions(+), 5 deletions(-)
> >  create mode 100644 tools/perf/arch/arm/util/pmu.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 233f83464814..b2a92245eece 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -1007,6 +1007,7 @@ F:        drivers/hwtracing/coresight/*
> >  F:     Documentation/trace/coresight.txt
> >  F:     Documentation/devicetree/bindings/arm/coresight.txt
> >  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
> > +F:     tools/perf/arch/arm/util/pmu.c
> >
> >  ARM/CORGI MACHINE SUPPORT
> >  M:     Richard Purdie <rpurdie@rpsys.net>
> > diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> > index d22e3d07de3d..66ab0b05549c 100644
> > --- a/tools/perf/arch/arm/util/Build
> > +++ b/tools/perf/arch/arm/util/Build
> > @@ -2,3 +2,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
> >
> >  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
> >  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> > +
> > +libperf-$(CONFIG_AUXTRACE) += pmu.o
> > diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c
> > new file mode 100644
> > index 000000000000..af9fb666b44f
> > --- /dev/null
> > +++ b/tools/perf/arch/arm/util/pmu.c
> > @@ -0,0 +1,34 @@
> > +/*
> > + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> > + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <string.h>
> > +#include <linux/coresight-pmu.h>
> > +#include <linux/perf_event.h>
> > +
> > +#include "../../util/pmu.h"
> > +
> > +struct perf_event_attr
> > +*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
> > +{
> > +#ifdef HAVE_AUXTRACE_SUPPORT
> > +       if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
> > +               /* add ETM default config here */
> > +               pmu->selectable = true;
> > +       }
> > +#endif
> > +       return NULL;
> > +}
> > diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
> > index de89ec574361..905faabff8b1 100644
> > --- a/tools/perf/config/Makefile
> > +++ b/tools/perf/config/Makefile
> > @@ -647,12 +647,19 @@ ifdef LIBBABELTRACE
> >  endif
> >
> >  ifndef NO_AUXTRACE
> > -  ifeq ($(feature-get_cpuid), 0)
> > -    msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
> > -    NO_AUXTRACE := 1
> > +  ifeq ($(ARCH),x86)
> > +    ifeq ($(feature-get_cpuid), 0)
> > +      msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
> > +      NO_AUXTRACE := 1
> > +    else
> > +      $(call detected,CONFIG_AUXTRACE)
> > +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> > +    endif
> >    else
> > -    $(call detected,CONFIG_AUXTRACE)
> > -    CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> > +    ifeq ($(ARCH),$(filter $(ARCH), arm arm64))
> > +      $(call detected,CONFIG_AUXTRACE)
> > +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> > +    endif
> >    endif
> >  endif
> >
> > --
> > 2.1.4
> >
> 
> Arnaldo,
> 
> This is the third (of four) patch that I can't queue without a
> reviewed by from you.
> 
> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> be happy to use another baseline should that be more adequate for you.

Adrian,

	Are you ok with this?

- Arnaldo

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

* [PATCH V8 22/23] perf tools: making coresight PMU listable
@ 2016-01-25 21:10       ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:10 UTC (permalink / raw)
  To: linux-arm-kernel

Em Mon, Jan 25, 2016 at 01:49:47PM -0700, Mathieu Poirier escreveu:
> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > Adding the required mechanic allowing 'perf list pmu' to
> > discover coresight ETM/PTM tracers.
> >
> > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> >  MAINTAINERS                    |  1 +
> >  tools/perf/arch/arm/util/Build |  2 ++
> >  tools/perf/arch/arm/util/pmu.c | 34 ++++++++++++++++++++++++++++++++++
> >  tools/perf/config/Makefile     | 17 ++++++++++++-----
> >  4 files changed, 49 insertions(+), 5 deletions(-)
> >  create mode 100644 tools/perf/arch/arm/util/pmu.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 233f83464814..b2a92245eece 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -1007,6 +1007,7 @@ F:        drivers/hwtracing/coresight/*
> >  F:     Documentation/trace/coresight.txt
> >  F:     Documentation/devicetree/bindings/arm/coresight.txt
> >  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
> > +F:     tools/perf/arch/arm/util/pmu.c
> >
> >  ARM/CORGI MACHINE SUPPORT
> >  M:     Richard Purdie <rpurdie@rpsys.net>
> > diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> > index d22e3d07de3d..66ab0b05549c 100644
> > --- a/tools/perf/arch/arm/util/Build
> > +++ b/tools/perf/arch/arm/util/Build
> > @@ -2,3 +2,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
> >
> >  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
> >  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> > +
> > +libperf-$(CONFIG_AUXTRACE) += pmu.o
> > diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c
> > new file mode 100644
> > index 000000000000..af9fb666b44f
> > --- /dev/null
> > +++ b/tools/perf/arch/arm/util/pmu.c
> > @@ -0,0 +1,34 @@
> > +/*
> > + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> > + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <string.h>
> > +#include <linux/coresight-pmu.h>
> > +#include <linux/perf_event.h>
> > +
> > +#include "../../util/pmu.h"
> > +
> > +struct perf_event_attr
> > +*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
> > +{
> > +#ifdef HAVE_AUXTRACE_SUPPORT
> > +       if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
> > +               /* add ETM default config here */
> > +               pmu->selectable = true;
> > +       }
> > +#endif
> > +       return NULL;
> > +}
> > diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
> > index de89ec574361..905faabff8b1 100644
> > --- a/tools/perf/config/Makefile
> > +++ b/tools/perf/config/Makefile
> > @@ -647,12 +647,19 @@ ifdef LIBBABELTRACE
> >  endif
> >
> >  ifndef NO_AUXTRACE
> > -  ifeq ($(feature-get_cpuid), 0)
> > -    msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
> > -    NO_AUXTRACE := 1
> > +  ifeq ($(ARCH),x86)
> > +    ifeq ($(feature-get_cpuid), 0)
> > +      msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
> > +      NO_AUXTRACE := 1
> > +    else
> > +      $(call detected,CONFIG_AUXTRACE)
> > +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> > +    endif
> >    else
> > -    $(call detected,CONFIG_AUXTRACE)
> > -    CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> > +    ifeq ($(ARCH),$(filter $(ARCH), arm arm64))
> > +      $(call detected,CONFIG_AUXTRACE)
> > +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
> > +    endif
> >    endif
> >  endif
> >
> > --
> > 2.1.4
> >
> 
> Arnaldo,
> 
> This is the third (of four) patch that I can't queue without a
> reviewed by from you.
> 
> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> be happy to use another baseline should that be more adequate for you.

Adrian,

	Are you ok with this?

- Arnaldo

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

* Re: [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-01-25 20:51     ` Mathieu Poirier
@ 2016-01-25 21:10       ` Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:10 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Mathieu Poirier, linux-kernel, linux-arm-kernel, Peter Zijlstra,
	Ingo Molnar

Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > Coresight ETMs are IP blocks used to perform HW assisted tracing
> > on a CPU core.  This patch introduce the required auxiliary API
> > functions allowing the perf core to interact with a tracer.
> >
> > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> >  MAINTAINERS                         |   3 +
> >  tools/perf/arch/arm/util/Build      |   2 +-
> >  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
> >  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
> >  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
> >  tools/perf/util/auxtrace.c          |   1 +
> >  tools/perf/util/auxtrace.h          |   1 +
> >  7 files changed, 570 insertions(+), 1 deletion(-)
> >  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
> >  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
> >  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index b2a92245eece..a81b2737ebc3 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
> >  F:     Documentation/devicetree/bindings/arm/coresight.txt
> >  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
> >  F:     tools/perf/arch/arm/util/pmu.c
> > +F:     tools/perf/arch/arm/util/auxtrace.c
> > +F:     tools/perf/arch/arm/util/cs_etm.c
> > +F:     tools/perf/arch/arm/util/cs_etm.h
> >
> >  ARM/CORGI MACHINE SUPPORT
> >  M:     Richard Purdie <rpurdie@rpsys.net>
> > diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> > index 66ab0b05549c..0a25a1248f42 100644
> > --- a/tools/perf/arch/arm/util/Build
> > +++ b/tools/perf/arch/arm/util/Build
> > @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
> >  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
> >  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> >
> > -libperf-$(CONFIG_AUXTRACE) += pmu.o
> > +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
> > diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
> > new file mode 100644
> > index 000000000000..d327316f0e8a
> > --- /dev/null
> > +++ b/tools/perf/arch/arm/util/auxtrace.c
> > @@ -0,0 +1,54 @@
> > +/*
> > + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> > + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <stdbool.h>
> > +#include <linux/coresight-pmu.h>
> > +
> > +#include "../../util/auxtrace.h"
> > +#include "../../util/evlist.h"
> > +#include "../../util/pmu.h"
> > +#include "cs_etm.h"
> > +
> > +struct auxtrace_record
> > +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
> > +{
> > +       struct perf_pmu *cs_etm_pmu;
> > +       struct perf_evsel *evsel;
> > +       bool found_etm = false;
> > +
> > +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> > +
> > +        if (evlist) {
> > +               evlist__for_each(evlist, evsel) {
> > +                       if (cs_etm_pmu &&
> > +                           evsel->attr.type == cs_etm_pmu->type)
> > +                               found_etm = true;
> > +               }
> > +       }
> > +
> > +       if (found_etm)
> > +               return cs_etm_record_init(err);
> > +
> > +       /*
> > +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
> > +        * record can still be used even if tracers aren't present.  The NULL
> > +        * return value will take care of telling the infrastructure HW tracing
> > +        * isn't available.
> > +        */
> > +       *err = 0;
> > +       return NULL;
> > +}
> > diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
> > new file mode 100644
> > index 000000000000..5710b90e23d5
> > --- /dev/null
> > +++ b/tools/perf/arch/arm/util/cs_etm.c
> > @@ -0,0 +1,466 @@
> > +/*
> > + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> > + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <api/fs/fs.h>
> > +#include <linux/bitops.h>
> > +#include <linux/coresight-pmu.h>
> > +#include <linux/kernel.h>
> > +#include <linux/log2.h>
> > +#include <linux/types.h>
> > +
> > +#include "../../perf.h"
> > +#include "../../util/auxtrace.h"
> > +#include "../../util/cpumap.h"
> > +#include "../../util/evlist.h"
> > +#include "../../util/pmu.h"
> > +#include "../../util/thread_map.h"
> > +#include "cs_etm.h"
> > +
> > +#include <stdlib.h>
> > +
> > +#define KiB(x) ((x) * 1024)
> > +#define MiB(x) ((x) * 1024 * 1024)
> > +
> > +struct cs_etm_recording {
> > +       struct auxtrace_record  itr;
> > +       struct perf_pmu         *cs_etm_pmu;
> > +       struct perf_evlist      *evlist;
> > +       bool                    snapshot_mode;
> > +       size_t                  snapshot_size;
> > +};
> > +
> > +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
> > +                                        struct record_opts *opts,
> > +                                        const char *str)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                               container_of(itr, struct cs_etm_recording, itr);
> > +       unsigned long long snapshot_size = 0;
> > +       char *endptr;
> > +
> > +       if (str) {
> > +               snapshot_size = strtoull(str, &endptr, 0);
> > +               if (*endptr || snapshot_size > SIZE_MAX)
> > +                       return -1;
> > +       }
> > +
> > +       opts->auxtrace_snapshot_mode = true;
> > +       opts->auxtrace_snapshot_size = snapshot_size;
> > +       ptr->snapshot_size = snapshot_size;
> > +
> > +       return 0;
> > +}
> > +
> > +static int cs_etm_recording_options(struct auxtrace_record *itr,
> > +                                   struct perf_evlist *evlist,
> > +                                   struct record_opts *opts)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                               container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> > +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
> > +       const struct cpu_map *cpus = evlist->cpus;
> > +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
> > +
> > +       ptr->evlist = evlist;
> > +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
> > +
> > +       evlist__for_each(evlist, evsel) {
> > +               if (evsel->attr.type == cs_etm_pmu->type) {
> > +                       if (cs_etm_evsel) {
> > +                               pr_err("There may be only one %s event\n",
> > +                                      CORESIGHT_ETM_PMU_NAME);
> > +                               return -EINVAL;
> > +                       }
> > +                       evsel->attr.freq = 0;
> > +                       evsel->attr.sample_period = 1;
> > +                       cs_etm_evsel = evsel;
> > +                       opts->full_auxtrace = true;
> > +               }
> > +       }
> > +
> > +       /* no need to continue if at least one event of interest was found */
> > +       if (!cs_etm_evsel)
> > +               return 0;
> > +
> > +       if (opts->use_clockid) {
> > +               pr_err("Cannot use clockid (-k option) with %s\n",
> > +                      CORESIGHT_ETM_PMU_NAME);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* we are in snapshot mode */
> > +       if (opts->auxtrace_snapshot_mode) {
> > +               /*
> > +                * No size were given to '-S' or '-m,', so go with
> > +                * the default
> > +                */
> > +               if (!opts->auxtrace_snapshot_size &&
> > +                   !opts->auxtrace_mmap_pages) {
> > +                       if (privileged) {
> > +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
> > +                       } else {
> > +                               opts->auxtrace_mmap_pages =
> > +                                                       KiB(128) / page_size;
> > +                               if (opts->mmap_pages == UINT_MAX)
> > +                                       opts->mmap_pages = KiB(256) / page_size;
> > +                       }
> > +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
> > +                                               opts->mmap_pages == UINT_MAX) {
> > +                       opts->mmap_pages = KiB(256) / page_size;
> > +               }
> > +
> > +               /*
> > +                * '-m,xyz' was specified but no snapshot size, so make the
> > +                * snapshot size as big as the auxtrace mmap area.
> > +                */
> > +               if (!opts->auxtrace_snapshot_size) {
> > +                       opts->auxtrace_snapshot_size =
> > +                               opts->auxtrace_mmap_pages * (size_t)page_size;
> > +               }
> > +
> > +               /*
> > +                * -Sxyz was specified but no auxtrace mmap area, so make the
> > +                * auxtrace mmap area big enough to fit the requested snapshot
> > +                * size.
> > +                */
> > +               if (!opts->auxtrace_mmap_pages) {
> > +                       size_t sz = opts->auxtrace_snapshot_size;
> > +
> > +                       sz = round_up(sz, page_size) / page_size;
> > +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
> > +               }
> > +
> > +               /* Snapshost size can't be bigger than the auxtrace area */
> > +               if (opts->auxtrace_snapshot_size >
> > +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
> > +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
> > +                              opts->auxtrace_snapshot_size,
> > +                              opts->auxtrace_mmap_pages * (size_t)page_size);
> > +                       return -EINVAL;
> > +               }
> > +
> > +               /* Something went wrong somewhere - this shouldn't happen */
> > +               if (!opts->auxtrace_snapshot_size ||
> > +                   !opts->auxtrace_mmap_pages) {
> > +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
> > +                       return -EINVAL;
> > +               }
> > +       }
> > +
> > +       /* We are in full trace mode but '-m,xyz' wasn't specified */
> > +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
> > +               if (privileged) {
> > +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
> > +               } else {
> > +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
> > +                       if (opts->mmap_pages == UINT_MAX)
> > +                               opts->mmap_pages = KiB(256) / page_size;
> > +               }
> > +
> > +       }
> > +
> > +       /* Validate auxtrace_mmap_pages provided by user */
> > +       if (opts->auxtrace_mmap_pages) {
> > +               unsigned int max_page = (KiB(128) / page_size);
> > +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
> > +
> > +               if (!privileged &&
> > +                   opts->auxtrace_mmap_pages > max_page) {
> > +                       opts->auxtrace_mmap_pages = max_page;
> > +                       pr_err("auxtrace too big, truncating to %d\n",
> > +                              max_page);
> > +               }
> > +
> > +               if (!is_power_of_2(sz)) {
> > +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
> > +                              CORESIGHT_ETM_PMU_NAME);
> > +                       return -EINVAL;
> > +               }
> > +       }
> > +
> > +       if (opts->auxtrace_snapshot_mode)
> > +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
> > +                         opts->auxtrace_snapshot_size);
> > +
> > +       if (cs_etm_evsel) {
> > +               /*
> > +                * To obtain the auxtrace buffer file descriptor, the auxtrace
> > +                * event must come first.
> > +                */
> > +               perf_evlist__to_front(evlist, cs_etm_evsel);
> > +               /*
> > +                * In the case of per-cpu mmaps, we need the CPU on the
> > +                * AUX event.
> > +                */
> > +               if (!cpu_map__empty(cpus))
> > +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
> > +       }
> > +
> > +       /* Add dummy event to keep tracking */
> > +       if (opts->full_auxtrace) {
> > +               struct perf_evsel *tracking_evsel;
> > +               int err;
> > +
> > +               err = parse_events(evlist, "dummy:u", NULL);
> > +               if (err)
> > +                       return err;
> > +
> > +               tracking_evsel = perf_evlist__last(evlist);
> > +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
> > +
> > +               tracking_evsel->attr.freq = 0;
> > +               tracking_evsel->attr.sample_period = 1;
> > +
> > +               /* In per-cpu case, always need the time of mmap events etc */
> > +               if (!cpu_map__empty(cpus))
> > +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static u64 cs_etm_get_config(struct auxtrace_record *itr)
> > +{
> > +       u64 config = 0;
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> > +       struct perf_evlist *evlist = ptr->evlist;
> > +       struct perf_evsel *evsel;
> > +
> > +       evlist__for_each(evlist, evsel) {
> > +               if (evsel->attr.type == cs_etm_pmu->type) {
> > +                       /*
> > +                        * Variable perf_event_attr::config is assigned to
> > +                        * ETMv3/PTM.  The bit fields have been made to match
> > +                        * the ETMv3.5 ETRMCR register specification.  See the
> > +                        * PMU_FORMAT_ATTR() declarations in
> > +                        * drivers/hwtracing/coresight/coresight-perf.c for
> > +                        * details.
> > +                        */
> > +                       config = evsel->attr.config;
> > +                       break;
> > +               }
> > +       }
> > +
> > +       return config;
> > +}
> > +
> > +static size_t
> > +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> > +                     struct perf_evlist *evlist __maybe_unused)
> > +{
> > +       int records;
> > +       const struct cpu_map *cpus = evlist->cpus;
> > +
> > +       if (!cpu_map__empty(cpus)) {
> > +               records = cpu_map__nr(cpus);
> > +               goto out;
> > +       }
> > +
> > +       set_max_cpu_num();
> > +       records = cpu__max_cpu();
> > +out:
> > +       return records * CS_ETM_PRIV_SIZE;
> > +}
> > +
> > +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
> > +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
> > +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
> > +};
> > +
> > +static int cs_etm_get_metadata(int cpu, int index,
> > +                              struct auxtrace_record *itr,
> > +                              struct auxtrace_info_event *info)
> > +{
> > +       char path[PATH_MAX];
> > +       int offset = 0, ret = 0;
> > +       int i, scan;
> > +       unsigned int val;
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> > +
> > +       offset = index * CS_ETM_PRIV_MAX;
> > +
> > +       /* Build generic header portion */
> > +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
> > +       info->priv[offset + CS_ETM_CPU] = cpu;
> > +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
> > +
> > +       /* Get user configurables from the session */
> > +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
> > +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
> > +
> > +       /* Get RO metadata from sysfs */
> > +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
> > +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
> > +
> > +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
> > +               if (scan != 1) {
> > +                       ret = -EINVAL;
> > +                       break;
> > +               }
> > +
> > +               info->priv[offset + i] = val;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static int cs_etm_info_fill(struct auxtrace_record *itr,
> > +                           struct perf_session *session,
> > +                           struct auxtrace_info_event *auxtrace_info,
> > +                           size_t priv_size)
> > +{
> > +       int i, nr_cpu, ret = 0;
> > +       const struct cpu_map *cpus = session->evlist->cpus;
> > +
> > +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
> > +               return -EINVAL;
> > +
> > +       if (!session->evlist->nr_mmaps)
> > +               return -EINVAL;
> > +
> > +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
> > +
> > +       /* cpu map is not empty, we have specific CPUs to work with */
> > +       if (!cpu_map__empty(cpus)) {
> > +               for (i = 0; i < cpu_map__nr(cpus); i++) {
> > +                       ret = cs_etm_get_metadata(cpus->map[i], i,
> > +                                                 itr, auxtrace_info);
> > +                       if (ret)
> > +                               goto out;
> > +               }
> > +       } else {
> > +               /* get configuration for all CPUs in the system */
> > +               nr_cpu = cpu__max_cpu();
> > +               for (i = 0; i < nr_cpu; i++) {
> > +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
> > +                       if (ret)
> > +                               goto out;
> > +               }
> > +       }
> > +
> > +out:
> > +       return ret;
> > +}
> > +
> > +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
> > +                               int idx, struct auxtrace_mmap *mm,
> > +                               unsigned char *data __maybe_unused,
> > +                               u64 *head, u64 *old)
> > +{
> > +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
> > +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
> > +
> > +       *old = *head;
> > +       *head += mm->len;
> > +
> > +       return 0;
> > +}
> > +
> > +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_evsel *evsel;
> > +
> > +       evlist__for_each(ptr->evlist, evsel) {
> > +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> > +                       return perf_evlist__disable_event(ptr->evlist, evsel);
> > +       }
> > +       return -EINVAL;
> > +}
> > +
> > +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_evsel *evsel;
> > +
> > +       evlist__for_each(ptr->evlist, evsel) {
> > +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> > +                       return perf_evlist__enable_event(ptr->evlist, evsel);
> > +       }
> > +       return -EINVAL;
> > +}
> > +
> > +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
> > +{
> > +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
> > +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
> > +}
> > +
> > +static void cs_etm_recording_free(struct auxtrace_record *itr)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       free(ptr);
> > +}
> > +
> > +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_evsel *evsel;
> > +
> > +       evlist__for_each(ptr->evlist, evsel) {
> > +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> > +                       return perf_evlist__enable_event_idx(ptr->evlist,
> > +                                                            evsel, idx);
> > +       }
> > +
> > +       return -EINVAL;
> > +}
> > +
> > +struct auxtrace_record *cs_etm_record_init(int *err)
> > +{
> > +       struct perf_pmu *cs_etm_pmu;
> > +       struct cs_etm_recording *ptr;
> > +
> > +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> > +
> > +       if (!cs_etm_pmu) {
> > +               *err = -EINVAL;
> > +               goto out;
> > +       }
> > +
> > +       ptr = zalloc(sizeof(struct cs_etm_recording));
> > +       if (!ptr) {
> > +               *err = -ENOMEM;
> > +               goto out;
> > +       }
> > +
> > +       ptr->cs_etm_pmu                 = cs_etm_pmu;
> > +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
> > +       ptr->itr.recording_options      = cs_etm_recording_options;
> > +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
> > +       ptr->itr.info_fill              = cs_etm_info_fill;
> > +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
> > +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
> > +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
> > +       ptr->itr.reference              = cs_etm_reference;
> > +       ptr->itr.free                   = cs_etm_recording_free;
> > +       ptr->itr.read_finish            = cs_etm_read_finish;
> > +
> > +       *err = 0;
> > +       return &ptr->itr;
> > +out:
> > +       return NULL;
> > +}
> > diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
> > new file mode 100644
> > index 000000000000..7e85c1b43598
> > --- /dev/null
> > +++ b/tools/perf/arch/arm/util/cs_etm.h
> > @@ -0,0 +1,44 @@
> > +/*
> > + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> > + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#ifndef INCLUDE__PERF_CS_ETM_H__
> > +#define INCLUDE__PERF_CS_ETM_H__
> > +
> > +/* Beginning of header common to both ETMv3 and V4 */
> > +enum {
> > +       CS_ETM_MAGIC,
> > +       CS_ETM_CPU,
> > +       CS_ETM_SNAPSHOT,
> > +};
> > +
> > +/* ETMv3/PTM metadata */
> > +enum {
> > +       /* Dynamic, configurable parameters */
> > +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
> > +       CS_ETM_ETMTRACEIDR,
> > +       /* RO, taken from sysFS */
> > +       CS_ETM_ETMCCER,
> > +       CS_ETM_ETMIDR,
> > +       CS_ETM_PRIV_MAX,
> > +};
> > +
> > +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
> > +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
> > +
> > +struct auxtrace_record *cs_etm_record_init(int *err);
> > +
> > +#endif
> > diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> > index cc1c9ce5cc56..a6f291dbc4d9 100644
> > --- a/tools/perf/util/auxtrace.c
> > +++ b/tools/perf/util/auxtrace.c
> > @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
> >                 return intel_pt_process_auxtrace_info(event, session);
> >         case PERF_AUXTRACE_INTEL_BTS:
> >                 return intel_bts_process_auxtrace_info(event, session);
> > +       case PERF_AUXTRACE_CS_ETM:
> >         case PERF_AUXTRACE_UNKNOWN:
> >         default:
> >                 return -EINVAL;
> > diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> > index e5a8e2d4f2af..adb53e7bcabf 100644
> > --- a/tools/perf/util/auxtrace.h
> > +++ b/tools/perf/util/auxtrace.h
> > @@ -41,6 +41,7 @@ enum auxtrace_type {
> >         PERF_AUXTRACE_UNKNOWN,
> >         PERF_AUXTRACE_INTEL_PT,
> >         PERF_AUXTRACE_INTEL_BTS,
> > +       PERF_AUXTRACE_CS_ETM,
> >  };
> >
> >  enum itrace_period_type {
> > --
> > 2.1.4
> >
> 
> Arnaldo,
> 
> Last but not least, this is the final patch that I would like you to
> review before queing.
> 
> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> be happy to use another baseline should that be more adequate for you.

Adrian,

	One more, are you ok with this?

- Arnaldo

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-01-25 21:10       ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:10 UTC (permalink / raw)
  To: linux-arm-kernel

Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > Coresight ETMs are IP blocks used to perform HW assisted tracing
> > on a CPU core.  This patch introduce the required auxiliary API
> > functions allowing the perf core to interact with a tracer.
> >
> > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> >  MAINTAINERS                         |   3 +
> >  tools/perf/arch/arm/util/Build      |   2 +-
> >  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
> >  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
> >  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
> >  tools/perf/util/auxtrace.c          |   1 +
> >  tools/perf/util/auxtrace.h          |   1 +
> >  7 files changed, 570 insertions(+), 1 deletion(-)
> >  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
> >  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
> >  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index b2a92245eece..a81b2737ebc3 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
> >  F:     Documentation/devicetree/bindings/arm/coresight.txt
> >  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
> >  F:     tools/perf/arch/arm/util/pmu.c
> > +F:     tools/perf/arch/arm/util/auxtrace.c
> > +F:     tools/perf/arch/arm/util/cs_etm.c
> > +F:     tools/perf/arch/arm/util/cs_etm.h
> >
> >  ARM/CORGI MACHINE SUPPORT
> >  M:     Richard Purdie <rpurdie@rpsys.net>
> > diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> > index 66ab0b05549c..0a25a1248f42 100644
> > --- a/tools/perf/arch/arm/util/Build
> > +++ b/tools/perf/arch/arm/util/Build
> > @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
> >  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
> >  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> >
> > -libperf-$(CONFIG_AUXTRACE) += pmu.o
> > +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
> > diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
> > new file mode 100644
> > index 000000000000..d327316f0e8a
> > --- /dev/null
> > +++ b/tools/perf/arch/arm/util/auxtrace.c
> > @@ -0,0 +1,54 @@
> > +/*
> > + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> > + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <stdbool.h>
> > +#include <linux/coresight-pmu.h>
> > +
> > +#include "../../util/auxtrace.h"
> > +#include "../../util/evlist.h"
> > +#include "../../util/pmu.h"
> > +#include "cs_etm.h"
> > +
> > +struct auxtrace_record
> > +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
> > +{
> > +       struct perf_pmu *cs_etm_pmu;
> > +       struct perf_evsel *evsel;
> > +       bool found_etm = false;
> > +
> > +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> > +
> > +        if (evlist) {
> > +               evlist__for_each(evlist, evsel) {
> > +                       if (cs_etm_pmu &&
> > +                           evsel->attr.type == cs_etm_pmu->type)
> > +                               found_etm = true;
> > +               }
> > +       }
> > +
> > +       if (found_etm)
> > +               return cs_etm_record_init(err);
> > +
> > +       /*
> > +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
> > +        * record can still be used even if tracers aren't present.  The NULL
> > +        * return value will take care of telling the infrastructure HW tracing
> > +        * isn't available.
> > +        */
> > +       *err = 0;
> > +       return NULL;
> > +}
> > diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
> > new file mode 100644
> > index 000000000000..5710b90e23d5
> > --- /dev/null
> > +++ b/tools/perf/arch/arm/util/cs_etm.c
> > @@ -0,0 +1,466 @@
> > +/*
> > + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> > + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <api/fs/fs.h>
> > +#include <linux/bitops.h>
> > +#include <linux/coresight-pmu.h>
> > +#include <linux/kernel.h>
> > +#include <linux/log2.h>
> > +#include <linux/types.h>
> > +
> > +#include "../../perf.h"
> > +#include "../../util/auxtrace.h"
> > +#include "../../util/cpumap.h"
> > +#include "../../util/evlist.h"
> > +#include "../../util/pmu.h"
> > +#include "../../util/thread_map.h"
> > +#include "cs_etm.h"
> > +
> > +#include <stdlib.h>
> > +
> > +#define KiB(x) ((x) * 1024)
> > +#define MiB(x) ((x) * 1024 * 1024)
> > +
> > +struct cs_etm_recording {
> > +       struct auxtrace_record  itr;
> > +       struct perf_pmu         *cs_etm_pmu;
> > +       struct perf_evlist      *evlist;
> > +       bool                    snapshot_mode;
> > +       size_t                  snapshot_size;
> > +};
> > +
> > +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
> > +                                        struct record_opts *opts,
> > +                                        const char *str)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                               container_of(itr, struct cs_etm_recording, itr);
> > +       unsigned long long snapshot_size = 0;
> > +       char *endptr;
> > +
> > +       if (str) {
> > +               snapshot_size = strtoull(str, &endptr, 0);
> > +               if (*endptr || snapshot_size > SIZE_MAX)
> > +                       return -1;
> > +       }
> > +
> > +       opts->auxtrace_snapshot_mode = true;
> > +       opts->auxtrace_snapshot_size = snapshot_size;
> > +       ptr->snapshot_size = snapshot_size;
> > +
> > +       return 0;
> > +}
> > +
> > +static int cs_etm_recording_options(struct auxtrace_record *itr,
> > +                                   struct perf_evlist *evlist,
> > +                                   struct record_opts *opts)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                               container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> > +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
> > +       const struct cpu_map *cpus = evlist->cpus;
> > +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
> > +
> > +       ptr->evlist = evlist;
> > +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
> > +
> > +       evlist__for_each(evlist, evsel) {
> > +               if (evsel->attr.type == cs_etm_pmu->type) {
> > +                       if (cs_etm_evsel) {
> > +                               pr_err("There may be only one %s event\n",
> > +                                      CORESIGHT_ETM_PMU_NAME);
> > +                               return -EINVAL;
> > +                       }
> > +                       evsel->attr.freq = 0;
> > +                       evsel->attr.sample_period = 1;
> > +                       cs_etm_evsel = evsel;
> > +                       opts->full_auxtrace = true;
> > +               }
> > +       }
> > +
> > +       /* no need to continue if at least one event of interest was found */
> > +       if (!cs_etm_evsel)
> > +               return 0;
> > +
> > +       if (opts->use_clockid) {
> > +               pr_err("Cannot use clockid (-k option) with %s\n",
> > +                      CORESIGHT_ETM_PMU_NAME);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* we are in snapshot mode */
> > +       if (opts->auxtrace_snapshot_mode) {
> > +               /*
> > +                * No size were given to '-S' or '-m,', so go with
> > +                * the default
> > +                */
> > +               if (!opts->auxtrace_snapshot_size &&
> > +                   !opts->auxtrace_mmap_pages) {
> > +                       if (privileged) {
> > +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
> > +                       } else {
> > +                               opts->auxtrace_mmap_pages =
> > +                                                       KiB(128) / page_size;
> > +                               if (opts->mmap_pages == UINT_MAX)
> > +                                       opts->mmap_pages = KiB(256) / page_size;
> > +                       }
> > +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
> > +                                               opts->mmap_pages == UINT_MAX) {
> > +                       opts->mmap_pages = KiB(256) / page_size;
> > +               }
> > +
> > +               /*
> > +                * '-m,xyz' was specified but no snapshot size, so make the
> > +                * snapshot size as big as the auxtrace mmap area.
> > +                */
> > +               if (!opts->auxtrace_snapshot_size) {
> > +                       opts->auxtrace_snapshot_size =
> > +                               opts->auxtrace_mmap_pages * (size_t)page_size;
> > +               }
> > +
> > +               /*
> > +                * -Sxyz was specified but no auxtrace mmap area, so make the
> > +                * auxtrace mmap area big enough to fit the requested snapshot
> > +                * size.
> > +                */
> > +               if (!opts->auxtrace_mmap_pages) {
> > +                       size_t sz = opts->auxtrace_snapshot_size;
> > +
> > +                       sz = round_up(sz, page_size) / page_size;
> > +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
> > +               }
> > +
> > +               /* Snapshost size can't be bigger than the auxtrace area */
> > +               if (opts->auxtrace_snapshot_size >
> > +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
> > +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
> > +                              opts->auxtrace_snapshot_size,
> > +                              opts->auxtrace_mmap_pages * (size_t)page_size);
> > +                       return -EINVAL;
> > +               }
> > +
> > +               /* Something went wrong somewhere - this shouldn't happen */
> > +               if (!opts->auxtrace_snapshot_size ||
> > +                   !opts->auxtrace_mmap_pages) {
> > +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
> > +                       return -EINVAL;
> > +               }
> > +       }
> > +
> > +       /* We are in full trace mode but '-m,xyz' wasn't specified */
> > +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
> > +               if (privileged) {
> > +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
> > +               } else {
> > +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
> > +                       if (opts->mmap_pages == UINT_MAX)
> > +                               opts->mmap_pages = KiB(256) / page_size;
> > +               }
> > +
> > +       }
> > +
> > +       /* Validate auxtrace_mmap_pages provided by user */
> > +       if (opts->auxtrace_mmap_pages) {
> > +               unsigned int max_page = (KiB(128) / page_size);
> > +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
> > +
> > +               if (!privileged &&
> > +                   opts->auxtrace_mmap_pages > max_page) {
> > +                       opts->auxtrace_mmap_pages = max_page;
> > +                       pr_err("auxtrace too big, truncating to %d\n",
> > +                              max_page);
> > +               }
> > +
> > +               if (!is_power_of_2(sz)) {
> > +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
> > +                              CORESIGHT_ETM_PMU_NAME);
> > +                       return -EINVAL;
> > +               }
> > +       }
> > +
> > +       if (opts->auxtrace_snapshot_mode)
> > +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
> > +                         opts->auxtrace_snapshot_size);
> > +
> > +       if (cs_etm_evsel) {
> > +               /*
> > +                * To obtain the auxtrace buffer file descriptor, the auxtrace
> > +                * event must come first.
> > +                */
> > +               perf_evlist__to_front(evlist, cs_etm_evsel);
> > +               /*
> > +                * In the case of per-cpu mmaps, we need the CPU on the
> > +                * AUX event.
> > +                */
> > +               if (!cpu_map__empty(cpus))
> > +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
> > +       }
> > +
> > +       /* Add dummy event to keep tracking */
> > +       if (opts->full_auxtrace) {
> > +               struct perf_evsel *tracking_evsel;
> > +               int err;
> > +
> > +               err = parse_events(evlist, "dummy:u", NULL);
> > +               if (err)
> > +                       return err;
> > +
> > +               tracking_evsel = perf_evlist__last(evlist);
> > +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
> > +
> > +               tracking_evsel->attr.freq = 0;
> > +               tracking_evsel->attr.sample_period = 1;
> > +
> > +               /* In per-cpu case, always need the time of mmap events etc */
> > +               if (!cpu_map__empty(cpus))
> > +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static u64 cs_etm_get_config(struct auxtrace_record *itr)
> > +{
> > +       u64 config = 0;
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> > +       struct perf_evlist *evlist = ptr->evlist;
> > +       struct perf_evsel *evsel;
> > +
> > +       evlist__for_each(evlist, evsel) {
> > +               if (evsel->attr.type == cs_etm_pmu->type) {
> > +                       /*
> > +                        * Variable perf_event_attr::config is assigned to
> > +                        * ETMv3/PTM.  The bit fields have been made to match
> > +                        * the ETMv3.5 ETRMCR register specification.  See the
> > +                        * PMU_FORMAT_ATTR() declarations in
> > +                        * drivers/hwtracing/coresight/coresight-perf.c for
> > +                        * details.
> > +                        */
> > +                       config = evsel->attr.config;
> > +                       break;
> > +               }
> > +       }
> > +
> > +       return config;
> > +}
> > +
> > +static size_t
> > +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> > +                     struct perf_evlist *evlist __maybe_unused)
> > +{
> > +       int records;
> > +       const struct cpu_map *cpus = evlist->cpus;
> > +
> > +       if (!cpu_map__empty(cpus)) {
> > +               records = cpu_map__nr(cpus);
> > +               goto out;
> > +       }
> > +
> > +       set_max_cpu_num();
> > +       records = cpu__max_cpu();
> > +out:
> > +       return records * CS_ETM_PRIV_SIZE;
> > +}
> > +
> > +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
> > +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
> > +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
> > +};
> > +
> > +static int cs_etm_get_metadata(int cpu, int index,
> > +                              struct auxtrace_record *itr,
> > +                              struct auxtrace_info_event *info)
> > +{
> > +       char path[PATH_MAX];
> > +       int offset = 0, ret = 0;
> > +       int i, scan;
> > +       unsigned int val;
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> > +
> > +       offset = index * CS_ETM_PRIV_MAX;
> > +
> > +       /* Build generic header portion */
> > +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
> > +       info->priv[offset + CS_ETM_CPU] = cpu;
> > +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
> > +
> > +       /* Get user configurables from the session */
> > +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
> > +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
> > +
> > +       /* Get RO metadata from sysfs */
> > +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
> > +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
> > +
> > +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
> > +               if (scan != 1) {
> > +                       ret = -EINVAL;
> > +                       break;
> > +               }
> > +
> > +               info->priv[offset + i] = val;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static int cs_etm_info_fill(struct auxtrace_record *itr,
> > +                           struct perf_session *session,
> > +                           struct auxtrace_info_event *auxtrace_info,
> > +                           size_t priv_size)
> > +{
> > +       int i, nr_cpu, ret = 0;
> > +       const struct cpu_map *cpus = session->evlist->cpus;
> > +
> > +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
> > +               return -EINVAL;
> > +
> > +       if (!session->evlist->nr_mmaps)
> > +               return -EINVAL;
> > +
> > +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
> > +
> > +       /* cpu map is not empty, we have specific CPUs to work with */
> > +       if (!cpu_map__empty(cpus)) {
> > +               for (i = 0; i < cpu_map__nr(cpus); i++) {
> > +                       ret = cs_etm_get_metadata(cpus->map[i], i,
> > +                                                 itr, auxtrace_info);
> > +                       if (ret)
> > +                               goto out;
> > +               }
> > +       } else {
> > +               /* get configuration for all CPUs in the system */
> > +               nr_cpu = cpu__max_cpu();
> > +               for (i = 0; i < nr_cpu; i++) {
> > +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
> > +                       if (ret)
> > +                               goto out;
> > +               }
> > +       }
> > +
> > +out:
> > +       return ret;
> > +}
> > +
> > +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
> > +                               int idx, struct auxtrace_mmap *mm,
> > +                               unsigned char *data __maybe_unused,
> > +                               u64 *head, u64 *old)
> > +{
> > +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
> > +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
> > +
> > +       *old = *head;
> > +       *head += mm->len;
> > +
> > +       return 0;
> > +}
> > +
> > +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_evsel *evsel;
> > +
> > +       evlist__for_each(ptr->evlist, evsel) {
> > +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> > +                       return perf_evlist__disable_event(ptr->evlist, evsel);
> > +       }
> > +       return -EINVAL;
> > +}
> > +
> > +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_evsel *evsel;
> > +
> > +       evlist__for_each(ptr->evlist, evsel) {
> > +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> > +                       return perf_evlist__enable_event(ptr->evlist, evsel);
> > +       }
> > +       return -EINVAL;
> > +}
> > +
> > +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
> > +{
> > +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
> > +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
> > +}
> > +
> > +static void cs_etm_recording_free(struct auxtrace_record *itr)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       free(ptr);
> > +}
> > +
> > +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
> > +{
> > +       struct cs_etm_recording *ptr =
> > +                       container_of(itr, struct cs_etm_recording, itr);
> > +       struct perf_evsel *evsel;
> > +
> > +       evlist__for_each(ptr->evlist, evsel) {
> > +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> > +                       return perf_evlist__enable_event_idx(ptr->evlist,
> > +                                                            evsel, idx);
> > +       }
> > +
> > +       return -EINVAL;
> > +}
> > +
> > +struct auxtrace_record *cs_etm_record_init(int *err)
> > +{
> > +       struct perf_pmu *cs_etm_pmu;
> > +       struct cs_etm_recording *ptr;
> > +
> > +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> > +
> > +       if (!cs_etm_pmu) {
> > +               *err = -EINVAL;
> > +               goto out;
> > +       }
> > +
> > +       ptr = zalloc(sizeof(struct cs_etm_recording));
> > +       if (!ptr) {
> > +               *err = -ENOMEM;
> > +               goto out;
> > +       }
> > +
> > +       ptr->cs_etm_pmu                 = cs_etm_pmu;
> > +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
> > +       ptr->itr.recording_options      = cs_etm_recording_options;
> > +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
> > +       ptr->itr.info_fill              = cs_etm_info_fill;
> > +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
> > +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
> > +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
> > +       ptr->itr.reference              = cs_etm_reference;
> > +       ptr->itr.free                   = cs_etm_recording_free;
> > +       ptr->itr.read_finish            = cs_etm_read_finish;
> > +
> > +       *err = 0;
> > +       return &ptr->itr;
> > +out:
> > +       return NULL;
> > +}
> > diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
> > new file mode 100644
> > index 000000000000..7e85c1b43598
> > --- /dev/null
> > +++ b/tools/perf/arch/arm/util/cs_etm.h
> > @@ -0,0 +1,44 @@
> > +/*
> > + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> > + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#ifndef INCLUDE__PERF_CS_ETM_H__
> > +#define INCLUDE__PERF_CS_ETM_H__
> > +
> > +/* Beginning of header common to both ETMv3 and V4 */
> > +enum {
> > +       CS_ETM_MAGIC,
> > +       CS_ETM_CPU,
> > +       CS_ETM_SNAPSHOT,
> > +};
> > +
> > +/* ETMv3/PTM metadata */
> > +enum {
> > +       /* Dynamic, configurable parameters */
> > +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
> > +       CS_ETM_ETMTRACEIDR,
> > +       /* RO, taken from sysFS */
> > +       CS_ETM_ETMCCER,
> > +       CS_ETM_ETMIDR,
> > +       CS_ETM_PRIV_MAX,
> > +};
> > +
> > +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
> > +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
> > +
> > +struct auxtrace_record *cs_etm_record_init(int *err);
> > +
> > +#endif
> > diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> > index cc1c9ce5cc56..a6f291dbc4d9 100644
> > --- a/tools/perf/util/auxtrace.c
> > +++ b/tools/perf/util/auxtrace.c
> > @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
> >                 return intel_pt_process_auxtrace_info(event, session);
> >         case PERF_AUXTRACE_INTEL_BTS:
> >                 return intel_bts_process_auxtrace_info(event, session);
> > +       case PERF_AUXTRACE_CS_ETM:
> >         case PERF_AUXTRACE_UNKNOWN:
> >         default:
> >                 return -EINVAL;
> > diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> > index e5a8e2d4f2af..adb53e7bcabf 100644
> > --- a/tools/perf/util/auxtrace.h
> > +++ b/tools/perf/util/auxtrace.h
> > @@ -41,6 +41,7 @@ enum auxtrace_type {
> >         PERF_AUXTRACE_UNKNOWN,
> >         PERF_AUXTRACE_INTEL_PT,
> >         PERF_AUXTRACE_INTEL_BTS,
> > +       PERF_AUXTRACE_CS_ETM,
> >  };
> >
> >  enum itrace_period_type {
> > --
> > 2.1.4
> >
> 
> Arnaldo,
> 
> Last but not least, this is the final patch that I would like you to
> review before queing.
> 
> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> be happy to use another baseline should that be more adequate for you.

Adrian,

	One more, are you ok with this?

- Arnaldo

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

* Re: [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
  2016-01-25 20:46     ` Mathieu Poirier
@ 2016-01-25 21:12       ` Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:12 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: linux-doc, Ingo Molnar, linux-arm-kernel, linux-kernel, Peter Zijlstra

Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > For memory allocation purposes, code located in other places
> > then util/cpumap.c may want to know how many CPUs the system has.
> >
> > This patch is making function set_max_cpu_num() available to
> > other parts of the perf tool so that global variable
> > 'max_cpu_num' gets the right value when referenced by cpu__max_cpu().
> >
> > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> >  tools/perf/util/cpumap.c | 2 +-
> >  tools/perf/util/cpumap.h | 1 +
> >  2 files changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
> > index 10af1e7524fb..ae179320c0c0 100644
> > --- a/tools/perf/util/cpumap.c
> > +++ b/tools/perf/util/cpumap.c
> > @@ -380,7 +380,7 @@ out:
> >  }
> >
> >  /* Determine highest possible cpu in the system for sparse allocation */
> > -static void set_max_cpu_num(void)
> > +void set_max_cpu_num(void)
> >  {
> >         const char *mnt;
> >         char path[PATH_MAX];
> > diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
> > index 85f7772457fa..45fa963345eb 100644
> > --- a/tools/perf/util/cpumap.h
> > +++ b/tools/perf/util/cpumap.h
> > @@ -14,6 +14,7 @@ struct cpu_map {
> >         int map[];
> >  };
> >
> > +void set_max_cpu_num(void);
> >  struct cpu_map *cpu_map__new(const char *cpu_list);
> >  struct cpu_map *cpu_map__empty_new(int nr);
> >  struct cpu_map *cpu_map__dummy_new(void);
> > --
> > 2.1.4
> >
> 
> Arnaldo,
> 
> I can't queue this patch for 4.6 without at least a reviewed by from you.

This one I remember, looks ugly, the name set_max_cpu_num() looks
strange, when that was restricted (static) to that cpumap.c file, it
wasn't a problem, exporting it for wider usage looks bad.

You've been waiting for this for quite a while, it seems, lemme stop
what I am doing to check this...

- Arnaldo
 
> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> be happy to use another baseline should that be more adequate for you.
> The same goes for [2]
> 
> Thanks,
> Mathieu
> 
> [1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
@ 2016-01-25 21:12       ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:12 UTC (permalink / raw)
  To: linux-arm-kernel

Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > For memory allocation purposes, code located in other places
> > then util/cpumap.c may want to know how many CPUs the system has.
> >
> > This patch is making function set_max_cpu_num() available to
> > other parts of the perf tool so that global variable
> > 'max_cpu_num' gets the right value when referenced by cpu__max_cpu().
> >
> > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> >  tools/perf/util/cpumap.c | 2 +-
> >  tools/perf/util/cpumap.h | 1 +
> >  2 files changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
> > index 10af1e7524fb..ae179320c0c0 100644
> > --- a/tools/perf/util/cpumap.c
> > +++ b/tools/perf/util/cpumap.c
> > @@ -380,7 +380,7 @@ out:
> >  }
> >
> >  /* Determine highest possible cpu in the system for sparse allocation */
> > -static void set_max_cpu_num(void)
> > +void set_max_cpu_num(void)
> >  {
> >         const char *mnt;
> >         char path[PATH_MAX];
> > diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
> > index 85f7772457fa..45fa963345eb 100644
> > --- a/tools/perf/util/cpumap.h
> > +++ b/tools/perf/util/cpumap.h
> > @@ -14,6 +14,7 @@ struct cpu_map {
> >         int map[];
> >  };
> >
> > +void set_max_cpu_num(void);
> >  struct cpu_map *cpu_map__new(const char *cpu_list);
> >  struct cpu_map *cpu_map__empty_new(int nr);
> >  struct cpu_map *cpu_map__dummy_new(void);
> > --
> > 2.1.4
> >
> 
> Arnaldo,
> 
> I can't queue this patch for 4.6 without at least a reviewed by from you.

This one I remember, looks ugly, the name set_max_cpu_num() looks
strange, when that was restricted (static) to that cpumap.c file, it
wasn't a problem, exporting it for wider usage looks bad.

You've been waiting for this for quite a while, it seems, lemme stop
what I am doing to check this...

- Arnaldo
 
> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> be happy to use another baseline should that be more adequate for you.
> The same goes for [2]
> 
> Thanks,
> Mathieu
> 
> [1]. https://git.linaro.org/people/mathieu.poirier/coresight.git/shortlog/refs/heads/perf-v4.5-rc1-V8

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

* Re: [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
  2016-01-25 21:12       ` Arnaldo Carvalho de Melo
@ 2016-01-25 21:29         ` Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:29 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: linux-doc, Ingo Molnar, linux-arm-kernel, linux-kernel, Peter Zijlstra

Em Mon, Jan 25, 2016 at 06:12:42PM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
> > On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > > For memory allocation purposes, code located in other places
> > > then util/cpumap.c may want to know how many CPUs the system has.
> > >
> > > This patch is making function set_max_cpu_num() available to
> > > other parts of the perf tool so that global variable
> > > 'max_cpu_num' gets the right value when referenced by cpu__max_cpu().
> > >
> > > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > > Cc: Ingo Molnar <mingo@redhat.com>
> > > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > > ---
> > >  tools/perf/util/cpumap.c | 2 +-
> > >  tools/perf/util/cpumap.h | 1 +
> > >  2 files changed, 2 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
> > > index 10af1e7524fb..ae179320c0c0 100644
> > > --- a/tools/perf/util/cpumap.c
> > > +++ b/tools/perf/util/cpumap.c
> > > @@ -380,7 +380,7 @@ out:
> > >  }
> > >
> > >  /* Determine highest possible cpu in the system for sparse allocation */
> > > -static void set_max_cpu_num(void)
> > > +void set_max_cpu_num(void)
> > >  {
> > >         const char *mnt;
> > >         char path[PATH_MAX];
> > > diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
> > > index 85f7772457fa..45fa963345eb 100644
> > > --- a/tools/perf/util/cpumap.h
> > > +++ b/tools/perf/util/cpumap.h
> > > @@ -14,6 +14,7 @@ struct cpu_map {
> > >         int map[];
> > >  };
> > >
> > > +void set_max_cpu_num(void);
> > >  struct cpu_map *cpu_map__new(const char *cpu_list);
> > >  struct cpu_map *cpu_map__empty_new(int nr);
> > >  struct cpu_map *cpu_map__dummy_new(void);
> > > --
> > > 2.1.4
> > >
> > 
> > Arnaldo,
> > 
> > I can't queue this patch for 4.6 without at least a reviewed by from you.
> 
> This one I remember, looks ugly, the name set_max_cpu_num() looks
> strange, when that was restricted (static) to that cpumap.c file, it
> wasn't a problem, exporting it for wider usage looks bad.
> 
> You've been waiting for this for quite a while, it seems, lemme stop
> what I am doing to check this...

So, please check the patch below, what you need then is just to use
cpu__max_cpu().

- Arnaldo

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index fa935093a599..9bcf2bed3a6d 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -8,6 +8,10 @@
 #include <linux/bitmap.h>
 #include "asm/bug.h"
 
+static int max_cpu_num;
+static int max_node_num;
+static int *cpunode_map;
+
 static struct cpu_map *cpu_map__default_new(void)
 {
 	struct cpu_map *cpus;
@@ -486,6 +490,32 @@ out:
 		pr_err("Failed to read max nodes, using default of %d\n", max_node_num);
 }
 
+int cpu__max_node(void)
+{
+	if (unlikely(!max_node_num))
+		set_max_node_num();
+
+	return max_node_num;
+}
+
+int cpu__max_cpu(void)
+{
+	if (unlikely(!max_cpu_num))
+		set_max_cpu_num();
+
+	return max_cpu_num;
+}
+
+int cpu__get_node(int cpu)
+{
+	if (unlikely(cpunode_map == NULL)) {
+		pr_debug("cpu_map not initialized\n");
+		return -1;
+	}
+
+	return cpunode_map[cpu];
+}
+
 static int init_cpunode_map(void)
 {
 	int i;
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 71c41b9efabb..81a2562aaa2b 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -57,37 +57,11 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
 	return map ? map->map[0] == -1 : true;
 }
 
-int max_cpu_num;
-int max_node_num;
-int *cpunode_map;
-
 int cpu__setup_cpunode_map(void);
 
-static inline int cpu__max_node(void)
-{
-	if (unlikely(!max_node_num))
-		pr_debug("cpu_map not initialized\n");
-
-	return max_node_num;
-}
-
-static inline int cpu__max_cpu(void)
-{
-	if (unlikely(!max_cpu_num))
-		pr_debug("cpu_map not initialized\n");
-
-	return max_cpu_num;
-}
-
-static inline int cpu__get_node(int cpu)
-{
-	if (unlikely(cpunode_map == NULL)) {
-		pr_debug("cpu_map not initialized\n");
-		return -1;
-	}
-
-	return cpunode_map[cpu];
-}
+int cpu__max_node(void);
+int cpu__max_cpu(void);
+int cpu__get_node(int cpu);
 
 int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
 		       int (*f)(struct cpu_map *map, int cpu, void *data),

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

* [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
@ 2016-01-25 21:29         ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-25 21:29 UTC (permalink / raw)
  To: linux-arm-kernel

Em Mon, Jan 25, 2016 at 06:12:42PM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
> > On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> > > For memory allocation purposes, code located in other places
> > > then util/cpumap.c may want to know how many CPUs the system has.
> > >
> > > This patch is making function set_max_cpu_num() available to
> > > other parts of the perf tool so that global variable
> > > 'max_cpu_num' gets the right value when referenced by cpu__max_cpu().
> > >
> > > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> > > Cc: Ingo Molnar <mingo@redhat.com>
> > > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > > ---
> > >  tools/perf/util/cpumap.c | 2 +-
> > >  tools/perf/util/cpumap.h | 1 +
> > >  2 files changed, 2 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
> > > index 10af1e7524fb..ae179320c0c0 100644
> > > --- a/tools/perf/util/cpumap.c
> > > +++ b/tools/perf/util/cpumap.c
> > > @@ -380,7 +380,7 @@ out:
> > >  }
> > >
> > >  /* Determine highest possible cpu in the system for sparse allocation */
> > > -static void set_max_cpu_num(void)
> > > +void set_max_cpu_num(void)
> > >  {
> > >         const char *mnt;
> > >         char path[PATH_MAX];
> > > diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
> > > index 85f7772457fa..45fa963345eb 100644
> > > --- a/tools/perf/util/cpumap.h
> > > +++ b/tools/perf/util/cpumap.h
> > > @@ -14,6 +14,7 @@ struct cpu_map {
> > >         int map[];
> > >  };
> > >
> > > +void set_max_cpu_num(void);
> > >  struct cpu_map *cpu_map__new(const char *cpu_list);
> > >  struct cpu_map *cpu_map__empty_new(int nr);
> > >  struct cpu_map *cpu_map__dummy_new(void);
> > > --
> > > 2.1.4
> > >
> > 
> > Arnaldo,
> > 
> > I can't queue this patch for 4.6 without at least a reviewed by from you.
> 
> This one I remember, looks ugly, the name set_max_cpu_num() looks
> strange, when that was restricted (static) to that cpumap.c file, it
> wasn't a problem, exporting it for wider usage looks bad.
> 
> You've been waiting for this for quite a while, it seems, lemme stop
> what I am doing to check this...

So, please check the patch below, what you need then is just to use
cpu__max_cpu().

- Arnaldo

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index fa935093a599..9bcf2bed3a6d 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -8,6 +8,10 @@
 #include <linux/bitmap.h>
 #include "asm/bug.h"
 
+static int max_cpu_num;
+static int max_node_num;
+static int *cpunode_map;
+
 static struct cpu_map *cpu_map__default_new(void)
 {
 	struct cpu_map *cpus;
@@ -486,6 +490,32 @@ out:
 		pr_err("Failed to read max nodes, using default of %d\n", max_node_num);
 }
 
+int cpu__max_node(void)
+{
+	if (unlikely(!max_node_num))
+		set_max_node_num();
+
+	return max_node_num;
+}
+
+int cpu__max_cpu(void)
+{
+	if (unlikely(!max_cpu_num))
+		set_max_cpu_num();
+
+	return max_cpu_num;
+}
+
+int cpu__get_node(int cpu)
+{
+	if (unlikely(cpunode_map == NULL)) {
+		pr_debug("cpu_map not initialized\n");
+		return -1;
+	}
+
+	return cpunode_map[cpu];
+}
+
 static int init_cpunode_map(void)
 {
 	int i;
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 71c41b9efabb..81a2562aaa2b 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -57,37 +57,11 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
 	return map ? map->map[0] == -1 : true;
 }
 
-int max_cpu_num;
-int max_node_num;
-int *cpunode_map;
-
 int cpu__setup_cpunode_map(void);
 
-static inline int cpu__max_node(void)
-{
-	if (unlikely(!max_node_num))
-		pr_debug("cpu_map not initialized\n");
-
-	return max_node_num;
-}
-
-static inline int cpu__max_cpu(void)
-{
-	if (unlikely(!max_cpu_num))
-		pr_debug("cpu_map not initialized\n");
-
-	return max_cpu_num;
-}
-
-static inline int cpu__get_node(int cpu)
-{
-	if (unlikely(cpunode_map == NULL)) {
-		pr_debug("cpu_map not initialized\n");
-		return -1;
-	}
-
-	return cpunode_map[cpu];
-}
+int cpu__max_node(void);
+int cpu__max_cpu(void);
+int cpu__get_node(int cpu);
 
 int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
 		       int (*f)(struct cpu_map *map, int cpu, void *data),

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

* Re: [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
  2016-01-25 21:08       ` Arnaldo Carvalho de Melo
@ 2016-01-26 14:27         ` Adrian Hunter
  -1 siblings, 0 replies; 108+ messages in thread
From: Adrian Hunter @ 2016-01-26 14:27 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Mathieu Poirier
  Cc: linux-arm-kernel, Peter Zijlstra, linux-kernel, Ingo Molnar

On 25/01/16 23:08, Arnaldo Carvalho de Melo wrote:
> Em Mon, Jan 25, 2016 at 01:48:28PM -0700, Mathieu Poirier escreveu:
>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>> On some architecture the size of the private header may
>>> be dependent on the number of tracers used in the session.  As
>>> such adding a "struct perf_evlist *" parameter, which should
>>> contain all the required information.
>>>
>>> Also adjusting the existing client of the interface to take
>>> the new parameter into account.
>>>
>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>> Cc: Ingo Molnar <mingo@redhat.com>
>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> ---
>>>  tools/perf/arch/x86/util/intel-bts.c | 4 +++-
>>>  tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
>>>  tools/perf/util/auxtrace.c           | 7 ++++---
>>>  tools/perf/util/auxtrace.h           | 6 ++++--
>>>  4 files changed, 14 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
>>> index 9b94ce520917..4685a40777cc 100644
>>> --- a/tools/perf/arch/x86/util/intel-bts.c
>>> +++ b/tools/perf/arch/x86/util/intel-bts.c
>>> @@ -60,7 +60,9 @@ struct branch {
>>>         u64 misc;
>>>  };
>>>
>>> -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
>>> +static size_t
>>> +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>> +                        struct perf_evlist *evlist __maybe_unused)
>>>  {
>>>         return INTEL_BTS_AUXTRACE_PRIV_SIZE;
>>>  }
>>> diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
>>> index b02af064f0f9..e5c1f2e21f87 100644
>>> --- a/tools/perf/arch/x86/util/intel-pt.c
>>> +++ b/tools/perf/arch/x86/util/intel-pt.c
>>> @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
>>>         return attr;
>>>  }
>>>
>>> -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
>>> +static size_t
>>> +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>> +                       struct perf_evlist *evlist __maybe_unused)
>>>  {
>>>         return INTEL_PT_AUXTRACE_PRIV_SIZE;
>>>  }
>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>>> index 7f10430af39c..cc1c9ce5cc56 100644
>>> --- a/tools/perf/util/auxtrace.c
>>> +++ b/tools/perf/util/auxtrace.c
>>> @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
>>>                          heap_array[last].ordinal);
>>>  }
>>>
>>> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
>>> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
>>> +                                      struct perf_evlist *evlist)
>>>  {
>>>         if (itr)
>>> -               return itr->info_priv_size(itr);
>>> +               return itr->info_priv_size(itr, evlist);
>>>         return 0;
>>>  }
>>>
>>> @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
>>>         int err;
>>>
>>>         pr_debug2("Synthesizing auxtrace information\n");
>>> -       priv_size = auxtrace_record__info_priv_size(itr);
>>> +       priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
>>>         ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
>>>         if (!ev)
>>>                 return -ENOMEM;
>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>>> index b86f90db1352..e5a8e2d4f2af 100644
>>> --- a/tools/perf/util/auxtrace.h
>>> +++ b/tools/perf/util/auxtrace.h
>>> @@ -293,7 +293,8 @@ struct auxtrace_record {
>>>         int (*recording_options)(struct auxtrace_record *itr,
>>>                                  struct perf_evlist *evlist,
>>>                                  struct record_opts *opts);
>>> -       size_t (*info_priv_size)(struct auxtrace_record *itr);
>>> +       size_t (*info_priv_size)(struct auxtrace_record *itr,
>>> +                                struct perf_evlist *evlist);
>>>         int (*info_fill)(struct auxtrace_record *itr,
>>>                          struct perf_session *session,
>>>                          struct auxtrace_info_event *auxtrace_info,
>>> @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
>>>  int auxtrace_record__options(struct auxtrace_record *itr,
>>>                              struct perf_evlist *evlist,
>>>                              struct record_opts *opts);
>>> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
>>> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
>>> +                                      struct perf_evlist *evlist);
>>>  int auxtrace_record__info_fill(struct auxtrace_record *itr,
>>>                                struct perf_session *session,
>>>                                struct auxtrace_info_event *auxtrace_info,
>>> --
>>> 2.1.4
>>>
>>
>> Arnaldo,
>>
>> As with my previous email, I can't queue this patch for 4.6 without at
>> least a reviewed by from you.
>>
>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>> be happy to use another baseline should that be more adequate for you.
> 
> Adrian, are you ok with this?

I will try to look at this and the others in the next day or two.

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

* [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
@ 2016-01-26 14:27         ` Adrian Hunter
  0 siblings, 0 replies; 108+ messages in thread
From: Adrian Hunter @ 2016-01-26 14:27 UTC (permalink / raw)
  To: linux-arm-kernel

On 25/01/16 23:08, Arnaldo Carvalho de Melo wrote:
> Em Mon, Jan 25, 2016 at 01:48:28PM -0700, Mathieu Poirier escreveu:
>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>> On some architecture the size of the private header may
>>> be dependent on the number of tracers used in the session.  As
>>> such adding a "struct perf_evlist *" parameter, which should
>>> contain all the required information.
>>>
>>> Also adjusting the existing client of the interface to take
>>> the new parameter into account.
>>>
>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>> Cc: Ingo Molnar <mingo@redhat.com>
>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> ---
>>>  tools/perf/arch/x86/util/intel-bts.c | 4 +++-
>>>  tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
>>>  tools/perf/util/auxtrace.c           | 7 ++++---
>>>  tools/perf/util/auxtrace.h           | 6 ++++--
>>>  4 files changed, 14 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
>>> index 9b94ce520917..4685a40777cc 100644
>>> --- a/tools/perf/arch/x86/util/intel-bts.c
>>> +++ b/tools/perf/arch/x86/util/intel-bts.c
>>> @@ -60,7 +60,9 @@ struct branch {
>>>         u64 misc;
>>>  };
>>>
>>> -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
>>> +static size_t
>>> +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>> +                        struct perf_evlist *evlist __maybe_unused)
>>>  {
>>>         return INTEL_BTS_AUXTRACE_PRIV_SIZE;
>>>  }
>>> diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
>>> index b02af064f0f9..e5c1f2e21f87 100644
>>> --- a/tools/perf/arch/x86/util/intel-pt.c
>>> +++ b/tools/perf/arch/x86/util/intel-pt.c
>>> @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
>>>         return attr;
>>>  }
>>>
>>> -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
>>> +static size_t
>>> +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>> +                       struct perf_evlist *evlist __maybe_unused)
>>>  {
>>>         return INTEL_PT_AUXTRACE_PRIV_SIZE;
>>>  }
>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>>> index 7f10430af39c..cc1c9ce5cc56 100644
>>> --- a/tools/perf/util/auxtrace.c
>>> +++ b/tools/perf/util/auxtrace.c
>>> @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
>>>                          heap_array[last].ordinal);
>>>  }
>>>
>>> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
>>> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
>>> +                                      struct perf_evlist *evlist)
>>>  {
>>>         if (itr)
>>> -               return itr->info_priv_size(itr);
>>> +               return itr->info_priv_size(itr, evlist);
>>>         return 0;
>>>  }
>>>
>>> @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
>>>         int err;
>>>
>>>         pr_debug2("Synthesizing auxtrace information\n");
>>> -       priv_size = auxtrace_record__info_priv_size(itr);
>>> +       priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
>>>         ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
>>>         if (!ev)
>>>                 return -ENOMEM;
>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>>> index b86f90db1352..e5a8e2d4f2af 100644
>>> --- a/tools/perf/util/auxtrace.h
>>> +++ b/tools/perf/util/auxtrace.h
>>> @@ -293,7 +293,8 @@ struct auxtrace_record {
>>>         int (*recording_options)(struct auxtrace_record *itr,
>>>                                  struct perf_evlist *evlist,
>>>                                  struct record_opts *opts);
>>> -       size_t (*info_priv_size)(struct auxtrace_record *itr);
>>> +       size_t (*info_priv_size)(struct auxtrace_record *itr,
>>> +                                struct perf_evlist *evlist);
>>>         int (*info_fill)(struct auxtrace_record *itr,
>>>                          struct perf_session *session,
>>>                          struct auxtrace_info_event *auxtrace_info,
>>> @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
>>>  int auxtrace_record__options(struct auxtrace_record *itr,
>>>                              struct perf_evlist *evlist,
>>>                              struct record_opts *opts);
>>> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
>>> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
>>> +                                      struct perf_evlist *evlist);
>>>  int auxtrace_record__info_fill(struct auxtrace_record *itr,
>>>                                struct perf_session *session,
>>>                                struct auxtrace_info_event *auxtrace_info,
>>> --
>>> 2.1.4
>>>
>>
>> Arnaldo,
>>
>> As with my previous email, I can't queue this patch for 4.6 without at
>> least a reviewed by from you.
>>
>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>> be happy to use another baseline should that be more adequate for you.
> 
> Adrian, are you ok with this?

I will try to look at this and the others in the next day or two.

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

* Re: [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
  2016-01-26 14:27         ` Adrian Hunter
@ 2016-01-26 14:33           ` Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-26 14:33 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Mathieu Poirier, linux-arm-kernel, Peter Zijlstra, linux-kernel,
	Ingo Molnar

Em Tue, Jan 26, 2016 at 04:27:52PM +0200, Adrian Hunter escreveu:
> On 25/01/16 23:08, Arnaldo Carvalho de Melo wrote:
> > Em Mon, Jan 25, 2016 at 01:48:28PM -0700, Mathieu Poirier escreveu:
> >> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> >>> On some architecture the size of the private header may
> >> Arnaldo,
> >>
> >> As with my previous email, I can't queue this patch for 4.6 without at
> >> least a reviewed by from you.
> >>
> >> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> >> be happy to use another baseline should that be more adequate for you.
> > 
> > Adrian, are you ok with this?
> 
> I will try to look at this and the others in the next day or two.
> 

Thanks, that will help,

- Arnaldo

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

* [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
@ 2016-01-26 14:33           ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-26 14:33 UTC (permalink / raw)
  To: linux-arm-kernel

Em Tue, Jan 26, 2016 at 04:27:52PM +0200, Adrian Hunter escreveu:
> On 25/01/16 23:08, Arnaldo Carvalho de Melo wrote:
> > Em Mon, Jan 25, 2016 at 01:48:28PM -0700, Mathieu Poirier escreveu:
> >> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> >>> On some architecture the size of the private header may
> >> Arnaldo,
> >>
> >> As with my previous email, I can't queue this patch for 4.6 without at
> >> least a reviewed by from you.
> >>
> >> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> >> be happy to use another baseline should that be more adequate for you.
> > 
> > Adrian, are you ok with this?
> 
> I will try to look at this and the others in the next day or two.
> 

Thanks, that will help,

- Arnaldo

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

* Re: [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
  2016-01-14 21:46   ` Mathieu Poirier
@ 2016-01-26 15:27     ` Alexander Shishkin
  -1 siblings, 0 replies; 108+ messages in thread
From: Alexander Shishkin @ 2016-01-26 15:27 UTC (permalink / raw)
  To: Mathieu Poirier, linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

> +static int etm_event_init(struct perf_event *event)
> +{
> +	if (event->attr.type != etm_pmu.type)
> +		return -ENOENT;
> +
> +	if (event->cpu >= nr_cpu_ids)
> +		return -EINVAL;

perf_event_alloc() already does this. Except for this one doesn't cover
the negative space.

[snip]

> +static void etm_free_aux(void *data)
> +{
> +	struct etm_event_data *event_data = data;
> +
> +	pr_err("Queing work\n");

Probably not pr_err().

> +	schedule_work(&event_data->work);
> +}

[snip]

> +static void etm_event_start(struct perf_event *event, int flags)
> +{
> +	int cpu = smp_processor_id();
> +	struct etm_event_data *event_data;
> +	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
> +	struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
> +
> +	if (!csdev)
> +		goto fail;
> +
> +	/*
> +	 * Deal with the ring buffer API and get a handle on the
> +	 * session's information.
> +	 */
> +	event_data = perf_aux_output_begin(handle, event);
> +	if (WARN_ON_ONCE(!event_data))
> +		goto fail;

There really shouldn't be a warning here. I understand that the 'no
buffer' case is taped over by the !csdev check above, but there are
other ligitimate reasons for perf_aux_output_begin() to return NULL,
like no-space-left.

> +
> +	/* We need a sink, no need to continue without one */
> +	sink = coresight_get_sink(event_data->path[cpu]);
> +	if (!sink || !sink_ops(sink)->set_buffer)
> +		goto fail_end_stop;

Is this possible after the coresight_build_path() things in setup_aux?
Might be a better candidate for WARN_*ONCE().

> +
> +	/* Configure the sink */
> +	if (sink_ops(sink)->set_buffer(sink, handle,
> +				       event_data->snk_config))
> +		goto fail_end_stop;
> +
> +	/* Nothing will happen without a path */
> +	if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF))
> +		goto fail_end_stop;

I'd like to understand all the potential failures here, because it's
really a good idea to keep those to a minimum for the sake of
consistency. That is, if the user succeeded in creating an event, about
the only good reason for the event not starting is a filled up buffer.

This is why it makes a lot of sense to keep all the
coresight_build_path()/coresight_enable_path() to the .event_init()
phase and let them fail early, if they should fail.

Regards,
--
Alex

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

* [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
@ 2016-01-26 15:27     ` Alexander Shishkin
  0 siblings, 0 replies; 108+ messages in thread
From: Alexander Shishkin @ 2016-01-26 15:27 UTC (permalink / raw)
  To: linux-arm-kernel

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

> +static int etm_event_init(struct perf_event *event)
> +{
> +	if (event->attr.type != etm_pmu.type)
> +		return -ENOENT;
> +
> +	if (event->cpu >= nr_cpu_ids)
> +		return -EINVAL;

perf_event_alloc() already does this. Except for this one doesn't cover
the negative space.

[snip]

> +static void etm_free_aux(void *data)
> +{
> +	struct etm_event_data *event_data = data;
> +
> +	pr_err("Queing work\n");

Probably not pr_err().

> +	schedule_work(&event_data->work);
> +}

[snip]

> +static void etm_event_start(struct perf_event *event, int flags)
> +{
> +	int cpu = smp_processor_id();
> +	struct etm_event_data *event_data;
> +	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
> +	struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
> +
> +	if (!csdev)
> +		goto fail;
> +
> +	/*
> +	 * Deal with the ring buffer API and get a handle on the
> +	 * session's information.
> +	 */
> +	event_data = perf_aux_output_begin(handle, event);
> +	if (WARN_ON_ONCE(!event_data))
> +		goto fail;

There really shouldn't be a warning here. I understand that the 'no
buffer' case is taped over by the !csdev check above, but there are
other ligitimate reasons for perf_aux_output_begin() to return NULL,
like no-space-left.

> +
> +	/* We need a sink, no need to continue without one */
> +	sink = coresight_get_sink(event_data->path[cpu]);
> +	if (!sink || !sink_ops(sink)->set_buffer)
> +		goto fail_end_stop;

Is this possible after the coresight_build_path() things in setup_aux?
Might be a better candidate for WARN_*ONCE().

> +
> +	/* Configure the sink */
> +	if (sink_ops(sink)->set_buffer(sink, handle,
> +				       event_data->snk_config))
> +		goto fail_end_stop;
> +
> +	/* Nothing will happen without a path */
> +	if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF))
> +		goto fail_end_stop;

I'd like to understand all the potential failures here, because it's
really a good idea to keep those to a minimum for the sake of
consistency. That is, if the user succeeded in creating an event, about
the only good reason for the event not starting is a filled up buffer.

This is why it makes a lot of sense to keep all the
coresight_build_path()/coresight_enable_path() to the .event_init()
phase and let them fail early, if they should fail.

Regards,
--
Alex

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

* Re: [PATCH V8 16/23] coresight: etb10: implementing AUX API
  2016-01-14 21:46   ` Mathieu Poirier
@ 2016-01-26 15:53     ` Alexander Shishkin
  -1 siblings, 0 replies; 108+ messages in thread
From: Alexander Shishkin @ 2016-01-26 15:53 UTC (permalink / raw)
  To: Mathieu Poirier, linux-arm-kernel, linux-kernel
  Cc: linux-doc, zhang.chunyan, mike.leach, tor, al.grant, rabin,
	Mathieu Poirier

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

> Adding an ETB10 specific AUX area operations to be used
> by the perf framework when events are initialised.
>
> Part of this operation involves modeling the mmap'ed area
> based on the specific ways a sink buffer gathers information.

I don't mind being CC'd on the rest of the patches too, btw. :)

> +static unsigned long etb_reset_buffer(struct coresight_device *csdev,
> +				      struct perf_output_handle *handle,
> +				      void *sink_config, bool *lost)
> +{
> +	unsigned long size = 0;
> +	struct cs_buffers *buf = sink_config;
> +
> +	if (buf) {
> +		/*
> +		 * In snapshot mode ->data_size holds the new address of the
> +		 * ring buffer's head.  The size itself is the whole address
> +		 * range since we want the latest information.
> +		 */
> +		if (buf->snapshot)
> +			handle->head = local_xchg(&buf->data_size,
> +						  buf->nr_pages << PAGE_SHIFT);
> +
> +		/*
> +		 * Tell the tracer PMU how much we got in this run and if
> +		 * something went wrong along the way.  Nobody else can use
> +		 * this cs_buffers instance until we are done.  As such
> +		 * resetting parameters here and squaring off with the ring
> +		 * buffer API in the tracer PMU is fine.
> +		 */
> +		*lost = local_xchg(&buf->lost, 0);

This is a thin ice, you can't really make assumptions about bool's
storage size or even type, afaict.

> +		size = local_xchg(&buf->data_size, 0);
> +	}
> +
> +	return size;
> +}
> +
> +static void etb_update_buffer(struct coresight_device *csdev,
> +			      struct perf_output_handle *handle,
> +			      void *sink_config)
> +{
> +	int i, cur;
> +	u8 *buf_ptr;
> +	u32 read_ptr, write_ptr, capacity;
> +	u32 status, read_data, to_read;
> +	unsigned long flags, offset;
> +	struct cs_buffers *buf = sink_config;
> +	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> +
> +	if (!buf)
> +		return;
> +
> +	capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS;
> +
> +	spin_lock_irqsave(&drvdata->spinlock, flags);

This spinlock seems to be held over the entire readout operation,
however, I can't find clear rules wrt what structures etc are serialized
on it. Instead, the comment says "only one at a time pls". Same for
etm's big drvdata spinlock.

Regards,
--
Alex

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

* [PATCH V8 16/23] coresight: etb10: implementing AUX API
@ 2016-01-26 15:53     ` Alexander Shishkin
  0 siblings, 0 replies; 108+ messages in thread
From: Alexander Shishkin @ 2016-01-26 15:53 UTC (permalink / raw)
  To: linux-arm-kernel

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

> Adding an ETB10 specific AUX area operations to be used
> by the perf framework when events are initialised.
>
> Part of this operation involves modeling the mmap'ed area
> based on the specific ways a sink buffer gathers information.

I don't mind being CC'd on the rest of the patches too, btw. :)

> +static unsigned long etb_reset_buffer(struct coresight_device *csdev,
> +				      struct perf_output_handle *handle,
> +				      void *sink_config, bool *lost)
> +{
> +	unsigned long size = 0;
> +	struct cs_buffers *buf = sink_config;
> +
> +	if (buf) {
> +		/*
> +		 * In snapshot mode ->data_size holds the new address of the
> +		 * ring buffer's head.  The size itself is the whole address
> +		 * range since we want the latest information.
> +		 */
> +		if (buf->snapshot)
> +			handle->head = local_xchg(&buf->data_size,
> +						  buf->nr_pages << PAGE_SHIFT);
> +
> +		/*
> +		 * Tell the tracer PMU how much we got in this run and if
> +		 * something went wrong along the way.  Nobody else can use
> +		 * this cs_buffers instance until we are done.  As such
> +		 * resetting parameters here and squaring off with the ring
> +		 * buffer API in the tracer PMU is fine.
> +		 */
> +		*lost = local_xchg(&buf->lost, 0);

This is a thin ice, you can't really make assumptions about bool's
storage size or even type, afaict.

> +		size = local_xchg(&buf->data_size, 0);
> +	}
> +
> +	return size;
> +}
> +
> +static void etb_update_buffer(struct coresight_device *csdev,
> +			      struct perf_output_handle *handle,
> +			      void *sink_config)
> +{
> +	int i, cur;
> +	u8 *buf_ptr;
> +	u32 read_ptr, write_ptr, capacity;
> +	u32 status, read_data, to_read;
> +	unsigned long flags, offset;
> +	struct cs_buffers *buf = sink_config;
> +	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> +
> +	if (!buf)
> +		return;
> +
> +	capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS;
> +
> +	spin_lock_irqsave(&drvdata->spinlock, flags);

This spinlock seems to be held over the entire readout operation,
however, I can't find clear rules wrt what structures etc are serialized
on it. Instead, the comment says "only one at a time pls". Same for
etm's big drvdata spinlock.

Regards,
--
Alex

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

* Re: [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
  2016-01-25 21:29         ` Arnaldo Carvalho de Melo
@ 2016-01-26 17:08           ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-26 17:08 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: linux-doc, Ingo Molnar, linux-arm-kernel, linux-kernel, Peter Zijlstra

On 25 January 2016 at 14:29, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> Em Mon, Jan 25, 2016 at 06:12:42PM -0300, Arnaldo Carvalho de Melo escreveu:
>> Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
>> > On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>> > > For memory allocation purposes, code located in other places
>> > > then util/cpumap.c may want to know how many CPUs the system has.
>> > >
>> > > This patch is making function set_max_cpu_num() available to
>> > > other parts of the perf tool so that global variable
>> > > 'max_cpu_num' gets the right value when referenced by cpu__max_cpu().
>> > >
>> > > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>> > > Cc: Ingo Molnar <mingo@redhat.com>
>> > > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>> > > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> > > ---
>> > >  tools/perf/util/cpumap.c | 2 +-
>> > >  tools/perf/util/cpumap.h | 1 +
>> > >  2 files changed, 2 insertions(+), 1 deletion(-)
>> > >
>> > > diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
>> > > index 10af1e7524fb..ae179320c0c0 100644
>> > > --- a/tools/perf/util/cpumap.c
>> > > +++ b/tools/perf/util/cpumap.c
>> > > @@ -380,7 +380,7 @@ out:
>> > >  }
>> > >
>> > >  /* Determine highest possible cpu in the system for sparse allocation */
>> > > -static void set_max_cpu_num(void)
>> > > +void set_max_cpu_num(void)
>> > >  {
>> > >         const char *mnt;
>> > >         char path[PATH_MAX];
>> > > diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
>> > > index 85f7772457fa..45fa963345eb 100644
>> > > --- a/tools/perf/util/cpumap.h
>> > > +++ b/tools/perf/util/cpumap.h
>> > > @@ -14,6 +14,7 @@ struct cpu_map {
>> > >         int map[];
>> > >  };
>> > >
>> > > +void set_max_cpu_num(void);
>> > >  struct cpu_map *cpu_map__new(const char *cpu_list);
>> > >  struct cpu_map *cpu_map__empty_new(int nr);
>> > >  struct cpu_map *cpu_map__dummy_new(void);
>> > > --
>> > > 2.1.4
>> > >
>> >
>> > Arnaldo,
>> >
>> > I can't queue this patch for 4.6 without at least a reviewed by from you.
>>
>> This one I remember, looks ugly, the name set_max_cpu_num() looks
>> strange, when that was restricted (static) to that cpumap.c file, it
>> wasn't a problem, exporting it for wider usage looks bad.
>>
>> You've been waiting for this for quite a while, it seems, lemme stop
>> what I am doing to check this...
>
> So, please check the patch below, what you need then is just to use
> cpu__max_cpu().

I like your approach - thanks for the review.  I will spin V9 when I
have received Adrian's comments.

Mathieu

>
> - Arnaldo
>
> diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
> index fa935093a599..9bcf2bed3a6d 100644
> --- a/tools/perf/util/cpumap.c
> +++ b/tools/perf/util/cpumap.c
> @@ -8,6 +8,10 @@
>  #include <linux/bitmap.h>
>  #include "asm/bug.h"
>
> +static int max_cpu_num;
> +static int max_node_num;
> +static int *cpunode_map;
> +
>  static struct cpu_map *cpu_map__default_new(void)
>  {
>         struct cpu_map *cpus;
> @@ -486,6 +490,32 @@ out:
>                 pr_err("Failed to read max nodes, using default of %d\n", max_node_num);
>  }
>
> +int cpu__max_node(void)
> +{
> +       if (unlikely(!max_node_num))
> +               set_max_node_num();
> +
> +       return max_node_num;
> +}
> +
> +int cpu__max_cpu(void)
> +{
> +       if (unlikely(!max_cpu_num))
> +               set_max_cpu_num();
> +
> +       return max_cpu_num;
> +}
> +
> +int cpu__get_node(int cpu)
> +{
> +       if (unlikely(cpunode_map == NULL)) {
> +               pr_debug("cpu_map not initialized\n");
> +               return -1;
> +       }
> +
> +       return cpunode_map[cpu];
> +}
> +
>  static int init_cpunode_map(void)
>  {
>         int i;
> diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
> index 71c41b9efabb..81a2562aaa2b 100644
> --- a/tools/perf/util/cpumap.h
> +++ b/tools/perf/util/cpumap.h
> @@ -57,37 +57,11 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
>         return map ? map->map[0] == -1 : true;
>  }
>
> -int max_cpu_num;
> -int max_node_num;
> -int *cpunode_map;
> -
>  int cpu__setup_cpunode_map(void);
>
> -static inline int cpu__max_node(void)
> -{
> -       if (unlikely(!max_node_num))
> -               pr_debug("cpu_map not initialized\n");
> -
> -       return max_node_num;
> -}
> -
> -static inline int cpu__max_cpu(void)
> -{
> -       if (unlikely(!max_cpu_num))
> -               pr_debug("cpu_map not initialized\n");
> -
> -       return max_cpu_num;
> -}
> -
> -static inline int cpu__get_node(int cpu)
> -{
> -       if (unlikely(cpunode_map == NULL)) {
> -               pr_debug("cpu_map not initialized\n");
> -               return -1;
> -       }
> -
> -       return cpunode_map[cpu];
> -}
> +int cpu__max_node(void);
> +int cpu__max_cpu(void);
> +int cpu__get_node(int cpu);
>
>  int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
>                        int (*f)(struct cpu_map *map, int cpu, void *data),

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

* [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
@ 2016-01-26 17:08           ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-26 17:08 UTC (permalink / raw)
  To: linux-arm-kernel

On 25 January 2016 at 14:29, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> Em Mon, Jan 25, 2016 at 06:12:42PM -0300, Arnaldo Carvalho de Melo escreveu:
>> Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
>> > On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>> > > For memory allocation purposes, code located in other places
>> > > then util/cpumap.c may want to know how many CPUs the system has.
>> > >
>> > > This patch is making function set_max_cpu_num() available to
>> > > other parts of the perf tool so that global variable
>> > > 'max_cpu_num' gets the right value when referenced by cpu__max_cpu().
>> > >
>> > > Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>> > > Cc: Ingo Molnar <mingo@redhat.com>
>> > > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>> > > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> > > ---
>> > >  tools/perf/util/cpumap.c | 2 +-
>> > >  tools/perf/util/cpumap.h | 1 +
>> > >  2 files changed, 2 insertions(+), 1 deletion(-)
>> > >
>> > > diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
>> > > index 10af1e7524fb..ae179320c0c0 100644
>> > > --- a/tools/perf/util/cpumap.c
>> > > +++ b/tools/perf/util/cpumap.c
>> > > @@ -380,7 +380,7 @@ out:
>> > >  }
>> > >
>> > >  /* Determine highest possible cpu in the system for sparse allocation */
>> > > -static void set_max_cpu_num(void)
>> > > +void set_max_cpu_num(void)
>> > >  {
>> > >         const char *mnt;
>> > >         char path[PATH_MAX];
>> > > diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
>> > > index 85f7772457fa..45fa963345eb 100644
>> > > --- a/tools/perf/util/cpumap.h
>> > > +++ b/tools/perf/util/cpumap.h
>> > > @@ -14,6 +14,7 @@ struct cpu_map {
>> > >         int map[];
>> > >  };
>> > >
>> > > +void set_max_cpu_num(void);
>> > >  struct cpu_map *cpu_map__new(const char *cpu_list);
>> > >  struct cpu_map *cpu_map__empty_new(int nr);
>> > >  struct cpu_map *cpu_map__dummy_new(void);
>> > > --
>> > > 2.1.4
>> > >
>> >
>> > Arnaldo,
>> >
>> > I can't queue this patch for 4.6 without at least a reviewed by from you.
>>
>> This one I remember, looks ugly, the name set_max_cpu_num() looks
>> strange, when that was restricted (static) to that cpumap.c file, it
>> wasn't a problem, exporting it for wider usage looks bad.
>>
>> You've been waiting for this for quite a while, it seems, lemme stop
>> what I am doing to check this...
>
> So, please check the patch below, what you need then is just to use
> cpu__max_cpu().

I like your approach - thanks for the review.  I will spin V9 when I
have received Adrian's comments.

Mathieu

>
> - Arnaldo
>
> diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
> index fa935093a599..9bcf2bed3a6d 100644
> --- a/tools/perf/util/cpumap.c
> +++ b/tools/perf/util/cpumap.c
> @@ -8,6 +8,10 @@
>  #include <linux/bitmap.h>
>  #include "asm/bug.h"
>
> +static int max_cpu_num;
> +static int max_node_num;
> +static int *cpunode_map;
> +
>  static struct cpu_map *cpu_map__default_new(void)
>  {
>         struct cpu_map *cpus;
> @@ -486,6 +490,32 @@ out:
>                 pr_err("Failed to read max nodes, using default of %d\n", max_node_num);
>  }
>
> +int cpu__max_node(void)
> +{
> +       if (unlikely(!max_node_num))
> +               set_max_node_num();
> +
> +       return max_node_num;
> +}
> +
> +int cpu__max_cpu(void)
> +{
> +       if (unlikely(!max_cpu_num))
> +               set_max_cpu_num();
> +
> +       return max_cpu_num;
> +}
> +
> +int cpu__get_node(int cpu)
> +{
> +       if (unlikely(cpunode_map == NULL)) {
> +               pr_debug("cpu_map not initialized\n");
> +               return -1;
> +       }
> +
> +       return cpunode_map[cpu];
> +}
> +
>  static int init_cpunode_map(void)
>  {
>         int i;
> diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
> index 71c41b9efabb..81a2562aaa2b 100644
> --- a/tools/perf/util/cpumap.h
> +++ b/tools/perf/util/cpumap.h
> @@ -57,37 +57,11 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
>         return map ? map->map[0] == -1 : true;
>  }
>
> -int max_cpu_num;
> -int max_node_num;
> -int *cpunode_map;
> -
>  int cpu__setup_cpunode_map(void);
>
> -static inline int cpu__max_node(void)
> -{
> -       if (unlikely(!max_node_num))
> -               pr_debug("cpu_map not initialized\n");
> -
> -       return max_node_num;
> -}
> -
> -static inline int cpu__max_cpu(void)
> -{
> -       if (unlikely(!max_cpu_num))
> -               pr_debug("cpu_map not initialized\n");
> -
> -       return max_cpu_num;
> -}
> -
> -static inline int cpu__get_node(int cpu)
> -{
> -       if (unlikely(cpunode_map == NULL)) {
> -               pr_debug("cpu_map not initialized\n");
> -               return -1;
> -       }
> -
> -       return cpunode_map[cpu];
> -}
> +int cpu__max_node(void);
> +int cpu__max_cpu(void);
> +int cpu__get_node(int cpu);
>
>  int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
>                        int (*f)(struct cpu_map *map, int cpu, void *data),

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

* Re: [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
  2016-01-26 17:08           ` Mathieu Poirier
@ 2016-01-26 18:51             ` Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-26 18:51 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: linux-doc, Ingo Molnar, linux-arm-kernel, linux-kernel, Peter Zijlstra

Em Tue, Jan 26, 2016 at 10:08:21AM -0700, Mathieu Poirier escreveu:
> On 25 January 2016 at 14:29, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> > Em Mon, Jan 25, 2016 at 06:12:42PM -0300, Arnaldo Carvalho de Melo escreveu:
> >> Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
> >> > On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> >> > I can't queue this patch for 4.6 without at least a reviewed by from you.
> >>
> >> This one I remember, looks ugly, the name set_max_cpu_num() looks
> >> strange, when that was restricted (static) to that cpumap.c file, it
> >> wasn't a problem, exporting it for wider usage looks bad.
> >>
> >> You've been waiting for this for quite a while, it seems, lemme stop
> >> what I am doing to check this...
> >
> > So, please check the patch below, what you need then is just to use
> > cpu__max_cpu().
> 
> I like your approach - thanks for the review.  I will spin V9 when I
> have received Adrian's comments.

I'll take that as an Acked-by: and since this improves the current
situation by hiding needlessly exported global variables, I'll get it in
now, thanks.

- Arnaldo

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

* [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
@ 2016-01-26 18:51             ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-26 18:51 UTC (permalink / raw)
  To: linux-arm-kernel

Em Tue, Jan 26, 2016 at 10:08:21AM -0700, Mathieu Poirier escreveu:
> On 25 January 2016 at 14:29, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> > Em Mon, Jan 25, 2016 at 06:12:42PM -0300, Arnaldo Carvalho de Melo escreveu:
> >> Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
> >> > On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> >> > I can't queue this patch for 4.6 without at least a reviewed by from you.
> >>
> >> This one I remember, looks ugly, the name set_max_cpu_num() looks
> >> strange, when that was restricted (static) to that cpumap.c file, it
> >> wasn't a problem, exporting it for wider usage looks bad.
> >>
> >> You've been waiting for this for quite a while, it seems, lemme stop
> >> what I am doing to check this...
> >
> > So, please check the patch below, what you need then is just to use
> > cpu__max_cpu().
> 
> I like your approach - thanks for the review.  I will spin V9 when I
> have received Adrian's comments.

I'll take that as an Acked-by: and since this improves the current
situation by hiding needlessly exported global variables, I'll get it in
now, thanks.

- Arnaldo

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

* Re: [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
  2016-01-26 18:51             ` Arnaldo Carvalho de Melo
@ 2016-01-27 16:24               ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-27 16:24 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: linux-doc, Ingo Molnar, linux-arm-kernel, linux-kernel, Peter Zijlstra

On 26 January 2016 at 11:51, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> Em Tue, Jan 26, 2016 at 10:08:21AM -0700, Mathieu Poirier escreveu:
>> On 25 January 2016 at 14:29, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
>> > Em Mon, Jan 25, 2016 at 06:12:42PM -0300, Arnaldo Carvalho de Melo escreveu:
>> >> Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
>> >> > On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>> >> > I can't queue this patch for 4.6 without at least a reviewed by from you.
>> >>
>> >> This one I remember, looks ugly, the name set_max_cpu_num() looks
>> >> strange, when that was restricted (static) to that cpumap.c file, it
>> >> wasn't a problem, exporting it for wider usage looks bad.
>> >>
>> >> You've been waiting for this for quite a while, it seems, lemme stop
>> >> what I am doing to check this...
>> >
>> > So, please check the patch below, what you need then is just to use
>> > cpu__max_cpu().
>>
>> I like your approach - thanks for the review.  I will spin V9 when I
>> have received Adrian's comments.
>
> I'll take that as an Acked-by: and since this improves the current
> situation by hiding needlessly exported global variables, I'll get it in
> now, thanks.

I would have added this code in my patchset with the right authorship
- whatever works best for you.

Thanks,
Mathieu

>
> - Arnaldo

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

* [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static
@ 2016-01-27 16:24               ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-27 16:24 UTC (permalink / raw)
  To: linux-arm-kernel

On 26 January 2016 at 11:51, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> Em Tue, Jan 26, 2016 at 10:08:21AM -0700, Mathieu Poirier escreveu:
>> On 25 January 2016 at 14:29, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
>> > Em Mon, Jan 25, 2016 at 06:12:42PM -0300, Arnaldo Carvalho de Melo escreveu:
>> >> Em Mon, Jan 25, 2016 at 01:46:22PM -0700, Mathieu Poirier escreveu:
>> >> > On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>> >> > I can't queue this patch for 4.6 without at least a reviewed by from you.
>> >>
>> >> This one I remember, looks ugly, the name set_max_cpu_num() looks
>> >> strange, when that was restricted (static) to that cpumap.c file, it
>> >> wasn't a problem, exporting it for wider usage looks bad.
>> >>
>> >> You've been waiting for this for quite a while, it seems, lemme stop
>> >> what I am doing to check this...
>> >
>> > So, please check the patch below, what you need then is just to use
>> > cpu__max_cpu().
>>
>> I like your approach - thanks for the review.  I will spin V9 when I
>> have received Adrian's comments.
>
> I'll take that as an Acked-by: and since this improves the current
> situation by hiding needlessly exported global variables, I'll get it in
> now, thanks.

I would have added this code in my patchset with the right authorship
- whatever works best for you.

Thanks,
Mathieu

>
> - Arnaldo

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

* Re: [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
  2016-01-26 15:27     ` Alexander Shishkin
@ 2016-01-27 18:33       ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-27 18:33 UTC (permalink / raw)
  To: Alexander Shishkin
  Cc: linux-arm-kernel, linux-kernel, linux-doc, Chunyan Zhang,
	Mike Leach, Jeremiassen, Tor, Al Grant, Rabin Vincent

On 26 January 2016 at 08:27, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>> +static int etm_event_init(struct perf_event *event)
>> +{
>> +     if (event->attr.type != etm_pmu.type)
>> +             return -ENOENT;
>> +
>> +     if (event->cpu >= nr_cpu_ids)
>> +             return -EINVAL;
>
> perf_event_alloc() already does this. Except for this one doesn't cover
> the negative space.

Ack

>
> [snip]
>
>> +static void etm_free_aux(void *data)
>> +{
>> +     struct etm_event_data *event_data = data;
>> +
>> +     pr_err("Queing work\n");
>
> Probably not pr_err().

That's an old debug message - I've removed it already.

>
>> +     schedule_work(&event_data->work);
>> +}
>
> [snip]
>
>> +static void etm_event_start(struct perf_event *event, int flags)
>> +{
>> +     int cpu = smp_processor_id();
>> +     struct etm_event_data *event_data;
>> +     struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
>> +     struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
>> +
>> +     if (!csdev)
>> +             goto fail;
>> +
>> +     /*
>> +      * Deal with the ring buffer API and get a handle on the
>> +      * session's information.
>> +      */
>> +     event_data = perf_aux_output_begin(handle, event);
>> +     if (WARN_ON_ONCE(!event_data))
>> +             goto fail;
>
> There really shouldn't be a warning here. I understand that the 'no
> buffer' case is taped over by the !csdev check above, but there are
> other ligitimate reasons for perf_aux_output_begin() to return NULL,
> like no-space-left.

That's too harsh yes.

>
>> +
>> +     /* We need a sink, no need to continue without one */
>> +     sink = coresight_get_sink(event_data->path[cpu]);
>> +     if (!sink || !sink_ops(sink)->set_buffer)
>> +             goto fail_end_stop;
>
> Is this possible after the coresight_build_path() things in setup_aux?
> Might be a better candidate for WARN_*ONCE().
>
>> +
>> +     /* Configure the sink */
>> +     if (sink_ops(sink)->set_buffer(sink, handle,
>> +                                    event_data->snk_config))
>> +             goto fail_end_stop;
>> +
>> +     /* Nothing will happen without a path */
>> +     if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF))
>> +             goto fail_end_stop;
>
> I'd like to understand all the potential failures here, because it's
> really a good idea to keep those to a minimum for the sake of
> consistency. That is, if the user succeeded in creating an event, about
> the only good reason for the event not starting is a filled up buffer.

Enabling a path should fail when one or many components of that path
are already enabled by an ongoing trace session.  This situation is
quite likely to happen since in a lot of design tracers share the link
and sinks.

>
> This is why it makes a lot of sense to keep all the
> coresight_build_path()/coresight_enable_path() to the .event_init()
> phase and let them fail early, if they should fail.

If we do enable enable paths in .event_init() we can't support
multiple concurrent trace session (see explanation above).  The
ultimate design is to have a source directly connected to a sink but
so far none of the coresight topologies I've seen have been wired like
that.

>
> Regards,
> --
> Alex

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

* [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
@ 2016-01-27 18:33       ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-27 18:33 UTC (permalink / raw)
  To: linux-arm-kernel

On 26 January 2016 at 08:27, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>> +static int etm_event_init(struct perf_event *event)
>> +{
>> +     if (event->attr.type != etm_pmu.type)
>> +             return -ENOENT;
>> +
>> +     if (event->cpu >= nr_cpu_ids)
>> +             return -EINVAL;
>
> perf_event_alloc() already does this. Except for this one doesn't cover
> the negative space.

Ack

>
> [snip]
>
>> +static void etm_free_aux(void *data)
>> +{
>> +     struct etm_event_data *event_data = data;
>> +
>> +     pr_err("Queing work\n");
>
> Probably not pr_err().

That's an old debug message - I've removed it already.

>
>> +     schedule_work(&event_data->work);
>> +}
>
> [snip]
>
>> +static void etm_event_start(struct perf_event *event, int flags)
>> +{
>> +     int cpu = smp_processor_id();
>> +     struct etm_event_data *event_data;
>> +     struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
>> +     struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
>> +
>> +     if (!csdev)
>> +             goto fail;
>> +
>> +     /*
>> +      * Deal with the ring buffer API and get a handle on the
>> +      * session's information.
>> +      */
>> +     event_data = perf_aux_output_begin(handle, event);
>> +     if (WARN_ON_ONCE(!event_data))
>> +             goto fail;
>
> There really shouldn't be a warning here. I understand that the 'no
> buffer' case is taped over by the !csdev check above, but there are
> other ligitimate reasons for perf_aux_output_begin() to return NULL,
> like no-space-left.

That's too harsh yes.

>
>> +
>> +     /* We need a sink, no need to continue without one */
>> +     sink = coresight_get_sink(event_data->path[cpu]);
>> +     if (!sink || !sink_ops(sink)->set_buffer)
>> +             goto fail_end_stop;
>
> Is this possible after the coresight_build_path() things in setup_aux?
> Might be a better candidate for WARN_*ONCE().
>
>> +
>> +     /* Configure the sink */
>> +     if (sink_ops(sink)->set_buffer(sink, handle,
>> +                                    event_data->snk_config))
>> +             goto fail_end_stop;
>> +
>> +     /* Nothing will happen without a path */
>> +     if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF))
>> +             goto fail_end_stop;
>
> I'd like to understand all the potential failures here, because it's
> really a good idea to keep those to a minimum for the sake of
> consistency. That is, if the user succeeded in creating an event, about
> the only good reason for the event not starting is a filled up buffer.

Enabling a path should fail when one or many components of that path
are already enabled by an ongoing trace session.  This situation is
quite likely to happen since in a lot of design tracers share the link
and sinks.

>
> This is why it makes a lot of sense to keep all the
> coresight_build_path()/coresight_enable_path() to the .event_init()
> phase and let them fail early, if they should fail.

If we do enable enable paths in .event_init() we can't support
multiple concurrent trace session (see explanation above).  The
ultimate design is to have a source directly connected to a sink but
so far none of the coresight topologies I've seen have been wired like
that.

>
> Regards,
> --
> Alex

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

* Re: [PATCH V8 16/23] coresight: etb10: implementing AUX API
  2016-01-26 15:53     ` Alexander Shishkin
@ 2016-01-27 20:55       ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-27 20:55 UTC (permalink / raw)
  To: Alexander Shishkin
  Cc: linux-arm-kernel, linux-kernel, linux-doc, Chunyan Zhang,
	Mike Leach, Jeremiassen, Tor, Al Grant, Rabin Vincent

On 26 January 2016 at 08:53, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>> Adding an ETB10 specific AUX area operations to be used
>> by the perf framework when events are initialised.
>>
>> Part of this operation involves modeling the mmap'ed area
>> based on the specific ways a sink buffer gathers information.
>
> I don't mind being CC'd on the rest of the patches too, btw. :)

Most definitely.

>
>> +static unsigned long etb_reset_buffer(struct coresight_device *csdev,
>> +                                   struct perf_output_handle *handle,
>> +                                   void *sink_config, bool *lost)
>> +{
>> +     unsigned long size = 0;
>> +     struct cs_buffers *buf = sink_config;
>> +
>> +     if (buf) {
>> +             /*
>> +              * In snapshot mode ->data_size holds the new address of the
>> +              * ring buffer's head.  The size itself is the whole address
>> +              * range since we want the latest information.
>> +              */
>> +             if (buf->snapshot)
>> +                     handle->head = local_xchg(&buf->data_size,
>> +                                               buf->nr_pages << PAGE_SHIFT);
>> +
>> +             /*
>> +              * Tell the tracer PMU how much we got in this run and if
>> +              * something went wrong along the way.  Nobody else can use
>> +              * this cs_buffers instance until we are done.  As such
>> +              * resetting parameters here and squaring off with the ring
>> +              * buffer API in the tracer PMU is fine.
>> +              */
>> +             *lost = local_xchg(&buf->lost, 0);
>
> This is a thin ice, you can't really make assumptions about bool's
> storage size or even type, afaict.

You are theoretically correct but I wonder if the value of &buf->lost
can get to a size where it won't fit in *lost... Nevertheless I'll fix
it with:

*lost = !!local_xchg(&buf->lost, 0);

>
>> +             size = local_xchg(&buf->data_size, 0);
>> +     }
>> +
>> +     return size;
>> +}
>> +
>> +static void etb_update_buffer(struct coresight_device *csdev,
>> +                           struct perf_output_handle *handle,
>> +                           void *sink_config)
>> +{
>> +     int i, cur;
>> +     u8 *buf_ptr;
>> +     u32 read_ptr, write_ptr, capacity;
>> +     u32 status, read_data, to_read;
>> +     unsigned long flags, offset;
>> +     struct cs_buffers *buf = sink_config;
>> +     struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>> +
>> +     if (!buf)
>> +             return;
>> +
>> +     capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS;
>> +
>> +     spin_lock_irqsave(&drvdata->spinlock, flags);
>
> This spinlock seems to be held over the entire readout operation,
> however, I can't find clear rules wrt what structures etc are serialized
> on it. Instead, the comment says "only one at a time pls". Same for
> etm's big drvdata spinlock.

That spinlock is there to serialise actions coming from sysFS.  I
originally added the spinlock to 'etb_update_buffer()' to guard
against reading the internal RAM buffer from sysfs while a perf
session is active.  But after supplementing 'etb_dump()' with 'mode'
awareness this spinlock it is no longer required.

>
> Regards,
> --
> Alex

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

* [PATCH V8 16/23] coresight: etb10: implementing AUX API
@ 2016-01-27 20:55       ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-27 20:55 UTC (permalink / raw)
  To: linux-arm-kernel

On 26 January 2016 at 08:53, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>> Adding an ETB10 specific AUX area operations to be used
>> by the perf framework when events are initialised.
>>
>> Part of this operation involves modeling the mmap'ed area
>> based on the specific ways a sink buffer gathers information.
>
> I don't mind being CC'd on the rest of the patches too, btw. :)

Most definitely.

>
>> +static unsigned long etb_reset_buffer(struct coresight_device *csdev,
>> +                                   struct perf_output_handle *handle,
>> +                                   void *sink_config, bool *lost)
>> +{
>> +     unsigned long size = 0;
>> +     struct cs_buffers *buf = sink_config;
>> +
>> +     if (buf) {
>> +             /*
>> +              * In snapshot mode ->data_size holds the new address of the
>> +              * ring buffer's head.  The size itself is the whole address
>> +              * range since we want the latest information.
>> +              */
>> +             if (buf->snapshot)
>> +                     handle->head = local_xchg(&buf->data_size,
>> +                                               buf->nr_pages << PAGE_SHIFT);
>> +
>> +             /*
>> +              * Tell the tracer PMU how much we got in this run and if
>> +              * something went wrong along the way.  Nobody else can use
>> +              * this cs_buffers instance until we are done.  As such
>> +              * resetting parameters here and squaring off with the ring
>> +              * buffer API in the tracer PMU is fine.
>> +              */
>> +             *lost = local_xchg(&buf->lost, 0);
>
> This is a thin ice, you can't really make assumptions about bool's
> storage size or even type, afaict.

You are theoretically correct but I wonder if the value of &buf->lost
can get to a size where it won't fit in *lost... Nevertheless I'll fix
it with:

*lost = !!local_xchg(&buf->lost, 0);

>
>> +             size = local_xchg(&buf->data_size, 0);
>> +     }
>> +
>> +     return size;
>> +}
>> +
>> +static void etb_update_buffer(struct coresight_device *csdev,
>> +                           struct perf_output_handle *handle,
>> +                           void *sink_config)
>> +{
>> +     int i, cur;
>> +     u8 *buf_ptr;
>> +     u32 read_ptr, write_ptr, capacity;
>> +     u32 status, read_data, to_read;
>> +     unsigned long flags, offset;
>> +     struct cs_buffers *buf = sink_config;
>> +     struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>> +
>> +     if (!buf)
>> +             return;
>> +
>> +     capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS;
>> +
>> +     spin_lock_irqsave(&drvdata->spinlock, flags);
>
> This spinlock seems to be held over the entire readout operation,
> however, I can't find clear rules wrt what structures etc are serialized
> on it. Instead, the comment says "only one at a time pls". Same for
> etm's big drvdata spinlock.

That spinlock is there to serialise actions coming from sysFS.  I
originally added the spinlock to 'etb_update_buffer()' to guard
against reading the internal RAM buffer from sysfs while a perf
session is active.  But after supplementing 'etb_dump()' with 'mode'
awareness this spinlock it is no longer required.

>
> Regards,
> --
> Alex

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

* Re: [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
  2016-01-27 18:33       ` Mathieu Poirier
@ 2016-01-28 15:42         ` Alexander Shishkin
  -1 siblings, 0 replies; 108+ messages in thread
From: Alexander Shishkin @ 2016-01-28 15:42 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: linux-arm-kernel, linux-kernel, linux-doc, Chunyan Zhang,
	Mike Leach, Jeremiassen, Tor, Al Grant, Rabin Vincent

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

>> I'd like to understand all the potential failures here, because it's
>> really a good idea to keep those to a minimum for the sake of
>> consistency. That is, if the user succeeded in creating an event, about
>> the only good reason for the event not starting is a filled up buffer.
>
> Enabling a path should fail when one or many components of that path
> are already enabled by an ongoing trace session.  This situation is
> quite likely to happen since in a lot of design tracers share the link
> and sinks.

Yes, but provided that we don't get interference from sysfs users
(which, I guess, could be blocked out while etm perf events exist), this
part shouldn't fail, as nobody else should be using these links and
sinks but etm events and those are safe from overlapping because of
PERF_PMU_CAP_EXCLUSIVE. Or am I missing something?

>> This is why it makes a lot of sense to keep all the
>> coresight_build_path()/coresight_enable_path() to the .event_init()
>> phase and let them fail early, if they should fail.
>
> If we do enable enable paths in .event_init() we can't support
> multiple concurrent trace session (see explanation above).  The
> ultimate design is to have a source directly connected to a sink but
> so far none of the coresight topologies I've seen have been wired like
> that.

So if we call dibs on those paths early (like event_init early), in such
a way that nobody but other etm events can use them, we should be ok, I
think.

Regards,
--
Alex

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

* [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
@ 2016-01-28 15:42         ` Alexander Shishkin
  0 siblings, 0 replies; 108+ messages in thread
From: Alexander Shishkin @ 2016-01-28 15:42 UTC (permalink / raw)
  To: linux-arm-kernel

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

>> I'd like to understand all the potential failures here, because it's
>> really a good idea to keep those to a minimum for the sake of
>> consistency. That is, if the user succeeded in creating an event, about
>> the only good reason for the event not starting is a filled up buffer.
>
> Enabling a path should fail when one or many components of that path
> are already enabled by an ongoing trace session.  This situation is
> quite likely to happen since in a lot of design tracers share the link
> and sinks.

Yes, but provided that we don't get interference from sysfs users
(which, I guess, could be blocked out while etm perf events exist), this
part shouldn't fail, as nobody else should be using these links and
sinks but etm events and those are safe from overlapping because of
PERF_PMU_CAP_EXCLUSIVE. Or am I missing something?

>> This is why it makes a lot of sense to keep all the
>> coresight_build_path()/coresight_enable_path() to the .event_init()
>> phase and let them fail early, if they should fail.
>
> If we do enable enable paths in .event_init() we can't support
> multiple concurrent trace session (see explanation above).  The
> ultimate design is to have a source directly connected to a sink but
> so far none of the coresight topologies I've seen have been wired like
> that.

So if we call dibs on those paths early (like event_init early), in such
a way that nobody but other etm events can use them, we should be ok, I
think.

Regards,
--
Alex

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

* Re: [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
  2016-01-28 15:42         ` Alexander Shishkin
@ 2016-01-28 21:12           ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-28 21:12 UTC (permalink / raw)
  To: Alexander Shishkin
  Cc: linux-arm-kernel, linux-kernel, linux-doc, Chunyan Zhang,
	Mike Leach, Jeremiassen, Tor, Al Grant, Rabin Vincent

On 28 January 2016 at 08:42, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>>> I'd like to understand all the potential failures here, because it's
>>> really a good idea to keep those to a minimum for the sake of
>>> consistency. That is, if the user succeeded in creating an event, about
>>> the only good reason for the event not starting is a filled up buffer.
>>
>> Enabling a path should fail when one or many components of that path
>> are already enabled by an ongoing trace session.  This situation is
>> quite likely to happen since in a lot of design tracers share the link
>> and sinks.
>
> Yes, but provided that we don't get interference from sysfs users
> (which, I guess, could be blocked out while etm perf events exist), this
> part shouldn't fail, as nobody else should be using these links and
> sinks but etm events and those are safe from overlapping because of
> PERF_PMU_CAP_EXCLUSIVE. Or am I missing something?

Interference from sysfs are being dealt with by drvdata->mode.  As
such a user can't muddle with perf sessions so things are good on that
front.

Reading the comment in [1], mutual exclusion rules aren't that clear
to me.  Perf is aware of the context and CPUs a session is slated for
but has no insight on the path from source to sink.  Tracing an "ls"
command on CPU1 and "uname" on CPU3 might look very distinct to Perf
but can easily share the same plumbing.  If setting
PERF_PMU_CAP_EXCLUSIVE in the PMU definition guarantees that a single
session on that PMU can be active at any given time, then yes, the
only cause of failutre is a buffer full condition.

[1]. http://lxr.free-electrons.com/source/kernel/events/core.c#L3608

>
>>> This is why it makes a lot of sense to keep all the
>>> coresight_build_path()/coresight_enable_path() to the .event_init()
>>> phase and let them fail early, if they should fail.
>>
>> If we do enable enable paths in .event_init() we can't support
>> multiple concurrent trace session (see explanation above).  The
>> ultimate design is to have a source directly connected to a sink but
>> so far none of the coresight topologies I've seen have been wired like
>> that.
>
> So if we call dibs on those paths early (like event_init early), in such
> a way that nobody but other etm events can use them, we should be ok, I
> think.

Elements get out of reach from sysfs as soon as a path is built, so we
are already covered there.

>
> Regards,
> --
> Alex

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

* [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers
@ 2016-01-28 21:12           ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-28 21:12 UTC (permalink / raw)
  To: linux-arm-kernel

On 28 January 2016 at 08:42, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>>> I'd like to understand all the potential failures here, because it's
>>> really a good idea to keep those to a minimum for the sake of
>>> consistency. That is, if the user succeeded in creating an event, about
>>> the only good reason for the event not starting is a filled up buffer.
>>
>> Enabling a path should fail when one or many components of that path
>> are already enabled by an ongoing trace session.  This situation is
>> quite likely to happen since in a lot of design tracers share the link
>> and sinks.
>
> Yes, but provided that we don't get interference from sysfs users
> (which, I guess, could be blocked out while etm perf events exist), this
> part shouldn't fail, as nobody else should be using these links and
> sinks but etm events and those are safe from overlapping because of
> PERF_PMU_CAP_EXCLUSIVE. Or am I missing something?

Interference from sysfs are being dealt with by drvdata->mode.  As
such a user can't muddle with perf sessions so things are good on that
front.

Reading the comment in [1], mutual exclusion rules aren't that clear
to me.  Perf is aware of the context and CPUs a session is slated for
but has no insight on the path from source to sink.  Tracing an "ls"
command on CPU1 and "uname" on CPU3 might look very distinct to Perf
but can easily share the same plumbing.  If setting
PERF_PMU_CAP_EXCLUSIVE in the PMU definition guarantees that a single
session on that PMU can be active at any given time, then yes, the
only cause of failutre is a buffer full condition.

[1]. http://lxr.free-electrons.com/source/kernel/events/core.c#L3608

>
>>> This is why it makes a lot of sense to keep all the
>>> coresight_build_path()/coresight_enable_path() to the .event_init()
>>> phase and let them fail early, if they should fail.
>>
>> If we do enable enable paths in .event_init() we can't support
>> multiple concurrent trace session (see explanation above).  The
>> ultimate design is to have a source directly connected to a sink but
>> so far none of the coresight topologies I've seen have been wired like
>> that.
>
> So if we call dibs on those paths early (like event_init early), in such
> a way that nobody but other etm events can use them, we should be ok, I
> think.

Elements get out of reach from sysfs as soon as a path is built, so we
are already covered there.

>
> Regards,
> --
> Alex

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

* Re: [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
  2016-01-26 14:27         ` Adrian Hunter
@ 2016-01-29 10:14           ` Adrian Hunter
  -1 siblings, 0 replies; 108+ messages in thread
From: Adrian Hunter @ 2016-01-29 10:14 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Mathieu Poirier
  Cc: linux-arm-kernel, Peter Zijlstra, linux-kernel, Ingo Molnar

On 26/01/16 16:27, Adrian Hunter wrote:
> On 25/01/16 23:08, Arnaldo Carvalho de Melo wrote:
>> Em Mon, Jan 25, 2016 at 01:48:28PM -0700, Mathieu Poirier escreveu:
>>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>>> On some architecture the size of the private header may
>>>> be dependent on the number of tracers used in the session.  As
>>>> such adding a "struct perf_evlist *" parameter, which should
>>>> contain all the required information.
>>>>
>>>> Also adjusting the existing client of the interface to take
>>>> the new parameter into account.
>>>>
>>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>>> Cc: Ingo Molnar <mingo@redhat.com>
>>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> ---
>>>>  tools/perf/arch/x86/util/intel-bts.c | 4 +++-
>>>>  tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
>>>>  tools/perf/util/auxtrace.c           | 7 ++++---
>>>>  tools/perf/util/auxtrace.h           | 6 ++++--
>>>>  4 files changed, 14 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
>>>> index 9b94ce520917..4685a40777cc 100644
>>>> --- a/tools/perf/arch/x86/util/intel-bts.c
>>>> +++ b/tools/perf/arch/x86/util/intel-bts.c
>>>> @@ -60,7 +60,9 @@ struct branch {
>>>>         u64 misc;
>>>>  };
>>>>
>>>> -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
>>>> +static size_t
>>>> +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>>> +                        struct perf_evlist *evlist __maybe_unused)
>>>>  {
>>>>         return INTEL_BTS_AUXTRACE_PRIV_SIZE;
>>>>  }
>>>> diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
>>>> index b02af064f0f9..e5c1f2e21f87 100644
>>>> --- a/tools/perf/arch/x86/util/intel-pt.c
>>>> +++ b/tools/perf/arch/x86/util/intel-pt.c
>>>> @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
>>>>         return attr;
>>>>  }
>>>>
>>>> -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
>>>> +static size_t
>>>> +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>>> +                       struct perf_evlist *evlist __maybe_unused)
>>>>  {
>>>>         return INTEL_PT_AUXTRACE_PRIV_SIZE;
>>>>  }
>>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>>>> index 7f10430af39c..cc1c9ce5cc56 100644
>>>> --- a/tools/perf/util/auxtrace.c
>>>> +++ b/tools/perf/util/auxtrace.c
>>>> @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
>>>>                          heap_array[last].ordinal);
>>>>  }
>>>>
>>>> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
>>>> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
>>>> +                                      struct perf_evlist *evlist)
>>>>  {
>>>>         if (itr)
>>>> -               return itr->info_priv_size(itr);
>>>> +               return itr->info_priv_size(itr, evlist);
>>>>         return 0;
>>>>  }
>>>>
>>>> @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
>>>>         int err;
>>>>
>>>>         pr_debug2("Synthesizing auxtrace information\n");
>>>> -       priv_size = auxtrace_record__info_priv_size(itr);
>>>> +       priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
>>>>         ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
>>>>         if (!ev)
>>>>                 return -ENOMEM;
>>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>>>> index b86f90db1352..e5a8e2d4f2af 100644
>>>> --- a/tools/perf/util/auxtrace.h
>>>> +++ b/tools/perf/util/auxtrace.h
>>>> @@ -293,7 +293,8 @@ struct auxtrace_record {
>>>>         int (*recording_options)(struct auxtrace_record *itr,
>>>>                                  struct perf_evlist *evlist,
>>>>                                  struct record_opts *opts);
>>>> -       size_t (*info_priv_size)(struct auxtrace_record *itr);
>>>> +       size_t (*info_priv_size)(struct auxtrace_record *itr,
>>>> +                                struct perf_evlist *evlist);
>>>>         int (*info_fill)(struct auxtrace_record *itr,
>>>>                          struct perf_session *session,
>>>>                          struct auxtrace_info_event *auxtrace_info,
>>>> @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
>>>>  int auxtrace_record__options(struct auxtrace_record *itr,
>>>>                              struct perf_evlist *evlist,
>>>>                              struct record_opts *opts);
>>>> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
>>>> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
>>>> +                                      struct perf_evlist *evlist);
>>>>  int auxtrace_record__info_fill(struct auxtrace_record *itr,
>>>>                                struct perf_session *session,
>>>>                                struct auxtrace_info_event *auxtrace_info,
>>>> --
>>>> 2.1.4
>>>>
>>>
>>> Arnaldo,
>>>
>>> As with my previous email, I can't queue this patch for 4.6 without at
>>> least a reviewed by from you.
>>>
>>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>>> be happy to use another baseline should that be more adequate for you.
>>
>> Adrian, are you ok with this?
> 
> I will try to look at this and the others in the next day or two.

Acked-by: Adrian Hunter <adrian.hunter@intel.com>

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

* [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size()
@ 2016-01-29 10:14           ` Adrian Hunter
  0 siblings, 0 replies; 108+ messages in thread
From: Adrian Hunter @ 2016-01-29 10:14 UTC (permalink / raw)
  To: linux-arm-kernel

On 26/01/16 16:27, Adrian Hunter wrote:
> On 25/01/16 23:08, Arnaldo Carvalho de Melo wrote:
>> Em Mon, Jan 25, 2016 at 01:48:28PM -0700, Mathieu Poirier escreveu:
>>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>>> On some architecture the size of the private header may
>>>> be dependent on the number of tracers used in the session.  As
>>>> such adding a "struct perf_evlist *" parameter, which should
>>>> contain all the required information.
>>>>
>>>> Also adjusting the existing client of the interface to take
>>>> the new parameter into account.
>>>>
>>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>>> Cc: Ingo Molnar <mingo@redhat.com>
>>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> ---
>>>>  tools/perf/arch/x86/util/intel-bts.c | 4 +++-
>>>>  tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
>>>>  tools/perf/util/auxtrace.c           | 7 ++++---
>>>>  tools/perf/util/auxtrace.h           | 6 ++++--
>>>>  4 files changed, 14 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
>>>> index 9b94ce520917..4685a40777cc 100644
>>>> --- a/tools/perf/arch/x86/util/intel-bts.c
>>>> +++ b/tools/perf/arch/x86/util/intel-bts.c
>>>> @@ -60,7 +60,9 @@ struct branch {
>>>>         u64 misc;
>>>>  };
>>>>
>>>> -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
>>>> +static size_t
>>>> +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>>> +                        struct perf_evlist *evlist __maybe_unused)
>>>>  {
>>>>         return INTEL_BTS_AUXTRACE_PRIV_SIZE;
>>>>  }
>>>> diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
>>>> index b02af064f0f9..e5c1f2e21f87 100644
>>>> --- a/tools/perf/arch/x86/util/intel-pt.c
>>>> +++ b/tools/perf/arch/x86/util/intel-pt.c
>>>> @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
>>>>         return attr;
>>>>  }
>>>>
>>>> -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
>>>> +static size_t
>>>> +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>>> +                       struct perf_evlist *evlist __maybe_unused)
>>>>  {
>>>>         return INTEL_PT_AUXTRACE_PRIV_SIZE;
>>>>  }
>>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>>>> index 7f10430af39c..cc1c9ce5cc56 100644
>>>> --- a/tools/perf/util/auxtrace.c
>>>> +++ b/tools/perf/util/auxtrace.c
>>>> @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
>>>>                          heap_array[last].ordinal);
>>>>  }
>>>>
>>>> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
>>>> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
>>>> +                                      struct perf_evlist *evlist)
>>>>  {
>>>>         if (itr)
>>>> -               return itr->info_priv_size(itr);
>>>> +               return itr->info_priv_size(itr, evlist);
>>>>         return 0;
>>>>  }
>>>>
>>>> @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
>>>>         int err;
>>>>
>>>>         pr_debug2("Synthesizing auxtrace information\n");
>>>> -       priv_size = auxtrace_record__info_priv_size(itr);
>>>> +       priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
>>>>         ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
>>>>         if (!ev)
>>>>                 return -ENOMEM;
>>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>>>> index b86f90db1352..e5a8e2d4f2af 100644
>>>> --- a/tools/perf/util/auxtrace.h
>>>> +++ b/tools/perf/util/auxtrace.h
>>>> @@ -293,7 +293,8 @@ struct auxtrace_record {
>>>>         int (*recording_options)(struct auxtrace_record *itr,
>>>>                                  struct perf_evlist *evlist,
>>>>                                  struct record_opts *opts);
>>>> -       size_t (*info_priv_size)(struct auxtrace_record *itr);
>>>> +       size_t (*info_priv_size)(struct auxtrace_record *itr,
>>>> +                                struct perf_evlist *evlist);
>>>>         int (*info_fill)(struct auxtrace_record *itr,
>>>>                          struct perf_session *session,
>>>>                          struct auxtrace_info_event *auxtrace_info,
>>>> @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
>>>>  int auxtrace_record__options(struct auxtrace_record *itr,
>>>>                              struct perf_evlist *evlist,
>>>>                              struct record_opts *opts);
>>>> -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
>>>> +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
>>>> +                                      struct perf_evlist *evlist);
>>>>  int auxtrace_record__info_fill(struct auxtrace_record *itr,
>>>>                                struct perf_session *session,
>>>>                                struct auxtrace_info_event *auxtrace_info,
>>>> --
>>>> 2.1.4
>>>>
>>>
>>> Arnaldo,
>>>
>>> As with my previous email, I can't queue this patch for 4.6 without at
>>> least a reviewed by from you.
>>>
>>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>>> be happy to use another baseline should that be more adequate for you.
>>
>> Adrian, are you ok with this?
> 
> I will try to look at this and the others in the next day or two.

Acked-by: Adrian Hunter <adrian.hunter@intel.com>

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

* Re: [PATCH V8 22/23] perf tools: making coresight PMU listable
  2016-01-25 21:10       ` Arnaldo Carvalho de Melo
@ 2016-01-29 10:24         ` Adrian Hunter
  -1 siblings, 0 replies; 108+ messages in thread
From: Adrian Hunter @ 2016-01-29 10:24 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Mathieu Poirier
  Cc: linux-arm-kernel, linux-kernel, Peter Zijlstra, Ingo Molnar

On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
> Em Mon, Jan 25, 2016 at 01:49:47PM -0700, Mathieu Poirier escreveu:
>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>> Adding the required mechanic allowing 'perf list pmu' to
>>> discover coresight ETM/PTM tracers.
>>>
>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>> Cc: Ingo Molnar <mingo@redhat.com>
>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> ---
>>>  MAINTAINERS                    |  1 +
>>>  tools/perf/arch/arm/util/Build |  2 ++
>>>  tools/perf/arch/arm/util/pmu.c | 34 ++++++++++++++++++++++++++++++++++
>>>  tools/perf/config/Makefile     | 17 ++++++++++++-----
>>>  4 files changed, 49 insertions(+), 5 deletions(-)
>>>  create mode 100644 tools/perf/arch/arm/util/pmu.c
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 233f83464814..b2a92245eece 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -1007,6 +1007,7 @@ F:        drivers/hwtracing/coresight/*
>>>  F:     Documentation/trace/coresight.txt
>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>>> +F:     tools/perf/arch/arm/util/pmu.c
>>>
>>>  ARM/CORGI MACHINE SUPPORT
>>>  M:     Richard Purdie <rpurdie@rpsys.net>
>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
>>> index d22e3d07de3d..66ab0b05549c 100644
>>> --- a/tools/perf/arch/arm/util/Build
>>> +++ b/tools/perf/arch/arm/util/Build
>>> @@ -2,3 +2,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>>>
>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>>> +
>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o
>>> diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c
>>> new file mode 100644
>>> index 000000000000..af9fb666b44f
>>> --- /dev/null
>>> +++ b/tools/perf/arch/arm/util/pmu.c
>>> @@ -0,0 +1,34 @@
>>> +/*
>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <string.h>
>>> +#include <linux/coresight-pmu.h>

Probably need to add include/linux/coresight-pmu.h it to tools/perf/MANIFEST

>>> +#include <linux/perf_event.h>
>>> +
>>> +#include "../../util/pmu.h"
>>> +
>>> +struct perf_event_attr
>>> +*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
>>> +{
>>> +#ifdef HAVE_AUXTRACE_SUPPORT
>>> +       if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
>>> +               /* add ETM default config here */
>>> +               pmu->selectable = true;
>>> +       }
>>> +#endif
>>> +       return NULL;
>>> +}
>>> diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
>>> index de89ec574361..905faabff8b1 100644
>>> --- a/tools/perf/config/Makefile
>>> +++ b/tools/perf/config/Makefile
>>> @@ -647,12 +647,19 @@ ifdef LIBBABELTRACE
>>>  endif
>>>
>>>  ifndef NO_AUXTRACE
>>> -  ifeq ($(feature-get_cpuid), 0)
>>> -    msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
>>> -    NO_AUXTRACE := 1
>>> +  ifeq ($(ARCH),x86)
>>> +    ifeq ($(feature-get_cpuid), 0)
>>> +      msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
>>> +      NO_AUXTRACE := 1
>>> +    else
>>> +      $(call detected,CONFIG_AUXTRACE)
>>> +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
>>> +    endif
>>>    else
>>> -    $(call detected,CONFIG_AUXTRACE)
>>> -    CFLAGS += -DHAVE_AUXTRACE_SUPPORT
>>> +    ifeq ($(ARCH),$(filter $(ARCH), arm arm64))
>>> +      $(call detected,CONFIG_AUXTRACE)
>>> +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
>>> +    endif

We still want AUXTRACE support by default, to support being able to decode a
trace from one machine on any other machine, so this should not exclude
HAVE_AUXTRACE_SUPPORT from every arch except x86 and arm.

>>>    endif
>>>  endif
>>>
>>> --
>>> 2.1.4
>>>
>>
>> Arnaldo,
>>
>> This is the third (of four) patch that I can't queue without a
>> reviewed by from you.
>>
>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>> be happy to use another baseline should that be more adequate for you.
> 
> Adrian,
> 
> 	Are you ok with this?

See comments

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

* [PATCH V8 22/23] perf tools: making coresight PMU listable
@ 2016-01-29 10:24         ` Adrian Hunter
  0 siblings, 0 replies; 108+ messages in thread
From: Adrian Hunter @ 2016-01-29 10:24 UTC (permalink / raw)
  To: linux-arm-kernel

On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
> Em Mon, Jan 25, 2016 at 01:49:47PM -0700, Mathieu Poirier escreveu:
>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>> Adding the required mechanic allowing 'perf list pmu' to
>>> discover coresight ETM/PTM tracers.
>>>
>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>> Cc: Ingo Molnar <mingo@redhat.com>
>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> ---
>>>  MAINTAINERS                    |  1 +
>>>  tools/perf/arch/arm/util/Build |  2 ++
>>>  tools/perf/arch/arm/util/pmu.c | 34 ++++++++++++++++++++++++++++++++++
>>>  tools/perf/config/Makefile     | 17 ++++++++++++-----
>>>  4 files changed, 49 insertions(+), 5 deletions(-)
>>>  create mode 100644 tools/perf/arch/arm/util/pmu.c
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 233f83464814..b2a92245eece 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -1007,6 +1007,7 @@ F:        drivers/hwtracing/coresight/*
>>>  F:     Documentation/trace/coresight.txt
>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>>> +F:     tools/perf/arch/arm/util/pmu.c
>>>
>>>  ARM/CORGI MACHINE SUPPORT
>>>  M:     Richard Purdie <rpurdie@rpsys.net>
>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
>>> index d22e3d07de3d..66ab0b05549c 100644
>>> --- a/tools/perf/arch/arm/util/Build
>>> +++ b/tools/perf/arch/arm/util/Build
>>> @@ -2,3 +2,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>>>
>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>>> +
>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o
>>> diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c
>>> new file mode 100644
>>> index 000000000000..af9fb666b44f
>>> --- /dev/null
>>> +++ b/tools/perf/arch/arm/util/pmu.c
>>> @@ -0,0 +1,34 @@
>>> +/*
>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <string.h>
>>> +#include <linux/coresight-pmu.h>

Probably need to add include/linux/coresight-pmu.h it to tools/perf/MANIFEST

>>> +#include <linux/perf_event.h>
>>> +
>>> +#include "../../util/pmu.h"
>>> +
>>> +struct perf_event_attr
>>> +*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
>>> +{
>>> +#ifdef HAVE_AUXTRACE_SUPPORT
>>> +       if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
>>> +               /* add ETM default config here */
>>> +               pmu->selectable = true;
>>> +       }
>>> +#endif
>>> +       return NULL;
>>> +}
>>> diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
>>> index de89ec574361..905faabff8b1 100644
>>> --- a/tools/perf/config/Makefile
>>> +++ b/tools/perf/config/Makefile
>>> @@ -647,12 +647,19 @@ ifdef LIBBABELTRACE
>>>  endif
>>>
>>>  ifndef NO_AUXTRACE
>>> -  ifeq ($(feature-get_cpuid), 0)
>>> -    msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
>>> -    NO_AUXTRACE := 1
>>> +  ifeq ($(ARCH),x86)
>>> +    ifeq ($(feature-get_cpuid), 0)
>>> +      msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
>>> +      NO_AUXTRACE := 1
>>> +    else
>>> +      $(call detected,CONFIG_AUXTRACE)
>>> +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
>>> +    endif
>>>    else
>>> -    $(call detected,CONFIG_AUXTRACE)
>>> -    CFLAGS += -DHAVE_AUXTRACE_SUPPORT
>>> +    ifeq ($(ARCH),$(filter $(ARCH), arm arm64))
>>> +      $(call detected,CONFIG_AUXTRACE)
>>> +      CFLAGS += -DHAVE_AUXTRACE_SUPPORT
>>> +    endif

We still want AUXTRACE support by default, to support being able to decode a
trace from one machine on any other machine, so this should not exclude
HAVE_AUXTRACE_SUPPORT from every arch except x86 and arm.

>>>    endif
>>>  endif
>>>
>>> --
>>> 2.1.4
>>>
>>
>> Arnaldo,
>>
>> This is the third (of four) patch that I can't queue without a
>> reviewed by from you.
>>
>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>> be happy to use another baseline should that be more adequate for you.
> 
> Adrian,
> 
> 	Are you ok with this?

See comments

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

* Re: [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-01-25 21:10       ` Arnaldo Carvalho de Melo
@ 2016-01-29 10:34         ` Adrian Hunter
  -1 siblings, 0 replies; 108+ messages in thread
From: Adrian Hunter @ 2016-01-29 10:34 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Mathieu Poirier
  Cc: linux-kernel, linux-arm-kernel, Peter Zijlstra, Ingo Molnar

On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
> Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>> Coresight ETMs are IP blocks used to perform HW assisted tracing
>>> on a CPU core.  This patch introduce the required auxiliary API
>>> functions allowing the perf core to interact with a tracer.
>>>
>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>> Cc: Ingo Molnar <mingo@redhat.com>
>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> ---
>>>  MAINTAINERS                         |   3 +
>>>  tools/perf/arch/arm/util/Build      |   2 +-
>>>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
>>>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
>>>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
>>>  tools/perf/util/auxtrace.c          |   1 +
>>>  tools/perf/util/auxtrace.h          |   1 +
>>>  7 files changed, 570 insertions(+), 1 deletion(-)
>>>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index b2a92245eece..a81b2737ebc3 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>>>  F:     tools/perf/arch/arm/util/pmu.c
>>> +F:     tools/perf/arch/arm/util/auxtrace.c
>>> +F:     tools/perf/arch/arm/util/cs_etm.c
>>> +F:     tools/perf/arch/arm/util/cs_etm.h
>>>
>>>  ARM/CORGI MACHINE SUPPORT
>>>  M:     Richard Purdie <rpurdie@rpsys.net>
>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
>>> index 66ab0b05549c..0a25a1248f42 100644
>>> --- a/tools/perf/arch/arm/util/Build
>>> +++ b/tools/perf/arch/arm/util/Build
>>> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>>>
>>> -libperf-$(CONFIG_AUXTRACE) += pmu.o
>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
>>> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
>>> new file mode 100644
>>> index 000000000000..d327316f0e8a
>>> --- /dev/null
>>> +++ b/tools/perf/arch/arm/util/auxtrace.c
>>> @@ -0,0 +1,54 @@
>>> +/*
>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <stdbool.h>
>>> +#include <linux/coresight-pmu.h>
>>> +
>>> +#include "../../util/auxtrace.h"
>>> +#include "../../util/evlist.h"
>>> +#include "../../util/pmu.h"
>>> +#include "cs_etm.h"
>>> +
>>> +struct auxtrace_record
>>> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
>>> +{
>>> +       struct perf_pmu *cs_etm_pmu;
>>> +       struct perf_evsel *evsel;
>>> +       bool found_etm = false;
>>> +
>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>>> +
>>> +        if (evlist) {
>>> +               evlist__for_each(evlist, evsel) {
>>> +                       if (cs_etm_pmu &&
>>> +                           evsel->attr.type == cs_etm_pmu->type)
>>> +                               found_etm = true;
>>> +               }
>>> +       }
>>> +
>>> +       if (found_etm)
>>> +               return cs_etm_record_init(err);
>>> +
>>> +       /*
>>> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
>>> +        * record can still be used even if tracers aren't present.  The NULL
>>> +        * return value will take care of telling the infrastructure HW tracing
>>> +        * isn't available.
>>> +        */
>>> +       *err = 0;
>>> +       return NULL;
>>> +}
>>> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
>>> new file mode 100644
>>> index 000000000000..5710b90e23d5
>>> --- /dev/null
>>> +++ b/tools/perf/arch/arm/util/cs_etm.c
>>> @@ -0,0 +1,466 @@
>>> +/*
>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <api/fs/fs.h>
>>> +#include <linux/bitops.h>
>>> +#include <linux/coresight-pmu.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/log2.h>
>>> +#include <linux/types.h>
>>> +
>>> +#include "../../perf.h"
>>> +#include "../../util/auxtrace.h"
>>> +#include "../../util/cpumap.h"
>>> +#include "../../util/evlist.h"
>>> +#include "../../util/pmu.h"
>>> +#include "../../util/thread_map.h"
>>> +#include "cs_etm.h"
>>> +
>>> +#include <stdlib.h>
>>> +
>>> +#define KiB(x) ((x) * 1024)
>>> +#define MiB(x) ((x) * 1024 * 1024)
>>> +
>>> +struct cs_etm_recording {
>>> +       struct auxtrace_record  itr;
>>> +       struct perf_pmu         *cs_etm_pmu;
>>> +       struct perf_evlist      *evlist;
>>> +       bool                    snapshot_mode;
>>> +       size_t                  snapshot_size;
>>> +};
>>> +
>>> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
>>> +                                        struct record_opts *opts,
>>> +                                        const char *str)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                               container_of(itr, struct cs_etm_recording, itr);
>>> +       unsigned long long snapshot_size = 0;
>>> +       char *endptr;
>>> +
>>> +       if (str) {
>>> +               snapshot_size = strtoull(str, &endptr, 0);
>>> +               if (*endptr || snapshot_size > SIZE_MAX)
>>> +                       return -1;
>>> +       }
>>> +
>>> +       opts->auxtrace_snapshot_mode = true;
>>> +       opts->auxtrace_snapshot_size = snapshot_size;
>>> +       ptr->snapshot_size = snapshot_size;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int cs_etm_recording_options(struct auxtrace_record *itr,
>>> +                                   struct perf_evlist *evlist,
>>> +                                   struct record_opts *opts)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                               container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
>>> +       const struct cpu_map *cpus = evlist->cpus;
>>> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
>>> +
>>> +       ptr->evlist = evlist;
>>> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
>>> +
>>> +       evlist__for_each(evlist, evsel) {
>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>>> +                       if (cs_etm_evsel) {
>>> +                               pr_err("There may be only one %s event\n",
>>> +                                      CORESIGHT_ETM_PMU_NAME);
>>> +                               return -EINVAL;
>>> +                       }
>>> +                       evsel->attr.freq = 0;
>>> +                       evsel->attr.sample_period = 1;
>>> +                       cs_etm_evsel = evsel;
>>> +                       opts->full_auxtrace = true;
>>> +               }
>>> +       }
>>> +
>>> +       /* no need to continue if at least one event of interest was found */
>>> +       if (!cs_etm_evsel)
>>> +               return 0;
>>> +
>>> +       if (opts->use_clockid) {
>>> +               pr_err("Cannot use clockid (-k option) with %s\n",
>>> +                      CORESIGHT_ETM_PMU_NAME);
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       /* we are in snapshot mode */
>>> +       if (opts->auxtrace_snapshot_mode) {
>>> +               /*
>>> +                * No size were given to '-S' or '-m,', so go with
>>> +                * the default
>>> +                */
>>> +               if (!opts->auxtrace_snapshot_size &&
>>> +                   !opts->auxtrace_mmap_pages) {
>>> +                       if (privileged) {
>>> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
>>> +                       } else {
>>> +                               opts->auxtrace_mmap_pages =
>>> +                                                       KiB(128) / page_size;
>>> +                               if (opts->mmap_pages == UINT_MAX)
>>> +                                       opts->mmap_pages = KiB(256) / page_size;
>>> +                       }
>>> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
>>> +                                               opts->mmap_pages == UINT_MAX) {
>>> +                       opts->mmap_pages = KiB(256) / page_size;
>>> +               }
>>> +
>>> +               /*
>>> +                * '-m,xyz' was specified but no snapshot size, so make the
>>> +                * snapshot size as big as the auxtrace mmap area.
>>> +                */
>>> +               if (!opts->auxtrace_snapshot_size) {
>>> +                       opts->auxtrace_snapshot_size =
>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
>>> +               }
>>> +
>>> +               /*
>>> +                * -Sxyz was specified but no auxtrace mmap area, so make the
>>> +                * auxtrace mmap area big enough to fit the requested snapshot
>>> +                * size.
>>> +                */
>>> +               if (!opts->auxtrace_mmap_pages) {
>>> +                       size_t sz = opts->auxtrace_snapshot_size;
>>> +
>>> +                       sz = round_up(sz, page_size) / page_size;
>>> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
>>> +               }
>>> +
>>> +               /* Snapshost size can't be bigger than the auxtrace area */
>>> +               if (opts->auxtrace_snapshot_size >
>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
>>> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
>>> +                              opts->auxtrace_snapshot_size,
>>> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
>>> +                       return -EINVAL;
>>> +               }
>>> +
>>> +               /* Something went wrong somewhere - this shouldn't happen */
>>> +               if (!opts->auxtrace_snapshot_size ||
>>> +                   !opts->auxtrace_mmap_pages) {
>>> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
>>> +                       return -EINVAL;
>>> +               }
>>> +       }
>>> +
>>> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
>>> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
>>> +               if (privileged) {
>>> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
>>> +               } else {
>>> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
>>> +                       if (opts->mmap_pages == UINT_MAX)
>>> +                               opts->mmap_pages = KiB(256) / page_size;
>>> +               }
>>> +
>>> +       }
>>> +
>>> +       /* Validate auxtrace_mmap_pages provided by user */
>>> +       if (opts->auxtrace_mmap_pages) {
>>> +               unsigned int max_page = (KiB(128) / page_size);
>>> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
>>> +
>>> +               if (!privileged &&
>>> +                   opts->auxtrace_mmap_pages > max_page) {
>>> +                       opts->auxtrace_mmap_pages = max_page;
>>> +                       pr_err("auxtrace too big, truncating to %d\n",
>>> +                              max_page);
>>> +               }
>>> +
>>> +               if (!is_power_of_2(sz)) {
>>> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
>>> +                              CORESIGHT_ETM_PMU_NAME);
>>> +                       return -EINVAL;
>>> +               }
>>> +       }
>>> +
>>> +       if (opts->auxtrace_snapshot_mode)
>>> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
>>> +                         opts->auxtrace_snapshot_size);
>>> +
>>> +       if (cs_etm_evsel) {
>>> +               /*
>>> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
>>> +                * event must come first.
>>> +                */
>>> +               perf_evlist__to_front(evlist, cs_etm_evsel);
>>> +               /*
>>> +                * In the case of per-cpu mmaps, we need the CPU on the
>>> +                * AUX event.
>>> +                */
>>> +               if (!cpu_map__empty(cpus))
>>> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
>>> +       }
>>> +
>>> +       /* Add dummy event to keep tracking */
>>> +       if (opts->full_auxtrace) {
>>> +               struct perf_evsel *tracking_evsel;
>>> +               int err;
>>> +
>>> +               err = parse_events(evlist, "dummy:u", NULL);
>>> +               if (err)
>>> +                       return err;
>>> +
>>> +               tracking_evsel = perf_evlist__last(evlist);
>>> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
>>> +
>>> +               tracking_evsel->attr.freq = 0;
>>> +               tracking_evsel->attr.sample_period = 1;
>>> +
>>> +               /* In per-cpu case, always need the time of mmap events etc */
>>> +               if (!cpu_map__empty(cpus))
>>> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
>>> +{
>>> +       u64 config = 0;
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>> +       struct perf_evlist *evlist = ptr->evlist;
>>> +       struct perf_evsel *evsel;
>>> +
>>> +       evlist__for_each(evlist, evsel) {
>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>>> +                       /*
>>> +                        * Variable perf_event_attr::config is assigned to
>>> +                        * ETMv3/PTM.  The bit fields have been made to match
>>> +                        * the ETMv3.5 ETRMCR register specification.  See the
>>> +                        * PMU_FORMAT_ATTR() declarations in
>>> +                        * drivers/hwtracing/coresight/coresight-perf.c for
>>> +                        * details.
>>> +                        */
>>> +                       config = evsel->attr.config;
>>> +                       break;
>>> +               }
>>> +       }
>>> +
>>> +       return config;
>>> +}
>>> +
>>> +static size_t
>>> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>> +                     struct perf_evlist *evlist __maybe_unused)
>>> +{
>>> +       int records;
>>> +       const struct cpu_map *cpus = evlist->cpus;
>>> +
>>> +       if (!cpu_map__empty(cpus)) {
>>> +               records = cpu_map__nr(cpus);
>>> +               goto out;
>>> +       }
>>> +
>>> +       set_max_cpu_num();
>>> +       records = cpu__max_cpu();
>>> +out:
>>> +       return records * CS_ETM_PRIV_SIZE;
>>> +}
>>> +
>>> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
>>> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
>>> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
>>> +};
>>> +
>>> +static int cs_etm_get_metadata(int cpu, int index,
>>> +                              struct auxtrace_record *itr,
>>> +                              struct auxtrace_info_event *info)
>>> +{
>>> +       char path[PATH_MAX];
>>> +       int offset = 0, ret = 0;
>>> +       int i, scan;
>>> +       unsigned int val;
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>> +
>>> +       offset = index * CS_ETM_PRIV_MAX;
>>> +
>>> +       /* Build generic header portion */
>>> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
>>> +       info->priv[offset + CS_ETM_CPU] = cpu;
>>> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
>>> +
>>> +       /* Get user configurables from the session */
>>> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
>>> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
>>> +
>>> +       /* Get RO metadata from sysfs */
>>> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
>>> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
>>> +
>>> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
>>> +               if (scan != 1) {
>>> +                       ret = -EINVAL;
>>> +                       break;
>>> +               }
>>> +
>>> +               info->priv[offset + i] = val;
>>> +       }
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +static int cs_etm_info_fill(struct auxtrace_record *itr,
>>> +                           struct perf_session *session,
>>> +                           struct auxtrace_info_event *auxtrace_info,
>>> +                           size_t priv_size)
>>> +{
>>> +       int i, nr_cpu, ret = 0;
>>> +       const struct cpu_map *cpus = session->evlist->cpus;
>>> +
>>> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
>>> +               return -EINVAL;
>>> +
>>> +       if (!session->evlist->nr_mmaps)
>>> +               return -EINVAL;
>>> +
>>> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
>>> +
>>> +       /* cpu map is not empty, we have specific CPUs to work with */
>>> +       if (!cpu_map__empty(cpus)) {
>>> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
>>> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
>>> +                                                 itr, auxtrace_info);
>>> +                       if (ret)
>>> +                               goto out;
>>> +               }
>>> +       } else {
>>> +               /* get configuration for all CPUs in the system */
>>> +               nr_cpu = cpu__max_cpu();
>>> +               for (i = 0; i < nr_cpu; i++) {
>>> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
>>> +                       if (ret)
>>> +                               goto out;
>>> +               }
>>> +       }
>>> +
>>> +out:
>>> +       return ret;
>>> +}
>>> +
>>> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
>>> +                               int idx, struct auxtrace_mmap *mm,
>>> +                               unsigned char *data __maybe_unused,
>>> +                               u64 *head, u64 *old)
>>> +{
>>> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
>>> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
>>> +
>>> +       *old = *head;
>>> +       *head += mm->len;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_evsel *evsel;
>>> +
>>> +       evlist__for_each(ptr->evlist, evsel) {
>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
>>> +       }
>>> +       return -EINVAL;
>>> +}
>>> +
>>> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_evsel *evsel;
>>> +
>>> +       evlist__for_each(ptr->evlist, evsel) {
>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
>>> +       }
>>> +       return -EINVAL;
>>> +}
>>> +
>>> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
>>> +{
>>> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
>>> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
>>> +}
>>> +
>>> +static void cs_etm_recording_free(struct auxtrace_record *itr)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       free(ptr);
>>> +}
>>> +
>>> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_evsel *evsel;
>>> +
>>> +       evlist__for_each(ptr->evlist, evsel) {
>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>> +                       return perf_evlist__enable_event_idx(ptr->evlist,
>>> +                                                            evsel, idx);
>>> +       }
>>> +
>>> +       return -EINVAL;
>>> +}
>>> +
>>> +struct auxtrace_record *cs_etm_record_init(int *err)
>>> +{
>>> +       struct perf_pmu *cs_etm_pmu;
>>> +       struct cs_etm_recording *ptr;
>>> +
>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>>> +
>>> +       if (!cs_etm_pmu) {
>>> +               *err = -EINVAL;
>>> +               goto out;
>>> +       }
>>> +
>>> +       ptr = zalloc(sizeof(struct cs_etm_recording));
>>> +       if (!ptr) {
>>> +               *err = -ENOMEM;
>>> +               goto out;
>>> +       }
>>> +
>>> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
>>> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
>>> +       ptr->itr.recording_options      = cs_etm_recording_options;
>>> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
>>> +       ptr->itr.info_fill              = cs_etm_info_fill;
>>> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
>>> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
>>> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
>>> +       ptr->itr.reference              = cs_etm_reference;
>>> +       ptr->itr.free                   = cs_etm_recording_free;
>>> +       ptr->itr.read_finish            = cs_etm_read_finish;
>>> +
>>> +       *err = 0;
>>> +       return &ptr->itr;
>>> +out:
>>> +       return NULL;
>>> +}
>>> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
>>> new file mode 100644
>>> index 000000000000..7e85c1b43598
>>> --- /dev/null
>>> +++ b/tools/perf/arch/arm/util/cs_etm.h
>>> @@ -0,0 +1,44 @@
>>> +/*
>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#ifndef INCLUDE__PERF_CS_ETM_H__
>>> +#define INCLUDE__PERF_CS_ETM_H__
>>> +
>>> +/* Beginning of header common to both ETMv3 and V4 */
>>> +enum {
>>> +       CS_ETM_MAGIC,
>>> +       CS_ETM_CPU,
>>> +       CS_ETM_SNAPSHOT,
>>> +};
>>> +
>>> +/* ETMv3/PTM metadata */
>>> +enum {
>>> +       /* Dynamic, configurable parameters */
>>> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
>>> +       CS_ETM_ETMTRACEIDR,
>>> +       /* RO, taken from sysFS */
>>> +       CS_ETM_ETMCCER,
>>> +       CS_ETM_ETMIDR,
>>> +       CS_ETM_PRIV_MAX,
>>> +};
>>> +
>>> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
>>> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
>>> +
>>> +struct auxtrace_record *cs_etm_record_init(int *err);
>>> +
>>> +#endif
>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>>> index cc1c9ce5cc56..a6f291dbc4d9 100644
>>> --- a/tools/perf/util/auxtrace.c
>>> +++ b/tools/perf/util/auxtrace.c
>>> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
>>>                 return intel_pt_process_auxtrace_info(event, session);
>>>         case PERF_AUXTRACE_INTEL_BTS:
>>>                 return intel_bts_process_auxtrace_info(event, session);
>>> +       case PERF_AUXTRACE_CS_ETM:
>>>         case PERF_AUXTRACE_UNKNOWN:
>>>         default:
>>>                 return -EINVAL;
>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>>> index e5a8e2d4f2af..adb53e7bcabf 100644
>>> --- a/tools/perf/util/auxtrace.h
>>> +++ b/tools/perf/util/auxtrace.h
>>> @@ -41,6 +41,7 @@ enum auxtrace_type {
>>>         PERF_AUXTRACE_UNKNOWN,
>>>         PERF_AUXTRACE_INTEL_PT,
>>>         PERF_AUXTRACE_INTEL_BTS,
>>> +       PERF_AUXTRACE_CS_ETM,
>>>  };
>>>
>>>  enum itrace_period_type {
>>> --
>>> 2.1.4
>>>
>>
>> Arnaldo,
>>
>> Last but not least, this is the final patch that I would like you to
>> review before queing.
>>
>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>> be happy to use another baseline should that be more adequate for you.
> 
> Adrian,
> 
> 	One more, are you ok with this?

Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
mentioned that on another patch.

However there is no decoder, which begs the question, is there anything you
can actually do with the perf.data file?  Might be a bit confusing for users
if they can capture traces but not use perf tools on the resulting perf.data
file?

Nevertheless, for what is there now:

Acked-by: Adrian Hunter <adrian.hunter@intel.com>

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-01-29 10:34         ` Adrian Hunter
  0 siblings, 0 replies; 108+ messages in thread
From: Adrian Hunter @ 2016-01-29 10:34 UTC (permalink / raw)
  To: linux-arm-kernel

On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
> Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>> Coresight ETMs are IP blocks used to perform HW assisted tracing
>>> on a CPU core.  This patch introduce the required auxiliary API
>>> functions allowing the perf core to interact with a tracer.
>>>
>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>> Cc: Ingo Molnar <mingo@redhat.com>
>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> ---
>>>  MAINTAINERS                         |   3 +
>>>  tools/perf/arch/arm/util/Build      |   2 +-
>>>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
>>>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
>>>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
>>>  tools/perf/util/auxtrace.c          |   1 +
>>>  tools/perf/util/auxtrace.h          |   1 +
>>>  7 files changed, 570 insertions(+), 1 deletion(-)
>>>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index b2a92245eece..a81b2737ebc3 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>>>  F:     tools/perf/arch/arm/util/pmu.c
>>> +F:     tools/perf/arch/arm/util/auxtrace.c
>>> +F:     tools/perf/arch/arm/util/cs_etm.c
>>> +F:     tools/perf/arch/arm/util/cs_etm.h
>>>
>>>  ARM/CORGI MACHINE SUPPORT
>>>  M:     Richard Purdie <rpurdie@rpsys.net>
>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
>>> index 66ab0b05549c..0a25a1248f42 100644
>>> --- a/tools/perf/arch/arm/util/Build
>>> +++ b/tools/perf/arch/arm/util/Build
>>> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>>>
>>> -libperf-$(CONFIG_AUXTRACE) += pmu.o
>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
>>> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
>>> new file mode 100644
>>> index 000000000000..d327316f0e8a
>>> --- /dev/null
>>> +++ b/tools/perf/arch/arm/util/auxtrace.c
>>> @@ -0,0 +1,54 @@
>>> +/*
>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <stdbool.h>
>>> +#include <linux/coresight-pmu.h>
>>> +
>>> +#include "../../util/auxtrace.h"
>>> +#include "../../util/evlist.h"
>>> +#include "../../util/pmu.h"
>>> +#include "cs_etm.h"
>>> +
>>> +struct auxtrace_record
>>> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
>>> +{
>>> +       struct perf_pmu *cs_etm_pmu;
>>> +       struct perf_evsel *evsel;
>>> +       bool found_etm = false;
>>> +
>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>>> +
>>> +        if (evlist) {
>>> +               evlist__for_each(evlist, evsel) {
>>> +                       if (cs_etm_pmu &&
>>> +                           evsel->attr.type == cs_etm_pmu->type)
>>> +                               found_etm = true;
>>> +               }
>>> +       }
>>> +
>>> +       if (found_etm)
>>> +               return cs_etm_record_init(err);
>>> +
>>> +       /*
>>> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
>>> +        * record can still be used even if tracers aren't present.  The NULL
>>> +        * return value will take care of telling the infrastructure HW tracing
>>> +        * isn't available.
>>> +        */
>>> +       *err = 0;
>>> +       return NULL;
>>> +}
>>> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
>>> new file mode 100644
>>> index 000000000000..5710b90e23d5
>>> --- /dev/null
>>> +++ b/tools/perf/arch/arm/util/cs_etm.c
>>> @@ -0,0 +1,466 @@
>>> +/*
>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <api/fs/fs.h>
>>> +#include <linux/bitops.h>
>>> +#include <linux/coresight-pmu.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/log2.h>
>>> +#include <linux/types.h>
>>> +
>>> +#include "../../perf.h"
>>> +#include "../../util/auxtrace.h"
>>> +#include "../../util/cpumap.h"
>>> +#include "../../util/evlist.h"
>>> +#include "../../util/pmu.h"
>>> +#include "../../util/thread_map.h"
>>> +#include "cs_etm.h"
>>> +
>>> +#include <stdlib.h>
>>> +
>>> +#define KiB(x) ((x) * 1024)
>>> +#define MiB(x) ((x) * 1024 * 1024)
>>> +
>>> +struct cs_etm_recording {
>>> +       struct auxtrace_record  itr;
>>> +       struct perf_pmu         *cs_etm_pmu;
>>> +       struct perf_evlist      *evlist;
>>> +       bool                    snapshot_mode;
>>> +       size_t                  snapshot_size;
>>> +};
>>> +
>>> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
>>> +                                        struct record_opts *opts,
>>> +                                        const char *str)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                               container_of(itr, struct cs_etm_recording, itr);
>>> +       unsigned long long snapshot_size = 0;
>>> +       char *endptr;
>>> +
>>> +       if (str) {
>>> +               snapshot_size = strtoull(str, &endptr, 0);
>>> +               if (*endptr || snapshot_size > SIZE_MAX)
>>> +                       return -1;
>>> +       }
>>> +
>>> +       opts->auxtrace_snapshot_mode = true;
>>> +       opts->auxtrace_snapshot_size = snapshot_size;
>>> +       ptr->snapshot_size = snapshot_size;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int cs_etm_recording_options(struct auxtrace_record *itr,
>>> +                                   struct perf_evlist *evlist,
>>> +                                   struct record_opts *opts)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                               container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
>>> +       const struct cpu_map *cpus = evlist->cpus;
>>> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
>>> +
>>> +       ptr->evlist = evlist;
>>> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
>>> +
>>> +       evlist__for_each(evlist, evsel) {
>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>>> +                       if (cs_etm_evsel) {
>>> +                               pr_err("There may be only one %s event\n",
>>> +                                      CORESIGHT_ETM_PMU_NAME);
>>> +                               return -EINVAL;
>>> +                       }
>>> +                       evsel->attr.freq = 0;
>>> +                       evsel->attr.sample_period = 1;
>>> +                       cs_etm_evsel = evsel;
>>> +                       opts->full_auxtrace = true;
>>> +               }
>>> +       }
>>> +
>>> +       /* no need to continue if at least one event of interest was found */
>>> +       if (!cs_etm_evsel)
>>> +               return 0;
>>> +
>>> +       if (opts->use_clockid) {
>>> +               pr_err("Cannot use clockid (-k option) with %s\n",
>>> +                      CORESIGHT_ETM_PMU_NAME);
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       /* we are in snapshot mode */
>>> +       if (opts->auxtrace_snapshot_mode) {
>>> +               /*
>>> +                * No size were given to '-S' or '-m,', so go with
>>> +                * the default
>>> +                */
>>> +               if (!opts->auxtrace_snapshot_size &&
>>> +                   !opts->auxtrace_mmap_pages) {
>>> +                       if (privileged) {
>>> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
>>> +                       } else {
>>> +                               opts->auxtrace_mmap_pages =
>>> +                                                       KiB(128) / page_size;
>>> +                               if (opts->mmap_pages == UINT_MAX)
>>> +                                       opts->mmap_pages = KiB(256) / page_size;
>>> +                       }
>>> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
>>> +                                               opts->mmap_pages == UINT_MAX) {
>>> +                       opts->mmap_pages = KiB(256) / page_size;
>>> +               }
>>> +
>>> +               /*
>>> +                * '-m,xyz' was specified but no snapshot size, so make the
>>> +                * snapshot size as big as the auxtrace mmap area.
>>> +                */
>>> +               if (!opts->auxtrace_snapshot_size) {
>>> +                       opts->auxtrace_snapshot_size =
>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
>>> +               }
>>> +
>>> +               /*
>>> +                * -Sxyz was specified but no auxtrace mmap area, so make the
>>> +                * auxtrace mmap area big enough to fit the requested snapshot
>>> +                * size.
>>> +                */
>>> +               if (!opts->auxtrace_mmap_pages) {
>>> +                       size_t sz = opts->auxtrace_snapshot_size;
>>> +
>>> +                       sz = round_up(sz, page_size) / page_size;
>>> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
>>> +               }
>>> +
>>> +               /* Snapshost size can't be bigger than the auxtrace area */
>>> +               if (opts->auxtrace_snapshot_size >
>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
>>> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
>>> +                              opts->auxtrace_snapshot_size,
>>> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
>>> +                       return -EINVAL;
>>> +               }
>>> +
>>> +               /* Something went wrong somewhere - this shouldn't happen */
>>> +               if (!opts->auxtrace_snapshot_size ||
>>> +                   !opts->auxtrace_mmap_pages) {
>>> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
>>> +                       return -EINVAL;
>>> +               }
>>> +       }
>>> +
>>> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
>>> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
>>> +               if (privileged) {
>>> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
>>> +               } else {
>>> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
>>> +                       if (opts->mmap_pages == UINT_MAX)
>>> +                               opts->mmap_pages = KiB(256) / page_size;
>>> +               }
>>> +
>>> +       }
>>> +
>>> +       /* Validate auxtrace_mmap_pages provided by user */
>>> +       if (opts->auxtrace_mmap_pages) {
>>> +               unsigned int max_page = (KiB(128) / page_size);
>>> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
>>> +
>>> +               if (!privileged &&
>>> +                   opts->auxtrace_mmap_pages > max_page) {
>>> +                       opts->auxtrace_mmap_pages = max_page;
>>> +                       pr_err("auxtrace too big, truncating to %d\n",
>>> +                              max_page);
>>> +               }
>>> +
>>> +               if (!is_power_of_2(sz)) {
>>> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
>>> +                              CORESIGHT_ETM_PMU_NAME);
>>> +                       return -EINVAL;
>>> +               }
>>> +       }
>>> +
>>> +       if (opts->auxtrace_snapshot_mode)
>>> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
>>> +                         opts->auxtrace_snapshot_size);
>>> +
>>> +       if (cs_etm_evsel) {
>>> +               /*
>>> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
>>> +                * event must come first.
>>> +                */
>>> +               perf_evlist__to_front(evlist, cs_etm_evsel);
>>> +               /*
>>> +                * In the case of per-cpu mmaps, we need the CPU on the
>>> +                * AUX event.
>>> +                */
>>> +               if (!cpu_map__empty(cpus))
>>> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
>>> +       }
>>> +
>>> +       /* Add dummy event to keep tracking */
>>> +       if (opts->full_auxtrace) {
>>> +               struct perf_evsel *tracking_evsel;
>>> +               int err;
>>> +
>>> +               err = parse_events(evlist, "dummy:u", NULL);
>>> +               if (err)
>>> +                       return err;
>>> +
>>> +               tracking_evsel = perf_evlist__last(evlist);
>>> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
>>> +
>>> +               tracking_evsel->attr.freq = 0;
>>> +               tracking_evsel->attr.sample_period = 1;
>>> +
>>> +               /* In per-cpu case, always need the time of mmap events etc */
>>> +               if (!cpu_map__empty(cpus))
>>> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
>>> +{
>>> +       u64 config = 0;
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>> +       struct perf_evlist *evlist = ptr->evlist;
>>> +       struct perf_evsel *evsel;
>>> +
>>> +       evlist__for_each(evlist, evsel) {
>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>>> +                       /*
>>> +                        * Variable perf_event_attr::config is assigned to
>>> +                        * ETMv3/PTM.  The bit fields have been made to match
>>> +                        * the ETMv3.5 ETRMCR register specification.  See the
>>> +                        * PMU_FORMAT_ATTR() declarations in
>>> +                        * drivers/hwtracing/coresight/coresight-perf.c for
>>> +                        * details.
>>> +                        */
>>> +                       config = evsel->attr.config;
>>> +                       break;
>>> +               }
>>> +       }
>>> +
>>> +       return config;
>>> +}
>>> +
>>> +static size_t
>>> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>> +                     struct perf_evlist *evlist __maybe_unused)
>>> +{
>>> +       int records;
>>> +       const struct cpu_map *cpus = evlist->cpus;
>>> +
>>> +       if (!cpu_map__empty(cpus)) {
>>> +               records = cpu_map__nr(cpus);
>>> +               goto out;
>>> +       }
>>> +
>>> +       set_max_cpu_num();
>>> +       records = cpu__max_cpu();
>>> +out:
>>> +       return records * CS_ETM_PRIV_SIZE;
>>> +}
>>> +
>>> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
>>> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
>>> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
>>> +};
>>> +
>>> +static int cs_etm_get_metadata(int cpu, int index,
>>> +                              struct auxtrace_record *itr,
>>> +                              struct auxtrace_info_event *info)
>>> +{
>>> +       char path[PATH_MAX];
>>> +       int offset = 0, ret = 0;
>>> +       int i, scan;
>>> +       unsigned int val;
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>> +
>>> +       offset = index * CS_ETM_PRIV_MAX;
>>> +
>>> +       /* Build generic header portion */
>>> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
>>> +       info->priv[offset + CS_ETM_CPU] = cpu;
>>> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
>>> +
>>> +       /* Get user configurables from the session */
>>> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
>>> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
>>> +
>>> +       /* Get RO metadata from sysfs */
>>> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
>>> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
>>> +
>>> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
>>> +               if (scan != 1) {
>>> +                       ret = -EINVAL;
>>> +                       break;
>>> +               }
>>> +
>>> +               info->priv[offset + i] = val;
>>> +       }
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +static int cs_etm_info_fill(struct auxtrace_record *itr,
>>> +                           struct perf_session *session,
>>> +                           struct auxtrace_info_event *auxtrace_info,
>>> +                           size_t priv_size)
>>> +{
>>> +       int i, nr_cpu, ret = 0;
>>> +       const struct cpu_map *cpus = session->evlist->cpus;
>>> +
>>> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
>>> +               return -EINVAL;
>>> +
>>> +       if (!session->evlist->nr_mmaps)
>>> +               return -EINVAL;
>>> +
>>> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
>>> +
>>> +       /* cpu map is not empty, we have specific CPUs to work with */
>>> +       if (!cpu_map__empty(cpus)) {
>>> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
>>> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
>>> +                                                 itr, auxtrace_info);
>>> +                       if (ret)
>>> +                               goto out;
>>> +               }
>>> +       } else {
>>> +               /* get configuration for all CPUs in the system */
>>> +               nr_cpu = cpu__max_cpu();
>>> +               for (i = 0; i < nr_cpu; i++) {
>>> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
>>> +                       if (ret)
>>> +                               goto out;
>>> +               }
>>> +       }
>>> +
>>> +out:
>>> +       return ret;
>>> +}
>>> +
>>> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
>>> +                               int idx, struct auxtrace_mmap *mm,
>>> +                               unsigned char *data __maybe_unused,
>>> +                               u64 *head, u64 *old)
>>> +{
>>> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
>>> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
>>> +
>>> +       *old = *head;
>>> +       *head += mm->len;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_evsel *evsel;
>>> +
>>> +       evlist__for_each(ptr->evlist, evsel) {
>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
>>> +       }
>>> +       return -EINVAL;
>>> +}
>>> +
>>> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_evsel *evsel;
>>> +
>>> +       evlist__for_each(ptr->evlist, evsel) {
>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
>>> +       }
>>> +       return -EINVAL;
>>> +}
>>> +
>>> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
>>> +{
>>> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
>>> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
>>> +}
>>> +
>>> +static void cs_etm_recording_free(struct auxtrace_record *itr)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       free(ptr);
>>> +}
>>> +
>>> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
>>> +{
>>> +       struct cs_etm_recording *ptr =
>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>> +       struct perf_evsel *evsel;
>>> +
>>> +       evlist__for_each(ptr->evlist, evsel) {
>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>> +                       return perf_evlist__enable_event_idx(ptr->evlist,
>>> +                                                            evsel, idx);
>>> +       }
>>> +
>>> +       return -EINVAL;
>>> +}
>>> +
>>> +struct auxtrace_record *cs_etm_record_init(int *err)
>>> +{
>>> +       struct perf_pmu *cs_etm_pmu;
>>> +       struct cs_etm_recording *ptr;
>>> +
>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>>> +
>>> +       if (!cs_etm_pmu) {
>>> +               *err = -EINVAL;
>>> +               goto out;
>>> +       }
>>> +
>>> +       ptr = zalloc(sizeof(struct cs_etm_recording));
>>> +       if (!ptr) {
>>> +               *err = -ENOMEM;
>>> +               goto out;
>>> +       }
>>> +
>>> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
>>> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
>>> +       ptr->itr.recording_options      = cs_etm_recording_options;
>>> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
>>> +       ptr->itr.info_fill              = cs_etm_info_fill;
>>> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
>>> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
>>> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
>>> +       ptr->itr.reference              = cs_etm_reference;
>>> +       ptr->itr.free                   = cs_etm_recording_free;
>>> +       ptr->itr.read_finish            = cs_etm_read_finish;
>>> +
>>> +       *err = 0;
>>> +       return &ptr->itr;
>>> +out:
>>> +       return NULL;
>>> +}
>>> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
>>> new file mode 100644
>>> index 000000000000..7e85c1b43598
>>> --- /dev/null
>>> +++ b/tools/perf/arch/arm/util/cs_etm.h
>>> @@ -0,0 +1,44 @@
>>> +/*
>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#ifndef INCLUDE__PERF_CS_ETM_H__
>>> +#define INCLUDE__PERF_CS_ETM_H__
>>> +
>>> +/* Beginning of header common to both ETMv3 and V4 */
>>> +enum {
>>> +       CS_ETM_MAGIC,
>>> +       CS_ETM_CPU,
>>> +       CS_ETM_SNAPSHOT,
>>> +};
>>> +
>>> +/* ETMv3/PTM metadata */
>>> +enum {
>>> +       /* Dynamic, configurable parameters */
>>> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
>>> +       CS_ETM_ETMTRACEIDR,
>>> +       /* RO, taken from sysFS */
>>> +       CS_ETM_ETMCCER,
>>> +       CS_ETM_ETMIDR,
>>> +       CS_ETM_PRIV_MAX,
>>> +};
>>> +
>>> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
>>> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
>>> +
>>> +struct auxtrace_record *cs_etm_record_init(int *err);
>>> +
>>> +#endif
>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>>> index cc1c9ce5cc56..a6f291dbc4d9 100644
>>> --- a/tools/perf/util/auxtrace.c
>>> +++ b/tools/perf/util/auxtrace.c
>>> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
>>>                 return intel_pt_process_auxtrace_info(event, session);
>>>         case PERF_AUXTRACE_INTEL_BTS:
>>>                 return intel_bts_process_auxtrace_info(event, session);
>>> +       case PERF_AUXTRACE_CS_ETM:
>>>         case PERF_AUXTRACE_UNKNOWN:
>>>         default:
>>>                 return -EINVAL;
>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>>> index e5a8e2d4f2af..adb53e7bcabf 100644
>>> --- a/tools/perf/util/auxtrace.h
>>> +++ b/tools/perf/util/auxtrace.h
>>> @@ -41,6 +41,7 @@ enum auxtrace_type {
>>>         PERF_AUXTRACE_UNKNOWN,
>>>         PERF_AUXTRACE_INTEL_PT,
>>>         PERF_AUXTRACE_INTEL_BTS,
>>> +       PERF_AUXTRACE_CS_ETM,
>>>  };
>>>
>>>  enum itrace_period_type {
>>> --
>>> 2.1.4
>>>
>>
>> Arnaldo,
>>
>> Last but not least, this is the final patch that I would like you to
>> review before queing.
>>
>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>> be happy to use another baseline should that be more adequate for you.
> 
> Adrian,
> 
> 	One more, are you ok with this?

Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
mentioned that on another patch.

However there is no decoder, which begs the question, is there anything you
can actually do with the perf.data file?  Might be a bit confusing for users
if they can capture traces but not use perf tools on the resulting perf.data
file?

Nevertheless, for what is there now:

Acked-by: Adrian Hunter <adrian.hunter@intel.com>

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

* Re: [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-01-29 10:34         ` Adrian Hunter
@ 2016-01-29 17:37           ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-29 17:37 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, linux-kernel, linux-arm-kernel,
	Peter Zijlstra, Ingo Molnar

On 29 January 2016 at 03:34, Adrian Hunter <adrian.hunter@intel.com> wrote:
> On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
>> Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
>>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>>> Coresight ETMs are IP blocks used to perform HW assisted tracing
>>>> on a CPU core.  This patch introduce the required auxiliary API
>>>> functions allowing the perf core to interact with a tracer.
>>>>
>>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>>> Cc: Ingo Molnar <mingo@redhat.com>
>>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> ---
>>>>  MAINTAINERS                         |   3 +
>>>>  tools/perf/arch/arm/util/Build      |   2 +-
>>>>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
>>>>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
>>>>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
>>>>  tools/perf/util/auxtrace.c          |   1 +
>>>>  tools/perf/util/auxtrace.h          |   1 +
>>>>  7 files changed, 570 insertions(+), 1 deletion(-)
>>>>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
>>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
>>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
>>>>
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index b2a92245eece..a81b2737ebc3 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
>>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>>>>  F:     tools/perf/arch/arm/util/pmu.c
>>>> +F:     tools/perf/arch/arm/util/auxtrace.c
>>>> +F:     tools/perf/arch/arm/util/cs_etm.c
>>>> +F:     tools/perf/arch/arm/util/cs_etm.h
>>>>
>>>>  ARM/CORGI MACHINE SUPPORT
>>>>  M:     Richard Purdie <rpurdie@rpsys.net>
>>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
>>>> index 66ab0b05549c..0a25a1248f42 100644
>>>> --- a/tools/perf/arch/arm/util/Build
>>>> +++ b/tools/perf/arch/arm/util/Build
>>>> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>>>>
>>>> -libperf-$(CONFIG_AUXTRACE) += pmu.o
>>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
>>>> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
>>>> new file mode 100644
>>>> index 000000000000..d327316f0e8a
>>>> --- /dev/null
>>>> +++ b/tools/perf/arch/arm/util/auxtrace.c
>>>> @@ -0,0 +1,54 @@
>>>> +/*
>>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms of the GNU General Public License version 2 as published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>>> + * more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <stdbool.h>
>>>> +#include <linux/coresight-pmu.h>
>>>> +
>>>> +#include "../../util/auxtrace.h"
>>>> +#include "../../util/evlist.h"
>>>> +#include "../../util/pmu.h"
>>>> +#include "cs_etm.h"
>>>> +
>>>> +struct auxtrace_record
>>>> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
>>>> +{
>>>> +       struct perf_pmu *cs_etm_pmu;
>>>> +       struct perf_evsel *evsel;
>>>> +       bool found_etm = false;
>>>> +
>>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>>>> +
>>>> +        if (evlist) {
>>>> +               evlist__for_each(evlist, evsel) {
>>>> +                       if (cs_etm_pmu &&
>>>> +                           evsel->attr.type == cs_etm_pmu->type)
>>>> +                               found_etm = true;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       if (found_etm)
>>>> +               return cs_etm_record_init(err);
>>>> +
>>>> +       /*
>>>> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
>>>> +        * record can still be used even if tracers aren't present.  The NULL
>>>> +        * return value will take care of telling the infrastructure HW tracing
>>>> +        * isn't available.
>>>> +        */
>>>> +       *err = 0;
>>>> +       return NULL;
>>>> +}
>>>> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
>>>> new file mode 100644
>>>> index 000000000000..5710b90e23d5
>>>> --- /dev/null
>>>> +++ b/tools/perf/arch/arm/util/cs_etm.c
>>>> @@ -0,0 +1,466 @@
>>>> +/*
>>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms of the GNU General Public License version 2 as published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>>> + * more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <api/fs/fs.h>
>>>> +#include <linux/bitops.h>
>>>> +#include <linux/coresight-pmu.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/log2.h>
>>>> +#include <linux/types.h>
>>>> +
>>>> +#include "../../perf.h"
>>>> +#include "../../util/auxtrace.h"
>>>> +#include "../../util/cpumap.h"
>>>> +#include "../../util/evlist.h"
>>>> +#include "../../util/pmu.h"
>>>> +#include "../../util/thread_map.h"
>>>> +#include "cs_etm.h"
>>>> +
>>>> +#include <stdlib.h>
>>>> +
>>>> +#define KiB(x) ((x) * 1024)
>>>> +#define MiB(x) ((x) * 1024 * 1024)
>>>> +
>>>> +struct cs_etm_recording {
>>>> +       struct auxtrace_record  itr;
>>>> +       struct perf_pmu         *cs_etm_pmu;
>>>> +       struct perf_evlist      *evlist;
>>>> +       bool                    snapshot_mode;
>>>> +       size_t                  snapshot_size;
>>>> +};
>>>> +
>>>> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
>>>> +                                        struct record_opts *opts,
>>>> +                                        const char *str)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                               container_of(itr, struct cs_etm_recording, itr);
>>>> +       unsigned long long snapshot_size = 0;
>>>> +       char *endptr;
>>>> +
>>>> +       if (str) {
>>>> +               snapshot_size = strtoull(str, &endptr, 0);
>>>> +               if (*endptr || snapshot_size > SIZE_MAX)
>>>> +                       return -1;
>>>> +       }
>>>> +
>>>> +       opts->auxtrace_snapshot_mode = true;
>>>> +       opts->auxtrace_snapshot_size = snapshot_size;
>>>> +       ptr->snapshot_size = snapshot_size;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int cs_etm_recording_options(struct auxtrace_record *itr,
>>>> +                                   struct perf_evlist *evlist,
>>>> +                                   struct record_opts *opts)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                               container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>>> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
>>>> +       const struct cpu_map *cpus = evlist->cpus;
>>>> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
>>>> +
>>>> +       ptr->evlist = evlist;
>>>> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
>>>> +
>>>> +       evlist__for_each(evlist, evsel) {
>>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>>>> +                       if (cs_etm_evsel) {
>>>> +                               pr_err("There may be only one %s event\n",
>>>> +                                      CORESIGHT_ETM_PMU_NAME);
>>>> +                               return -EINVAL;
>>>> +                       }
>>>> +                       evsel->attr.freq = 0;
>>>> +                       evsel->attr.sample_period = 1;
>>>> +                       cs_etm_evsel = evsel;
>>>> +                       opts->full_auxtrace = true;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       /* no need to continue if at least one event of interest was found */
>>>> +       if (!cs_etm_evsel)
>>>> +               return 0;
>>>> +
>>>> +       if (opts->use_clockid) {
>>>> +               pr_err("Cannot use clockid (-k option) with %s\n",
>>>> +                      CORESIGHT_ETM_PMU_NAME);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       /* we are in snapshot mode */
>>>> +       if (opts->auxtrace_snapshot_mode) {
>>>> +               /*
>>>> +                * No size were given to '-S' or '-m,', so go with
>>>> +                * the default
>>>> +                */
>>>> +               if (!opts->auxtrace_snapshot_size &&
>>>> +                   !opts->auxtrace_mmap_pages) {
>>>> +                       if (privileged) {
>>>> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
>>>> +                       } else {
>>>> +                               opts->auxtrace_mmap_pages =
>>>> +                                                       KiB(128) / page_size;
>>>> +                               if (opts->mmap_pages == UINT_MAX)
>>>> +                                       opts->mmap_pages = KiB(256) / page_size;
>>>> +                       }
>>>> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
>>>> +                                               opts->mmap_pages == UINT_MAX) {
>>>> +                       opts->mmap_pages = KiB(256) / page_size;
>>>> +               }
>>>> +
>>>> +               /*
>>>> +                * '-m,xyz' was specified but no snapshot size, so make the
>>>> +                * snapshot size as big as the auxtrace mmap area.
>>>> +                */
>>>> +               if (!opts->auxtrace_snapshot_size) {
>>>> +                       opts->auxtrace_snapshot_size =
>>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
>>>> +               }
>>>> +
>>>> +               /*
>>>> +                * -Sxyz was specified but no auxtrace mmap area, so make the
>>>> +                * auxtrace mmap area big enough to fit the requested snapshot
>>>> +                * size.
>>>> +                */
>>>> +               if (!opts->auxtrace_mmap_pages) {
>>>> +                       size_t sz = opts->auxtrace_snapshot_size;
>>>> +
>>>> +                       sz = round_up(sz, page_size) / page_size;
>>>> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
>>>> +               }
>>>> +
>>>> +               /* Snapshost size can't be bigger than the auxtrace area */
>>>> +               if (opts->auxtrace_snapshot_size >
>>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
>>>> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
>>>> +                              opts->auxtrace_snapshot_size,
>>>> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +
>>>> +               /* Something went wrong somewhere - this shouldn't happen */
>>>> +               if (!opts->auxtrace_snapshot_size ||
>>>> +                   !opts->auxtrace_mmap_pages) {
>>>> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
>>>> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
>>>> +               if (privileged) {
>>>> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
>>>> +               } else {
>>>> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
>>>> +                       if (opts->mmap_pages == UINT_MAX)
>>>> +                               opts->mmap_pages = KiB(256) / page_size;
>>>> +               }
>>>> +
>>>> +       }
>>>> +
>>>> +       /* Validate auxtrace_mmap_pages provided by user */
>>>> +       if (opts->auxtrace_mmap_pages) {
>>>> +               unsigned int max_page = (KiB(128) / page_size);
>>>> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
>>>> +
>>>> +               if (!privileged &&
>>>> +                   opts->auxtrace_mmap_pages > max_page) {
>>>> +                       opts->auxtrace_mmap_pages = max_page;
>>>> +                       pr_err("auxtrace too big, truncating to %d\n",
>>>> +                              max_page);
>>>> +               }
>>>> +
>>>> +               if (!is_power_of_2(sz)) {
>>>> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
>>>> +                              CORESIGHT_ETM_PMU_NAME);
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       if (opts->auxtrace_snapshot_mode)
>>>> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
>>>> +                         opts->auxtrace_snapshot_size);
>>>> +
>>>> +       if (cs_etm_evsel) {
>>>> +               /*
>>>> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
>>>> +                * event must come first.
>>>> +                */
>>>> +               perf_evlist__to_front(evlist, cs_etm_evsel);
>>>> +               /*
>>>> +                * In the case of per-cpu mmaps, we need the CPU on the
>>>> +                * AUX event.
>>>> +                */
>>>> +               if (!cpu_map__empty(cpus))
>>>> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
>>>> +       }
>>>> +
>>>> +       /* Add dummy event to keep tracking */
>>>> +       if (opts->full_auxtrace) {
>>>> +               struct perf_evsel *tracking_evsel;
>>>> +               int err;
>>>> +
>>>> +               err = parse_events(evlist, "dummy:u", NULL);
>>>> +               if (err)
>>>> +                       return err;
>>>> +
>>>> +               tracking_evsel = perf_evlist__last(evlist);
>>>> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
>>>> +
>>>> +               tracking_evsel->attr.freq = 0;
>>>> +               tracking_evsel->attr.sample_period = 1;
>>>> +
>>>> +               /* In per-cpu case, always need the time of mmap events etc */
>>>> +               if (!cpu_map__empty(cpus))
>>>> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
>>>> +{
>>>> +       u64 config = 0;
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>>> +       struct perf_evlist *evlist = ptr->evlist;
>>>> +       struct perf_evsel *evsel;
>>>> +
>>>> +       evlist__for_each(evlist, evsel) {
>>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>>>> +                       /*
>>>> +                        * Variable perf_event_attr::config is assigned to
>>>> +                        * ETMv3/PTM.  The bit fields have been made to match
>>>> +                        * the ETMv3.5 ETRMCR register specification.  See the
>>>> +                        * PMU_FORMAT_ATTR() declarations in
>>>> +                        * drivers/hwtracing/coresight/coresight-perf.c for
>>>> +                        * details.
>>>> +                        */
>>>> +                       config = evsel->attr.config;
>>>> +                       break;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       return config;
>>>> +}
>>>> +
>>>> +static size_t
>>>> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>>> +                     struct perf_evlist *evlist __maybe_unused)
>>>> +{
>>>> +       int records;
>>>> +       const struct cpu_map *cpus = evlist->cpus;
>>>> +
>>>> +       if (!cpu_map__empty(cpus)) {
>>>> +               records = cpu_map__nr(cpus);
>>>> +               goto out;
>>>> +       }
>>>> +
>>>> +       set_max_cpu_num();
>>>> +       records = cpu__max_cpu();
>>>> +out:
>>>> +       return records * CS_ETM_PRIV_SIZE;
>>>> +}
>>>> +
>>>> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
>>>> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
>>>> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
>>>> +};
>>>> +
>>>> +static int cs_etm_get_metadata(int cpu, int index,
>>>> +                              struct auxtrace_record *itr,
>>>> +                              struct auxtrace_info_event *info)
>>>> +{
>>>> +       char path[PATH_MAX];
>>>> +       int offset = 0, ret = 0;
>>>> +       int i, scan;
>>>> +       unsigned int val;
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>>> +
>>>> +       offset = index * CS_ETM_PRIV_MAX;
>>>> +
>>>> +       /* Build generic header portion */
>>>> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
>>>> +       info->priv[offset + CS_ETM_CPU] = cpu;
>>>> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
>>>> +
>>>> +       /* Get user configurables from the session */
>>>> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
>>>> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
>>>> +
>>>> +       /* Get RO metadata from sysfs */
>>>> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
>>>> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
>>>> +
>>>> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
>>>> +               if (scan != 1) {
>>>> +                       ret = -EINVAL;
>>>> +                       break;
>>>> +               }
>>>> +
>>>> +               info->priv[offset + i] = val;
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static int cs_etm_info_fill(struct auxtrace_record *itr,
>>>> +                           struct perf_session *session,
>>>> +                           struct auxtrace_info_event *auxtrace_info,
>>>> +                           size_t priv_size)
>>>> +{
>>>> +       int i, nr_cpu, ret = 0;
>>>> +       const struct cpu_map *cpus = session->evlist->cpus;
>>>> +
>>>> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
>>>> +               return -EINVAL;
>>>> +
>>>> +       if (!session->evlist->nr_mmaps)
>>>> +               return -EINVAL;
>>>> +
>>>> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
>>>> +
>>>> +       /* cpu map is not empty, we have specific CPUs to work with */
>>>> +       if (!cpu_map__empty(cpus)) {
>>>> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
>>>> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
>>>> +                                                 itr, auxtrace_info);
>>>> +                       if (ret)
>>>> +                               goto out;
>>>> +               }
>>>> +       } else {
>>>> +               /* get configuration for all CPUs in the system */
>>>> +               nr_cpu = cpu__max_cpu();
>>>> +               for (i = 0; i < nr_cpu; i++) {
>>>> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
>>>> +                       if (ret)
>>>> +                               goto out;
>>>> +               }
>>>> +       }
>>>> +
>>>> +out:
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
>>>> +                               int idx, struct auxtrace_mmap *mm,
>>>> +                               unsigned char *data __maybe_unused,
>>>> +                               u64 *head, u64 *old)
>>>> +{
>>>> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
>>>> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
>>>> +
>>>> +       *old = *head;
>>>> +       *head += mm->len;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_evsel *evsel;
>>>> +
>>>> +       evlist__for_each(ptr->evlist, evsel) {
>>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>>> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
>>>> +       }
>>>> +       return -EINVAL;
>>>> +}
>>>> +
>>>> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_evsel *evsel;
>>>> +
>>>> +       evlist__for_each(ptr->evlist, evsel) {
>>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>>> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
>>>> +       }
>>>> +       return -EINVAL;
>>>> +}
>>>> +
>>>> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
>>>> +{
>>>> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
>>>> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
>>>> +}
>>>> +
>>>> +static void cs_etm_recording_free(struct auxtrace_record *itr)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       free(ptr);
>>>> +}
>>>> +
>>>> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_evsel *evsel;
>>>> +
>>>> +       evlist__for_each(ptr->evlist, evsel) {
>>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>>> +                       return perf_evlist__enable_event_idx(ptr->evlist,
>>>> +                                                            evsel, idx);
>>>> +       }
>>>> +
>>>> +       return -EINVAL;
>>>> +}
>>>> +
>>>> +struct auxtrace_record *cs_etm_record_init(int *err)
>>>> +{
>>>> +       struct perf_pmu *cs_etm_pmu;
>>>> +       struct cs_etm_recording *ptr;
>>>> +
>>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>>>> +
>>>> +       if (!cs_etm_pmu) {
>>>> +               *err = -EINVAL;
>>>> +               goto out;
>>>> +       }
>>>> +
>>>> +       ptr = zalloc(sizeof(struct cs_etm_recording));
>>>> +       if (!ptr) {
>>>> +               *err = -ENOMEM;
>>>> +               goto out;
>>>> +       }
>>>> +
>>>> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
>>>> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
>>>> +       ptr->itr.recording_options      = cs_etm_recording_options;
>>>> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
>>>> +       ptr->itr.info_fill              = cs_etm_info_fill;
>>>> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
>>>> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
>>>> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
>>>> +       ptr->itr.reference              = cs_etm_reference;
>>>> +       ptr->itr.free                   = cs_etm_recording_free;
>>>> +       ptr->itr.read_finish            = cs_etm_read_finish;
>>>> +
>>>> +       *err = 0;
>>>> +       return &ptr->itr;
>>>> +out:
>>>> +       return NULL;
>>>> +}
>>>> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
>>>> new file mode 100644
>>>> index 000000000000..7e85c1b43598
>>>> --- /dev/null
>>>> +++ b/tools/perf/arch/arm/util/cs_etm.h
>>>> @@ -0,0 +1,44 @@
>>>> +/*
>>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms of the GNU General Public License version 2 as published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>>> + * more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#ifndef INCLUDE__PERF_CS_ETM_H__
>>>> +#define INCLUDE__PERF_CS_ETM_H__
>>>> +
>>>> +/* Beginning of header common to both ETMv3 and V4 */
>>>> +enum {
>>>> +       CS_ETM_MAGIC,
>>>> +       CS_ETM_CPU,
>>>> +       CS_ETM_SNAPSHOT,
>>>> +};
>>>> +
>>>> +/* ETMv3/PTM metadata */
>>>> +enum {
>>>> +       /* Dynamic, configurable parameters */
>>>> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
>>>> +       CS_ETM_ETMTRACEIDR,
>>>> +       /* RO, taken from sysFS */
>>>> +       CS_ETM_ETMCCER,
>>>> +       CS_ETM_ETMIDR,
>>>> +       CS_ETM_PRIV_MAX,
>>>> +};
>>>> +
>>>> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
>>>> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
>>>> +
>>>> +struct auxtrace_record *cs_etm_record_init(int *err);
>>>> +
>>>> +#endif
>>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>>>> index cc1c9ce5cc56..a6f291dbc4d9 100644
>>>> --- a/tools/perf/util/auxtrace.c
>>>> +++ b/tools/perf/util/auxtrace.c
>>>> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
>>>>                 return intel_pt_process_auxtrace_info(event, session);
>>>>         case PERF_AUXTRACE_INTEL_BTS:
>>>>                 return intel_bts_process_auxtrace_info(event, session);
>>>> +       case PERF_AUXTRACE_CS_ETM:
>>>>         case PERF_AUXTRACE_UNKNOWN:
>>>>         default:
>>>>                 return -EINVAL;
>>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>>>> index e5a8e2d4f2af..adb53e7bcabf 100644
>>>> --- a/tools/perf/util/auxtrace.h
>>>> +++ b/tools/perf/util/auxtrace.h
>>>> @@ -41,6 +41,7 @@ enum auxtrace_type {
>>>>         PERF_AUXTRACE_UNKNOWN,
>>>>         PERF_AUXTRACE_INTEL_PT,
>>>>         PERF_AUXTRACE_INTEL_BTS,
>>>> +       PERF_AUXTRACE_CS_ETM,
>>>>  };
>>>>
>>>>  enum itrace_period_type {
>>>> --
>>>> 2.1.4
>>>>
>>>
>>> Arnaldo,
>>>
>>> Last but not least, this is the final patch that I would like you to
>>> review before queing.
>>>
>>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>>> be happy to use another baseline should that be more adequate for you.
>>
>> Adrian,
>>
>>       One more, are you ok with this?
>
> Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
> mentioned that on another patch.
>
> However there is no decoder, which begs the question, is there anything you
> can actually do with the perf.data file?  Might be a bit confusing for users
> if they can capture traces but not use perf tools on the resulting perf.data
> file?

We are working on a decoding library in parallel to this work.

>
> Nevertheless, for what is there now:
>
> Acked-by: Adrian Hunter <adrian.hunter@intel.com>

Thanks for the review.
Mathieu

>

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-01-29 17:37           ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-29 17:37 UTC (permalink / raw)
  To: linux-arm-kernel

On 29 January 2016 at 03:34, Adrian Hunter <adrian.hunter@intel.com> wrote:
> On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
>> Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
>>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>>> Coresight ETMs are IP blocks used to perform HW assisted tracing
>>>> on a CPU core.  This patch introduce the required auxiliary API
>>>> functions allowing the perf core to interact with a tracer.
>>>>
>>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>>>> Cc: Ingo Molnar <mingo@redhat.com>
>>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> ---
>>>>  MAINTAINERS                         |   3 +
>>>>  tools/perf/arch/arm/util/Build      |   2 +-
>>>>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
>>>>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
>>>>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
>>>>  tools/perf/util/auxtrace.c          |   1 +
>>>>  tools/perf/util/auxtrace.h          |   1 +
>>>>  7 files changed, 570 insertions(+), 1 deletion(-)
>>>>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
>>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
>>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
>>>>
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index b2a92245eece..a81b2737ebc3 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
>>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>>>>  F:     tools/perf/arch/arm/util/pmu.c
>>>> +F:     tools/perf/arch/arm/util/auxtrace.c
>>>> +F:     tools/perf/arch/arm/util/cs_etm.c
>>>> +F:     tools/perf/arch/arm/util/cs_etm.h
>>>>
>>>>  ARM/CORGI MACHINE SUPPORT
>>>>  M:     Richard Purdie <rpurdie@rpsys.net>
>>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
>>>> index 66ab0b05549c..0a25a1248f42 100644
>>>> --- a/tools/perf/arch/arm/util/Build
>>>> +++ b/tools/perf/arch/arm/util/Build
>>>> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>>>>
>>>> -libperf-$(CONFIG_AUXTRACE) += pmu.o
>>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
>>>> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
>>>> new file mode 100644
>>>> index 000000000000..d327316f0e8a
>>>> --- /dev/null
>>>> +++ b/tools/perf/arch/arm/util/auxtrace.c
>>>> @@ -0,0 +1,54 @@
>>>> +/*
>>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms of the GNU General Public License version 2 as published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>>> + * more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <stdbool.h>
>>>> +#include <linux/coresight-pmu.h>
>>>> +
>>>> +#include "../../util/auxtrace.h"
>>>> +#include "../../util/evlist.h"
>>>> +#include "../../util/pmu.h"
>>>> +#include "cs_etm.h"
>>>> +
>>>> +struct auxtrace_record
>>>> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
>>>> +{
>>>> +       struct perf_pmu *cs_etm_pmu;
>>>> +       struct perf_evsel *evsel;
>>>> +       bool found_etm = false;
>>>> +
>>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>>>> +
>>>> +        if (evlist) {
>>>> +               evlist__for_each(evlist, evsel) {
>>>> +                       if (cs_etm_pmu &&
>>>> +                           evsel->attr.type == cs_etm_pmu->type)
>>>> +                               found_etm = true;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       if (found_etm)
>>>> +               return cs_etm_record_init(err);
>>>> +
>>>> +       /*
>>>> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
>>>> +        * record can still be used even if tracers aren't present.  The NULL
>>>> +        * return value will take care of telling the infrastructure HW tracing
>>>> +        * isn't available.
>>>> +        */
>>>> +       *err = 0;
>>>> +       return NULL;
>>>> +}
>>>> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
>>>> new file mode 100644
>>>> index 000000000000..5710b90e23d5
>>>> --- /dev/null
>>>> +++ b/tools/perf/arch/arm/util/cs_etm.c
>>>> @@ -0,0 +1,466 @@
>>>> +/*
>>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms of the GNU General Public License version 2 as published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>>> + * more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <api/fs/fs.h>
>>>> +#include <linux/bitops.h>
>>>> +#include <linux/coresight-pmu.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/log2.h>
>>>> +#include <linux/types.h>
>>>> +
>>>> +#include "../../perf.h"
>>>> +#include "../../util/auxtrace.h"
>>>> +#include "../../util/cpumap.h"
>>>> +#include "../../util/evlist.h"
>>>> +#include "../../util/pmu.h"
>>>> +#include "../../util/thread_map.h"
>>>> +#include "cs_etm.h"
>>>> +
>>>> +#include <stdlib.h>
>>>> +
>>>> +#define KiB(x) ((x) * 1024)
>>>> +#define MiB(x) ((x) * 1024 * 1024)
>>>> +
>>>> +struct cs_etm_recording {
>>>> +       struct auxtrace_record  itr;
>>>> +       struct perf_pmu         *cs_etm_pmu;
>>>> +       struct perf_evlist      *evlist;
>>>> +       bool                    snapshot_mode;
>>>> +       size_t                  snapshot_size;
>>>> +};
>>>> +
>>>> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
>>>> +                                        struct record_opts *opts,
>>>> +                                        const char *str)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                               container_of(itr, struct cs_etm_recording, itr);
>>>> +       unsigned long long snapshot_size = 0;
>>>> +       char *endptr;
>>>> +
>>>> +       if (str) {
>>>> +               snapshot_size = strtoull(str, &endptr, 0);
>>>> +               if (*endptr || snapshot_size > SIZE_MAX)
>>>> +                       return -1;
>>>> +       }
>>>> +
>>>> +       opts->auxtrace_snapshot_mode = true;
>>>> +       opts->auxtrace_snapshot_size = snapshot_size;
>>>> +       ptr->snapshot_size = snapshot_size;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int cs_etm_recording_options(struct auxtrace_record *itr,
>>>> +                                   struct perf_evlist *evlist,
>>>> +                                   struct record_opts *opts)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                               container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>>> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
>>>> +       const struct cpu_map *cpus = evlist->cpus;
>>>> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
>>>> +
>>>> +       ptr->evlist = evlist;
>>>> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
>>>> +
>>>> +       evlist__for_each(evlist, evsel) {
>>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>>>> +                       if (cs_etm_evsel) {
>>>> +                               pr_err("There may be only one %s event\n",
>>>> +                                      CORESIGHT_ETM_PMU_NAME);
>>>> +                               return -EINVAL;
>>>> +                       }
>>>> +                       evsel->attr.freq = 0;
>>>> +                       evsel->attr.sample_period = 1;
>>>> +                       cs_etm_evsel = evsel;
>>>> +                       opts->full_auxtrace = true;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       /* no need to continue if at least one event of interest was found */
>>>> +       if (!cs_etm_evsel)
>>>> +               return 0;
>>>> +
>>>> +       if (opts->use_clockid) {
>>>> +               pr_err("Cannot use clockid (-k option) with %s\n",
>>>> +                      CORESIGHT_ETM_PMU_NAME);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       /* we are in snapshot mode */
>>>> +       if (opts->auxtrace_snapshot_mode) {
>>>> +               /*
>>>> +                * No size were given to '-S' or '-m,', so go with
>>>> +                * the default
>>>> +                */
>>>> +               if (!opts->auxtrace_snapshot_size &&
>>>> +                   !opts->auxtrace_mmap_pages) {
>>>> +                       if (privileged) {
>>>> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
>>>> +                       } else {
>>>> +                               opts->auxtrace_mmap_pages =
>>>> +                                                       KiB(128) / page_size;
>>>> +                               if (opts->mmap_pages == UINT_MAX)
>>>> +                                       opts->mmap_pages = KiB(256) / page_size;
>>>> +                       }
>>>> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
>>>> +                                               opts->mmap_pages == UINT_MAX) {
>>>> +                       opts->mmap_pages = KiB(256) / page_size;
>>>> +               }
>>>> +
>>>> +               /*
>>>> +                * '-m,xyz' was specified but no snapshot size, so make the
>>>> +                * snapshot size as big as the auxtrace mmap area.
>>>> +                */
>>>> +               if (!opts->auxtrace_snapshot_size) {
>>>> +                       opts->auxtrace_snapshot_size =
>>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
>>>> +               }
>>>> +
>>>> +               /*
>>>> +                * -Sxyz was specified but no auxtrace mmap area, so make the
>>>> +                * auxtrace mmap area big enough to fit the requested snapshot
>>>> +                * size.
>>>> +                */
>>>> +               if (!opts->auxtrace_mmap_pages) {
>>>> +                       size_t sz = opts->auxtrace_snapshot_size;
>>>> +
>>>> +                       sz = round_up(sz, page_size) / page_size;
>>>> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
>>>> +               }
>>>> +
>>>> +               /* Snapshost size can't be bigger than the auxtrace area */
>>>> +               if (opts->auxtrace_snapshot_size >
>>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
>>>> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
>>>> +                              opts->auxtrace_snapshot_size,
>>>> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +
>>>> +               /* Something went wrong somewhere - this shouldn't happen */
>>>> +               if (!opts->auxtrace_snapshot_size ||
>>>> +                   !opts->auxtrace_mmap_pages) {
>>>> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
>>>> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
>>>> +               if (privileged) {
>>>> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
>>>> +               } else {
>>>> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
>>>> +                       if (opts->mmap_pages == UINT_MAX)
>>>> +                               opts->mmap_pages = KiB(256) / page_size;
>>>> +               }
>>>> +
>>>> +       }
>>>> +
>>>> +       /* Validate auxtrace_mmap_pages provided by user */
>>>> +       if (opts->auxtrace_mmap_pages) {
>>>> +               unsigned int max_page = (KiB(128) / page_size);
>>>> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
>>>> +
>>>> +               if (!privileged &&
>>>> +                   opts->auxtrace_mmap_pages > max_page) {
>>>> +                       opts->auxtrace_mmap_pages = max_page;
>>>> +                       pr_err("auxtrace too big, truncating to %d\n",
>>>> +                              max_page);
>>>> +               }
>>>> +
>>>> +               if (!is_power_of_2(sz)) {
>>>> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
>>>> +                              CORESIGHT_ETM_PMU_NAME);
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       if (opts->auxtrace_snapshot_mode)
>>>> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
>>>> +                         opts->auxtrace_snapshot_size);
>>>> +
>>>> +       if (cs_etm_evsel) {
>>>> +               /*
>>>> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
>>>> +                * event must come first.
>>>> +                */
>>>> +               perf_evlist__to_front(evlist, cs_etm_evsel);
>>>> +               /*
>>>> +                * In the case of per-cpu mmaps, we need the CPU on the
>>>> +                * AUX event.
>>>> +                */
>>>> +               if (!cpu_map__empty(cpus))
>>>> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
>>>> +       }
>>>> +
>>>> +       /* Add dummy event to keep tracking */
>>>> +       if (opts->full_auxtrace) {
>>>> +               struct perf_evsel *tracking_evsel;
>>>> +               int err;
>>>> +
>>>> +               err = parse_events(evlist, "dummy:u", NULL);
>>>> +               if (err)
>>>> +                       return err;
>>>> +
>>>> +               tracking_evsel = perf_evlist__last(evlist);
>>>> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
>>>> +
>>>> +               tracking_evsel->attr.freq = 0;
>>>> +               tracking_evsel->attr.sample_period = 1;
>>>> +
>>>> +               /* In per-cpu case, always need the time of mmap events etc */
>>>> +               if (!cpu_map__empty(cpus))
>>>> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
>>>> +{
>>>> +       u64 config = 0;
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>>> +       struct perf_evlist *evlist = ptr->evlist;
>>>> +       struct perf_evsel *evsel;
>>>> +
>>>> +       evlist__for_each(evlist, evsel) {
>>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>>>> +                       /*
>>>> +                        * Variable perf_event_attr::config is assigned to
>>>> +                        * ETMv3/PTM.  The bit fields have been made to match
>>>> +                        * the ETMv3.5 ETRMCR register specification.  See the
>>>> +                        * PMU_FORMAT_ATTR() declarations in
>>>> +                        * drivers/hwtracing/coresight/coresight-perf.c for
>>>> +                        * details.
>>>> +                        */
>>>> +                       config = evsel->attr.config;
>>>> +                       break;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       return config;
>>>> +}
>>>> +
>>>> +static size_t
>>>> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>>>> +                     struct perf_evlist *evlist __maybe_unused)
>>>> +{
>>>> +       int records;
>>>> +       const struct cpu_map *cpus = evlist->cpus;
>>>> +
>>>> +       if (!cpu_map__empty(cpus)) {
>>>> +               records = cpu_map__nr(cpus);
>>>> +               goto out;
>>>> +       }
>>>> +
>>>> +       set_max_cpu_num();
>>>> +       records = cpu__max_cpu();
>>>> +out:
>>>> +       return records * CS_ETM_PRIV_SIZE;
>>>> +}
>>>> +
>>>> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
>>>> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
>>>> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
>>>> +};
>>>> +
>>>> +static int cs_etm_get_metadata(int cpu, int index,
>>>> +                              struct auxtrace_record *itr,
>>>> +                              struct auxtrace_info_event *info)
>>>> +{
>>>> +       char path[PATH_MAX];
>>>> +       int offset = 0, ret = 0;
>>>> +       int i, scan;
>>>> +       unsigned int val;
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>>>> +
>>>> +       offset = index * CS_ETM_PRIV_MAX;
>>>> +
>>>> +       /* Build generic header portion */
>>>> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
>>>> +       info->priv[offset + CS_ETM_CPU] = cpu;
>>>> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
>>>> +
>>>> +       /* Get user configurables from the session */
>>>> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
>>>> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
>>>> +
>>>> +       /* Get RO metadata from sysfs */
>>>> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
>>>> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
>>>> +
>>>> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
>>>> +               if (scan != 1) {
>>>> +                       ret = -EINVAL;
>>>> +                       break;
>>>> +               }
>>>> +
>>>> +               info->priv[offset + i] = val;
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static int cs_etm_info_fill(struct auxtrace_record *itr,
>>>> +                           struct perf_session *session,
>>>> +                           struct auxtrace_info_event *auxtrace_info,
>>>> +                           size_t priv_size)
>>>> +{
>>>> +       int i, nr_cpu, ret = 0;
>>>> +       const struct cpu_map *cpus = session->evlist->cpus;
>>>> +
>>>> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
>>>> +               return -EINVAL;
>>>> +
>>>> +       if (!session->evlist->nr_mmaps)
>>>> +               return -EINVAL;
>>>> +
>>>> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
>>>> +
>>>> +       /* cpu map is not empty, we have specific CPUs to work with */
>>>> +       if (!cpu_map__empty(cpus)) {
>>>> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
>>>> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
>>>> +                                                 itr, auxtrace_info);
>>>> +                       if (ret)
>>>> +                               goto out;
>>>> +               }
>>>> +       } else {
>>>> +               /* get configuration for all CPUs in the system */
>>>> +               nr_cpu = cpu__max_cpu();
>>>> +               for (i = 0; i < nr_cpu; i++) {
>>>> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
>>>> +                       if (ret)
>>>> +                               goto out;
>>>> +               }
>>>> +       }
>>>> +
>>>> +out:
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
>>>> +                               int idx, struct auxtrace_mmap *mm,
>>>> +                               unsigned char *data __maybe_unused,
>>>> +                               u64 *head, u64 *old)
>>>> +{
>>>> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
>>>> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
>>>> +
>>>> +       *old = *head;
>>>> +       *head += mm->len;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_evsel *evsel;
>>>> +
>>>> +       evlist__for_each(ptr->evlist, evsel) {
>>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>>> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
>>>> +       }
>>>> +       return -EINVAL;
>>>> +}
>>>> +
>>>> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_evsel *evsel;
>>>> +
>>>> +       evlist__for_each(ptr->evlist, evsel) {
>>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>>> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
>>>> +       }
>>>> +       return -EINVAL;
>>>> +}
>>>> +
>>>> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
>>>> +{
>>>> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
>>>> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
>>>> +}
>>>> +
>>>> +static void cs_etm_recording_free(struct auxtrace_record *itr)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       free(ptr);
>>>> +}
>>>> +
>>>> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
>>>> +{
>>>> +       struct cs_etm_recording *ptr =
>>>> +                       container_of(itr, struct cs_etm_recording, itr);
>>>> +       struct perf_evsel *evsel;
>>>> +
>>>> +       evlist__for_each(ptr->evlist, evsel) {
>>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>>>> +                       return perf_evlist__enable_event_idx(ptr->evlist,
>>>> +                                                            evsel, idx);
>>>> +       }
>>>> +
>>>> +       return -EINVAL;
>>>> +}
>>>> +
>>>> +struct auxtrace_record *cs_etm_record_init(int *err)
>>>> +{
>>>> +       struct perf_pmu *cs_etm_pmu;
>>>> +       struct cs_etm_recording *ptr;
>>>> +
>>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>>>> +
>>>> +       if (!cs_etm_pmu) {
>>>> +               *err = -EINVAL;
>>>> +               goto out;
>>>> +       }
>>>> +
>>>> +       ptr = zalloc(sizeof(struct cs_etm_recording));
>>>> +       if (!ptr) {
>>>> +               *err = -ENOMEM;
>>>> +               goto out;
>>>> +       }
>>>> +
>>>> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
>>>> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
>>>> +       ptr->itr.recording_options      = cs_etm_recording_options;
>>>> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
>>>> +       ptr->itr.info_fill              = cs_etm_info_fill;
>>>> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
>>>> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
>>>> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
>>>> +       ptr->itr.reference              = cs_etm_reference;
>>>> +       ptr->itr.free                   = cs_etm_recording_free;
>>>> +       ptr->itr.read_finish            = cs_etm_read_finish;
>>>> +
>>>> +       *err = 0;
>>>> +       return &ptr->itr;
>>>> +out:
>>>> +       return NULL;
>>>> +}
>>>> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
>>>> new file mode 100644
>>>> index 000000000000..7e85c1b43598
>>>> --- /dev/null
>>>> +++ b/tools/perf/arch/arm/util/cs_etm.h
>>>> @@ -0,0 +1,44 @@
>>>> +/*
>>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms of the GNU General Public License version 2 as published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>>>> + * more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#ifndef INCLUDE__PERF_CS_ETM_H__
>>>> +#define INCLUDE__PERF_CS_ETM_H__
>>>> +
>>>> +/* Beginning of header common to both ETMv3 and V4 */
>>>> +enum {
>>>> +       CS_ETM_MAGIC,
>>>> +       CS_ETM_CPU,
>>>> +       CS_ETM_SNAPSHOT,
>>>> +};
>>>> +
>>>> +/* ETMv3/PTM metadata */
>>>> +enum {
>>>> +       /* Dynamic, configurable parameters */
>>>> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
>>>> +       CS_ETM_ETMTRACEIDR,
>>>> +       /* RO, taken from sysFS */
>>>> +       CS_ETM_ETMCCER,
>>>> +       CS_ETM_ETMIDR,
>>>> +       CS_ETM_PRIV_MAX,
>>>> +};
>>>> +
>>>> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
>>>> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
>>>> +
>>>> +struct auxtrace_record *cs_etm_record_init(int *err);
>>>> +
>>>> +#endif
>>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>>>> index cc1c9ce5cc56..a6f291dbc4d9 100644
>>>> --- a/tools/perf/util/auxtrace.c
>>>> +++ b/tools/perf/util/auxtrace.c
>>>> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
>>>>                 return intel_pt_process_auxtrace_info(event, session);
>>>>         case PERF_AUXTRACE_INTEL_BTS:
>>>>                 return intel_bts_process_auxtrace_info(event, session);
>>>> +       case PERF_AUXTRACE_CS_ETM:
>>>>         case PERF_AUXTRACE_UNKNOWN:
>>>>         default:
>>>>                 return -EINVAL;
>>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>>>> index e5a8e2d4f2af..adb53e7bcabf 100644
>>>> --- a/tools/perf/util/auxtrace.h
>>>> +++ b/tools/perf/util/auxtrace.h
>>>> @@ -41,6 +41,7 @@ enum auxtrace_type {
>>>>         PERF_AUXTRACE_UNKNOWN,
>>>>         PERF_AUXTRACE_INTEL_PT,
>>>>         PERF_AUXTRACE_INTEL_BTS,
>>>> +       PERF_AUXTRACE_CS_ETM,
>>>>  };
>>>>
>>>>  enum itrace_period_type {
>>>> --
>>>> 2.1.4
>>>>
>>>
>>> Arnaldo,
>>>
>>> Last but not least, this is the final patch that I would like you to
>>> review before queing.
>>>
>>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>>> be happy to use another baseline should that be more adequate for you.
>>
>> Adrian,
>>
>>       One more, are you ok with this?
>
> Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
> mentioned that on another patch.
>
> However there is no decoder, which begs the question, is there anything you
> can actually do with the perf.data file?  Might be a bit confusing for users
> if they can capture traces but not use perf tools on the resulting perf.data
> file?

We are working on a decoding library in parallel to this work.

>
> Nevertheless, for what is there now:
>
> Acked-by: Adrian Hunter <adrian.hunter@intel.com>

Thanks for the review.
Mathieu

>

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

* Re: [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-01-29 17:37           ` Mathieu Poirier
@ 2016-01-29 21:12             ` Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-29 21:12 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Adrian Hunter, linux-kernel, linux-arm-kernel, Peter Zijlstra,
	Ingo Molnar

Em Fri, Jan 29, 2016 at 10:37:48AM -0700, Mathieu Poirier escreveu:
> On 29 January 2016 at 03:34, Adrian Hunter <adrian.hunter@intel.com> wrote:
> > On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
> >> Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
> >>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> >>>> Coresight ETMs are IP blocks used to perform HW assisted tracing
> >>>> on a CPU core.  This patch introduce the required auxiliary API
> >>>> functions allowing the perf core to interact with a tracer.
> >>>>
> >>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> >>>> Cc: Ingo Molnar <mingo@redhat.com>
> >>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> >>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>>> ---
> >>>>  MAINTAINERS                         |   3 +
> >>>>  tools/perf/arch/arm/util/Build      |   2 +-
> >>>>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
> >>>>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
> >>>>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
> >>>>  tools/perf/util/auxtrace.c          |   1 +
> >>>>  tools/perf/util/auxtrace.h          |   1 +
> >>>>  7 files changed, 570 insertions(+), 1 deletion(-)
> >>>>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
> >>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
> >>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
> >>>>
> >>>> diff --git a/MAINTAINERS b/MAINTAINERS
> >>>> index b2a92245eece..a81b2737ebc3 100644
> >>>> --- a/MAINTAINERS
> >>>> +++ b/MAINTAINERS
> >>>> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
> >>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
> >>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
> >>>>  F:     tools/perf/arch/arm/util/pmu.c
> >>>> +F:     tools/perf/arch/arm/util/auxtrace.c
> >>>> +F:     tools/perf/arch/arm/util/cs_etm.c
> >>>> +F:     tools/perf/arch/arm/util/cs_etm.h
> >>>>
> >>>>  ARM/CORGI MACHINE SUPPORT
> >>>>  M:     Richard Purdie <rpurdie@rpsys.net>
> >>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> >>>> index 66ab0b05549c..0a25a1248f42 100644
> >>>> --- a/tools/perf/arch/arm/util/Build
> >>>> +++ b/tools/perf/arch/arm/util/Build
> >>>> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
> >>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
> >>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> >>>>
> >>>> -libperf-$(CONFIG_AUXTRACE) += pmu.o
> >>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
> >>>> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
> >>>> new file mode 100644
> >>>> index 000000000000..d327316f0e8a
> >>>> --- /dev/null
> >>>> +++ b/tools/perf/arch/arm/util/auxtrace.c
> >>>> @@ -0,0 +1,54 @@
> >>>> +/*
> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>>> + *
> >>>> + * This program is free software; you can redistribute it and/or modify it
> >>>> + * under the terms of the GNU General Public License version 2 as published by
> >>>> + * the Free Software Foundation.
> >>>> + *
> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> >>>> + * more details.
> >>>> + *
> >>>> + * You should have received a copy of the GNU General Public License along with
> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> >>>> + */
> >>>> +
> >>>> +#include <stdbool.h>
> >>>> +#include <linux/coresight-pmu.h>
> >>>> +
> >>>> +#include "../../util/auxtrace.h"
> >>>> +#include "../../util/evlist.h"
> >>>> +#include "../../util/pmu.h"
> >>>> +#include "cs_etm.h"
> >>>> +
> >>>> +struct auxtrace_record
> >>>> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
> >>>> +{
> >>>> +       struct perf_pmu *cs_etm_pmu;
> >>>> +       struct perf_evsel *evsel;
> >>>> +       bool found_etm = false;
> >>>> +
> >>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> >>>> +
> >>>> +        if (evlist) {
> >>>> +               evlist__for_each(evlist, evsel) {
> >>>> +                       if (cs_etm_pmu &&
> >>>> +                           evsel->attr.type == cs_etm_pmu->type)
> >>>> +                               found_etm = true;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       if (found_etm)
> >>>> +               return cs_etm_record_init(err);
> >>>> +
> >>>> +       /*
> >>>> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
> >>>> +        * record can still be used even if tracers aren't present.  The NULL
> >>>> +        * return value will take care of telling the infrastructure HW tracing
> >>>> +        * isn't available.
> >>>> +        */
> >>>> +       *err = 0;
> >>>> +       return NULL;
> >>>> +}
> >>>> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
> >>>> new file mode 100644
> >>>> index 000000000000..5710b90e23d5
> >>>> --- /dev/null
> >>>> +++ b/tools/perf/arch/arm/util/cs_etm.c
> >>>> @@ -0,0 +1,466 @@
> >>>> +/*
> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>>> + *
> >>>> + * This program is free software; you can redistribute it and/or modify it
> >>>> + * under the terms of the GNU General Public License version 2 as published by
> >>>> + * the Free Software Foundation.
> >>>> + *
> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> >>>> + * more details.
> >>>> + *
> >>>> + * You should have received a copy of the GNU General Public License along with
> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> >>>> + */
> >>>> +
> >>>> +#include <api/fs/fs.h>
> >>>> +#include <linux/bitops.h>
> >>>> +#include <linux/coresight-pmu.h>
> >>>> +#include <linux/kernel.h>
> >>>> +#include <linux/log2.h>
> >>>> +#include <linux/types.h>
> >>>> +
> >>>> +#include "../../perf.h"
> >>>> +#include "../../util/auxtrace.h"
> >>>> +#include "../../util/cpumap.h"
> >>>> +#include "../../util/evlist.h"
> >>>> +#include "../../util/pmu.h"
> >>>> +#include "../../util/thread_map.h"
> >>>> +#include "cs_etm.h"
> >>>> +
> >>>> +#include <stdlib.h>
> >>>> +
> >>>> +#define KiB(x) ((x) * 1024)
> >>>> +#define MiB(x) ((x) * 1024 * 1024)
> >>>> +
> >>>> +struct cs_etm_recording {
> >>>> +       struct auxtrace_record  itr;
> >>>> +       struct perf_pmu         *cs_etm_pmu;
> >>>> +       struct perf_evlist      *evlist;
> >>>> +       bool                    snapshot_mode;
> >>>> +       size_t                  snapshot_size;
> >>>> +};
> >>>> +
> >>>> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
> >>>> +                                        struct record_opts *opts,
> >>>> +                                        const char *str)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                               container_of(itr, struct cs_etm_recording, itr);
> >>>> +       unsigned long long snapshot_size = 0;
> >>>> +       char *endptr;
> >>>> +
> >>>> +       if (str) {
> >>>> +               snapshot_size = strtoull(str, &endptr, 0);
> >>>> +               if (*endptr || snapshot_size > SIZE_MAX)
> >>>> +                       return -1;
> >>>> +       }
> >>>> +
> >>>> +       opts->auxtrace_snapshot_mode = true;
> >>>> +       opts->auxtrace_snapshot_size = snapshot_size;
> >>>> +       ptr->snapshot_size = snapshot_size;
> >>>> +
> >>>> +       return 0;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_recording_options(struct auxtrace_record *itr,
> >>>> +                                   struct perf_evlist *evlist,
> >>>> +                                   struct record_opts *opts)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                               container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> >>>> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
> >>>> +       const struct cpu_map *cpus = evlist->cpus;
> >>>> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
> >>>> +
> >>>> +       ptr->evlist = evlist;
> >>>> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
> >>>> +
> >>>> +       evlist__for_each(evlist, evsel) {
> >>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
> >>>> +                       if (cs_etm_evsel) {
> >>>> +                               pr_err("There may be only one %s event\n",
> >>>> +                                      CORESIGHT_ETM_PMU_NAME);
> >>>> +                               return -EINVAL;
> >>>> +                       }
> >>>> +                       evsel->attr.freq = 0;
> >>>> +                       evsel->attr.sample_period = 1;
> >>>> +                       cs_etm_evsel = evsel;
> >>>> +                       opts->full_auxtrace = true;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       /* no need to continue if at least one event of interest was found */
> >>>> +       if (!cs_etm_evsel)
> >>>> +               return 0;
> >>>> +
> >>>> +       if (opts->use_clockid) {
> >>>> +               pr_err("Cannot use clockid (-k option) with %s\n",
> >>>> +                      CORESIGHT_ETM_PMU_NAME);
> >>>> +               return -EINVAL;
> >>>> +       }
> >>>> +
> >>>> +       /* we are in snapshot mode */
> >>>> +       if (opts->auxtrace_snapshot_mode) {
> >>>> +               /*
> >>>> +                * No size were given to '-S' or '-m,', so go with
> >>>> +                * the default
> >>>> +                */
> >>>> +               if (!opts->auxtrace_snapshot_size &&
> >>>> +                   !opts->auxtrace_mmap_pages) {
> >>>> +                       if (privileged) {
> >>>> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
> >>>> +                       } else {
> >>>> +                               opts->auxtrace_mmap_pages =
> >>>> +                                                       KiB(128) / page_size;
> >>>> +                               if (opts->mmap_pages == UINT_MAX)
> >>>> +                                       opts->mmap_pages = KiB(256) / page_size;
> >>>> +                       }
> >>>> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
> >>>> +                                               opts->mmap_pages == UINT_MAX) {
> >>>> +                       opts->mmap_pages = KiB(256) / page_size;
> >>>> +               }
> >>>> +
> >>>> +               /*
> >>>> +                * '-m,xyz' was specified but no snapshot size, so make the
> >>>> +                * snapshot size as big as the auxtrace mmap area.
> >>>> +                */
> >>>> +               if (!opts->auxtrace_snapshot_size) {
> >>>> +                       opts->auxtrace_snapshot_size =
> >>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
> >>>> +               }
> >>>> +
> >>>> +               /*
> >>>> +                * -Sxyz was specified but no auxtrace mmap area, so make the
> >>>> +                * auxtrace mmap area big enough to fit the requested snapshot
> >>>> +                * size.
> >>>> +                */
> >>>> +               if (!opts->auxtrace_mmap_pages) {
> >>>> +                       size_t sz = opts->auxtrace_snapshot_size;
> >>>> +
> >>>> +                       sz = round_up(sz, page_size) / page_size;
> >>>> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
> >>>> +               }
> >>>> +
> >>>> +               /* Snapshost size can't be bigger than the auxtrace area */
> >>>> +               if (opts->auxtrace_snapshot_size >
> >>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
> >>>> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
> >>>> +                              opts->auxtrace_snapshot_size,
> >>>> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
> >>>> +                       return -EINVAL;
> >>>> +               }
> >>>> +
> >>>> +               /* Something went wrong somewhere - this shouldn't happen */
> >>>> +               if (!opts->auxtrace_snapshot_size ||
> >>>> +                   !opts->auxtrace_mmap_pages) {
> >>>> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
> >>>> +                       return -EINVAL;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
> >>>> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
> >>>> +               if (privileged) {
> >>>> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
> >>>> +               } else {
> >>>> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
> >>>> +                       if (opts->mmap_pages == UINT_MAX)
> >>>> +                               opts->mmap_pages = KiB(256) / page_size;
> >>>> +               }
> >>>> +
> >>>> +       }
> >>>> +
> >>>> +       /* Validate auxtrace_mmap_pages provided by user */
> >>>> +       if (opts->auxtrace_mmap_pages) {
> >>>> +               unsigned int max_page = (KiB(128) / page_size);
> >>>> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
> >>>> +
> >>>> +               if (!privileged &&
> >>>> +                   opts->auxtrace_mmap_pages > max_page) {
> >>>> +                       opts->auxtrace_mmap_pages = max_page;
> >>>> +                       pr_err("auxtrace too big, truncating to %d\n",
> >>>> +                              max_page);
> >>>> +               }
> >>>> +
> >>>> +               if (!is_power_of_2(sz)) {
> >>>> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
> >>>> +                              CORESIGHT_ETM_PMU_NAME);
> >>>> +                       return -EINVAL;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       if (opts->auxtrace_snapshot_mode)
> >>>> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
> >>>> +                         opts->auxtrace_snapshot_size);
> >>>> +
> >>>> +       if (cs_etm_evsel) {
> >>>> +               /*
> >>>> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
> >>>> +                * event must come first.
> >>>> +                */
> >>>> +               perf_evlist__to_front(evlist, cs_etm_evsel);
> >>>> +               /*
> >>>> +                * In the case of per-cpu mmaps, we need the CPU on the
> >>>> +                * AUX event.
> >>>> +                */
> >>>> +               if (!cpu_map__empty(cpus))
> >>>> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
> >>>> +       }
> >>>> +
> >>>> +       /* Add dummy event to keep tracking */
> >>>> +       if (opts->full_auxtrace) {
> >>>> +               struct perf_evsel *tracking_evsel;
> >>>> +               int err;
> >>>> +
> >>>> +               err = parse_events(evlist, "dummy:u", NULL);
> >>>> +               if (err)
> >>>> +                       return err;
> >>>> +
> >>>> +               tracking_evsel = perf_evlist__last(evlist);
> >>>> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
> >>>> +
> >>>> +               tracking_evsel->attr.freq = 0;
> >>>> +               tracking_evsel->attr.sample_period = 1;
> >>>> +
> >>>> +               /* In per-cpu case, always need the time of mmap events etc */
> >>>> +               if (!cpu_map__empty(cpus))
> >>>> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
> >>>> +       }
> >>>> +
> >>>> +       return 0;
> >>>> +}
> >>>> +
> >>>> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
> >>>> +{
> >>>> +       u64 config = 0;
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> >>>> +       struct perf_evlist *evlist = ptr->evlist;
> >>>> +       struct perf_evsel *evsel;
> >>>> +
> >>>> +       evlist__for_each(evlist, evsel) {
> >>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
> >>>> +                       /*
> >>>> +                        * Variable perf_event_attr::config is assigned to
> >>>> +                        * ETMv3/PTM.  The bit fields have been made to match
> >>>> +                        * the ETMv3.5 ETRMCR register specification.  See the
> >>>> +                        * PMU_FORMAT_ATTR() declarations in
> >>>> +                        * drivers/hwtracing/coresight/coresight-perf.c for
> >>>> +                        * details.
> >>>> +                        */
> >>>> +                       config = evsel->attr.config;
> >>>> +                       break;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       return config;
> >>>> +}
> >>>> +
> >>>> +static size_t
> >>>> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> >>>> +                     struct perf_evlist *evlist __maybe_unused)
> >>>> +{
> >>>> +       int records;
> >>>> +       const struct cpu_map *cpus = evlist->cpus;
> >>>> +
> >>>> +       if (!cpu_map__empty(cpus)) {
> >>>> +               records = cpu_map__nr(cpus);
> >>>> +               goto out;
> >>>> +       }
> >>>> +
> >>>> +       set_max_cpu_num();
> >>>> +       records = cpu__max_cpu();
> >>>> +out:
> >>>> +       return records * CS_ETM_PRIV_SIZE;
> >>>> +}
> >>>> +
> >>>> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
> >>>> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
> >>>> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
> >>>> +};
> >>>> +
> >>>> +static int cs_etm_get_metadata(int cpu, int index,
> >>>> +                              struct auxtrace_record *itr,
> >>>> +                              struct auxtrace_info_event *info)
> >>>> +{
> >>>> +       char path[PATH_MAX];
> >>>> +       int offset = 0, ret = 0;
> >>>> +       int i, scan;
> >>>> +       unsigned int val;
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> >>>> +
> >>>> +       offset = index * CS_ETM_PRIV_MAX;
> >>>> +
> >>>> +       /* Build generic header portion */
> >>>> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
> >>>> +       info->priv[offset + CS_ETM_CPU] = cpu;
> >>>> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
> >>>> +
> >>>> +       /* Get user configurables from the session */
> >>>> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
> >>>> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
> >>>> +
> >>>> +       /* Get RO metadata from sysfs */
> >>>> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
> >>>> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
> >>>> +
> >>>> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
> >>>> +               if (scan != 1) {
> >>>> +                       ret = -EINVAL;
> >>>> +                       break;
> >>>> +               }
> >>>> +
> >>>> +               info->priv[offset + i] = val;
> >>>> +       }
> >>>> +
> >>>> +       return ret;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_info_fill(struct auxtrace_record *itr,
> >>>> +                           struct perf_session *session,
> >>>> +                           struct auxtrace_info_event *auxtrace_info,
> >>>> +                           size_t priv_size)
> >>>> +{
> >>>> +       int i, nr_cpu, ret = 0;
> >>>> +       const struct cpu_map *cpus = session->evlist->cpus;
> >>>> +
> >>>> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
> >>>> +               return -EINVAL;
> >>>> +
> >>>> +       if (!session->evlist->nr_mmaps)
> >>>> +               return -EINVAL;
> >>>> +
> >>>> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
> >>>> +
> >>>> +       /* cpu map is not empty, we have specific CPUs to work with */
> >>>> +       if (!cpu_map__empty(cpus)) {
> >>>> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
> >>>> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
> >>>> +                                                 itr, auxtrace_info);
> >>>> +                       if (ret)
> >>>> +                               goto out;
> >>>> +               }
> >>>> +       } else {
> >>>> +               /* get configuration for all CPUs in the system */
> >>>> +               nr_cpu = cpu__max_cpu();
> >>>> +               for (i = 0; i < nr_cpu; i++) {
> >>>> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
> >>>> +                       if (ret)
> >>>> +                               goto out;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +out:
> >>>> +       return ret;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
> >>>> +                               int idx, struct auxtrace_mmap *mm,
> >>>> +                               unsigned char *data __maybe_unused,
> >>>> +                               u64 *head, u64 *old)
> >>>> +{
> >>>> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
> >>>> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
> >>>> +
> >>>> +       *old = *head;
> >>>> +       *head += mm->len;
> >>>> +
> >>>> +       return 0;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_evsel *evsel;
> >>>> +
> >>>> +       evlist__for_each(ptr->evlist, evsel) {
> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> >>>> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
> >>>> +       }
> >>>> +       return -EINVAL;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_evsel *evsel;
> >>>> +
> >>>> +       evlist__for_each(ptr->evlist, evsel) {
> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> >>>> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
> >>>> +       }
> >>>> +       return -EINVAL;
> >>>> +}
> >>>> +
> >>>> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
> >>>> +{
> >>>> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
> >>>> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
> >>>> +}
> >>>> +
> >>>> +static void cs_etm_recording_free(struct auxtrace_record *itr)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       free(ptr);
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_evsel *evsel;
> >>>> +
> >>>> +       evlist__for_each(ptr->evlist, evsel) {
> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> >>>> +                       return perf_evlist__enable_event_idx(ptr->evlist,
> >>>> +                                                            evsel, idx);
> >>>> +       }
> >>>> +
> >>>> +       return -EINVAL;
> >>>> +}
> >>>> +
> >>>> +struct auxtrace_record *cs_etm_record_init(int *err)
> >>>> +{
> >>>> +       struct perf_pmu *cs_etm_pmu;
> >>>> +       struct cs_etm_recording *ptr;
> >>>> +
> >>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> >>>> +
> >>>> +       if (!cs_etm_pmu) {
> >>>> +               *err = -EINVAL;
> >>>> +               goto out;
> >>>> +       }
> >>>> +
> >>>> +       ptr = zalloc(sizeof(struct cs_etm_recording));
> >>>> +       if (!ptr) {
> >>>> +               *err = -ENOMEM;
> >>>> +               goto out;
> >>>> +       }
> >>>> +
> >>>> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
> >>>> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
> >>>> +       ptr->itr.recording_options      = cs_etm_recording_options;
> >>>> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
> >>>> +       ptr->itr.info_fill              = cs_etm_info_fill;
> >>>> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
> >>>> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
> >>>> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
> >>>> +       ptr->itr.reference              = cs_etm_reference;
> >>>> +       ptr->itr.free                   = cs_etm_recording_free;
> >>>> +       ptr->itr.read_finish            = cs_etm_read_finish;
> >>>> +
> >>>> +       *err = 0;
> >>>> +       return &ptr->itr;
> >>>> +out:
> >>>> +       return NULL;
> >>>> +}
> >>>> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
> >>>> new file mode 100644
> >>>> index 000000000000..7e85c1b43598
> >>>> --- /dev/null
> >>>> +++ b/tools/perf/arch/arm/util/cs_etm.h
> >>>> @@ -0,0 +1,44 @@
> >>>> +/*
> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>>> + *
> >>>> + * This program is free software; you can redistribute it and/or modify it
> >>>> + * under the terms of the GNU General Public License version 2 as published by
> >>>> + * the Free Software Foundation.
> >>>> + *
> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> >>>> + * more details.
> >>>> + *
> >>>> + * You should have received a copy of the GNU General Public License along with
> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> >>>> + */
> >>>> +
> >>>> +#ifndef INCLUDE__PERF_CS_ETM_H__
> >>>> +#define INCLUDE__PERF_CS_ETM_H__
> >>>> +
> >>>> +/* Beginning of header common to both ETMv3 and V4 */
> >>>> +enum {
> >>>> +       CS_ETM_MAGIC,
> >>>> +       CS_ETM_CPU,
> >>>> +       CS_ETM_SNAPSHOT,
> >>>> +};
> >>>> +
> >>>> +/* ETMv3/PTM metadata */
> >>>> +enum {
> >>>> +       /* Dynamic, configurable parameters */
> >>>> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
> >>>> +       CS_ETM_ETMTRACEIDR,
> >>>> +       /* RO, taken from sysFS */
> >>>> +       CS_ETM_ETMCCER,
> >>>> +       CS_ETM_ETMIDR,
> >>>> +       CS_ETM_PRIV_MAX,
> >>>> +};
> >>>> +
> >>>> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
> >>>> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
> >>>> +
> >>>> +struct auxtrace_record *cs_etm_record_init(int *err);
> >>>> +
> >>>> +#endif
> >>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> >>>> index cc1c9ce5cc56..a6f291dbc4d9 100644
> >>>> --- a/tools/perf/util/auxtrace.c
> >>>> +++ b/tools/perf/util/auxtrace.c
> >>>> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
> >>>>                 return intel_pt_process_auxtrace_info(event, session);
> >>>>         case PERF_AUXTRACE_INTEL_BTS:
> >>>>                 return intel_bts_process_auxtrace_info(event, session);
> >>>> +       case PERF_AUXTRACE_CS_ETM:
> >>>>         case PERF_AUXTRACE_UNKNOWN:
> >>>>         default:
> >>>>                 return -EINVAL;
> >>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> >>>> index e5a8e2d4f2af..adb53e7bcabf 100644
> >>>> --- a/tools/perf/util/auxtrace.h
> >>>> +++ b/tools/perf/util/auxtrace.h
> >>>> @@ -41,6 +41,7 @@ enum auxtrace_type {
> >>>>         PERF_AUXTRACE_UNKNOWN,
> >>>>         PERF_AUXTRACE_INTEL_PT,
> >>>>         PERF_AUXTRACE_INTEL_BTS,
> >>>> +       PERF_AUXTRACE_CS_ETM,
> >>>>  };
> >>>>
> >>>>  enum itrace_period_type {
> >>>> --
> >>>> 2.1.4
> >>>>
> >>>
> >>> Arnaldo,
> >>>
> >>> Last but not least, this is the final patch that I would like you to
> >>> review before queing.
> >>>
> >>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> >>> be happy to use another baseline should that be more adequate for you.
> >>
> >> Adrian,
> >>
> >>       One more, are you ok with this?
> >
> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
> > mentioned that on another patch.
> >
> > However there is no decoder, which begs the question, is there anything you
> > can actually do with the perf.data file?  Might be a bit confusing for users
> > if they can capture traces but not use perf tools on the resulting perf.data
> > file?
> 
> We are working on a decoding library in parallel to this work.

Would be nice to be able to get both in the same patch kit, no? So that
one can both record and process the traces, verifying it all works.

- Arnaldo
 
> >
> > Nevertheless, for what is there now:
> >
> > Acked-by: Adrian Hunter <adrian.hunter@intel.com>
> 
> Thanks for the review.
> Mathieu
> 
> >

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-01-29 21:12             ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-01-29 21:12 UTC (permalink / raw)
  To: linux-arm-kernel

Em Fri, Jan 29, 2016 at 10:37:48AM -0700, Mathieu Poirier escreveu:
> On 29 January 2016 at 03:34, Adrian Hunter <adrian.hunter@intel.com> wrote:
> > On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
> >> Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
> >>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> >>>> Coresight ETMs are IP blocks used to perform HW assisted tracing
> >>>> on a CPU core.  This patch introduce the required auxiliary API
> >>>> functions allowing the perf core to interact with a tracer.
> >>>>
> >>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
> >>>> Cc: Ingo Molnar <mingo@redhat.com>
> >>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> >>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>>> ---
> >>>>  MAINTAINERS                         |   3 +
> >>>>  tools/perf/arch/arm/util/Build      |   2 +-
> >>>>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
> >>>>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
> >>>>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
> >>>>  tools/perf/util/auxtrace.c          |   1 +
> >>>>  tools/perf/util/auxtrace.h          |   1 +
> >>>>  7 files changed, 570 insertions(+), 1 deletion(-)
> >>>>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
> >>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
> >>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
> >>>>
> >>>> diff --git a/MAINTAINERS b/MAINTAINERS
> >>>> index b2a92245eece..a81b2737ebc3 100644
> >>>> --- a/MAINTAINERS
> >>>> +++ b/MAINTAINERS
> >>>> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
> >>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
> >>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
> >>>>  F:     tools/perf/arch/arm/util/pmu.c
> >>>> +F:     tools/perf/arch/arm/util/auxtrace.c
> >>>> +F:     tools/perf/arch/arm/util/cs_etm.c
> >>>> +F:     tools/perf/arch/arm/util/cs_etm.h
> >>>>
> >>>>  ARM/CORGI MACHINE SUPPORT
> >>>>  M:     Richard Purdie <rpurdie@rpsys.net>
> >>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> >>>> index 66ab0b05549c..0a25a1248f42 100644
> >>>> --- a/tools/perf/arch/arm/util/Build
> >>>> +++ b/tools/perf/arch/arm/util/Build
> >>>> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
> >>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
> >>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> >>>>
> >>>> -libperf-$(CONFIG_AUXTRACE) += pmu.o
> >>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
> >>>> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
> >>>> new file mode 100644
> >>>> index 000000000000..d327316f0e8a
> >>>> --- /dev/null
> >>>> +++ b/tools/perf/arch/arm/util/auxtrace.c
> >>>> @@ -0,0 +1,54 @@
> >>>> +/*
> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>>> + *
> >>>> + * This program is free software; you can redistribute it and/or modify it
> >>>> + * under the terms of the GNU General Public License version 2 as published by
> >>>> + * the Free Software Foundation.
> >>>> + *
> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> >>>> + * more details.
> >>>> + *
> >>>> + * You should have received a copy of the GNU General Public License along with
> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> >>>> + */
> >>>> +
> >>>> +#include <stdbool.h>
> >>>> +#include <linux/coresight-pmu.h>
> >>>> +
> >>>> +#include "../../util/auxtrace.h"
> >>>> +#include "../../util/evlist.h"
> >>>> +#include "../../util/pmu.h"
> >>>> +#include "cs_etm.h"
> >>>> +
> >>>> +struct auxtrace_record
> >>>> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
> >>>> +{
> >>>> +       struct perf_pmu *cs_etm_pmu;
> >>>> +       struct perf_evsel *evsel;
> >>>> +       bool found_etm = false;
> >>>> +
> >>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> >>>> +
> >>>> +        if (evlist) {
> >>>> +               evlist__for_each(evlist, evsel) {
> >>>> +                       if (cs_etm_pmu &&
> >>>> +                           evsel->attr.type == cs_etm_pmu->type)
> >>>> +                               found_etm = true;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       if (found_etm)
> >>>> +               return cs_etm_record_init(err);
> >>>> +
> >>>> +       /*
> >>>> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
> >>>> +        * record can still be used even if tracers aren't present.  The NULL
> >>>> +        * return value will take care of telling the infrastructure HW tracing
> >>>> +        * isn't available.
> >>>> +        */
> >>>> +       *err = 0;
> >>>> +       return NULL;
> >>>> +}
> >>>> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
> >>>> new file mode 100644
> >>>> index 000000000000..5710b90e23d5
> >>>> --- /dev/null
> >>>> +++ b/tools/perf/arch/arm/util/cs_etm.c
> >>>> @@ -0,0 +1,466 @@
> >>>> +/*
> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>>> + *
> >>>> + * This program is free software; you can redistribute it and/or modify it
> >>>> + * under the terms of the GNU General Public License version 2 as published by
> >>>> + * the Free Software Foundation.
> >>>> + *
> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> >>>> + * more details.
> >>>> + *
> >>>> + * You should have received a copy of the GNU General Public License along with
> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> >>>> + */
> >>>> +
> >>>> +#include <api/fs/fs.h>
> >>>> +#include <linux/bitops.h>
> >>>> +#include <linux/coresight-pmu.h>
> >>>> +#include <linux/kernel.h>
> >>>> +#include <linux/log2.h>
> >>>> +#include <linux/types.h>
> >>>> +
> >>>> +#include "../../perf.h"
> >>>> +#include "../../util/auxtrace.h"
> >>>> +#include "../../util/cpumap.h"
> >>>> +#include "../../util/evlist.h"
> >>>> +#include "../../util/pmu.h"
> >>>> +#include "../../util/thread_map.h"
> >>>> +#include "cs_etm.h"
> >>>> +
> >>>> +#include <stdlib.h>
> >>>> +
> >>>> +#define KiB(x) ((x) * 1024)
> >>>> +#define MiB(x) ((x) * 1024 * 1024)
> >>>> +
> >>>> +struct cs_etm_recording {
> >>>> +       struct auxtrace_record  itr;
> >>>> +       struct perf_pmu         *cs_etm_pmu;
> >>>> +       struct perf_evlist      *evlist;
> >>>> +       bool                    snapshot_mode;
> >>>> +       size_t                  snapshot_size;
> >>>> +};
> >>>> +
> >>>> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
> >>>> +                                        struct record_opts *opts,
> >>>> +                                        const char *str)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                               container_of(itr, struct cs_etm_recording, itr);
> >>>> +       unsigned long long snapshot_size = 0;
> >>>> +       char *endptr;
> >>>> +
> >>>> +       if (str) {
> >>>> +               snapshot_size = strtoull(str, &endptr, 0);
> >>>> +               if (*endptr || snapshot_size > SIZE_MAX)
> >>>> +                       return -1;
> >>>> +       }
> >>>> +
> >>>> +       opts->auxtrace_snapshot_mode = true;
> >>>> +       opts->auxtrace_snapshot_size = snapshot_size;
> >>>> +       ptr->snapshot_size = snapshot_size;
> >>>> +
> >>>> +       return 0;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_recording_options(struct auxtrace_record *itr,
> >>>> +                                   struct perf_evlist *evlist,
> >>>> +                                   struct record_opts *opts)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                               container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> >>>> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
> >>>> +       const struct cpu_map *cpus = evlist->cpus;
> >>>> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
> >>>> +
> >>>> +       ptr->evlist = evlist;
> >>>> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
> >>>> +
> >>>> +       evlist__for_each(evlist, evsel) {
> >>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
> >>>> +                       if (cs_etm_evsel) {
> >>>> +                               pr_err("There may be only one %s event\n",
> >>>> +                                      CORESIGHT_ETM_PMU_NAME);
> >>>> +                               return -EINVAL;
> >>>> +                       }
> >>>> +                       evsel->attr.freq = 0;
> >>>> +                       evsel->attr.sample_period = 1;
> >>>> +                       cs_etm_evsel = evsel;
> >>>> +                       opts->full_auxtrace = true;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       /* no need to continue if at least one event of interest was found */
> >>>> +       if (!cs_etm_evsel)
> >>>> +               return 0;
> >>>> +
> >>>> +       if (opts->use_clockid) {
> >>>> +               pr_err("Cannot use clockid (-k option) with %s\n",
> >>>> +                      CORESIGHT_ETM_PMU_NAME);
> >>>> +               return -EINVAL;
> >>>> +       }
> >>>> +
> >>>> +       /* we are in snapshot mode */
> >>>> +       if (opts->auxtrace_snapshot_mode) {
> >>>> +               /*
> >>>> +                * No size were given to '-S' or '-m,', so go with
> >>>> +                * the default
> >>>> +                */
> >>>> +               if (!opts->auxtrace_snapshot_size &&
> >>>> +                   !opts->auxtrace_mmap_pages) {
> >>>> +                       if (privileged) {
> >>>> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
> >>>> +                       } else {
> >>>> +                               opts->auxtrace_mmap_pages =
> >>>> +                                                       KiB(128) / page_size;
> >>>> +                               if (opts->mmap_pages == UINT_MAX)
> >>>> +                                       opts->mmap_pages = KiB(256) / page_size;
> >>>> +                       }
> >>>> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
> >>>> +                                               opts->mmap_pages == UINT_MAX) {
> >>>> +                       opts->mmap_pages = KiB(256) / page_size;
> >>>> +               }
> >>>> +
> >>>> +               /*
> >>>> +                * '-m,xyz' was specified but no snapshot size, so make the
> >>>> +                * snapshot size as big as the auxtrace mmap area.
> >>>> +                */
> >>>> +               if (!opts->auxtrace_snapshot_size) {
> >>>> +                       opts->auxtrace_snapshot_size =
> >>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
> >>>> +               }
> >>>> +
> >>>> +               /*
> >>>> +                * -Sxyz was specified but no auxtrace mmap area, so make the
> >>>> +                * auxtrace mmap area big enough to fit the requested snapshot
> >>>> +                * size.
> >>>> +                */
> >>>> +               if (!opts->auxtrace_mmap_pages) {
> >>>> +                       size_t sz = opts->auxtrace_snapshot_size;
> >>>> +
> >>>> +                       sz = round_up(sz, page_size) / page_size;
> >>>> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
> >>>> +               }
> >>>> +
> >>>> +               /* Snapshost size can't be bigger than the auxtrace area */
> >>>> +               if (opts->auxtrace_snapshot_size >
> >>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
> >>>> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
> >>>> +                              opts->auxtrace_snapshot_size,
> >>>> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
> >>>> +                       return -EINVAL;
> >>>> +               }
> >>>> +
> >>>> +               /* Something went wrong somewhere - this shouldn't happen */
> >>>> +               if (!opts->auxtrace_snapshot_size ||
> >>>> +                   !opts->auxtrace_mmap_pages) {
> >>>> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
> >>>> +                       return -EINVAL;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
> >>>> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
> >>>> +               if (privileged) {
> >>>> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
> >>>> +               } else {
> >>>> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
> >>>> +                       if (opts->mmap_pages == UINT_MAX)
> >>>> +                               opts->mmap_pages = KiB(256) / page_size;
> >>>> +               }
> >>>> +
> >>>> +       }
> >>>> +
> >>>> +       /* Validate auxtrace_mmap_pages provided by user */
> >>>> +       if (opts->auxtrace_mmap_pages) {
> >>>> +               unsigned int max_page = (KiB(128) / page_size);
> >>>> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
> >>>> +
> >>>> +               if (!privileged &&
> >>>> +                   opts->auxtrace_mmap_pages > max_page) {
> >>>> +                       opts->auxtrace_mmap_pages = max_page;
> >>>> +                       pr_err("auxtrace too big, truncating to %d\n",
> >>>> +                              max_page);
> >>>> +               }
> >>>> +
> >>>> +               if (!is_power_of_2(sz)) {
> >>>> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
> >>>> +                              CORESIGHT_ETM_PMU_NAME);
> >>>> +                       return -EINVAL;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       if (opts->auxtrace_snapshot_mode)
> >>>> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
> >>>> +                         opts->auxtrace_snapshot_size);
> >>>> +
> >>>> +       if (cs_etm_evsel) {
> >>>> +               /*
> >>>> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
> >>>> +                * event must come first.
> >>>> +                */
> >>>> +               perf_evlist__to_front(evlist, cs_etm_evsel);
> >>>> +               /*
> >>>> +                * In the case of per-cpu mmaps, we need the CPU on the
> >>>> +                * AUX event.
> >>>> +                */
> >>>> +               if (!cpu_map__empty(cpus))
> >>>> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
> >>>> +       }
> >>>> +
> >>>> +       /* Add dummy event to keep tracking */
> >>>> +       if (opts->full_auxtrace) {
> >>>> +               struct perf_evsel *tracking_evsel;
> >>>> +               int err;
> >>>> +
> >>>> +               err = parse_events(evlist, "dummy:u", NULL);
> >>>> +               if (err)
> >>>> +                       return err;
> >>>> +
> >>>> +               tracking_evsel = perf_evlist__last(evlist);
> >>>> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
> >>>> +
> >>>> +               tracking_evsel->attr.freq = 0;
> >>>> +               tracking_evsel->attr.sample_period = 1;
> >>>> +
> >>>> +               /* In per-cpu case, always need the time of mmap events etc */
> >>>> +               if (!cpu_map__empty(cpus))
> >>>> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
> >>>> +       }
> >>>> +
> >>>> +       return 0;
> >>>> +}
> >>>> +
> >>>> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
> >>>> +{
> >>>> +       u64 config = 0;
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> >>>> +       struct perf_evlist *evlist = ptr->evlist;
> >>>> +       struct perf_evsel *evsel;
> >>>> +
> >>>> +       evlist__for_each(evlist, evsel) {
> >>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
> >>>> +                       /*
> >>>> +                        * Variable perf_event_attr::config is assigned to
> >>>> +                        * ETMv3/PTM.  The bit fields have been made to match
> >>>> +                        * the ETMv3.5 ETRMCR register specification.  See the
> >>>> +                        * PMU_FORMAT_ATTR() declarations in
> >>>> +                        * drivers/hwtracing/coresight/coresight-perf.c for
> >>>> +                        * details.
> >>>> +                        */
> >>>> +                       config = evsel->attr.config;
> >>>> +                       break;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       return config;
> >>>> +}
> >>>> +
> >>>> +static size_t
> >>>> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> >>>> +                     struct perf_evlist *evlist __maybe_unused)
> >>>> +{
> >>>> +       int records;
> >>>> +       const struct cpu_map *cpus = evlist->cpus;
> >>>> +
> >>>> +       if (!cpu_map__empty(cpus)) {
> >>>> +               records = cpu_map__nr(cpus);
> >>>> +               goto out;
> >>>> +       }
> >>>> +
> >>>> +       set_max_cpu_num();
> >>>> +       records = cpu__max_cpu();
> >>>> +out:
> >>>> +       return records * CS_ETM_PRIV_SIZE;
> >>>> +}
> >>>> +
> >>>> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
> >>>> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
> >>>> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
> >>>> +};
> >>>> +
> >>>> +static int cs_etm_get_metadata(int cpu, int index,
> >>>> +                              struct auxtrace_record *itr,
> >>>> +                              struct auxtrace_info_event *info)
> >>>> +{
> >>>> +       char path[PATH_MAX];
> >>>> +       int offset = 0, ret = 0;
> >>>> +       int i, scan;
> >>>> +       unsigned int val;
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
> >>>> +
> >>>> +       offset = index * CS_ETM_PRIV_MAX;
> >>>> +
> >>>> +       /* Build generic header portion */
> >>>> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
> >>>> +       info->priv[offset + CS_ETM_CPU] = cpu;
> >>>> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
> >>>> +
> >>>> +       /* Get user configurables from the session */
> >>>> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
> >>>> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
> >>>> +
> >>>> +       /* Get RO metadata from sysfs */
> >>>> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
> >>>> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
> >>>> +
> >>>> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
> >>>> +               if (scan != 1) {
> >>>> +                       ret = -EINVAL;
> >>>> +                       break;
> >>>> +               }
> >>>> +
> >>>> +               info->priv[offset + i] = val;
> >>>> +       }
> >>>> +
> >>>> +       return ret;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_info_fill(struct auxtrace_record *itr,
> >>>> +                           struct perf_session *session,
> >>>> +                           struct auxtrace_info_event *auxtrace_info,
> >>>> +                           size_t priv_size)
> >>>> +{
> >>>> +       int i, nr_cpu, ret = 0;
> >>>> +       const struct cpu_map *cpus = session->evlist->cpus;
> >>>> +
> >>>> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
> >>>> +               return -EINVAL;
> >>>> +
> >>>> +       if (!session->evlist->nr_mmaps)
> >>>> +               return -EINVAL;
> >>>> +
> >>>> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
> >>>> +
> >>>> +       /* cpu map is not empty, we have specific CPUs to work with */
> >>>> +       if (!cpu_map__empty(cpus)) {
> >>>> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
> >>>> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
> >>>> +                                                 itr, auxtrace_info);
> >>>> +                       if (ret)
> >>>> +                               goto out;
> >>>> +               }
> >>>> +       } else {
> >>>> +               /* get configuration for all CPUs in the system */
> >>>> +               nr_cpu = cpu__max_cpu();
> >>>> +               for (i = 0; i < nr_cpu; i++) {
> >>>> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
> >>>> +                       if (ret)
> >>>> +                               goto out;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +out:
> >>>> +       return ret;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
> >>>> +                               int idx, struct auxtrace_mmap *mm,
> >>>> +                               unsigned char *data __maybe_unused,
> >>>> +                               u64 *head, u64 *old)
> >>>> +{
> >>>> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
> >>>> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
> >>>> +
> >>>> +       *old = *head;
> >>>> +       *head += mm->len;
> >>>> +
> >>>> +       return 0;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_evsel *evsel;
> >>>> +
> >>>> +       evlist__for_each(ptr->evlist, evsel) {
> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> >>>> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
> >>>> +       }
> >>>> +       return -EINVAL;
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_evsel *evsel;
> >>>> +
> >>>> +       evlist__for_each(ptr->evlist, evsel) {
> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> >>>> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
> >>>> +       }
> >>>> +       return -EINVAL;
> >>>> +}
> >>>> +
> >>>> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
> >>>> +{
> >>>> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
> >>>> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
> >>>> +}
> >>>> +
> >>>> +static void cs_etm_recording_free(struct auxtrace_record *itr)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       free(ptr);
> >>>> +}
> >>>> +
> >>>> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
> >>>> +{
> >>>> +       struct cs_etm_recording *ptr =
> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
> >>>> +       struct perf_evsel *evsel;
> >>>> +
> >>>> +       evlist__for_each(ptr->evlist, evsel) {
> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
> >>>> +                       return perf_evlist__enable_event_idx(ptr->evlist,
> >>>> +                                                            evsel, idx);
> >>>> +       }
> >>>> +
> >>>> +       return -EINVAL;
> >>>> +}
> >>>> +
> >>>> +struct auxtrace_record *cs_etm_record_init(int *err)
> >>>> +{
> >>>> +       struct perf_pmu *cs_etm_pmu;
> >>>> +       struct cs_etm_recording *ptr;
> >>>> +
> >>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
> >>>> +
> >>>> +       if (!cs_etm_pmu) {
> >>>> +               *err = -EINVAL;
> >>>> +               goto out;
> >>>> +       }
> >>>> +
> >>>> +       ptr = zalloc(sizeof(struct cs_etm_recording));
> >>>> +       if (!ptr) {
> >>>> +               *err = -ENOMEM;
> >>>> +               goto out;
> >>>> +       }
> >>>> +
> >>>> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
> >>>> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
> >>>> +       ptr->itr.recording_options      = cs_etm_recording_options;
> >>>> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
> >>>> +       ptr->itr.info_fill              = cs_etm_info_fill;
> >>>> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
> >>>> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
> >>>> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
> >>>> +       ptr->itr.reference              = cs_etm_reference;
> >>>> +       ptr->itr.free                   = cs_etm_recording_free;
> >>>> +       ptr->itr.read_finish            = cs_etm_read_finish;
> >>>> +
> >>>> +       *err = 0;
> >>>> +       return &ptr->itr;
> >>>> +out:
> >>>> +       return NULL;
> >>>> +}
> >>>> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
> >>>> new file mode 100644
> >>>> index 000000000000..7e85c1b43598
> >>>> --- /dev/null
> >>>> +++ b/tools/perf/arch/arm/util/cs_etm.h
> >>>> @@ -0,0 +1,44 @@
> >>>> +/*
> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>>> + *
> >>>> + * This program is free software; you can redistribute it and/or modify it
> >>>> + * under the terms of the GNU General Public License version 2 as published by
> >>>> + * the Free Software Foundation.
> >>>> + *
> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> >>>> + * more details.
> >>>> + *
> >>>> + * You should have received a copy of the GNU General Public License along with
> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> >>>> + */
> >>>> +
> >>>> +#ifndef INCLUDE__PERF_CS_ETM_H__
> >>>> +#define INCLUDE__PERF_CS_ETM_H__
> >>>> +
> >>>> +/* Beginning of header common to both ETMv3 and V4 */
> >>>> +enum {
> >>>> +       CS_ETM_MAGIC,
> >>>> +       CS_ETM_CPU,
> >>>> +       CS_ETM_SNAPSHOT,
> >>>> +};
> >>>> +
> >>>> +/* ETMv3/PTM metadata */
> >>>> +enum {
> >>>> +       /* Dynamic, configurable parameters */
> >>>> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
> >>>> +       CS_ETM_ETMTRACEIDR,
> >>>> +       /* RO, taken from sysFS */
> >>>> +       CS_ETM_ETMCCER,
> >>>> +       CS_ETM_ETMIDR,
> >>>> +       CS_ETM_PRIV_MAX,
> >>>> +};
> >>>> +
> >>>> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
> >>>> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
> >>>> +
> >>>> +struct auxtrace_record *cs_etm_record_init(int *err);
> >>>> +
> >>>> +#endif
> >>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> >>>> index cc1c9ce5cc56..a6f291dbc4d9 100644
> >>>> --- a/tools/perf/util/auxtrace.c
> >>>> +++ b/tools/perf/util/auxtrace.c
> >>>> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
> >>>>                 return intel_pt_process_auxtrace_info(event, session);
> >>>>         case PERF_AUXTRACE_INTEL_BTS:
> >>>>                 return intel_bts_process_auxtrace_info(event, session);
> >>>> +       case PERF_AUXTRACE_CS_ETM:
> >>>>         case PERF_AUXTRACE_UNKNOWN:
> >>>>         default:
> >>>>                 return -EINVAL;
> >>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> >>>> index e5a8e2d4f2af..adb53e7bcabf 100644
> >>>> --- a/tools/perf/util/auxtrace.h
> >>>> +++ b/tools/perf/util/auxtrace.h
> >>>> @@ -41,6 +41,7 @@ enum auxtrace_type {
> >>>>         PERF_AUXTRACE_UNKNOWN,
> >>>>         PERF_AUXTRACE_INTEL_PT,
> >>>>         PERF_AUXTRACE_INTEL_BTS,
> >>>> +       PERF_AUXTRACE_CS_ETM,
> >>>>  };
> >>>>
> >>>>  enum itrace_period_type {
> >>>> --
> >>>> 2.1.4
> >>>>
> >>>
> >>> Arnaldo,
> >>>
> >>> Last but not least, this is the final patch that I would like you to
> >>> review before queing.
> >>>
> >>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
> >>> be happy to use another baseline should that be more adequate for you.
> >>
> >> Adrian,
> >>
> >>       One more, are you ok with this?
> >
> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
> > mentioned that on another patch.
> >
> > However there is no decoder, which begs the question, is there anything you
> > can actually do with the perf.data file?  Might be a bit confusing for users
> > if they can capture traces but not use perf tools on the resulting perf.data
> > file?
> 
> We are working on a decoding library in parallel to this work.

Would be nice to be able to get both in the same patch kit, no? So that
one can both record and process the traces, verifying it all works.

- Arnaldo
 
> >
> > Nevertheless, for what is there now:
> >
> > Acked-by: Adrian Hunter <adrian.hunter@intel.com>
> 
> Thanks for the review.
> Mathieu
> 
> >

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

* Re: [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-01-29 21:12             ` Arnaldo Carvalho de Melo
@ 2016-01-29 22:24               ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-29 22:24 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Adrian Hunter, linux-kernel, linux-arm-kernel, Peter Zijlstra,
	Ingo Molnar

On 29 January 2016 at 14:12, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> Em Fri, Jan 29, 2016 at 10:37:48AM -0700, Mathieu Poirier escreveu:
>> On 29 January 2016 at 03:34, Adrian Hunter <adrian.hunter@intel.com> wrote:
>> > On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
>> >> Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
>> >>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>> >>>> Coresight ETMs are IP blocks used to perform HW assisted tracing
>> >>>> on a CPU core.  This patch introduce the required auxiliary API
>> >>>> functions allowing the perf core to interact with a tracer.
>> >>>>
>> >>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>> >>>> Cc: Ingo Molnar <mingo@redhat.com>
>> >>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>> >>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> >>>> ---
>> >>>>  MAINTAINERS                         |   3 +
>> >>>>  tools/perf/arch/arm/util/Build      |   2 +-
>> >>>>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
>> >>>>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
>> >>>>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
>> >>>>  tools/perf/util/auxtrace.c          |   1 +
>> >>>>  tools/perf/util/auxtrace.h          |   1 +
>> >>>>  7 files changed, 570 insertions(+), 1 deletion(-)
>> >>>>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
>> >>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
>> >>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
>> >>>>
>> >>>> diff --git a/MAINTAINERS b/MAINTAINERS
>> >>>> index b2a92245eece..a81b2737ebc3 100644
>> >>>> --- a/MAINTAINERS
>> >>>> +++ b/MAINTAINERS
>> >>>> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
>> >>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>> >>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>> >>>>  F:     tools/perf/arch/arm/util/pmu.c
>> >>>> +F:     tools/perf/arch/arm/util/auxtrace.c
>> >>>> +F:     tools/perf/arch/arm/util/cs_etm.c
>> >>>> +F:     tools/perf/arch/arm/util/cs_etm.h
>> >>>>
>> >>>>  ARM/CORGI MACHINE SUPPORT
>> >>>>  M:     Richard Purdie <rpurdie@rpsys.net>
>> >>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
>> >>>> index 66ab0b05549c..0a25a1248f42 100644
>> >>>> --- a/tools/perf/arch/arm/util/Build
>> >>>> +++ b/tools/perf/arch/arm/util/Build
>> >>>> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>> >>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>> >>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>> >>>>
>> >>>> -libperf-$(CONFIG_AUXTRACE) += pmu.o
>> >>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
>> >>>> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
>> >>>> new file mode 100644
>> >>>> index 000000000000..d327316f0e8a
>> >>>> --- /dev/null
>> >>>> +++ b/tools/perf/arch/arm/util/auxtrace.c
>> >>>> @@ -0,0 +1,54 @@
>> >>>> +/*
>> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>> >>>> + *
>> >>>> + * This program is free software; you can redistribute it and/or modify it
>> >>>> + * under the terms of the GNU General Public License version 2 as published by
>> >>>> + * the Free Software Foundation.
>> >>>> + *
>> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> >>>> + * more details.
>> >>>> + *
>> >>>> + * You should have received a copy of the GNU General Public License along with
>> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> >>>> + */
>> >>>> +
>> >>>> +#include <stdbool.h>
>> >>>> +#include <linux/coresight-pmu.h>
>> >>>> +
>> >>>> +#include "../../util/auxtrace.h"
>> >>>> +#include "../../util/evlist.h"
>> >>>> +#include "../../util/pmu.h"
>> >>>> +#include "cs_etm.h"
>> >>>> +
>> >>>> +struct auxtrace_record
>> >>>> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
>> >>>> +{
>> >>>> +       struct perf_pmu *cs_etm_pmu;
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +       bool found_etm = false;
>> >>>> +
>> >>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>> >>>> +
>> >>>> +        if (evlist) {
>> >>>> +               evlist__for_each(evlist, evsel) {
>> >>>> +                       if (cs_etm_pmu &&
>> >>>> +                           evsel->attr.type == cs_etm_pmu->type)
>> >>>> +                               found_etm = true;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       if (found_etm)
>> >>>> +               return cs_etm_record_init(err);
>> >>>> +
>> >>>> +       /*
>> >>>> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
>> >>>> +        * record can still be used even if tracers aren't present.  The NULL
>> >>>> +        * return value will take care of telling the infrastructure HW tracing
>> >>>> +        * isn't available.
>> >>>> +        */
>> >>>> +       *err = 0;
>> >>>> +       return NULL;
>> >>>> +}
>> >>>> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
>> >>>> new file mode 100644
>> >>>> index 000000000000..5710b90e23d5
>> >>>> --- /dev/null
>> >>>> +++ b/tools/perf/arch/arm/util/cs_etm.c
>> >>>> @@ -0,0 +1,466 @@
>> >>>> +/*
>> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>> >>>> + *
>> >>>> + * This program is free software; you can redistribute it and/or modify it
>> >>>> + * under the terms of the GNU General Public License version 2 as published by
>> >>>> + * the Free Software Foundation.
>> >>>> + *
>> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> >>>> + * more details.
>> >>>> + *
>> >>>> + * You should have received a copy of the GNU General Public License along with
>> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> >>>> + */
>> >>>> +
>> >>>> +#include <api/fs/fs.h>
>> >>>> +#include <linux/bitops.h>
>> >>>> +#include <linux/coresight-pmu.h>
>> >>>> +#include <linux/kernel.h>
>> >>>> +#include <linux/log2.h>
>> >>>> +#include <linux/types.h>
>> >>>> +
>> >>>> +#include "../../perf.h"
>> >>>> +#include "../../util/auxtrace.h"
>> >>>> +#include "../../util/cpumap.h"
>> >>>> +#include "../../util/evlist.h"
>> >>>> +#include "../../util/pmu.h"
>> >>>> +#include "../../util/thread_map.h"
>> >>>> +#include "cs_etm.h"
>> >>>> +
>> >>>> +#include <stdlib.h>
>> >>>> +
>> >>>> +#define KiB(x) ((x) * 1024)
>> >>>> +#define MiB(x) ((x) * 1024 * 1024)
>> >>>> +
>> >>>> +struct cs_etm_recording {
>> >>>> +       struct auxtrace_record  itr;
>> >>>> +       struct perf_pmu         *cs_etm_pmu;
>> >>>> +       struct perf_evlist      *evlist;
>> >>>> +       bool                    snapshot_mode;
>> >>>> +       size_t                  snapshot_size;
>> >>>> +};
>> >>>> +
>> >>>> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
>> >>>> +                                        struct record_opts *opts,
>> >>>> +                                        const char *str)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                               container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       unsigned long long snapshot_size = 0;
>> >>>> +       char *endptr;
>> >>>> +
>> >>>> +       if (str) {
>> >>>> +               snapshot_size = strtoull(str, &endptr, 0);
>> >>>> +               if (*endptr || snapshot_size > SIZE_MAX)
>> >>>> +                       return -1;
>> >>>> +       }
>> >>>> +
>> >>>> +       opts->auxtrace_snapshot_mode = true;
>> >>>> +       opts->auxtrace_snapshot_size = snapshot_size;
>> >>>> +       ptr->snapshot_size = snapshot_size;
>> >>>> +
>> >>>> +       return 0;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_recording_options(struct auxtrace_record *itr,
>> >>>> +                                   struct perf_evlist *evlist,
>> >>>> +                                   struct record_opts *opts)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                               container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>> >>>> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
>> >>>> +       const struct cpu_map *cpus = evlist->cpus;
>> >>>> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
>> >>>> +
>> >>>> +       ptr->evlist = evlist;
>> >>>> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
>> >>>> +
>> >>>> +       evlist__for_each(evlist, evsel) {
>> >>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>> >>>> +                       if (cs_etm_evsel) {
>> >>>> +                               pr_err("There may be only one %s event\n",
>> >>>> +                                      CORESIGHT_ETM_PMU_NAME);
>> >>>> +                               return -EINVAL;
>> >>>> +                       }
>> >>>> +                       evsel->attr.freq = 0;
>> >>>> +                       evsel->attr.sample_period = 1;
>> >>>> +                       cs_etm_evsel = evsel;
>> >>>> +                       opts->full_auxtrace = true;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       /* no need to continue if at least one event of interest was found */
>> >>>> +       if (!cs_etm_evsel)
>> >>>> +               return 0;
>> >>>> +
>> >>>> +       if (opts->use_clockid) {
>> >>>> +               pr_err("Cannot use clockid (-k option) with %s\n",
>> >>>> +                      CORESIGHT_ETM_PMU_NAME);
>> >>>> +               return -EINVAL;
>> >>>> +       }
>> >>>> +
>> >>>> +       /* we are in snapshot mode */
>> >>>> +       if (opts->auxtrace_snapshot_mode) {
>> >>>> +               /*
>> >>>> +                * No size were given to '-S' or '-m,', so go with
>> >>>> +                * the default
>> >>>> +                */
>> >>>> +               if (!opts->auxtrace_snapshot_size &&
>> >>>> +                   !opts->auxtrace_mmap_pages) {
>> >>>> +                       if (privileged) {
>> >>>> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
>> >>>> +                       } else {
>> >>>> +                               opts->auxtrace_mmap_pages =
>> >>>> +                                                       KiB(128) / page_size;
>> >>>> +                               if (opts->mmap_pages == UINT_MAX)
>> >>>> +                                       opts->mmap_pages = KiB(256) / page_size;
>> >>>> +                       }
>> >>>> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
>> >>>> +                                               opts->mmap_pages == UINT_MAX) {
>> >>>> +                       opts->mmap_pages = KiB(256) / page_size;
>> >>>> +               }
>> >>>> +
>> >>>> +               /*
>> >>>> +                * '-m,xyz' was specified but no snapshot size, so make the
>> >>>> +                * snapshot size as big as the auxtrace mmap area.
>> >>>> +                */
>> >>>> +               if (!opts->auxtrace_snapshot_size) {
>> >>>> +                       opts->auxtrace_snapshot_size =
>> >>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
>> >>>> +               }
>> >>>> +
>> >>>> +               /*
>> >>>> +                * -Sxyz was specified but no auxtrace mmap area, so make the
>> >>>> +                * auxtrace mmap area big enough to fit the requested snapshot
>> >>>> +                * size.
>> >>>> +                */
>> >>>> +               if (!opts->auxtrace_mmap_pages) {
>> >>>> +                       size_t sz = opts->auxtrace_snapshot_size;
>> >>>> +
>> >>>> +                       sz = round_up(sz, page_size) / page_size;
>> >>>> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
>> >>>> +               }
>> >>>> +
>> >>>> +               /* Snapshost size can't be bigger than the auxtrace area */
>> >>>> +               if (opts->auxtrace_snapshot_size >
>> >>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
>> >>>> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
>> >>>> +                              opts->auxtrace_snapshot_size,
>> >>>> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
>> >>>> +                       return -EINVAL;
>> >>>> +               }
>> >>>> +
>> >>>> +               /* Something went wrong somewhere - this shouldn't happen */
>> >>>> +               if (!opts->auxtrace_snapshot_size ||
>> >>>> +                   !opts->auxtrace_mmap_pages) {
>> >>>> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
>> >>>> +                       return -EINVAL;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
>> >>>> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
>> >>>> +               if (privileged) {
>> >>>> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
>> >>>> +               } else {
>> >>>> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
>> >>>> +                       if (opts->mmap_pages == UINT_MAX)
>> >>>> +                               opts->mmap_pages = KiB(256) / page_size;
>> >>>> +               }
>> >>>> +
>> >>>> +       }
>> >>>> +
>> >>>> +       /* Validate auxtrace_mmap_pages provided by user */
>> >>>> +       if (opts->auxtrace_mmap_pages) {
>> >>>> +               unsigned int max_page = (KiB(128) / page_size);
>> >>>> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
>> >>>> +
>> >>>> +               if (!privileged &&
>> >>>> +                   opts->auxtrace_mmap_pages > max_page) {
>> >>>> +                       opts->auxtrace_mmap_pages = max_page;
>> >>>> +                       pr_err("auxtrace too big, truncating to %d\n",
>> >>>> +                              max_page);
>> >>>> +               }
>> >>>> +
>> >>>> +               if (!is_power_of_2(sz)) {
>> >>>> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
>> >>>> +                              CORESIGHT_ETM_PMU_NAME);
>> >>>> +                       return -EINVAL;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       if (opts->auxtrace_snapshot_mode)
>> >>>> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
>> >>>> +                         opts->auxtrace_snapshot_size);
>> >>>> +
>> >>>> +       if (cs_etm_evsel) {
>> >>>> +               /*
>> >>>> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
>> >>>> +                * event must come first.
>> >>>> +                */
>> >>>> +               perf_evlist__to_front(evlist, cs_etm_evsel);
>> >>>> +               /*
>> >>>> +                * In the case of per-cpu mmaps, we need the CPU on the
>> >>>> +                * AUX event.
>> >>>> +                */
>> >>>> +               if (!cpu_map__empty(cpus))
>> >>>> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
>> >>>> +       }
>> >>>> +
>> >>>> +       /* Add dummy event to keep tracking */
>> >>>> +       if (opts->full_auxtrace) {
>> >>>> +               struct perf_evsel *tracking_evsel;
>> >>>> +               int err;
>> >>>> +
>> >>>> +               err = parse_events(evlist, "dummy:u", NULL);
>> >>>> +               if (err)
>> >>>> +                       return err;
>> >>>> +
>> >>>> +               tracking_evsel = perf_evlist__last(evlist);
>> >>>> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
>> >>>> +
>> >>>> +               tracking_evsel->attr.freq = 0;
>> >>>> +               tracking_evsel->attr.sample_period = 1;
>> >>>> +
>> >>>> +               /* In per-cpu case, always need the time of mmap events etc */
>> >>>> +               if (!cpu_map__empty(cpus))
>> >>>> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
>> >>>> +       }
>> >>>> +
>> >>>> +       return 0;
>> >>>> +}
>> >>>> +
>> >>>> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
>> >>>> +{
>> >>>> +       u64 config = 0;
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>> >>>> +       struct perf_evlist *evlist = ptr->evlist;
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +
>> >>>> +       evlist__for_each(evlist, evsel) {
>> >>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>> >>>> +                       /*
>> >>>> +                        * Variable perf_event_attr::config is assigned to
>> >>>> +                        * ETMv3/PTM.  The bit fields have been made to match
>> >>>> +                        * the ETMv3.5 ETRMCR register specification.  See the
>> >>>> +                        * PMU_FORMAT_ATTR() declarations in
>> >>>> +                        * drivers/hwtracing/coresight/coresight-perf.c for
>> >>>> +                        * details.
>> >>>> +                        */
>> >>>> +                       config = evsel->attr.config;
>> >>>> +                       break;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       return config;
>> >>>> +}
>> >>>> +
>> >>>> +static size_t
>> >>>> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>> >>>> +                     struct perf_evlist *evlist __maybe_unused)
>> >>>> +{
>> >>>> +       int records;
>> >>>> +       const struct cpu_map *cpus = evlist->cpus;
>> >>>> +
>> >>>> +       if (!cpu_map__empty(cpus)) {
>> >>>> +               records = cpu_map__nr(cpus);
>> >>>> +               goto out;
>> >>>> +       }
>> >>>> +
>> >>>> +       set_max_cpu_num();
>> >>>> +       records = cpu__max_cpu();
>> >>>> +out:
>> >>>> +       return records * CS_ETM_PRIV_SIZE;
>> >>>> +}
>> >>>> +
>> >>>> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
>> >>>> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
>> >>>> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
>> >>>> +};
>> >>>> +
>> >>>> +static int cs_etm_get_metadata(int cpu, int index,
>> >>>> +                              struct auxtrace_record *itr,
>> >>>> +                              struct auxtrace_info_event *info)
>> >>>> +{
>> >>>> +       char path[PATH_MAX];
>> >>>> +       int offset = 0, ret = 0;
>> >>>> +       int i, scan;
>> >>>> +       unsigned int val;
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>> >>>> +
>> >>>> +       offset = index * CS_ETM_PRIV_MAX;
>> >>>> +
>> >>>> +       /* Build generic header portion */
>> >>>> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
>> >>>> +       info->priv[offset + CS_ETM_CPU] = cpu;
>> >>>> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
>> >>>> +
>> >>>> +       /* Get user configurables from the session */
>> >>>> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
>> >>>> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
>> >>>> +
>> >>>> +       /* Get RO metadata from sysfs */
>> >>>> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
>> >>>> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
>> >>>> +
>> >>>> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
>> >>>> +               if (scan != 1) {
>> >>>> +                       ret = -EINVAL;
>> >>>> +                       break;
>> >>>> +               }
>> >>>> +
>> >>>> +               info->priv[offset + i] = val;
>> >>>> +       }
>> >>>> +
>> >>>> +       return ret;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_info_fill(struct auxtrace_record *itr,
>> >>>> +                           struct perf_session *session,
>> >>>> +                           struct auxtrace_info_event *auxtrace_info,
>> >>>> +                           size_t priv_size)
>> >>>> +{
>> >>>> +       int i, nr_cpu, ret = 0;
>> >>>> +       const struct cpu_map *cpus = session->evlist->cpus;
>> >>>> +
>> >>>> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
>> >>>> +               return -EINVAL;
>> >>>> +
>> >>>> +       if (!session->evlist->nr_mmaps)
>> >>>> +               return -EINVAL;
>> >>>> +
>> >>>> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
>> >>>> +
>> >>>> +       /* cpu map is not empty, we have specific CPUs to work with */
>> >>>> +       if (!cpu_map__empty(cpus)) {
>> >>>> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
>> >>>> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
>> >>>> +                                                 itr, auxtrace_info);
>> >>>> +                       if (ret)
>> >>>> +                               goto out;
>> >>>> +               }
>> >>>> +       } else {
>> >>>> +               /* get configuration for all CPUs in the system */
>> >>>> +               nr_cpu = cpu__max_cpu();
>> >>>> +               for (i = 0; i < nr_cpu; i++) {
>> >>>> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
>> >>>> +                       if (ret)
>> >>>> +                               goto out;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +out:
>> >>>> +       return ret;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
>> >>>> +                               int idx, struct auxtrace_mmap *mm,
>> >>>> +                               unsigned char *data __maybe_unused,
>> >>>> +                               u64 *head, u64 *old)
>> >>>> +{
>> >>>> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
>> >>>> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
>> >>>> +
>> >>>> +       *old = *head;
>> >>>> +       *head += mm->len;
>> >>>> +
>> >>>> +       return 0;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +
>> >>>> +       evlist__for_each(ptr->evlist, evsel) {
>> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>> >>>> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
>> >>>> +       }
>> >>>> +       return -EINVAL;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +
>> >>>> +       evlist__for_each(ptr->evlist, evsel) {
>> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>> >>>> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
>> >>>> +       }
>> >>>> +       return -EINVAL;
>> >>>> +}
>> >>>> +
>> >>>> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
>> >>>> +{
>> >>>> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
>> >>>> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
>> >>>> +}
>> >>>> +
>> >>>> +static void cs_etm_recording_free(struct auxtrace_record *itr)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       free(ptr);
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +
>> >>>> +       evlist__for_each(ptr->evlist, evsel) {
>> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>> >>>> +                       return perf_evlist__enable_event_idx(ptr->evlist,
>> >>>> +                                                            evsel, idx);
>> >>>> +       }
>> >>>> +
>> >>>> +       return -EINVAL;
>> >>>> +}
>> >>>> +
>> >>>> +struct auxtrace_record *cs_etm_record_init(int *err)
>> >>>> +{
>> >>>> +       struct perf_pmu *cs_etm_pmu;
>> >>>> +       struct cs_etm_recording *ptr;
>> >>>> +
>> >>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>> >>>> +
>> >>>> +       if (!cs_etm_pmu) {
>> >>>> +               *err = -EINVAL;
>> >>>> +               goto out;
>> >>>> +       }
>> >>>> +
>> >>>> +       ptr = zalloc(sizeof(struct cs_etm_recording));
>> >>>> +       if (!ptr) {
>> >>>> +               *err = -ENOMEM;
>> >>>> +               goto out;
>> >>>> +       }
>> >>>> +
>> >>>> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
>> >>>> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
>> >>>> +       ptr->itr.recording_options      = cs_etm_recording_options;
>> >>>> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
>> >>>> +       ptr->itr.info_fill              = cs_etm_info_fill;
>> >>>> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
>> >>>> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
>> >>>> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
>> >>>> +       ptr->itr.reference              = cs_etm_reference;
>> >>>> +       ptr->itr.free                   = cs_etm_recording_free;
>> >>>> +       ptr->itr.read_finish            = cs_etm_read_finish;
>> >>>> +
>> >>>> +       *err = 0;
>> >>>> +       return &ptr->itr;
>> >>>> +out:
>> >>>> +       return NULL;
>> >>>> +}
>> >>>> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
>> >>>> new file mode 100644
>> >>>> index 000000000000..7e85c1b43598
>> >>>> --- /dev/null
>> >>>> +++ b/tools/perf/arch/arm/util/cs_etm.h
>> >>>> @@ -0,0 +1,44 @@
>> >>>> +/*
>> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>> >>>> + *
>> >>>> + * This program is free software; you can redistribute it and/or modify it
>> >>>> + * under the terms of the GNU General Public License version 2 as published by
>> >>>> + * the Free Software Foundation.
>> >>>> + *
>> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> >>>> + * more details.
>> >>>> + *
>> >>>> + * You should have received a copy of the GNU General Public License along with
>> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> >>>> + */
>> >>>> +
>> >>>> +#ifndef INCLUDE__PERF_CS_ETM_H__
>> >>>> +#define INCLUDE__PERF_CS_ETM_H__
>> >>>> +
>> >>>> +/* Beginning of header common to both ETMv3 and V4 */
>> >>>> +enum {
>> >>>> +       CS_ETM_MAGIC,
>> >>>> +       CS_ETM_CPU,
>> >>>> +       CS_ETM_SNAPSHOT,
>> >>>> +};
>> >>>> +
>> >>>> +/* ETMv3/PTM metadata */
>> >>>> +enum {
>> >>>> +       /* Dynamic, configurable parameters */
>> >>>> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
>> >>>> +       CS_ETM_ETMTRACEIDR,
>> >>>> +       /* RO, taken from sysFS */
>> >>>> +       CS_ETM_ETMCCER,
>> >>>> +       CS_ETM_ETMIDR,
>> >>>> +       CS_ETM_PRIV_MAX,
>> >>>> +};
>> >>>> +
>> >>>> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
>> >>>> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
>> >>>> +
>> >>>> +struct auxtrace_record *cs_etm_record_init(int *err);
>> >>>> +
>> >>>> +#endif
>> >>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>> >>>> index cc1c9ce5cc56..a6f291dbc4d9 100644
>> >>>> --- a/tools/perf/util/auxtrace.c
>> >>>> +++ b/tools/perf/util/auxtrace.c
>> >>>> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
>> >>>>                 return intel_pt_process_auxtrace_info(event, session);
>> >>>>         case PERF_AUXTRACE_INTEL_BTS:
>> >>>>                 return intel_bts_process_auxtrace_info(event, session);
>> >>>> +       case PERF_AUXTRACE_CS_ETM:
>> >>>>         case PERF_AUXTRACE_UNKNOWN:
>> >>>>         default:
>> >>>>                 return -EINVAL;
>> >>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>> >>>> index e5a8e2d4f2af..adb53e7bcabf 100644
>> >>>> --- a/tools/perf/util/auxtrace.h
>> >>>> +++ b/tools/perf/util/auxtrace.h
>> >>>> @@ -41,6 +41,7 @@ enum auxtrace_type {
>> >>>>         PERF_AUXTRACE_UNKNOWN,
>> >>>>         PERF_AUXTRACE_INTEL_PT,
>> >>>>         PERF_AUXTRACE_INTEL_BTS,
>> >>>> +       PERF_AUXTRACE_CS_ETM,
>> >>>>  };
>> >>>>
>> >>>>  enum itrace_period_type {
>> >>>> --
>> >>>> 2.1.4
>> >>>>
>> >>>
>> >>> Arnaldo,
>> >>>
>> >>> Last but not least, this is the final patch that I would like you to
>> >>> review before queing.
>> >>>
>> >>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>> >>> be happy to use another baseline should that be more adequate for you.
>> >>
>> >> Adrian,
>> >>
>> >>       One more, are you ok with this?
>> >
>> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
>> > mentioned that on another patch.
>> >
>> > However there is no decoder, which begs the question, is there anything you
>> > can actually do with the perf.data file?  Might be a bit confusing for users
>> > if they can capture traces but not use perf tools on the resulting perf.data
>> > file?
>>
>> We are working on a decoding library in parallel to this work.
>
> Would be nice to be able to get both in the same patch kit, no? So that
> one can both record and process the traces, verifying it all works.

We are still a few weeks away from being in a position where the
community can start playing with the decoding library.  I can hold off
on the "perf tools" patches when I queue the kernel side of the work
for 4.6 but since you and Adrian have already reviewed the work it
would be nice to have that part included as well.

We've been playing with the perf.data files for a couple of months now
and things look at the right place.  This isn't surprising since we
are using the same framework as X86.

I think the generation of the perf.data file should be coupled with
the submission of the kernel driver but would also respect a diverging
point of view.  Simply let me know what you prefer and I will adjust
V9 accordingly.

Regards,
Mathieu

>
> - Arnaldo
>
>> >
>> > Nevertheless, for what is there now:
>> >
>> > Acked-by: Adrian Hunter <adrian.hunter@intel.com>
>>
>> Thanks for the review.
>> Mathieu
>>
>> >

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-01-29 22:24               ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-01-29 22:24 UTC (permalink / raw)
  To: linux-arm-kernel

On 29 January 2016 at 14:12, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> Em Fri, Jan 29, 2016 at 10:37:48AM -0700, Mathieu Poirier escreveu:
>> On 29 January 2016 at 03:34, Adrian Hunter <adrian.hunter@intel.com> wrote:
>> > On 25/01/16 23:10, Arnaldo Carvalho de Melo wrote:
>> >> Em Mon, Jan 25, 2016 at 01:51:18PM -0700, Mathieu Poirier escreveu:
>> >>> On 14 January 2016 at 14:46, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>> >>>> Coresight ETMs are IP blocks used to perform HW assisted tracing
>> >>>> on a CPU core.  This patch introduce the required auxiliary API
>> >>>> functions allowing the perf core to interact with a tracer.
>> >>>>
>> >>>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>> >>>> Cc: Ingo Molnar <mingo@redhat.com>
>> >>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>> >>>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> >>>> ---
>> >>>>  MAINTAINERS                         |   3 +
>> >>>>  tools/perf/arch/arm/util/Build      |   2 +-
>> >>>>  tools/perf/arch/arm/util/auxtrace.c |  54 +++++
>> >>>>  tools/perf/arch/arm/util/cs_etm.c   | 466 ++++++++++++++++++++++++++++++++++++
>> >>>>  tools/perf/arch/arm/util/cs_etm.h   |  44 ++++
>> >>>>  tools/perf/util/auxtrace.c          |   1 +
>> >>>>  tools/perf/util/auxtrace.h          |   1 +
>> >>>>  7 files changed, 570 insertions(+), 1 deletion(-)
>> >>>>  create mode 100644 tools/perf/arch/arm/util/auxtrace.c
>> >>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.c
>> >>>>  create mode 100644 tools/perf/arch/arm/util/cs_etm.h
>> >>>>
>> >>>> diff --git a/MAINTAINERS b/MAINTAINERS
>> >>>> index b2a92245eece..a81b2737ebc3 100644
>> >>>> --- a/MAINTAINERS
>> >>>> +++ b/MAINTAINERS
>> >>>> @@ -1008,6 +1008,9 @@ F:        Documentation/trace/coresight.txt
>> >>>>  F:     Documentation/devicetree/bindings/arm/coresight.txt
>> >>>>  F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
>> >>>>  F:     tools/perf/arch/arm/util/pmu.c
>> >>>> +F:     tools/perf/arch/arm/util/auxtrace.c
>> >>>> +F:     tools/perf/arch/arm/util/cs_etm.c
>> >>>> +F:     tools/perf/arch/arm/util/cs_etm.h
>> >>>>
>> >>>>  ARM/CORGI MACHINE SUPPORT
>> >>>>  M:     Richard Purdie <rpurdie@rpsys.net>
>> >>>> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
>> >>>> index 66ab0b05549c..0a25a1248f42 100644
>> >>>> --- a/tools/perf/arch/arm/util/Build
>> >>>> +++ b/tools/perf/arch/arm/util/Build
>> >>>> @@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
>> >>>>  libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>> >>>>  libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>> >>>>
>> >>>> -libperf-$(CONFIG_AUXTRACE) += pmu.o
>> >>>> +libperf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs_etm.o
>> >>>> diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
>> >>>> new file mode 100644
>> >>>> index 000000000000..d327316f0e8a
>> >>>> --- /dev/null
>> >>>> +++ b/tools/perf/arch/arm/util/auxtrace.c
>> >>>> @@ -0,0 +1,54 @@
>> >>>> +/*
>> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>> >>>> + *
>> >>>> + * This program is free software; you can redistribute it and/or modify it
>> >>>> + * under the terms of the GNU General Public License version 2 as published by
>> >>>> + * the Free Software Foundation.
>> >>>> + *
>> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> >>>> + * more details.
>> >>>> + *
>> >>>> + * You should have received a copy of the GNU General Public License along with
>> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> >>>> + */
>> >>>> +
>> >>>> +#include <stdbool.h>
>> >>>> +#include <linux/coresight-pmu.h>
>> >>>> +
>> >>>> +#include "../../util/auxtrace.h"
>> >>>> +#include "../../util/evlist.h"
>> >>>> +#include "../../util/pmu.h"
>> >>>> +#include "cs_etm.h"
>> >>>> +
>> >>>> +struct auxtrace_record
>> >>>> +*auxtrace_record__init(struct perf_evlist *evlist, int *err)
>> >>>> +{
>> >>>> +       struct perf_pmu *cs_etm_pmu;
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +       bool found_etm = false;
>> >>>> +
>> >>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>> >>>> +
>> >>>> +        if (evlist) {
>> >>>> +               evlist__for_each(evlist, evsel) {
>> >>>> +                       if (cs_etm_pmu &&
>> >>>> +                           evsel->attr.type == cs_etm_pmu->type)
>> >>>> +                               found_etm = true;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       if (found_etm)
>> >>>> +               return cs_etm_record_init(err);
>> >>>> +
>> >>>> +       /*
>> >>>> +        * Clear 'err' even if we haven't found a cs_etm event - that way perf
>> >>>> +        * record can still be used even if tracers aren't present.  The NULL
>> >>>> +        * return value will take care of telling the infrastructure HW tracing
>> >>>> +        * isn't available.
>> >>>> +        */
>> >>>> +       *err = 0;
>> >>>> +       return NULL;
>> >>>> +}
>> >>>> diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
>> >>>> new file mode 100644
>> >>>> index 000000000000..5710b90e23d5
>> >>>> --- /dev/null
>> >>>> +++ b/tools/perf/arch/arm/util/cs_etm.c
>> >>>> @@ -0,0 +1,466 @@
>> >>>> +/*
>> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>> >>>> + *
>> >>>> + * This program is free software; you can redistribute it and/or modify it
>> >>>> + * under the terms of the GNU General Public License version 2 as published by
>> >>>> + * the Free Software Foundation.
>> >>>> + *
>> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> >>>> + * more details.
>> >>>> + *
>> >>>> + * You should have received a copy of the GNU General Public License along with
>> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> >>>> + */
>> >>>> +
>> >>>> +#include <api/fs/fs.h>
>> >>>> +#include <linux/bitops.h>
>> >>>> +#include <linux/coresight-pmu.h>
>> >>>> +#include <linux/kernel.h>
>> >>>> +#include <linux/log2.h>
>> >>>> +#include <linux/types.h>
>> >>>> +
>> >>>> +#include "../../perf.h"
>> >>>> +#include "../../util/auxtrace.h"
>> >>>> +#include "../../util/cpumap.h"
>> >>>> +#include "../../util/evlist.h"
>> >>>> +#include "../../util/pmu.h"
>> >>>> +#include "../../util/thread_map.h"
>> >>>> +#include "cs_etm.h"
>> >>>> +
>> >>>> +#include <stdlib.h>
>> >>>> +
>> >>>> +#define KiB(x) ((x) * 1024)
>> >>>> +#define MiB(x) ((x) * 1024 * 1024)
>> >>>> +
>> >>>> +struct cs_etm_recording {
>> >>>> +       struct auxtrace_record  itr;
>> >>>> +       struct perf_pmu         *cs_etm_pmu;
>> >>>> +       struct perf_evlist      *evlist;
>> >>>> +       bool                    snapshot_mode;
>> >>>> +       size_t                  snapshot_size;
>> >>>> +};
>> >>>> +
>> >>>> +static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
>> >>>> +                                        struct record_opts *opts,
>> >>>> +                                        const char *str)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                               container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       unsigned long long snapshot_size = 0;
>> >>>> +       char *endptr;
>> >>>> +
>> >>>> +       if (str) {
>> >>>> +               snapshot_size = strtoull(str, &endptr, 0);
>> >>>> +               if (*endptr || snapshot_size > SIZE_MAX)
>> >>>> +                       return -1;
>> >>>> +       }
>> >>>> +
>> >>>> +       opts->auxtrace_snapshot_mode = true;
>> >>>> +       opts->auxtrace_snapshot_size = snapshot_size;
>> >>>> +       ptr->snapshot_size = snapshot_size;
>> >>>> +
>> >>>> +       return 0;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_recording_options(struct auxtrace_record *itr,
>> >>>> +                                   struct perf_evlist *evlist,
>> >>>> +                                   struct record_opts *opts)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                               container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>> >>>> +       struct perf_evsel *evsel, *cs_etm_evsel = NULL;
>> >>>> +       const struct cpu_map *cpus = evlist->cpus;
>> >>>> +       bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
>> >>>> +
>> >>>> +       ptr->evlist = evlist;
>> >>>> +       ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
>> >>>> +
>> >>>> +       evlist__for_each(evlist, evsel) {
>> >>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>> >>>> +                       if (cs_etm_evsel) {
>> >>>> +                               pr_err("There may be only one %s event\n",
>> >>>> +                                      CORESIGHT_ETM_PMU_NAME);
>> >>>> +                               return -EINVAL;
>> >>>> +                       }
>> >>>> +                       evsel->attr.freq = 0;
>> >>>> +                       evsel->attr.sample_period = 1;
>> >>>> +                       cs_etm_evsel = evsel;
>> >>>> +                       opts->full_auxtrace = true;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       /* no need to continue if at least one event of interest was found */
>> >>>> +       if (!cs_etm_evsel)
>> >>>> +               return 0;
>> >>>> +
>> >>>> +       if (opts->use_clockid) {
>> >>>> +               pr_err("Cannot use clockid (-k option) with %s\n",
>> >>>> +                      CORESIGHT_ETM_PMU_NAME);
>> >>>> +               return -EINVAL;
>> >>>> +       }
>> >>>> +
>> >>>> +       /* we are in snapshot mode */
>> >>>> +       if (opts->auxtrace_snapshot_mode) {
>> >>>> +               /*
>> >>>> +                * No size were given to '-S' or '-m,', so go with
>> >>>> +                * the default
>> >>>> +                */
>> >>>> +               if (!opts->auxtrace_snapshot_size &&
>> >>>> +                   !opts->auxtrace_mmap_pages) {
>> >>>> +                       if (privileged) {
>> >>>> +                               opts->auxtrace_mmap_pages = MiB(4) / page_size;
>> >>>> +                       } else {
>> >>>> +                               opts->auxtrace_mmap_pages =
>> >>>> +                                                       KiB(128) / page_size;
>> >>>> +                               if (opts->mmap_pages == UINT_MAX)
>> >>>> +                                       opts->mmap_pages = KiB(256) / page_size;
>> >>>> +                       }
>> >>>> +               } else if (!opts->auxtrace_mmap_pages && !privileged &&
>> >>>> +                                               opts->mmap_pages == UINT_MAX) {
>> >>>> +                       opts->mmap_pages = KiB(256) / page_size;
>> >>>> +               }
>> >>>> +
>> >>>> +               /*
>> >>>> +                * '-m,xyz' was specified but no snapshot size, so make the
>> >>>> +                * snapshot size as big as the auxtrace mmap area.
>> >>>> +                */
>> >>>> +               if (!opts->auxtrace_snapshot_size) {
>> >>>> +                       opts->auxtrace_snapshot_size =
>> >>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size;
>> >>>> +               }
>> >>>> +
>> >>>> +               /*
>> >>>> +                * -Sxyz was specified but no auxtrace mmap area, so make the
>> >>>> +                * auxtrace mmap area big enough to fit the requested snapshot
>> >>>> +                * size.
>> >>>> +                */
>> >>>> +               if (!opts->auxtrace_mmap_pages) {
>> >>>> +                       size_t sz = opts->auxtrace_snapshot_size;
>> >>>> +
>> >>>> +                       sz = round_up(sz, page_size) / page_size;
>> >>>> +                       opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
>> >>>> +               }
>> >>>> +
>> >>>> +               /* Snapshost size can't be bigger than the auxtrace area */
>> >>>> +               if (opts->auxtrace_snapshot_size >
>> >>>> +                               opts->auxtrace_mmap_pages * (size_t)page_size) {
>> >>>> +                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
>> >>>> +                              opts->auxtrace_snapshot_size,
>> >>>> +                              opts->auxtrace_mmap_pages * (size_t)page_size);
>> >>>> +                       return -EINVAL;
>> >>>> +               }
>> >>>> +
>> >>>> +               /* Something went wrong somewhere - this shouldn't happen */
>> >>>> +               if (!opts->auxtrace_snapshot_size ||
>> >>>> +                   !opts->auxtrace_mmap_pages) {
>> >>>> +                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
>> >>>> +                       return -EINVAL;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       /* We are in full trace mode but '-m,xyz' wasn't specified */
>> >>>> +        if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
>> >>>> +               if (privileged) {
>> >>>> +                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
>> >>>> +               } else {
>> >>>> +                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
>> >>>> +                       if (opts->mmap_pages == UINT_MAX)
>> >>>> +                               opts->mmap_pages = KiB(256) / page_size;
>> >>>> +               }
>> >>>> +
>> >>>> +       }
>> >>>> +
>> >>>> +       /* Validate auxtrace_mmap_pages provided by user */
>> >>>> +       if (opts->auxtrace_mmap_pages) {
>> >>>> +               unsigned int max_page = (KiB(128) / page_size);
>> >>>> +               size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
>> >>>> +
>> >>>> +               if (!privileged &&
>> >>>> +                   opts->auxtrace_mmap_pages > max_page) {
>> >>>> +                       opts->auxtrace_mmap_pages = max_page;
>> >>>> +                       pr_err("auxtrace too big, truncating to %d\n",
>> >>>> +                              max_page);
>> >>>> +               }
>> >>>> +
>> >>>> +               if (!is_power_of_2(sz)) {
>> >>>> +                       pr_err("Invalid mmap size for %s: must be a power of 2\n",
>> >>>> +                              CORESIGHT_ETM_PMU_NAME);
>> >>>> +                       return -EINVAL;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       if (opts->auxtrace_snapshot_mode)
>> >>>> +               pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
>> >>>> +                         opts->auxtrace_snapshot_size);
>> >>>> +
>> >>>> +       if (cs_etm_evsel) {
>> >>>> +               /*
>> >>>> +                * To obtain the auxtrace buffer file descriptor, the auxtrace
>> >>>> +                * event must come first.
>> >>>> +                */
>> >>>> +               perf_evlist__to_front(evlist, cs_etm_evsel);
>> >>>> +               /*
>> >>>> +                * In the case of per-cpu mmaps, we need the CPU on the
>> >>>> +                * AUX event.
>> >>>> +                */
>> >>>> +               if (!cpu_map__empty(cpus))
>> >>>> +                       perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
>> >>>> +       }
>> >>>> +
>> >>>> +       /* Add dummy event to keep tracking */
>> >>>> +       if (opts->full_auxtrace) {
>> >>>> +               struct perf_evsel *tracking_evsel;
>> >>>> +               int err;
>> >>>> +
>> >>>> +               err = parse_events(evlist, "dummy:u", NULL);
>> >>>> +               if (err)
>> >>>> +                       return err;
>> >>>> +
>> >>>> +               tracking_evsel = perf_evlist__last(evlist);
>> >>>> +               perf_evlist__set_tracking_event(evlist, tracking_evsel);
>> >>>> +
>> >>>> +               tracking_evsel->attr.freq = 0;
>> >>>> +               tracking_evsel->attr.sample_period = 1;
>> >>>> +
>> >>>> +               /* In per-cpu case, always need the time of mmap events etc */
>> >>>> +               if (!cpu_map__empty(cpus))
>> >>>> +                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
>> >>>> +       }
>> >>>> +
>> >>>> +       return 0;
>> >>>> +}
>> >>>> +
>> >>>> +static u64 cs_etm_get_config(struct auxtrace_record *itr)
>> >>>> +{
>> >>>> +       u64 config = 0;
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>> >>>> +       struct perf_evlist *evlist = ptr->evlist;
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +
>> >>>> +       evlist__for_each(evlist, evsel) {
>> >>>> +               if (evsel->attr.type == cs_etm_pmu->type) {
>> >>>> +                       /*
>> >>>> +                        * Variable perf_event_attr::config is assigned to
>> >>>> +                        * ETMv3/PTM.  The bit fields have been made to match
>> >>>> +                        * the ETMv3.5 ETRMCR register specification.  See the
>> >>>> +                        * PMU_FORMAT_ATTR() declarations in
>> >>>> +                        * drivers/hwtracing/coresight/coresight-perf.c for
>> >>>> +                        * details.
>> >>>> +                        */
>> >>>> +                       config = evsel->attr.config;
>> >>>> +                       break;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +       return config;
>> >>>> +}
>> >>>> +
>> >>>> +static size_t
>> >>>> +cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
>> >>>> +                     struct perf_evlist *evlist __maybe_unused)
>> >>>> +{
>> >>>> +       int records;
>> >>>> +       const struct cpu_map *cpus = evlist->cpus;
>> >>>> +
>> >>>> +       if (!cpu_map__empty(cpus)) {
>> >>>> +               records = cpu_map__nr(cpus);
>> >>>> +               goto out;
>> >>>> +       }
>> >>>> +
>> >>>> +       set_max_cpu_num();
>> >>>> +       records = cpu__max_cpu();
>> >>>> +out:
>> >>>> +       return records * CS_ETM_PRIV_SIZE;
>> >>>> +}
>> >>>> +
>> >>>> +static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
>> >>>> +       [CS_ETM_ETMCCER]        = "mgmt/etmccer",
>> >>>> +       [CS_ETM_ETMIDR]         = "mgmt/etmidr",
>> >>>> +};
>> >>>> +
>> >>>> +static int cs_etm_get_metadata(int cpu, int index,
>> >>>> +                              struct auxtrace_record *itr,
>> >>>> +                              struct auxtrace_info_event *info)
>> >>>> +{
>> >>>> +       char path[PATH_MAX];
>> >>>> +       int offset = 0, ret = 0;
>> >>>> +       int i, scan;
>> >>>> +       unsigned int val;
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
>> >>>> +
>> >>>> +       offset = index * CS_ETM_PRIV_MAX;
>> >>>> +
>> >>>> +       /* Build generic header portion */
>> >>>> +       info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
>> >>>> +       info->priv[offset + CS_ETM_CPU] = cpu;
>> >>>> +       info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
>> >>>> +
>> >>>> +       /* Get user configurables from the session */
>> >>>> +       info->priv[offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
>> >>>> +       info->priv[offset + CS_ETM_ETMTRACEIDR] = coresight_get_trace_id(cpu);
>> >>>> +
>> >>>> +       /* Get RO metadata from sysfs */
>> >>>> +       for (i = CS_ETM_ETMCCER; i < CS_ETM_PRIV_MAX; i++) {
>> >>>> +               snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv3_ro[i]);
>> >>>> +
>> >>>> +               scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
>> >>>> +               if (scan != 1) {
>> >>>> +                       ret = -EINVAL;
>> >>>> +                       break;
>> >>>> +               }
>> >>>> +
>> >>>> +               info->priv[offset + i] = val;
>> >>>> +       }
>> >>>> +
>> >>>> +       return ret;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_info_fill(struct auxtrace_record *itr,
>> >>>> +                           struct perf_session *session,
>> >>>> +                           struct auxtrace_info_event *auxtrace_info,
>> >>>> +                           size_t priv_size)
>> >>>> +{
>> >>>> +       int i, nr_cpu, ret = 0;
>> >>>> +       const struct cpu_map *cpus = session->evlist->cpus;
>> >>>> +
>> >>>> +       if (priv_size != cs_etm_info_priv_size(itr, session->evlist))
>> >>>> +               return -EINVAL;
>> >>>> +
>> >>>> +       if (!session->evlist->nr_mmaps)
>> >>>> +               return -EINVAL;
>> >>>> +
>> >>>> +       auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
>> >>>> +
>> >>>> +       /* cpu map is not empty, we have specific CPUs to work with */
>> >>>> +       if (!cpu_map__empty(cpus)) {
>> >>>> +               for (i = 0; i < cpu_map__nr(cpus); i++) {
>> >>>> +                       ret = cs_etm_get_metadata(cpus->map[i], i,
>> >>>> +                                                 itr, auxtrace_info);
>> >>>> +                       if (ret)
>> >>>> +                               goto out;
>> >>>> +               }
>> >>>> +       } else {
>> >>>> +               /* get configuration for all CPUs in the system */
>> >>>> +               nr_cpu = cpu__max_cpu();
>> >>>> +               for (i = 0; i < nr_cpu; i++) {
>> >>>> +                       ret = cs_etm_get_metadata(i, i, itr, auxtrace_info);
>> >>>> +                       if (ret)
>> >>>> +                               goto out;
>> >>>> +               }
>> >>>> +       }
>> >>>> +
>> >>>> +out:
>> >>>> +       return ret;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
>> >>>> +                               int idx, struct auxtrace_mmap *mm,
>> >>>> +                               unsigned char *data __maybe_unused,
>> >>>> +                               u64 *head, u64 *old)
>> >>>> +{
>> >>>> +       pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
>> >>>> +                 __func__, idx, (size_t)*old, (size_t)*head, mm->len);
>> >>>> +
>> >>>> +       *old = *head;
>> >>>> +       *head += mm->len;
>> >>>> +
>> >>>> +       return 0;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_snapshot_start(struct auxtrace_record *itr)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +
>> >>>> +       evlist__for_each(ptr->evlist, evsel) {
>> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>> >>>> +                       return perf_evlist__disable_event(ptr->evlist, evsel);
>> >>>> +       }
>> >>>> +       return -EINVAL;
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +
>> >>>> +       evlist__for_each(ptr->evlist, evsel) {
>> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>> >>>> +                       return perf_evlist__enable_event(ptr->evlist, evsel);
>> >>>> +       }
>> >>>> +       return -EINVAL;
>> >>>> +}
>> >>>> +
>> >>>> +static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
>> >>>> +{
>> >>>> +       return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
>> >>>> +               (((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
>> >>>> +}
>> >>>> +
>> >>>> +static void cs_etm_recording_free(struct auxtrace_record *itr)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       free(ptr);
>> >>>> +}
>> >>>> +
>> >>>> +static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
>> >>>> +{
>> >>>> +       struct cs_etm_recording *ptr =
>> >>>> +                       container_of(itr, struct cs_etm_recording, itr);
>> >>>> +       struct perf_evsel *evsel;
>> >>>> +
>> >>>> +       evlist__for_each(ptr->evlist, evsel) {
>> >>>> +               if (evsel->attr.type == ptr->cs_etm_pmu->type)
>> >>>> +                       return perf_evlist__enable_event_idx(ptr->evlist,
>> >>>> +                                                            evsel, idx);
>> >>>> +       }
>> >>>> +
>> >>>> +       return -EINVAL;
>> >>>> +}
>> >>>> +
>> >>>> +struct auxtrace_record *cs_etm_record_init(int *err)
>> >>>> +{
>> >>>> +       struct perf_pmu *cs_etm_pmu;
>> >>>> +       struct cs_etm_recording *ptr;
>> >>>> +
>> >>>> +       cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
>> >>>> +
>> >>>> +       if (!cs_etm_pmu) {
>> >>>> +               *err = -EINVAL;
>> >>>> +               goto out;
>> >>>> +       }
>> >>>> +
>> >>>> +       ptr = zalloc(sizeof(struct cs_etm_recording));
>> >>>> +       if (!ptr) {
>> >>>> +               *err = -ENOMEM;
>> >>>> +               goto out;
>> >>>> +       }
>> >>>> +
>> >>>> +       ptr->cs_etm_pmu                 = cs_etm_pmu;
>> >>>> +       ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
>> >>>> +       ptr->itr.recording_options      = cs_etm_recording_options;
>> >>>> +       ptr->itr.info_priv_size         = cs_etm_info_priv_size;
>> >>>> +       ptr->itr.info_fill              = cs_etm_info_fill;
>> >>>> +       ptr->itr.find_snapshot          = cs_etm_find_snapshot;
>> >>>> +       ptr->itr.snapshot_start         = cs_etm_snapshot_start;
>> >>>> +       ptr->itr.snapshot_finish        = cs_etm_snapshot_finish;
>> >>>> +       ptr->itr.reference              = cs_etm_reference;
>> >>>> +       ptr->itr.free                   = cs_etm_recording_free;
>> >>>> +       ptr->itr.read_finish            = cs_etm_read_finish;
>> >>>> +
>> >>>> +       *err = 0;
>> >>>> +       return &ptr->itr;
>> >>>> +out:
>> >>>> +       return NULL;
>> >>>> +}
>> >>>> diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
>> >>>> new file mode 100644
>> >>>> index 000000000000..7e85c1b43598
>> >>>> --- /dev/null
>> >>>> +++ b/tools/perf/arch/arm/util/cs_etm.h
>> >>>> @@ -0,0 +1,44 @@
>> >>>> +/*
>> >>>> + * Copyright(C) 2015 Linaro Limited. All rights reserved.
>> >>>> + * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
>> >>>> + *
>> >>>> + * This program is free software; you can redistribute it and/or modify it
>> >>>> + * under the terms of the GNU General Public License version 2 as published by
>> >>>> + * the Free Software Foundation.
>> >>>> + *
>> >>>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> >>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> >>>> + * more details.
>> >>>> + *
>> >>>> + * You should have received a copy of the GNU General Public License along with
>> >>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> >>>> + */
>> >>>> +
>> >>>> +#ifndef INCLUDE__PERF_CS_ETM_H__
>> >>>> +#define INCLUDE__PERF_CS_ETM_H__
>> >>>> +
>> >>>> +/* Beginning of header common to both ETMv3 and V4 */
>> >>>> +enum {
>> >>>> +       CS_ETM_MAGIC,
>> >>>> +       CS_ETM_CPU,
>> >>>> +       CS_ETM_SNAPSHOT,
>> >>>> +};
>> >>>> +
>> >>>> +/* ETMv3/PTM metadata */
>> >>>> +enum {
>> >>>> +       /* Dynamic, configurable parameters */
>> >>>> +       CS_ETM_ETMCR = CS_ETM_SNAPSHOT + 1,
>> >>>> +       CS_ETM_ETMTRACEIDR,
>> >>>> +       /* RO, taken from sysFS */
>> >>>> +       CS_ETM_ETMCCER,
>> >>>> +       CS_ETM_ETMIDR,
>> >>>> +       CS_ETM_PRIV_MAX,
>> >>>> +};
>> >>>> +
>> >>>> +static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
>> >>>> +#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
>> >>>> +
>> >>>> +struct auxtrace_record *cs_etm_record_init(int *err);
>> >>>> +
>> >>>> +#endif
>> >>>> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
>> >>>> index cc1c9ce5cc56..a6f291dbc4d9 100644
>> >>>> --- a/tools/perf/util/auxtrace.c
>> >>>> +++ b/tools/perf/util/auxtrace.c
>> >>>> @@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
>> >>>>                 return intel_pt_process_auxtrace_info(event, session);
>> >>>>         case PERF_AUXTRACE_INTEL_BTS:
>> >>>>                 return intel_bts_process_auxtrace_info(event, session);
>> >>>> +       case PERF_AUXTRACE_CS_ETM:
>> >>>>         case PERF_AUXTRACE_UNKNOWN:
>> >>>>         default:
>> >>>>                 return -EINVAL;
>> >>>> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
>> >>>> index e5a8e2d4f2af..adb53e7bcabf 100644
>> >>>> --- a/tools/perf/util/auxtrace.h
>> >>>> +++ b/tools/perf/util/auxtrace.h
>> >>>> @@ -41,6 +41,7 @@ enum auxtrace_type {
>> >>>>         PERF_AUXTRACE_UNKNOWN,
>> >>>>         PERF_AUXTRACE_INTEL_PT,
>> >>>>         PERF_AUXTRACE_INTEL_BTS,
>> >>>> +       PERF_AUXTRACE_CS_ETM,
>> >>>>  };
>> >>>>
>> >>>>  enum itrace_period_type {
>> >>>> --
>> >>>> 2.1.4
>> >>>>
>> >>>
>> >>> Arnaldo,
>> >>>
>> >>> Last but not least, this is the final patch that I would like you to
>> >>> review before queing.
>> >>>
>> >>> It has been rebased to 4.5-rc1 here [1] for your convenience.  I will
>> >>> be happy to use another baseline should that be more adequate for you.
>> >>
>> >> Adrian,
>> >>
>> >>       One more, are you ok with this?
>> >
>> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
>> > mentioned that on another patch.
>> >
>> > However there is no decoder, which begs the question, is there anything you
>> > can actually do with the perf.data file?  Might be a bit confusing for users
>> > if they can capture traces but not use perf tools on the resulting perf.data
>> > file?
>>
>> We are working on a decoding library in parallel to this work.
>
> Would be nice to be able to get both in the same patch kit, no? So that
> one can both record and process the traces, verifying it all works.

We are still a few weeks away from being in a position where the
community can start playing with the decoding library.  I can hold off
on the "perf tools" patches when I queue the kernel side of the work
for 4.6 but since you and Adrian have already reviewed the work it
would be nice to have that part included as well.

We've been playing with the perf.data files for a couple of months now
and things look at the right place.  This isn't surprising since we
are using the same framework as X86.

I think the generation of the perf.data file should be coupled with
the submission of the kernel driver but would also respect a diverging
point of view.  Simply let me know what you prefer and I will adjust
V9 accordingly.

Regards,
Mathieu

>
> - Arnaldo
>
>> >
>> > Nevertheless, for what is there now:
>> >
>> > Acked-by: Adrian Hunter <adrian.hunter@intel.com>
>>
>> Thanks for the review.
>> Mathieu
>>
>> >

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

* Re: [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-01-29 22:24               ` Mathieu Poirier
@ 2016-02-02 16:20                 ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-02-02 16:20 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: Adrian Hunter, linux-kernel, linux-arm-kernel

[...]

>>> >
>>> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
>>> > mentioned that on another patch.
>>> >
>>> > However there is no decoder, which begs the question, is there anything you
>>> > can actually do with the perf.data file?  Might be a bit confusing for users
>>> > if they can capture traces but not use perf tools on the resulting perf.data
>>> > file?
>>>
>>> We are working on a decoding library in parallel to this work.
>>
>> Would be nice to be able to get both in the same patch kit, no? So that
>> one can both record and process the traces, verifying it all works.
>
> We are still a few weeks away from being in a position where the
> community can start playing with the decoding library.  I can hold off
> on the "perf tools" patches when I queue the kernel side of the work
> for 4.6 but since you and Adrian have already reviewed the work it
> would be nice to have that part included as well.
>
> We've been playing with the perf.data files for a couple of months now
> and things look at the right place.  This isn't surprising since we
> are using the same framework as X86.
>
> I think the generation of the perf.data file should be coupled with
> the submission of the kernel driver but would also respect a diverging
> point of view.  Simply let me know what you prefer and I will adjust
> V9 accordingly.

Arnaldo,

I'm preparing V9 at this time - what's your view on the above?

Thanks,
Mathieu

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-02-02 16:20                 ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-02-02 16:20 UTC (permalink / raw)
  To: linux-arm-kernel

[...]

>>> >
>>> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
>>> > mentioned that on another patch.
>>> >
>>> > However there is no decoder, which begs the question, is there anything you
>>> > can actually do with the perf.data file?  Might be a bit confusing for users
>>> > if they can capture traces but not use perf tools on the resulting perf.data
>>> > file?
>>>
>>> We are working on a decoding library in parallel to this work.
>>
>> Would be nice to be able to get both in the same patch kit, no? So that
>> one can both record and process the traces, verifying it all works.
>
> We are still a few weeks away from being in a position where the
> community can start playing with the decoding library.  I can hold off
> on the "perf tools" patches when I queue the kernel side of the work
> for 4.6 but since you and Adrian have already reviewed the work it
> would be nice to have that part included as well.
>
> We've been playing with the perf.data files for a couple of months now
> and things look at the right place.  This isn't surprising since we
> are using the same framework as X86.
>
> I think the generation of the perf.data file should be coupled with
> the submission of the kernel driver but would also respect a diverging
> point of view.  Simply let me know what you prefer and I will adjust
> V9 accordingly.

Arnaldo,

I'm preparing V9 at this time - what's your view on the above?

Thanks,
Mathieu

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

* Re: [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-02-02 16:20                 ` Mathieu Poirier
@ 2016-02-02 16:41                   ` Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-02-02 16:41 UTC (permalink / raw)
  To: Mathieu Poirier; +Cc: Adrian Hunter, linux-kernel, linux-arm-kernel

Em Tue, Feb 02, 2016 at 09:20:51AM -0700, Mathieu Poirier escreveu:
> [...]
> 
> >>> >
> >>> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
> >>> > mentioned that on another patch.
> >>> >
> >>> > However there is no decoder, which begs the question, is there anything you
> >>> > can actually do with the perf.data file?  Might be a bit confusing for users
> >>> > if they can capture traces but not use perf tools on the resulting perf.data
> >>> > file?
> >>>
> >>> We are working on a decoding library in parallel to this work.
> >>
> >> Would be nice to be able to get both in the same patch kit, no? So that
> >> one can both record and process the traces, verifying it all works.
> >
> > We are still a few weeks away from being in a position where the
> > community can start playing with the decoding library.  I can hold off
> > on the "perf tools" patches when I queue the kernel side of the work
> > for 4.6 but since you and Adrian have already reviewed the work it
> > would be nice to have that part included as well.
> >
> > We've been playing with the perf.data files for a couple of months now
> > and things look at the right place.  This isn't surprising since we
> > are using the same framework as X86.
> >
> > I think the generation of the perf.data file should be coupled with
> > the submission of the kernel driver but would also respect a diverging
> > point of view.  Simply let me know what you prefer and I will adjust
> > V9 accordingly.
> 
> Arnaldo,
> 
> I'm preparing V9 at this time - what's your view on the above?

I'd say go with something we can test, i.e. if we generate a perf.data
file we can't then process to figure out if what was inserted is right,
how can we decide if it is ok?

Otherwise please describe how you test it, preferably by having this in
the commit log, i.e. if you decide that using plain 'perf report -D' is
enough, state that and show the output, etc.

- Arnaldo

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-02-02 16:41                   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 108+ messages in thread
From: Arnaldo Carvalho de Melo @ 2016-02-02 16:41 UTC (permalink / raw)
  To: linux-arm-kernel

Em Tue, Feb 02, 2016 at 09:20:51AM -0700, Mathieu Poirier escreveu:
> [...]
> 
> >>> >
> >>> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
> >>> > mentioned that on another patch.
> >>> >
> >>> > However there is no decoder, which begs the question, is there anything you
> >>> > can actually do with the perf.data file?  Might be a bit confusing for users
> >>> > if they can capture traces but not use perf tools on the resulting perf.data
> >>> > file?
> >>>
> >>> We are working on a decoding library in parallel to this work.
> >>
> >> Would be nice to be able to get both in the same patch kit, no? So that
> >> one can both record and process the traces, verifying it all works.
> >
> > We are still a few weeks away from being in a position where the
> > community can start playing with the decoding library.  I can hold off
> > on the "perf tools" patches when I queue the kernel side of the work
> > for 4.6 but since you and Adrian have already reviewed the work it
> > would be nice to have that part included as well.
> >
> > We've been playing with the perf.data files for a couple of months now
> > and things look at the right place.  This isn't surprising since we
> > are using the same framework as X86.
> >
> > I think the generation of the perf.data file should be coupled with
> > the submission of the kernel driver but would also respect a diverging
> > point of view.  Simply let me know what you prefer and I will adjust
> > V9 accordingly.
> 
> Arnaldo,
> 
> I'm preparing V9 at this time - what's your view on the above?

I'd say go with something we can test, i.e. if we generate a perf.data
file we can't then process to figure out if what was inserted is right,
how can we decide if it is ok?

Otherwise please describe how you test it, preferably by having this in
the commit log, i.e. if you decide that using plain 'perf report -D' is
enough, state that and show the output, etc.

- Arnaldo

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

* [tip:perf/core] perf cpumap: Auto initialize cpu__max_{node,cpu}
  2016-01-25 21:29         ` Arnaldo Carvalho de Melo
  (?)
  (?)
@ 2016-02-03 10:15         ` tip-bot for Arnaldo Carvalho de Melo
  -1 siblings, 0 replies; 108+ messages in thread
From: tip-bot for Arnaldo Carvalho de Melo @ 2016-02-03 10:15 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: linux-kernel, fweisbec, dsahern, hpa, tglx, namhyung, acme,
	kan.liang, wangnan0, mingo, bp, adrian.hunter, eranian,
	mathieu.poirier, jolsa

Commit-ID:  5ac76283b32b116c58e362e99542182ddcfc8262
Gitweb:     http://git.kernel.org/tip/5ac76283b32b116c58e362e99542182ddcfc8262
Author:     Arnaldo Carvalho de Melo <acme@redhat.com>
AuthorDate: Tue, 26 Jan 2016 15:51:46 -0300
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Tue, 26 Jan 2016 16:08:36 -0300

perf cpumap: Auto initialize cpu__max_{node,cpu}

Since it was always checking if the initialization was done, use that
branch to do the initialization if not done already.

With this we reduce the number of exported globals from these files.

Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/20160125212955.GG22501@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/cpumap.c | 30 ++++++++++++++++++++++++++++++
 tools/perf/util/cpumap.h | 32 +++-----------------------------
 2 files changed, 33 insertions(+), 29 deletions(-)

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index fa93509..9bcf2be 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -8,6 +8,10 @@
 #include <linux/bitmap.h>
 #include "asm/bug.h"
 
+static int max_cpu_num;
+static int max_node_num;
+static int *cpunode_map;
+
 static struct cpu_map *cpu_map__default_new(void)
 {
 	struct cpu_map *cpus;
@@ -486,6 +490,32 @@ out:
 		pr_err("Failed to read max nodes, using default of %d\n", max_node_num);
 }
 
+int cpu__max_node(void)
+{
+	if (unlikely(!max_node_num))
+		set_max_node_num();
+
+	return max_node_num;
+}
+
+int cpu__max_cpu(void)
+{
+	if (unlikely(!max_cpu_num))
+		set_max_cpu_num();
+
+	return max_cpu_num;
+}
+
+int cpu__get_node(int cpu)
+{
+	if (unlikely(cpunode_map == NULL)) {
+		pr_debug("cpu_map not initialized\n");
+		return -1;
+	}
+
+	return cpunode_map[cpu];
+}
+
 static int init_cpunode_map(void)
 {
 	int i;
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 71c41b9..81a2562 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -57,37 +57,11 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
 	return map ? map->map[0] == -1 : true;
 }
 
-int max_cpu_num;
-int max_node_num;
-int *cpunode_map;
-
 int cpu__setup_cpunode_map(void);
 
-static inline int cpu__max_node(void)
-{
-	if (unlikely(!max_node_num))
-		pr_debug("cpu_map not initialized\n");
-
-	return max_node_num;
-}
-
-static inline int cpu__max_cpu(void)
-{
-	if (unlikely(!max_cpu_num))
-		pr_debug("cpu_map not initialized\n");
-
-	return max_cpu_num;
-}
-
-static inline int cpu__get_node(int cpu)
-{
-	if (unlikely(cpunode_map == NULL)) {
-		pr_debug("cpu_map not initialized\n");
-		return -1;
-	}
-
-	return cpunode_map[cpu];
-}
+int cpu__max_node(void);
+int cpu__max_cpu(void);
+int cpu__get_node(int cpu);
 
 int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
 		       int (*f)(struct cpu_map *map, int cpu, void *data),

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

* [tip:perf/core] perf auxtrace: Add perf_evlist pointer to *info_priv_size()
  2016-01-14 21:46   ` Mathieu Poirier
  (?)
  (?)
@ 2016-02-03 10:17   ` tip-bot for Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: tip-bot for Mathieu Poirier @ 2016-02-03 10:17 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: tglx, tor, acme, rabin, zhang.chunyan, adrian.hunter, mike.leach,
	a.p.zijlstra, hpa, al.grant, mathieu.poirier, mingo,
	linux-kernel

Commit-ID:  14a05e13a044c1cd6aaa3eb1a5fcdad7b4f6c990
Gitweb:     http://git.kernel.org/tip/14a05e13a044c1cd6aaa3eb1a5fcdad7b4f6c990
Author:     Mathieu Poirier <mathieu.poirier@linaro.org>
AuthorDate: Thu, 14 Jan 2016 14:46:15 -0700
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 29 Jan 2016 17:14:30 -0300

perf auxtrace: Add perf_evlist pointer to *info_priv_size()

On some architecture the size of the private header may be dependent on
the number of tracers used in the session.  As such adding a "struct
perf_evlist *" parameter, which should contain all the required
information.

Also adjusting the existing client of the interface to take the new
parameter into account.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Al Grant <al.grant@arm.com>
Cc: Chunyan Zhang <zhang.chunyan@linaro.org>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-doc@vger.kernel.org
Cc: Mike Leach <mike.leach@arm.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Rabin Vincent <rabin@rab.in>
Cc: Tor Jeremiassen <tor@ti.com>
Link: http://lkml.kernel.org/r/1452807977-8069-22-git-send-email-mathieu.poirier@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/arch/x86/util/intel-bts.c | 4 +++-
 tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
 tools/perf/util/auxtrace.c           | 7 ++++---
 tools/perf/util/auxtrace.h           | 6 ++++--
 4 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
index 8d8150f..d66f9ad 100644
--- a/tools/perf/arch/x86/util/intel-bts.c
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -60,7 +60,9 @@ struct branch {
 	u64 misc;
 };
 
-static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
+static size_t
+intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+			 struct perf_evlist *evlist __maybe_unused)
 {
 	return INTEL_BTS_AUXTRACE_PRIV_SIZE;
 }
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index f05daac..6f7d453 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
 	return attr;
 }
 
-static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
+static size_t
+intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+			struct perf_evlist *evlist __maybe_unused)
 {
 	return INTEL_PT_AUXTRACE_PRIV_SIZE;
 }
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 360fda0..ec164fe 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
 			 heap_array[last].ordinal);
 }
 
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+				       struct perf_evlist *evlist)
 {
 	if (itr)
-		return itr->info_priv_size(itr);
+		return itr->info_priv_size(itr, evlist);
 	return 0;
 }
 
@@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
 	int err;
 
 	pr_debug2("Synthesizing auxtrace information\n");
-	priv_size = auxtrace_record__info_priv_size(itr);
+	priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
 	ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
 	if (!ev)
 		return -ENOMEM;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index b86f90db..e5a8e2d 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -293,7 +293,8 @@ struct auxtrace_record {
 	int (*recording_options)(struct auxtrace_record *itr,
 				 struct perf_evlist *evlist,
 				 struct record_opts *opts);
-	size_t (*info_priv_size)(struct auxtrace_record *itr);
+	size_t (*info_priv_size)(struct auxtrace_record *itr,
+				 struct perf_evlist *evlist);
 	int (*info_fill)(struct auxtrace_record *itr,
 			 struct perf_session *session,
 			 struct auxtrace_info_event *auxtrace_info,
@@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
 int auxtrace_record__options(struct auxtrace_record *itr,
 			     struct perf_evlist *evlist,
 			     struct record_opts *opts);
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+				       struct perf_evlist *evlist);
 int auxtrace_record__info_fill(struct auxtrace_record *itr,
 			       struct perf_session *session,
 			       struct auxtrace_info_event *auxtrace_info,

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

* Re: [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
  2016-02-02 16:41                   ` Arnaldo Carvalho de Melo
@ 2016-02-03 16:11                     ` Mathieu Poirier
  -1 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-02-03 16:11 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: Adrian Hunter, linux-kernel, linux-arm-kernel

On 2 February 2016 at 09:41, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> Em Tue, Feb 02, 2016 at 09:20:51AM -0700, Mathieu Poirier escreveu:
>> [...]
>>
>> >>> >
>> >>> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
>> >>> > mentioned that on another patch.
>> >>> >
>> >>> > However there is no decoder, which begs the question, is there anything you
>> >>> > can actually do with the perf.data file?  Might be a bit confusing for users
>> >>> > if they can capture traces but not use perf tools on the resulting perf.data
>> >>> > file?
>> >>>
>> >>> We are working on a decoding library in parallel to this work.
>> >>
>> >> Would be nice to be able to get both in the same patch kit, no? So that
>> >> one can both record and process the traces, verifying it all works.
>> >
>> > We are still a few weeks away from being in a position where the
>> > community can start playing with the decoding library.  I can hold off
>> > on the "perf tools" patches when I queue the kernel side of the work
>> > for 4.6 but since you and Adrian have already reviewed the work it
>> > would be nice to have that part included as well.
>> >
>> > We've been playing with the perf.data files for a couple of months now
>> > and things look at the right place.  This isn't surprising since we
>> > are using the same framework as X86.
>> >
>> > I think the generation of the perf.data file should be coupled with
>> > the submission of the kernel driver but would also respect a diverging
>> > point of view.  Simply let me know what you prefer and I will adjust
>> > V9 accordingly.
>>
>> Arnaldo,
>>
>> I'm preparing V9 at this time - what's your view on the above?
>
> I'd say go with something we can test, i.e. if we generate a perf.data
> file we can't then process to figure out if what was inserted is right,
> how can we decide if it is ok?

Very well - I'll hold off on the perf tools patches for now.  We can
make a submission of all the user space components when things are in
a working order.

Mathieu

>
> Otherwise please describe how you test it, preferably by having this in
> the commit log, i.e. if you decide that using plain 'perf report -D' is
> enough, state that and show the output, etc.
>
> - Arnaldo

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

* [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities
@ 2016-02-03 16:11                     ` Mathieu Poirier
  0 siblings, 0 replies; 108+ messages in thread
From: Mathieu Poirier @ 2016-02-03 16:11 UTC (permalink / raw)
  To: linux-arm-kernel

On 2 February 2016 at 09:41, Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> Em Tue, Feb 02, 2016 at 09:20:51AM -0700, Mathieu Poirier escreveu:
>> [...]
>>
>> >>> >
>> >>> > Looks OK, apart from adding linux/coresight-pmu.h to the manifest, but I
>> >>> > mentioned that on another patch.
>> >>> >
>> >>> > However there is no decoder, which begs the question, is there anything you
>> >>> > can actually do with the perf.data file?  Might be a bit confusing for users
>> >>> > if they can capture traces but not use perf tools on the resulting perf.data
>> >>> > file?
>> >>>
>> >>> We are working on a decoding library in parallel to this work.
>> >>
>> >> Would be nice to be able to get both in the same patch kit, no? So that
>> >> one can both record and process the traces, verifying it all works.
>> >
>> > We are still a few weeks away from being in a position where the
>> > community can start playing with the decoding library.  I can hold off
>> > on the "perf tools" patches when I queue the kernel side of the work
>> > for 4.6 but since you and Adrian have already reviewed the work it
>> > would be nice to have that part included as well.
>> >
>> > We've been playing with the perf.data files for a couple of months now
>> > and things look at the right place.  This isn't surprising since we
>> > are using the same framework as X86.
>> >
>> > I think the generation of the perf.data file should be coupled with
>> > the submission of the kernel driver but would also respect a diverging
>> > point of view.  Simply let me know what you prefer and I will adjust
>> > V9 accordingly.
>>
>> Arnaldo,
>>
>> I'm preparing V9 at this time - what's your view on the above?
>
> I'd say go with something we can test, i.e. if we generate a perf.data
> file we can't then process to figure out if what was inserted is right,
> how can we decide if it is ok?

Very well - I'll hold off on the perf tools patches for now.  We can
make a submission of all the user space components when things are in
a working order.

Mathieu

>
> Otherwise please describe how you test it, preferably by having this in
> the commit log, i.e. if you decide that using plain 'perf report -D' is
> enough, state that and show the output, etc.
>
> - Arnaldo

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

end of thread, other threads:[~2016-02-03 16:12 UTC | newest]

Thread overview: 108+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-14 21:45 [PATCH V8 00/23] Coresight integration with perf Mathieu Poirier
2016-01-14 21:45 ` Mathieu Poirier
2016-01-14 21:45 ` [PATCH V8 01/23] coresight: associating path with session rather than tracer Mathieu Poirier
2016-01-14 21:45   ` Mathieu Poirier
2016-01-14 21:45 ` [PATCH V8 02/23] coresight: add API to get sink from path Mathieu Poirier
2016-01-14 21:45   ` Mathieu Poirier
2016-01-14 21:45 ` [PATCH V8 03/23] coresight: moving PM runtime operations to core framework Mathieu Poirier
2016-01-14 21:45   ` Mathieu Poirier
2016-01-14 21:45 ` [PATCH V8 04/23] coresight: etm3x: moving etm_readl/writel to header file Mathieu Poirier
2016-01-14 21:45   ` Mathieu Poirier
2016-01-14 21:45 ` [PATCH V8 05/23] coresight: etm3x: moving sysFS entries to dedicated file Mathieu Poirier
2016-01-14 21:45   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 06/23] coresight: etm3x: unlocking tracers in default arch init Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 07/23] coresight: etm3x: splitting struct etm_drvdata Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 08/23] coresight: etm3x: adding operation mode for etm_enable() Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 09/23] coresight: etm3x: set progbit to stop trace collection Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 10/23] coresight: etm3x: changing default trace configuration Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 11/23] coresight: etm3x: consolidating initial config Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 12/23] coresight: etm3x: implementing user/kernel mode tracing Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 13/23] coresight: etm3x: implementing perf_enable/disable() API Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 14/23] coresight: etb10: moving to local atomic operations Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 15/23] coresight: etb10: adding operation mode for sink->enable() Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 16/23] coresight: etb10: implementing AUX API Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-26 15:53   ` Alexander Shishkin
2016-01-26 15:53     ` Alexander Shishkin
2016-01-27 20:55     ` Mathieu Poirier
2016-01-27 20:55       ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 17/23] coresight: updating documentation to reflect integration with perf Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 18/23] coresight: etm-perf: new PMU driver for ETM tracers Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-26 15:27   ` Alexander Shishkin
2016-01-26 15:27     ` Alexander Shishkin
2016-01-27 18:33     ` Mathieu Poirier
2016-01-27 18:33       ` Mathieu Poirier
2016-01-28 15:42       ` Alexander Shishkin
2016-01-28 15:42         ` Alexander Shishkin
2016-01-28 21:12         ` Mathieu Poirier
2016-01-28 21:12           ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 19/23] coresight: introducing a global trace ID function Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 20/23] perf tools: making function set_max_cpu_num() non static Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-25 20:46   ` Mathieu Poirier
2016-01-25 20:46     ` Mathieu Poirier
2016-01-25 21:12     ` Arnaldo Carvalho de Melo
2016-01-25 21:12       ` Arnaldo Carvalho de Melo
2016-01-25 21:29       ` Arnaldo Carvalho de Melo
2016-01-25 21:29         ` Arnaldo Carvalho de Melo
2016-01-26 17:08         ` Mathieu Poirier
2016-01-26 17:08           ` Mathieu Poirier
2016-01-26 18:51           ` Arnaldo Carvalho de Melo
2016-01-26 18:51             ` Arnaldo Carvalho de Melo
2016-01-27 16:24             ` Mathieu Poirier
2016-01-27 16:24               ` Mathieu Poirier
2016-02-03 10:15         ` [tip:perf/core] perf cpumap: Auto initialize cpu__max_{node,cpu} tip-bot for Arnaldo Carvalho de Melo
2016-01-14 21:46 ` [PATCH V8 21/23] perf tools: adding perf_evlist to *info_priv_size() Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-25 20:48   ` Mathieu Poirier
2016-01-25 20:48     ` Mathieu Poirier
2016-01-25 21:08     ` Arnaldo Carvalho de Melo
2016-01-25 21:08       ` Arnaldo Carvalho de Melo
2016-01-26 14:27       ` Adrian Hunter
2016-01-26 14:27         ` Adrian Hunter
2016-01-26 14:33         ` Arnaldo Carvalho de Melo
2016-01-26 14:33           ` Arnaldo Carvalho de Melo
2016-01-29 10:14         ` Adrian Hunter
2016-01-29 10:14           ` Adrian Hunter
2016-02-03 10:17   ` [tip:perf/core] perf auxtrace: Add perf_evlist pointer " tip-bot for Mathieu Poirier
2016-01-14 21:46 ` [PATCH V8 22/23] perf tools: making coresight PMU listable Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-25 20:49   ` Mathieu Poirier
2016-01-25 20:49     ` Mathieu Poirier
2016-01-25 21:10     ` Arnaldo Carvalho de Melo
2016-01-25 21:10       ` Arnaldo Carvalho de Melo
2016-01-29 10:24       ` Adrian Hunter
2016-01-29 10:24         ` Adrian Hunter
2016-01-14 21:46 ` [PATCH V8 23/23] perf tools: adding coresight etm PMU record capabilities Mathieu Poirier
2016-01-14 21:46   ` Mathieu Poirier
2016-01-25 20:51   ` Mathieu Poirier
2016-01-25 20:51     ` Mathieu Poirier
2016-01-25 21:10     ` Arnaldo Carvalho de Melo
2016-01-25 21:10       ` Arnaldo Carvalho de Melo
2016-01-29 10:34       ` Adrian Hunter
2016-01-29 10:34         ` Adrian Hunter
2016-01-29 17:37         ` Mathieu Poirier
2016-01-29 17:37           ` Mathieu Poirier
2016-01-29 21:12           ` Arnaldo Carvalho de Melo
2016-01-29 21:12             ` Arnaldo Carvalho de Melo
2016-01-29 22:24             ` Mathieu Poirier
2016-01-29 22:24               ` Mathieu Poirier
2016-02-02 16:20               ` Mathieu Poirier
2016-02-02 16:20                 ` Mathieu Poirier
2016-02-02 16:41                 ` Arnaldo Carvalho de Melo
2016-02-02 16:41                   ` Arnaldo Carvalho de Melo
2016-02-03 16:11                   ` Mathieu Poirier
2016-02-03 16:11                     ` Mathieu Poirier

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.