]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/btrfs/dev-replace.c
Merge tag 'stable/for-linus-3.15-tag2' of git://git.kernel.org/pub/scm/linux/kernel...
[karo-tx-linux.git] / fs / btrfs / dev-replace.c
index b20d59e5e5dd4ffa1db428e81320c46099f72ea9..9f2290509acaeb105e3097831be8401b9a659dbe 100644 (file)
@@ -431,6 +431,35 @@ leave_no_lock:
        return ret;
 }
 
+/*
+ * blocked until all flighting bios are finished.
+ */
+static void btrfs_rm_dev_replace_blocked(struct btrfs_fs_info *fs_info)
+{
+       s64 writers;
+       DEFINE_WAIT(wait);
+
+       set_bit(BTRFS_FS_STATE_DEV_REPLACING, &fs_info->fs_state);
+       do {
+               prepare_to_wait(&fs_info->replace_wait, &wait,
+                               TASK_UNINTERRUPTIBLE);
+               writers = percpu_counter_sum(&fs_info->bio_counter);
+               if (writers)
+                       schedule();
+               finish_wait(&fs_info->replace_wait, &wait);
+       } while (writers);
+}
+
+/*
+ * we have removed target device, it is safe to allow new bios request.
+ */
+static void btrfs_rm_dev_replace_unblocked(struct btrfs_fs_info *fs_info)
+{
+       clear_bit(BTRFS_FS_STATE_DEV_REPLACING, &fs_info->fs_state);
+       if (waitqueue_active(&fs_info->replace_wait))
+               wake_up(&fs_info->replace_wait);
+}
+
 static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                                       int scrub_ret)
 {
@@ -458,17 +487,11 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        src_device = dev_replace->srcdev;
        btrfs_dev_replace_unlock(dev_replace);
 
-       /* replace old device with new one in mapping tree */
-       if (!scrub_ret)
-               btrfs_dev_replace_update_device_in_mapping_tree(fs_info,
-                                                               src_device,
-                                                               tgt_device);
-
        /*
         * flush all outstanding I/O and inode extent mappings before the
         * copy operation is declared as being finished
         */
-       ret = btrfs_start_delalloc_roots(root->fs_info, 0);
+       ret = btrfs_start_delalloc_roots(root->fs_info, 0, -1);
        if (ret) {
                mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
                return ret;
@@ -495,7 +518,12 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        dev_replace->time_stopped = get_seconds();
        dev_replace->item_needs_writeback = 1;
 
-       if (scrub_ret) {
+       /* replace old device with new one in mapping tree */
+       if (!scrub_ret) {
+               btrfs_dev_replace_update_device_in_mapping_tree(fs_info,
+                                                               src_device,
+                                                               tgt_device);
+       } else {
                printk_in_rcu(KERN_ERR
                              "BTRFS: btrfs_scrub_dev(%s, %llu, %s) failed %d\n",
                              src_device->missing ? "<missing disk>" :
@@ -534,8 +562,12 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                fs_info->fs_devices->latest_bdev = tgt_device->bdev;
        list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list);
 
+       btrfs_rm_dev_replace_blocked(fs_info);
+
        btrfs_rm_dev_replace_srcdev(fs_info, src_device);
 
+       btrfs_rm_dev_replace_unblocked(fs_info);
+
        /*
         * this is again a consistent state where no dev_replace procedure
         * is running, the target device is part of the filesystem, the
@@ -865,3 +897,31 @@ void btrfs_dev_replace_unlock(struct btrfs_dev_replace *dev_replace)
                mutex_unlock(&dev_replace->lock_management_lock);
        }
 }
+
+void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info)
+{
+       percpu_counter_inc(&fs_info->bio_counter);
+}
+
+void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info)
+{
+       percpu_counter_dec(&fs_info->bio_counter);
+
+       if (waitqueue_active(&fs_info->replace_wait))
+               wake_up(&fs_info->replace_wait);
+}
+
+void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info)
+{
+       DEFINE_WAIT(wait);
+again:
+       percpu_counter_inc(&fs_info->bio_counter);
+       if (test_bit(BTRFS_FS_STATE_DEV_REPLACING, &fs_info->fs_state)) {
+               btrfs_bio_counter_dec(fs_info);
+               wait_event(fs_info->replace_wait,
+                          !test_bit(BTRFS_FS_STATE_DEV_REPLACING,
+                                    &fs_info->fs_state));
+               goto again;
+       }
+
+}