Re: [PATCH 2/3] iomap: decouple simple direct I/O reads from iomap_dio_rw

"changfengnan" <[email protected]>
Newsgroups org.kernel.vger.linux-ext4,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-xfs
Message-ID <d9210bcdf73fbe1ac8b6ec132865609a3ed68688.fb281486.dc82.4049.92e1.5b912f7e663c@bytedance.com>
> From: "Joanne Koong"<[email protected]>
> Date:  Thu, Jul 23, 2026, 03:04
> Subject:  Re: [PATCH 2/3] iomap: decouple simple direct I/O reads from iomap_dio_rw
> To: "Christoph Hellwig"<[email protected]>
> Cc: <[email protected]>, "Darrick J. Wong"<[email protected]>, "Christian Brauner"<[email protected]>, "Theodore Ts'o"<[email protected]>, "Carlos Maiolino"<[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>
> On Wed, Jul 22, 2026 at 5:49 AM Christoph Hellwig <[email protected]> wrote:
> >
> > The pending iomap_iter_next conversion creates performance issues for the
> > new simple direct I/O read fast path, because it assumes a model where
> > the iterator must be advanced at the end, which the direct I/O read fast
> > path tries to avoid.
> >
> > Side step this by splitting the simple path from iomap_dio_rw, and
> > require the file systems to call into it explicitly, and pass only a
> > ->begin callback.  This allows to drop various checks for incompatible
> > features while creating a requirement for the file system to only call
> > the simple path for cases that it can handle.
> 
> Nice, this approach looks cleaner to me.
> 
> >
> > As a side-benefit we can now inline the initial part of the simple
> > direct I/O read fast path and let the compiler convert the indirect call
> > to ->begin into a direct call.
> >
> > Signed-off-by: Christoph Hellwig <[email protected]>
> > ---
> >  fs/ext4/ext4.h        |   3 +
> >  fs/ext4/file.c        |   4 +-
> >  fs/ext4/inode.c       |   2 +-
> >  fs/iomap/direct-io.c  | 209 ++++++++++--------------------------------
> >  fs/xfs/xfs_file.c     |  14 +--
> >  fs/xfs/xfs_iomap.c    |   2 +-
> >  fs/xfs/xfs_iomap.h    |   4 +
> >  include/linux/iomap.h |  65 +++++++++++++
> >  8 files changed, 133 insertions(+), 170 deletions(-)
> >
> > index 3df851d0f76b..810dc4b8dc94 100644
> > --- a/include/linux/iomap.h
> > +++ b/include/linux/iomap.h
> >
> > +/*
> > + * Fast path for small, block-aligned direct I/Os that map to a single
> > + * contiguous on-disk extent.
> > + *
> > + * @iter must describe a non-empty READ no larger than the inode block size:
> > + * writes, zero-length I/O, and larger requests need the generic iomap direct
> > + * I/O path.
> > + *
> > + * Does not support iomap_dio_ops, dio_flags, done_before or private data.
> > + * The range must also stay within i_size and encrypted inodes must use the
> > + * generic iomap direct I/O path.
> > + *
> > + * -ENOTBLK inidicates the generic path must be used by the caller instead.
> > + * Any other errno is a real result and is propagated as-is, in particular
> > + * -EAGAIN for IOCB_NOWAIT must reach the caller.
> > + *
> > + * The caller can only provide an iomap begin handler, and the iterator
> > + * is never advanced.
> > + */
> > +ssize_t __iomap_dio_simple(struct kiocb *iocb, struct iov_iter *iter,
> > +               struct iomap_iter *iomi);
> 
> Might be nice to have _read_ in this name as well, eg __iomap_dio_read_simple
> 
> > +static __always_inline ssize_t iomap_dio_read_simple(struct kiocb *iocb,
> > +               struct iov_iter *iter, iomap_iter_begin_fn begin)
> > +{
> > +       struct iomap_iter iomi = {
> > +               .inode          = file_inode(iocb->ki_filp),
> > +               .pos            = iocb->ki_pos,
> > +               .len            = iov_iter_count(iter),
> > +               .flags          = IOMAP_DIRECT,
> > +       };
> > +       ssize_t ret;
> > +
> > +       if (!iomi.len)
> > +               return 0;
> > +
> > +       /*
> > +        * Simple dio is an optimization for small IO. Filter out large IO
> > +        * early as it's the most common case to fail for typical direct IO
> > +        * workloads.
> > +        */
> > +       if (iomi.len > iomi.inode->i_sb->s_blocksize)
> > +               return -ENOTBLK;
> > +       if (iocb->ki_pos + iomi.len > i_size_read(iomi.inode))
> > +               return -ENOTBLK;
> > +       if (IS_ENCRYPTED(iomi.inode))
> > +               return -ENOTBLK;
> > +
> > +       ret = kiocb_write_and_wait(iocb, iomi.len);
> > +       if (ret)
> > +               return ret;
> > +
> > +       if (iocb->ki_flags & IOCB_NOWAIT)
> > +               iomi.flags |= IOMAP_NOWAIT;
> > +
> > +       inode_dio_begin(iomi.inode);
> > +       ret = begin(iomi.inode, iomi.pos, iomi.len, iomi.flags, &iomi.iomap,
> > +                       &iomi.srcmap);
> > +       if (!ret)
> > +               ret = __iomap_dio_simple(iocb, iter, &iomi);
> > +       if (ret <= 0 && ret != -EIOCBQUEUED)
> > +               inode_dio_end(iomi.inode);
> 
> afaict, the inode_dio_end() error handling has to be done by
> __iomap_dio_simple() to properly account for the synchronous case. I
> ran into this earlier when doing the iomap_next_fn inlining. For the
> synchronous case, __iomap_dio_simple() calls
> iomap_dio_simple_complete() which calls inode_dio_end() even on
> errors.

Yes, there is an issue with the handling of the inode_dio_end event in the
synchronization scenario.
How about this ?

diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
index 3bdfbd0efa076..f6c8091a65ac9 100644
--- a/fs/iomap/direct-io.c
+++ b/fs/iomap/direct-io.c
@@ -994,18 +994,22 @@ ssize_t __iomap_dio_simple(struct kiocb *iocb, struct iov_iter *iter,
 
         if (iomi->iomap.type != IOMAP_MAPPED ||
             iomi->iomap.offset + iomi->iomap.length < iomi->pos + iomi->len ||
-            (iomi->iomap.flags & IOMAP_F_INTEGRITY))
-                return -ENOTBLK;
+            (iomi->iomap.flags & IOMAP_F_INTEGRITY)) {
+                ret = -ENOTBLK;
+                goto out_dio_end;
+        }
 
         alignment = iomap_dio_alignment(iomi->inode, iomi->iomap.bdev, 0);
-        if ((iomi->pos | iomi->len) & (alignment - 1))
-                return -EINVAL;
+        if ((iomi->pos | iomi->len) & (alignment - 1)) {
+                ret = -EINVAL;
+                goto out_dio_end;
+        }
 
         if (unlikely(!iomi->inode->i_sb->s_dio_done_wq &&
                         !is_sync_kiocb(iocb))) {
                 ret = sb_init_dio_done_wq(iomi->inode->i_sb);
                 if (ret < 0)
-                        return ret;
+                        goto out_dio_end;
         }
 
         trace_iomap_dio_rw_begin(iocb, iter, 0, 0);
@@ -1013,8 +1017,10 @@ ssize_t __iomap_dio_simple(struct kiocb *iocb, struct iov_iter *iter,
         bio = bio_alloc_bioset(iomi->iomap.bdev,
                                bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),
                                REQ_OP_READ, gfp, &iomap_dio_simple_pool);
-        if (!bio)
-                return -EAGAIN;
+        if (!bio) {
+                ret = -EAGAIN;
+                goto out_dio_end;
+        }
         sr = container_of(bio, struct iomap_dio_simple, bio);
         sr->iocb = iocb;
         sr->dio_flags = 0;
@@ -1060,6 +1066,8 @@ ssize_t __iomap_dio_simple(struct kiocb *iocb, struct iov_iter *iter,
         bio_release_pages(bio, false);
 out_bio_put:
         bio_put(bio);
+out_dio_end:
+        inode_dio_end(iomi->inode);
         return ret;
 }
 EXPORT_SYMBOL_GPL(__iomap_dio_simple);
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 810dc4b8dc94d..3cdf475059048 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -732,11 +732,11 @@ static __always_inline ssize_t iomap_dio_read_simple(struct kiocb *iocb,
         inode_dio_begin(iomi.inode);
         ret = begin(iomi.inode, iomi.pos, iomi.len, iomi.flags, &iomi.iomap,
                         &iomi.srcmap);
-        if (!ret)
-                ret = __iomap_dio_simple(iocb, iter, &iomi);
-        if (ret <= 0 && ret != -EIOCBQUEUED)
+        if (ret) {
                 inode_dio_end(iomi.inode);
-        return ret;
+                return ret;
+        }
+        return __iomap_dio_simple(iocb, iter, &iomi);
 }
 
 #ifdef CONFIG_SWAP
> 
> Thanks,
> Joanne
> 
> > +       return ret;
> > +}
> 
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.