From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org
Cc: Qu Wenruo <wqu@suse.com>, Filipe Manana <fdmanana@suse.com>,
David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>
Subject: [PATCH 6.18.y 3/5] btrfs: remove folio parameter from ordered io related functions
Date: Mon, 20 Jul 2026 12:51:13 -0400 [thread overview]
Message-ID: <20260720165115.2527045-3-sashal@kernel.org> (raw)
In-Reply-To: <20260720165115.2527045-1-sashal@kernel.org>
From: Qu Wenruo <wqu@suse.com>
[ Upstream commit a11d6912fdd9e57aff889ec97256b1d6b4e5bf06 ]
Both functions btrfs_finish_ordered_extent() and
btrfs_mark_ordered_io_finished() are accepting an optional folio
parameter.
That @folio is passed into can_finish_ordered_extent(), which later will
test and clear the ordered flag for the involved range.
However I do not think there is any other call site that can clear
ordered flags of an page cache folio and can affect
can_finish_ordered_extent().
There are limited *_clear_ordered() callers out of
can_finish_ordered_extent() function:
- btrfs_migrate_folio()
This is completely unrelated, it's just migrating the ordered flag to
the new folio.
- btrfs_cleanup_ordered_extents()
We manually clean the ordered flags of all involved folios, then call
btrfs_mark_ordered_io_finished() without a @folio parameter.
So it doesn't need and didn't pass a @folio parameter in the first
place.
- btrfs_writepage_fixup_worker()
This function is going to be removed soon, and we should not hit that
function anymore.
- btrfs_invalidate_folio()
This is the real call site we need to bother with.
If we already have a bio running, btrfs_finish_ordered_extent() in
end_bbio_data_write() will be executed first, as
btrfs_invalidate_folio() will wait for the writeback to finish.
Thus if there is a running bio, it will not see the range has
ordered flags, and just skip to the next range.
If there is no bio running, meaning the ordered extent is created but
the folio is not yet submitted.
In that case btrfs_invalidate_folio() will manually clear the folio
ordered range, but then manually finish the ordered extent with
btrfs_dec_test_ordered_pending() without bothering the folio ordered
flags.
Meaning if the OE range with folio ordered flags will be finished
manually without the need to call can_finish_ordered_extent().
This means all can_finish_ordered_extent() call sites should get a range
that has folio ordered flag set, thus the old "return false" branch
should never be triggered.
Now we can:
- Remove the @folio parameter from involved functions
* btrfs_mark_ordered_io_finished()
* btrfs_finish_ordered_extent()
For call sites passing a @folio into those functions, let them
manually clear the ordered flag of involved folios.
- Move btrfs_finish_ordered_extent() out of the loop in
end_bbio_data_write()
We only need to call btrfs_finish_ordered_extent() once per bbio,
not per folio.
- Add an ASSERT() to make sure all folio ranges have ordered flags
It's only for end_bbio_data_write().
And we already have enough safe nets to catch over-accounting of ordered
extents.
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Stable-dep-of: ff66fe666233 ("btrfs: fix incorrect buffered IO fallback for append direct writes")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/btrfs/compression.c | 2 +-
fs/btrfs/direct-io.c | 9 ++++-----
fs/btrfs/extent_io.c | 23 ++++++++++++++---------
fs/btrfs/inode.c | 10 ++++++----
fs/btrfs/ordered-data.c | 29 +++++------------------------
fs/btrfs/ordered-data.h | 6 ++----
6 files changed, 32 insertions(+), 47 deletions(-)
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 8c3899832a1aa1..42ba417acc28d0 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -324,7 +324,7 @@ static void btrfs_finish_compressed_write_work(struct work_struct *work)
struct compressed_bio *cb =
container_of(work, struct compressed_bio, write_end_work);
- btrfs_finish_ordered_extent(cb->bbio.ordered, NULL, cb->start, cb->len,
+ btrfs_finish_ordered_extent(cb->bbio.ordered, cb->start, cb->len,
cb->bbio.bio.bi_status == BLK_STS_OK);
if (cb->writeback)
diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c
index 3836414cbe3716..3460a8bcbad76d 100644
--- a/fs/btrfs/direct-io.c
+++ b/fs/btrfs/direct-io.c
@@ -625,7 +625,7 @@ static int btrfs_dio_iomap_end(struct inode *inode, loff_t pos, loff_t length,
pos += submitted;
length -= submitted;
if (write)
- btrfs_finish_ordered_extent(dio_data->ordered, NULL,
+ btrfs_finish_ordered_extent(dio_data->ordered,
pos, length, false);
else
btrfs_unlock_dio_extent(&BTRFS_I(inode)->io_tree, pos,
@@ -657,9 +657,8 @@ static void btrfs_dio_end_io(struct btrfs_bio *bbio)
}
if (btrfs_op(bio) == BTRFS_MAP_WRITE) {
- btrfs_finish_ordered_extent(bbio->ordered, NULL,
- dip->file_offset, dip->bytes,
- !bio->bi_status);
+ btrfs_finish_ordered_extent(bbio->ordered, dip->file_offset,
+ dip->bytes, !bio->bi_status);
} else {
btrfs_unlock_dio_extent(&inode->io_tree, dip->file_offset,
dip->file_offset + dip->bytes - 1, NULL);
@@ -735,7 +734,7 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio,
ret = btrfs_extract_ordered_extent(bbio, dio_data->ordered);
if (ret) {
- btrfs_finish_ordered_extent(dio_data->ordered, NULL,
+ btrfs_finish_ordered_extent(dio_data->ordered,
file_offset, dip->bytes,
!ret);
bio->bi_status = errno_to_blk_status(ret);
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 88b5581df7dc16..f955b4003d4246 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -523,6 +523,7 @@ static void end_bbio_data_write(struct btrfs_bio *bbio)
int error = blk_status_to_errno(bio->bi_status);
struct folio_iter fi;
const u32 sectorsize = fs_info->sectorsize;
+ u32 bio_size = 0;
ASSERT(!bio_flagged(bio, BIO_CLONED));
bio_for_each_folio_all(fi, bio) {
@@ -530,6 +531,7 @@ static void end_bbio_data_write(struct btrfs_bio *bbio)
u64 start = folio_pos(folio) + fi.offset;
u32 len = fi.length;
+ bio_size += len;
/* Our read/write should always be sector aligned. */
if (!IS_ALIGNED(fi.offset, sectorsize))
btrfs_err(fs_info,
@@ -540,13 +542,15 @@ static void end_bbio_data_write(struct btrfs_bio *bbio)
"incomplete page write with offset %zu and length %zu",
fi.offset, fi.length);
- btrfs_finish_ordered_extent(bbio->ordered, folio, start, len,
- !error);
if (error)
mapping_set_error(folio->mapping, error);
+
+ ASSERT(btrfs_folio_test_ordered(fs_info, folio, start, len));
+ btrfs_folio_clear_ordered(fs_info, folio, start, len);
btrfs_folio_clear_writeback(fs_info, folio, start, len);
}
+ btrfs_finish_ordered_extent(bbio->ordered, bbio->file_offset, bio_size, !error);
bio_put(bio);
}
@@ -1566,7 +1570,8 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode,
u64 start = page_start + (start_bit << fs_info->sectorsize_bits);
u32 len = (end_bit - start_bit) << fs_info->sectorsize_bits;
- btrfs_mark_ordered_io_finished(inode, folio, start, len, false);
+ btrfs_folio_clear_ordered(fs_info, folio, start, len);
+ btrfs_mark_ordered_io_finished(inode, start, len, false);
}
return ret;
}
@@ -1642,6 +1647,7 @@ static int submit_one_sector(struct btrfs_inode *inode,
* ordered extent.
*/
btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize);
+ btrfs_folio_clear_ordered(fs_info, folio, filepos, sectorsize);
btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize);
btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize);
@@ -1649,8 +1655,8 @@ static int submit_one_sector(struct btrfs_inode *inode,
* Since there is no bio submitted to finish the ordered
* extent, we have to manually finish this sector.
*/
- btrfs_mark_ordered_io_finished(inode, folio, filepos,
- fs_info->sectorsize, false);
+ btrfs_mark_ordered_io_finished(inode, filepos, fs_info->sectorsize,
+ false);
return PTR_ERR(em);
}
@@ -1761,8 +1767,8 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
btrfs_put_ordered_extent(ordered);
- btrfs_mark_ordered_io_finished(inode, folio, cur,
- fs_info->sectorsize, true);
+ btrfs_folio_clear_ordered(fs_info, folio, cur, fs_info->sectorsize);
+ btrfs_mark_ordered_io_finished(inode, cur, fs_info->sectorsize, true);
/*
* This range is beyond i_size, thus we don't need to
* bother writing back.
@@ -2640,8 +2646,7 @@ void extent_write_locked_range(struct inode *inode, const struct folio *locked_f
if (IS_ERR(folio)) {
cur_end = min(round_down(cur, PAGE_SIZE) + PAGE_SIZE - 1, end);
cur_len = cur_end + 1 - cur;
- btrfs_mark_ordered_io_finished(BTRFS_I(inode), NULL,
- cur, cur_len, false);
+ btrfs_mark_ordered_io_finished(BTRFS_I(inode), cur, cur_len, false);
mapping_set_error(mapping, PTR_ERR(folio));
cur = cur_end;
continue;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index b972d4cba98023..25b4a8fd31133f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -424,7 +424,7 @@ static inline void btrfs_cleanup_ordered_extents(struct btrfs_inode *inode,
folio_put(folio);
}
- return btrfs_mark_ordered_io_finished(inode, NULL, offset, bytes, false);
+ return btrfs_mark_ordered_io_finished(inode, offset, bytes, false);
}
static int btrfs_dirty_inode(struct btrfs_inode *inode);
@@ -2864,7 +2864,9 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
* to reflect the errors and clean the page.
*/
mapping_set_error(folio->mapping, ret);
- btrfs_mark_ordered_io_finished(inode, folio, page_start,
+ btrfs_folio_clear_ordered(fs_info, folio, page_start,
+ folio_size(folio));
+ btrfs_mark_ordered_io_finished(inode, page_start,
folio_size(folio), !ret);
folio_clear_dirty_for_io(folio);
}
@@ -7679,11 +7681,11 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset,
EXTENT_LOCKED | EXTENT_DO_ACCOUNTING |
EXTENT_DEFRAG, &cached_state);
- spin_lock_irq(&inode->ordered_tree_lock);
+ spin_lock(&inode->ordered_tree_lock);
set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
ordered->truncated_len = min(ordered->truncated_len,
cur - ordered->file_offset);
- spin_unlock_irq(&inode->ordered_tree_lock);
+ spin_unlock(&inode->ordered_tree_lock);
/*
* If the ordered extent has finished, we're safe to delete all
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 8a8aa6ed405bd4..89f6b6f8cc5312 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -348,30 +348,13 @@ static void finish_ordered_fn(struct btrfs_work *work)
}
static bool can_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
- struct folio *folio, u64 file_offset,
- u64 len, bool uptodate)
+ u64 file_offset, u64 len, bool uptodate)
{
struct btrfs_inode *inode = ordered->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
lockdep_assert_held(&inode->ordered_tree_lock);
- if (folio) {
- ASSERT(folio->mapping);
- ASSERT(folio_pos(folio) <= file_offset);
- ASSERT(file_offset + len <= folio_end(folio));
-
- /*
- * Ordered flag indicates whether we still have
- * pending io unfinished for the ordered extent.
- *
- * If it's not set, we need to skip to next range.
- */
- if (!btrfs_folio_test_ordered(fs_info, folio, file_offset, len))
- return false;
- btrfs_folio_clear_ordered(fs_info, folio, file_offset, len);
- }
-
/* Now we're fine to update the accounting. */
if (WARN_ON_ONCE(len > ordered->bytes_left)) {
btrfs_crit(fs_info,
@@ -413,8 +396,7 @@ static void btrfs_queue_ordered_fn(struct btrfs_ordered_extent *ordered)
}
void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
- struct folio *folio, u64 file_offset, u64 len,
- bool uptodate)
+ u64 file_offset, u64 len, bool uptodate)
{
struct btrfs_inode *inode = ordered->inode;
unsigned long flags;
@@ -423,7 +405,7 @@ void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
trace_btrfs_finish_ordered_extent(inode, file_offset, len, uptodate);
spin_lock_irqsave(&inode->ordered_tree_lock, flags);
- ret = can_finish_ordered_extent(ordered, folio, file_offset, len,
+ ret = can_finish_ordered_extent(ordered, file_offset, len,
uptodate);
spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
@@ -476,8 +458,7 @@ void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
* extent(s) covering it.
*/
void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode,
- struct folio *folio, u64 file_offset,
- u64 num_bytes, bool uptodate)
+ u64 file_offset, u64 num_bytes, bool uptodate)
{
struct rb_node *node;
struct btrfs_ordered_extent *entry = NULL;
@@ -540,7 +521,7 @@ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode,
ASSERT(end + 1 - cur < U32_MAX);
len = end + 1 - cur;
- if (can_finish_ordered_extent(entry, folio, cur, len, uptodate)) {
+ if (can_finish_ordered_extent(entry, cur, len, uptodate)) {
spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
btrfs_queue_ordered_fn(entry);
spin_lock_irqsave(&inode->ordered_tree_lock, flags);
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 1e6b0b182b2954..ab71c164fb0898 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -164,11 +164,9 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry);
void btrfs_remove_ordered_extent(struct btrfs_inode *btrfs_inode,
struct btrfs_ordered_extent *entry);
void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
- struct folio *folio, u64 file_offset, u64 len,
- bool uptodate);
+ u64 file_offset, u64 len, bool uptodate);
void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode,
- struct folio *folio, u64 file_offset,
- u64 num_bytes, bool uptodate);
+ u64 file_offset, u64 num_bytes, bool uptodate);
bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode,
struct btrfs_ordered_extent **cached,
u64 file_offset, u64 io_size);
--
2.53.0
next prev parent reply other threads:[~2026-07-20 16:51 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-15 10:13 FAILED: patch "[PATCH] btrfs: fix incorrect buffered IO fallback for append direct" failed to apply to 6.18-stable tree gregkh
2026-07-20 16:51 ` [PATCH 6.18.y 1/5] btrfs: concentrate the error handling of submit_one_sector() Sasha Levin
2026-07-20 16:51 ` [PATCH 6.18.y 2/5] btrfs: replace for_each_set_bit() with for_each_set_bitmap() Sasha Levin
2026-07-20 16:51 ` Sasha Levin [this message]
2026-07-20 16:51 ` [PATCH 6.18.y 4/5] btrfs: fix false IO failure after falling back to buffered write Sasha Levin
2026-07-20 16:51 ` [PATCH 6.18.y 5/5] btrfs: fix incorrect buffered IO fallback for append direct writes Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260720165115.2527045-3-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=dsterba@suse.com \
--cc=fdmanana@suse.com \
--cc=stable@vger.kernel.org \
--cc=wqu@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox