* [PATCH v2 0/3] Add interconnect debugfs client
@ 2023-07-31 22:42 Mike Tipton
2023-07-31 22:42 ` [PATCH v2 1/3] debugfs: Add write support to debugfs_create_str() Mike Tipton
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Mike Tipton @ 2023-07-31 22:42 UTC (permalink / raw)
To: djakov, gregkh, rafael, corbet
Cc: linux-pm, linux-doc, linux-kernel, quic_okukatla, quic_viveka,
peterz, Mike Tipton
This series introduces interconnect debugfs files that support voting
for any path the framework supports.
We've historically relied on an out-of-tree module for this, which used
the old icc_get() that was recently removed in [0]. The old icc_get()
took integer endpoint IDs, which made identifying paths in our old
implementation non-intuitive. The logical node names typically don't
change much chip-to-chip, but the raw integer IDs do. Take this
opportunity to introduce an icc_get() that uses string names instead,
which allows for a more intuitive and generic debugfs interface.
We rely on this support for debug, test, and verification. Hopefully
it'll be useful for other vendors as well.
[0] commit 7dcdad6f32c9 ("interconnect: drop unused icc_get() interface")
Changes in v2:
- Make icc_get() an internal interface.
- RCU-protect src_node and dst_node.
- Replace PLATFORM_DEVID_AUTO with PLATFORM_DEVID_NONE.
- Remove unnecessary #include.
- Add debugfs client documentation.
Mike Tipton (3):
debugfs: Add write support to debugfs_create_str()
interconnect: Reintroduce icc_get()
interconnect: Add debugfs test client
Documentation/driver-api/interconnect.rst | 25 ++++
drivers/interconnect/Makefile | 2 +-
drivers/interconnect/core.c | 66 +++++++++
drivers/interconnect/debugfs-client.c | 168 ++++++++++++++++++++++
drivers/interconnect/internal.h | 3 +
fs/debugfs/file.c | 48 ++++++-
6 files changed, 309 insertions(+), 3 deletions(-)
create mode 100644 drivers/interconnect/debugfs-client.c
--
2.17.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/3] debugfs: Add write support to debugfs_create_str()
2023-07-31 22:42 [PATCH v2 0/3] Add interconnect debugfs client Mike Tipton
@ 2023-07-31 22:42 ` Mike Tipton
2023-07-31 22:42 ` [PATCH v2 2/3] interconnect: Reintroduce icc_get() Mike Tipton
2023-07-31 22:42 ` [PATCH v2 3/3] interconnect: Add debugfs test client Mike Tipton
2 siblings, 0 replies; 5+ messages in thread
From: Mike Tipton @ 2023-07-31 22:42 UTC (permalink / raw)
To: djakov, gregkh, rafael, corbet
Cc: linux-pm, linux-doc, linux-kernel, quic_okukatla, quic_viveka,
peterz, Mike Tipton
Currently, debugfs_create_str() only supports reading strings from
debugfs. Add support for writing them as well.
Based on original implementation by Peter Zijlstra [0]. Write support
was present in the initial patch version, but dropped in v2 due to lack
of users. We have a user now, so reintroduce it.
[0] https://lore.kernel.org/all/YF3Hv5zXb%2F6lauzs@hirez.programming.kicks-ass.net/
Signed-off-by: Mike Tipton <quic_mdtipton@quicinc.com>
---
fs/debugfs/file.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 46 insertions(+), 2 deletions(-)
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index b7711888dd17..87b3753aa4b1 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -904,8 +904,52 @@ EXPORT_SYMBOL_GPL(debugfs_create_str);
static ssize_t debugfs_write_file_str(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
- /* This is really only for read-only strings */
- return -EINVAL;
+ struct dentry *dentry = F_DENTRY(file);
+ char *old, *new = NULL;
+ int pos = *ppos;
+ int r;
+
+ r = debugfs_file_get(dentry);
+ if (unlikely(r))
+ return r;
+
+ old = *(char **)file->private_data;
+
+ /* only allow strict concatenation */
+ r = -EINVAL;
+ if (pos && pos != strlen(old))
+ goto error;
+
+ r = -E2BIG;
+ if (pos + count + 1 > PAGE_SIZE)
+ goto error;
+
+ r = -ENOMEM;
+ new = kmalloc(pos + count + 1, GFP_KERNEL);
+ if (!new)
+ goto error;
+
+ if (pos)
+ memcpy(new, old, pos);
+
+ r = -EFAULT;
+ if (copy_from_user(new + pos, user_buf, count))
+ goto error;
+
+ new[pos + count] = '\0';
+ strim(new);
+
+ rcu_assign_pointer(*(char **)file->private_data, new);
+ synchronize_rcu();
+ kfree(old);
+
+ debugfs_file_put(dentry);
+ return count;
+
+error:
+ kfree(new);
+ debugfs_file_put(dentry);
+ return r;
}
static const struct file_operations fops_str = {
--
2.17.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/3] interconnect: Reintroduce icc_get()
2023-07-31 22:42 [PATCH v2 0/3] Add interconnect debugfs client Mike Tipton
2023-07-31 22:42 ` [PATCH v2 1/3] debugfs: Add write support to debugfs_create_str() Mike Tipton
@ 2023-07-31 22:42 ` Mike Tipton
2023-07-31 22:42 ` [PATCH v2 3/3] interconnect: Add debugfs test client Mike Tipton
2 siblings, 0 replies; 5+ messages in thread
From: Mike Tipton @ 2023-07-31 22:42 UTC (permalink / raw)
To: djakov, gregkh, rafael, corbet
Cc: linux-pm, linux-doc, linux-kernel, quic_okukatla, quic_viveka,
peterz, Mike Tipton
The original icc_get() that took integer node IDs was removed due to
lack of users. Reintroduce a new version that takes string node names,
which is needed for the debugfs client.
Signed-off-by: Mike Tipton <quic_mdtipton@quicinc.com>
---
drivers/interconnect/core.c | 63 +++++++++++++++++++++++++++++++++
drivers/interconnect/internal.h | 2 ++
2 files changed, 65 insertions(+)
diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index 5fac448c28fd..fc1461dfc61b 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -147,6 +147,21 @@ static struct icc_node *node_find(const int id)
return idr_find(&icc_idr, id);
}
+static struct icc_node *node_find_by_name(const char *name)
+{
+ struct icc_provider *provider;
+ struct icc_node *n;
+
+ list_for_each_entry(provider, &icc_providers, provider_list) {
+ list_for_each_entry(n, &provider->nodes, node_list) {
+ if (!strcmp(n->name, name))
+ return n;
+ }
+ }
+
+ return NULL;
+}
+
static struct icc_path *path_init(struct device *dev, struct icc_node *dst,
ssize_t num_nodes)
{
@@ -561,6 +576,54 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
}
EXPORT_SYMBOL_GPL(of_icc_get);
+/**
+ * icc_get() - get a path handle between two endpoints
+ * @dev: device pointer for the consumer device
+ * @src: source node name
+ * @dst: destination node name
+ *
+ * This function will search for a path between two endpoints and return an
+ * icc_path handle on success. Use icc_put() to release constraints when they
+ * are not needed anymore.
+ *
+ * Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned
+ * when the API is disabled.
+ */
+struct icc_path *icc_get(struct device *dev, const char *src, const char *dst)
+{
+ struct icc_node *src_node, *dst_node;
+ struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
+
+ mutex_lock(&icc_lock);
+
+ src_node = node_find_by_name(src);
+ if (!src_node) {
+ dev_err(dev, "%s: invalid src=%s\n", __func__, src);
+ goto out;
+ }
+
+ dst_node = node_find_by_name(dst);
+ if (!dst_node) {
+ dev_err(dev, "%s: invalid dst=%s\n", __func__, dst);
+ goto out;
+ }
+
+ path = path_find(dev, src_node, dst_node);
+ if (IS_ERR(path)) {
+ dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
+ goto out;
+ }
+
+ path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name);
+ if (!path->name) {
+ kfree(path);
+ path = ERR_PTR(-ENOMEM);
+ }
+out:
+ mutex_unlock(&icc_lock);
+ return path;
+}
+
/**
* icc_set_tag() - set an optional tag on a path
* @path: the path we want to tag
diff --git a/drivers/interconnect/internal.h b/drivers/interconnect/internal.h
index f5f82a5c939e..95d6ba27bf78 100644
--- a/drivers/interconnect/internal.h
+++ b/drivers/interconnect/internal.h
@@ -41,4 +41,6 @@ struct icc_path {
struct icc_req reqs[];
};
+struct icc_path *icc_get(struct device *dev, const char *src, const char *dst);
+
#endif
--
2.17.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 3/3] interconnect: Add debugfs test client
2023-07-31 22:42 [PATCH v2 0/3] Add interconnect debugfs client Mike Tipton
2023-07-31 22:42 ` [PATCH v2 1/3] debugfs: Add write support to debugfs_create_str() Mike Tipton
2023-07-31 22:42 ` [PATCH v2 2/3] interconnect: Reintroduce icc_get() Mike Tipton
@ 2023-07-31 22:42 ` Mike Tipton
2023-08-01 4:29 ` Pavan Kondeti
2 siblings, 1 reply; 5+ messages in thread
From: Mike Tipton @ 2023-07-31 22:42 UTC (permalink / raw)
To: djakov, gregkh, rafael, corbet
Cc: linux-pm, linux-doc, linux-kernel, quic_okukatla, quic_viveka,
peterz, Mike Tipton
It's often useful during test, debug, and development to issue path
votes from shell. Add a debugfs client for this purpose.
Example usage:
cd /sys/kernel/debug/interconnect/test-client/
# Configure node endpoints for the path from CPU to DDR on
# qcom/sm8550.
echo chm_apps > src_node
echo ebi > dst_node
# Get path between src_node and dst_node. This is only
# necessary after updating the node endpoints.
echo 1 > get
# Set desired BW to 1GBps avg and 2GBps peak.
echo 1000000 > avg_bw
echo 2000000 > peak_bw
# Vote for avg_bw and peak_bw on the latest path from "get".
# Voting for multiple paths is possible by repeating this
# process for different nodes endpoints.
echo 1 > commit
Allowing userspace to directly enable and set bus rates can be dangerous
So, following in the footsteps of the regmap [0] and clk [1] frameworks,
keep these userspace controls compile-time disabled without Kconfig
options to enable them. Enabling this will require code changes to
define INTERCONNECT_ALLOW_WRITE_DEBUGFS.
[0] commit 09c6ecd39410 ("regmap: Add support for writing to regmap registers via debugfs")
[1] commit 37215da5553e ("clk: Add support for setting clk_rate via debugfs")
Signed-off-by: Mike Tipton <quic_mdtipton@quicinc.com>
---
Documentation/driver-api/interconnect.rst | 25 ++++
drivers/interconnect/Makefile | 2 +-
drivers/interconnect/core.c | 3 +
drivers/interconnect/debugfs-client.c | 168 ++++++++++++++++++++++
drivers/interconnect/internal.h | 1 +
5 files changed, 198 insertions(+), 1 deletion(-)
create mode 100644 drivers/interconnect/debugfs-client.c
diff --git a/Documentation/driver-api/interconnect.rst b/Documentation/driver-api/interconnect.rst
index 5ed4f57a6bac..a92d0f277a1f 100644
--- a/Documentation/driver-api/interconnect.rst
+++ b/Documentation/driver-api/interconnect.rst
@@ -113,3 +113,28 @@ through dot to generate diagrams in many graphical formats::
$ cat /sys/kernel/debug/interconnect/interconnect_graph | \
dot -Tsvg > interconnect_graph.svg
+
+The ``test-client`` directory provides interfaces for issuing BW requests to
+any arbitrary path. Note that for safety reasons, this feature is disabled by
+default without a Kconfig to enable it. Enabling it requires code changes to
+``#define INTERCONNECT_ALLOW_WRITE_DEBUGFS``. Example usage::
+
+ cd /sys/kernel/debug/interconnect/test-client/
+
+ # Configure node endpoints for the path from CPU to DDR on
+ # qcom/sm8550.
+ echo chm_apps > src_node
+ echo ebi > dst_node
+
+ # Get path between src_node and dst_node. This is only
+ # necessary after updating the node endpoints.
+ echo 1 > get
+
+ # Set desired BW to 1GBps avg and 2GBps peak.
+ echo 1000000 > avg_bw
+ echo 2000000 > peak_bw
+
+ # Vote for avg_bw and peak_bw on the latest path from "get".
+ # Voting for multiple paths is possible by repeating this
+ # process for different nodes endpoints.
+ echo 1 > commit
diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
index 5604ce351a9f..d0888babb9a1 100644
--- a/drivers/interconnect/Makefile
+++ b/drivers/interconnect/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS_core.o := -I$(src)
-icc-core-objs := core.o bulk.o
+icc-core-objs := core.o bulk.o debugfs-client.o
obj-$(CONFIG_INTERCONNECT) += icc-core.o
obj-$(CONFIG_INTERCONNECT_IMX) += imx/
diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index fc1461dfc61b..9e12bb5e523d 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -1116,6 +1116,9 @@ static int __init icc_init(void)
icc_debugfs_dir, NULL, &icc_summary_fops);
debugfs_create_file("interconnect_graph", 0444,
icc_debugfs_dir, NULL, &icc_graph_fops);
+
+ icc_debugfs_client_init(icc_debugfs_dir);
+
return 0;
}
diff --git a/drivers/interconnect/debugfs-client.c b/drivers/interconnect/debugfs-client.c
new file mode 100644
index 000000000000..c41255cc44ca
--- /dev/null
+++ b/drivers/interconnect/debugfs-client.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#include <linux/debugfs.h>
+#include <linux/interconnect.h>
+#include <linux/platform_device.h>
+
+#include "internal.h"
+
+/*
+ * This can be dangerous, therefore don't provide any real compile time
+ * configuration option for this feature.
+ * People who want to use this will need to modify the source code directly.
+ */
+#undef INTERCONNECT_ALLOW_WRITE_DEBUGFS
+
+#if defined(INTERCONNECT_ALLOW_WRITE_DEBUGFS) && defined(CONFIG_DEBUG_FS)
+
+static LIST_HEAD(debugfs_paths);
+static DEFINE_MUTEX(debugfs_lock);
+
+static struct platform_device *pdev;
+static struct icc_path *cur_path;
+
+static char *src_node;
+static char *dst_node;
+static u32 avg_bw;
+static u32 peak_bw;
+static u32 tag;
+
+struct debugfs_path {
+ const char *src;
+ const char *dst;
+ struct icc_path *path;
+ struct list_head list;
+};
+
+static struct icc_path *get_path(const char *src, const char *dst)
+{
+ struct debugfs_path *path;
+
+ list_for_each_entry(path, &debugfs_paths, list) {
+ if (!strcmp(path->src, src) && !strcmp(path->dst, dst))
+ return path->path;
+ }
+
+ return NULL;
+}
+
+static int icc_get_set(void *data, u64 val)
+{
+ struct debugfs_path *debugfs_path;
+ char *src, *dst;
+ int ret = 0;
+
+ mutex_lock(&debugfs_lock);
+
+ rcu_read_lock();
+ src = rcu_dereference(src_node);
+ dst = rcu_dereference(dst_node);
+
+ /*
+ * If we've already looked up a path, then use the existing one instead
+ * of calling icc_get() again. This allows for updating previous BW
+ * votes when "get" is written to multiple times for multiple paths.
+ */
+ cur_path = get_path(src, dst);
+ if (cur_path) {
+ rcu_read_unlock();
+ goto out;
+ }
+
+ src = kstrdup(src, GFP_KERNEL);
+ dst = kstrdup(dst, GFP_KERNEL);
+ rcu_read_unlock();
+
+ if (!src || !dst) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ cur_path = icc_get(&pdev->dev, src, dst);
+ if (IS_ERR(cur_path)) {
+ ret = PTR_ERR(cur_path);
+ goto err_free;
+ }
+
+ debugfs_path = kzalloc(sizeof(*debugfs_path), GFP_KERNEL);
+ if (!debugfs_path) {
+ ret = -ENOMEM;
+ goto err_put;
+ }
+
+ debugfs_path->path = cur_path;
+ debugfs_path->src = src;
+ debugfs_path->dst = dst;
+ list_add_tail(&debugfs_path->list, &debugfs_paths);
+
+ goto out;
+
+err_put:
+ icc_put(cur_path);
+err_free:
+ kfree(src);
+ kfree(dst);
+out:
+ mutex_unlock(&debugfs_lock);
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(icc_get_fops, NULL, icc_get_set, "%llu\n");
+
+static int icc_commit_set(void *data, u64 val)
+{
+ int ret;
+
+ mutex_lock(&debugfs_lock);
+
+ if (IS_ERR_OR_NULL(cur_path)) {
+ ret = PTR_ERR(cur_path);
+ goto out;
+ }
+
+ icc_set_tag(cur_path, tag);
+ ret = icc_set_bw(cur_path, avg_bw, peak_bw);
+out:
+ mutex_unlock(&debugfs_lock);
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(icc_commit_fops, NULL, icc_commit_set, "%llu\n");
+
+int icc_debugfs_client_init(struct dentry *icc_dir)
+{
+ struct dentry *client_dir;
+ int ret;
+
+ pdev = platform_device_alloc("icc-debugfs-client", PLATFORM_DEVID_NONE);
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ pr_err("%s: failed to add platform device: %d\n", __func__, ret);
+ platform_device_put(pdev);
+ return ret;
+ }
+
+ client_dir = debugfs_create_dir("test_client", icc_dir);
+
+ debugfs_create_str("src_node", 0600, client_dir, &src_node);
+ debugfs_create_str("dst_node", 0600, client_dir, &dst_node);
+ debugfs_create_file("get", 0200, client_dir, NULL, &icc_get_fops);
+ debugfs_create_u32("avg_bw", 0600, client_dir, &avg_bw);
+ debugfs_create_u32("peak_bw", 0600, client_dir, &peak_bw);
+ debugfs_create_u32("tag", 0600, client_dir, &tag);
+ debugfs_create_file("commit", 0200, client_dir, NULL, &icc_commit_fops);
+
+ return 0;
+}
+
+#else
+
+int icc_debugfs_client_init(struct dentry *icc_dir)
+{
+ return 0;
+}
+
+#endif
diff --git a/drivers/interconnect/internal.h b/drivers/interconnect/internal.h
index 95d6ba27bf78..3b2cfd32e449 100644
--- a/drivers/interconnect/internal.h
+++ b/drivers/interconnect/internal.h
@@ -42,5 +42,6 @@ struct icc_path {
};
struct icc_path *icc_get(struct device *dev, const char *src, const char *dst);
+int icc_debugfs_client_init(struct dentry *icc_dir);
#endif
--
2.17.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 3/3] interconnect: Add debugfs test client
2023-07-31 22:42 ` [PATCH v2 3/3] interconnect: Add debugfs test client Mike Tipton
@ 2023-08-01 4:29 ` Pavan Kondeti
0 siblings, 0 replies; 5+ messages in thread
From: Pavan Kondeti @ 2023-08-01 4:29 UTC (permalink / raw)
To: Mike Tipton
Cc: djakov, gregkh, rafael, corbet, linux-pm, linux-doc,
linux-kernel, quic_okukatla, quic_viveka, peterz
On Mon, Jul 31, 2023 at 03:42:47PM -0700, Mike Tipton wrote:
> +static int icc_get_set(void *data, u64 val)
> +{
> + struct debugfs_path *debugfs_path;
> + char *src, *dst;
> + int ret = 0;
> +
> + mutex_lock(&debugfs_lock);
> +
> + rcu_read_lock();
> + src = rcu_dereference(src_node);
> + dst = rcu_dereference(dst_node);
> +
> + /*
> + * If we've already looked up a path, then use the existing one instead
> + * of calling icc_get() again. This allows for updating previous BW
> + * votes when "get" is written to multiple times for multiple paths.
> + */
> + cur_path = get_path(src, dst);
> + if (cur_path) {
> + rcu_read_unlock();
> + goto out;
> + }
> +
> + src = kstrdup(src, GFP_KERNEL);
> + dst = kstrdup(dst, GFP_KERNEL);
> + rcu_read_unlock();
> +
Blocking calls are not allowed from RCU critical section. So we would
need to replace GFP_KERNEL with GFP_ATOMIC.
> + if (!src || !dst) {
> + ret = -ENOMEM;
> + goto err_free;
> + }
> +
> + cur_path = icc_get(&pdev->dev, src, dst);
> + if (IS_ERR(cur_path)) {
> + ret = PTR_ERR(cur_path);
> + goto err_free;
> + }
> +
> + debugfs_path = kzalloc(sizeof(*debugfs_path), GFP_KERNEL);
> + if (!debugfs_path) {
> + ret = -ENOMEM;
> + goto err_put;
> + }
> +
> + debugfs_path->path = cur_path;
> + debugfs_path->src = src;
> + debugfs_path->dst = dst;
> + list_add_tail(&debugfs_path->list, &debugfs_paths);
> +
> + goto out;
> +
> +err_put:
> + icc_put(cur_path);
> +err_free:
> + kfree(src);
> + kfree(dst);
> +out:
> + mutex_unlock(&debugfs_lock);
> + return ret;
> +}
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2023-08-01 4:30 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-31 22:42 [PATCH v2 0/3] Add interconnect debugfs client Mike Tipton
2023-07-31 22:42 ` [PATCH v2 1/3] debugfs: Add write support to debugfs_create_str() Mike Tipton
2023-07-31 22:42 ` [PATCH v2 2/3] interconnect: Reintroduce icc_get() Mike Tipton
2023-07-31 22:42 ` [PATCH v2 3/3] interconnect: Add debugfs test client Mike Tipton
2023-08-01 4:29 ` Pavan Kondeti
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).