OSDN Git Service

btrfs: add wrapper for conditional start of exclusive operation
authorDavid Sterba <dsterba@suse.com>
Fri, 14 May 2021 19:32:44 +0000 (21:32 +0200)
committerDavid Sterba <dsterba@suse.com>
Mon, 21 Jun 2021 13:19:07 +0000 (15:19 +0200)
To support optional cancellation of some operations, add helper that will
wrap all the combinations. In normal mode it's same as
btrfs_exclop_start, in cancellation mode it checks if it's already
running and request cancellation and waits until completion.

The error codes can be returned to to user space and semantics is not
changed, adding ECANCELED. This should be evaluated as an error and that
the operation has not completed and the operation should be restarted
or the filesystem status reviewed.

Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ioctl.c

index cacd6ee..572f575 100644 (file)
@@ -1600,6 +1600,48 @@ out_ra:
        return ret;
 }
 
+/*
+ * Try to start exclusive operation @type or cancel it if it's running.
+ *
+ * Return:
+ *   0        - normal mode, newly claimed op started
+ *  >0        - normal mode, something else is running,
+ *              return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS to user space
+ * ECANCELED  - cancel mode, successful cancel
+ * ENOTCONN   - cancel mode, operation not running anymore
+ */
+static int exclop_start_or_cancel_reloc(struct btrfs_fs_info *fs_info,
+                       enum btrfs_exclusive_operation type, bool cancel)
+{
+       if (!cancel) {
+               /* Start normal op */
+               if (!btrfs_exclop_start(fs_info, type))
+                       return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
+               /* Exclusive operation is now claimed */
+               return 0;
+       }
+
+       /* Cancel running op */
+       if (btrfs_exclop_start_try_lock(fs_info, type)) {
+               /*
+                * This blocks any exclop finish from setting it to NONE, so we
+                * request cancellation. Either it runs and we will wait for it,
+                * or it has finished and no waiting will happen.
+                */
+               atomic_inc(&fs_info->reloc_cancel_req);
+               btrfs_exclop_start_unlock(fs_info);
+
+               if (test_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags))
+                       wait_on_bit(&fs_info->flags, BTRFS_FS_RELOC_RUNNING,
+                                   TASK_INTERRUPTIBLE);
+
+               return -ECANCELED;
+       }
+
+       /* Something else is running or none */
+       return -ENOTCONN;
+}
+
 static noinline int btrfs_ioctl_resize(struct file *file,
                                        void __user *arg)
 {