From: Yichong Chen <chenyichong@uniontech.com>
To: gregkh@linuxfoundation.org
Cc: dakr@kernel.org, djakov@kernel.org, driver-core@lists.linux.dev,
linux-kernel@vger.kernel.org, quic_mdtipton@quicinc.com,
rafael@kernel.org
Subject: Re: [PATCH] debugfs: serialize debugfs_create_str() writers
Date: Tue, 4 Aug 2026 13:50:56 +0800 [thread overview]
Message-ID: <20260804055056.877823-1-chenyichong@uniontech.com> (raw)
In-Reply-To: <2026080349-gristle-underdone-415d@gregkh>
Hi Greg,
I tried the direction you suggested and converted the SoundWire
firmware_file debugfs entry away from debugfs_create_str().
The draft diff below does two things:
1. debugfs_create_str() becomes read-only only. It drops write
permission bits from the requested mode. If the caller passed only
write bits, it creates an owner-readable file instead of a 0000 file.
2. drivers/soundwire/debugfs.c uses debugfs_create_file() with a small
local write-only file operation for firmware_file.
The SoundWire command path copies firmware_file under a mutex before using
it for request_firmware(), so a later debugfs write can replace the global
string without invalidating the name being used by the command.
I tested the generic debugfs_create_str() mode handling with a small test
module:
0444 -> 0444, readable, write fails
0600 -> 0400, readable, write fails
0200 -> 0400, readable, write fails
I do not have SoundWire hardware in my test VM, so I could only build that
part.
Does this match the direction you had in mind? If so, I can finish the
conversion for the interconnect writable string users as well and send a
proper v2.
Thanks,
Yichong
diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c
index 099eb84a548e..9aa881689fb9 100644
--- a/drivers/soundwire/debugfs.c
+++ b/drivers/soundwire/debugfs.c
@@ -5,6 +5,7 @@
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/firmware.h>
+#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/soundwire/sdw.h>
@@ -145,6 +146,7 @@ static u32 start_addr;
static size_t num_bytes;
static u8 read_buffer[MAX_CMD_BYTES];
static char *firmware_file;
+static DEFINE_MUTEX(firmware_file_lock);
static int set_command(void *data, u64 value)
{
@@ -246,6 +248,7 @@ static int cmd_go(void *data, u64 value)
{
const struct firmware *fw = NULL;
struct sdw_slave *slave = data;
+ char *fw_name __free(kfree) = NULL;
ktime_t start_t;
ktime_t finish_t;
int ret;
@@ -265,15 +268,23 @@ static int cmd_go(void *data, u64 value)
}
if (cmd == 0) {
- ret = request_firmware(&fw, firmware_file, &slave->dev);
+ mutex_lock(&firmware_file_lock);
+ fw_name = kstrdup(firmware_file, GFP_KERNEL);
+ mutex_unlock(&firmware_file_lock);
+ if (!fw_name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = request_firmware(&fw, fw_name, &slave->dev);
if (ret < 0) {
- dev_err(&slave->dev, "firmware %s not found\n", firmware_file);
+ dev_err(&slave->dev, "firmware %s not found\n", fw_name);
goto out;
}
if (fw->size < num_bytes) {
dev_err(&slave->dev,
"firmware %s: firmware size %zd, desired %zd\n",
- firmware_file, fw->size, num_bytes);
+ fw_name, fw->size, num_bytes);
goto out;
}
}
@@ -315,6 +326,37 @@ static int cmd_go(void *data, u64 value)
DEFINE_DEBUGFS_ATTRIBUTE(cmd_go_fops, NULL,
cmd_go, "%llu\n");
+static ssize_t firmware_file_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char *new, *old;
+
+ if (*ppos)
+ return -EINVAL;
+ if (count > PAGE_SIZE - 1)
+ return -E2BIG;
+
+ new = memdup_user_nul(user_buf, count);
+ if (IS_ERR(new))
+ return PTR_ERR(new);
+ strim(new);
+
+ mutex_lock(&firmware_file_lock);
+ old = firmware_file;
+ firmware_file = new;
+ mutex_unlock(&firmware_file_lock);
+
+ kfree(old);
+ return count;
+}
+
+static const struct file_operations firmware_file_fops = {
+ .open = simple_open,
+ .write = firmware_file_write,
+ .llseek = default_llseek,
+};
+
#define MAX_LINE_LEN 128
static int read_buffer_show(struct seq_file *s_file, void *data)
@@ -358,7 +400,8 @@ void sdw_slave_debugfs_init(struct sdw_slave *slave)
debugfs_create_file("read_buffer", 0400, d, slave, &read_buffer_fops);
if (firmware_file)
- debugfs_create_str("firmware_file", 0200, d, &firmware_file);
+ debugfs_create_file("firmware_file", 0200, d, NULL,
+ &firmware_file_fops);
slave->debugfs = d;
}
@@ -379,6 +422,8 @@ void sdw_debugfs_init(void)
void sdw_debugfs_exit(void)
{
debugfs_remove_recursive(sdw_debugfs_root);
+ mutex_lock(&firmware_file_lock);
kfree(firmware_file);
firmware_file = NULL;
+ mutex_unlock(&firmware_file_lock);
}
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 08de6652a4f3..4ce768539b4f 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -1049,89 +1049,26 @@ ssize_t debugfs_read_file_str(struct file *file, char __user *user_buf,
return ret;
}
-static ssize_t debugfs_write_file_str(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- 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 __rcu **)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 = {
- .read = debugfs_read_file_str,
- .write = debugfs_write_file_str,
- .open = simple_open,
- .llseek = default_llseek,
-};
-
static const struct file_operations fops_str_ro = {
.read = debugfs_read_file_str,
.open = simple_open,
.llseek = default_llseek,
};
-static const struct file_operations fops_str_wo = {
- .write = debugfs_write_file_str,
- .open = simple_open,
- .llseek = default_llseek,
-};
-
/**
- * debugfs_create_str - create a debugfs file that is used to read and write a string value
+ * debugfs_create_str - create a debugfs file that is used to read a string value
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem.
- * @value: a pointer to the variable that the file should read to and write
- * from. This pointer and the string it points to must not be %NULL.
+ * @value: a pointer to the variable that the file should read from. This
+ * pointer and the string it points to must not be %NULL.
*
* This function creates a file in debugfs with the given name that
- * contains the value of the variable @value. If the @mode variable is so
- * set, it can be read from, and written to.
+ * contains the value of the variable @value. Write permission bits in
+ * @mode are ignored. If this leaves no read permission bits, the file is
+ * created owner-readable.
*/
void debugfs_create_str(const char *name, umode_t mode,
struct dentry *parent, char **value)
@@ -1139,8 +1076,11 @@ void debugfs_create_str(const char *name, umode_t mode,
if (WARN_ON(!value || !*value))
return;
- debugfs_create_mode_unsafe(name, mode, parent, value, &fops_str,
- &fops_str_ro, &fops_str_wo);
+ mode &= ~S_IWUGO;
+ if (!(mode & S_IRUGO))
+ mode |= S_IRUSR;
+
+ debugfs_create_file_unsafe(name, mode, parent, value, &fops_str_ro);
}
EXPORT_SYMBOL_GPL(debugfs_create_str);
next prev parent reply other threads:[~2026-08-04 5:51 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-03 6:09 [PATCH] debugfs: serialize debugfs_create_str() writers Yichong Chen
2026-08-03 6:19 ` Greg KH
2026-08-03 8:34 ` Yichong Chen
2026-08-03 8:43 ` Greg KH
2026-08-04 5:50 ` Yichong Chen [this message]
2026-08-05 8:00 ` Greg KH
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260804055056.877823-1-chenyichong@uniontech.com \
--to=chenyichong@uniontech.com \
--cc=dakr@kernel.org \
--cc=djakov@kernel.org \
--cc=driver-core@lists.linux.dev \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=quic_mdtipton@quicinc.com \
--cc=rafael@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox