/* * Use IDR to map the hash of the source's device name * to the pointer of path for the source. The idr is for * the sources which aren't associated with CPU.
*/ static DEFINE_IDR(path_idr);
/* * When operating Coresight drivers from the sysFS interface, only a single * path can exist from a tracer (associated to a CPU) to a sink.
*/ static DEFINE_PER_CPU(struct coresight_path *, tracer_path);
/* * Comparison with CS_MODE_SYSFS works without taking any device * specific spinlock because the truthyness of that comparison can only * change with coresight_mutex held, which we already have here.
*/
lockdep_assert_held(&coresight_mutex); if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {
ret = source_ops(csdev)->enable(csdev, NULL, mode, path); if (ret) return ret;
}
csdev->refcnt++;
return 0;
}
/** * coresight_disable_source_sysfs - Drop the reference count by 1 and disable * the device if there are no users left. * * @csdev: The coresight device to disable * @data: Opaque data to pass on to the disable function of the source device. * For example in perf mode this is a pointer to the struct perf_event. * * Returns true if the device has been disabled.
*/ staticbool coresight_disable_source_sysfs(struct coresight_device *csdev, void *data)
{
lockdep_assert_held(&coresight_mutex); if (coresight_get_mode(csdev) != CS_MODE_SYSFS) returnfalse;
/** * coresight_find_activated_sysfs_sink - returns the first sink activated via * sysfs using connection based search starting from the source reference. * * @csdev: Coresight source device reference
*/ staticstruct coresight_device *
coresight_find_activated_sysfs_sink(struct coresight_device *csdev)
{ int i; struct coresight_device *sink = NULL;
/* * Recursively explore each port found on this element.
*/ for (i = 0; i < csdev->pdata->nr_outconns; i++) { struct coresight_device *child_dev;
child_dev = csdev->pdata->out_conns[i]->dest_dev; if (child_dev)
sink = coresight_find_activated_sysfs_sink(child_dev); if (sink) return sink;
}
return NULL;
}
/** coresight_validate_source - make sure a source has the right credentials to * be used via sysfs. * @csdev: the device structure for a source. * @function: the function this was called from. * * Assumes the coresight_mutex is held.
*/ staticint coresight_validate_source_sysfs(struct coresight_device *csdev, constchar *function)
{
u32 type, subtype;
type = csdev->type;
subtype = csdev->subtype.source_subtype;
if (type != CORESIGHT_DEV_TYPE_SOURCE) {
dev_err(&csdev->dev, "wrong device type in %s\n", function); return -EINVAL;
}
int coresight_enable_sysfs(struct coresight_device *csdev)
{ int cpu, ret = 0; struct coresight_device *sink; struct coresight_path *path; enum coresight_dev_subtype_source subtype;
u32 hash;
subtype = csdev->subtype.source_subtype;
mutex_lock(&coresight_mutex);
ret = coresight_validate_source_sysfs(csdev, __func__); if (ret) goto out;
/* * mode == SYSFS implies that it's already enabled. Don't look at the * refcount to determine this because we don't claim the source until * coresight_enable_source() so can still race with Perf mode which * doesn't hold coresight_mutex.
*/ if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { /* * There could be multiple applications driving the software * source. So keep the refcount for each such user when the * source is already enabled.
*/ if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
csdev->refcnt++; goto out;
}
sink = coresight_find_activated_sysfs_sink(csdev); if (!sink) {
ret = -EINVAL; goto out;
}
path = coresight_build_path(csdev, sink); if (IS_ERR(path)) {
pr_err("building path(s) failed\n");
ret = PTR_ERR(path); goto out;
}
coresight_path_assign_trace_id(path, CS_MODE_SYSFS); if (!IS_VALID_CS_TRACE_ID(path->trace_id)) goto err_path;
ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); if (ret) goto err_path;
ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, path); if (ret) goto err_source;
switch (subtype) { case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: /* * When working from sysFS it is important to keep track * of the paths that were created so that they can be * undone in 'coresight_disable()'. Since there can only * be a single session per tracer (when working from sysFS) * a per-cpu variable will do just fine.
*/
cpu = source_ops(csdev)->cpu_id(csdev);
per_cpu(tracer_path, cpu) = path; break; case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: /* * Use the hash of source's device name as ID * and map the ID to the pointer of the path.
*/
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); if (ret) goto err_source; break; default: /* We can't be here */ break;
}
ret = coresight_validate_source_sysfs(csdev, __func__); if (ret) goto out;
if (!coresight_disable_source_sysfs(csdev, NULL)) goto out;
switch (csdev->subtype.source_subtype) { case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
cpu = source_ops(csdev)->cpu_id(csdev);
path = per_cpu(tracer_path, cpu);
per_cpu(tracer_path, cpu) = NULL; break; case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); /* Find the path by the hash. */
path = idr_find(&path_idr, hash); if (path == NULL) {
pr_err("Path is not found for %s\n", dev_name(&csdev->dev)); goto out;
}
idr_remove(&path_idr, hash); break; default: /* We can't be here */ break;
}
/* * Create connections group for CoreSight devices. * This group will then be used to collate the sysfs links between * devices.
*/ int coresight_create_conns_sysfs_group(struct coresight_device *csdev)
{ int ret = 0;
if (!csdev) return -EINVAL;
ret = sysfs_create_group(&csdev->dev.kobj, &coresight_conns_group); if (ret) return ret;
csdev->has_conns_grp = true; return ret;
}
void coresight_remove_conns_sysfs_group(struct coresight_device *csdev)
{ if (!csdev) return;
if (csdev->has_conns_grp) {
sysfs_remove_group(&csdev->dev.kobj, &coresight_conns_group);
csdev->has_conns_grp = false;
}
}
int coresight_add_sysfs_link(struct coresight_sysfs_link *info)
{ int ret = 0;
if (!info) return -EINVAL; if (!info->orig || !info->target ||
!info->orig_name || !info->target_name) return -EINVAL; if (!info->orig->has_conns_grp || !info->target->has_conns_grp) return -EINVAL;
/* first link orig->target */
ret = sysfs_add_link_to_group(&info->orig->dev.kobj,
coresight_conns_group.name,
&info->target->dev.kobj,
info->orig_name); if (ret) return ret;
/* second link target->orig */
ret = sysfs_add_link_to_group(&info->target->dev.kobj,
coresight_conns_group.name,
&info->orig->dev.kobj,
info->target_name);
/* error in second link - remove first - otherwise inc counts */ if (ret) {
sysfs_remove_link_from_group(&info->orig->dev.kobj,
coresight_conns_group.name,
info->orig_name);
} else {
info->orig->nr_links++;
info->target->nr_links++;
}
/* * coresight_make_links: Make a link for a connection from a @orig * device to @target, represented by @conn. * * e.g, for devOrig[output_X] -> devTarget[input_Y] is represented * as two symbolic links : * * /sys/.../devOrig/out:X -> /sys/.../devTarget/ * /sys/.../devTarget/in:Y -> /sys/.../devOrig/ * * The link names are allocated for a device where it appears. i.e, the * "out" link on the master and "in" link on the slave device. * The link info is stored in the connection record for avoiding * the reconstruction of names for removal.
*/ int coresight_make_links(struct coresight_device *orig, struct coresight_connection *conn, struct coresight_device *target)
{ int ret = -ENOMEM; char *outs = NULL, *ins = NULL; struct coresight_sysfs_link *link = NULL;
/* Helper devices aren't shown in sysfs */ if (conn->dest_port == -1 && conn->src_port == -1) return 0;
do {
outs = devm_kasprintf(&orig->dev, GFP_KERNEL, "out:%d", conn->src_port); if (!outs) break;
ins = devm_kasprintf(&target->dev, GFP_KERNEL, "in:%d", conn->dest_port); if (!ins) break;
link = devm_kzalloc(&orig->dev, sizeof(struct coresight_sysfs_link),
GFP_KERNEL); if (!link) break;
ret = coresight_add_sysfs_link(link); if (ret) break;
conn->link = link; return 0;
} while (0);
return ret;
}
/* * coresight_remove_links: Remove the sysfs links for a given connection @conn, * from @orig device to @target device. See coresight_make_links() for more * details.
*/ void coresight_remove_links(struct coresight_device *orig, struct coresight_connection *conn)
{ if (!orig || !conn->link) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.