mirror of
https://github.com/torvalds/linux.git
synced 2024-11-18 10:01:43 +00:00
coresight: etm-perf: configuring filters from perf core
This patch implements the required API needed to access and retrieve range and start/stop filters from the perf core. Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
2b7adc460f
commit
ca878b1466
@ -27,6 +27,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "coresight-etm-perf.h"
|
||||
#include "coresight-priv.h"
|
||||
|
||||
static struct pmu etm_pmu;
|
||||
@ -71,14 +72,48 @@ static const struct attribute_group *etm_pmu_attr_groups[] = {
|
||||
|
||||
static void etm_event_read(struct perf_event *event) {}
|
||||
|
||||
static int etm_event_init(struct perf_event *event)
|
||||
static int etm_addr_filters_alloc(struct perf_event *event)
|
||||
{
|
||||
if (event->attr.type != etm_pmu.type)
|
||||
return -ENOENT;
|
||||
struct etm_filters *filters;
|
||||
int node = event->cpu == -1 ? -1 : cpu_to_node(event->cpu);
|
||||
|
||||
filters = kzalloc_node(sizeof(struct etm_filters), GFP_KERNEL, node);
|
||||
if (!filters)
|
||||
return -ENOMEM;
|
||||
|
||||
if (event->parent)
|
||||
memcpy(filters, event->parent->hw.addr_filters,
|
||||
sizeof(*filters));
|
||||
|
||||
event->hw.addr_filters = filters;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void etm_event_destroy(struct perf_event *event)
|
||||
{
|
||||
kfree(event->hw.addr_filters);
|
||||
event->hw.addr_filters = NULL;
|
||||
}
|
||||
|
||||
static int etm_event_init(struct perf_event *event)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (event->attr.type != etm_pmu.type) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = etm_addr_filters_alloc(event);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
event->destroy = etm_event_destroy;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void free_event_data(struct work_struct *work)
|
||||
{
|
||||
int cpu;
|
||||
@ -342,6 +377,87 @@ static void etm_event_del(struct perf_event *event, int mode)
|
||||
etm_event_stop(event, PERF_EF_UPDATE);
|
||||
}
|
||||
|
||||
static int etm_addr_filters_validate(struct list_head *filters)
|
||||
{
|
||||
bool range = false, address = false;
|
||||
int index = 0;
|
||||
struct perf_addr_filter *filter;
|
||||
|
||||
list_for_each_entry(filter, filters, entry) {
|
||||
/*
|
||||
* No need to go further if there's no more
|
||||
* room for filters.
|
||||
*/
|
||||
if (++index > ETM_ADDR_CMP_MAX)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* As taken from the struct perf_addr_filter documentation:
|
||||
* @range: 1: range, 0: address
|
||||
*
|
||||
* At this time we don't allow range and start/stop filtering
|
||||
* to cohabitate, they have to be mutually exclusive.
|
||||
*/
|
||||
if ((filter->range == 1) && address)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if ((filter->range == 0) && range)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* For range filtering, the second address in the address
|
||||
* range comparator needs to be higher than the first.
|
||||
* Invalid otherwise.
|
||||
*/
|
||||
if (filter->range && filter->size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Everything checks out with this filter, record what we've
|
||||
* received before moving on to the next one.
|
||||
*/
|
||||
if (filter->range)
|
||||
range = true;
|
||||
else
|
||||
address = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void etm_addr_filters_sync(struct perf_event *event)
|
||||
{
|
||||
struct perf_addr_filters_head *head = perf_event_addr_filters(event);
|
||||
unsigned long start, stop, *offs = event->addr_filters_offs;
|
||||
struct etm_filters *filters = event->hw.addr_filters;
|
||||
struct etm_filter *etm_filter;
|
||||
struct perf_addr_filter *filter;
|
||||
int i = 0;
|
||||
|
||||
list_for_each_entry(filter, &head->list, entry) {
|
||||
start = filter->offset + offs[i];
|
||||
stop = start + filter->size;
|
||||
etm_filter = &filters->etm_filter[i];
|
||||
|
||||
if (filter->range == 1) {
|
||||
etm_filter->start_addr = start;
|
||||
etm_filter->stop_addr = stop;
|
||||
etm_filter->type = ETM_ADDR_TYPE_RANGE;
|
||||
} else {
|
||||
if (filter->filter == 1) {
|
||||
etm_filter->start_addr = start;
|
||||
etm_filter->type = ETM_ADDR_TYPE_START;
|
||||
} else {
|
||||
etm_filter->stop_addr = stop;
|
||||
etm_filter->type = ETM_ADDR_TYPE_STOP;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
filters->nr_filters = i;
|
||||
}
|
||||
|
||||
int etm_perf_symlink(struct coresight_device *csdev, bool link)
|
||||
{
|
||||
char entry[sizeof("cpu9999999")];
|
||||
@ -371,18 +487,21 @@ static int __init etm_perf_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE;
|
||||
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;
|
||||
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;
|
||||
etm_pmu.addr_filters_sync = etm_addr_filters_sync;
|
||||
etm_pmu.addr_filters_validate = etm_addr_filters_validate;
|
||||
etm_pmu.nr_addr_filters = ETM_ADDR_CMP_MAX;
|
||||
|
||||
ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
|
||||
if (ret == 0)
|
||||
|
@ -18,8 +18,42 @@
|
||||
#ifndef _CORESIGHT_ETM_PERF_H
|
||||
#define _CORESIGHT_ETM_PERF_H
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
struct coresight_device;
|
||||
|
||||
/*
|
||||
* In both ETMv3 and v4 the maximum number of address comparator implentable
|
||||
* is 8. The actual number is implementation specific and will be checked
|
||||
* when filters are applied.
|
||||
*/
|
||||
#define ETM_ADDR_CMP_MAX 8
|
||||
|
||||
/**
|
||||
* struct etm_filter - single instruction range or start/stop configuration.
|
||||
* @start_addr: The address to start tracing on.
|
||||
* @stop_addr: The address to stop tracing on.
|
||||
* @type: Is this a range or start/stop filter.
|
||||
*/
|
||||
struct etm_filter {
|
||||
unsigned long start_addr;
|
||||
unsigned long stop_addr;
|
||||
enum etm_addr_type type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct etm_filters - set of filters for a session
|
||||
* @etm_filter: All the filters for this session.
|
||||
* @nr_filters: Number of filters
|
||||
* @ssstatus: Status of the start/stop logic.
|
||||
*/
|
||||
struct etm_filters {
|
||||
struct etm_filter etm_filter[ETM_ADDR_CMP_MAX];
|
||||
unsigned int nr_filters;
|
||||
bool ssstatus;
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_CORESIGHT
|
||||
int etm_perf_symlink(struct coresight_device *csdev, bool link);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user