btrfs: return any hit error from extent_writepage_io()

[ Upstream commit 2d83ed6c6c4607b42ee7927e92a9d2fa31d6f30b ]

Since the support of bs < ps support, extent_writepage_io() will submit
multiple blocks inside the folio.

But if we hit error submitting one sector, but the next sector can still
be submitted successfully, the function extent_writepage_io() will still
return 0.

This will make btrfs to silently ignore the error without setting error
flag for the filemap.

Fix it by recording the first error hit, and always return that value.

Fixes: 8bf334beb3 ("btrfs: fix double accounting race when extent_writepage_io() failed")
Reviewed-by: Daniel Vacek <neelx@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Qu Wenruo 2025-09-18 08:40:45 +09:30 committed by Greg Kroah-Hartman
parent 1c060a1476
commit d673f78da3

View File

@ -1479,7 +1479,7 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
struct btrfs_fs_info *fs_info = inode->root->fs_info; struct btrfs_fs_info *fs_info = inode->root->fs_info;
unsigned long range_bitmap = 0; unsigned long range_bitmap = 0;
bool submitted_io = false; bool submitted_io = false;
bool error = false; int found_error = 0;
const u64 folio_start = folio_pos(folio); const u64 folio_start = folio_pos(folio);
u64 cur; u64 cur;
int bit; int bit;
@ -1536,7 +1536,8 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
*/ */
btrfs_mark_ordered_io_finished(inode, folio, cur, btrfs_mark_ordered_io_finished(inode, folio, cur,
fs_info->sectorsize, false); fs_info->sectorsize, false);
error = true; if (!found_error)
found_error = ret;
continue; continue;
} }
submitted_io = true; submitted_io = true;
@ -1553,11 +1554,11 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
* If we hit any error, the corresponding sector will still be dirty * If we hit any error, the corresponding sector will still be dirty
* thus no need to clear PAGECACHE_TAG_DIRTY. * thus no need to clear PAGECACHE_TAG_DIRTY.
*/ */
if (!submitted_io && !error) { if (!submitted_io && !found_error) {
btrfs_folio_set_writeback(fs_info, folio, start, len); btrfs_folio_set_writeback(fs_info, folio, start, len);
btrfs_folio_clear_writeback(fs_info, folio, start, len); btrfs_folio_clear_writeback(fs_info, folio, start, len);
} }
return ret; return found_error;
} }
/* /*