Merge tag '6.9-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6
[linux-2.6-microblaze.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 <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 }