Merge tag 'qcom-dt-for-3.18-3' of git://git.kernel.org/pub/scm/linux/kernel/git/galak...
[cascardo/linux.git] / fs / btrfs / dev-replace.c
index eea26e1..6f662b3 100644 (file)
@@ -168,8 +168,12 @@ no_valid_dev_replace_entry_found:
                                        dev_replace->srcdev->total_bytes;
                                dev_replace->tgtdev->disk_total_bytes =
                                        dev_replace->srcdev->disk_total_bytes;
+                               dev_replace->tgtdev->commit_total_bytes =
+                                       dev_replace->srcdev->commit_total_bytes;
                                dev_replace->tgtdev->bytes_used =
                                        dev_replace->srcdev->bytes_used;
+                               dev_replace->tgtdev->commit_bytes_used =
+                                       dev_replace->srcdev->commit_bytes_used;
                        }
                        dev_replace->tgtdev->is_tgtdev_for_dev_replace = 1;
                        btrfs_init_dev_replace_tgtdev_for_resume(fs_info,
@@ -329,30 +333,34 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
            args->start.tgtdev_name[0] == '\0')
                return -EINVAL;
 
-       mutex_lock(&fs_info->volume_mutex);
-       ret = btrfs_init_dev_replace_tgtdev(root, args->start.tgtdev_name,
-                                           &tgt_device);
-       if (ret) {
-               btrfs_err(fs_info, "target device %s is invalid!",
-                      args->start.tgtdev_name);
-               mutex_unlock(&fs_info->volume_mutex);
-               return -EINVAL;
+       /*
+        * Here we commit the transaction to make sure commit_total_bytes
+        * of all the devices are updated.
+        */
+       trans = btrfs_attach_transaction(root);
+       if (!IS_ERR(trans)) {
+               ret = btrfs_commit_transaction(trans, root);
+               if (ret)
+                       return ret;
+       } else if (PTR_ERR(trans) != -ENOENT) {
+               return PTR_ERR(trans);
        }
 
+       /* the disk copy procedure reuses the scrub code */
+       mutex_lock(&fs_info->volume_mutex);
        ret = btrfs_dev_replace_find_srcdev(root, args->start.srcdevid,
                                            args->start.srcdev_name,
                                            &src_device);
-       mutex_unlock(&fs_info->volume_mutex);
        if (ret) {
-               ret = -EINVAL;
-               goto leave_no_lock;
+               mutex_unlock(&fs_info->volume_mutex);
+               return ret;
        }
 
-       if (tgt_device->total_bytes < src_device->total_bytes) {
-               btrfs_err(fs_info, "target device is smaller than source device!");
-               ret = -EINVAL;
-               goto leave_no_lock;
-       }
+       ret = btrfs_init_dev_replace_tgtdev(root, args->start.tgtdev_name,
+                                           src_device, &tgt_device);
+       mutex_unlock(&fs_info->volume_mutex);
+       if (ret)
+               return ret;
 
        btrfs_dev_replace_lock(dev_replace);
        switch (dev_replace->replace_state) {
@@ -380,10 +388,6 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
                      src_device->devid,
                      rcu_str_deref(tgt_device->name));
 
-       tgt_device->total_bytes = src_device->total_bytes;
-       tgt_device->disk_total_bytes = src_device->disk_total_bytes;
-       tgt_device->bytes_used = src_device->bytes_used;
-
        /*
         * from now on, the writes to the srcdev are all duplicated to
         * go to the tgtdev as well (refer to btrfs_map_block()).
@@ -414,7 +418,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
 
        /* the disk copy procedure reuses the scrub code */
        ret = btrfs_scrub_dev(fs_info, src_device->devid, 0,
-                             src_device->total_bytes,
+                             btrfs_device_get_total_bytes(src_device),
                              &dev_replace->scrub_progress, 0, 1);
 
        ret = btrfs_dev_replace_finishing(root->fs_info, ret);
@@ -426,9 +430,7 @@ leave:
        dev_replace->srcdev = NULL;
        dev_replace->tgtdev = NULL;
        btrfs_dev_replace_unlock(dev_replace);
-leave_no_lock:
-       if (tgt_device)
-               btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
+       btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
        return ret;
 }
 
@@ -507,9 +509,10 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        ret = btrfs_commit_transaction(trans, root);
        WARN_ON(ret);
 
+       mutex_lock(&uuid_mutex);
        /* keep away write_all_supers() during the finishing procedure */
-       mutex_lock(&root->fs_info->chunk_mutex);
        mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
+       mutex_lock(&root->fs_info->chunk_mutex);
        btrfs_dev_replace_lock(dev_replace);
        dev_replace->replace_state =
                scrub_ret ? BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED
@@ -532,8 +535,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                              src_device->devid,
                              rcu_str_deref(tgt_device->name), scrub_ret);
                btrfs_dev_replace_unlock(dev_replace);
-               mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
                mutex_unlock(&root->fs_info->chunk_mutex);
+               mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
+               mutex_unlock(&uuid_mutex);
                if (tgt_device)
                        btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
                mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
@@ -542,7 +546,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        }
 
        printk_in_rcu(KERN_INFO
-                     "BTRFS: dev_replace from %s (devid %llu) to %s) finished\n",
+                     "BTRFS: dev_replace from %s (devid %llu) to %s finished\n",
                      src_device->missing ? "<missing disk>" :
                        rcu_str_deref(src_device->name),
                      src_device->devid,
@@ -550,23 +554,29 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        tgt_device->is_tgtdev_for_dev_replace = 0;
        tgt_device->devid = src_device->devid;
        src_device->devid = BTRFS_DEV_REPLACE_DEVID;
-       tgt_device->bytes_used = src_device->bytes_used;
        memcpy(uuid_tmp, tgt_device->uuid, sizeof(uuid_tmp));
        memcpy(tgt_device->uuid, src_device->uuid, sizeof(tgt_device->uuid));
        memcpy(src_device->uuid, uuid_tmp, sizeof(src_device->uuid));
-       tgt_device->total_bytes = src_device->total_bytes;
-       tgt_device->disk_total_bytes = src_device->disk_total_bytes;
-       tgt_device->bytes_used = src_device->bytes_used;
+       btrfs_device_set_total_bytes(tgt_device, src_device->total_bytes);
+       btrfs_device_set_disk_total_bytes(tgt_device,
+                                         src_device->disk_total_bytes);
+       btrfs_device_set_bytes_used(tgt_device, src_device->bytes_used);
+       ASSERT(list_empty(&src_device->resized_list));
+       tgt_device->commit_total_bytes = src_device->commit_total_bytes;
+       tgt_device->commit_bytes_used = src_device->bytes_used;
        if (fs_info->sb->s_bdev == src_device->bdev)
                fs_info->sb->s_bdev = tgt_device->bdev;
        if (fs_info->fs_devices->latest_bdev == src_device->bdev)
                fs_info->fs_devices->latest_bdev = tgt_device->bdev;
        list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list);
+       fs_info->fs_devices->rw_devices++;
 
        /* replace the sysfs entry */
        btrfs_kobj_rm_device(fs_info, src_device);
        btrfs_kobj_add_device(fs_info, tgt_device);
 
+       btrfs_dev_replace_unlock(dev_replace);
+
        btrfs_rm_dev_replace_blocked(fs_info);
 
        btrfs_rm_dev_replace_srcdev(fs_info, src_device);
@@ -580,9 +590,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
         * superblock is scratched out so that it is no longer marked to
         * belong to this filesystem.
         */
-       btrfs_dev_replace_unlock(dev_replace);
-       mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
        mutex_unlock(&root->fs_info->chunk_mutex);
+       mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
+       mutex_unlock(&uuid_mutex);
 
        /* write back the superblocks */
        trans = btrfs_start_transaction(root, 0);
@@ -643,6 +653,7 @@ void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info,
                              struct btrfs_ioctl_dev_replace_args *args)
 {
        struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
+       struct btrfs_device *srcdev;
 
        btrfs_dev_replace_lock(dev_replace);
        /* even if !dev_replace_is_valid, the values are good enough for
@@ -665,8 +676,9 @@ void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info,
                break;
        case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
        case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
+               srcdev = dev_replace->srcdev;
                args->status.progress_1000 = div64_u64(dev_replace->cursor_left,
-                       div64_u64(dev_replace->srcdev->total_bytes, 1000));
+                       div64_u64(btrfs_device_get_total_bytes(srcdev), 1000));
                break;
        }
        btrfs_dev_replace_unlock(dev_replace);
@@ -825,7 +837,7 @@ static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info)
 
        ret = btrfs_scrub_dev(fs_info, dev_replace->srcdev->devid,
                              dev_replace->committed_cursor_left,
-                             dev_replace->srcdev->total_bytes,
+                             btrfs_device_get_total_bytes(dev_replace->srcdev),
                              &dev_replace->scrub_progress, 0, 1);
        ret = btrfs_dev_replace_finishing(fs_info, ret);
        WARN_ON(ret);