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) > {