Commit 5a087a6b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 1594c493 f10bef73
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -81,6 +81,9 @@ static struct btrfs_bio *btrfs_split_bio(struct btrfs_fs_info *fs_info,

	bio = bio_split(&orig_bbio->bio, map_length >> SECTOR_SHIFT, GFP_NOFS,
			&btrfs_clone_bioset);
	if (IS_ERR(bio))
		return ERR_CAST(bio);

	bbio = btrfs_bio(bio);
	btrfs_bio_init(bbio, fs_info, NULL, orig_bbio);
	bbio->inode = orig_bbio->inode;
@@ -678,7 +681,8 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
				&bioc, &smap, &mirror_num);
	if (error) {
		ret = errno_to_blk_status(error);
		goto fail;
		btrfs_bio_counter_dec(fs_info);
		goto end_bbio;
	}

	map_length = min(map_length, length);
@@ -686,7 +690,15 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
		map_length = btrfs_append_map_length(bbio, map_length);

	if (map_length < length) {
		bbio = btrfs_split_bio(fs_info, bbio, map_length);
		struct btrfs_bio *split;

		split = btrfs_split_bio(fs_info, bbio, map_length);
		if (IS_ERR(split)) {
			ret = errno_to_blk_status(PTR_ERR(split));
			btrfs_bio_counter_dec(fs_info);
			goto end_bbio;
		}
		bbio = split;
		bio = &bbio->bio;
	}

@@ -760,6 +772,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)

		btrfs_bio_end_io(remaining, ret);
	}
end_bbio:
	btrfs_bio_end_io(bbio, ret);
	/* Do not submit another chunk */
	return true;
+9 −0
Original line number Diff line number Diff line
@@ -4262,6 +4262,15 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
	 * already the cleaner, but below we run all pending delayed iputs.
	 */
	btrfs_flush_workqueue(fs_info->fixup_workers);
	/*
	 * Similar case here, we have to wait for delalloc workers before we
	 * proceed below and stop the cleaner kthread, otherwise we trigger a
	 * use-after-tree on the cleaner kthread task_struct when a delalloc
	 * worker running submit_compressed_extents() adds a delayed iput, which
	 * does a wake up on the cleaner kthread, which was already freed below
	 * when we call kthread_stop().
	 */
	btrfs_flush_workqueue(fs_info->delalloc_workers);

	/*
	 * After we parked the cleaner kthread, ordered extents may have
+1 −0
Original line number Diff line number Diff line
@@ -911,6 +911,7 @@ static noinline int prepare_one_folio(struct inode *inode, struct folio **folio_
			ret = PTR_ERR(folio);
		return ret;
	}
	folio_wait_writeback(folio);
	/* Only support page sized folio yet. */
	ASSERT(folio_order(folio) == 0);
	ret = set_folio_extent_mapped(folio);
+1 −0
Original line number Diff line number Diff line
@@ -9876,6 +9876,7 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file,
	if (btrfs_root_dead(root)) {
		spin_unlock(&root->root_item_lock);

		btrfs_drew_write_unlock(&root->snapshot_lock);
		btrfs_exclop_finish(fs_info);
		btrfs_warn(fs_info,
		"cannot activate swapfile because subvolume %llu is being deleted",
+27 −39
Original line number Diff line number Diff line
@@ -1885,19 +1885,22 @@ 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;
		}
	}

	btrfs_clear_oneshot_options(fs_info);

@@ -1982,39 +1985,18 @@ static int btrfs_get_tree_super(struct fs_context *fc)
 * 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);
	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);
	if (IS_ERR(mnt)) {
		put_fs_context(dup_fc);
	if (IS_ERR(mnt))
		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