brintos

brintos / linux-shallow public Read only

0
0
Text · 14.9 KiB · a01c9e5 Raw
596 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2019, Linaro Limited, All rights reserved.4 * Author: Mike Leach <mike.leach@linaro.org>5 */6 7#include <linux/device.h>8#include <linux/idr.h>9#include <linux/kernel.h>10 11#include "coresight-priv.h"12#include "coresight-trace-id.h"13 14/*15 * Use IDR to map the hash of the source's device name16 * to the pointer of path for the source. The idr is for17 * the sources which aren't associated with CPU.18 */19static DEFINE_IDR(path_idr);20 21/*22 * When operating Coresight drivers from the sysFS interface, only a single23 * path can exist from a tracer (associated to a CPU) to a sink.24 */25static DEFINE_PER_CPU(struct list_head *, tracer_path);26 27ssize_t coresight_simple_show_pair(struct device *_dev,28			      struct device_attribute *attr, char *buf)29{30	struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);31	struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr);32	u64 val;33 34	pm_runtime_get_sync(_dev->parent);35	val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off);36	pm_runtime_put_sync(_dev->parent);37	return sysfs_emit(buf, "0x%llx\n", val);38}39EXPORT_SYMBOL_GPL(coresight_simple_show_pair);40 41ssize_t coresight_simple_show32(struct device *_dev,42			      struct device_attribute *attr, char *buf)43{44	struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);45	struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr);46	u64 val;47 48	pm_runtime_get_sync(_dev->parent);49	val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off);50	pm_runtime_put_sync(_dev->parent);51	return sysfs_emit(buf, "0x%llx\n", val);52}53EXPORT_SYMBOL_GPL(coresight_simple_show32);54 55static int coresight_enable_source_sysfs(struct coresight_device *csdev,56					 enum cs_mode mode, void *data)57{58	int ret;59 60	/*61	 * Comparison with CS_MODE_SYSFS works without taking any device62	 * specific spinlock because the truthyness of that comparison can only63	 * change with coresight_mutex held, which we already have here.64	 */65	lockdep_assert_held(&coresight_mutex);66	if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {67		ret = source_ops(csdev)->enable(csdev, data, mode, NULL);68		if (ret)69			return ret;70	}71 72	csdev->refcnt++;73 74	return 0;75}76 77/**78 *  coresight_disable_source_sysfs - Drop the reference count by 1 and disable79 *  the device if there are no users left.80 *81 *  @csdev: The coresight device to disable82 *  @data: Opaque data to pass on to the disable function of the source device.83 *         For example in perf mode this is a pointer to the struct perf_event.84 *85 *  Returns true if the device has been disabled.86 */87static bool coresight_disable_source_sysfs(struct coresight_device *csdev,88					   void *data)89{90	lockdep_assert_held(&coresight_mutex);91	if (coresight_get_mode(csdev) != CS_MODE_SYSFS)92		return false;93 94	csdev->refcnt--;95	if (csdev->refcnt == 0) {96		coresight_disable_source(csdev, data);97		return true;98	}99	return false;100}101 102/**103 * coresight_find_activated_sysfs_sink - returns the first sink activated via104 * sysfs using connection based search starting from the source reference.105 *106 * @csdev: Coresight source device reference107 */108static struct coresight_device *109coresight_find_activated_sysfs_sink(struct coresight_device *csdev)110{111	int i;112	struct coresight_device *sink = NULL;113 114	if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||115	     csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&116	     csdev->sysfs_sink_activated)117		return csdev;118 119	/*120	 * Recursively explore each port found on this element.121	 */122	for (i = 0; i < csdev->pdata->nr_outconns; i++) {123		struct coresight_device *child_dev;124 125		child_dev = csdev->pdata->out_conns[i]->dest_dev;126		if (child_dev)127			sink = coresight_find_activated_sysfs_sink(child_dev);128		if (sink)129			return sink;130	}131 132	return NULL;133}134 135/** coresight_validate_source - make sure a source has the right credentials to136 *  be used via sysfs.137 *  @csdev:	the device structure for a source.138 *  @function:	the function this was called from.139 *140 * Assumes the coresight_mutex is held.141 */142static int coresight_validate_source_sysfs(struct coresight_device *csdev,143				     const char *function)144{145	u32 type, subtype;146 147	type = csdev->type;148	subtype = csdev->subtype.source_subtype;149 150	if (type != CORESIGHT_DEV_TYPE_SOURCE) {151		dev_err(&csdev->dev, "wrong device type in %s\n", function);152		return -EINVAL;153	}154 155	if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&156	    subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE &&157	    subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM &&158	    subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) {159		dev_err(&csdev->dev, "wrong device subtype in %s\n", function);160		return -EINVAL;161	}162 163	return 0;164}165 166int coresight_enable_sysfs(struct coresight_device *csdev)167{168	int cpu, ret = 0;169	struct coresight_device *sink;170	struct list_head *path;171	enum coresight_dev_subtype_source subtype;172	u32 hash;173 174	subtype = csdev->subtype.source_subtype;175 176	mutex_lock(&coresight_mutex);177 178	ret = coresight_validate_source_sysfs(csdev, __func__);179	if (ret)180		goto out;181 182	/*183	 * mode == SYSFS implies that it's already enabled. Don't look at the184	 * refcount to determine this because we don't claim the source until185	 * coresight_enable_source() so can still race with Perf mode which186	 * doesn't hold coresight_mutex.187	 */188	if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {189		/*190		 * There could be multiple applications driving the software191		 * source. So keep the refcount for each such user when the192		 * source is already enabled.193		 */194		if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)195			csdev->refcnt++;196		goto out;197	}198 199	sink = coresight_find_activated_sysfs_sink(csdev);200	if (!sink) {201		ret = -EINVAL;202		goto out;203	}204 205	path = coresight_build_path(csdev, sink);206	if (IS_ERR(path)) {207		pr_err("building path(s) failed\n");208		ret = PTR_ERR(path);209		goto out;210	}211 212	ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL);213	if (ret)214		goto err_path;215 216	ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, NULL);217	if (ret)218		goto err_source;219 220	switch (subtype) {221	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:222		/*223		 * When working from sysFS it is important to keep track224		 * of the paths that were created so that they can be225		 * undone in 'coresight_disable()'.  Since there can only226		 * be a single session per tracer (when working from sysFS)227		 * a per-cpu variable will do just fine.228		 */229		cpu = source_ops(csdev)->cpu_id(csdev);230		per_cpu(tracer_path, cpu) = path;231		break;232	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:233	case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:234	case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:235		/*236		 * Use the hash of source's device name as ID237		 * and map the ID to the pointer of the path.238		 */239		hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));240		ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL);241		if (ret)242			goto err_source;243		break;244	default:245		/* We can't be here */246		break;247	}248 249out:250	mutex_unlock(&coresight_mutex);251	return ret;252 253err_source:254	coresight_disable_path(path);255 256err_path:257	coresight_release_path(path);258	goto out;259}260EXPORT_SYMBOL_GPL(coresight_enable_sysfs);261 262void coresight_disable_sysfs(struct coresight_device *csdev)263{264	int cpu, ret;265	struct list_head *path = NULL;266	u32 hash;267 268	mutex_lock(&coresight_mutex);269 270	ret = coresight_validate_source_sysfs(csdev, __func__);271	if (ret)272		goto out;273 274	if (!coresight_disable_source_sysfs(csdev, NULL))275		goto out;276 277	switch (csdev->subtype.source_subtype) {278	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:279		cpu = source_ops(csdev)->cpu_id(csdev);280		path = per_cpu(tracer_path, cpu);281		per_cpu(tracer_path, cpu) = NULL;282		break;283	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:284	case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:285	case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:286		hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));287		/* Find the path by the hash. */288		path = idr_find(&path_idr, hash);289		if (path == NULL) {290			pr_err("Path is not found for %s\n", dev_name(&csdev->dev));291			goto out;292		}293		idr_remove(&path_idr, hash);294		break;295	default:296		/* We can't be here */297		break;298	}299 300	coresight_disable_path(path);301	coresight_release_path(path);302 303out:304	mutex_unlock(&coresight_mutex);305}306EXPORT_SYMBOL_GPL(coresight_disable_sysfs);307 308static ssize_t enable_sink_show(struct device *dev,309				struct device_attribute *attr, char *buf)310{311	struct coresight_device *csdev = to_coresight_device(dev);312 313	return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->sysfs_sink_activated);314}315 316static ssize_t enable_sink_store(struct device *dev,317				 struct device_attribute *attr,318				 const char *buf, size_t size)319{320	int ret;321	unsigned long val;322	struct coresight_device *csdev = to_coresight_device(dev);323 324	ret = kstrtoul(buf, 10, &val);325	if (ret)326		return ret;327 328	csdev->sysfs_sink_activated = !!val;329 330	return size;331 332}333static DEVICE_ATTR_RW(enable_sink);334 335static ssize_t enable_source_show(struct device *dev,336				  struct device_attribute *attr, char *buf)337{338	struct coresight_device *csdev = to_coresight_device(dev);339 340	guard(mutex)(&coresight_mutex);341	return scnprintf(buf, PAGE_SIZE, "%u\n",342			 coresight_get_mode(csdev) == CS_MODE_SYSFS);343}344 345static ssize_t enable_source_store(struct device *dev,346				   struct device_attribute *attr,347				   const char *buf, size_t size)348{349	int ret = 0;350	unsigned long val;351	struct coresight_device *csdev = to_coresight_device(dev);352 353	ret = kstrtoul(buf, 10, &val);354	if (ret)355		return ret;356 357	if (val) {358		ret = coresight_enable_sysfs(csdev);359		if (ret)360			return ret;361	} else {362		coresight_disable_sysfs(csdev);363	}364 365	return size;366}367static DEVICE_ATTR_RW(enable_source);368 369static struct attribute *coresight_sink_attrs[] = {370	&dev_attr_enable_sink.attr,371	NULL,372};373ATTRIBUTE_GROUPS(coresight_sink);374 375static struct attribute *coresight_source_attrs[] = {376	&dev_attr_enable_source.attr,377	NULL,378};379ATTRIBUTE_GROUPS(coresight_source);380 381const struct device_type coresight_dev_type[] = {382	[CORESIGHT_DEV_TYPE_SINK] = {383		.name = "sink",384		.groups = coresight_sink_groups,385	},386	[CORESIGHT_DEV_TYPE_LINK] = {387		.name = "link",388	},389	[CORESIGHT_DEV_TYPE_LINKSINK] = {390		.name = "linksink",391		.groups = coresight_sink_groups,392	},393	[CORESIGHT_DEV_TYPE_SOURCE] = {394		.name = "source",395		.groups = coresight_source_groups,396	},397	[CORESIGHT_DEV_TYPE_HELPER] = {398		.name = "helper",399	}400};401/* Ensure the enum matches the names and groups */402static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX);403 404/*405 * Connections group - links attribute.406 * Count of created links between coresight components in the group.407 */408static ssize_t nr_links_show(struct device *dev,409			     struct device_attribute *attr,410			     char *buf)411{412	struct coresight_device *csdev = to_coresight_device(dev);413 414	return sprintf(buf, "%d\n", csdev->nr_links);415}416static DEVICE_ATTR_RO(nr_links);417 418static struct attribute *coresight_conns_attrs[] = {419	&dev_attr_nr_links.attr,420	NULL,421};422 423static struct attribute_group coresight_conns_group = {424	.attrs = coresight_conns_attrs,425	.name = "connections",426};427 428/*429 * Create connections group for CoreSight devices.430 * This group will then be used to collate the sysfs links between431 * devices.432 */433int coresight_create_conns_sysfs_group(struct coresight_device *csdev)434{435	int ret = 0;436 437	if (!csdev)438		return -EINVAL;439 440	ret = sysfs_create_group(&csdev->dev.kobj, &coresight_conns_group);441	if (ret)442		return ret;443 444	csdev->has_conns_grp = true;445	return ret;446}447 448void coresight_remove_conns_sysfs_group(struct coresight_device *csdev)449{450	if (!csdev)451		return;452 453	if (csdev->has_conns_grp) {454		sysfs_remove_group(&csdev->dev.kobj, &coresight_conns_group);455		csdev->has_conns_grp = false;456	}457}458 459int coresight_add_sysfs_link(struct coresight_sysfs_link *info)460{461	int ret = 0;462 463	if (!info)464		return -EINVAL;465	if (!info->orig || !info->target ||466	    !info->orig_name || !info->target_name)467		return -EINVAL;468	if (!info->orig->has_conns_grp || !info->target->has_conns_grp)469		return -EINVAL;470 471	/* first link orig->target */472	ret = sysfs_add_link_to_group(&info->orig->dev.kobj,473				      coresight_conns_group.name,474				      &info->target->dev.kobj,475				      info->orig_name);476	if (ret)477		return ret;478 479	/* second link target->orig */480	ret = sysfs_add_link_to_group(&info->target->dev.kobj,481				      coresight_conns_group.name,482				      &info->orig->dev.kobj,483				      info->target_name);484 485	/* error in second link - remove first - otherwise inc counts */486	if (ret) {487		sysfs_remove_link_from_group(&info->orig->dev.kobj,488					     coresight_conns_group.name,489					     info->orig_name);490	} else {491		info->orig->nr_links++;492		info->target->nr_links++;493	}494 495	return ret;496}497EXPORT_SYMBOL_GPL(coresight_add_sysfs_link);498 499void coresight_remove_sysfs_link(struct coresight_sysfs_link *info)500{501	if (!info)502		return;503	if (!info->orig || !info->target ||504	    !info->orig_name || !info->target_name)505		return;506 507	sysfs_remove_link_from_group(&info->orig->dev.kobj,508				     coresight_conns_group.name,509				     info->orig_name);510 511	sysfs_remove_link_from_group(&info->target->dev.kobj,512				     coresight_conns_group.name,513				     info->target_name);514 515	info->orig->nr_links--;516	info->target->nr_links--;517}518EXPORT_SYMBOL_GPL(coresight_remove_sysfs_link);519 520/*521 * coresight_make_links: Make a link for a connection from a @orig522 * device to @target, represented by @conn.523 *524 *   e.g, for devOrig[output_X] -> devTarget[input_Y] is represented525 *   as two symbolic links :526 *527 *	/sys/.../devOrig/out:X	-> /sys/.../devTarget/528 *	/sys/.../devTarget/in:Y	-> /sys/.../devOrig/529 *530 * The link names are allocated for a device where it appears. i.e, the531 * "out" link on the master and "in" link on the slave device.532 * The link info is stored in the connection record for avoiding533 * the reconstruction of names for removal.534 */535int coresight_make_links(struct coresight_device *orig,536			 struct coresight_connection *conn,537			 struct coresight_device *target)538{539	int ret = -ENOMEM;540	char *outs = NULL, *ins = NULL;541	struct coresight_sysfs_link *link = NULL;542 543	/* Helper devices aren't shown in sysfs */544	if (conn->dest_port == -1 && conn->src_port == -1)545		return 0;546 547	do {548		outs = devm_kasprintf(&orig->dev, GFP_KERNEL,549				      "out:%d", conn->src_port);550		if (!outs)551			break;552		ins = devm_kasprintf(&target->dev, GFP_KERNEL,553				     "in:%d", conn->dest_port);554		if (!ins)555			break;556		link = devm_kzalloc(&orig->dev,557				    sizeof(struct coresight_sysfs_link),558				    GFP_KERNEL);559		if (!link)560			break;561 562		link->orig = orig;563		link->target = target;564		link->orig_name = outs;565		link->target_name = ins;566 567		ret = coresight_add_sysfs_link(link);568		if (ret)569			break;570 571		conn->link = link;572		return 0;573	} while (0);574 575	return ret;576}577 578/*579 * coresight_remove_links: Remove the sysfs links for a given connection @conn,580 * from @orig device to @target device. See coresight_make_links() for more581 * details.582 */583void coresight_remove_links(struct coresight_device *orig,584			    struct coresight_connection *conn)585{586	if (!orig || !conn->link)587		return;588 589	coresight_remove_sysfs_link(conn->link);590 591	devm_kfree(&conn->dest_dev->dev, conn->link->target_name);592	devm_kfree(&orig->dev, conn->link->orig_name);593	devm_kfree(&orig->dev, conn->link);594	conn->link = NULL;595}596