File Index
Files and directories
Sign in
FILE

coresight-sysfs.c

File preview with syntax highlighting.

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