Re: [PATCH] btrfs: zoned: don't submit orphaned extent buffers
Johannes Thumshirn <[email protected]>
| Newsgroups | org.kernel.vger.linux-btrfs,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
On 7/9/26 12:31 PM, Qu Wenruo wrote: > > > 在 2026/7/3 15:24, Johannes Thumshirn 写道: >> On a zoned filesystem btree_writepages() can encounter a dirty metadata >> extent buffer whose block group no longer exists. Submitting a write for >> such a buffer maps it to a stale/removed block-group and leaves the >> folio >> under writeback forever, hanging later in filemap_fdatawait_range(), for >> example the iput(btree_inode) in close_ctree(), which then hangs >> unmount. >> >> This is caused by btrfs_clear_buffer_dirty() not clearing the dirty >> bit of >> a freed tree block but it sets EXTENT_BUFFER_ZONED_ZEROOUT and keeps the >> buffer dirty so that it is still written out to keep the zone's >> meta_write_pointer advancing sequentially. So a freed metadata block >> legitimately stays dirty until that zero-write completes. >> >> Dropping these buffers is safe: the block group is empty, so they are >> stale, unreferenced, already-freed blocks. Once the zone is reset their >> zero-write is unneeded. Instead of submitting a such a write, finish the >> writeback immediately. >> >> Reported-by: Shin'ichiro Kawasaki <[email protected]> >> Fixes: 7db94301a980 ("btrfs: zoned: introduce block group context to >> btrfs_eb_write_context") >> Cc: [email protected] >> Signed-off-by: Johannes Thumshirn <[email protected]> >> --- >> fs/btrfs/extent_io.c | 16 ++++++++++++++-- >> 1 file changed, 14 insertions(+), 2 deletions(-) >> >> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c >> index 0edd532174fa..4a029ae719e9 100644 >> --- a/fs/btrfs/extent_io.c >> +++ b/fs/btrfs/extent_io.c >> @@ -2280,7 +2280,8 @@ static void prepare_eb_write(struct >> extent_buffer *eb) >> } >> static noinline_for_stack void write_one_eb(struct extent_buffer >> *eb, >> - struct writeback_control *wbc) >> + struct writeback_control *wbc, >> + bool submit) >> { >> struct btrfs_fs_info *fs_info = eb->fs_info; >> struct btrfs_bio *bbio; >> @@ -2310,6 +2311,12 @@ static noinline_for_stack void >> write_one_eb(struct extent_buffer *eb, >> wbc_account_cgroup_owner(wbc, folio, range_len); >> folio_unlock(folio); >> } >> + >> + if (!submit) { >> + btrfs_bio_end_io(bbio, BLK_STS_OK); >> + return; >> + } >> + >> /* >> * If the fs is already in error status, do not submit any >> writeback >> * but immediately finish it. >> @@ -2397,6 +2404,8 @@ int btree_writepages(struct address_space >> *mapping, struct writeback_control *wb >> struct extent_buffer *eb; >> while ((eb = eb_batch_next(&batch)) != NULL) { >> + bool submit = true; >> + >> ctx.eb = eb; >> ret = btrfs_check_meta_write_pointer(eb->fs_info, &ctx); >> @@ -2411,6 +2420,9 @@ int btree_writepages(struct address_space >> *mapping, struct writeback_control *wb >> continue; >> } >> + if (btrfs_is_zoned(fs_info) && !ctx.zoned_bg) >> + submit = false; >> + >> if (!lock_extent_buffer_for_io(eb, wbc)) >> continue; >> @@ -2420,7 +2432,7 @@ int btree_writepages(struct address_space >> *mapping, struct writeback_control *wb >> btrfs_schedule_zone_finish_bg(ctx.zoned_bg, eb); >> ctx.zoned_bg->meta_write_pointer += eb->len; >> } >> - write_one_eb(eb, wbc); >> + write_one_eb(eb, wbc, submit); > > I understand this is the minimal fix, but I can't help but wondering, > would it be more instinctual to release all ebs inside a zoned > metadata bg when freeing the bg? Yes but quite frankly I'm still hunting the root cause. One thing I think I've found so far is, we're re-dirtying a eb, then relocation GC's the block-group, then writeback comes along trying to submit these ebs but the bg is gone. This analysis could be totally bogus of cause because I missed something.