Btrfs: Fix misuse of chunk mutex
[deliverable/linux.git] / fs / btrfs / volumes.c
index 340a92d08e84d9e5268a18fe5f3112465fa47888..105c5fe004db15e9d5f5de8412872a543d14eb50 100644 (file)
@@ -74,6 +74,7 @@ static struct btrfs_fs_devices *__alloc_fs_devices(void)
        mutex_init(&fs_devs->device_list_mutex);
 
        INIT_LIST_HEAD(&fs_devs->devices);
+       INIT_LIST_HEAD(&fs_devs->resized_devices);
        INIT_LIST_HEAD(&fs_devs->alloc_list);
        INIT_LIST_HEAD(&fs_devs->list);
 
@@ -154,11 +155,13 @@ static struct btrfs_device *__alloc_device(void)
 
        INIT_LIST_HEAD(&dev->dev_list);
        INIT_LIST_HEAD(&dev->dev_alloc_list);
+       INIT_LIST_HEAD(&dev->resized_list);
 
        spin_lock_init(&dev->io_lock);
 
        spin_lock_init(&dev->reada_lock);
        atomic_set(&dev->reada_in_flight, 0);
+       atomic_set(&dev->dev_stats_ccnt, 0);
        INIT_RADIX_TREE(&dev->reada_zones, GFP_NOFS & ~__GFP_WAIT);
        INIT_RADIX_TREE(&dev->reada_extents, GFP_NOFS & ~__GFP_WAIT);
 
@@ -474,14 +477,13 @@ static noinline int device_list_add(const char *path,
                        return PTR_ERR(fs_devices);
 
                list_add(&fs_devices->list, &fs_uuids);
-               fs_devices->latest_devid = devid;
-               fs_devices->latest_trans = found_transid;
 
                device = NULL;
        } else {
                device = __find_device(&fs_devices->devices, devid,
                                       disk_super->dev_item.uuid);
        }
+
        if (!device) {
                if (fs_devices->opened)
                        return -EBUSY;
@@ -566,10 +568,6 @@ static noinline int device_list_add(const char *path,
        if (!fs_devices->opened)
                device->generation = found_transid;
 
-       if (found_transid > fs_devices->latest_trans) {
-               fs_devices->latest_devid = devid;
-               fs_devices->latest_trans = found_transid;
-       }
        *fs_devices_ret = fs_devices;
 
        return ret;
@@ -585,8 +583,6 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
        if (IS_ERR(fs_devices))
                return fs_devices;
 
-       fs_devices->latest_devid = orig->latest_devid;
-       fs_devices->latest_trans = orig->latest_trans;
        fs_devices->total_devices = orig->total_devices;
 
        /* We have held the volume lock, it is safe to get the devices. */
@@ -625,10 +621,7 @@ void btrfs_close_extra_devices(struct btrfs_fs_info *fs_info,
                               struct btrfs_fs_devices *fs_devices, int step)
 {
        struct btrfs_device *device, *next;
-
-       struct block_device *latest_bdev = NULL;
-       u64 latest_devid = 0;
-       u64 latest_transid = 0;
+       struct btrfs_device *latest_dev = NULL;
 
        mutex_lock(&uuid_mutex);
 again:
@@ -636,11 +629,9 @@ again:
        list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
                if (device->in_fs_metadata) {
                        if (!device->is_tgtdev_for_dev_replace &&
-                           (!latest_transid ||
-                            device->generation > latest_transid)) {
-                               latest_devid = device->devid;
-                               latest_transid = device->generation;
-                               latest_bdev = device->bdev;
+                           (!latest_dev ||
+                            device->generation > latest_dev->generation)) {
+                               latest_dev = device;
                        }
                        continue;
                }
@@ -682,9 +673,7 @@ again:
                goto again;
        }
 
-       fs_devices->latest_bdev = latest_bdev;
-       fs_devices->latest_devid = latest_devid;
-       fs_devices->latest_trans = latest_transid;
+       fs_devices->latest_bdev = latest_dev->bdev;
 
        mutex_unlock(&uuid_mutex);
 }
@@ -733,8 +722,6 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
                        fs_devices->rw_devices--;
                }
 
-               if (device->can_discard)
-                       fs_devices->num_can_discard--;
                if (device->missing)
                        fs_devices->missing_devices--;
 
@@ -799,11 +786,9 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
        struct block_device *bdev;
        struct list_head *head = &fs_devices->devices;
        struct btrfs_device *device;
-       struct block_device *latest_bdev = NULL;
+       struct btrfs_device *latest_dev = NULL;
        struct buffer_head *bh;
        struct btrfs_super_block *disk_super;
-       u64 latest_devid = 0;
-       u64 latest_transid = 0;
        u64 devid;
        int seeding = 1;
        int ret = 0;
@@ -831,11 +816,9 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
                        goto error_brelse;
 
                device->generation = btrfs_super_generation(disk_super);
-               if (!latest_transid || device->generation > latest_transid) {
-                       latest_devid = devid;
-                       latest_transid = device->generation;
-                       latest_bdev = bdev;
-               }
+               if (!latest_dev ||
+                   device->generation > latest_dev->generation)
+                       latest_dev = device;
 
                if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_SEEDING) {
                        device->writeable = 0;
@@ -845,10 +828,8 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
                }
 
                q = bdev_get_queue(bdev);
-               if (blk_queue_discard(q)) {
+               if (blk_queue_discard(q))
                        device->can_discard = 1;
-                       fs_devices->num_can_discard++;
-               }
 
                device->bdev = bdev;
                device->in_fs_metadata = 0;
@@ -878,9 +859,7 @@ error_brelse:
        }
        fs_devices->seeding = seeding;
        fs_devices->opened = 1;
-       fs_devices->latest_bdev = latest_bdev;
-       fs_devices->latest_devid = latest_devid;
-       fs_devices->latest_trans = latest_transid;
+       fs_devices->latest_bdev = latest_dev->bdev;
        fs_devices->total_rw_bytes = 0;
 out:
        return ret;
@@ -1054,7 +1033,7 @@ int btrfs_account_dev_extents_size(struct btrfs_device *device, u64 start,
                if (key.objectid > device->devid)
                        break;
 
-               if (btrfs_key_type(&key) != BTRFS_DEV_EXTENT_KEY)
+               if (key.type != BTRFS_DEV_EXTENT_KEY)
                        goto next;
 
                dev_extent = btrfs_item_ptr(l, slot, struct btrfs_dev_extent);
@@ -1206,7 +1185,7 @@ again:
                if (key.objectid > device->devid)
                        break;
 
-               if (btrfs_key_type(&key) != BTRFS_DEV_EXTENT_KEY)
+               if (key.type != BTRFS_DEV_EXTENT_KEY)
                        goto next;
 
                if (key.offset > search_start) {
@@ -1285,7 +1264,7 @@ out:
 
 static int btrfs_free_dev_extent(struct btrfs_trans_handle *trans,
                          struct btrfs_device *device,
-                         u64 start)
+                         u64 start, u64 *dev_extent_len)
 {
        int ret;
        struct btrfs_path *path;
@@ -1327,13 +1306,8 @@ again:
                goto out;
        }
 
-       if (device->bytes_used > 0) {
-               u64 len = btrfs_dev_extent_length(leaf, extent);
-               device->bytes_used -= len;
-               spin_lock(&root->fs_info->free_chunk_lock);
-               root->fs_info->free_chunk_space += len;
-               spin_unlock(&root->fs_info->free_chunk_lock);
-       }
+       *dev_extent_len = btrfs_dev_extent_length(leaf, extent);
+
        ret = btrfs_del_item(trans, root, path);
        if (ret) {
                btrfs_error(root->fs_info, ret,
@@ -1483,8 +1457,10 @@ static int btrfs_add_device(struct btrfs_trans_handle *trans,
        btrfs_set_device_io_align(leaf, dev_item, device->io_align);
        btrfs_set_device_io_width(leaf, dev_item, device->io_width);
        btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
-       btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
-       btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
+       btrfs_set_device_total_bytes(leaf, dev_item,
+                                    btrfs_device_get_disk_total_bytes(device));
+       btrfs_set_device_bytes_used(leaf, dev_item,
+                                   btrfs_device_get_bytes_used(device));
        btrfs_set_device_group(leaf, dev_item, 0);
        btrfs_set_device_seek_speed(leaf, dev_item, 0);
        btrfs_set_device_bandwidth(leaf, dev_item, 0);
@@ -1540,7 +1516,6 @@ static int btrfs_rm_dev_item(struct btrfs_root *root,
        key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
        key.type = BTRFS_DEV_ITEM_KEY;
        key.offset = device->devid;
-       lock_chunks(root);
 
        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
        if (ret < 0)
@@ -1556,7 +1531,6 @@ static int btrfs_rm_dev_item(struct btrfs_root *root,
                goto out;
 out:
        btrfs_free_path(path);
-       unlock_chunks(root);
        btrfs_commit_transaction(trans, root);
        return ret;
 }
@@ -1692,11 +1666,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
        if (ret)
                goto error_undo;
 
-       spin_lock(&root->fs_info->free_chunk_lock);
-       root->fs_info->free_chunk_space = device->total_bytes -
-               device->bytes_used;
-       spin_unlock(&root->fs_info->free_chunk_lock);
-
        device->in_fs_metadata = 0;
        btrfs_scrub_cancel_dev(root->fs_info, device);
 
@@ -1750,9 +1719,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
                        fs_devices = fs_devices->seed;
                }
                cur_devices->seed = NULL;
-               lock_chunks(root);
                __btrfs_close_devices(cur_devices);
-               unlock_chunks(root);
                free_fs_devices(cur_devices);
        }
 
@@ -1834,19 +1801,29 @@ error_undo:
 void btrfs_rm_dev_replace_srcdev(struct btrfs_fs_info *fs_info,
                                 struct btrfs_device *srcdev)
 {
+       struct btrfs_fs_devices *fs_devices;
+
        WARN_ON(!mutex_is_locked(&fs_info->fs_devices->device_list_mutex));
 
+       /*
+        * in case of fs with no seed, srcdev->fs_devices will point
+        * to fs_devices of fs_info. However when the dev being replaced is
+        * a seed dev it will point to the seed's local fs_devices. In short
+        * srcdev will have its correct fs_devices in both the cases.
+        */
+       fs_devices = srcdev->fs_devices;
+
        list_del_rcu(&srcdev->dev_list);
        list_del_rcu(&srcdev->dev_alloc_list);
-       fs_info->fs_devices->num_devices--;
+       fs_devices->num_devices--;
        if (srcdev->missing) {
-               fs_info->fs_devices->missing_devices--;
-               fs_info->fs_devices->rw_devices++;
+               fs_devices->missing_devices--;
+               if (!fs_devices->seeding)
+                       fs_devices->rw_devices++;
        }
-       if (srcdev->can_discard)
-               fs_info->fs_devices->num_can_discard--;
+
        if (srcdev->bdev) {
-               fs_info->fs_devices->open_devices--;
+               fs_devices->open_devices--;
 
                /*
                 * zero out the old super if it is not writable
@@ -1857,6 +1834,29 @@ void btrfs_rm_dev_replace_srcdev(struct btrfs_fs_info *fs_info,
        }
 
        call_rcu(&srcdev->rcu, free_device);
+
+       /*
+        * unless fs_devices is seed fs, num_devices shouldn't go
+        * zero
+        */
+       BUG_ON(!fs_devices->num_devices && !fs_devices->seeding);
+
+       /* if this is no devs we rather delete the fs_devices */
+       if (!fs_devices->num_devices) {
+               struct btrfs_fs_devices *tmp_fs_devices;
+
+               tmp_fs_devices = fs_info->fs_devices;
+               while (tmp_fs_devices) {
+                       if (tmp_fs_devices->seed == fs_devices) {
+                               tmp_fs_devices->seed = fs_devices->seed;
+                               break;
+                       }
+                       tmp_fs_devices = tmp_fs_devices->seed;
+               }
+               fs_devices->seed = NULL;
+               __btrfs_close_devices(fs_devices);
+               free_fs_devices(fs_devices);
+       }
 }
 
 void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
@@ -1871,8 +1871,6 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
                fs_info->fs_devices->open_devices--;
        }
        fs_info->fs_devices->num_devices--;
-       if (tgtdev->can_discard)
-               fs_info->fs_devices->num_can_discard++;
 
        next_device = list_entry(fs_info->fs_devices->devices.next,
                                 struct btrfs_device, dev_list);
@@ -1983,17 +1981,17 @@ static int btrfs_prepare_sprout(struct btrfs_root *root)
        mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
        list_splice_init_rcu(&fs_devices->devices, &seed_devices->devices,
                              synchronize_rcu);
+       list_for_each_entry(device, &seed_devices->devices, dev_list)
+               device->fs_devices = seed_devices;
 
+       lock_chunks(root);
        list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list);
-       list_for_each_entry(device, &seed_devices->devices, dev_list) {
-               device->fs_devices = seed_devices;
-       }
+       unlock_chunks(root);
 
        fs_devices->seeding = 0;
        fs_devices->num_devices = 0;
        fs_devices->open_devices = 0;
        fs_devices->missing_devices = 0;
-       fs_devices->num_can_discard = 0;
        fs_devices->rotating = 0;
        fs_devices->seed = seed_devices;
 
@@ -2093,7 +2091,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        struct list_head *devices;
        struct super_block *sb = root->fs_info->sb;
        struct rcu_string *name;
-       u64 total_bytes;
+       u64 tmp;
        int seeding_dev = 0;
        int ret = 0;
 
@@ -2149,8 +2147,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
                goto error;
        }
 
-       lock_chunks(root);
-
        q = bdev_get_queue(bdev);
        if (blk_queue_discard(q))
                device->can_discard = 1;
@@ -2161,6 +2157,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        device->sector_size = root->sectorsize;
        device->total_bytes = i_size_read(bdev->bd_inode);
        device->disk_total_bytes = device->total_bytes;
+       device->commit_total_bytes = device->total_bytes;
        device->dev_root = root->fs_info->dev_root;
        device->bdev = bdev;
        device->in_fs_metadata = 1;
@@ -2178,6 +2175,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        device->fs_devices = root->fs_info->fs_devices;
 
        mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
+       lock_chunks(root);
        list_add_rcu(&device->dev_list, &root->fs_info->fs_devices->devices);
        list_add(&device->dev_alloc_list,
                 &root->fs_info->fs_devices->alloc_list);
@@ -2185,8 +2183,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        root->fs_info->fs_devices->open_devices++;
        root->fs_info->fs_devices->rw_devices++;
        root->fs_info->fs_devices->total_devices++;
-       if (device->can_discard)
-               root->fs_info->fs_devices->num_can_discard++;
        root->fs_info->fs_devices->total_rw_bytes += device->total_bytes;
 
        spin_lock(&root->fs_info->free_chunk_lock);
@@ -2196,26 +2192,45 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        if (!blk_queue_nonrot(bdev_get_queue(bdev)))
                root->fs_info->fs_devices->rotating = 1;
 
-       total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy);
+       tmp = btrfs_super_total_bytes(root->fs_info->super_copy);
        btrfs_set_super_total_bytes(root->fs_info->super_copy,
-                                   total_bytes + device->total_bytes);
+                                   tmp + device->total_bytes);
 
-       total_bytes = btrfs_super_num_devices(root->fs_info->super_copy);
+       tmp = btrfs_super_num_devices(root->fs_info->super_copy);
        btrfs_set_super_num_devices(root->fs_info->super_copy,
-                                   total_bytes + 1);
+                                   tmp + 1);
 
        /* add sysfs device entry */
        btrfs_kobj_add_device(root->fs_info, device);
 
+       /*
+        * we've got more storage, clear any full flags on the space
+        * infos
+        */
+       btrfs_clear_space_info_full(root->fs_info);
+
+       unlock_chunks(root);
        mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
 
        if (seeding_dev) {
-               char fsid_buf[BTRFS_UUID_UNPARSED_SIZE];
+               lock_chunks(root);
                ret = init_first_rw_device(trans, root, device);
+               unlock_chunks(root);
                if (ret) {
                        btrfs_abort_transaction(trans, root, ret);
                        goto error_trans;
                }
+       }
+
+       ret = btrfs_add_device(trans, root, device);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto error_trans;
+       }
+
+       if (seeding_dev) {
+               char fsid_buf[BTRFS_UUID_UNPARSED_SIZE];
+
                ret = btrfs_finish_sprout(trans, root);
                if (ret) {
                        btrfs_abort_transaction(trans, root, ret);
@@ -2229,21 +2244,8 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
                                                root->fs_info->fsid);
                if (kobject_rename(&root->fs_info->super_kobj, fsid_buf))
                        goto error_trans;
-       } else {
-               ret = btrfs_add_device(trans, root, device);
-               if (ret) {
-                       btrfs_abort_transaction(trans, root, ret);
-                       goto error_trans;
-               }
        }
 
-       /*
-        * we've got more storage, clear any full flags on the space
-        * infos
-        */
-       btrfs_clear_space_info_full(root->fs_info);
-
-       unlock_chunks(root);
        root->fs_info->num_tolerated_disk_barrier_failures =
                btrfs_calc_num_tolerated_disk_barrier_failures(root->fs_info);
        ret = btrfs_commit_transaction(trans, root);
@@ -2275,7 +2277,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        return ret;
 
 error_trans:
-       unlock_chunks(root);
        btrfs_end_transaction(trans, root);
        rcu_string_free(device->name);
        btrfs_kobj_rm_device(root->fs_info, device);
@@ -2290,6 +2291,7 @@ error:
 }
 
 int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
+                                 struct btrfs_device *srcdev,
                                  struct btrfs_device **device_out)
 {
        struct request_queue *q;
@@ -2302,24 +2304,38 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
        int ret = 0;
 
        *device_out = NULL;
-       if (fs_info->fs_devices->seeding)
+       if (fs_info->fs_devices->seeding) {
+               btrfs_err(fs_info, "the filesystem is a seed filesystem!");
                return -EINVAL;
+       }
 
        bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL,
                                  fs_info->bdev_holder);
-       if (IS_ERR(bdev))
+       if (IS_ERR(bdev)) {
+               btrfs_err(fs_info, "target device %s is invalid!", device_path);
                return PTR_ERR(bdev);
+       }
 
        filemap_write_and_wait(bdev->bd_inode->i_mapping);
 
        devices = &fs_info->fs_devices->devices;
        list_for_each_entry(device, devices, dev_list) {
                if (device->bdev == bdev) {
+                       btrfs_err(fs_info, "target device is in the filesystem!");
                        ret = -EEXIST;
                        goto error;
                }
        }
 
+
+       if (i_size_read(bdev->bd_inode) <
+           btrfs_device_get_total_bytes(srcdev)) {
+               btrfs_err(fs_info, "target device is smaller than source device!");
+               ret = -EINVAL;
+               goto error;
+       }
+
+
        device = btrfs_alloc_device(NULL, &devid, NULL);
        if (IS_ERR(device)) {
                ret = PTR_ERR(device);
@@ -2343,8 +2359,12 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
        device->io_width = root->sectorsize;
        device->io_align = root->sectorsize;
        device->sector_size = root->sectorsize;
-       device->total_bytes = i_size_read(bdev->bd_inode);
-       device->disk_total_bytes = device->total_bytes;
+       device->total_bytes = btrfs_device_get_total_bytes(srcdev);
+       device->disk_total_bytes = btrfs_device_get_disk_total_bytes(srcdev);
+       device->bytes_used = btrfs_device_get_bytes_used(srcdev);
+       ASSERT(list_empty(&srcdev->resized_list));
+       device->commit_total_bytes = srcdev->commit_total_bytes;
+       device->commit_bytes_used = device->bytes_used;
        device->dev_root = fs_info->dev_root;
        device->bdev = bdev;
        device->in_fs_metadata = 1;
@@ -2356,8 +2376,6 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
        list_add(&device->dev_list, &fs_info->fs_devices->devices);
        fs_info->fs_devices->num_devices++;
        fs_info->fs_devices->open_devices++;
-       if (device->can_discard)
-               fs_info->fs_devices->num_can_discard++;
        mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
 
        *device_out = device;
@@ -2416,8 +2434,10 @@ static noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
        btrfs_set_device_io_align(leaf, dev_item, device->io_align);
        btrfs_set_device_io_width(leaf, dev_item, device->io_width);
        btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
-       btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
-       btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
+       btrfs_set_device_total_bytes(leaf, dev_item,
+                                    btrfs_device_get_disk_total_bytes(device));
+       btrfs_set_device_bytes_used(leaf, dev_item,
+                                   btrfs_device_get_bytes_used(device));
        btrfs_mark_buffer_dirty(leaf);
 
 out:
@@ -2425,40 +2445,44 @@ out:
        return ret;
 }
 
-static int __btrfs_grow_device(struct btrfs_trans_handle *trans,
+int btrfs_grow_device(struct btrfs_trans_handle *trans,
                      struct btrfs_device *device, u64 new_size)
 {
        struct btrfs_super_block *super_copy =
                device->dev_root->fs_info->super_copy;
-       u64 old_total = btrfs_super_total_bytes(super_copy);
-       u64 diff = new_size - device->total_bytes;
+       struct btrfs_fs_devices *fs_devices;
+       u64 old_total;
+       u64 diff;
 
        if (!device->writeable)
                return -EACCES;
+
+       lock_chunks(device->dev_root);
+       old_total = btrfs_super_total_bytes(super_copy);
+       diff = new_size - device->total_bytes;
+
        if (new_size <= device->total_bytes ||
-           device->is_tgtdev_for_dev_replace)
+           device->is_tgtdev_for_dev_replace) {
+               unlock_chunks(device->dev_root);
                return -EINVAL;
+       }
+
+       fs_devices = device->dev_root->fs_info->fs_devices;
 
        btrfs_set_super_total_bytes(super_copy, old_total + diff);
        device->fs_devices->total_rw_bytes += diff;
 
-       device->total_bytes = new_size;
-       device->disk_total_bytes = new_size;
+       btrfs_device_set_total_bytes(device, new_size);
+       btrfs_device_set_disk_total_bytes(device, new_size);
        btrfs_clear_space_info_full(device->dev_root->fs_info);
+       if (list_empty(&device->resized_list))
+               list_add_tail(&device->resized_list,
+                             &fs_devices->resized_devices);
+       unlock_chunks(device->dev_root);
 
        return btrfs_update_device(trans, device);
 }
 
-int btrfs_grow_device(struct btrfs_trans_handle *trans,
-                     struct btrfs_device *device, u64 new_size)
-{
-       int ret;
-       lock_chunks(device->dev_root);
-       ret = __btrfs_grow_device(trans, device, new_size);
-       unlock_chunks(device->dev_root);
-       return ret;
-}
-
 static int btrfs_free_chunk(struct btrfs_trans_handle *trans,
                            struct btrfs_root *root,
                            u64 chunk_tree, u64 chunk_objectid,
@@ -2510,6 +2534,7 @@ static int btrfs_del_sys_chunk(struct btrfs_root *root, u64 chunk_objectid, u64
        u32 cur;
        struct btrfs_key key;
 
+       lock_chunks(root);
        array_size = btrfs_super_sys_array_size(super_copy);
 
        ptr = super_copy->sys_chunk_array;
@@ -2539,6 +2564,7 @@ static int btrfs_del_sys_chunk(struct btrfs_root *root, u64 chunk_objectid, u64
                        cur += len;
                }
        }
+       unlock_chunks(root);
        return ret;
 }
 
@@ -2549,8 +2575,10 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
        struct extent_map_tree *em_tree;
        struct btrfs_root *extent_root;
        struct btrfs_trans_handle *trans;
+       struct btrfs_device *device;
        struct extent_map *em;
        struct map_lookup *map;
+       u64 dev_extent_len = 0;
        int ret;
        int i;
 
@@ -2574,8 +2602,6 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
                return ret;
        }
 
-       lock_chunks(root);
-
        /*
         * step two, delete the device extents and the
         * chunk tree entries
@@ -2589,10 +2615,23 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
        map = (struct map_lookup *)em->bdev;
 
        for (i = 0; i < map->num_stripes; i++) {
-               ret = btrfs_free_dev_extent(trans, map->stripes[i].dev,
-                                           map->stripes[i].physical);
+               device = map->stripes[i].dev;
+               ret = btrfs_free_dev_extent(trans, device,
+                                           map->stripes[i].physical,
+                                           &dev_extent_len);
                BUG_ON(ret);
 
+               if (device->bytes_used > 0) {
+                       lock_chunks(root);
+                       btrfs_device_set_bytes_used(device,
+                                       device->bytes_used - dev_extent_len);
+                       spin_lock(&root->fs_info->free_chunk_lock);
+                       root->fs_info->free_chunk_space += dev_extent_len;
+                       spin_unlock(&root->fs_info->free_chunk_lock);
+                       btrfs_clear_space_info_full(root->fs_info);
+                       unlock_chunks(root);
+               }
+
                if (map->stripes[i].dev) {
                        ret = btrfs_update_device(trans, map->stripes[i].dev);
                        BUG_ON(ret);
@@ -2622,7 +2661,6 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
        /* once for us */
        free_extent_map(em);
 
-       unlock_chunks(root);
        btrfs_end_transaction(trans, root);
        return 0;
 }
@@ -2677,8 +2715,8 @@ again:
                                                   found_key.offset);
                        if (ret == -ENOSPC)
                                failed++;
-                       else if (ret)
-                               BUG();
+                       else
+                               BUG_ON(ret);
                }
 
                if (found_key.offset == 0)
@@ -3085,11 +3123,12 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info)
        /* step one make some room on all the devices */
        devices = &fs_info->fs_devices->devices;
        list_for_each_entry(device, devices, dev_list) {
-               old_size = device->total_bytes;
+               old_size = btrfs_device_get_total_bytes(device);
                size_to_free = div_factor(old_size, 1);
                size_to_free = min(size_to_free, (u64)1 * 1024 * 1024);
                if (!device->writeable ||
-                   device->total_bytes - device->bytes_used > size_to_free ||
+                   btrfs_device_get_total_bytes(device) -
+                   btrfs_device_get_bytes_used(device) > size_to_free ||
                    device->is_tgtdev_for_dev_replace)
                        continue;
 
@@ -3644,8 +3683,6 @@ static int btrfs_uuid_scan_kthread(void *data)
        max_key.type = BTRFS_ROOT_ITEM_KEY;
        max_key.offset = (u64)-1;
 
-       path->keep_locks = 1;
-
        while (1) {
                ret = btrfs_search_forward(root, &key, path, 0);
                if (ret) {
@@ -3897,8 +3934,8 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
        struct btrfs_key key;
        struct btrfs_super_block *super_copy = root->fs_info->super_copy;
        u64 old_total = btrfs_super_total_bytes(super_copy);
-       u64 old_size = device->total_bytes;
-       u64 diff = device->total_bytes - new_size;
+       u64 old_size = btrfs_device_get_total_bytes(device);
+       u64 diff = old_size - new_size;
 
        if (device->is_tgtdev_for_dev_replace)
                return -EINVAL;
@@ -3911,7 +3948,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
 
        lock_chunks(root);
 
-       device->total_bytes = new_size;
+       btrfs_device_set_total_bytes(device, new_size);
        if (device->writeable) {
                device->fs_devices->total_rw_bytes -= diff;
                spin_lock(&root->fs_info->free_chunk_lock);
@@ -3977,7 +4014,7 @@ again:
                ret = -ENOSPC;
                lock_chunks(root);
 
-               device->total_bytes = old_size;
+               btrfs_device_set_total_bytes(device, old_size);
                if (device->writeable)
                        device->fs_devices->total_rw_bytes += diff;
                spin_lock(&root->fs_info->free_chunk_lock);
@@ -3995,18 +4032,17 @@ again:
        }
 
        lock_chunks(root);
+       btrfs_device_set_disk_total_bytes(device, new_size);
+       if (list_empty(&device->resized_list))
+               list_add_tail(&device->resized_list,
+                             &root->fs_info->fs_devices->resized_devices);
 
-       device->disk_total_bytes = new_size;
-       /* Now btrfs_update_device() will change the on-disk size. */
-       ret = btrfs_update_device(trans, device);
-       if (ret) {
-               unlock_chunks(root);
-               btrfs_end_transaction(trans, root);
-               goto done;
-       }
        WARN_ON(diff > old_total);
        btrfs_set_super_total_bytes(super_copy, old_total - diff);
        unlock_chunks(root);
+
+       /* Now btrfs_update_device() will change the on-disk size. */
+       ret = btrfs_update_device(trans, device);
        btrfs_end_transaction(trans, root);
 done:
        btrfs_free_path(path);
@@ -4022,10 +4058,13 @@ static int btrfs_add_system_chunk(struct btrfs_root *root,
        u32 array_size;
        u8 *ptr;
 
+       lock_chunks(root);
        array_size = btrfs_super_sys_array_size(super_copy);
        if (array_size + item_size + sizeof(disk_key)
-                       > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE)
+                       > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
+               unlock_chunks(root);
                return -EFBIG;
+       }
 
        ptr = super_copy->sys_chunk_array + array_size;
        btrfs_cpu_key_to_disk(&disk_key, key);
@@ -4034,6 +4073,8 @@ static int btrfs_add_system_chunk(struct btrfs_root *root,
        memcpy(ptr, chunk, item_size);
        item_size += sizeof(disk_key);
        btrfs_set_super_sys_array_size(super_copy, array_size + item_size);
+       unlock_chunks(root);
+
        return 0;
 }
 
@@ -4403,6 +4444,16 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
        if (ret)
                goto error_del_extent;
 
+       for (i = 0; i < map->num_stripes; i++) {
+               num_bytes = map->stripes[i].dev->bytes_used + stripe_size;
+               btrfs_device_set_bytes_used(map->stripes[i].dev, num_bytes);
+       }
+
+       spin_lock(&extent_root->fs_info->free_chunk_lock);
+       extent_root->fs_info->free_chunk_space -= (stripe_size *
+                                                  map->num_stripes);
+       spin_unlock(&extent_root->fs_info->free_chunk_lock);
+
        free_extent_map(em);
        check_raid56_incompat_flag(extent_root->fs_info, type);
 
@@ -4474,7 +4525,6 @@ int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans,
                device = map->stripes[i].dev;
                dev_offset = map->stripes[i].physical;
 
-               device->bytes_used += stripe_size;
                ret = btrfs_update_device(trans, device);
                if (ret)
                        goto out;
@@ -4487,11 +4537,6 @@ int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans,
                        goto out;
        }
 
-       spin_lock(&extent_root->fs_info->free_chunk_lock);
-       extent_root->fs_info->free_chunk_space -= (stripe_size *
-                                                  map->num_stripes);
-       spin_unlock(&extent_root->fs_info->free_chunk_lock);
-
        stripe = &chunk->stripe;
        for (i = 0; i < map->num_stripes; i++) {
                device = map->stripes[i].dev;
@@ -4571,16 +4616,25 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
        alloc_profile = btrfs_get_alloc_profile(fs_info->chunk_root, 0);
        ret = __btrfs_alloc_chunk(trans, extent_root, sys_chunk_offset,
                                  alloc_profile);
-       if (ret) {
-               btrfs_abort_transaction(trans, root, ret);
-               goto out;
+       return ret;
+}
+
+static inline int btrfs_chunk_max_errors(struct map_lookup *map)
+{
+       int max_errors;
+
+       if (map->type & (BTRFS_BLOCK_GROUP_RAID1 |
+                        BTRFS_BLOCK_GROUP_RAID10 |
+                        BTRFS_BLOCK_GROUP_RAID5 |
+                        BTRFS_BLOCK_GROUP_DUP)) {
+               max_errors = 1;
+       } else if (map->type & BTRFS_BLOCK_GROUP_RAID6) {
+               max_errors = 2;
+       } else {
+               max_errors = 0;
        }
 
-       ret = btrfs_add_device(trans, fs_info->chunk_root, device);
-       if (ret)
-               btrfs_abort_transaction(trans, root, ret);
-out:
-       return ret;
+       return max_errors;
 }
 
 int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
@@ -4589,6 +4643,7 @@ int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
        struct map_lookup *map;
        struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
        int readonly = 0;
+       int miss_ndevs = 0;
        int i;
 
        read_lock(&map_tree->map_tree.lock);
@@ -4597,18 +4652,27 @@ int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
        if (!em)
                return 1;
 
-       if (btrfs_test_opt(root, DEGRADED)) {
-               free_extent_map(em);
-               return 0;
-       }
-
        map = (struct map_lookup *)em->bdev;
        for (i = 0; i < map->num_stripes; i++) {
+               if (map->stripes[i].dev->missing) {
+                       miss_ndevs++;
+                       continue;
+               }
+
                if (!map->stripes[i].dev->writeable) {
                        readonly = 1;
-                       break;
+                       goto end;
                }
        }
+
+       /*
+        * If the number of missing devices is larger than max errors,
+        * we can not write the data into that chunk successfully, so
+        * set it readonly.
+        */
+       if (miss_ndevs > btrfs_chunk_max_errors(map))
+               readonly = 1;
+end:
        free_extent_map(em);
        return readonly;
 }
@@ -5219,16 +5283,8 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
                }
        }
 
-       if (rw & (REQ_WRITE | REQ_GET_READ_MIRRORS)) {
-               if (map->type & (BTRFS_BLOCK_GROUP_RAID1 |
-                                BTRFS_BLOCK_GROUP_RAID10 |
-                                BTRFS_BLOCK_GROUP_RAID5 |
-                                BTRFS_BLOCK_GROUP_DUP)) {
-                       max_errors = 1;
-               } else if (map->type & BTRFS_BLOCK_GROUP_RAID6) {
-                       max_errors = 2;
-               }
-       }
+       if (rw & (REQ_WRITE | REQ_GET_READ_MIRRORS))
+               max_errors = btrfs_chunk_max_errors(map);
 
        if (dev_replace_is_ongoing && (rw & (REQ_WRITE | REQ_DISCARD)) &&
            dev_replace->tgtdev != NULL) {
@@ -5957,7 +6013,9 @@ static void fill_device_from_item(struct extent_buffer *leaf,
        device->devid = btrfs_device_id(leaf, dev_item);
        device->disk_total_bytes = btrfs_device_total_bytes(leaf, dev_item);
        device->total_bytes = device->disk_total_bytes;
+       device->commit_total_bytes = device->disk_total_bytes;
        device->bytes_used = btrfs_device_bytes_used(leaf, dev_item);
+       device->commit_bytes_used = device->bytes_used;
        device->type = btrfs_device_type(leaf, dev_item);
        device->io_align = btrfs_device_io_align(leaf, dev_item);
        device->io_width = btrfs_device_io_width(leaf, dev_item);
@@ -6374,16 +6432,18 @@ int btrfs_run_dev_stats(struct btrfs_trans_handle *trans,
        struct btrfs_root *dev_root = fs_info->dev_root;
        struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
        struct btrfs_device *device;
+       int stats_cnt;
        int ret = 0;
 
        mutex_lock(&fs_devices->device_list_mutex);
        list_for_each_entry(device, &fs_devices->devices, dev_list) {
-               if (!device->dev_stats_valid || !device->dev_stats_dirty)
+               if (!device->dev_stats_valid || !btrfs_dev_stats_dirty(device))
                        continue;
 
+               stats_cnt = atomic_read(&device->dev_stats_ccnt);
                ret = update_dev_stat_item(trans, dev_root, device);
                if (!ret)
-                       device->dev_stats_dirty = 0;
+                       atomic_sub(stats_cnt, &device->dev_stats_ccnt);
        }
        mutex_unlock(&fs_devices->device_list_mutex);
 
@@ -6482,3 +6542,51 @@ int btrfs_scratch_superblock(struct btrfs_device *device)
 
        return 0;
 }
+
+/*
+ * Update the size of all devices, which is used for writing out the
+ * super blocks.
+ */
+void btrfs_update_commit_device_size(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+       struct btrfs_device *curr, *next;
+
+       if (list_empty(&fs_devices->resized_devices))
+               return;
+
+       mutex_lock(&fs_devices->device_list_mutex);
+       lock_chunks(fs_info->dev_root);
+       list_for_each_entry_safe(curr, next, &fs_devices->resized_devices,
+                                resized_list) {
+               list_del_init(&curr->resized_list);
+               curr->commit_total_bytes = curr->disk_total_bytes;
+       }
+       unlock_chunks(fs_info->dev_root);
+       mutex_unlock(&fs_devices->device_list_mutex);
+}
+
+/* Must be invoked during the transaction commit */
+void btrfs_update_commit_device_bytes_used(struct btrfs_root *root,
+                                       struct btrfs_transaction *transaction)
+{
+       struct extent_map *em;
+       struct map_lookup *map;
+       struct btrfs_device *dev;
+       int i;
+
+       if (list_empty(&transaction->pending_chunks))
+               return;
+
+       /* In order to kick the device replace finish process */
+       lock_chunks(root);
+       list_for_each_entry(em, &transaction->pending_chunks, list) {
+               map = (struct map_lookup *)em->bdev;
+
+               for (i = 0; i < map->num_stripes; i++) {
+                       dev = map->stripes[i].dev;
+                       dev->commit_bytes_used = dev->bytes_used;
+               }
+       }
+       unlock_chunks(root);
+}
This page took 0.048225 seconds and 5 git commands to generate.