Skip to content

Commit 4a55cf6

Browse files
committed
btrfs: warn about extent buffer that can not be released
When we unmount the fs or during mount failures, btrfs will call invalidate_inode_pages() to release all btree inode folios. However that function can return -EBUSY if any folios can not be invalidated. This can be caused by: - Some extent buffers are still held by btrfs This is a logic error, as we should release all tree root nodes during unmount and mount failure handling. - Some extent buffers are under readahead and haven't yet finished These are much rarer but valid cases. In that case we should wait for those extent buffers. Introduce a new helper invalidate_and_check_btree_folios() which will: - Call invalidate_inode_pages2() and catch its return value If it returned 0 as expected, that's great and we can call it a day. - Otherwise go through each extent buffer in buffer_tree Increase the ref by one first for the eb we're checking. This is to ensure the eb won't be freed after the readahead is finished. For ebs that still have EXTENT_BUFFER_READING flag, wait for them to finish first. After waiting for the readahead, check the refs of the eb and if it's still dirty. If the eb ref count is greater than 2 (one for the buffer tree, one held by us), it means we are still holding the extent buffer somewhere else, which is a code bug. If the eb is still dirty, it means a bug in transaction handling, e.g. the bug fixed by patch "btrfs: only release the dirty pages io tree after successful writes". For either case, show a warning message about the eb, including its bytenr, owner, refs and flags. And if it's a debug build, also trigger WARN_ON_ONCE() so that fstests can properly catch such situation. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221270 Reported-by: AHN SEOK-YOUNG <iamsyahn@gmail.com> Cc: Teng Liu <27rabbitlt@gmail.com> Tested-by: Teng Liu <27rabbitlt@gmail.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Qu Wenruo <wqu@suse.com>
1 parent e51c348 commit 4a55cf6

3 files changed

Lines changed: 58 additions & 8 deletions

File tree

fs/btrfs/disk-io.c

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3272,6 +3272,56 @@ static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info)
32723272
return false;
32733273
}
32743274

3275+
/*
3276+
* Try to wait for any metadata readahead, and invalidate all btree folios.
3277+
*
3278+
* If the invalidation failed, report any dirty/held extent buffers.
3279+
*/
3280+
static void invalidate_and_check_btree_folios(struct btrfs_fs_info *fs_info)
3281+
{
3282+
unsigned long index = 0;
3283+
struct extent_buffer *eb;
3284+
int ret;
3285+
3286+
ret = invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
3287+
if (likely(ret == 0))
3288+
return;
3289+
3290+
/*
3291+
* Some btree pages can not be invalidated, this happens when some
3292+
* tree blocks are still held (either by some pointer or readahead).
3293+
*/
3294+
rcu_read_lock();
3295+
xa_for_each(&fs_info->buffer_tree, index, eb) {
3296+
/* Increase the ref so that the eb won't disappear. */
3297+
if (!refcount_inc_not_zero(&eb->refs))
3298+
continue;
3299+
rcu_read_unlock();
3300+
3301+
/* Wait for any readahead first. */
3302+
if (test_bit(EXTENT_BUFFER_READING, &eb->bflags))
3303+
wait_on_bit_io(&eb->bflags, EXTENT_BUFFER_READING,
3304+
TASK_UNINTERRUPTIBLE);
3305+
/*
3306+
* The refs threshold is 2, one held by us at the beginning
3307+
* of the loop, one for the ownership in the buffer tree.
3308+
*/
3309+
if (unlikely(refcount_read(&eb->refs) > 2 ||
3310+
extent_buffer_under_io(eb))) {
3311+
WARN_ON_ONCE(IS_ENABLED(CONFIG_BTRFS_DEBUG));
3312+
btrfs_warn(fs_info,
3313+
"unable to release extent buffer %llu owner %llu gen %llu refs %u flags 0x%lx",
3314+
eb->start, btrfs_header_owner(eb),
3315+
btrfs_header_generation(eb),
3316+
refcount_read(&eb->refs), eb->bflags);
3317+
}
3318+
free_extent_buffer(eb);
3319+
rcu_read_lock();
3320+
}
3321+
rcu_read_unlock();
3322+
invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
3323+
}
3324+
32753325
int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices)
32763326
{
32773327
u32 sectorsize;
@@ -3709,7 +3759,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
37093759
if (fs_info->data_reloc_root)
37103760
btrfs_drop_and_free_fs_root(fs_info, fs_info->data_reloc_root);
37113761
free_root_pointers(fs_info, true);
3712-
invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
3762+
invalidate_and_check_btree_folios(fs_info);
37133763

37143764
fail_sb_buffer:
37153765
btrfs_stop_all_workers(fs_info);
@@ -4438,7 +4488,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
44384488
* We must make sure there is not any read request to
44394489
* submit after we stop all workers.
44404490
*/
4441-
invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
4491+
invalidate_and_check_btree_folios(fs_info);
44424492
btrfs_stop_all_workers(fs_info);
44434493

44444494
/*

fs/btrfs/extent_io.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2882,12 +2882,6 @@ bool try_release_extent_mapping(struct folio *folio, gfp_t mask)
28822882
return try_release_extent_state(io_tree, folio);
28832883
}
28842884

2885-
static int extent_buffer_under_io(const struct extent_buffer *eb)
2886-
{
2887-
return (test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) ||
2888-
test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
2889-
}
2890-
28912885
static bool folio_range_has_eb(struct folio *folio)
28922886
{
28932887
struct btrfs_folio_state *bfs;

fs/btrfs/extent_io.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,12 @@ static inline bool extent_buffer_uptodate(const struct extent_buffer *eb)
327327
return test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
328328
}
329329

330+
static inline bool extent_buffer_under_io(const struct extent_buffer *eb)
331+
{
332+
return (test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) ||
333+
test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
334+
}
335+
330336
int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
331337
unsigned long start, unsigned long len);
332338
void read_extent_buffer(const struct extent_buffer *eb, void *dst,

0 commit comments

Comments
 (0)