block: only use REQ_FUA for direct writes if the device supports it

When a block device does not support FUA natively, the block layer
emulates it by adding a cache flush to every write bio.  An O_DSYNC
direct write that spans N bios then costs N flushes, rather than the
single generic_write_sync() issued after the write completes.

Introduce blkdev_dio_fua() to decide when REQ_FUA is set.  A synchronous
write can rely on generic_write_sync() when the device lacks FUA, so it
sets REQ_FUA only when bdev_fua() is true.  An asynchronous write
completes in blkdev_bio_end_io() and cannot call the blocking
generic_write_sync(), so it keeps REQ_FUA (emulated when needed) to stay
durable.

Suggested-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Zhenxian Ma <mzx199711@gmail.com>
Signed-off-by: Zhenxian Ma <mazhenxian@xiaohongshu.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/tencent_A41247F29AC0971AE96B52964BE4491B440A@qq.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Zhenxian Ma 2026-08-15 20:02:15 +08:00 committed by Jens Axboe
parent bec7d36a65
commit 0d492f40c4

View File

@ -26,12 +26,26 @@ static inline struct inode *bdev_file_inode(struct file *file)
return file->f_mapping->host;
}
static blk_opf_t dio_bio_write_op(struct kiocb *iocb)
static bool blkdev_dio_fua(struct kiocb *iocb, struct block_device *bdev)
{
if (!iocb_is_dsync(iocb))
return false;
/*
* Async writes cannot fall back to generic_write_sync(), so they must
* use FUA (emulated if needed); sync writes only need it when the
* device supports FUA natively.
*/
if (!is_sync_kiocb(iocb))
return true;
return bdev_fua(bdev);
}
static blk_opf_t dio_bio_write_op(struct kiocb *iocb, struct block_device *bdev)
{
blk_opf_t opf = REQ_OP_WRITE | REQ_SYNC | REQ_IDLE;
/* avoid the need for a I/O completion work item */
if (iocb_is_dsync(iocb))
if (blkdev_dio_fua(iocb, bdev))
opf |= REQ_FUA;
return opf;
}
@ -75,7 +89,8 @@ static ssize_t __blkdev_direct_IO_simple(struct kiocb *iocb,
if (user_backed_iter(iter))
should_dirty = true;
} else {
bio_init(&bio, bdev, vecs, nr_pages, dio_bio_write_op(iocb));
bio_init(&bio, bdev, vecs, nr_pages,
dio_bio_write_op(iocb, bdev));
}
bio.bi_iter.bi_sector = pos >> SECTOR_SHIFT;
bio.bi_write_hint = file_inode(iocb->ki_filp)->i_write_hint;
@ -179,7 +194,7 @@ static ssize_t __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
struct blkdev_dio *dio;
struct bio *bio;
bool is_read = (iov_iter_rw(iter) == READ), is_sync;
blk_opf_t opf = is_read ? REQ_OP_READ : dio_bio_write_op(iocb);
blk_opf_t opf = is_read ? REQ_OP_READ : dio_bio_write_op(iocb, bdev);
loff_t pos = iocb->ki_pos;
int ret = 0;
@ -325,7 +340,7 @@ static ssize_t __blkdev_direct_IO_async(struct kiocb *iocb,
unsigned int nr_pages)
{
bool is_read = iov_iter_rw(iter) == READ;
blk_opf_t opf = is_read ? REQ_OP_READ : dio_bio_write_op(iocb);
blk_opf_t opf = is_read ? REQ_OP_READ : dio_bio_write_op(iocb, bdev);
struct blkdev_dio *dio;
struct bio *bio;
loff_t pos = iocb->ki_pos;
@ -770,6 +785,13 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
ret = direct_write_fallback(iocb, from, ret,
blkdev_buffered_write(iocb, from));
need_sync = true;
} else if (ret > 0 && !blkdev_dio_fua(iocb, bdev)) {
/*
* The device does not support FUA, so REQ_FUA was not
* set and the O_DSYNC direct write still needs a flush
* to be made durable.
*/
need_sync = true;
}
} else {
/*