Btrfs: fix wrong device bytes_used in the super block
[linux-2.6-block.git] / fs / btrfs / dev-replace.c
index eea26e1b2fda1d21230dd5f1e01e25f8fb23a3e8..1be03d85d2672e9f533b1a8bae42f5029d3c3d59 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()).
@@ -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;
 }
 
@@ -542,7 +544,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 +552,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;
+       ASSERT(list_empty(&src_device->resized_list));
+       tgt_device->commit_total_bytes = src_device->commit_total_bytes;
        tgt_device->bytes_used = src_device->bytes_used;
+       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);
+       if (src_device->fs_devices->seeding)
+               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,7 +588,6 @@ 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);