@@ -42,6 +42,7 @@
#include "coresight-etm4x-cfg.h"
#include "coresight-self-hosted-trace.h"
#include "coresight-syscfg.h"
+#include "coresight-trace-id.h"
static int boot_enable;
module_param(boot_enable, int, 0444);
@@ -237,6 +238,30 @@ static int etm4_trace_id(struct coresight_device *csdev)
return drvdata->trcid;
}
+int etm4_read_alloc_trace_id(struct etmv4_drvdata *drvdata)
+{
+ int trace_id;
+
+ /*
+ * This will allocate a trace ID to the cpu,
+ * or return the one currently allocated.
+ * The trace id function has its own lock
+ */
+ trace_id = coresight_trace_id_get_cpu_id(drvdata->cpu);
+ if (IS_VALID_CS_TRACE_ID(trace_id))
+ drvdata->trcid = (u8)trace_id;
+ else
+ dev_err(&drvdata->csdev->dev,
+ "Failed to allocate trace ID for %s on CPU%d\n",
+ dev_name(&drvdata->csdev->dev), drvdata->cpu);
+ return trace_id;
+}
+
+void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
+{
+ coresight_trace_id_put_cpu_id(drvdata->cpu);
+}
+
struct etm4_enable_arg {
struct etmv4_drvdata *drvdata;
int rc;
@@ -720,7 +745,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
static int etm4_enable_perf(struct coresight_device *csdev,
struct perf_event *event)
{
- int ret = 0;
+ int ret = 0, trace_id;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
@@ -732,6 +757,24 @@ static int etm4_enable_perf(struct coresight_device *csdev,
ret = etm4_parse_event_config(csdev, event);
if (ret)
goto out;
+
+ /*
+ * perf allocates cpu ids as part of _setup_aux() - device needs to use
+ * the allocated ID. This reads the current version without allocation.
+ *
+ * This does not use the trace id lock to prevent lock_dep issues
+ * with perf locks - we know the ID cannot change until perf shuts down
+ * the session
+ */
+ trace_id = coresight_trace_id_read_cpu_id(drvdata->cpu);
+ if (!IS_VALID_CS_TRACE_ID(trace_id)) {
+ dev_err(&drvdata->csdev->dev, "Failed to set trace ID for %s on CPU%d\n",
+ dev_name(&drvdata->csdev->dev), drvdata->cpu);
+ ret = -EINVAL;
+ goto out;
+ }
+ drvdata->trcid = (u8)trace_id;
+
/* And enable it */
ret = etm4_enable_hw(drvdata);
@@ -756,6 +799,11 @@ static int etm4_enable_sysfs(struct coresight_device *csdev)
spin_lock(&drvdata->spinlock);
+ /* sysfs needs to read and allocate a trace ID */
+ ret = etm4_read_alloc_trace_id(drvdata);
+ if (ret < 0)
+ goto unlock_sysfs_enable;
+
/*
* Executing etm4_enable_hw on the cpu whose ETM is being enabled
* ensures that register writes occur when cpu is powered.
@@ -767,6 +815,11 @@ static int etm4_enable_sysfs(struct coresight_device *csdev)
ret = arg.rc;
if (!ret)
drvdata->sticky_enable = true;
+
+ if (ret)
+ etm4_release_trace_id(drvdata);
+
+unlock_sysfs_enable:
spin_unlock(&drvdata->spinlock);
if (!ret)
@@ -898,6 +951,11 @@ static int etm4_disable_perf(struct coresight_device *csdev,
/* TRCVICTLR::SSSTATUS, bit[9] */
filters->ssstatus = (control & BIT(9));
+ /*
+ * perf will release trace ids when _free_aux() is
+ * called at the end of the session.
+ */
+
return 0;
}
@@ -923,6 +981,13 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
spin_unlock(&drvdata->spinlock);
cpus_read_unlock();
+ /*
+ * we only release trace IDs when resetting sysfs.
+ * This permits sysfs users to read the trace ID after the trace
+ * session has completed. This maintains operational behaviour with
+ * prior trace id allocation method
+ */
+
dev_dbg(&csdev->dev, "ETM tracing disabled\n");
}
@@ -1565,11 +1630,6 @@ static int etm4_dying_cpu(unsigned int cpu)
return 0;
}
-static void etm4_init_trace_id(struct etmv4_drvdata *drvdata)
-{
- drvdata->trcid = coresight_get_trace_id(drvdata->cpu);
-}
-
static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
{
int i, ret = 0;
@@ -1946,7 +2006,6 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
if (!desc.name)
return -ENOMEM;
- etm4_init_trace_id(drvdata);
etm4_set_default(&drvdata->config);
pdata = coresight_get_platform_data(dev);
@@ -266,10 +266,11 @@ static ssize_t reset_store(struct device *dev,
config->vmid_mask0 = 0x0;
config->vmid_mask1 = 0x0;
- drvdata->trcid = drvdata->cpu + 1;
-
spin_unlock(&drvdata->spinlock);
+ /* for sysfs - only release trace id when resetting */
+ etm4_release_trace_id(drvdata);
+
cscfg_csdev_reset_feats(to_coresight_device(dev));
return size;
@@ -2392,6 +2393,26 @@ static struct attribute *coresight_etmv4_attrs[] = {
NULL,
};
+/*
+ * Trace ID allocated dynamically on enable - but also allocate on read
+ * in case sysfs or perf read before enable to ensure consistent metadata
+ * information for trace decode
+ */
+static ssize_t trctraceid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int trace_id;
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+ trace_id = etm4_read_alloc_trace_id(drvdata);
+ if (trace_id < 0)
+ return trace_id;
+
+ return sysfs_emit(buf, "0x%x\n", trace_id);
+}
+static DEVICE_ATTR_RO(trctraceid);
+
struct etmv4_reg {
struct coresight_device *csdev;
u32 offset;
@@ -2528,7 +2549,7 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = {
coresight_etm4x_reg(trcpidr3, TRCPIDR3),
coresight_etm4x_reg(trcoslsr, TRCOSLSR),
coresight_etm4x_reg(trcconfig, TRCCONFIGR),
- coresight_etm4x_reg(trctraceid, TRCTRACEIDR),
+ &dev_attr_trctraceid.attr,
coresight_etm4x_reg(trcdevarch, TRCDEVARCH),
NULL,
};
@@ -1095,4 +1095,7 @@ static inline bool etm4x_is_ete(struct etmv4_drvdata *drvdata)
{
return drvdata->arch >= ETM_ARCH_ETE;
}
+
+int etm4_read_alloc_trace_id(struct etmv4_drvdata *drvdata);
+void etm4_release_trace_id(struct etmv4_drvdata *drvdata);
#endif