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: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Anand Jain <anand.jain@oracle.com>
Acked-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Josef Bacik 2023-11-22 12:17:42 -05:00 committed by David Sterba
parent 272efa308f
commit 9ef40c2e9b

View File

@ -1676,6 +1676,115 @@ 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_rw(struct btrfs_fs_info *fs_info)
{
int ret;
if (BTRFS_FS_ERROR(fs_info)) {
btrfs_err(fs_info,
"remounting read-write after error is not allowed");
return -EINVAL;
}
if (fs_info->fs_devices->rw_devices == 0)
return -EACCES;
if (!btrfs_check_rw_degradable(fs_info, NULL)) {
btrfs_warn(fs_info,
"too many missing devices, writable remount is not allowed");
return -EACCES;
}
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)
return ret;
btrfs_clear_sb_rdonly(fs_info->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);
return 0;
}
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.
*/
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 */
down(&fs_info->uuid_tree_rescan_sem);
/* Avoid complains from lockdep et al. */
up(&fs_info->uuid_tree_rescan_sem);
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
* 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()).
*/
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.
*/
btrfs_run_delayed_iputs(fs_info);
btrfs_dev_replace_suspend_for_unmount(fs_info);
btrfs_scrub_cancel(fs_info);
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.
*/
btrfs_qgroup_wait_for_completion(fs_info, false);
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);
@ -1729,120 +1838,14 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
}
}
if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
goto out;
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;
if (*flags & SB_RDONLY) {
/*
* 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 */
down(&fs_info->uuid_tree_rescan_sem);
/* avoid complains from lockdep et al. */
up(&fs_info->uuid_tree_rescan_sem);
btrfs_set_sb_rdonly(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
* 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()).
*/
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.
*/
btrfs_run_delayed_iputs(fs_info);
btrfs_dev_replace_suspend_for_unmount(fs_info);
btrfs_scrub_cancel(fs_info);
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.
*/
btrfs_qgroup_wait_for_completion(fs_info, false);
ret = btrfs_commit_super(fs_info);
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;
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;
goto restore;
}
if (btrfs_super_log_root(fs_info->super_copy) != 0) {
btrfs_warn(fs_info,
"mount required to replay tree-log, cannot remount read-write");
ret = -EINVAL;
goto restore;
}
/*
* 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;
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.