diff mbox series

[11/13] fs: Make sb_start_write() return error on shutdown filesystem

Message ID 20240807183003.23562-11-jack@suse.cz (mailing list archive)
State New
Headers show
Series fs: generic filesystem shutdown handling | expand

Commit Message

Jan Kara Aug. 7, 2024, 6:29 p.m. UTC
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@suse.cz>
---
 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 mbox series

Patch

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.