Re: [PATCH 2/5] btrfs: zoned: drop stale dirty btree buffers at close_ctree()

Qu Wenruo <[email protected]>
Newsgroups org.kernel.vger.linux-btrfs
Message-ID <[email protected]>

在 2026/7/16 21:26, Johannes Thumshirn 写道:
> On a zoned filesystem btrfs_clear_buffer_dirty() keeps a freed-but-dirty
> tree block dirty (EXTENT_BUFFER_ZONED_ZEROOUT) so its zero-out keeps the
> zone write pointer moving. A block left ahead of the block group's
> meta_write_pointer (e.g. a tree-log block freed before being written) can
> never be written by btree_writepages() and survives to unmount.
> 
> close_ctree() stops the endio workqueues before the final iput() of the
> btree inode but frees the block groups only afterwards. Once the block
> group is gone btrfs_check_meta_write_pointer() no longer defers the buffer,
> so the iput() submits it for writeback; its completion can no longer be
> queued on the destroyed endio_meta_workers and umount hangs on writeback
> that never finishes.

Can we just trigger a btree inode write back for zoned cases?

That sounds much simpler and less hacky.

Thanks,
Qu
> 
> btrfs_free_block_groups() has to stay after btrfs_stop_all_workers()
> (see also commit 5cdd7db6c5c9), so instead drop the dirty state of such
> stale buffers in invalidate_and_check_btree_folios(), which still runs
> while the workqueues are alive.
> 
> Assisted-by: LLM (debugging, commit message)
> Signed-off-by: Johannes Thumshirn <[email protected]>
> ---
>   fs/btrfs/disk-io.c   |  4 ++++
>   fs/btrfs/extent_io.c | 12 ++++++++++++
>   fs/btrfs/extent_io.h |  1 +
>   3 files changed, 17 insertions(+)
> 
> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> index acff40469589..b66525701b33 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -3317,6 +3317,10 @@ static void invalidate_and_check_btree_folios(struct btrfs_fs_info *fs_info)
>   		if (test_bit(EXTENT_BUFFER_READING, &eb->bflags))
>   			wait_on_bit_io(&eb->bflags, EXTENT_BUFFER_READING,
>   				       TASK_UNINTERRUPTIBLE);
> +
> +		if (btrfs_is_zoned(fs_info) &&
> +		    test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags))
> +			btrfs_clear_stale_buffer_dirty(eb);
>   		/*
>   		 * The refs threshold is 2, one held by us at the beginning
>   		 * of the loop, one for the ownership in the buffer tree.
> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
> index 4dd8fc5ad247..480d4ec8ca7f 100644
> --- a/fs/btrfs/extent_io.c
> +++ b/fs/btrfs/extent_io.c
> @@ -3841,6 +3841,18 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans,
>   	__btrfs_clear_buffer_dirty(eb);
>   }
>   
> +/*
> + * Drop the dirty state of a stale, already-freed metadata buffer, bypassing
> + * the zoned EXTENT_BUFFER_ZONED_ZEROOUT deferral of btrfs_clear_buffer_dirty().
> + */
> +void btrfs_clear_stale_buffer_dirty(struct extent_buffer *eb)
> +{
> +	btrfs_tree_lock(eb);
> +	clear_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags);
> +	__btrfs_clear_buffer_dirty(eb);
> +	btrfs_tree_unlock(eb);
> +}
> +
>   void set_extent_buffer_dirty(struct extent_buffer *eb)
>   {
>   	bool was_dirty;
> diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
> index 869925337699..290d6cff6ddb 100644
> --- a/fs/btrfs/extent_io.h
> +++ b/fs/btrfs/extent_io.h
> @@ -393,6 +393,7 @@ void extent_clear_unlock_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
>   				  u32 bits_to_clear, unsigned long page_ops);
>   void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans,
>   			      struct extent_buffer *buf);
> +void btrfs_clear_stale_buffer_dirty(struct extent_buffer *eb);
>   
>   static inline void btrfs_clear_folio_dirty_tag(struct folio *folio)
>   {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.