2015-12-18 20:59:38

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 00/24] Coresight integration with perf

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.

Best regards,
Mathieu

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 (24):
coresight: implementing 'cpu_id()' API
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 space 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 | 386 +++++
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 | 1824 +++++---------------
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 | 330 ++--
include/linux/coresight-pmu.h | 39 +
include/linux/coresight.h | 34 +-
tools/perf/arch/arm/util/Build | 2 +
tools/perf/arch/arm/util/auxtrace.c | 48 +
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, 3518 insertions(+), 1645 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


2015-12-18 20:59:44

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 01/24] coresight: implementing 'cpu_id()' API

Other than plainly parsing the device tree there is no way to
know which CPU a tracer is affined to. As such adding an
interface to lookup the CPU field enclosed in the etm_drvdata
structure that was initialised at boot time.

Signed-off-by: Mathieu Poirier <[email protected]>
---
drivers/hwtracing/coresight/coresight-etm3x.c | 8 ++++++++
include/linux/coresight.h | 3 +++
2 files changed, 11 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index d630b7ece735..755f6f4d6d79 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -315,6 +315,13 @@ static void etm_enable_hw(void *info)
dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
}

+static int etm_cpu_id(struct coresight_device *csdev)
+{
+ struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+ return drvdata->cpu;
+}
+
static int etm_trace_id(struct coresight_device *csdev)
{
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -421,6 +428,7 @@ static void etm_disable(struct coresight_device *csdev)
}

static const struct coresight_ops_source etm_source_ops = {
+ .cpu_id = etm_cpu_id,
.trace_id = etm_trace_id,
.enable = etm_enable,
.disable = etm_disable,
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index a7cabfa23b55..bf62b265bf52 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -205,12 +205,15 @@ struct coresight_ops_link {
/**
* struct coresight_ops_source - basic operations for a source
* Operations available for sources.
+ * @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.
* @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);
void (*disable)(struct coresight_device *csdev);
--
2.1.4

2015-12-18 21:09:30

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 02/24] coresight: associating path with session rather than tracer

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 <[email protected]>
---
drivers/hwtracing/coresight/coresight-priv.h | 5 +
drivers/hwtracing/coresight/coresight.c | 300 ++++++++++++++++++---------
include/linux/coresight.h | 2 -
3 files changed, 208 insertions(+), 99 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 93738dfbf631..74beba461aa2 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;
-
- /*
- * 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);
+ struct coresight_node *nd;
+ struct coresight_device *csdev, *parent, *child;
+
+ 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;
-
- 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);
+ int i;
+ bool found = false;
+ struct coresight_node *node;
+ struct coresight_connection *conn;
+
+ /* 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

2015-12-18 21:09:28

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 03/24] coresight: add API to get sink from path

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 <[email protected]>
---
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 74beba461aa2..b488af14a143 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

2015-12-18 21:08:53

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 04/24] coresight: moving PM runtime operations to core framework

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 <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
---
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 b488af14a143..d053e5940fbf 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

2015-12-18 20:59:48

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 05/24] coresight: etm3x: moving etm_readl/writel to header file

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 <[email protected]>
---
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

2015-12-18 20:59:53

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 06/24] coresight: etm3x: moving sysFS entries to dedicated file

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 <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2015-12-18 20:59:55

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 07/24] coresight: etm3x: unlocking tracers in default arch init

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 <[email protected]>
---
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

2015-12-18 21:07:52

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 08/24] coresight: etm3x: splitting struct etm_drvdata

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 <[email protected]>
---
drivers/hwtracing/coresight/coresight-etm.h | 102 ++++----
.../hwtracing/coresight/coresight-etm3x-sysfs.c | 290 ++++++++++++---------
drivers/hwtracing/coresight/coresight-etm3x.c | 155 +++++------
3 files changed, 307 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..3036154e1a6a 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);
@@ -384,6 +390,7 @@ static void etm_disable_hw(void *info)
static void etm_disable(struct coresight_device *csdev)
{
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+ struct etm_config *config = &drvdata->config;

/*
* Taking hotplug lock here protects from clocks getting disabled
@@ -400,6 +407,7 @@ static void etm_disable(struct coresight_device *csdev)
*/
smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1);
drvdata->enable = false;
+ kfree(config);

spin_unlock(&drvdata->spinlock);
put_online_cpus();
@@ -522,14 +530,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 +539,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 +628,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

2015-12-18 21:07:22

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 09/24] coresight: etm3x: adding operation mode for etm_enable()

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 <[email protected]>
---
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 3036154e1a6a..ff9b83d02cb7 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);
struct etm_config *config = &drvdata->config;
@@ -406,7 +432,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;
kfree(config);

spin_unlock(&drvdata->spinlock);
@@ -415,6 +440,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,
@@ -442,7 +494,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;
@@ -455,7 +507,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 d053e5940fbf..c02da2edfd80 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

2015-12-18 21:00:00

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 10/24] coresight: etm3x: set progbit to stop trace collection

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 <[email protected]>
---
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 ff9b83d02cb7..221629ff8916 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

2015-12-18 21:06:29

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 11/24] coresight: etm3x: changing default trace configuration

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 <[email protected]>
---
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 221629ff8916..c97232ab7564 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -581,26 +581,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

2015-12-18 21:04:54

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 12/24] coresight: etm3x: consolidating initial config

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 <[email protected]>
---
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 c97232ab7564..9293e71a075a 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;

@@ -579,27 +591,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)
{
/*
@@ -676,7 +667,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

2015-12-18 21:00:11

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 13/24] coresight: etm3x: implementing user/kernel mode tracing

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

Signed-off-by: Mathieu Poirier <[email protected]>
---
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 9293e71a075a..60e06a865628 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

2015-12-18 21:00:08

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 14/24] coresight: etm3x: implementing perf_enable/disable() API

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

Signed-off-by: Mathieu Poirier <[email protected]>
---
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 6c8921140f02..e252dd1522e5 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 60e06a865628..6980880458ae 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);
@@ -530,6 +612,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 c02da2edfd80..3cd0e87d8a16 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

2015-12-18 21:04:22

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 15/24] coresight: etb10: moving to local atomic operations

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 <[email protected]>
---
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

2015-12-18 21:03:39

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 16/24] coresight: etb10: adding operation mode for sink->enable()

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 <[email protected]>
---
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 3cd0e87d8a16..aab81158d3fb 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

2015-12-18 21:02:37

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 17/24] coresight: etb10: implementing AUX space API

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 <[email protected]>
---
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..8e72166b47ce 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;
+ u32 *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

2015-12-18 21:02:08

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 18/24] coresight: updating documentation to reflect integration with perf

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 <[email protected]>
---
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

2015-12-18 21:00:16

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 19/24] coresight: etm-perf: new PMU driver for ETM tracers

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 <[email protected]>
Signed-off-by: Mathieu Poirier <[email protected]>
---
drivers/hwtracing/coresight/Makefile | 3 +-
drivers/hwtracing/coresight/coresight-etm-perf.c | 386 +++++++++++++++++++++++
drivers/hwtracing/coresight/coresight-etm-perf.h | 32 ++
drivers/hwtracing/coresight/coresight-etm3x.c | 7 +
include/linux/coresight-pmu.h | 27 ++
5 files changed, 454 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..fa8c9d914c31
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <[email protected]>
+ *
+ * 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 "coresight-priv.h"
+
+static struct pmu etm_pmu;
+static bool etm_perf_up;
+
+/**
+ * struct etm_event_data - Coresight specifics associated to an event
+ * @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 {
+ 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 etm_event_data *event_data)
+{
+ int cpu;
+ cpumask_t *mask = &event_data->mask;
+ struct coresight_device *sink;
+
+ /*
+ * 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);
+}
+
+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_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;
+
+ 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:
+ free_event_data(event_data);
+ event_data = NULL;
+ goto out;
+}
+
+static void etm_free_aux(void *data)
+{
+ free_event_data(data);
+}
+
+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 <[email protected]>
+ *
+ * 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 6980880458ae..26310db643f7 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);
@@ -829,6 +830,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 <[email protected]>
+ *
+ * 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

2015-12-18 21:00:20

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 20/24] coresight: introducing a global trace ID function

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 <[email protected]>
---
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 26310db643f7..471b9579cea8 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>
@@ -742,11 +743,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

2015-12-18 21:01:46

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 21/24] perf tools: making function set_max_cpu_num() non static

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 <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Mathieu Poirier <[email protected]>
---
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

2015-12-18 21:01:19

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 22/24] perf tools: adding perf_evlist 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.

Cc: Peter Zijlstra <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Mathieu Poirier <[email protected]>
---
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

2015-12-18 21:00:25

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 23/24] perf tools: making coresight PMU listable

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

Cc: Peter Zijlstra <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Mathieu Poirier <[email protected]>
---
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 9bff63cf326e..e1a5de0e5733 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 <[email protected]>
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 <[email protected]>
+ *
+ * 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

2015-12-18 21:00:48

by Mathieu Poirier

[permalink] [raw]
Subject: [PATCH V7 24/24] perf tools: adding coresight etm PMU record capabilities

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 <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Mathieu Poirier <[email protected]>
---
MAINTAINERS | 3 +
tools/perf/arch/arm/util/Build | 2 +-
tools/perf/arch/arm/util/auxtrace.c | 48 ++++
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, 564 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 e1a5de0e5733..1368a22f61c0 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 <[email protected]>
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..4988fdf7cb8a
--- /dev/null
+++ b/tools/perf/arch/arm/util/auxtrace.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <[email protected]>
+ *
+ * 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);
+
+ *err = -EINVAL;
+ 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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

2015-12-19 17:14:00

by Rabin Vincent

[permalink] [raw]
Subject: Re: [PATCH V7 04/24] coresight: moving PM runtime operations to core framework

On Fri, Dec 18, 2015 at 01:59:00PM -0700, Mathieu Poirier wrote:
> @@ -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);
> }

This leads to the following splat:

BUG: sleeping function called from invalid context at /home/rabin/dev/linux/drivers/base/power/runtime.c:892
in_atomic(): 1, irqs_disabled(): 128, pid: 763, name: perf
2 locks held by perf/763:
#0: (&mm->mmap_sem){++++++}, at: [<c012e42c>] vm_munmap+0x2c/0x50
#1: (&event->mmap_mutex){+.+.+.}, at: [<c008248c>] atomic_dec_and_mutex_lock+0x58/0x98
irq event stamp: 63152
hardirqs last enabled at (63151): [<c04f7edc>] _raw_spin_unlock_irqrestore+0x30/0x5c
hardirqs last disabled at (63152): [<c04f88e8>] __irq_svc+0x48/0x78
softirqs last enabled at (61242): [<c003a914>] __do_softirq+0x408/0x4fc
softirqs last disabled at (61223): [<c003ad50>] irq_exit+0xcc/0x130
CPU: 1 PID: 763 Comm: perf Not tainted 4.4.0-rc5-00224-ge461459-dirty #152
Hardware name: Generic OMAP4 (Flattened Device Tree)
[<c00184dc>] (unwind_backtrace) from [<c0014c74>] (show_stack+0x10/0x14)
[<c0014c74>] (show_stack) from [<c025c834>] (dump_stack+0x90/0xb8)
[<c025c834>] (dump_stack) from [<c02f026c>] (__pm_runtime_idle+0xa4/0xa8)
[<c02f026c>] (__pm_runtime_idle) from [<c03e1a08>] (coresight_release_path+0x38/0x7c)
[<c03e1a08>] (coresight_release_path) from [<c03e210c>] (free_event_data+0x84/0x9c)
[<c03e210c>] (free_event_data) from [<c00f8be8>] (rb_irq_work+0x4c/0xcc)
[<c00f8be8>] (rb_irq_work) from [<c00e8860>] (irq_work_run_list+0x7c/0xb4)
[<c00e8860>] (irq_work_run_list) from [<c00e88b8>] (irq_work_run+0x20/0x34)
[<c00e88b8>] (irq_work_run) from [<c0016dcc>] (handle_IPI+0x1cc/0x334)
[<c0016dcc>] (handle_IPI) from [<c0009538>] (gic_handle_irq+0x84/0x88)
[<c0009538>] (gic_handle_irq) from [<c04f88f8>] (__irq_svc+0x58/0x78)
Exception stack(0xed865e98 to 0xed865ee0)
5e80: 00000001 00000110
5ea0: 00000000 ee2f1080 20000113 c0784808 edbe4e1c b6ae5000 edb882f0 ed9b1e04
5ec0: edb88298 c075648c 00000002 ed865ee8 c008724c c04f7ee0 20000113 ffffffff
[<c04f88f8>] (__irq_svc) from [<c04f7ee0>] (_raw_spin_unlock_irqrestore+0x34/0x5c)
[<c04f7ee0>] (_raw_spin_unlock_irqrestore) from [<c00e8978>] (irq_work_queue+0xac/0xb4)
[<c00e8978>] (irq_work_queue) from [<c00f5020>] (perf_mmap_close+0x370/0x3c8)
[<c00f5020>] (perf_mmap_close) from [<c012c560>] (remove_vma+0x40/0x6c)
[<c012c560>] (remove_vma) from [<c012e2b4>] (do_munmap+0x210/0x35c)
[<c012e2b4>] (do_munmap) from [<c012e43c>] (vm_munmap+0x3c/0x50)
[<c012e43c>] (vm_munmap) from [<c0010320>] (ret_fast_syscall+0x0/0x1c)

It should presumably be using pm_runtime_put() instead.

2015-12-19 17:23:21

by Rabin Vincent

[permalink] [raw]
Subject: Re: [PATCH V7 17/24] coresight: etb10: implementing AUX space API

On Fri, Dec 18, 2015 at 01:59:13PM -0700, Mathieu Poirier wrote:
> +static void etb_update_buffer(struct coresight_device *csdev,
> + struct perf_output_handle *handle,
> + void *sink_config)
> +{
> + int i, cur;
> + u32 *buf_ptr;

...

> + 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;

buf_ptr should either be changed to a u8* or just the first write should
be present. Otherwise this overwrites lots of memory.

> +
> + offset += 4;
> + if (offset >= PAGE_SIZE) {

2015-12-19 17:27:12

by Rabin Vincent

[permalink] [raw]
Subject: Re: [PATCH V7 24/24] perf tools: adding coresight etm PMU record capabilities

On Fri, Dec 18, 2015 at 01:59:20PM -0700, Mathieu Poirier wrote:
> +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);
> +
> + *err = -EINVAL;

This should not set an error code when found_etm is false. Otherwise
any attempt to uses perf record without a cs_etm event enabled errors
out.

> + return NULL;
> +}

2015-12-19 17:42:37

by Rabin Vincent

[permalink] [raw]
Subject: Re: [PATCH V7 00/24] Coresight integration with perf

On Fri, Dec 18, 2015 at 01:58:56PM -0700, Mathieu Poirier wrote:
> 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.

These patches allow the trace to be recorded with perf, but what do you
use to actually get something useful out of the resulting perf.data?
AFAICS it can't be processed by perf script or the other perf tools.

# echo 1 > /sys/bus/coresight/devices/54162000.etb/enable_sink

# perf record -e cs_etm// true
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.032 MB perf.data ]

# perf script
0x188 [0x80]: failed to process type: 70

2015-12-20 15:29:48

by Rabin Vincent

[permalink] [raw]
Subject: Re: [PATCH V7 02/24] coresight: associating path with session rather than tracer

On Fri, Dec 18, 2015 at 01:58:58PM -0700, Mathieu Poirier wrote:
> 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.

This patch introduces a use-after-free/double kfree() if the sink is
disabled after the source.

With this command sequence:

# echo 1 > /sys/bus/coresight/devices/54162000.etb/enable_sink
# echo 1 > /sys/bus/coresight/devices/5414c000.ptm/enable_source
...
# echo 0 > /sys/bus/coresight/devices/54162000.etb/enable_sink
# echo 0 > /sys/bus/coresight/devices/5414c000.ptm/enable_source

Before these patches, we get these messages while disabling:

[ 165.822326] coresight-etm3x 5414c000.ptm: ETM tracing disabled
[ 165.828491] coresight 5414c000.ptm: releasing path(s) failed

After these patches, we get this (with SLUB debugging enabled):

=============================================================================
BUG kmalloc-512 (Not tainted): Invalid object pointer 0xed60e164
-----------------------------------------------------------------------------

Disabling lock debugging due to kernel taint
INFO: Slab 0xeebac180 objects=23 used=23 fp=0x (null) flags=0x4081
CPU: 0 PID: 856 Comm: sh Tainted: G B 4.4.0-rc5-00224-ge461459-dirty #168
Hardware name: Generic OMAP4 (Flattened Device Tree)
Backtrace:
[<c00154d4>] (dump_backtrace) from [<c00156d0>] (show_stack+0x18/0x1c)
r7:00000001 r6:eebac180 r5:c07ae71c r4:00000000
[<c00156b8>] (show_stack) from [<c027e69c>] (dump_stack+0x98/0xc0)
[<c027e604>] (dump_stack) from [<c014f018>] (slab_err+0x78/0x80)
r5:ee0013c0 r4:eebac180
[<c014efa4>] (slab_err) from [<c0153880>] (free_debug_processing+0x234/0x34c)
r3:ed60e164 r2:c068d484
r5:ee0013c0 r4:ed60e164
[<c015364c>] (free_debug_processing) from [<c0153c34>] (__slab_free+0x29c/0x428)
r10:ee0013c0 r9:00000000 r8:20000013 r7:c041a5f4 r6:ed60e164 r5:00010d00
r4:eebac180
[<c0153998>] (__slab_free) from [<c0154770>] (kfree+0x2dc/0x2f4)
r10:eda29f80 r9:00000000 r8:20000013 r7:c041a5f4 r6:ed60e164 r5:eebac180
r4:ee0013c0
[<c0154494>] (kfree) from [<c041a5f4>] (etm_disable+0xf8/0x148)
r10:eda29f80 r9:00000000 r8:ed7ba500 r7:00000000 r6:ed60e120 r5:00000001
r4:ed60e110
[<c041a4fc>] (etm_disable) from [<c0415e64>] (coresight_disable+0xbc/0x100)
r7:00000000 r6:c0771150 r5:c076c900 r4:ed662600
[<c0415da8>] (coresight_disable) from [<c0415ef0>] (enable_source_store+0x48/0x68)
r9:ed67ec8c r8:ed7d7900 r7:00000000 r6:ed7d7900 r5:00000002 r4:ed662620
[<c0415ea8>] (enable_source_store) from [<c030b37c>] (dev_attr_store+0x20/0x2c)
r5:ed67ec80 r4:c0415ea8
[<c030b35c>] (dev_attr_store) from [<c01d55d8>] (sysfs_kf_write+0x50/0x54)
r5:ed67ec80 r4:c030b35c
[<c01d5588>] (sysfs_kf_write) from [<c01d4b98>] (kernfs_fop_write+0xc4/0x1c0)
r7:00000000 r6:00000000 r5:00000002 r4:ed67ec80
[<c01d4ad4>] (kernfs_fop_write) from [<c015b60c>] (__vfs_write+0x34/0xe4)
r10:00000000 r9:eda28000 r8:c0010964 r7:eda29f80 r6:00000002 r5:c01d4ad4
r4:ed811180
[<c015b5d8>] (__vfs_write) from [<c015bf38>] (vfs_write+0x98/0x174)
r9:eda28000 r8:c0010964 r7:eda29f80 r6:000a9e40 r5:00000002 r4:ed811180
[<c015bea0>] (vfs_write) from [<c015c7a8>] (SyS_write+0x4c/0xa8)
r8:c0010964 r7:00000002 r6:000a9e40 r5:ed811180 r4:ed811180
[<c015c75c>] (SyS_write) from [<c00107c0>] (ret_fast_syscall+0x0/0x1c)
r7:00000004 r6:00000001 r5:000a9e40 r4:00000002
FIX kmalloc-512: Object at 0xed60e164 not freed

2015-12-23 16:24:04

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH V7 02/24] coresight: associating path with session rather than tracer

On 20 December 2015 at 08:29, Rabin Vincent <[email protected]> wrote:
> On Fri, Dec 18, 2015 at 01:58:58PM -0700, Mathieu Poirier wrote:
>> 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.
>
> This patch introduces a use-after-free/double kfree() if the sink is
> disabled after the source.
>
> With this command sequence:
>
> # echo 1 > /sys/bus/coresight/devices/54162000.etb/enable_sink
> # echo 1 > /sys/bus/coresight/devices/5414c000.ptm/enable_source
> ...
> # echo 0 > /sys/bus/coresight/devices/54162000.etb/enable_sink
> # echo 0 > /sys/bus/coresight/devices/5414c000.ptm/enable_source
>
> Before these patches, we get these messages while disabling:
>
> [ 165.822326] coresight-etm3x 5414c000.ptm: ETM tracing disabled
> [ 165.828491] coresight 5414c000.ptm: releasing path(s) failed

I always assumed the source would gets disabled first followed by the
sink but your sequence is entirely valid. This will be addressed.

>
> After these patches, we get this (with SLUB debugging enabled):
>
> =============================================================================
> BUG kmalloc-512 (Not tainted): Invalid object pointer 0xed60e164
> -----------------------------------------------------------------------------
>
> Disabling lock debugging due to kernel taint
> INFO: Slab 0xeebac180 objects=23 used=23 fp=0x (null) flags=0x4081
> CPU: 0 PID: 856 Comm: sh Tainted: G B 4.4.0-rc5-00224-ge461459-dirty #168
> Hardware name: Generic OMAP4 (Flattened Device Tree)
> Backtrace:
> [<c00154d4>] (dump_backtrace) from [<c00156d0>] (show_stack+0x18/0x1c)
> r7:00000001 r6:eebac180 r5:c07ae71c r4:00000000
> [<c00156b8>] (show_stack) from [<c027e69c>] (dump_stack+0x98/0xc0)
> [<c027e604>] (dump_stack) from [<c014f018>] (slab_err+0x78/0x80)
> r5:ee0013c0 r4:eebac180
> [<c014efa4>] (slab_err) from [<c0153880>] (free_debug_processing+0x234/0x34c)
> r3:ed60e164 r2:c068d484
> r5:ee0013c0 r4:ed60e164
> [<c015364c>] (free_debug_processing) from [<c0153c34>] (__slab_free+0x29c/0x428)
> r10:ee0013c0 r9:00000000 r8:20000013 r7:c041a5f4 r6:ed60e164 r5:00010d00
> r4:eebac180
> [<c0153998>] (__slab_free) from [<c0154770>] (kfree+0x2dc/0x2f4)
> r10:eda29f80 r9:00000000 r8:20000013 r7:c041a5f4 r6:ed60e164 r5:eebac180
> r4:ee0013c0
> [<c0154494>] (kfree) from [<c041a5f4>] (etm_disable+0xf8/0x148)
> r10:eda29f80 r9:00000000 r8:ed7ba500 r7:00000000 r6:ed60e120 r5:00000001
> r4:ed60e110
> [<c041a4fc>] (etm_disable) from [<c0415e64>] (coresight_disable+0xbc/0x100)
> r7:00000000 r6:c0771150 r5:c076c900 r4:ed662600
> [<c0415da8>] (coresight_disable) from [<c0415ef0>] (enable_source_store+0x48/0x68)
> r9:ed67ec8c r8:ed7d7900 r7:00000000 r6:ed7d7900 r5:00000002 r4:ed662620
> [<c0415ea8>] (enable_source_store) from [<c030b37c>] (dev_attr_store+0x20/0x2c)
> r5:ed67ec80 r4:c0415ea8
> [<c030b35c>] (dev_attr_store) from [<c01d55d8>] (sysfs_kf_write+0x50/0x54)
> r5:ed67ec80 r4:c030b35c
> [<c01d5588>] (sysfs_kf_write) from [<c01d4b98>] (kernfs_fop_write+0xc4/0x1c0)
> r7:00000000 r6:00000000 r5:00000002 r4:ed67ec80
> [<c01d4ad4>] (kernfs_fop_write) from [<c015b60c>] (__vfs_write+0x34/0xe4)
> r10:00000000 r9:eda28000 r8:c0010964 r7:eda29f80 r6:00000002 r5:c01d4ad4
> r4:ed811180
> [<c015b5d8>] (__vfs_write) from [<c015bf38>] (vfs_write+0x98/0x174)
> r9:eda28000 r8:c0010964 r7:eda29f80 r6:000a9e40 r5:00000002 r4:ed811180
> [<c015bea0>] (vfs_write) from [<c015c7a8>] (SyS_write+0x4c/0xa8)
> r8:c0010964 r7:00000002 r6:000a9e40 r5:ed811180 r4:ed811180
> [<c015c75c>] (SyS_write) from [<c00107c0>] (ret_fast_syscall+0x0/0x1c)
> r7:00000004 r6:00000001 r5:000a9e40 r4:00000002
> FIX kmalloc-512: Object at 0xed60e164 not freed

2015-12-23 16:27:34

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH V7 04/24] coresight: moving PM runtime operations to core framework

On 19 December 2015 at 10:13, Rabin Vincent <[email protected]> wrote:
> On Fri, Dec 18, 2015 at 01:59:00PM -0700, Mathieu Poirier wrote:
>> @@ -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);
>> }
>
> This leads to the following splat:
>
> BUG: sleeping function called from invalid context at /home/rabin/dev/linux/drivers/base/power/runtime.c:892
> in_atomic(): 1, irqs_disabled(): 128, pid: 763, name: perf
> 2 locks held by perf/763:
> #0: (&mm->mmap_sem){++++++}, at: [<c012e42c>] vm_munmap+0x2c/0x50
> #1: (&event->mmap_mutex){+.+.+.}, at: [<c008248c>] atomic_dec_and_mutex_lock+0x58/0x98
> irq event stamp: 63152
> hardirqs last enabled at (63151): [<c04f7edc>] _raw_spin_unlock_irqrestore+0x30/0x5c
> hardirqs last disabled at (63152): [<c04f88e8>] __irq_svc+0x48/0x78
> softirqs last enabled at (61242): [<c003a914>] __do_softirq+0x408/0x4fc
> softirqs last disabled at (61223): [<c003ad50>] irq_exit+0xcc/0x130
> CPU: 1 PID: 763 Comm: perf Not tainted 4.4.0-rc5-00224-ge461459-dirty #152
> Hardware name: Generic OMAP4 (Flattened Device Tree)
> [<c00184dc>] (unwind_backtrace) from [<c0014c74>] (show_stack+0x10/0x14)
> [<c0014c74>] (show_stack) from [<c025c834>] (dump_stack+0x90/0xb8)
> [<c025c834>] (dump_stack) from [<c02f026c>] (__pm_runtime_idle+0xa4/0xa8)
> [<c02f026c>] (__pm_runtime_idle) from [<c03e1a08>] (coresight_release_path+0x38/0x7c)
> [<c03e1a08>] (coresight_release_path) from [<c03e210c>] (free_event_data+0x84/0x9c)
> [<c03e210c>] (free_event_data) from [<c00f8be8>] (rb_irq_work+0x4c/0xcc)
> [<c00f8be8>] (rb_irq_work) from [<c00e8860>] (irq_work_run_list+0x7c/0xb4)
> [<c00e8860>] (irq_work_run_list) from [<c00e88b8>] (irq_work_run+0x20/0x34)
> [<c00e88b8>] (irq_work_run) from [<c0016dcc>] (handle_IPI+0x1cc/0x334)
> [<c0016dcc>] (handle_IPI) from [<c0009538>] (gic_handle_irq+0x84/0x88)
> [<c0009538>] (gic_handle_irq) from [<c04f88f8>] (__irq_svc+0x58/0x78)
> Exception stack(0xed865e98 to 0xed865ee0)
> 5e80: 00000001 00000110
> 5ea0: 00000000 ee2f1080 20000113 c0784808 edbe4e1c b6ae5000 edb882f0 ed9b1e04
> 5ec0: edb88298 c075648c 00000002 ed865ee8 c008724c c04f7ee0 20000113 ffffffff
> [<c04f88f8>] (__irq_svc) from [<c04f7ee0>] (_raw_spin_unlock_irqrestore+0x34/0x5c)
> [<c04f7ee0>] (_raw_spin_unlock_irqrestore) from [<c00e8978>] (irq_work_queue+0xac/0xb4)
> [<c00e8978>] (irq_work_queue) from [<c00f5020>] (perf_mmap_close+0x370/0x3c8)
> [<c00f5020>] (perf_mmap_close) from [<c012c560>] (remove_vma+0x40/0x6c)
> [<c012c560>] (remove_vma) from [<c012e2b4>] (do_munmap+0x210/0x35c)
> [<c012e2b4>] (do_munmap) from [<c012e43c>] (vm_munmap+0x3c/0x50)
> [<c012e43c>] (vm_munmap) from [<c0010320>] (ret_fast_syscall+0x0/0x1c)
>
> It should presumably be using pm_runtime_put() instead.

That's a first - what platform did you test on? If I send you fixes
will you be able to help me with the verification?

Thanks,
Mathieu

2015-12-23 16:29:47

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH V7 00/24] Coresight integration with perf

On 19 December 2015 at 10:42, Rabin Vincent <[email protected]> wrote:
> On Fri, Dec 18, 2015 at 01:58:56PM -0700, Mathieu Poirier wrote:
>> 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.
>
> These patches allow the trace to be recorded with perf, but what do you
> use to actually get something useful out of the resulting perf.data?
> AFAICS it can't be processed by perf script or the other perf tools.

You are correct. The team is working on a trace decoder that will be
embedded in the perf tool suite.

>
> # echo 1 > /sys/bus/coresight/devices/54162000.etb/enable_sink
>
> # perf record -e cs_etm// true
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.032 MB perf.data ]
>
> # perf script
> 0x188 [0x80]: failed to process type: 70

2015-12-23 16:33:15

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH V7 17/24] coresight: etb10: implementing AUX space API

On 19 December 2015 at 10:23, Rabin Vincent <[email protected]> wrote:
> On Fri, Dec 18, 2015 at 01:59:13PM -0700, Mathieu Poirier wrote:
>> +static void etb_update_buffer(struct coresight_device *csdev,
>> + struct perf_output_handle *handle,
>> + void *sink_config)
>> +{
>> + int i, cur;
>> + u32 *buf_ptr;
>
> ...
>
>> + 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;
>
> buf_ptr should either be changed to a u8* or just the first write should
> be present. Otherwise this overwrites lots of memory.

Good catch!

>
>> +
>> + offset += 4;
>> + if (offset >= PAGE_SIZE) {

2015-12-23 16:46:34

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH V7 24/24] perf tools: adding coresight etm PMU record capabilities

On 19 December 2015 at 10:27, Rabin Vincent <[email protected]> wrote:
> On Fri, Dec 18, 2015 at 01:59:20PM -0700, Mathieu Poirier wrote:
>> +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);
>> +
>> + *err = -EINVAL;
>
> This should not set an error code when found_etm is false. Otherwise
> any attempt to uses perf record without a cs_etm event enabled errors
> out.

Yes, you're right.

Many thanks for the thorough review,
Mathieu

>
>> + return NULL;
>> +}

2015-12-24 19:20:58

by Rabin Vincent

[permalink] [raw]
Subject: Re: [PATCH V7 04/24] coresight: moving PM runtime operations to core framework

On Wed, Dec 23, 2015 at 09:27:28AM -0700, Mathieu Poirier wrote:
> On 19 December 2015 at 10:13, Rabin Vincent <[email protected]> wrote:
> > It should presumably be using pm_runtime_put() instead.
>
> That's a first - what platform did you test on? If I send you fixes
> will you be able to help me with the verification?

I tested on a Pandaboard (OMAP4), with [1] applied, but you're likely
to see this on any platform if you have CONFIG_PM=y and
CONFIG_DEBUG_ATOMIC_SLEEP=y.

[1] https://github.com/rabinv/linux/commit/omap-coresight