]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
btrfs: add cancellable chunk relocation support
authorDavid Sterba <dsterba@suse.com>
Mon, 17 May 2021 22:37:36 +0000 (00:37 +0200)
committerDavid Sterba <dsterba@suse.com>
Mon, 31 May 2021 13:16:44 +0000 (15:16 +0200)
Add support code that will allow canceling relocation on the chunk
granularity. This is different and independent of balance, that also
uses relocation but is a higher level operation and manages it's own
state and pause/cancellation requests.

Relocation is used for resize (shrink) and device deletion so this will
be a common point to implement cancellation for both. The context is
entirely in btrfs_relocate_block_group and btrfs_recover_relocation,
enclosing one chunk relocation. The status bit is set and unset between
the chunks. As relocation can take long, the effects may not be
immediate and the request and actual action can slightly race.

The fs_info::reloc_cancel_req is only supposed to be increased and does
not pair with decrement like fs_info::balance_cancel_req.

Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/relocation.c

index 595582aa94d3faa591e5356f869de0e18f146798..d7c7c2d75b4ce92b21aa18291de72ed06cb7e755 100644 (file)
@@ -565,6 +565,12 @@ enum {
         */
        BTRFS_FS_BALANCE_RUNNING,
 
+       /*
+        * Indicate that relocation of a chunk has started, it's set per chunk
+        * and is toggled between chunks.
+        */
+       BTRFS_FS_RELOC_RUNNING,
+
        /* Indicate that the cleaner thread is awake and doing something. */
        BTRFS_FS_CLEANER_RUNNING,
 
@@ -871,6 +877,9 @@ struct btrfs_fs_info {
        struct btrfs_balance_control *balance_ctl;
        wait_queue_head_t balance_wait_q;
 
+       /* Cancellation requests for chunk relocation */
+       atomic_t reloc_cancel_req;
+
        u32 data_chunk_allocations;
        u32 metadata_ratio;
 
index 594fc090163a444a0b16f2f815cf1b496021fef0..99757939f8b00247cc3c60b0b51b93b6adc297dd 100644 (file)
@@ -2251,6 +2251,7 @@ static void btrfs_init_balance(struct btrfs_fs_info *fs_info)
        atomic_set(&fs_info->balance_cancel_req, 0);
        fs_info->balance_ctl = NULL;
        init_waitqueue_head(&fs_info->balance_wait_q);
+       atomic_set(&fs_info->reloc_cancel_req, 0);
 }
 
 static void btrfs_init_btree_inode(struct btrfs_fs_info *fs_info)
index b70be2ac2e9e61a37865f058f9a368752fe4337c..147d78d19e90b0a5787fcf2a5559aec421fe3d72 100644 (file)
@@ -2876,11 +2876,12 @@ int setup_extent_mapping(struct inode *inode, u64 start, u64 end,
 }
 
 /*
- * Allow error injection to test balance cancellation
+ * Allow error injection to test balance/relocation cancellation
  */
 noinline int btrfs_should_cancel_balance(struct btrfs_fs_info *fs_info)
 {
        return atomic_read(&fs_info->balance_cancel_req) ||
+               atomic_read(&fs_info->reloc_cancel_req) ||
                fatal_signal_pending(current);
 }
 ALLOW_ERROR_INJECTION(btrfs_should_cancel_balance, TRUE);
@@ -3780,6 +3781,47 @@ out:
        return inode;
 }
 
+/*
+ * Mark start of chunk relocation that is cancellable. Check if the cancellation
+ * has been requested meanwhile and don't start in that case.
+ *
+ * Return:
+ *   0             success
+ *   -EINPROGRESS  operation is already in progress, that's probably a bug
+ *   -ECANCELED    cancellation request was set before the operation started
+ */
+static int reloc_chunk_start(struct btrfs_fs_info *fs_info)
+{
+       if (test_and_set_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags)) {
+               /* This should not happen */
+               btrfs_err(fs_info, "reloc already running, cannot start");
+               return -EINPROGRESS;
+       }
+
+       if (atomic_read(&fs_info->reloc_cancel_req) > 0) {
+               btrfs_info(fs_info, "chunk relocation canceled on start");
+               /*
+                * On cancel, clear all requests but let the caller mark
+                * the end after cleanup operations.
+                */
+               atomic_set(&fs_info->reloc_cancel_req, 0);
+               return -ECANCELED;
+       }
+       return 0;
+}
+
+/*
+ * Mark end of chunk relocation that is cancellable and wake any waiters.
+ */
+static void reloc_chunk_end(struct btrfs_fs_info *fs_info)
+{
+       /* Requested after start, clear bit first so any waiters can continue */
+       if (atomic_read(&fs_info->reloc_cancel_req) > 0)
+               btrfs_info(fs_info, "chunk relocation canceled during operation");
+       clear_and_wake_up_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags);
+       atomic_set(&fs_info->reloc_cancel_req, 0);
+}
+
 static struct reloc_control *alloc_reloc_control(struct btrfs_fs_info *fs_info)
 {
        struct reloc_control *rc;
@@ -3862,6 +3904,12 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start)
                return -ENOMEM;
        }
 
+       ret = reloc_chunk_start(fs_info);
+       if (ret < 0) {
+               err = ret;
+               goto out_put_bg;
+       }
+
        rc->extent_root = extent_root;
        rc->block_group = bg;
 
@@ -3952,7 +4000,9 @@ out:
        if (err && rw)
                btrfs_dec_block_group_ro(rc->block_group);
        iput(rc->data_inode);
+out_put_bg:
        btrfs_put_block_group(rc->block_group);
+       reloc_chunk_end(fs_info);
        free_reloc_control(rc);
        return err;
 }
@@ -4073,6 +4123,12 @@ int btrfs_recover_relocation(struct btrfs_root *root)
                goto out;
        }
 
+       ret = reloc_chunk_start(fs_info);
+       if (ret < 0) {
+               err = ret;
+               goto out_end;
+       }
+
        rc->extent_root = fs_info->extent_root;
 
        set_reloc_control(rc);
@@ -4137,6 +4193,8 @@ out_clean:
                err = ret;
 out_unset:
        unset_reloc_control(rc);
+out_end:
+       reloc_chunk_end(fs_info);
        free_reloc_control(rc);
 out:
        free_reloc_roots(&reloc_roots);