simple_pin_fs() is protected by an internal spin lock, to protect against concurrent use of simple_release_fs(). But as we also need to clone the mountpoint we cannot easily expand the scope of the lock as it's internal to fs/libfs. So open-code simple_pin_fs() and simple_release_fs() to be able to use a lock covering all use-cases. Signed-off-by: Hannes Reinecke --- fs/configfs/mount.c | 56 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c index 36f275aaaf871f748dfcb011b782c22abe19d9d5..1f61bc1efe6e2644985c6b90777fa8ddcbb4b742 100644 --- a/fs/configfs/mount.c +++ b/fs/configfs/mount.c @@ -25,6 +25,7 @@ struct kmem_cache *configfs_dir_cachep; static DEFINE_XARRAY(configfs_super_xa); static struct configfs_super_info *configfs_root; +static DEFINE_SPINLOCK(configfs_pin_lock); static u64 configfs_ns_id(struct net *net_ns) { @@ -219,21 +220,60 @@ MODULE_ALIAS_FS("configfs"); struct dentry *configfs_pin_fs(struct super_block *sb) { - struct configfs_super_info *info = configfs_root; - int err; - - err = simple_pin_fs(&configfs_fs_type, &info->mnt, &info->mnt_count); - if (err) - return ERR_PTR(err); + struct configfs_super_info *info; + spin_lock(&configfs_pin_lock); + if (sb) { + struct configfs_super_info *root = configfs_root; + struct dentry *dentry = sb->s_root; + + info = sb->s_fs_info; + if (!info->mnt) { + struct vfsmount *mnt; + + spin_unlock(&configfs_pin_lock); + mnt = mnt_clone_direct(root->mnt, dentry); + if (IS_ERR(mnt)) + return ERR_CAST(mnt); + spin_lock(&configfs_pin_lock); + info->mnt = mnt; + } else { + mntget(info->mnt); + } + } else { + info = configfs_root; + if (!info->mnt) { + struct vfsmount *mnt; + + spin_unlock(&configfs_pin_lock); + mnt = vfs_kern_mount(&configfs_fs_type, SB_KERNMOUNT, + configfs_fs_type.name, NULL); + if (IS_ERR(mnt)) + return ERR_CAST(mnt); + spin_lock(&configfs_pin_lock); + info->mnt = mnt; + } else { + mntget(info->mnt); + } + } + info->mnt_count++; + spin_unlock(&configfs_pin_lock); return info->mnt->mnt_root; } void configfs_release_fs(struct super_block *sb) { struct configfs_super_info *info = configfs_root; - - simple_release_fs(&info->mnt, &info->mnt_count); + struct vfsmount *mnt; + + spin_lock(&configfs_pin_lock); + if (sb) + info = sb->s_fs_info; + mnt = info->mnt; + if (--info->mnt_count) + info->mnt = NULL; + spin_unlock(&configfs_pin_lock); + mntput(mnt); } static int __init configfs_init(void) -- 2.51.0