Skip to content

Commit c31edf6

Browse files
adam900710kdave
authored andcommitted
btrfs-progs: Fix false ENOSPC alert by tracking used space correctly
[BUG] There is a bug report of unexpected ENOSPC from btrfs-convert, issue #123. After some debugging, even when we have enough unallocated space, we still hit ENOSPC at btrfs_reserve_extent(). [CAUSE] Btrfs-progs relies on chunk preallocator to make enough space for data/metadata. However after the introduction of delayed-ref, it's no longer reliable to rely on btrfs_space_info::bytes_used and btrfs_space_info::bytes_pinned to calculate used metadata space. For a running transaction with a lot of allocated tree blocks, btrfs_space_info::bytes_used stays its original value, and will only be updated when running delayed ref. This makes btrfs-progs chunk preallocator completely useless. And for btrfs-convert/mkfs.btrfs --rootdir, if we're going to have enough metadata to fill a metadata block group in one transaction, we will hit ENOSPC no matter whether we have enough unallocated space. [FIX] This patch will introduce btrfs_space_info::bytes_reserved to track how many space we have reserved but not yet committed to extent tree. To support this change, this commit also introduces the following modification: - More comment on btrfs_space_info::bytes_* To make code a little easier to read - Export update_space_info() to preallocate empty data/metadata space info for mkfs. For mkfs, we only have a temporary fs image with SYSTEM chunk only. Export update_space_info() so that we can preallocate empty data/metadata space info before we start a transaction. - Proper btrfs_space_info::bytes_reserved update The timing is the as kernel (except we don't need to update bytes_reserved for data extents) * Increase bytes_reserved when call alloc_reserved_tree_block() * Decrease bytes_reserved when running delayed refs With the help of head->must_insert_reserved to determine whether we need to decrease. Issue: #123 Signed-off-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent ab5079c commit c31edf6

File tree

4 files changed

+83
-6
lines changed

4 files changed

+83
-6
lines changed

ctree.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,8 +1058,29 @@ struct btrfs_qgroup_limit_item {
10581058
struct btrfs_space_info {
10591059
u64 flags;
10601060
u64 total_bytes;
1061+
/*
1062+
* Space already used.
1063+
* Only accounting space in current extent tree, thus delayed ref
1064+
* won't be accounted here.
1065+
*/
10611066
u64 bytes_used;
1067+
1068+
/*
1069+
* Space being pinned down.
1070+
* So extent allocator will not try to allocate space from them.
1071+
*
1072+
* For cases like extents being freed in current transaction, or
1073+
* manually pinned bytes for re-initializing certain trees.
1074+
*/
10621075
u64 bytes_pinned;
1076+
1077+
/*
1078+
* Space being reserved.
1079+
* Space has already being reserved but not yet reach extent tree.
1080+
*
1081+
* New tree blocks allocated in current transaction goes here.
1082+
*/
1083+
u64 bytes_reserved;
10631084
int full;
10641085
struct list_head list;
10651086
};
@@ -2513,6 +2534,9 @@ int btrfs_update_extent_ref(struct btrfs_trans_handle *trans,
25132534
u64 root_objectid, u64 ref_generation,
25142535
u64 owner_objectid);
25152536
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans);
2537+
int update_space_info(struct btrfs_fs_info *info, u64 flags,
2538+
u64 total_bytes, u64 bytes_used,
2539+
struct btrfs_space_info **space_info);
25162540
int btrfs_free_block_groups(struct btrfs_fs_info *info);
25172541
int btrfs_read_block_groups(struct btrfs_root *root);
25182542
struct btrfs_block_group_cache *

extent-tree.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,9 +1630,9 @@ static int free_space_info(struct btrfs_fs_info *fs_info, u64 flags,
16301630
return 0;
16311631
}
16321632

1633-
static int update_space_info(struct btrfs_fs_info *info, u64 flags,
1634-
u64 total_bytes, u64 bytes_used,
1635-
struct btrfs_space_info **space_info)
1633+
int update_space_info(struct btrfs_fs_info *info, u64 flags,
1634+
u64 total_bytes, u64 bytes_used,
1635+
struct btrfs_space_info **space_info)
16361636
{
16371637
struct btrfs_space_info *found;
16381638

@@ -1658,6 +1658,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
16581658
found->total_bytes = total_bytes;
16591659
found->bytes_used = bytes_used;
16601660
found->bytes_pinned = 0;
1661+
found->bytes_reserved = 0;
16611662
found->full = 0;
16621663
*space_info = found;
16631664
return 0;
@@ -1703,8 +1704,8 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
17031704
return 0;
17041705

17051706
thresh = div_factor(space_info->total_bytes, 7);
1706-
if ((space_info->bytes_used + space_info->bytes_pinned + alloc_bytes) <
1707-
thresh)
1707+
if ((space_info->bytes_used + space_info->bytes_pinned +
1708+
space_info->bytes_reserved + alloc_bytes) < thresh)
17081709
return 0;
17091710

17101711
/*
@@ -2375,13 +2376,17 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
23752376
struct btrfs_fs_info *fs_info = trans->fs_info;
23762377
struct btrfs_extent_item *extent_item;
23772378
struct btrfs_extent_inline_ref *iref;
2379+
struct btrfs_space_info *sinfo;
23782380
struct extent_buffer *leaf;
23792381
struct btrfs_path *path;
23802382
struct btrfs_key ins;
23812383
u32 size = sizeof(*extent_item) + sizeof(*iref);
23822384
u64 start, end;
23832385
int ret;
23842386

2387+
sinfo = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA);
2388+
ASSERT(sinfo);
2389+
23852390
ins.objectid = node->bytenr;
23862391
if (skinny_metadata) {
23872392
ins.offset = ref->level;
@@ -2442,6 +2447,14 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
24422447

24432448
ret = update_block_group(fs_info, ins.objectid, fs_info->nodesize, 1,
24442449
0);
2450+
if (sinfo) {
2451+
if (fs_info->nodesize > sinfo->bytes_reserved) {
2452+
WARN_ON(1);
2453+
sinfo->bytes_reserved = 0;
2454+
} else {
2455+
sinfo->bytes_reserved -= fs_info->nodesize;
2456+
}
2457+
}
24452458

24462459
if (ref->root == BTRFS_EXTENT_TREE_OBJECTID) {
24472460
clear_extent_bits(&trans->fs_info->extent_ins, start, end,
@@ -2461,13 +2474,20 @@ static int alloc_tree_block(struct btrfs_trans_handle *trans,
24612474
int ret;
24622475
u64 extent_size;
24632476
struct btrfs_delayed_extent_op *extent_op;
2477+
struct btrfs_space_info *sinfo;
2478+
struct btrfs_fs_info *fs_info = root->fs_info;
24642479
bool skinny_metadata = btrfs_fs_incompat(root->fs_info,
24652480
SKINNY_METADATA);
24662481

24672482
extent_op = btrfs_alloc_delayed_extent_op();
24682483
if (!extent_op)
24692484
return -ENOMEM;
24702485

2486+
sinfo = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA);
2487+
if (!sinfo) {
2488+
error("Corrupted fs, no valid METADATA block group found");
2489+
return -EUCLEAN;
2490+
}
24712491
ret = btrfs_reserve_extent(trans, root, num_bytes, empty_size,
24722492
hint_byte, search_end, ins, 0);
24732493
if (ret < 0)
@@ -2500,6 +2520,7 @@ static int alloc_tree_block(struct btrfs_trans_handle *trans,
25002520
BUG_ON(ret);
25012521
}
25022522

2523+
sinfo->bytes_reserved += extent_size;
25032524
ret = btrfs_add_delayed_tree_ref(root->fs_info, trans, ins->objectid,
25042525
extent_size, 0, root_objectid,
25052526
level, BTRFS_ADD_DELAYED_EXTENT,
@@ -2837,6 +2858,10 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
28372858
sinfo = list_entry(info->space_info.next,
28382859
struct btrfs_space_info, list);
28392860
list_del_init(&sinfo->list);
2861+
if (sinfo->bytes_reserved)
2862+
warning(
2863+
"reserved space leaked, flag=0x%llx bytes_reserved=%llu",
2864+
sinfo->flags, sinfo->bytes_reserved);
28402865
kfree(sinfo);
28412866
}
28422867
return 0;
@@ -3943,8 +3968,17 @@ int cleanup_ref_head(struct btrfs_trans_handle *trans,
39433968
rb_erase(&head->href_node, &delayed_refs->href_root);
39443969
RB_CLEAR_NODE(&head->href_node);
39453970

3946-
if (head->must_insert_reserved)
3971+
if (head->must_insert_reserved) {
39473972
btrfs_pin_extent(fs_info, head->bytenr, head->num_bytes);
3973+
if (!head->is_data) {
3974+
struct btrfs_space_info *sinfo;
3975+
3976+
sinfo = __find_space_info(trans->fs_info,
3977+
BTRFS_BLOCK_GROUP_METADATA);
3978+
ASSERT(sinfo);
3979+
sinfo->bytes_reserved -= head->num_bytes;
3980+
}
3981+
}
39483982

39493983
btrfs_put_delayed_ref_head(head);
39503984
return 0;

mkfs/main.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,22 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
5959
{
6060
struct btrfs_fs_info *fs_info = root->fs_info;
6161
struct btrfs_trans_handle *trans;
62+
struct btrfs_space_info *sinfo;
6263
u64 bytes_used;
6364
u64 chunk_start = 0;
6465
u64 chunk_size = 0;
6566
int ret;
6667

68+
/* Create needed space info to trace extents reservation */
69+
ret = update_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA,
70+
0, 0, &sinfo);
71+
if (ret < 0)
72+
return ret;
73+
ret = update_space_info(fs_info, BTRFS_BLOCK_GROUP_DATA,
74+
0, 0, &sinfo);
75+
if (ret < 0)
76+
return ret;
77+
6778
trans = btrfs_start_transaction(root, 1);
6879
BUG_ON(IS_ERR(trans));
6980
bytes_used = btrfs_super_bytes_used(fs_info->super_copy);

transaction.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
159159
u64 transid = trans->transid;
160160
int ret = 0;
161161
struct btrfs_fs_info *fs_info = root->fs_info;
162+
struct btrfs_space_info *sinfo;
162163

163164
if (trans->fs_info->transaction_aborted)
164165
return -EROFS;
@@ -210,6 +211,13 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
210211
root->commit_root = NULL;
211212
fs_info->running_transaction = NULL;
212213
fs_info->last_trans_committed = transid;
214+
list_for_each_entry(sinfo, &fs_info->space_info, list) {
215+
if (sinfo->bytes_reserved) {
216+
warning(
217+
"reserved space leaked, transid=%llu flag=0x%llx bytes_reserved=%llu",
218+
transid, sinfo->flags, sinfo->bytes_reserved);
219+
}
220+
}
213221
return ret;
214222
error:
215223
btrfs_destroy_delayed_refs(trans);

0 commit comments

Comments
 (0)