Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca878b14 authored by Mathieu Poirier's avatar Mathieu Poirier Committed by Greg Kroah-Hartman
Browse files

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: default avatarMathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 2b7adc46
Loading
Loading
Loading
Loading
+134 −15
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/types.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/workqueue.h>


#include "coresight-etm-perf.h"
#include "coresight-priv.h"
#include "coresight-priv.h"


static struct pmu etm_pmu;
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 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)
	struct etm_filters *filters;
		return -ENOENT;
	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;
	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)
static void free_event_data(struct work_struct *work)
{
{
	int cpu;
	int cpu;
@@ -342,6 +377,87 @@ static void etm_event_del(struct perf_event *event, int mode)
	etm_event_stop(event, PERF_EF_UPDATE);
	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)
int etm_perf_symlink(struct coresight_device *csdev, bool link)
{
{
	char entry[sizeof("cpu9999999")];
	char entry[sizeof("cpu9999999")];
@@ -383,6 +499,9 @@ static int __init etm_perf_init(void)
	etm_pmu.stop			= etm_event_stop;
	etm_pmu.stop			= etm_event_stop;
	etm_pmu.add			= etm_event_add;
	etm_pmu.add			= etm_event_add;
	etm_pmu.del			= etm_event_del;
	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);
	ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
	if (ret == 0)
	if (ret == 0)
+34 −0
Original line number Original line Diff line number Diff line
@@ -18,8 +18,42 @@
#ifndef _CORESIGHT_ETM_PERF_H
#ifndef _CORESIGHT_ETM_PERF_H
#define _CORESIGHT_ETM_PERF_H
#define _CORESIGHT_ETM_PERF_H


#include "coresight-priv.h"

struct coresight_device;
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
#ifdef CONFIG_CORESIGHT
int etm_perf_symlink(struct coresight_device *csdev, bool link);
int etm_perf_symlink(struct coresight_device *csdev, bool link);