]> Git Repo - J-linux.git/blob - drivers/hwtracing/coresight/coresight-sysfs.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / hwtracing / coresight / coresight-sysfs.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2019, Linaro Limited, All rights reserved.
4  * Author: Mike Leach <[email protected]>
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
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
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
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  */
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 *
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  */
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
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
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
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
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
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
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  */
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  */
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
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
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
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  */
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  */
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 }
This page took 0.059806 seconds and 4 git commands to generate.