coresight: Add default sink selection to CoreSight base

Adds a method to select a suitable sink connected to a given source.

In cases where no sink is defined, the coresight_find_default_sink
routine can search from a given source, through the child connections
until a suitable sink is found.

The suitability is defined in by the sink coresight_dev_subtype on the
CoreSight device, and the distance from the source by counting
connections.

Higher value subtype is preferred - where these are equal, shorter
distance from source is used as a tie-break.

This allows for default sink to be discovered were none is specified
(e.g. perf command line)

Signed-off-by: Mike Leach <mike.leach@linaro.org>
Suggested-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Leo Yan <leo.yan@linaro.org>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Link: https://lore.kernel.org/r/20200716175746.3338735-15-mathieu.poirier@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Mike Leach 2020-07-16 11:57:43 -06:00 committed by Greg Kroah-Hartman
parent d021f5c5ff
commit 0336bdfd73
3 changed files with 171 additions and 0 deletions

View File

@ -150,6 +150,8 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data);
struct coresight_device *coresight_get_sink(struct list_head *path);
struct coresight_device *coresight_get_enabled_sink(bool reset);
struct coresight_device *coresight_get_sink_by_id(u32 id);
struct coresight_device *
coresight_find_default_sink(struct coresight_device *csdev);
struct list_head *coresight_build_path(struct coresight_device *csdev,
struct coresight_device *sink);
void coresight_release_path(struct list_head *path);

View File

@ -769,6 +769,171 @@ void coresight_release_path(struct list_head *path)
path = NULL;
}
/* return true if the device is a suitable type for a default sink */
static inline bool coresight_is_def_sink_type(struct coresight_device *csdev)
{
/* sink & correct subtype */
if (((csdev->type == CORESIGHT_DEV_TYPE_SINK) ||
(csdev->type == CORESIGHT_DEV_TYPE_LINKSINK)) &&
(csdev->subtype.sink_subtype >= CORESIGHT_DEV_SUBTYPE_SINK_BUFFER))
return true;
return false;
}
/**
* coresight_select_best_sink - return the best sink for use as default from
* the two provided.
*
* @sink: current best sink.
* @depth: search depth where current sink was found.
* @new_sink: new sink for comparison with current sink.
* @new_depth: search depth where new sink was found.
*
* Sinks prioritised according to coresight_dev_subtype_sink, with only
* subtypes CORESIGHT_DEV_SUBTYPE_SINK_BUFFER or higher being used.
*
* Where two sinks of equal priority are found, the sink closest to the
* source is used (smallest search depth).
*
* return @new_sink & update @depth if better than @sink, else return @sink.
*/
static struct coresight_device *
coresight_select_best_sink(struct coresight_device *sink, int *depth,
struct coresight_device *new_sink, int new_depth)
{
bool update = false;
if (!sink) {
/* first found at this level */
update = true;
} else if (new_sink->subtype.sink_subtype >
sink->subtype.sink_subtype) {
/* found better sink */
update = true;
} else if ((new_sink->subtype.sink_subtype ==
sink->subtype.sink_subtype) &&
(*depth > new_depth)) {
/* found same but closer sink */
update = true;
}
if (update)
*depth = new_depth;
return update ? new_sink : sink;
}
/**
* coresight_find_sink - recursive function to walk trace connections from
* source to find a suitable default sink.
*
* @csdev: source / current device to check.
* @depth: [in] search depth of calling dev, [out] depth of found sink.
*
* This will walk the connection path from a source (ETM) till a suitable
* sink is encountered and return that sink to the original caller.
*
* If current device is a plain sink return that & depth, otherwise recursively
* call child connections looking for a sink. Select best possible using
* coresight_select_best_sink.
*
* return best sink found, or NULL if not found at this node or child nodes.
*/
static struct coresight_device *
coresight_find_sink(struct coresight_device *csdev, int *depth)
{
int i, curr_depth = *depth + 1, found_depth = 0;
struct coresight_device *found_sink = NULL;
if (coresight_is_def_sink_type(csdev)) {
found_depth = curr_depth;
found_sink = csdev;
if (csdev->type == CORESIGHT_DEV_TYPE_SINK)
goto return_def_sink;
/* look past LINKSINK for something better */
}
/*
* Not a sink we want - or possible child sink may be better.
* recursively explore each port found on this element.
*/
for (i = 0; i < csdev->pdata->nr_outport; i++) {
struct coresight_device *child_dev, *sink = NULL;
int child_depth = curr_depth;
child_dev = csdev->pdata->conns[i].child_dev;
if (child_dev)
sink = coresight_find_sink(child_dev, &child_depth);
if (sink)
found_sink = coresight_select_best_sink(found_sink,
&found_depth,
sink,
child_depth);
}
return_def_sink:
/* return found sink and depth */
if (found_sink)
*depth = found_depth;
return found_sink;
}
/**
* coresight_find_default_sink: Find a sink suitable for use as a
* default sink.
*
* @csdev: starting source to find a connected sink.
*
* Walks connections graph looking for a suitable sink to enable for the
* supplied source. Uses CoreSight device subtypes and distance from source
* to select the best sink.
*
* If a sink is found, then the default sink for this device is set and
* will be automatically used in future.
*
* Used in cases where the CoreSight user (perf / sysfs) has not selected a
* sink.
*/
struct coresight_device *
coresight_find_default_sink(struct coresight_device *csdev)
{
int depth = 0;
/* look for a default sink if we have not found for this device */
if (!csdev->def_sink)
csdev->def_sink = coresight_find_sink(csdev, &depth);
return csdev->def_sink;
}
static int coresight_remove_sink_ref(struct device *dev, void *data)
{
struct coresight_device *sink = data;
struct coresight_device *source = to_coresight_device(dev);
if (source->def_sink == sink)
source->def_sink = NULL;
return 0;
}
/**
* coresight_clear_default_sink: Remove all default sink references to the
* supplied sink.
*
* If supplied device is a sink, then check all the bus devices and clear
* out all the references to this sink from the coresight_device def_sink
* parameter.
*
* @csdev: coresight sink - remove references to this from all sources.
*/
static void coresight_clear_default_sink(struct coresight_device *csdev)
{
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK) ||
(csdev->type == CORESIGHT_DEV_TYPE_LINKSINK)) {
bus_for_each_dev(&coresight_bustype, NULL, csdev,
coresight_remove_sink_ref);
}
}
/** coresight_validate_source - make sure a source has the right credentials
* @csdev: the device structure for a source.
* @function: the function this was called from.
@ -1358,6 +1523,7 @@ void coresight_unregister(struct coresight_device *csdev)
etm_perf_del_symlink_sink(csdev);
/* Remove references of that device in the topology */
coresight_remove_conns(csdev);
coresight_clear_default_sink(csdev);
coresight_release_platform_data(csdev, csdev->pdata);
device_unregister(&csdev->dev);
}

View File

@ -48,6 +48,7 @@ enum coresight_dev_subtype_sink {
CORESIGHT_DEV_SUBTYPE_SINK_NONE,
CORESIGHT_DEV_SUBTYPE_SINK_PORT,
CORESIGHT_DEV_SUBTYPE_SINK_BUFFER,
CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM,
};
enum coresight_dev_subtype_link {
@ -182,6 +183,7 @@ struct coresight_sysfs_link {
* happens when a source has been selected and a path is enabled
* from source to that sink.
* @ea: Device attribute for sink representation under PMU directory.
* @def_sink: cached reference to default sink found for this device.
* @ect_dev: Associated cross trigger device. Not part of the trace data
* path or connections.
* @nr_links: number of sysfs links created to other components from this
@ -200,6 +202,7 @@ struct coresight_device {
/* sink specific fields */
bool activated; /* true only if a sink is part of a path */
struct dev_ext_attribute *ea;
struct coresight_device *def_sink;
/* cross trigger handling */
struct coresight_device *ect_dev;
/* sysfs links between components */