Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'icc-debugfs' into icc-next

This series introduces interconnect debugfs files that support voting
for any interconnect path the framework supports. It is useful for debug,
test and verification.

* icc-debugfs
debugfs: Add write support to debugfs_create_str()
interconnect: Reintroduce icc_get()
interconnect: Add debugfs test client

Link: https://lore.kernel.org/r/20230807142914.12480-1-quic_mdtipton@quicinc.com
Signed-off-by: Georgi Djakov <djakov@kernel.org>

+309 -3
+25
Documentation/driver-api/interconnect.rst
··· 113 113 114 114 $ cat /sys/kernel/debug/interconnect/interconnect_graph | \ 115 115 dot -Tsvg > interconnect_graph.svg 116 + 117 + The ``test-client`` directory provides interfaces for issuing BW requests to 118 + any arbitrary path. Note that for safety reasons, this feature is disabled by 119 + default without a Kconfig to enable it. Enabling it requires code changes to 120 + ``#define INTERCONNECT_ALLOW_WRITE_DEBUGFS``. Example usage:: 121 + 122 + cd /sys/kernel/debug/interconnect/test-client/ 123 + 124 + # Configure node endpoints for the path from CPU to DDR on 125 + # qcom/sm8550. 126 + echo chm_apps > src_node 127 + echo ebi > dst_node 128 + 129 + # Get path between src_node and dst_node. This is only 130 + # necessary after updating the node endpoints. 131 + echo 1 > get 132 + 133 + # Set desired BW to 1GBps avg and 2GBps peak. 134 + echo 1000000 > avg_bw 135 + echo 2000000 > peak_bw 136 + 137 + # Vote for avg_bw and peak_bw on the latest path from "get". 138 + # Voting for multiple paths is possible by repeating this 139 + # process for different nodes endpoints. 140 + echo 1 > commit
+1 -1
drivers/interconnect/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 3 CFLAGS_core.o := -I$(src) 4 - icc-core-objs := core.o bulk.o 4 + icc-core-objs := core.o bulk.o debugfs-client.o 5 5 6 6 obj-$(CONFIG_INTERCONNECT) += icc-core.o 7 7 obj-$(CONFIG_INTERCONNECT_IMX) += imx/
+66
drivers/interconnect/core.c
··· 148 148 return idr_find(&icc_idr, id); 149 149 } 150 150 151 + static struct icc_node *node_find_by_name(const char *name) 152 + { 153 + struct icc_provider *provider; 154 + struct icc_node *n; 155 + 156 + list_for_each_entry(provider, &icc_providers, provider_list) { 157 + list_for_each_entry(n, &provider->nodes, node_list) { 158 + if (!strcmp(n->name, name)) 159 + return n; 160 + } 161 + } 162 + 163 + return NULL; 164 + } 165 + 151 166 static struct icc_path *path_init(struct device *dev, struct icc_node *dst, 152 167 ssize_t num_nodes) 153 168 { ··· 576 561 return of_icc_get_by_index(dev, idx); 577 562 } 578 563 EXPORT_SYMBOL_GPL(of_icc_get); 564 + 565 + /** 566 + * icc_get() - get a path handle between two endpoints 567 + * @dev: device pointer for the consumer device 568 + * @src: source node name 569 + * @dst: destination node name 570 + * 571 + * This function will search for a path between two endpoints and return an 572 + * icc_path handle on success. Use icc_put() to release constraints when they 573 + * are not needed anymore. 574 + * 575 + * Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned 576 + * when the API is disabled. 577 + */ 578 + struct icc_path *icc_get(struct device *dev, const char *src, const char *dst) 579 + { 580 + struct icc_node *src_node, *dst_node; 581 + struct icc_path *path = ERR_PTR(-EPROBE_DEFER); 582 + 583 + mutex_lock(&icc_lock); 584 + 585 + src_node = node_find_by_name(src); 586 + if (!src_node) { 587 + dev_err(dev, "%s: invalid src=%s\n", __func__, src); 588 + goto out; 589 + } 590 + 591 + dst_node = node_find_by_name(dst); 592 + if (!dst_node) { 593 + dev_err(dev, "%s: invalid dst=%s\n", __func__, dst); 594 + goto out; 595 + } 596 + 597 + path = path_find(dev, src_node, dst_node); 598 + if (IS_ERR(path)) { 599 + dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path)); 600 + goto out; 601 + } 602 + 603 + path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name); 604 + if (!path->name) { 605 + kfree(path); 606 + path = ERR_PTR(-ENOMEM); 607 + } 608 + out: 609 + mutex_unlock(&icc_lock); 610 + return path; 611 + } 579 612 580 613 /** 581 614 * icc_set_tag() - set an optional tag on a path ··· 1128 1065 icc_debugfs_dir, NULL, &icc_summary_fops); 1129 1066 debugfs_create_file("interconnect_graph", 0444, 1130 1067 icc_debugfs_dir, NULL, &icc_graph_fops); 1068 + 1069 + icc_debugfs_client_init(icc_debugfs_dir); 1070 + 1131 1071 return 0; 1132 1072 } 1133 1073
+168
drivers/interconnect/debugfs-client.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. 4 + */ 5 + #include <linux/debugfs.h> 6 + #include <linux/interconnect.h> 7 + #include <linux/platform_device.h> 8 + 9 + #include "internal.h" 10 + 11 + /* 12 + * This can be dangerous, therefore don't provide any real compile time 13 + * configuration option for this feature. 14 + * People who want to use this will need to modify the source code directly. 15 + */ 16 + #undef INTERCONNECT_ALLOW_WRITE_DEBUGFS 17 + 18 + #if defined(INTERCONNECT_ALLOW_WRITE_DEBUGFS) && defined(CONFIG_DEBUG_FS) 19 + 20 + static LIST_HEAD(debugfs_paths); 21 + static DEFINE_MUTEX(debugfs_lock); 22 + 23 + static struct platform_device *pdev; 24 + static struct icc_path *cur_path; 25 + 26 + static char *src_node; 27 + static char *dst_node; 28 + static u32 avg_bw; 29 + static u32 peak_bw; 30 + static u32 tag; 31 + 32 + struct debugfs_path { 33 + const char *src; 34 + const char *dst; 35 + struct icc_path *path; 36 + struct list_head list; 37 + }; 38 + 39 + static struct icc_path *get_path(const char *src, const char *dst) 40 + { 41 + struct debugfs_path *path; 42 + 43 + list_for_each_entry(path, &debugfs_paths, list) { 44 + if (!strcmp(path->src, src) && !strcmp(path->dst, dst)) 45 + return path->path; 46 + } 47 + 48 + return NULL; 49 + } 50 + 51 + static int icc_get_set(void *data, u64 val) 52 + { 53 + struct debugfs_path *debugfs_path; 54 + char *src, *dst; 55 + int ret = 0; 56 + 57 + mutex_lock(&debugfs_lock); 58 + 59 + rcu_read_lock(); 60 + src = rcu_dereference(src_node); 61 + dst = rcu_dereference(dst_node); 62 + 63 + /* 64 + * If we've already looked up a path, then use the existing one instead 65 + * of calling icc_get() again. This allows for updating previous BW 66 + * votes when "get" is written to multiple times for multiple paths. 67 + */ 68 + cur_path = get_path(src, dst); 69 + if (cur_path) { 70 + rcu_read_unlock(); 71 + goto out; 72 + } 73 + 74 + src = kstrdup(src, GFP_ATOMIC); 75 + dst = kstrdup(dst, GFP_ATOMIC); 76 + rcu_read_unlock(); 77 + 78 + if (!src || !dst) { 79 + ret = -ENOMEM; 80 + goto err_free; 81 + } 82 + 83 + cur_path = icc_get(&pdev->dev, src, dst); 84 + if (IS_ERR(cur_path)) { 85 + ret = PTR_ERR(cur_path); 86 + goto err_free; 87 + } 88 + 89 + debugfs_path = kzalloc(sizeof(*debugfs_path), GFP_KERNEL); 90 + if (!debugfs_path) { 91 + ret = -ENOMEM; 92 + goto err_put; 93 + } 94 + 95 + debugfs_path->path = cur_path; 96 + debugfs_path->src = src; 97 + debugfs_path->dst = dst; 98 + list_add_tail(&debugfs_path->list, &debugfs_paths); 99 + 100 + goto out; 101 + 102 + err_put: 103 + icc_put(cur_path); 104 + err_free: 105 + kfree(src); 106 + kfree(dst); 107 + out: 108 + mutex_unlock(&debugfs_lock); 109 + return ret; 110 + } 111 + 112 + DEFINE_DEBUGFS_ATTRIBUTE(icc_get_fops, NULL, icc_get_set, "%llu\n"); 113 + 114 + static int icc_commit_set(void *data, u64 val) 115 + { 116 + int ret; 117 + 118 + mutex_lock(&debugfs_lock); 119 + 120 + if (IS_ERR_OR_NULL(cur_path)) { 121 + ret = PTR_ERR(cur_path); 122 + goto out; 123 + } 124 + 125 + icc_set_tag(cur_path, tag); 126 + ret = icc_set_bw(cur_path, avg_bw, peak_bw); 127 + out: 128 + mutex_unlock(&debugfs_lock); 129 + return ret; 130 + } 131 + 132 + DEFINE_DEBUGFS_ATTRIBUTE(icc_commit_fops, NULL, icc_commit_set, "%llu\n"); 133 + 134 + int icc_debugfs_client_init(struct dentry *icc_dir) 135 + { 136 + struct dentry *client_dir; 137 + int ret; 138 + 139 + pdev = platform_device_alloc("icc-debugfs-client", PLATFORM_DEVID_NONE); 140 + 141 + ret = platform_device_add(pdev); 142 + if (ret) { 143 + pr_err("%s: failed to add platform device: %d\n", __func__, ret); 144 + platform_device_put(pdev); 145 + return ret; 146 + } 147 + 148 + client_dir = debugfs_create_dir("test_client", icc_dir); 149 + 150 + debugfs_create_str("src_node", 0600, client_dir, &src_node); 151 + debugfs_create_str("dst_node", 0600, client_dir, &dst_node); 152 + debugfs_create_file("get", 0200, client_dir, NULL, &icc_get_fops); 153 + debugfs_create_u32("avg_bw", 0600, client_dir, &avg_bw); 154 + debugfs_create_u32("peak_bw", 0600, client_dir, &peak_bw); 155 + debugfs_create_u32("tag", 0600, client_dir, &tag); 156 + debugfs_create_file("commit", 0200, client_dir, NULL, &icc_commit_fops); 157 + 158 + return 0; 159 + } 160 + 161 + #else 162 + 163 + int icc_debugfs_client_init(struct dentry *icc_dir) 164 + { 165 + return 0; 166 + } 167 + 168 + #endif
+3
drivers/interconnect/internal.h
··· 41 41 struct icc_req reqs[] __counted_by(num_nodes); 42 42 }; 43 43 44 + struct icc_path *icc_get(struct device *dev, const char *src, const char *dst); 45 + int icc_debugfs_client_init(struct dentry *icc_dir); 46 + 44 47 #endif
+46 -2
fs/debugfs/file.c
··· 904 904 static ssize_t debugfs_write_file_str(struct file *file, const char __user *user_buf, 905 905 size_t count, loff_t *ppos) 906 906 { 907 - /* This is really only for read-only strings */ 908 - return -EINVAL; 907 + struct dentry *dentry = F_DENTRY(file); 908 + char *old, *new = NULL; 909 + int pos = *ppos; 910 + int r; 911 + 912 + r = debugfs_file_get(dentry); 913 + if (unlikely(r)) 914 + return r; 915 + 916 + old = *(char **)file->private_data; 917 + 918 + /* only allow strict concatenation */ 919 + r = -EINVAL; 920 + if (pos && pos != strlen(old)) 921 + goto error; 922 + 923 + r = -E2BIG; 924 + if (pos + count + 1 > PAGE_SIZE) 925 + goto error; 926 + 927 + r = -ENOMEM; 928 + new = kmalloc(pos + count + 1, GFP_KERNEL); 929 + if (!new) 930 + goto error; 931 + 932 + if (pos) 933 + memcpy(new, old, pos); 934 + 935 + r = -EFAULT; 936 + if (copy_from_user(new + pos, user_buf, count)) 937 + goto error; 938 + 939 + new[pos + count] = '\0'; 940 + strim(new); 941 + 942 + rcu_assign_pointer(*(char **)file->private_data, new); 943 + synchronize_rcu(); 944 + kfree(old); 945 + 946 + debugfs_file_put(dentry); 947 + return count; 948 + 949 + error: 950 + kfree(new); 951 + debugfs_file_put(dentry); 952 + return r; 909 953 } 910 954 911 955 static const struct file_operations fops_str = {