]> Git Repo - linux.git/blobdiff - fs/btrfs/dev-replace.c
enetc: Migrate to PHYLINK and PCS_LYNX
[linux.git] / fs / btrfs / dev-replace.c
index db93909b25e08e1a46219bb84e6575720573477b..e4a1c6afe35dcd9892e4690793fa490221f6df01 100644 (file)
@@ -599,6 +599,37 @@ static void btrfs_rm_dev_replace_unblocked(struct btrfs_fs_info *fs_info)
        wake_up(&fs_info->dev_replace.replace_wait);
 }
 
+/*
+ * When finishing the device replace, before swapping the source device with the
+ * target device we must update the chunk allocation state in the target device,
+ * as it is empty because replace works by directly copying the chunks and not
+ * through the normal chunk allocation path.
+ */
+static int btrfs_set_target_alloc_state(struct btrfs_device *srcdev,
+                                       struct btrfs_device *tgtdev)
+{
+       struct extent_state *cached_state = NULL;
+       u64 start = 0;
+       u64 found_start;
+       u64 found_end;
+       int ret = 0;
+
+       lockdep_assert_held(&srcdev->fs_info->chunk_mutex);
+
+       while (!find_first_extent_bit(&srcdev->alloc_state, start,
+                                     &found_start, &found_end,
+                                     CHUNK_ALLOCATED, &cached_state)) {
+               ret = set_extent_bits(&tgtdev->alloc_state, found_start,
+                                     found_end, CHUNK_ALLOCATED);
+               if (ret)
+                       break;
+               start = found_end + 1;
+       }
+
+       free_extent_state(cached_state);
+       return ret;
+}
+
 static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                                       int scrub_ret)
 {
@@ -673,8 +704,14 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        dev_replace->time_stopped = ktime_get_real_seconds();
        dev_replace->item_needs_writeback = 1;
 
-       /* replace old device with new one in mapping tree */
+       /*
+        * Update allocation state in the new device and replace the old device
+        * with the new one in the mapping tree.
+        */
        if (!scrub_ret) {
+               scrub_ret = btrfs_set_target_alloc_state(src_device, tgt_device);
+               if (scrub_ret)
+                       goto error;
                btrfs_dev_replace_update_device_in_mapping_tree(fs_info,
                                                                src_device,
                                                                tgt_device);
@@ -685,6 +722,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                                 btrfs_dev_name(src_device),
                                 src_device->devid,
                                 rcu_str_deref(tgt_device->name), scrub_ret);
+error:
                up_write(&dev_replace->rwsem);
                mutex_unlock(&fs_info->chunk_mutex);
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
@@ -745,7 +783,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        /* replace the sysfs entry */
        btrfs_sysfs_remove_devices_dir(fs_info->fs_devices, src_device);
        btrfs_sysfs_update_devid(tgt_device);
-       btrfs_rm_dev_replace_free_srcdev(src_device);
+       if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &src_device->dev_state))
+               btrfs_scratch_superblocks(fs_info, src_device->bdev,
+                                         src_device->name->str);
 
        /* write back the superblocks */
        trans = btrfs_start_transaction(root, 0);
@@ -754,6 +794,8 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
 
        mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
 
+       btrfs_rm_dev_replace_free_srcdev(src_device);
+
        return 0;
 }
 
This page took 0.028913 seconds and 4 git commands to generate.