btrfs: fix deadlock between chunk allocation and chunk btree modifications
[platform/kernel/linux-rpi.git] / fs / btrfs / volumes.c
index b75ce79..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)
@@ -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);