[PATCH v2 2/2] block: only use REQ_FUA for direct writes if the device supports it
Zhenxian Ma <[email protected]>
| Newsgroups | org.kernel.vger.linux-block |
|---|---|
| Message-ID | <[email protected]> |
From: Zhenxian Ma <[email protected]> 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 <[email protected]> Signed-off-by: Zhenxian Ma <[email protected]> Signed-off-by: Zhenxian Ma <[email protected]> --- block/fops.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/block/fops.c b/block/fops.c index a2c3af38106b..e7d073620271 100644 --- a/block/fops.c +++ b/block/fops.c @@ -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; @@ -323,7 +338,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; @@ -768,6 +783,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 { /* -- 2.43.5