@@ -2542,6 +2542,9 @@ int close_ctree(struct btrfs_root *root)
fs_info->closing = 1;
smp_mb();
+ /* pause restriper and free restripe_ctl */
+ btrfs_pause_restripe(root->fs_info, 1);
+
btrfs_scrub_cancel(root);
/* wait for any defraggers to finish */
@@ -2921,6 +2921,8 @@ static long btrfs_ioctl_restripe_ctl(struct btrfs_root *root,
switch (cmd) {
case BTRFS_RESTRIPE_CTL_CANCEL:
return btrfs_cancel_restripe(root->fs_info);
+ case BTRFS_RESTRIPE_CTL_PAUSE:
+ return btrfs_pause_restripe(root->fs_info, 0);
}
return -EINVAL;
@@ -110,6 +110,7 @@ struct btrfs_ioctl_fs_info_args {
};
#define BTRFS_RESTRIPE_CTL_CANCEL 1
+#define BTRFS_RESTRIPE_CTL_PAUSE 2
struct btrfs_restripe_args {
__u64 profiles;
@@ -2555,7 +2555,8 @@ static int __btrfs_restripe(struct btrfs_root *dev_root)
while (1) {
struct btrfs_fs_info *fs_info = dev_root->fs_info;
- if (test_bit(RESTRIPE_CANCEL_REQ, &fs_info->restripe_state)) {
+ if (test_bit(RESTRIPE_CANCEL_REQ, &fs_info->restripe_state) ||
+ test_bit(RESTRIPE_PAUSE_REQ, &fs_info->restripe_state)) {
ret = -ECANCELED;
goto error;
}
@@ -2730,7 +2731,9 @@ do_restripe:
mutex_lock(&fs_info->restripe_mutex);
clear_bit(RESTRIPE_RUNNING, &fs_info->restripe_state);
- if (test_bit(RESTRIPE_CANCEL_REQ, &fs_info->restripe_state)) {
+ if (test_bit(RESTRIPE_CANCEL_REQ, &fs_info->restripe_state) ||
+ (!test_bit(RESTRIPE_PAUSE_REQ, &fs_info->restripe_state) &&
+ !test_bit(RESTRIPE_CANCEL_REQ, &fs_info->restripe_state))) {
mutex_lock(&fs_info->volume_mutex);
unset_restripe_control(fs_info);
@@ -2858,6 +2861,43 @@ out:
return ret;
}
+int btrfs_pause_restripe(struct btrfs_fs_info *fs_info, int unset)
+{
+ int ret = 0;
+
+ mutex_lock(&fs_info->restripe_mutex);
+ if (!fs_info->restripe_ctl) {
+ ret = -ENOTCONN;
+ goto out;
+ }
+
+ /* only running restripe can be paused */
+ if (!test_bit(RESTRIPE_RUNNING, &fs_info->restripe_state)) {
+ ret = -ENOTCONN;
+ goto out_unset;
+ }
+
+ set_bit(RESTRIPE_PAUSE_REQ, &fs_info->restripe_state);
+ while (test_bit(RESTRIPE_RUNNING, &fs_info->restripe_state)) {
+ mutex_unlock(&fs_info->restripe_mutex);
+ wait_event(fs_info->restripe_wait,
+ !test_bit(RESTRIPE_RUNNING,
+ &fs_info->restripe_state));
+ mutex_lock(&fs_info->restripe_mutex);
+ }
+ clear_bit(RESTRIPE_PAUSE_REQ, &fs_info->restripe_state);
+
+out_unset:
+ if (unset) {
+ mutex_lock(&fs_info->volume_mutex);
+ unset_restripe_control(fs_info);
+ mutex_unlock(&fs_info->volume_mutex);
+ }
+out:
+ mutex_unlock(&fs_info->restripe_mutex);
+ return ret;
+}
+
/*
* shrinking a device means finding all of the device extents past
* the new size, and then following the back refs to the chunks.
@@ -204,6 +204,7 @@ struct map_lookup {
*/
#define RESTRIPE_RUNNING 0
#define RESTRIPE_CANCEL_REQ 1
+#define RESTRIPE_PAUSE_REQ 2
struct btrfs_restripe_args;
struct restripe_control {
@@ -261,6 +262,7 @@ int btrfs_balance(struct btrfs_root *dev_root);
int btrfs_restripe(struct restripe_control *rctl, int resume);
int btrfs_recover_restripe(struct btrfs_root *tree_root);
int btrfs_cancel_restripe(struct btrfs_fs_info *fs_info);
+int btrfs_pause_restripe(struct btrfs_fs_info *fs_info, int unset);
int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
int find_free_dev_extent(struct btrfs_trans_handle *trans,
struct btrfs_device *device, u64 num_bytes,
Implement an ioctl for pausing restriper. This pauses the relocation, but restripe is still considered to be "in progress": restriper item is not deleted, other volume operations cannot be started, etc. If paused in the middle of profile changing operation we will continue making allocations with the target profile. Add a hook to close_ctree() to be able to pause restriper and free it's data structures on unmount. (It's safe to unmount when restriper is in 'paused' state, we will resume with the same parameters on the next mount) Signed-off-by: Ilya Dryomov <idryomov@gmail.com> --- fs/btrfs/disk-io.c | 3 +++ fs/btrfs/ioctl.c | 2 ++ fs/btrfs/ioctl.h | 1 + fs/btrfs/volumes.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- fs/btrfs/volumes.h | 2 ++ 5 files changed, 50 insertions(+), 2 deletions(-)