diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 8ee5645ef9e121595fccf1556190adcae997c974..92e908394403739cb40af4e8128b0d29d8854e14 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3516,6 +3516,10 @@ int btrfs_delayed_refs_qgroup_accounting(struct btrfs_trans_handle *trans, int __get_raid_index(u64 flags); int btrfs_start_write_no_snapshoting(struct btrfs_root *root); void btrfs_end_write_no_snapshoting(struct btrfs_root *root); +void check_system_chunk(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + const u64 type, + const bool is_allocation); /* ctree.c */ int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key, int level, int *slot); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 4e08e47ace300f8429e6add6fbd0ff3ab3b25a75..e78ab29f8f1b23b9943a9e83a2395aee0382aa3e 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4092,7 +4092,7 @@ static int should_alloc_chunk(struct btrfs_root *root, return 1; } -static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type) +static u64 get_profile_num_devs(struct btrfs_root *root, u64 type) { u64 num_dev; @@ -4106,17 +4106,24 @@ static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type) else num_dev = 1; /* DUP or single */ - /* metadata for updaing devices and chunk tree */ - return btrfs_calc_trans_metadata_size(root, num_dev + 1); + return num_dev; } -static void check_system_chunk(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 type) +/* + * If @is_allocation is true, reserve space in the system space info necessary + * for allocating a chunk, otherwise if it's false, reserve space necessary for + * removing a chunk. + */ +void check_system_chunk(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 type, + const bool is_allocation) { struct btrfs_space_info *info; u64 left; u64 thresh; int ret = 0; + u64 num_devs; /* * Needed because we can end up allocating a system chunk and for an @@ -4131,7 +4138,15 @@ static void check_system_chunk(struct btrfs_trans_handle *trans, info->bytes_may_use; spin_unlock(&info->lock); - thresh = get_system_chunk_thresh(root, type); + num_devs = get_profile_num_devs(root, type); + + /* num_devs device items to update and 1 chunk item to add or remove */ + if (is_allocation) + thresh = btrfs_calc_trans_metadata_size(root, num_devs + 1); + else + thresh = btrfs_calc_trans_metadata_size(root, num_devs) + + btrfs_calc_trunc_metadata_size(root, 1); + if (left < thresh && btrfs_test_opt(root, ENOSPC_DEBUG)) { btrfs_info(root->fs_info, "left=%llu, need=%llu, flags=%llu", left, thresh, type); @@ -4243,7 +4258,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans, * Check if we have enough space in SYSTEM chunk because we may need * to update devices. */ - check_system_chunk(trans, extent_root, flags); + check_system_chunk(trans, extent_root, flags, true); ret = btrfs_alloc_chunk(trans, extent_root, flags); trans->allocating_chunk = false; @@ -8905,7 +8920,7 @@ int btrfs_set_block_group_ro(struct btrfs_root *root, if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) { alloc_flags = update_block_group_flags(root, cache->flags); lock_chunks(root->fs_info->chunk_root); - check_system_chunk(trans, root, alloc_flags); + check_system_chunk(trans, root, alloc_flags, true); unlock_chunks(root->fs_info->chunk_root); } mutex_unlock(&root->fs_info->ro_block_group_mutex); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 534be440dd567e55b417dd78e1efda553c7a75ce..d7668756b9d07d8b566d561c8350f463a29cc4b9 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2625,6 +2625,9 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans, return -EINVAL; } map = (struct map_lookup *)em->bdev; + lock_chunks(root->fs_info->chunk_root); + check_system_chunk(trans, extent_root, map->type, false); + unlock_chunks(root->fs_info->chunk_root); for (i = 0; i < map->num_stripes; i++) { struct btrfs_device *device = map->stripes[i].dev;