Skip to content

Commit 6edbd02

Browse files
adam900710gregkh
authored andcommitted
btrfs: clear block dirty if submit_one_sector() failed
[ Upstream commit 4bcd306 ] [BUG] If submit_one_sector() failed, the block will be kept dirty, but with their corresponding range finished in the ordered extent. This means if a writeback happens later again, we can hit the following problems: - ASSERT(block_start != EXTENT_MAP_HOLE) in submit_one_sector() If the original extent map is a hole, then we can hit this case, as the new ordered extent failed, we will drop the new extent map and re-read one from the disk. - DEBUG_WARN() in btrfs_writepage_cow_fixup() This is because we no longer have an ordered extent for those dirty blocks. The original for them is already finished with error. [CAUSE] The function submit_one_sector() is not following the regular error handling of writeback. The common practice is to clear the folio dirty, start and finish the writeback for the block. This is normally done by extent_clear_unlock_delalloc() with PAGE_START_WRITEBACK | PAGE_END_WRITEBACK flags during run_delalloc_range(). So if we keep those failed blocks dirty, they will stay in the page cache and wait for the next writeback. And since the original ordered extent is already finished and removed, depending on the original extent map, we either hit the ASSERT() inside submit_one_sector(), or hit the DEBUG_WARN() in btrfs_writepage_cow_fixup(). [FIX] Follow the regular error handling to clear the dirty flag for the block, start and finish writeback for that block instead. Reviewed-by: Filipe Manana <fdmanana@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>
1 parent e1ec77b commit 6edbd02

File tree

1 file changed

+13
-4
lines changed

1 file changed

+13
-4
lines changed

fs/btrfs/extent_io.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,7 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode,
14831483

14841484
/*
14851485
* Return 0 if we have submitted or queued the sector for submission.
1486-
* Return <0 for critical errors.
1486+
* Return <0 for critical errors, and the sector will have its dirty flag cleared.
14871487
*
14881488
* Caller should make sure filepos < i_size and handle filepos >= i_size case.
14891489
*/
@@ -1506,8 +1506,17 @@ static int submit_one_sector(struct btrfs_inode *inode,
15061506
ASSERT(filepos < i_size);
15071507

15081508
em = btrfs_get_extent(inode, NULL, filepos, sectorsize);
1509-
if (IS_ERR(em))
1509+
if (IS_ERR(em)) {
1510+
/*
1511+
* When submission failed, we should still clear the folio dirty.
1512+
* Or the folio will be written back again but without any
1513+
* ordered extent.
1514+
*/
1515+
btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize);
1516+
btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize);
1517+
btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize);
15101518
return PTR_ERR(em);
1519+
}
15111520

15121521
extent_offset = filepos - em->start;
15131522
em_end = btrfs_extent_map_end(em);
@@ -1637,8 +1646,8 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
16371646
* Here we set writeback and clear for the range. If the full folio
16381647
* is no longer dirty then we clear the PAGECACHE_TAG_DIRTY tag.
16391648
*
1640-
* If we hit any error, the corresponding sector will still be dirty
1641-
* thus no need to clear PAGECACHE_TAG_DIRTY.
1649+
* If we hit any error, the corresponding sector will have its dirty
1650+
* flag cleared and writeback finished, thus no need to handle the error case.
16421651
*/
16431652
if (!submitted_io && !error) {
16441653
btrfs_folio_set_writeback(fs_info, folio, start, len);

0 commit comments

Comments
 (0)