The function is called from generic_shutdown_super upon super destruction, but also can be called by a filesystem during unmount to ensure a new superblock is created upon the next mount. This is necessary for the cases, where force unmount of a filesystem renders superblock disfunctional, in a state that can not be re-used (e.g. FUSE severes the connection of the userspace daemon to the superblock which prevents any further communications between them). Signed-off-by: Daniil Lunev <dlunev@xxxxxxxxxxxx> --- Changes in v2: - Remove super from list of superblocks instead of using a flag fs/super.c | 51 ++++++++++++++++++++++++++++++++++++---------- include/linux/fs.h | 1 + 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/fs/super.c b/fs/super.c index f1d4a193602d6..f23e45434de15 100644 --- a/fs/super.c +++ b/fs/super.c @@ -422,6 +422,45 @@ bool trylock_super(struct super_block *sb) return false; } +/** + * retire_locked_super - prevent superblock from re-use + * @sb: superblock that shouldn't be re-used + * + * retire_locked_super excludes the super from the list of superblocks and + * removes the link between superblock and it bdi. After a call to this + * function, any subsequent mount will allocate a new superblock, even if + * the retired one would have been re-used otherwise. It is safe to call + * the function multiple times for the same superblock, any subsequent + * invocations after the first one have no effect. + */ +static void retire_locked_super(struct super_block *sb) +{ + spin_lock(&sb_lock); + hlist_del_init(&sb->s_instances); + spin_unlock(&sb_lock); + up_write(&sb->s_umount); + if (sb->s_bdi != &noop_backing_dev_info) { + if (sb->s_iflags & SB_I_PERSB_BDI) + bdi_unregister(sb->s_bdi); + bdi_put(sb->s_bdi); + sb->s_bdi = &noop_backing_dev_info; + } +} + +/** + * retire_super - prevent superblock from re-use + * @sb: superblock that shouldn't be re-used + * + * Variant of retire_locked_super, except that superblock is *not* locked + * by caller. + */ +void retire_super(struct super_block *sb) +{ + down_write(&sb->s_umount); + retire_locked_super(sb); +} +EXPORT_SYMBOL(retire_super); + /** * generic_shutdown_super - common helper for ->kill_sb() * @sb: superblock to kill @@ -467,17 +506,7 @@ void generic_shutdown_super(struct super_block *sb) sb->s_id); } } - spin_lock(&sb_lock); - /* should be initialized for __put_super_and_need_restart() */ - hlist_del_init(&sb->s_instances); - spin_unlock(&sb_lock); - up_write(&sb->s_umount); - if (sb->s_bdi != &noop_backing_dev_info) { - if (sb->s_iflags & SB_I_PERSB_BDI) - bdi_unregister(sb->s_bdi); - bdi_put(sb->s_bdi); - sb->s_bdi = &noop_backing_dev_info; - } + retire_locked_super(sb); } EXPORT_SYMBOL(generic_shutdown_super); diff --git a/include/linux/fs.h b/include/linux/fs.h index bbde95387a23a..dc9151b7f0102 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2424,6 +2424,7 @@ extern struct dentry *mount_nodev(struct file_system_type *fs_type, int flags, void *data, int (*fill_super)(struct super_block *, void *, int)); extern struct dentry *mount_subtree(struct vfsmount *mnt, const char *path); +void retire_super(struct super_block *sb); void generic_shutdown_super(struct super_block *sb); void kill_block_super(struct super_block *sb); void kill_anon_super(struct super_block *sb); -- 2.31.0