diff options
author | Daniil Lunev <dlunev@chromium.org> | 2022-07-27 09:44:24 +0300 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2022-07-27 12:30:30 +0300 |
commit | 04b9407197789c81fffac52921e703cb47967d6a (patch) | |
tree | 7a3cff2cee9ac275b7a3201a13f1c6d4c08e2a7a /fs/super.c | |
parent | 73fb2c8b61783e2e8a87f91d141bf72a12404566 (diff) | |
download | linux-04b9407197789c81fffac52921e703cb47967d6a.tar.xz |
vfs: function to prevent re-use of block-device-based superblocks
The function is to be called from filesystem-specific code to mark a
superblock to be ignored by superblock test and thus never re-used. The
function also unregisters bdi if the bdi is per-superblock to avoid
collision if a new superblock is created to represent the filesystem.
generic_shutdown_super() skips unregistering bdi for a retired superlock as
it assumes retire function has already done it.
This patch adds the functionality only for the block-device-based supers,
since the primary use case of the feature is to gracefully handle force
unmount of external devices, mounted with FUSE. This can be further
extended to cover all superblocks, if the need arises.
Signed-off-by: Daniil Lunev <dlunev@chromium.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/super.c')
-rw-r--r-- | fs/super.c | 33 |
1 files changed, 31 insertions, 2 deletions
diff --git a/fs/super.c b/fs/super.c index 60f57c7bc0a6..258d9257b0a7 100644 --- a/fs/super.c +++ b/fs/super.c @@ -423,6 +423,35 @@ bool trylock_super(struct super_block *sb) } /** + * retire_super - prevents superblock from being reused + * @sb: superblock to retire + * + * The function marks superblock to be ignored in superblock test, which + * prevents it from being reused for any new mounts. If the superblock has + * a private bdi, it also unregisters it, but doesn't reduce the refcount + * of the superblock to prevent potential races. The refcount is reduced + * by generic_shutdown_super(). The function can not be called + * concurrently with generic_shutdown_super(). It is safe to call the + * function multiple times, subsequent calls have no effect. + * + * The marker will affect the re-use only for block-device-based + * superblocks. Other superblocks will still get marked if this function + * is used, but that will not affect their reusability. + */ +void retire_super(struct super_block *sb) +{ + WARN_ON(!sb->s_bdev); + down_write(&sb->s_umount); + if (sb->s_iflags & SB_I_PERSB_BDI) { + bdi_unregister(sb->s_bdi); + sb->s_iflags &= ~SB_I_PERSB_BDI; + } + sb->s_iflags |= SB_I_RETIRED; + up_write(&sb->s_umount); +} +EXPORT_SYMBOL(retire_super); + +/** * generic_shutdown_super - common helper for ->kill_sb() * @sb: superblock to kill * @@ -1216,7 +1245,7 @@ static int set_bdev_super_fc(struct super_block *s, struct fs_context *fc) static int test_bdev_super_fc(struct super_block *s, struct fs_context *fc) { - return s->s_bdev == fc->sget_key; + return !(s->s_iflags & SB_I_RETIRED) && s->s_bdev == fc->sget_key; } /** @@ -1307,7 +1336,7 @@ EXPORT_SYMBOL(get_tree_bdev); static int test_bdev_super(struct super_block *s, void *data) { - return (void *)s->s_bdev == data; + return !(s->s_iflags & SB_I_RETIRED) && (void *)s->s_bdev == data; } struct dentry *mount_bdev(struct file_system_type *fs_type, |