[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 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);
 		}
 		nr_to_write_done = (wbc->nr_to_write <= 0);
 		eb_batch_release(&batch);
-- 
2.54.0
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.