* [PATCH] block: skip redundant flush for O_DSYNC direct writes
@ 2026-08-12 13:03 Zhenxian Ma
2026-08-12 16:42 ` Christoph Hellwig
0 siblings, 1 reply; 4+ messages in thread
From: Zhenxian Ma @ 2026-08-12 13:03 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, Zhenxian Ma, Zhenxian Ma
For an O_DIRECT | O_DSYNC write to a block device, dio_bio_write_op()
adds REQ_FUA to the bio, so the data is durable when the direct-IO
returns. blkdev_write_iter() then falls through to
generic_write_sync(), which for O_DSYNC turns into an additional
REQ_PREFLUSH. That flush is redundant: the FUA write already made
the data persistent.
Skip generic_write_sync() when the direct-IO path already provided
durability via FUA. The buffered and direct_write_fallback() paths
are unchanged.
Measured on a Seagate ST20000NM007D (20 TB, 7200 rpm, fua=1,
write_cache=write back), Linux v7.2.0-rc7, single-threaded pwrite()
loop opening the raw block device with O_WRONLY | O_DIRECT | O_DSYNC,
4 KiB writes for 60 s:
Sequential 4 KiB writes:
baseline patched
IOPS 119.7 7497.0
avg latency (us) 8357 133
p50 latency (us) 8346 127
p99 latency (us) 8368 395
p99.9 latency (us) 8728 569
Random 4 KiB writes (100 GiB span):
baseline patched
IOPS 156.1 666.4
avg latency (us) 6405 1500
p50 latency (us) 6186 1450
p99 latency (us) 16133 2285
p99.9 latency (us) 17250 9916
Signed-off-by: Zhenxian Ma <mzx199711@gmail.com>
Signed-off-by: Zhenxian Ma <mazhenxian@xiaohongshu.com>
---
block/fops.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/block/fops.c b/block/fops.c
index 15783a6180de..9c34a372b476 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -726,6 +726,7 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
bool atomic = iocb->ki_flags & IOCB_ATOMIC;
loff_t size = bdev_nr_bytes(bdev);
size_t shorted = 0;
+ bool dio_fua_done = false;
ssize_t ret;
if (bdev_read_only(bdev))
@@ -763,9 +764,13 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (iocb->ki_flags & IOCB_DIRECT) {
ret = blkdev_direct_write(iocb, from);
- if (ret >= 0 && iov_iter_count(from))
+ if (ret >= 0 && iov_iter_count(from)) {
ret = direct_write_fallback(iocb, from, ret,
blkdev_buffered_write(iocb, from));
+ } else if (ret > 0 && iocb_is_dsync(iocb)) {
+ /* FUA from dio_bio_write_op() already made it durable */
+ dio_fua_done = true;
+ }
} else {
/*
* Take i_rwsem and invalidate_lock to avoid racing with
@@ -777,7 +782,7 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
inode_unlock_shared(bd_inode);
}
- if (ret > 0)
+ if (ret > 0 && !dio_fua_done)
ret = generic_write_sync(iocb, ret);
iov_iter_reexpand(from, iov_iter_count(from) + shorted);
return ret;
--
2.43.5
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH] block: skip redundant flush for O_DSYNC direct writes
2026-08-12 13:03 [PATCH] block: skip redundant flush for O_DSYNC direct writes Zhenxian Ma
@ 2026-08-12 16:42 ` Christoph Hellwig
2026-08-13 7:35 ` 马振先
0 siblings, 1 reply; 4+ messages in thread
From: Christoph Hellwig @ 2026-08-12 16:42 UTC (permalink / raw)
To: Zhenxian Ma; +Cc: Jens Axboe, linux-block, Zhenxian Ma
> + bool dio_fua_done = false;
> ssize_t ret;
>
> if (bdev_read_only(bdev))
> @@ -763,9 +764,13 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
>
> if (iocb->ki_flags & IOCB_DIRECT) {
> ret = blkdev_direct_write(iocb, from);
> - if (ret >= 0 && iov_iter_count(from))
> + if (ret >= 0 && iov_iter_count(from)) {
> ret = direct_write_fallback(iocb, from, ret,
> blkdev_buffered_write(iocb, from));
> + } else if (ret > 0 && iocb_is_dsync(iocb)) {
> + /* FUA from dio_bio_write_op() already made it durable */
> + dio_fua_done = true;
This is kinda the wrong way aroud, it should just have a need_sync
variable that is only set for the fallback.
But looking at this, the original logic here actually is very fishy.
If we have a driver that does not actually support FUA, emulating it
for every write command is a bad idea. So this should be fixed to
only do FUA when actually useful, and then return from the non-fallback
implementations if that actually happened.
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH] block: skip redundant flush for O_DSYNC direct writes
2026-08-12 16:42 ` Christoph Hellwig
@ 2026-08-13 7:35 ` 马振先
2026-08-14 6:32 ` Christoph Hellwig
0 siblings, 1 reply; 4+ messages in thread
From: 马振先 @ 2026-08-13 7:35 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Jens Axboe, linux-block, Zhenxian Ma
On Thu, Aug 13, 2026 at 12:42 AM Christoph Hellwig <hch@infradead.org> wrote:
>
> > + bool dio_fua_done = false;
> > ssize_t ret;
> >
> > if (bdev_read_only(bdev))
> > @@ -763,9 +764,13 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
> >
> > if (iocb->ki_flags & IOCB_DIRECT) {
> > ret = blkdev_direct_write(iocb, from);
> > - if (ret >= 0 && iov_iter_count(from))
> > + if (ret >= 0 && iov_iter_count(from)) {
> > ret = direct_write_fallback(iocb, from, ret,
> > blkdev_buffered_write(iocb, from));
> > + } else if (ret > 0 && iocb_is_dsync(iocb)) {
> > + /* FUA from dio_bio_write_op() already made it durable */
> > + dio_fua_done = true;
>
> This is kinda the wrong way aroud, it should just have a need_sync
> variable that is only set for the fallback.
Agreed, that reads much cleaner, I'll modify the flag in v2.
> But looking at this, the original logic here actually is very fishy.
> If we have a driver that does not actually support FUA, emulating it
> for every write command is a bad idea. So this should be fixed to
> only do FUA when actually useful, and then return from the non-fallback
> implementations if that actually happened.
Let me make sure:
blkdev_write_iter() then only skips generic_write_sync() when
the direct-IO path actually issued REQ_FUA (i.e. iocb_is_dsync(iocb)
&& bdev_fua(bdev) && the write completed without falling back to
buffered). Is that the direction you had in mind? If yes, I'll send patch
v2 later.
Thanks for the review,
Zhenxian
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH] block: skip redundant flush for O_DSYNC direct writes
2026-08-13 7:35 ` 马振先
@ 2026-08-14 6:32 ` Christoph Hellwig
0 siblings, 0 replies; 4+ messages in thread
From: Christoph Hellwig @ 2026-08-14 6:32 UTC (permalink / raw)
To: 马振先
Cc: Christoph Hellwig, Jens Axboe, linux-block, Zhenxian Ma
On Thu, Aug 13, 2026 at 03:35:13PM +0800, 马振先 wrote:
> > But looking at this, the original logic here actually is very fishy.
> > If we have a driver that does not actually support FUA, emulating it
> > for every write command is a bad idea. So this should be fixed to
> > only do FUA when actually useful, and then return from the non-fallback
> > implementations if that actually happened.
>
> Let me make sure:
> blkdev_write_iter() then only skips generic_write_sync() when
> the direct-IO path actually issued REQ_FUA (i.e. iocb_is_dsync(iocb)
> && bdev_fua(bdev) && the write completed without falling back to
> buffered). Is that the direction you had in mind? If yes, I'll send patch
Yes. If you want you can probably do this as an incremental patch
on top of the new version of this patch.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-14 6:32 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-12 13:03 [PATCH] block: skip redundant flush for O_DSYNC direct writes Zhenxian Ma
2026-08-12 16:42 ` Christoph Hellwig
2026-08-13 7:35 ` 马振先
2026-08-14 6:32 ` Christoph Hellwig
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox