btrfs: clear TAG_TOWRITE from buffer tree when submitting a tree block
authorQu Wenruo <wqu@suse.com>
Wed, 30 Jul 2025 22:50:01 +0000 (08:20 +0930)
committerDavid Sterba <dsterba@suse.com>
Wed, 13 Aug 2025 12:08:45 +0000 (14:08 +0200)
[POSSIBLE BUG]
After commit 5e121ae687b8 ("btrfs: use buffer xarray for extent buffer
writeback operations"), we have a dedicated xarray for extent buffers,
and a lot of tags are migrated to that buffer tree, like
PAGECACHE_TAG_TOWRITE/DIRTY/WRITEBACK.

This frees us from the limits of page flags, but there is a new
asymmetric behavior, we call buffer_tree_tag_for_writeback() to set
PAGECACHE_TAG_TOWRITE for the involved ranges, but there is no one to
clear that tag.

Before that rework, we relied on the page cache tag which was cleared
when folio_start_writeback() was called.
Although this has its own problems (e.g. the first one calling
folio_start_writeback() will clear the tag for the whole page), it at
least cleared the tag.

But now our real tags are stored in the buffer tree, no one is really
clearing the PAGECACHE_TAG_TOWRITE tag now.

[FIX]
Thankfully this is not going to cause any real bug, but just some
inefficiency iterating the extent buffers.

As if we hit an extent buffer which is not dirty but still has the
PAGECACHE_TAG_TOWRITE tag, lock_extent_buffer_for_io() will skip it so
we won't writeback the extent buffer again.

To properly fix the inefficiency, just clear the PAGECACHE_TAG_TOWRITE
inside lock_extent_buffer_for_io().

There is no error path between lock_extent_buffer_for_io() and
write_one_eb(), so we're safe to clear the tag there.

Reviewed-by: Naohiro Aota <naohiro.aota@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent_io.c

index be9c9c80495229f936bdd1dc629a6d0ec257046f..c953297aa89a01e0c1e8c3035899b6387bf6e64c 100644 (file)
@@ -1826,6 +1826,7 @@ static noinline_for_stack bool lock_extent_buffer_for_io(struct extent_buffer *e
                xas_load(&xas);
                xas_set_mark(&xas, PAGECACHE_TAG_WRITEBACK);
                xas_clear_mark(&xas, PAGECACHE_TAG_DIRTY);
+               xas_clear_mark(&xas, PAGECACHE_TAG_TOWRITE);
                xas_unlock_irqrestore(&xas, flags);
 
                btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);