From: Qu Wenruo <wqu@suse.com>
To: linux-btrfs@vger.kernel.org,
Christoph Hellwig <hch@infradead.org>,
Filipe Manana <fdmanana@kernel.org>
Subject: Re: [PATCH v2] btrfs: use IOMAP_DIO_BOUNCE flag instead of falling back to buffered IO
Date: Tue, 26 May 2026 10:08:24 +0930 [thread overview]
Message-ID: <dd2e62d9-086d-4c55-b521-e7fa101065e7@suse.com> (raw)
In-Reply-To: <150d5b1f-d1b0-48c1-ae33-56b4c049576f@suse.com>
在 2026/5/25 18:44, Qu Wenruo 写道:
>
>
> 在 2026/5/25 14:35, Qu Wenruo 写道:
>> Previously btrfs forces direct writes to fall back to buffered ones if
>> the
>> inode has data checksum or the profile has duplication.
>>
>> That fallback is to avoid the content being modified that the final
>> content may mismatch with the checksum or the other mirrors.
>>
>> That brings a pretty huge performance cost, which already caused some
>> concern at that time.
>>
>> But later upstream commit c9d114846b38 ("iomap: add a flag to bounce
>> buffer direct I/O") introduced a new method by copying the content into
>> new pages, and do all the operations based on the newly allocated pages.
>>
>> So let btrfs to utilize the new flag for direct writes if we require
>> stable folios.
>>
>> There is a quick benchmark, using the following fio setup:
>>
>> fio --name=randwrite --filename $mnt/foobar --ioengine=libaio --
>> size=4G \
>> --rw=randwrite --iodepth=64 --runtime=60 --time_based --direct=1 \
>> --bs=$blocksize
>>
>> Unit is MiB/s.
>>
>> Blocksize | Zero-copy (*) | Buffered | Bounce
>> -----------+---------------+----------+-----------
>> 4K | 35.1 | 17.1 | 33.8
>> 64K | 522 | 251 | 492
>>
>> *: This is done by reverting the commit 968f19c5b1b7 ("btrfs: always
>> fallback to buffered write if the inode requires checksum")
>>
>> Although with page bouncing the performance is only around 95% of
>> true-zero copy, it's still almost double the performance of buffered
>> fallback.
>>
>> Signed-off-by: Qu Wenruo <wqu@suse.com>
>> ---
>> Changelog:
>> v2:
>> - Rework the comment in btrfs_dio_write()
>> ---
>> fs/btrfs/direct-io.c | 45 ++++++++++++++++----------------------------
>> 1 file changed, 16 insertions(+), 29 deletions(-)
>>
>> diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c
>> index 57167d56dc72..173fe065fc38 100644
>> --- a/fs/btrfs/direct-io.c
>> +++ b/fs/btrfs/direct-io.c
>> @@ -768,10 +768,25 @@ static ssize_t btrfs_dio_read(struct kiocb
>> *iocb, struct iov_iter *iter,
>> static struct iomap_dio *btrfs_dio_write(struct kiocb *iocb, struct
>> iov_iter *iter,
>> size_t done_before)
>> {
>> + struct btrfs_inode *inode = BTRFS_I(file_inode(iocb->ki_filp));
>> struct btrfs_dio_data data = { 0 };
>> + const u64 data_profile = btrfs_data_alloc_profile(inode->root-
>> >fs_info) &
>> + BTRFS_BLOCK_GROUP_PROFILE_MASK;
>> + unsigned int dio_flags = IOMAP_DIO_PARTIAL |
>> IOMAP_DIO_FSBLOCK_ALIGNED;
>> +
>> + /*
>> + * Userspace may modify the buffer while DIO is in flight. With
>> + * data checksumming this would produce a checksum that doesn't
>> + * match the persisted data; with duplicated profiles the mirrors
>> + * would diverge. Bounce in those cases so writeback sees stable
>> + * content.
>> + */
>> + if (!(inode->flags & BTRFS_INODE_NODATASUM) ||
>> + (data_profile != BTRFS_BLOCK_GROUP_RAID0 && data_profile != 0))
>> + dio_flags |= IOMAP_DIO_BOUNCE;
>
> Unfortunately this will deadlock at generic/647.
>
> Currently btrfs avoids the deadlock by disabling page fault for the
> @from iov_iter.
>
> But that iov_iter->nofault is not respected during
> bio_iov_iter_bounce_write() -> copy_from_iter(), thus we will hit a
> deadlock at exactly the situation described in the comment just before
> btrfs_dio_write() call.
It turns out that we can simply disable page faulting during
btrfs_dio_write().
But this will come with new problems.
With page fault disabled, we will not hit the deadlock, but we will hit
another case where we have already allocated a new OE, then page
bouncing failed, resulting no real dio bio being submitted.
Then we go into btrfs_dio_iomap_end() which will mark the allocated OE
as error.
Later even if we fall back to buffered write, we got to
fdatawait_range(), and since we got an OE marked as error, it returned
error, failing the buffered write fallback.
So this new BOUNCE flag indeed exposed several btrfs error handling
problems.
next prev parent reply other threads:[~2026-05-26 0:38 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-25 5:05 [PATCH v2] btrfs: use IOMAP_DIO_BOUNCE flag instead of falling back to buffered IO Qu Wenruo
2026-05-25 7:05 ` Christoph Hellwig
2026-05-25 7:16 ` Qu Wenruo
2026-05-25 7:23 ` Christoph Hellwig
2026-05-25 9:14 ` Qu Wenruo
2026-05-26 0:38 ` Qu Wenruo [this message]
2026-05-26 6:38 ` Christoph Hellwig
2026-05-25 23:55 ` Wang Yugui
2026-05-26 17:59 ` Boris Burkov
2026-05-26 21:42 ` Qu Wenruo
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=dd2e62d9-086d-4c55-b521-e7fa101065e7@suse.com \
--to=wqu@suse.com \
--cc=fdmanana@kernel.org \
--cc=hch@infradead.org \
--cc=linux-btrfs@vger.kernel.org \
/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