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