]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xfs: use bounce buffering direct I/O when the device requires stable pages
authorChristoph Hellwig <hch@lst.de>
Mon, 26 Jan 2026 05:53:46 +0000 (06:53 +0100)
committerJens Axboe <axboe@kernel.dk>
Wed, 28 Jan 2026 12:16:40 +0000 (05:16 -0700)
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>
fs/xfs/xfs_aops.c
fs/xfs/xfs_file.c

index 56a544638491aa75854a95e5d75fb02f87271525..c3c1e149fff4c2e2e185e6659927b22681ac2af2 100644 (file)
@@ -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();
        }
 }
index 7874cf745af372fe8d90af09c6916d4c635472e0..f6cc63dcf961e7bc5d6a8d02456657ba34396040 100644 (file)
@@ -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);