Skip to content

Commit c7459b0

Browse files
adam900710kdave
authored andcommitted
btrfs: make sure all ordered extents beyond EOF is properly truncated
[POSSIBLE BUG] If there are multiple ordered extents beyond EOF, at folio writeback time we may only truncate the first ordered extent, but leaving the remaining ones finished but not marked as truncated. Since those OEs are not marked as truncated, it will still insert an file extent item, and may lead to false missing csum errors during "btrfs check". [CAUSE] Since we have bs < ps support for a while and experimental large data folios are also going to graduate from experimental features soon, we can have the following folio to be written back: fs block size 4K page size 4K, folio size 64K. 0 16K 32K 64K |<---------------- Dirty -------------->| |<-OE A->|<-OE B->|<----- OE C -------->| | i_size 4K. In above case we need to submit the writeback for the range [0, 4K). For range [4K, 64K) there is no need to submit any IO but mark the involved OEs (OE A, B, C) all as truncated. However during the EOF handling, we only call btrfs_lookup_first_ordered_range() once, thus only got OE A and mark it as truncated. But OE B and C are not marked as truncated, they will finish as usual, which will leave a regular file extent item to be inserted beyond EOF, and without any data checksum. [FIX] Introduce a new helper, btrfs_mark_ordered_io_truncated(), to handle all OEs of a range, and mark them all as truncated. With that helper, all OEs (A B and C) will be marked as truncated. OE B and C will have 0 truncated_len, preventing any file extent item to be inserted from them. Fixes: f0a0f5b ("btrfs: truncate ordered extent when skipping writeback past i_size") Reviewed-by: Boris Burkov <boris@bur.io> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 1553e00 commit c7459b0

File tree

3 files changed

+41
-18
lines changed

3 files changed

+41
-18
lines changed

fs/btrfs/extent_io.c

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,24 +1725,7 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
17251725
cur = folio_pos(folio) + (bit << fs_info->sectorsize_bits);
17261726

17271727
if (cur >= i_size) {
1728-
struct btrfs_ordered_extent *ordered;
1729-
1730-
ordered = btrfs_lookup_first_ordered_range(inode, cur,
1731-
folio_end - cur);
1732-
/*
1733-
* We have just run delalloc before getting here, so
1734-
* there must be an ordered extent.
1735-
*/
1736-
ASSERT(ordered != NULL);
1737-
spin_lock(&inode->ordered_tree_lock);
1738-
set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
1739-
ordered->truncated_len = min(ordered->truncated_len,
1740-
cur - ordered->file_offset);
1741-
spin_unlock(&inode->ordered_tree_lock);
1742-
btrfs_put_ordered_extent(ordered);
1743-
1744-
btrfs_mark_ordered_io_finished(inode, folio, cur,
1745-
end - cur, true);
1728+
btrfs_mark_ordered_io_truncated(inode, folio, cur, end - cur);
17461729
/*
17471730
* This range is beyond i_size, thus we don't need to
17481731
* bother writing back.

fs/btrfs/ordered-data.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,44 @@ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode,
546546
spin_unlock(&inode->ordered_tree_lock);
547547
}
548548

549+
/*
550+
* Mark all ordered extents io inside the specified range as truncated.
551+
*
552+
* This is utilized by writeback path, thus there must be an ordered extent
553+
* for the range.
554+
*/
555+
void btrfs_mark_ordered_io_truncated(struct btrfs_inode *inode, struct folio *folio,
556+
u64 file_offset, u32 len)
557+
{
558+
u64 cur = file_offset;
559+
560+
ASSERT(file_offset >= folio_pos(folio));
561+
ASSERT(file_offset + len <= folio_end(folio));
562+
563+
while (cur < file_offset + len) {
564+
u32 cur_len = file_offset + len - cur;
565+
struct btrfs_ordered_extent *ordered;
566+
567+
ordered = btrfs_lookup_first_ordered_range(inode, cur, cur_len);
568+
569+
/*
570+
* We have just run delalloc before getting here, so there must
571+
* be an ordered extent.
572+
*/
573+
ASSERT(ordered != NULL);
574+
scoped_guard(spinlock, &inode->ordered_tree_lock) {
575+
set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
576+
ordered->truncated_len = min(ordered->truncated_len,
577+
cur - ordered->file_offset);
578+
}
579+
cur_len = min(cur_len, ordered->file_offset + ordered->num_bytes - cur);
580+
btrfs_put_ordered_extent(ordered);
581+
582+
cur += cur_len;
583+
}
584+
btrfs_mark_ordered_io_finished(inode, folio, file_offset, len, true);
585+
}
586+
549587
/*
550588
* Finish IO for one ordered extent across a given range. The range can only
551589
* contain one ordered extent.

fs/btrfs/ordered-data.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
169169
void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode,
170170
struct folio *folio, u64 file_offset,
171171
u64 num_bytes, bool uptodate);
172+
void btrfs_mark_ordered_io_truncated(struct btrfs_inode *inode, struct folio *folio,
173+
u64 file_offset, u32 len);
172174
bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode,
173175
struct btrfs_ordered_extent **cached,
174176
u64 file_offset, u64 io_size);

0 commit comments

Comments
 (0)