xref: /linux/drivers/hwtracing/coresight/coresight-sysfs.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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 
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 name
16  * to the pointer of path for the source. The idr is for
17  * the sources which aren't associated with CPU.
18  */
19 static DEFINE_IDR(path_idr);
20 
21 /*
22  * When operating Coresight drivers from the sysFS interface, only a single
23  * path can exist from a tracer (associated to a CPU) to a sink.
24  */
25 static DEFINE_PER_CPU(struct list_head *, tracer_path);
26 
coresight_simple_show_pair(struct device * _dev,struct device_attribute * attr,char * buf)27 ssize_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 }
39 EXPORT_SYMBOL_GPL(coresight_simple_show_pair);
40 
coresight_simple_show32(struct device * _dev,struct device_attribute * attr,char * buf)41 ssize_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 }
53 EXPORT_SYMBOL_GPL(coresight_simple_show32);
54 
coresight_enable_source_sysfs(struct coresight_device * csdev,enum cs_mode mode,void * data)55 static 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 device
62 	 * specific spinlock because the truthyness of that comparison can only
63 	 * 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 disable
79  *  the device if there are no users left.
80  *
81  *  @csdev: The coresight device to disable
82  *  @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  */
coresight_disable_source_sysfs(struct coresight_device * csdev,void * data)87 static 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 via
104  * sysfs using connection based search starting from the source reference.
105  *
106  * @csdev: Coresight source device reference
107  */
108 static struct coresight_device *
coresight_find_activated_sysfs_sink(struct coresight_device * csdev)109 coresight_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 to
136  *  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  */
coresight_validate_source_sysfs(struct coresight_device * csdev,const char * function)142 static 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 
coresight_enable_sysfs(struct coresight_device * csdev)166 int 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 the
184 	 * refcount to determine this because we don't claim the source until
185 	 * coresight_enable_source() so can still race with Perf mode which
186 	 * doesn't hold coresight_mutex.
187 	 */
188 	if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
189 		/*
190 		 * There could be multiple applications driving the software
191 		 * source. So keep the refcount for each such user when the
192 		 * 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 track
224 		 * of the paths that were created so that they can be
225 		 * undone in 'coresight_disable()'.  Since there can only
226 		 * 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 ID
237 		 * 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 
249 out:
250 	mutex_unlock(&coresight_mutex);
251 	return ret;
252 
253 err_source:
254 	coresight_disable_path(path);
255 
256 err_path:
257 	coresight_release_path(path);
258 	goto out;
259 }
260 EXPORT_SYMBOL_GPL(coresight_enable_sysfs);
261 
coresight_disable_sysfs(struct coresight_device * csdev)262 void 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 
303 out:
304 	mutex_unlock(&coresight_mutex);
305 }
306 EXPORT_SYMBOL_GPL(coresight_disable_sysfs);
307 
enable_sink_show(struct device * dev,struct device_attribute * attr,char * buf)308 static 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 
enable_sink_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)316 static 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 }
333 static DEVICE_ATTR_RW(enable_sink);
334 
enable_source_show(struct device * dev,struct device_attribute * attr,char * buf)335 static 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 
enable_source_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)345 static 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 }
367 static DEVICE_ATTR_RW(enable_source);
368 
369 static struct attribute *coresight_sink_attrs[] = {
370 	&dev_attr_enable_sink.attr,
371 	NULL,
372 };
373 ATTRIBUTE_GROUPS(coresight_sink);
374 
375 static struct attribute *coresight_source_attrs[] = {
376 	&dev_attr_enable_source.attr,
377 	NULL,
378 };
379 ATTRIBUTE_GROUPS(coresight_source);
380 
381 const 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 */
402 static_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  */
nr_links_show(struct device * dev,struct device_attribute * attr,char * buf)408 static 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 }
416 static DEVICE_ATTR_RO(nr_links);
417 
418 static struct attribute *coresight_conns_attrs[] = {
419 	&dev_attr_nr_links.attr,
420 	NULL,
421 };
422 
423 static 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 between
431  * devices.
432  */
coresight_create_conns_sysfs_group(struct coresight_device * csdev)433 int 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 
coresight_remove_conns_sysfs_group(struct coresight_device * csdev)448 void 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 
coresight_add_sysfs_link(struct coresight_sysfs_link * info)459 int 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 }
497 EXPORT_SYMBOL_GPL(coresight_add_sysfs_link);
498 
coresight_remove_sysfs_link(struct coresight_sysfs_link * info)499 void 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 }
518 EXPORT_SYMBOL_GPL(coresight_remove_sysfs_link);
519 
520 /*
521  * coresight_make_links: Make a link for a connection from a @orig
522  * device to @target, represented by @conn.
523  *
524  *   e.g, for devOrig[output_X] -> devTarget[input_Y] is represented
525  *   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, the
531  * "out" link on the master and "in" link on the slave device.
532  * The link info is stored in the connection record for avoiding
533  * the reconstruction of names for removal.
534  */
coresight_make_links(struct coresight_device * orig,struct coresight_connection * conn,struct coresight_device * target)535 int 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 more
581  * details.
582  */
coresight_remove_links(struct coresight_device * orig,struct coresight_connection * conn)583 void 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