btrfs: fix deadlock between chunk allocation and chunk btree modifications
[platform/kernel/linux-rpi.git] / fs / btrfs / volumes.c
index 378e03a..fa68efd 100644 (file)
@@ -1879,8 +1879,10 @@ static int btrfs_add_dev_item(struct btrfs_trans_handle *trans,
        key.type = BTRFS_DEV_ITEM_KEY;
        key.offset = device->devid;
 
+       btrfs_reserve_chunk_metadata(trans, true);
        ret = btrfs_insert_empty_item(trans, trans->fs_info->chunk_root, path,
                                      &key, sizeof(*dev_item));
+       btrfs_trans_release_chunk_metadata(trans);
        if (ret)
                goto out;
 
@@ -1957,7 +1959,9 @@ static int btrfs_rm_dev_item(struct btrfs_device *device)
        key.type = BTRFS_DEV_ITEM_KEY;
        key.offset = device->devid;
 
+       btrfs_reserve_chunk_metadata(trans, false);
        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+       btrfs_trans_release_chunk_metadata(trans);
        if (ret) {
                if (ret > 0)
                        ret = -ENOENT;
@@ -2513,7 +2517,9 @@ static int btrfs_finish_sprout(struct btrfs_trans_handle *trans)
        key.type = BTRFS_DEV_ITEM_KEY;
 
        while (1) {
+               btrfs_reserve_chunk_metadata(trans, false);
                ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+               btrfs_trans_release_chunk_metadata(trans);
                if (ret < 0)
                        goto error;
 
@@ -2861,6 +2867,7 @@ int btrfs_grow_device(struct btrfs_trans_handle *trans,
        struct btrfs_super_block *super_copy = fs_info->super_copy;
        u64 old_total;
        u64 diff;
+       int ret;
 
        if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state))
                return -EACCES;
@@ -2889,7 +2896,11 @@ int btrfs_grow_device(struct btrfs_trans_handle *trans,
                              &trans->transaction->dev_update_list);
        mutex_unlock(&fs_info->chunk_mutex);
 
-       return btrfs_update_device(trans, device);
+       btrfs_reserve_chunk_metadata(trans, false);
+       ret = btrfs_update_device(trans, device);
+       btrfs_trans_release_chunk_metadata(trans);
+
+       return ret;
 }
 
 static int btrfs_free_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset)
@@ -3131,7 +3142,7 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset)
                const u64 sys_flags = btrfs_system_alloc_profile(fs_info);
                struct btrfs_block_group *sys_bg;
 
-               sys_bg = btrfs_alloc_chunk(trans, sys_flags);
+               sys_bg = btrfs_create_chunk(trans, sys_flags);
                if (IS_ERR(sys_bg)) {
                        ret = PTR_ERR(sys_bg);
                        btrfs_abort_transaction(trans, ret);
@@ -4926,8 +4937,10 @@ again:
                        round_down(old_total - diff, fs_info->sectorsize));
        mutex_unlock(&fs_info->chunk_mutex);
 
+       btrfs_reserve_chunk_metadata(trans, false);
        /* Now btrfs_update_device() will change the on-disk size. */
        ret = btrfs_update_device(trans, device);
+       btrfs_trans_release_chunk_metadata(trans);
        if (ret < 0) {
                btrfs_abort_transaction(trans, ret);
                btrfs_end_transaction(trans);
@@ -5010,7 +5023,7 @@ static void check_raid1c34_incompat_flag(struct btrfs_fs_info *info, u64 type)
 }
 
 /*
- * Structure used internally for __btrfs_alloc_chunk() function.
+ * Structure used internally for btrfs_create_chunk() function.
  * Wraps needed parameters.
  */
 struct alloc_chunk_ctl {
@@ -5414,7 +5427,7 @@ error_del_extent:
        return block_group;
 }
 
-struct btrfs_block_group *btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
+struct btrfs_block_group *btrfs_create_chunk(struct btrfs_trans_handle *trans,
                                            u64 type)
 {
        struct btrfs_fs_info *info = trans->fs_info;
@@ -5615,12 +5628,12 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans)
         */
 
        alloc_profile = btrfs_metadata_alloc_profile(fs_info);
-       meta_bg = btrfs_alloc_chunk(trans, alloc_profile);
+       meta_bg = btrfs_create_chunk(trans, alloc_profile);
        if (IS_ERR(meta_bg))
                return PTR_ERR(meta_bg);
 
        alloc_profile = btrfs_system_alloc_profile(fs_info);
-       sys_bg = btrfs_alloc_chunk(trans, alloc_profile);
+       sys_bg = btrfs_create_chunk(trans, alloc_profile);
        if (IS_ERR(sys_bg))
                return PTR_ERR(sys_bg);