summaryrefslogtreecommitdiff
path: root/drivers/hwtracing/coresight/coresight-etm-perf.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-etm-perf.c')
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.c150
1 files changed, 120 insertions, 30 deletions
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 6f398377fec9..8ebd728d3a80 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -18,8 +18,10 @@
#include <linux/types.h>
#include <linux/workqueue.h>
+#include "coresight-config.h"
#include "coresight-etm-perf.h"
#include "coresight-priv.h"
+#include "coresight-syscfg.h"
static struct pmu etm_pmu;
static bool etm_perf_up;
@@ -57,8 +59,13 @@ PMU_FORMAT_ATTR(contextid1, "config:" __stringify(ETM_OPT_CTXTID));
PMU_FORMAT_ATTR(contextid2, "config:" __stringify(ETM_OPT_CTXTID2));
PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS));
PMU_FORMAT_ATTR(retstack, "config:" __stringify(ETM_OPT_RETSTK));
+/* preset - if sink ID is used as a configuration selector */
+PMU_FORMAT_ATTR(preset, "config:0-3");
/* Sink ID - same for all ETMs */
PMU_FORMAT_ATTR(sinkid, "config2:0-31");
+/* config ID - set if a system configuration is selected */
+PMU_FORMAT_ATTR(configid, "config2:32-63");
+
/*
* contextid always traces the "PID". The PID is in CONTEXTIDR_EL1
@@ -88,6 +95,8 @@ static struct attribute *etm_config_formats_attr[] = {
&format_attr_timestamp.attr,
&format_attr_retstack.attr,
&format_attr_sinkid.attr,
+ &format_attr_preset.attr,
+ &format_attr_configid.attr,
NULL,
};
@@ -105,9 +114,19 @@ static const struct attribute_group etm_pmu_sinks_group = {
.attrs = etm_config_sinks_attr,
};
+static struct attribute *etm_config_events_attr[] = {
+ NULL,
+};
+
+static const struct attribute_group etm_pmu_events_group = {
+ .name = "events",
+ .attrs = etm_config_events_attr,
+};
+
static const struct attribute_group *etm_pmu_attr_groups[] = {
&etm_pmu_format_group,
&etm_pmu_sinks_group,
+ &etm_pmu_events_group,
NULL,
};
@@ -196,6 +215,10 @@ static void free_event_data(struct work_struct *work)
/* Free the sink buffers, if there are any */
free_sink_buffer(event_data);
+ /* clear any configuration we were using */
+ if (event_data->cfg_hash)
+ cscfg_deactivate_config(event_data->cfg_hash);
+
for_each_cpu(cpu, mask) {
struct list_head **ppath;
@@ -273,7 +296,7 @@ static bool sinks_compatible(struct coresight_device *a,
static void *etm_setup_aux(struct perf_event *event, void **pages,
int nr_pages, bool overwrite)
{
- u32 id;
+ u32 id, cfg_hash;
int cpu = event->cpu;
cpumask_t *mask;
struct coresight_device *sink = NULL;
@@ -286,11 +309,19 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
INIT_WORK(&event_data->work, free_event_data);
/* First get the selected sink from user space. */
- if (event->attr.config2) {
+ if (event->attr.config2 & GENMASK_ULL(31, 0)) {
id = (u32)event->attr.config2;
sink = user_sink = coresight_get_sink_by_id(id);
}
+ /* check if user wants a coresight configuration selected */
+ cfg_hash = (u32)((event->attr.config2 & GENMASK_ULL(63, 32)) >> 32);
+ if (cfg_hash) {
+ if (cscfg_activate_config(cfg_hash))
+ goto err;
+ event_data->cfg_hash = cfg_hash;
+ }
+
mask = &event_data->mask;
/*
@@ -658,68 +689,127 @@ static ssize_t etm_perf_sink_name_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "0x%lx\n", (unsigned long)(ea->var));
}
-int etm_perf_add_symlink_sink(struct coresight_device *csdev)
+static struct dev_ext_attribute *
+etm_perf_add_symlink_group(struct device *dev, const char *name, const char *group_name)
{
- int ret;
+ struct dev_ext_attribute *ea;
unsigned long hash;
- const char *name;
+ int ret;
struct device *pmu_dev = etm_pmu.dev;
- struct device *dev = &csdev->dev;
- struct dev_ext_attribute *ea;
-
- if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
- csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
- return -EINVAL;
-
- if (csdev->ea != NULL)
- return -EINVAL;
if (!etm_perf_up)
- return -EPROBE_DEFER;
+ return ERR_PTR(-EPROBE_DEFER);
ea = devm_kzalloc(dev, sizeof(*ea), GFP_KERNEL);
if (!ea)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- name = dev_name(dev);
- /* See function coresight_get_sink_by_id() to know where this is used */
+ /*
+ * If this function is called adding a sink then the hash is used for
+ * sink selection - see function coresight_get_sink_by_id().
+ * If adding a configuration then the hash is used for selection in
+ * cscfg_activate_config()
+ */
hash = hashlen_hash(hashlen_string(NULL, name));
sysfs_attr_init(&ea->attr.attr);
ea->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
if (!ea->attr.attr.name)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
ea->attr.attr.mode = 0444;
- ea->attr.show = etm_perf_sink_name_show;
ea->var = (unsigned long *)hash;
ret = sysfs_add_file_to_group(&pmu_dev->kobj,
- &ea->attr.attr, "sinks");
+ &ea->attr.attr, group_name);
- if (!ret)
- csdev->ea = ea;
+ return ret ? ERR_PTR(ret) : ea;
+}
- return ret;
+int etm_perf_add_symlink_sink(struct coresight_device *csdev)
+{
+ const char *name;
+ struct device *dev = &csdev->dev;
+ int err = 0;
+
+ if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
+ csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
+ return -EINVAL;
+
+ if (csdev->ea != NULL)
+ return -EINVAL;
+
+ name = dev_name(dev);
+ csdev->ea = etm_perf_add_symlink_group(dev, name, "sinks");
+ if (IS_ERR(csdev->ea)) {
+ err = PTR_ERR(csdev->ea);
+ csdev->ea = NULL;
+ } else
+ csdev->ea->attr.show = etm_perf_sink_name_show;
+
+ return err;
}
-void etm_perf_del_symlink_sink(struct coresight_device *csdev)
+static void etm_perf_del_symlink_group(struct dev_ext_attribute *ea, const char *group_name)
{
struct device *pmu_dev = etm_pmu.dev;
- struct dev_ext_attribute *ea = csdev->ea;
+ sysfs_remove_file_from_group(&pmu_dev->kobj,
+ &ea->attr.attr, group_name);
+}
+
+void etm_perf_del_symlink_sink(struct coresight_device *csdev)
+{
if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
return;
- if (!ea)
+ if (!csdev->ea)
return;
- sysfs_remove_file_from_group(&pmu_dev->kobj,
- &ea->attr.attr, "sinks");
+ etm_perf_del_symlink_group(csdev->ea, "sinks");
csdev->ea = NULL;
}
+static ssize_t etm_perf_cscfg_event_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct dev_ext_attribute *ea;
+
+ ea = container_of(dattr, struct dev_ext_attribute, attr);
+ return scnprintf(buf, PAGE_SIZE, "configid=0x%lx\n", (unsigned long)(ea->var));
+}
+
+int etm_perf_add_symlink_cscfg(struct device *dev, struct cscfg_config_desc *config_desc)
+{
+ int err = 0;
+
+ if (config_desc->event_ea != NULL)
+ return 0;
+
+ config_desc->event_ea = etm_perf_add_symlink_group(dev, config_desc->name, "events");
+
+ /* set the show function to the custom cscfg event */
+ if (!IS_ERR(config_desc->event_ea))
+ config_desc->event_ea->attr.show = etm_perf_cscfg_event_show;
+ else {
+ err = PTR_ERR(config_desc->event_ea);
+ config_desc->event_ea = NULL;
+ }
+
+ return err;
+}
+
+void etm_perf_del_symlink_cscfg(struct cscfg_config_desc *config_desc)
+{
+ if (!config_desc->event_ea)
+ return;
+
+ etm_perf_del_symlink_group(config_desc->event_ea, "events");
+ config_desc->event_ea = NULL;
+}
+
int __init etm_perf_init(void)
{
int ret;
@@ -748,7 +838,7 @@ int __init etm_perf_init(void)
return ret;
}
-void __exit etm_perf_exit(void)
+void etm_perf_exit(void)
{
perf_pmu_unregister(&etm_pmu);
}