]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
configfs: dentry refcount needs to be pinned only once
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 12 May 2026 07:19:41 +0000 (03:19 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 8 Jun 2026 18:53:09 +0000 (14:53 -0400)
currently we have a weird situation where
* symlinks and roots of subtrees created by mkdir are pinned once
* subdirectories of subtrees created by mkdir are pinned twice
* roots of subtrees created by register_{group,subsystem} are pinned
twice.

It makes things harder to follow for no good reason.  The goal is to
encapsulate the unbalanced dget/dput into d_{make,discard}_persisitent()
and, preferably, allow a use of simple_recursive_removal() or analogue
thereof.  So let's regularize that and pin things only once.

create_default_group() and configfs_register_subsystem() don't need to
keep their reference around on success - configfs_create_dir() has pinned
the sucker already.  So we can drop the reference passed to
configfs_create_dir() (via configfs_attach_group(), etc.) both on success
and on failure.  On the removal side we no longer have the double references,
so we need an explicit dget() to compensate.

Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/configfs/dir.c

index 87a0847d88b9d4b5f36df83f4730f3003ee0a996..4836ffb3b70f784420b98f8afdb84986784df84f 100644 (file)
@@ -657,7 +657,7 @@ static void detach_groups(struct dentry *dentry)
                if (!(sd->s_type & CONFIGFS_USET_DEFAULT))
                        continue;
 
-               child = sd->s_dentry;
+               child = dget(sd->s_dentry);
                spin_unlock(&configfs_dirent_lock);
 
                inode_lock(d_inode(child));
@@ -708,8 +708,8 @@ static int create_default_group(struct config_group *parent_group,
                } else {
                        BUG_ON(d_inode(child));
                        d_drop(child);
-                       dput(child);
                }
+               dput(child);
        }
 
        return ret;
@@ -1781,7 +1781,7 @@ EXPORT_SYMBOL(configfs_register_group);
 void configfs_unregister_group(struct config_group *group)
 {
        struct configfs_subsystem *subsys = group->cg_subsys;
-       struct dentry *dentry = group->cg_item.ci_dentry;
+       struct dentry *dentry = dget(group->cg_item.ci_dentry);
        struct dentry *parent = group->cg_item.ci_parent->ci_dentry;
        struct configfs_dirent *sd = dentry->d_fsdata;
        struct configfs_fragment *frag = sd->s_frag;
@@ -1896,12 +1896,12 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
                if (err) {
                        BUG_ON(d_inode(dentry));
                        d_drop(dentry);
-                       dput(dentry);
                } else {
                        spin_lock(&configfs_dirent_lock);
                        configfs_dir_set_ready(dentry->d_fsdata);
                        spin_unlock(&configfs_dirent_lock);
                }
+               dput(dentry);
        }
 
        inode_unlock(d_inode(root));
@@ -1920,7 +1920,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
 void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
 {
        struct config_group *group = &subsys->su_group;
-       struct dentry *dentry = group->cg_item.ci_dentry;
+       struct dentry *dentry = dget(group->cg_item.ci_dentry);
        struct dentry *root = dentry->d_sb->s_root;
        struct configfs_dirent *sd = dentry->d_fsdata;
        struct configfs_fragment *frag = sd->s_frag;