Commit 9ef40c2e authored by Josef Bacik's avatar Josef Bacik Committed by David Sterba
Browse files

btrfs: split out ro->rw and rw->ro helpers into their own functions



When we remount ro->rw or rw->ro we have some cleanup tasks that have to
be managed.  Split these out into their own function to make
btrfs_remount smaller.

Reviewed-by: default avatarJohannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: default avatarAnand Jain <anand.jain@oracle.com>
Acked-by: default avatarChristian Brauner <brauner@kernel.org>
Signed-off-by: default avatarJosef Bacik <josef@toxicpanda.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 272efa30
Loading
Loading
Loading
Loading
+116 −113
Original line number Diff line number Diff line
@@ -1676,109 +1676,97 @@ static inline void btrfs_remount_cleanup(struct btrfs_fs_info *fs_info,
		btrfs_set_free_space_cache_v1_active(fs_info, cache_opt);
}

static int btrfs_remount(struct super_block *sb, int *flags, char *data)
static int btrfs_remount_rw(struct btrfs_fs_info *fs_info)
{
	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
	unsigned old_flags = sb->s_flags;
	unsigned long old_opts = fs_info->mount_opt;
	unsigned long old_compress_type = fs_info->compress_type;
	u64 old_max_inline = fs_info->max_inline;
	u32 old_thread_pool_size = fs_info->thread_pool_size;
	u32 old_metadata_ratio = fs_info->metadata_ratio;
	int ret;

	sync_filesystem(sb);
	set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
	if (BTRFS_FS_ERROR(fs_info)) {
		btrfs_err(fs_info,
			  "remounting read-write after error is not allowed");
		return -EINVAL;
	}

	if (data) {
		void *new_sec_opts = NULL;
	if (fs_info->fs_devices->rw_devices == 0)
		return -EACCES;

		ret = security_sb_eat_lsm_opts(data, &new_sec_opts);
		if (!ret)
			ret = security_sb_remount(sb, new_sec_opts);
		security_free_mnt_opts(&new_sec_opts);
		if (ret)
			goto restore;
	if (!btrfs_check_rw_degradable(fs_info, NULL)) {
		btrfs_warn(fs_info,
			   "too many missing devices, writable remount is not allowed");
		return -EACCES;
	}

	ret = btrfs_parse_options(fs_info, data, *flags);
	if (btrfs_super_log_root(fs_info->super_copy) != 0) {
		btrfs_warn(fs_info,
			   "mount required to replay tree-log, cannot remount read-write");
		return -EINVAL;
	}

	/*
	 * NOTE: when remounting with a change that does writes, don't put it
	 * anywhere above this point, as we are not sure to be safe to write
	 * until we pass the above checks.
	 */
	ret = btrfs_start_pre_rw_mount(fs_info);
	if (ret)
		goto restore;
		return ret;

	ret = btrfs_check_features(fs_info, !(*flags & SB_RDONLY));
	if (ret < 0)
		goto restore;
	btrfs_clear_sb_rdonly(fs_info->sb);

	btrfs_remount_begin(fs_info, old_opts, *flags);
	btrfs_resize_thread_pool(fs_info,
		fs_info->thread_pool_size, old_thread_pool_size);
	set_bit(BTRFS_FS_OPEN, &fs_info->flags);

	if ((bool)btrfs_test_opt(fs_info, FREE_SPACE_TREE) !=
	    (bool)btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
	    (!sb_rdonly(sb) || (*flags & SB_RDONLY))) {
		btrfs_warn(fs_info,
		"remount supports changing free space tree only from ro to rw");
		/* Make sure free space cache options match the state on disk */
		if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
			btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE);
			btrfs_clear_opt(fs_info->mount_opt, SPACE_CACHE);
		}
		if (btrfs_free_space_cache_v1_active(fs_info)) {
			btrfs_clear_opt(fs_info->mount_opt, FREE_SPACE_TREE);
			btrfs_set_opt(fs_info->mount_opt, SPACE_CACHE);
		}
	}
	/*
	 * If we've gone from readonly -> read-write, we need to get our
	 * sync/async discard lists in the right state.
	 */
	btrfs_discard_resume(fs_info);

	if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
		goto out;
	return 0;
}

	if (*flags & SB_RDONLY) {
static int btrfs_remount_ro(struct btrfs_fs_info *fs_info)
{
	/*
		 * this also happens on 'umount -rf' or on shutdown, when
		 * the filesystem is busy.
	 * This also happens on 'umount -rf' or on shutdown, when the
	 * filesystem is busy.
	 */
	cancel_work_sync(&fs_info->async_reclaim_work);
	cancel_work_sync(&fs_info->async_data_reclaim_work);

	btrfs_discard_cleanup(fs_info);

		/* wait for the uuid_scan task to finish */
	/* Wait for the uuid_scan task to finish */
	down(&fs_info->uuid_tree_rescan_sem);
		/* avoid complains from lockdep et al. */
	/* Avoid complains from lockdep et al. */
	up(&fs_info->uuid_tree_rescan_sem);

		btrfs_set_sb_rdonly(sb);
	btrfs_set_sb_rdonly(fs_info->sb);

	/*
		 * Setting SB_RDONLY will put the cleaner thread to
		 * sleep at the next loop if it's already active.
		 * If it's already asleep, we'll leave unused block
		 * groups on disk until we're mounted read-write again
	 * Setting SB_RDONLY will put the cleaner thread to sleep at the next
	 * loop if it's already active.  If it's already asleep, we'll leave
	 * unused block groups on disk until we're mounted read-write again
	 * unless we clean them up here.
	 */
	btrfs_delete_unused_bgs(fs_info);

	/*
		 * The cleaner task could be already running before we set the
		 * flag BTRFS_FS_STATE_RO (and SB_RDONLY in the superblock).
		 * We must make sure that after we finish the remount, i.e. after
		 * we call btrfs_commit_super(), the cleaner can no longer start
		 * a transaction - either because it was dropping a dead root,
		 * running delayed iputs or deleting an unused block group (the
		 * cleaner picked a block group from the list of unused block
		 * groups before we were able to in the previous call to
		 * btrfs_delete_unused_bgs()).
	 * The cleaner task could be already running before we set the flag
	 * BTRFS_FS_STATE_RO (and SB_RDONLY in the superblock).  We must make
	 * sure that after we finish the remount, i.e. after we call
	 * btrfs_commit_super(), the cleaner can no longer start a transaction
	 * - either because it was dropping a dead root, running delayed iputs
	 *   or deleting an unused block group (the cleaner picked a block
	 *   group from the list of unused block groups before we were able to
	 *   in the previous call to btrfs_delete_unused_bgs()).
	 */
		wait_on_bit(&fs_info->flags, BTRFS_FS_CLEANER_RUNNING,
			    TASK_UNINTERRUPTIBLE);
	wait_on_bit(&fs_info->flags, BTRFS_FS_CLEANER_RUNNING, TASK_UNINTERRUPTIBLE);

	/*
		 * We've set the superblock to RO mode, so we might have made
		 * the cleaner task sleep without running all pending delayed
		 * iputs. Go through all the delayed iputs here, so that if an
		 * unmount happens without remounting RW we don't end up at
		 * finishing close_ctree() with a non-empty list of delayed
		 * iputs.
	 * We've set the superblock to RO mode, so we might have made the
	 * cleaner task sleep without running all pending delayed iputs. Go
	 * through all the delayed iputs here, so that if an unmount happens
	 * without remounting RW we don't end up at finishing close_ctree()
	 * with a non-empty list of delayed iputs.
	 */
	btrfs_run_delayed_iputs(fs_info);

@@ -1787,62 +1775,77 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
	btrfs_pause_balance(fs_info);

	/*
		 * Pause the qgroup rescan worker if it is running. We don't want
		 * it to be still running after we are in RO mode, as after that,
		 * by the time we unmount, it might have left a transaction open,
		 * so we would leak the transaction and/or crash.
	 * Pause the qgroup rescan worker if it is running. We don't want it to
	 * be still running after we are in RO mode, as after that, by the time
	 * we unmount, it might have left a transaction open, so we would leak
	 * the transaction and/or crash.
	 */
	btrfs_qgroup_wait_for_completion(fs_info, false);

		ret = btrfs_commit_super(fs_info);
	return btrfs_commit_super(fs_info);
}

static int btrfs_remount(struct super_block *sb, int *flags, char *data)
{
	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
	unsigned old_flags = sb->s_flags;
	unsigned long old_opts = fs_info->mount_opt;
	unsigned long old_compress_type = fs_info->compress_type;
	u64 old_max_inline = fs_info->max_inline;
	u32 old_thread_pool_size = fs_info->thread_pool_size;
	u32 old_metadata_ratio = fs_info->metadata_ratio;
	int ret;

	sync_filesystem(sb);
	set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);

	if (data) {
		void *new_sec_opts = NULL;

		ret = security_sb_eat_lsm_opts(data, &new_sec_opts);
		if (!ret)
			ret = security_sb_remount(sb, new_sec_opts);
		security_free_mnt_opts(&new_sec_opts);
		if (ret)
			goto restore;
	} else {
		if (BTRFS_FS_ERROR(fs_info)) {
			btrfs_err(fs_info,
				"Remounting read-write after error is not allowed");
			ret = -EINVAL;
			goto restore;
	}
		if (fs_info->fs_devices->rw_devices == 0) {
			ret = -EACCES;

	ret = btrfs_parse_options(fs_info, data, *flags);
	if (ret)
		goto restore;
		}

		if (!btrfs_check_rw_degradable(fs_info, NULL)) {
			btrfs_warn(fs_info,
		"too many missing devices, writable remount is not allowed");
			ret = -EACCES;
	ret = btrfs_check_features(fs_info, !(*flags & SB_RDONLY));
	if (ret < 0)
		goto restore;
		}

		if (btrfs_super_log_root(fs_info->super_copy) != 0) {
	btrfs_remount_begin(fs_info, old_opts, *flags);
	btrfs_resize_thread_pool(fs_info,
		fs_info->thread_pool_size, old_thread_pool_size);

	if ((bool)btrfs_test_opt(fs_info, FREE_SPACE_TREE) !=
	    (bool)btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
	    (!sb_rdonly(sb) || (*flags & SB_RDONLY))) {
		btrfs_warn(fs_info,
		"mount required to replay tree-log, cannot remount read-write");
			ret = -EINVAL;
			goto restore;
		"remount supports changing free space tree only from ro to rw");
		/* Make sure free space cache options match the state on disk */
		if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
			btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE);
			btrfs_clear_opt(fs_info->mount_opt, SPACE_CACHE);
		}
		if (btrfs_free_space_cache_v1_active(fs_info)) {
			btrfs_clear_opt(fs_info->mount_opt, FREE_SPACE_TREE);
			btrfs_set_opt(fs_info->mount_opt, SPACE_CACHE);
		}
	}

		/*
		 * NOTE: when remounting with a change that does writes, don't
		 * put it anywhere above this point, as we are not sure to be
		 * safe to write until we pass the above checks.
		 */
		ret = btrfs_start_pre_rw_mount(fs_info);
	ret = 0;
	if (!sb_rdonly(sb) && (*flags & SB_RDONLY))
		ret = btrfs_remount_ro(fs_info);
	else if (sb_rdonly(sb) && !(*flags & SB_RDONLY))
		ret = btrfs_remount_rw(fs_info);
	if (ret)
		goto restore;

		btrfs_clear_sb_rdonly(sb);

		set_bit(BTRFS_FS_OPEN, &fs_info->flags);

		/*
		 * If we've gone from readonly -> read/write, we need to get
		 * our sync/async discard lists in the right state.
		 */
		btrfs_discard_resume(fs_info);
	}
out:
	/*
	 * We need to set SB_I_VERSION here otherwise it'll get cleared by VFS,
	 * since the absence of the flag means it can be toggled off by remount.