Skip to content

Commit b6282eb

Browse files
adam900710kdave
authored andcommitted
btrfs: clear block dirty if submit_one_sector() failed
[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 <[email protected]> Signed-off-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 250238f commit b6282eb

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
@@ -1570,7 +1570,7 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode,
15701570

15711571
/*
15721572
* Return 0 if we have submitted or queued the sector for submission.
1573-
* Return <0 for critical errors.
1573+
* Return <0 for critical errors, and the sector will have its dirty flag cleared.
15741574
*
15751575
* Caller should make sure filepos < i_size and handle filepos >= i_size case.
15761576
*/
@@ -1593,8 +1593,17 @@ static int submit_one_sector(struct btrfs_inode *inode,
15931593
ASSERT(filepos < i_size);
15941594

15951595
em = btrfs_get_extent(inode, NULL, filepos, sectorsize);
1596-
if (IS_ERR(em))
1596+
if (IS_ERR(em)) {
1597+
/*
1598+
* When submission failed, we should still clear the folio dirty.
1599+
* Or the folio will be written back again but without any
1600+
* ordered extent.
1601+
*/
1602+
btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize);
1603+
btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize);
1604+
btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize);
15971605
return PTR_ERR(em);
1606+
}
15981607

15991608
extent_offset = filepos - em->start;
16001609
em_end = btrfs_extent_map_end(em);
@@ -1724,8 +1733,8 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
17241733
* Here we set writeback and clear for the range. If the full folio
17251734
* is no longer dirty then we clear the PAGECACHE_TAG_DIRTY tag.
17261735
*
1727-
* If we hit any error, the corresponding sector will still be dirty
1728-
* thus no need to clear PAGECACHE_TAG_DIRTY.
1736+
* If we hit any error, the corresponding sector will have its dirty
1737+
* flag cleared and writeback finished, thus no need to handle the error case.
17291738
*/
17301739
if (!submitted_io && !error) {
17311740
btrfs_folio_set_writeback(fs_info, folio, start, len);

0 commit comments

Comments
 (0)