aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/fs/btrfs/super.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-12-10 18:18:01 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2024-12-10 18:18:01 -0800
commit5a087a6b17eeb64893b81d08d38e6f6300419ee5 (patch)
tree7db289218a94a28e96af7de7c120a0fa8ca9f42d /fs/btrfs/super.c
parentMerge tag 'probes-fixes-v6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace (diff)
parentbtrfs: flush delalloc workers queue before stopping cleaner kthread during unmount (diff)
downloadwireguard-linux-5a087a6b17eeb64893b81d08d38e6f6300419ee5.tar.xz
wireguard-linux-5a087a6b17eeb64893b81d08d38e6f6300419ee5.zip
Merge tag 'for-6.13-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux
Pull btrfs fixes from David Sterba: "A few more fixes. Apart from the one liners and updated bio splitting error handling there's a fix for subvolume mount with different flags. This was known and fixed for some time but I've delayed it to give it more testing. - fix unbalanced locking when swapfile activation fails when the subvolume gets deleted in the meantime - add btrfs error handling after bio_split() calls that got error handling recently - during unmount, flush delalloc workers at the right time before the cleaner thread is shut down - fix regression in buffered write folio conversion, explicitly wait for writeback as FGP_STABLE flag is currently a no-op on btrfs - handle race in subvolume mount with different flags, the conversion to the new mount API did not handle the case where multiple subvolumes get mounted in parallel, which is a distro use case" * tag 'for-6.13-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux: btrfs: flush delalloc workers queue before stopping cleaner kthread during unmount btrfs: handle bio_split() errors btrfs: properly wait for writeback before buffered write btrfs: fix missing snapshot drew unlock when root is dead during swap activation btrfs: fix mount failure due to remount races
Diffstat (limited to '')
-rw-r--r--fs/btrfs/super.c66
1 files changed, 27 insertions, 39 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 97a85d180b61..7dfe5005129a 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1885,18 +1885,21 @@ static int btrfs_get_tree_super(struct fs_context *fc)
if (sb->s_root) {
btrfs_close_devices(fs_devices);
- if ((fc->sb_flags ^ sb->s_flags) & SB_RDONLY)
- ret = -EBUSY;
+ /*
+ * At this stage we may have RO flag mismatch between
+ * fc->sb_flags and sb->s_flags. Caller should detect such
+ * mismatch and reconfigure with sb->s_umount rwsem held if
+ * needed.
+ */
} else {
snprintf(sb->s_id, sizeof(sb->s_id), "%pg", bdev);
shrinker_debugfs_rename(sb->s_shrink, "sb-btrfs:%s", sb->s_id);
btrfs_sb(sb)->bdev_holder = &btrfs_fs_type;
ret = btrfs_fill_super(sb, fs_devices);
- }
-
- if (ret) {
- deactivate_locked_super(sb);
- return ret;
+ if (ret) {
+ deactivate_locked_super(sb);
+ return ret;
+ }
}
btrfs_clear_oneshot_options(fs_info);
@@ -1982,39 +1985,18 @@ error:
* btrfs or not, setting the whole super block RO. To make per-subvolume mounting
* work with different options work we need to keep backward compatibility.
*/
-static struct vfsmount *btrfs_reconfigure_for_mount(struct fs_context *fc)
+static int btrfs_reconfigure_for_mount(struct fs_context *fc, struct vfsmount *mnt)
{
- struct vfsmount *mnt;
- int ret;
- const bool ro2rw = !(fc->sb_flags & SB_RDONLY);
-
- /*
- * We got an EBUSY because our SB_RDONLY flag didn't match the existing
- * super block, so invert our setting here and retry the mount so we
- * can get our vfsmount.
- */
- if (ro2rw)
- fc->sb_flags |= SB_RDONLY;
- else
- fc->sb_flags &= ~SB_RDONLY;
-
- mnt = fc_mount(fc);
- if (IS_ERR(mnt))
- return mnt;
+ int ret = 0;
- if (!ro2rw)
- return mnt;
+ if (fc->sb_flags & SB_RDONLY)
+ return ret;
- /* We need to convert to rw, call reconfigure. */
- fc->sb_flags &= ~SB_RDONLY;
down_write(&mnt->mnt_sb->s_umount);
- ret = btrfs_reconfigure(fc);
+ if (!(fc->sb_flags & SB_RDONLY) && (mnt->mnt_sb->s_flags & SB_RDONLY))
+ ret = btrfs_reconfigure(fc);
up_write(&mnt->mnt_sb->s_umount);
- if (ret) {
- mntput(mnt);
- return ERR_PTR(ret);
- }
- return mnt;
+ return ret;
}
static int btrfs_get_tree_subvol(struct fs_context *fc)
@@ -2024,6 +2006,7 @@ static int btrfs_get_tree_subvol(struct fs_context *fc)
struct fs_context *dup_fc;
struct dentry *dentry;
struct vfsmount *mnt;
+ int ret = 0;
/*
* Setup a dummy root and fs_info for test/set super. This is because
@@ -2066,11 +2049,16 @@ static int btrfs_get_tree_subvol(struct fs_context *fc)
fc->security = NULL;
mnt = fc_mount(dup_fc);
- if (PTR_ERR_OR_ZERO(mnt) == -EBUSY)
- mnt = btrfs_reconfigure_for_mount(dup_fc);
- put_fs_context(dup_fc);
- if (IS_ERR(mnt))
+ if (IS_ERR(mnt)) {
+ put_fs_context(dup_fc);
return PTR_ERR(mnt);
+ }
+ ret = btrfs_reconfigure_for_mount(dup_fc, mnt);
+ put_fs_context(dup_fc);
+ if (ret) {
+ mntput(mnt);
+ return ret;
+ }
/*
* This free's ->subvol_name, because if it isn't set we have to