saner replacement for debugfs_rename()
JIRA: https://issues.redhat.com/browse/RHEL-87378 commit f7862dfef6612b87b2ad8352c4d73886f09456d6 Author: Al Viro <viro@zeniv.linux.org.uk> Date: Sun Jan 12 08:07:05 2025 +0000 saner replacement for debugfs_rename() Existing primitive has several problems: 1) calling conventions are clumsy - it returns a dentry reference that is either identical to its second argument or is an ERR_PTR(-E...); in both cases no refcount changes happen. Inconvenient for users and bug-prone; it would be better to have it return 0 on success and -E... on failure. 2) it allows cross-directory moves; however, no such caller have ever materialized and considering the way debugfs is used, it's unlikely to happen in the future. What's more, any such caller would have fun issues to deal with wrt interplay with recursive removal. It also makes the calling conventions clumsier... 3) tautological rename fails; the callers have no race-free way to deal with that. 4) new name must have been formed by the caller; quite a few callers have it done by sprintf/kasprintf/etc., ending up with considerable boilerplate. Proposed replacement: int debugfs_change_name(dentry, fmt, ...). All callers convert to that easily, and it's simpler internally. IMO debugfs_rename() should go; if we ever get a real-world use case for cross-directory moves in debugfs, we can always look into the right way to handle that. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Link: https://lore.kernel.org/r/20250112080705.141166-21-viro@zeniv.linux.org.uk Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
This commit is contained in:
parent
e095420ca1
commit
615def7e82
|
@ -211,18 +211,16 @@ seq_file content.
|
||||||
|
|
||||||
There are a couple of other directory-oriented helper functions::
|
There are a couple of other directory-oriented helper functions::
|
||||||
|
|
||||||
struct dentry *debugfs_rename(struct dentry *old_dir,
|
struct dentry *debugfs_change_name(struct dentry *dentry,
|
||||||
struct dentry *old_dentry,
|
const char *fmt, ...);
|
||||||
struct dentry *new_dir,
|
|
||||||
const char *new_name);
|
|
||||||
|
|
||||||
struct dentry *debugfs_create_symlink(const char *name,
|
struct dentry *debugfs_create_symlink(const char *name,
|
||||||
struct dentry *parent,
|
struct dentry *parent,
|
||||||
const char *target);
|
const char *target);
|
||||||
|
|
||||||
A call to debugfs_rename() will give a new name to an existing debugfs
|
A call to debugfs_change_name() will give a new name to an existing debugfs
|
||||||
file, possibly in a different directory. The new_name must not exist prior
|
file, always in the same directory. The new_name must not exist prior
|
||||||
to the call; the return value is old_dentry with updated information.
|
to the call; the return value is 0 on success and -E... on failuer.
|
||||||
Symbolic links can be created with debugfs_create_symlink().
|
Symbolic links can be created with debugfs_create_symlink().
|
||||||
|
|
||||||
There is one important thing that all debugfs users must take into account:
|
There is one important thing that all debugfs users must take into account:
|
||||||
|
|
|
@ -63,13 +63,8 @@ void bond_debug_unregister(struct bonding *bond)
|
||||||
|
|
||||||
void bond_debug_reregister(struct bonding *bond)
|
void bond_debug_reregister(struct bonding *bond)
|
||||||
{
|
{
|
||||||
struct dentry *d;
|
int err = debugfs_change_name(bond->debug_dir, "%s", bond->dev->name);
|
||||||
|
if (err) {
|
||||||
d = debugfs_rename(bonding_debug_root, bond->debug_dir,
|
|
||||||
bonding_debug_root, bond->dev->name);
|
|
||||||
if (!IS_ERR(d)) {
|
|
||||||
bond->debug_dir = d;
|
|
||||||
} else {
|
|
||||||
netdev_warn(bond->dev, "failed to reregister, so just unregister old one\n");
|
netdev_warn(bond->dev, "failed to reregister, so just unregister old one\n");
|
||||||
bond_debug_unregister(bond);
|
bond_debug_unregister(bond);
|
||||||
}
|
}
|
||||||
|
|
|
@ -505,21 +505,6 @@ void xgbe_debugfs_exit(struct xgbe_prv_data *pdata)
|
||||||
|
|
||||||
void xgbe_debugfs_rename(struct xgbe_prv_data *pdata)
|
void xgbe_debugfs_rename(struct xgbe_prv_data *pdata)
|
||||||
{
|
{
|
||||||
char *buf;
|
debugfs_change_name(pdata->xgbe_debugfs,
|
||||||
|
"amd-xgbe-%s", pdata->netdev->name);
|
||||||
if (!pdata->xgbe_debugfs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name);
|
|
||||||
if (!buf)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!strcmp(pdata->xgbe_debugfs->d_name.name, buf))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
debugfs_rename(pdata->xgbe_debugfs->d_parent, pdata->xgbe_debugfs,
|
|
||||||
pdata->xgbe_debugfs->d_parent, buf);
|
|
||||||
|
|
||||||
out:
|
|
||||||
kfree(buf);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3742,10 +3742,7 @@ static int skge_device_event(struct notifier_block *unused,
|
||||||
skge = netdev_priv(dev);
|
skge = netdev_priv(dev);
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case NETDEV_CHANGENAME:
|
case NETDEV_CHANGENAME:
|
||||||
if (skge->debugfs)
|
debugfs_change_name(skge->debugfs, "%s", dev->name);
|
||||||
skge->debugfs = debugfs_rename(skge_debug,
|
|
||||||
skge->debugfs,
|
|
||||||
skge_debug, dev->name);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NETDEV_GOING_DOWN:
|
case NETDEV_GOING_DOWN:
|
||||||
|
|
|
@ -4635,10 +4635,7 @@ static int sky2_device_event(struct notifier_block *unused,
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case NETDEV_CHANGENAME:
|
case NETDEV_CHANGENAME:
|
||||||
if (sky2->debugfs) {
|
debugfs_change_name(sky2->debugfs, "%s", dev->name);
|
||||||
sky2->debugfs = debugfs_rename(sky2_debug, sky2->debugfs,
|
|
||||||
sky2_debug, dev->name);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NETDEV_GOING_DOWN:
|
case NETDEV_GOING_DOWN:
|
||||||
|
|
|
@ -6564,11 +6564,7 @@ static int stmmac_device_event(struct notifier_block *unused,
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case NETDEV_CHANGENAME:
|
case NETDEV_CHANGENAME:
|
||||||
if (priv->dbgfs_dir)
|
debugfs_change_name(priv->dbgfs_dir, "%s", dev->name);
|
||||||
priv->dbgfs_dir = debugfs_rename(stmmac_fs_dir,
|
|
||||||
priv->dbgfs_dir,
|
|
||||||
stmmac_fs_dir,
|
|
||||||
dev->name);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
|
|
@ -215,7 +215,7 @@ static void opp_migrate_dentry(struct opp_device *opp_dev,
|
||||||
{
|
{
|
||||||
struct opp_device *new_dev = NULL, *iter;
|
struct opp_device *new_dev = NULL, *iter;
|
||||||
const struct device *dev;
|
const struct device *dev;
|
||||||
struct dentry *dentry;
|
int err;
|
||||||
|
|
||||||
/* Look for next opp-dev */
|
/* Look for next opp-dev */
|
||||||
list_for_each_entry(iter, &opp_table->dev_list, node)
|
list_for_each_entry(iter, &opp_table->dev_list, node)
|
||||||
|
@ -232,16 +232,14 @@ static void opp_migrate_dentry(struct opp_device *opp_dev,
|
||||||
|
|
||||||
opp_set_dev_name(dev, opp_table->dentry_name);
|
opp_set_dev_name(dev, opp_table->dentry_name);
|
||||||
|
|
||||||
dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir,
|
err = debugfs_change_name(opp_dev->dentry, "%s", opp_table->dentry_name);
|
||||||
opp_table->dentry_name);
|
if (err) {
|
||||||
if (IS_ERR(dentry)) {
|
|
||||||
dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
|
dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
|
||||||
__func__, dev_name(opp_dev->dev), dev_name(dev));
|
__func__, dev_name(opp_dev->dev), dev_name(dev));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_dev->dentry = dentry;
|
new_dev->dentry = opp_table->dentry = opp_dev->dentry;
|
||||||
opp_table->dentry = dentry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -812,76 +812,70 @@ void debugfs_lookup_and_remove(const char *name, struct dentry *parent)
|
||||||
EXPORT_SYMBOL_GPL(debugfs_lookup_and_remove);
|
EXPORT_SYMBOL_GPL(debugfs_lookup_and_remove);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* debugfs_rename - rename a file/directory in the debugfs filesystem
|
* debugfs_change_name - rename a file/directory in the debugfs filesystem
|
||||||
* @old_dir: a pointer to the parent dentry for the renamed object. This
|
* @dentry: dentry of an object to be renamed.
|
||||||
* should be a directory dentry.
|
* @fmt: format for new name
|
||||||
* @old_dentry: dentry of an object to be renamed.
|
|
||||||
* @new_dir: a pointer to the parent dentry where the object should be
|
|
||||||
* moved. This should be a directory dentry.
|
|
||||||
* @new_name: a pointer to a string containing the target name.
|
|
||||||
*
|
*
|
||||||
* This function renames a file/directory in debugfs. The target must not
|
* This function renames a file/directory in debugfs. The target must not
|
||||||
* exist for rename to succeed.
|
* exist for rename to succeed.
|
||||||
*
|
*
|
||||||
* This function will return a pointer to old_dentry (which is updated to
|
* This function will return 0 on success and -E... on failure.
|
||||||
* reflect renaming) if it succeeds. If an error occurs, ERR_PTR(-ERROR)
|
|
||||||
* will be returned.
|
|
||||||
*
|
*
|
||||||
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
|
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
|
int __printf(2, 3) debugfs_change_name(struct dentry *dentry, const char *fmt, ...)
|
||||||
struct dentry *new_dir, const char *new_name)
|
|
||||||
{
|
{
|
||||||
int error;
|
int error = 0;
|
||||||
struct dentry *dentry = NULL, *trap;
|
const char *new_name;
|
||||||
struct name_snapshot old_name;
|
struct name_snapshot old_name;
|
||||||
|
struct dentry *parent, *target;
|
||||||
|
struct inode *dir;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
if (IS_ERR(old_dir))
|
if (IS_ERR_OR_NULL(dentry))
|
||||||
return old_dir;
|
return 0;
|
||||||
if (IS_ERR(new_dir))
|
|
||||||
return new_dir;
|
|
||||||
if (IS_ERR_OR_NULL(old_dentry))
|
|
||||||
return old_dentry;
|
|
||||||
|
|
||||||
trap = lock_rename(new_dir, old_dir);
|
va_start(ap, fmt);
|
||||||
/* Source or destination directories don't exist? */
|
new_name = kvasprintf_const(GFP_KERNEL, fmt, ap);
|
||||||
if (d_really_is_negative(old_dir) || d_really_is_negative(new_dir))
|
va_end(ap);
|
||||||
goto exit;
|
if (!new_name)
|
||||||
/* Source does not exist, cyclic rename, or mountpoint? */
|
return -ENOMEM;
|
||||||
if (d_really_is_negative(old_dentry) || old_dentry == trap ||
|
|
||||||
d_mountpoint(old_dentry))
|
|
||||||
goto exit;
|
|
||||||
dentry = lookup_one_len(new_name, new_dir, strlen(new_name));
|
|
||||||
/* Lookup failed, cyclic rename or target exists? */
|
|
||||||
if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
take_dentry_name_snapshot(&old_name, old_dentry);
|
parent = dget_parent(dentry);
|
||||||
|
dir = d_inode(parent);
|
||||||
|
inode_lock(dir);
|
||||||
|
|
||||||
error = simple_rename(&nop_mnt_idmap, d_inode(old_dir), old_dentry,
|
take_dentry_name_snapshot(&old_name, dentry);
|
||||||
d_inode(new_dir), dentry, 0);
|
|
||||||
if (error) {
|
if (WARN_ON_ONCE(dentry->d_parent != parent)) {
|
||||||
release_dentry_name_snapshot(&old_name);
|
error = -EINVAL;
|
||||||
goto exit;
|
goto out;
|
||||||
}
|
}
|
||||||
d_move(old_dentry, dentry);
|
if (strcmp(old_name.name.name, new_name) == 0)
|
||||||
fsnotify_move(d_inode(old_dir), d_inode(new_dir), &old_name.name,
|
goto out;
|
||||||
d_is_dir(old_dentry),
|
target = lookup_one_len(new_name, parent, strlen(new_name));
|
||||||
NULL, old_dentry);
|
if (IS_ERR(target)) {
|
||||||
|
error = PTR_ERR(target);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (d_really_is_positive(target)) {
|
||||||
|
dput(target);
|
||||||
|
error = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
simple_rename_timestamp(dir, dentry, dir, target);
|
||||||
|
d_move(dentry, target);
|
||||||
|
dput(target);
|
||||||
|
fsnotify_move(dir, dir, &old_name.name, d_is_dir(dentry), NULL, dentry);
|
||||||
|
out:
|
||||||
release_dentry_name_snapshot(&old_name);
|
release_dentry_name_snapshot(&old_name);
|
||||||
unlock_rename(new_dir, old_dir);
|
inode_unlock(dir);
|
||||||
dput(dentry);
|
dput(parent);
|
||||||
return old_dentry;
|
kfree_const(new_name);
|
||||||
exit:
|
return error;
|
||||||
if (dentry && !IS_ERR(dentry))
|
|
||||||
dput(dentry);
|
|
||||||
unlock_rename(new_dir, old_dir);
|
|
||||||
if (IS_ERR(dentry))
|
|
||||||
return dentry;
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(debugfs_rename);
|
EXPORT_SYMBOL_GPL(debugfs_change_name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* debugfs_initialized - Tells whether debugfs has been registered
|
* debugfs_initialized - Tells whether debugfs has been registered
|
||||||
|
|
|
@ -176,8 +176,7 @@ ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
|
||||||
ssize_t debugfs_attr_write_signed(struct file *file, const char __user *buf,
|
ssize_t debugfs_attr_write_signed(struct file *file, const char __user *buf,
|
||||||
size_t len, loff_t *ppos);
|
size_t len, loff_t *ppos);
|
||||||
|
|
||||||
struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
|
int debugfs_change_name(struct dentry *dentry, const char *fmt, ...) __printf(2, 3);
|
||||||
struct dentry *new_dir, const char *new_name);
|
|
||||||
|
|
||||||
void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent,
|
void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent,
|
||||||
u8 *value);
|
u8 *value);
|
||||||
|
@ -362,10 +361,10 @@ static inline ssize_t debugfs_attr_write_signed(struct file *file,
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
|
static inline int __printf(2, 3) debugfs_change_name(struct dentry *dentry,
|
||||||
struct dentry *new_dir, char *new_name)
|
const char *fmt, ...)
|
||||||
{
|
{
|
||||||
return ERR_PTR(-ENODEV);
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void debugfs_create_u8(const char *name, umode_t mode,
|
static inline void debugfs_create_u8(const char *name, umode_t mode,
|
||||||
|
|
|
@ -195,8 +195,6 @@ int shrinker_debugfs_add(struct shrinker *shrinker)
|
||||||
|
|
||||||
int shrinker_debugfs_rename(struct shrinker *shrinker, const char *fmt, ...)
|
int shrinker_debugfs_rename(struct shrinker *shrinker, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
struct dentry *entry;
|
|
||||||
char buf[128];
|
|
||||||
const char *new, *old;
|
const char *new, *old;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -213,18 +211,8 @@ int shrinker_debugfs_rename(struct shrinker *shrinker, const char *fmt, ...)
|
||||||
old = shrinker->name;
|
old = shrinker->name;
|
||||||
shrinker->name = new;
|
shrinker->name = new;
|
||||||
|
|
||||||
if (shrinker->debugfs_entry) {
|
ret = debugfs_change_name(shrinker->debugfs_entry, "%s-%d",
|
||||||
snprintf(buf, sizeof(buf), "%s-%d", shrinker->name,
|
shrinker->name, shrinker->debugfs_id);
|
||||||
shrinker->debugfs_id);
|
|
||||||
|
|
||||||
entry = debugfs_rename(shrinker_debugfs_root,
|
|
||||||
shrinker->debugfs_entry,
|
|
||||||
shrinker_debugfs_root, buf);
|
|
||||||
if (IS_ERR(entry))
|
|
||||||
ret = PTR_ERR(entry);
|
|
||||||
else
|
|
||||||
shrinker->debugfs_entry = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
up_write(&shrinker_rwsem);
|
up_write(&shrinker_rwsem);
|
||||||
|
|
||||||
|
|
|
@ -57,14 +57,11 @@ DEFINE_SHOW_ATTRIBUTE(hsr_node_table);
|
||||||
void hsr_debugfs_rename(struct net_device *dev)
|
void hsr_debugfs_rename(struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct hsr_priv *priv = netdev_priv(dev);
|
struct hsr_priv *priv = netdev_priv(dev);
|
||||||
struct dentry *d;
|
int err;
|
||||||
|
|
||||||
d = debugfs_rename(hsr_debugfs_root_dir, priv->node_tbl_root,
|
err = debugfs_change_name(priv->node_tbl_root, "%s", dev->name);
|
||||||
hsr_debugfs_root_dir, dev->name);
|
if (err)
|
||||||
if (IS_ERR(d))
|
|
||||||
netdev_warn(dev, "failed to rename\n");
|
netdev_warn(dev, "failed to rename\n");
|
||||||
else
|
|
||||||
priv->node_tbl_root = d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* hsr_debugfs_init - create hsr node_table file for dumping
|
/* hsr_debugfs_init - create hsr node_table file for dumping
|
||||||
|
|
|
@ -1025,16 +1025,7 @@ void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata)
|
||||||
|
|
||||||
void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata)
|
void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata)
|
||||||
{
|
{
|
||||||
struct dentry *dir;
|
debugfs_change_name(sdata->vif.debugfs_dir, "netdev:%s", sdata->name);
|
||||||
char buf[10 + IFNAMSIZ];
|
|
||||||
|
|
||||||
dir = sdata->vif.debugfs_dir;
|
|
||||||
|
|
||||||
if (IS_ERR_OR_NULL(dir))
|
|
||||||
return;
|
|
||||||
|
|
||||||
sprintf(buf, "netdev:%s", sdata->name);
|
|
||||||
debugfs_rename(dir->d_parent, dir, dir->d_parent, buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ieee80211_debugfs_recreate_netdev(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_debugfs_recreate_netdev(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
|
@ -143,10 +143,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
if (!IS_ERR_OR_NULL(rdev->wiphy.debugfsdir))
|
debugfs_change_name(rdev->wiphy.debugfsdir, "%s", newname);
|
||||||
debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
|
|
||||||
rdev->wiphy.debugfsdir,
|
|
||||||
rdev->wiphy.debugfsdir->d_parent, newname);
|
|
||||||
|
|
||||||
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
|
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue