summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2026-01-26 06:53:46 +0100
committerJens Axboe <axboe@kernel.dk>2026-01-28 05:16:40 -0700
commit3373503df025ab6c9a8ad2ce6b7febd2eb3c99dc (patch)
tree708993b4a62e728af26ced143d574b6c47493976
parentc9d114846b380fec1093b7bca91ee5a8cd7b575d (diff)
xfs: use bounce buffering direct I/O when the device requires stable pages
Fix direct I/O on devices that require stable pages by asking iomap to bounce buffer. To support this, ioends are used for direct reads in this case to provide a user context for copying data back from the bounce buffer. This fixes qemu when used on devices using T10 protection information and probably other cases like iSCSI using data digests. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Tested-by: Anuj Gupta <anuj20.g@samsung.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--fs/xfs/xfs_aops.c8
-rw-r--r--fs/xfs/xfs_file.c41
2 files changed, 44 insertions, 5 deletions
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 56a544638491..c3c1e149fff4 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -103,7 +103,7 @@ xfs_ioend_put_open_zones(
* IO write completion.
*/
STATIC void
-xfs_end_ioend(
+xfs_end_ioend_write(
struct iomap_ioend *ioend)
{
struct xfs_inode *ip = XFS_I(ioend->io_inode);
@@ -202,7 +202,11 @@ xfs_end_io(
io_list))) {
list_del_init(&ioend->io_list);
iomap_ioend_try_merge(ioend, &tmp);
- xfs_end_ioend(ioend);
+ if (bio_op(&ioend->io_bio) == REQ_OP_READ)
+ iomap_finish_ioends(ioend,
+ blk_status_to_errno(ioend->io_bio.bi_status));
+ else
+ xfs_end_ioend_write(ioend);
cond_resched();
}
}
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 7874cf745af3..f6cc63dcf961 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -224,12 +224,34 @@ xfs_ilock_iocb_for_write(
return 0;
}
+/*
+ * Bounce buffering dio reads need a user context to copy back the data.
+ * Use an ioend to provide that.
+ */
+static void
+xfs_dio_read_bounce_submit_io(
+ const struct iomap_iter *iter,
+ struct bio *bio,
+ loff_t file_offset)
+{
+ iomap_init_ioend(iter->inode, bio, file_offset, IOMAP_IOEND_DIRECT);
+ bio->bi_end_io = xfs_end_bio;
+ submit_bio(bio);
+}
+
+static const struct iomap_dio_ops xfs_dio_read_bounce_ops = {
+ .submit_io = xfs_dio_read_bounce_submit_io,
+ .bio_set = &iomap_ioend_bioset,
+};
+
STATIC ssize_t
xfs_file_dio_read(
struct kiocb *iocb,
struct iov_iter *to)
{
struct xfs_inode *ip = XFS_I(file_inode(iocb->ki_filp));
+ unsigned int dio_flags = 0;
+ const struct iomap_dio_ops *dio_ops = NULL;
ssize_t ret;
trace_xfs_file_direct_read(iocb, to);
@@ -242,7 +264,12 @@ xfs_file_dio_read(
ret = xfs_ilock_iocb(iocb, XFS_IOLOCK_SHARED);
if (ret)
return ret;
- ret = iomap_dio_rw(iocb, to, &xfs_read_iomap_ops, NULL, 0, NULL, 0);
+ if (mapping_stable_writes(iocb->ki_filp->f_mapping)) {
+ dio_ops = &xfs_dio_read_bounce_ops;
+ dio_flags |= IOMAP_DIO_BOUNCE;
+ }
+ ret = iomap_dio_rw(iocb, to, &xfs_read_iomap_ops, dio_ops, dio_flags,
+ NULL, 0);
xfs_iunlock(ip, XFS_IOLOCK_SHARED);
return ret;
@@ -703,6 +730,8 @@ xfs_file_dio_write_aligned(
xfs_ilock_demote(ip, XFS_IOLOCK_EXCL);
iolock = XFS_IOLOCK_SHARED;
}
+ if (mapping_stable_writes(iocb->ki_filp->f_mapping))
+ dio_flags |= IOMAP_DIO_BOUNCE;
trace_xfs_file_direct_write(iocb, from);
ret = iomap_dio_rw(iocb, from, ops, dops, dio_flags, ac, 0);
out_unlock:
@@ -750,6 +779,7 @@ xfs_file_dio_write_atomic(
{
unsigned int iolock = XFS_IOLOCK_SHARED;
ssize_t ret, ocount = iov_iter_count(from);
+ unsigned int dio_flags = 0;
const struct iomap_ops *dops;
/*
@@ -777,8 +807,10 @@ retry:
}
trace_xfs_file_direct_write(iocb, from);
- ret = iomap_dio_rw(iocb, from, dops, &xfs_dio_write_ops,
- 0, NULL, 0);
+ if (mapping_stable_writes(iocb->ki_filp->f_mapping))
+ dio_flags |= IOMAP_DIO_BOUNCE;
+ ret = iomap_dio_rw(iocb, from, dops, &xfs_dio_write_ops, dio_flags,
+ NULL, 0);
/*
* The retry mechanism is based on the ->iomap_begin method returning
@@ -867,6 +899,9 @@ retry_exclusive:
if (flags & IOMAP_DIO_FORCE_WAIT)
inode_dio_wait(VFS_I(ip));
+ if (mapping_stable_writes(iocb->ki_filp->f_mapping))
+ flags |= IOMAP_DIO_BOUNCE;
+
trace_xfs_file_direct_write(iocb, from);
ret = iomap_dio_rw(iocb, from, &xfs_direct_write_iomap_ops,
&xfs_dio_write_ops, flags, NULL, 0);