Introduce new SB_I_SHUTDOWN flag that a filesystem can set when it is forcefully shutting down (usually due to errors). Make sb_start_write() return errors for such superblocks to avoid modifications to it which reduces noise in the error logs and generally makes life somewhat easier for filesystems. We teach all sb_start_write() callers to handle the error. Signed-off-by: Jan Kara <jack@xxxxxxx> --- fs/btrfs/block-group.c | 3 ++- fs/btrfs/defrag.c | 6 +++++- fs/btrfs/volumes.c | 13 +++++++++---- fs/ext4/mmp.c | 4 +++- fs/namespace.c | 8 ++++++-- fs/open.c | 4 +++- fs/overlayfs/util.c | 3 +-- fs/quota/quota.c | 4 ++-- fs/xfs/xfs_ioctl.c | 4 +++- include/linux/fs.h | 16 ++++++++++++---- 10 files changed, 46 insertions(+), 19 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 498442d0c216..fdd833f1f7df 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1800,7 +1800,8 @@ void btrfs_reclaim_bgs_work(struct work_struct *work) if (!btrfs_should_reclaim(fs_info)) return; - sb_start_write(fs_info->sb); + if (sb_start_write(fs_info->sb) < 0) + return; if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_BALANCE)) { sb_end_write(fs_info->sb); diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index f6dbda37a361..6d14c9be4060 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -274,7 +274,11 @@ static int __btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info, range.start = cur; range.extent_thresh = defrag->extent_thresh; - sb_start_write(fs_info->sb); + ret = sb_start_write(fs_info->sb); + if (ret < 0) { + iput(inode); + goto cleanup; + } ret = btrfs_defrag_file(inode, NULL, &range, defrag->transid, BTRFS_DEFRAG_BATCH); sb_end_write(fs_info->sb); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index fcedc43ef291..f7b6b307c4bf 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4589,9 +4589,11 @@ int btrfs_balance(struct btrfs_fs_info *fs_info, static int balance_kthread(void *data) { struct btrfs_fs_info *fs_info = data; - int ret = 0; + int ret; - sb_start_write(fs_info->sb); + ret = sb_start_write(fs_info->sb); + if (ret < 0) + return ret; mutex_lock(&fs_info->balance_mutex); if (fs_info->balance_ctl) ret = btrfs_balance(fs_info, fs_info->balance_ctl, NULL); @@ -8231,12 +8233,15 @@ static int relocating_repair_kthread(void *data) struct btrfs_block_group *cache = data; struct btrfs_fs_info *fs_info = cache->fs_info; u64 target; - int ret = 0; + int ret; target = cache->start; btrfs_put_block_group(cache); - sb_start_write(fs_info->sb); + ret = sb_start_write(fs_info->sb); + if (ret < 0) + return ret; + if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_BALANCE)) { btrfs_info(fs_info, "zoned: skip relocating block group %llu to repair: EBUSY", diff --git a/fs/ext4/mmp.c b/fs/ext4/mmp.c index bd946d0c71b7..96f69b6835f9 100644 --- a/fs/ext4/mmp.c +++ b/fs/ext4/mmp.c @@ -63,7 +63,9 @@ static int write_mmp_block(struct super_block *sb, struct buffer_head *bh) * We protect against freezing so that we don't create dirty buffers * on frozen filesystem. */ - sb_start_write(sb); + err = sb_start_write(sb); + if (err < 0) + return err; err = write_mmp_block_thawed(sb, bh); sb_end_write(sb); return err; diff --git a/fs/namespace.c b/fs/namespace.c index 1c5591673f96..43fad685531e 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -512,7 +512,9 @@ int mnt_want_write(struct vfsmount *m) { int ret; - sb_start_write(m->mnt_sb); + ret = sb_start_write(m->mnt_sb); + if (ret) + return ret; ret = mnt_get_write_access(m); if (ret) sb_end_write(m->mnt_sb); @@ -556,7 +558,9 @@ int mnt_want_write_file(struct file *file) { int ret; - sb_start_write(file_inode(file)->i_sb); + ret = sb_start_write(file_inode(file)->i_sb); + if (ret) + return ret; ret = mnt_get_write_access_file(file); if (ret) sb_end_write(file_inode(file)->i_sb); diff --git a/fs/open.c b/fs/open.c index 4bce4ba776ab..8fe9f4968969 100644 --- a/fs/open.c +++ b/fs/open.c @@ -175,7 +175,9 @@ long do_ftruncate(struct file *file, loff_t length, int small) /* Check IS_APPEND on real upper inode */ if (IS_APPEND(file_inode(file))) return -EPERM; - sb_start_write(inode->i_sb); + error = sb_start_write(inode->i_sb); + if (error) + return error; error = security_file_truncate(file); if (!error) error = do_truncate(file_mnt_idmap(file), dentry, length, diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index b53fa14506a9..f97bf2458c66 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -28,8 +28,7 @@ int ovl_get_write_access(struct dentry *dentry) int __must_check ovl_start_write(struct dentry *dentry) { struct ovl_fs *ofs = OVL_FS(dentry->d_sb); - sb_start_write(ovl_upper_mnt(ofs)->mnt_sb); - return 0; + return sb_start_write(ovl_upper_mnt(ofs)->mnt_sb); } int ovl_want_write(struct dentry *dentry) diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 0e41fb84060f..df9c4d08f135 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -896,8 +896,8 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) else up_read(&sb->s_umount); /* Wait for sb to unfreeze */ - sb_start_write(sb); - sb_end_write(sb); + if (sb_start_write(sb) == 0) + sb_end_write(sb); put_super(sb); goto retry; } diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 4e933db75b12..5cf9e568324a 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -1499,7 +1499,9 @@ xfs_file_ioctl( trace_xfs_ioc_free_eofblocks(mp, &icw, _RET_IP_); - sb_start_write(mp->m_super); + error = sb_start_write(mp->m_super); + if (error) + return error; error = xfs_blockgc_free_space(mp, &icw); sb_end_write(mp->m_super); return error; diff --git a/include/linux/fs.h b/include/linux/fs.h index 755a4c83a2bf..44ae86f46b12 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1190,6 +1190,9 @@ enum { SB_I_TS_EXPIRY_WARNED, /* warned about timestamp range expiry */ SB_I_RETIRED, /* superblock shouldn't be reused */ SB_I_NOUMASK, /* VFS does not apply umask */ + SB_I_SHUTDOWN, /* The filesystem has shutdown. Refuse + * modification attempts with error as they are + * a futile exercise. */ }; /* Possible states of 'frozen' field */ @@ -1823,9 +1826,12 @@ static inline void sb_end_intwrite(struct super_block *sb) * -> i_mutex (write path, truncate, directory ops, ...) * -> s_umount (freeze_super, thaw_super) */ -static inline void sb_start_write(struct super_block *sb) +static inline int __must_check sb_start_write(struct super_block *sb) { + if (sb_test_iflag(sb, SB_I_SHUTDOWN)) + return -EROFS; __sb_start_write(sb, SB_FREEZE_WRITE); + return 0; } static inline bool __must_check sb_start_write_trylock(struct super_block *sb) @@ -2891,8 +2897,7 @@ static inline int __must_check file_start_write(struct file *file) { if (!S_ISREG(file_inode(file)->i_mode)) return 0; - sb_start_write(file_inode(file)->i_sb); - return 0; + return sb_start_write(file_inode(file)->i_sb); } static inline bool __must_check file_start_write_trylock(struct file *file) @@ -2925,8 +2930,11 @@ static inline void file_end_write(struct file *file) static inline int __must_check kiocb_start_write(struct kiocb *iocb) { struct inode *inode = file_inode(iocb->ki_filp); + int err; - sb_start_write(inode->i_sb); + err = sb_start_write(inode->i_sb); + if (err) + return err; /* * Fool lockdep by telling it the lock got released so that it * doesn't complain about the held lock when we return to userspace. -- 2.35.3