[PATCH 2/5] btrfs: zoned: drop stale dirty btree buffers at close_ctree()
Johannes Thumshirn <[email protected]>
| Newsgroups | org.kernel.vger.linux-btrfs |
|---|---|
| Message-ID | <[email protected]> |
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. 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) { -- 2.54.0