Skip to content

Commit 39c2d7f

Browse files
fdmananamasoncl
authored andcommitted
Btrfs: fix -ENOSPC on block group removal
Unlike when attempting to allocate a new block group, where we check that we have enough space in the system space_info to update the device items and insert a new chunk item in the chunk tree, we were not checking if the system space_info had enough space for updating the device items and deleting the chunk item in the chunk tree. This often lead to -ENOSPC error when attempting to allocate blocks for the chunk tree (during btree node/leaf COW operations) while updating the device items or deleting the chunk item, which resulted in the current transaction being aborted and turning the filesystem into read-only mode. While running fstests generic/038, which stresses allocation of block groups and removal of unused block groups, with a large scratch device (750Gb) this happened often, despite more than enough unallocated space, and resulted in the following trace: [68663.586604] WARNING: CPU: 3 PID: 1521 at fs/btrfs/super.c:260 __btrfs_abort_transaction+0x52/0x114 [btrfs]() [68663.600407] BTRFS: Transaction aborted (error -28) (...) [68663.730829] Call Trace: [68663.732585] [<ffffffff8142fa46>] dump_stack+0x4f/0x7b [68663.734334] [<ffffffff8108b6a2>] ? console_unlock+0x361/0x3ad [68663.739980] [<ffffffff81045ea5>] warn_slowpath_common+0xa1/0xbb [68663.757153] [<ffffffffa036ca6d>] ? __btrfs_abort_transaction+0x52/0x114 [btrfs] [68663.760925] [<ffffffff81045f05>] warn_slowpath_fmt+0x46/0x48 [68663.762854] [<ffffffffa03b159d>] ? btrfs_update_device+0x15a/0x16c [btrfs] [68663.764073] [<ffffffffa036ca6d>] __btrfs_abort_transaction+0x52/0x114 [btrfs] [68663.765130] [<ffffffffa03b3638>] btrfs_remove_chunk+0x597/0x5ee [btrfs] [68663.765998] [<ffffffffa0384663>] ? btrfs_delete_unused_bgs+0x245/0x296 [btrfs] [68663.767068] [<ffffffffa0384676>] btrfs_delete_unused_bgs+0x258/0x296 [btrfs] [68663.768227] [<ffffffff8143527f>] ? _raw_spin_unlock_irq+0x2d/0x4c [68663.769081] [<ffffffffa038b109>] cleaner_kthread+0x13d/0x16c [btrfs] [68663.799485] [<ffffffffa038afcc>] ? btrfs_alloc_root+0x28/0x28 [btrfs] [68663.809208] [<ffffffff8105f367>] kthread+0xef/0xf7 [68663.828795] [<ffffffff810e603f>] ? time_hardirqs_on+0x15/0x28 [68663.844942] [<ffffffff8105f278>] ? __kthread_parkme+0xad/0xad [68663.846486] [<ffffffff81435a88>] ret_from_fork+0x58/0x90 [68663.847760] [<ffffffff8105f278>] ? __kthread_parkme+0xad/0xad [68663.849503] ---[ end trace 798477c6d6dbaad6 ]--- [68663.850525] BTRFS: error (device sdc) in btrfs_remove_chunk:2652: errno=-28 No space left So fix this by verifying that enough space exists in system space_info, and reserving the space in the chunk block reserve, before attempting to delete the block group and allocate a new system chunk if we don't have enough space to perform the necessary updates and delete in the chunk tree. Like for the block group creation case, we don't error our if we fail to allocate a new system chunk, since we might end up not needing it (no node/leaf splits happen during the COW operations and/or we end up not needing to COW any btree nodes or leafs because they were already COWed in the current transaction and their writeback didn't start yet). Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
1 parent 4fbcdf6 commit 39c2d7f

File tree

3 files changed

+30
-8
lines changed

3 files changed

+30
-8
lines changed

fs/btrfs/ctree.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3516,6 +3516,10 @@ int btrfs_delayed_refs_qgroup_accounting(struct btrfs_trans_handle *trans,
35163516
int __get_raid_index(u64 flags);
35173517
int btrfs_start_write_no_snapshoting(struct btrfs_root *root);
35183518
void btrfs_end_write_no_snapshoting(struct btrfs_root *root);
3519+
void check_system_chunk(struct btrfs_trans_handle *trans,
3520+
struct btrfs_root *root,
3521+
const u64 type,
3522+
const bool is_allocation);
35193523
/* ctree.c */
35203524
int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
35213525
int level, int *slot);

fs/btrfs/extent-tree.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4092,7 +4092,7 @@ static int should_alloc_chunk(struct btrfs_root *root,
40924092
return 1;
40934093
}
40944094

4095-
static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type)
4095+
static u64 get_profile_num_devs(struct btrfs_root *root, u64 type)
40964096
{
40974097
u64 num_dev;
40984098

@@ -4106,17 +4106,24 @@ static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type)
41064106
else
41074107
num_dev = 1; /* DUP or single */
41084108

4109-
/* metadata for updaing devices and chunk tree */
4110-
return btrfs_calc_trans_metadata_size(root, num_dev + 1);
4109+
return num_dev;
41114110
}
41124111

4113-
static void check_system_chunk(struct btrfs_trans_handle *trans,
4114-
struct btrfs_root *root, u64 type)
4112+
/*
4113+
* If @is_allocation is true, reserve space in the system space info necessary
4114+
* for allocating a chunk, otherwise if it's false, reserve space necessary for
4115+
* removing a chunk.
4116+
*/
4117+
void check_system_chunk(struct btrfs_trans_handle *trans,
4118+
struct btrfs_root *root,
4119+
u64 type,
4120+
const bool is_allocation)
41154121
{
41164122
struct btrfs_space_info *info;
41174123
u64 left;
41184124
u64 thresh;
41194125
int ret = 0;
4126+
u64 num_devs;
41204127

41214128
/*
41224129
* 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,
41314138
info->bytes_may_use;
41324139
spin_unlock(&info->lock);
41334140

4134-
thresh = get_system_chunk_thresh(root, type);
4141+
num_devs = get_profile_num_devs(root, type);
4142+
4143+
/* num_devs device items to update and 1 chunk item to add or remove */
4144+
if (is_allocation)
4145+
thresh = btrfs_calc_trans_metadata_size(root, num_devs + 1);
4146+
else
4147+
thresh = btrfs_calc_trans_metadata_size(root, num_devs) +
4148+
btrfs_calc_trunc_metadata_size(root, 1);
4149+
41354150
if (left < thresh && btrfs_test_opt(root, ENOSPC_DEBUG)) {
41364151
btrfs_info(root->fs_info, "left=%llu, need=%llu, flags=%llu",
41374152
left, thresh, type);
@@ -4243,7 +4258,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
42434258
* Check if we have enough space in SYSTEM chunk because we may need
42444259
* to update devices.
42454260
*/
4246-
check_system_chunk(trans, extent_root, flags);
4261+
check_system_chunk(trans, extent_root, flags, true);
42474262

42484263
ret = btrfs_alloc_chunk(trans, extent_root, flags);
42494264
trans->allocating_chunk = false;
@@ -8905,7 +8920,7 @@ int btrfs_set_block_group_ro(struct btrfs_root *root,
89058920
if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) {
89068921
alloc_flags = update_block_group_flags(root, cache->flags);
89078922
lock_chunks(root->fs_info->chunk_root);
8908-
check_system_chunk(trans, root, alloc_flags);
8923+
check_system_chunk(trans, root, alloc_flags, true);
89098924
unlock_chunks(root->fs_info->chunk_root);
89108925
}
89118926
mutex_unlock(&root->fs_info->ro_block_group_mutex);

fs/btrfs/volumes.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2625,6 +2625,9 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans,
26252625
return -EINVAL;
26262626
}
26272627
map = (struct map_lookup *)em->bdev;
2628+
lock_chunks(root->fs_info->chunk_root);
2629+
check_system_chunk(trans, extent_root, map->type, false);
2630+
unlock_chunks(root->fs_info->chunk_root);
26282631

26292632
for (i = 0; i < map->num_stripes; i++) {
26302633
struct btrfs_device *device = map->stripes[i].dev;

0 commit comments

Comments
 (0)