You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
btrfs: fix double accounting of ordered extents during errors
[BUG]
Btrfs will fail generic/750 randomly if its sector size is smaller than
page size.
One of the warning looks like this:
------------[ cut here ]------------
WARNING: CPU: 1 PID: 90263 at fs/btrfs/ordered-data.c:360 can_finish_ordered_extent+0x33c/0x390 [btrfs]
CPU: 1 UID: 0 PID: 90263 Comm: kworker/u18:1 Tainted: G OE 6.12.0-rc3-custom+ torvalds#79
Workqueue: events_unbound btrfs_async_reclaim_metadata_space [btrfs]
pc : can_finish_ordered_extent+0x33c/0x390 [btrfs]
lr : can_finish_ordered_extent+0xdc/0x390 [btrfs]
Call trace:
can_finish_ordered_extent+0x33c/0x390 [btrfs]
btrfs_mark_ordered_io_finished+0x130/0x2b8 [btrfs]
extent_writepage+0xfc/0x338 [btrfs]
extent_write_cache_pages+0x1d4/0x4b8 [btrfs]
btrfs_writepages+0x94/0x158 [btrfs]
do_writepages+0x74/0x190
filemap_fdatawrite_wbc+0x88/0xc8
start_delalloc_inodes+0x180/0x3b0 [btrfs]
btrfs_start_delalloc_roots+0x17c/0x288 [btrfs]
shrink_delalloc+0x11c/0x280 [btrfs]
flush_space+0x27c/0x310 [btrfs]
btrfs_async_reclaim_metadata_space+0xcc/0x208 [btrfs]
process_one_work+0x228/0x670
worker_thread+0x1bc/0x360
kthread+0x100/0x118
ret_from_fork+0x10/0x20
irq event stamp: 9784200
hardirqs last enabled at (9784199): [<ffffd21ec54dc01c>] _raw_spin_unlock_irqrestore+0x74/0x80
hardirqs last disabled at (9784200): [<ffffd21ec54db374>] _raw_spin_lock_irqsave+0x8c/0xa0
softirqs last enabled at (9784148): [<ffffd21ec472ff44>] handle_softirqs+0x45c/0x4b0
softirqs last disabled at (9784141): [<ffffd21ec46d01e4>] __do_softirq+0x1c/0x28
---[ end trace 0000000000000000 ]---
BTRFS critical (device dm-2): bad ordered extent accounting, root=5 ino=1492 OE offset=1654784 OE len=57344 to_dec=49152 left=0
[CAUSE]
There are several error paths not properly handling during folio
writeback:
1) Partially submitted folio
During extent_writepage_io() if some error happened (the only
possible case is submit_one_sector() failed to grab an extent map),
then we can have partially submitted folio.
Since extent_writepage_io() failed, we need to call
btrfs_mark_ordered_io_finished() to cleanup the submitted range.
But we will call btrfs_mark_ordered_io_finished() for submitted range
too, causing double accounting.
2) Partially created ordered extents
We cal also fail at writepage_delalloc(), which will stop creating
new ordered extents if it hit any error from
btrfs_run_delalloc_range().
In that case, we will call btrfs_mark_ordered_io_finished() for
ranges where there is no ordered extent at all.
Both bugs are only affecting sector size < page size cases.
[FIX]
- Introduce a new member btrfs_bio_ctrl::last_submitted
This will trace the last sector submitted through
extent_writepage_io().
So for the above extent_writepage() case, we will know exactly which
sectors are submitted and should not do the ordered extent accounting.
- Clear the submit_bitmap for ranges where no ordered extent is created
So if btrfs_run_delalloc_range() failed for a range, it will be not
cleaned up.
- Pass NULL to __unlock_delalloc() inside writepage_delalloc() for error
handling
Since we will no longer unlock the folio range (subbit_bitmap
cleared), so we have to unlock the folio at the error handling.
- Introduce a helper cleanup_ordered_extents()
This will do a sector-by-sector cleanup with
btrfs_bio_ctrl::last_submitted and btrfs_bio_ctrl::submit_bitmap into
consideartion.
Using @last_submitted is to avoid double accounting on the submitted
ranges.
Meanwhile using @submit_bitmap is to avoid touching ranges going
through compression.
cc: stable@vger.kernel.org # 5.15+
Signed-off-by: Qu Wenruo <wqu@suse.com>
0 commit comments