From: Wang Yugui <wangyugui@e16-tech.com>
To: Qu Wenruo <wqu@suse.com>
Cc: linux-btrfs@vger.kernel.org
Subject: Re: [PATCH v2] btrfs: use IOMAP_DIO_BOUNCE flag instead of falling back to buffered IO
Date: Tue, 26 May 2026 07:55:38 +0800 [thread overview]
Message-ID: <20260526075538.BA75.409509F4@e16-tech.com> (raw)
In-Reply-To: <e578ff1a2dd9a4f720ea77bc601534734ce87e18.1779685479.git.wqu@suse.com>
Hi,
> 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;
>
> return __iomap_dio_rw(iocb, iter, &btrfs_dio_iomap_ops, &btrfs_dio_ops,
> - IOMAP_DIO_PARTIAL | IOMAP_DIO_FSBLOCK_ALIGNED, &data, done_before);
> + dio_flags, &data, done_before);
> }
>
> static ssize_t check_direct_IO(struct btrfs_fs_info *fs_info,
> @@ -800,8 +815,6 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
> ssize_t ret;
> unsigned int ilock_flags = 0;
> struct iomap_dio *dio;
> - const u64 data_profile = btrfs_data_alloc_profile(fs_info) &
> - BTRFS_BLOCK_GROUP_PROFILE_MASK;
>
> if (iocb->ki_flags & IOCB_NOWAIT)
> ilock_flags |= BTRFS_ILOCK_TRY;
> @@ -815,16 +828,6 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
> if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode) && IS_NOSEC(inode))
> ilock_flags |= BTRFS_ILOCK_SHARED;
>
> - /*
> - * If our data profile has duplication (either extra mirrors or RAID56),
> - * we can not trust the direct IO buffer, the content may change during
> - * writeback and cause different contents written to different mirrors.
> - *
> - * Thus only RAID0 and SINGLE can go true zero-copy direct IO.
> - */
> - if (data_profile != BTRFS_BLOCK_GROUP_RAID0 && data_profile != 0)
> - goto buffered;
> -
> relock:
> ret = btrfs_inode_lock(BTRFS_I(inode), ilock_flags);
> if (ret < 0)
> @@ -865,22 +868,6 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
> btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);
> goto buffered;
> }
> - /*
> - * We can't control the folios being passed in, applications can write
> - * to them while a direct IO write is in progress. This means the
> - * content might change after we calculated the data checksum.
> - * Therefore we can end up storing a checksum that doesn't match the
> - * persisted data.
> - *
> - * To be extra safe and avoid false data checksum mismatch, if the
> - * inode requires data checksum, just fallback to buffered IO.
> - * For buffered IO we have full control of page cache and can ensure
> - * no one is modifying the content during writeback.
> - */
> - if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
> - btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);
> - goto buffered;
> - }
>
> /*
> * The iov_iter can be mapped to the same file range we are writing to.
Do we need this too just like xfs?
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index fa82def46e39..64eae7417242 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3843,7 +3843,7 @@ const struct file_operations btrfs_file_operations = {
#endif
.remap_file_range = btrfs_remap_file_range,
.uring_cmd = btrfs_uring_cmd,
- .fop_flags = FOP_BUFFER_RASYNC | FOP_BUFFER_WASYNC,
+ .fop_flags = FOP_BUFFER_RASYNC | FOP_BUFFER_WASYNC | FOP_DONTCACHE,
};
int btrfs_fdatawrite_range(struct btrfs_inode *inode, loff_t start, loff_t end)
Best Regards
Wang Yugui (wangyugui@e16-tech.com)
2026/05/26
next prev parent reply other threads:[~2026-05-25 23:55 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
2026-05-26 6:38 ` Christoph Hellwig
2026-05-25 23:55 ` Wang Yugui [this message]
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=20260526075538.BA75.409509F4@e16-tech.com \
--to=wangyugui@e16-tech.com \
--cc=linux-btrfs@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