Commit 44e5801f authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba

btrfs: return correct error number for __extent_writepage_io()

[BUG]
If we hit an error from submit_extent_page() inside
__extent_writepage_io(), we could still return 0 to the caller, and
even trigger the warning in btrfs_page_assert_not_dirty().

[CAUSE]
In __extent_writepage_io(), if we hit an error from
submit_extent_page(), we will just clean up the range and continue.

This is completely fine for regular PAGE_SIZE == sectorsize, as we can
only hit one sector in one page, thus after the error we're ensured to
exit and @ret will be saved.

But for subpage case, we may have other dirty subpage range in the page,
and in the next loop, we may succeeded submitting the next range.

In that case, @ret will be overwritten, and we return 0 to the caller,
while we have hit some error.

[FIX]
Introduce @has_error and @saved_ret to record the first error we hit, so
we will never forget what error we hit.

CC: stable@vger.kernel.org # 5.15+
Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 10f7f6f8
...@@ -3959,10 +3959,12 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, ...@@ -3959,10 +3959,12 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode,
u64 extent_offset; u64 extent_offset;
u64 block_start; u64 block_start;
struct extent_map *em; struct extent_map *em;
int saved_ret = 0;
int ret = 0; int ret = 0;
int nr = 0; int nr = 0;
u32 opf = REQ_OP_WRITE; u32 opf = REQ_OP_WRITE;
const unsigned int write_flags = wbc_to_write_flags(wbc); const unsigned int write_flags = wbc_to_write_flags(wbc);
bool has_error = false;
bool compressed; bool compressed;
ret = btrfs_writepage_cow_fixup(page); ret = btrfs_writepage_cow_fixup(page);
...@@ -4012,6 +4014,9 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, ...@@ -4012,6 +4014,9 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode,
if (IS_ERR(em)) { if (IS_ERR(em)) {
btrfs_page_set_error(fs_info, page, cur, end - cur + 1); btrfs_page_set_error(fs_info, page, cur, end - cur + 1);
ret = PTR_ERR_OR_ZERO(em); ret = PTR_ERR_OR_ZERO(em);
has_error = true;
if (!saved_ret)
saved_ret = ret;
break; break;
} }
...@@ -4075,6 +4080,10 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, ...@@ -4075,6 +4080,10 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode,
end_bio_extent_writepage, end_bio_extent_writepage,
0, 0, false); 0, 0, false);
if (ret) { if (ret) {
has_error = true;
if (!saved_ret)
saved_ret = ret;
btrfs_page_set_error(fs_info, page, cur, iosize); btrfs_page_set_error(fs_info, page, cur, iosize);
if (PageWriteback(page)) if (PageWriteback(page))
btrfs_page_clear_writeback(fs_info, page, cur, btrfs_page_clear_writeback(fs_info, page, cur,
...@@ -4088,8 +4097,10 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, ...@@ -4088,8 +4097,10 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode,
* If we finish without problem, we should not only clear page dirty, * If we finish without problem, we should not only clear page dirty,
* but also empty subpage dirty bits * but also empty subpage dirty bits
*/ */
if (!ret) if (!has_error)
btrfs_page_assert_not_dirty(fs_info, page); btrfs_page_assert_not_dirty(fs_info, page);
else
ret = saved_ret;
*nr_ret = nr; *nr_ret = nr;
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment