Re: [PATCH v6 3/3] iomap: add simple dio path for small direct I/O
From: changfengnan
Date: Wed Jul 08 2026 - 04:52:35 EST
> From: "Joanne Koong"<joannelkoong@xxxxxxxxx>
> Date: Wed, Jul 8, 2026, 10:07
> Subject: Re: [PATCH v6 3/3] iomap: add simple dio path for small direct I/O
> To: "changfengnan"<changfengnan@xxxxxxxxxxxxx>
> Cc: "Christoph Hellwig"<hch@xxxxxxxxxxxxx>, "brauner"<brauner@xxxxxxxxxx>, "djwong"<djwong@xxxxxxxxxx>, "ojaswin"<ojaswin@xxxxxxxxxxxxx>, "dgc"<dgc@xxxxxxxxxx>, "linux-xfs"<linux-xfs@xxxxxxxxxxxxxxx>, "linux-fsdevel"<linux-fsdevel@xxxxxxxxxxxxxxx>, "linux-ext4"<linux-ext4@xxxxxxxxxxxxxxx>, "linux-kernel"<linux-kernel@xxxxxxxxxxxxxxx>, <lidiangang@xxxxxxxxxxxxx>, "pankaj.raghav"<pankaj.raghav@xxxxxxxxx>, "Brian Foster"<bfoster@xxxxxxxxxx>
> On Mon, Jul 6, 2026 at 11:57 PM changfengnan <changfengnan@xxxxxxxxxxxxx> wrote:
> >
> >
> > > From: "Joanne Koong"<joannelkoong@xxxxxxxxx>
> > > Date: Tue, Jul 7, 2026, 03:27
> > > Subject: Re: [PATCH v6 3/3] iomap: add simple dio path for small direct I/O
> > > To: "changfengnan"<changfengnan@xxxxxxxxxxxxx>
> > > Cc: "Christoph Hellwig"<hch@xxxxxxxxxxxxx>, "brauner"<brauner@xxxxxxxxxx>, "djwong"<djwong@xxxxxxxxxx>, "ojaswin"<ojaswin@xxxxxxxxxxxxx>, "dgc"<dgc@xxxxxxxxxx>, "linux-xfs"<linux-xfs@xxxxxxxxxxxxxxx>, "linux-fsdevel"<linux-fsdevel@xxxxxxxxxxxxxxx>, "linux-ext4"<linux-ext4@xxxxxxxxxxxxxxx>, "linux-kernel"<linux-kernel@xxxxxxxxxxxxxxx>, <lidiangang@xxxxxxxxxxxxx>, "pankaj.raghav"<pankaj.raghav@xxxxxxxxx>, "Brian Foster"<bfoster@xxxxxxxxxx>
> > > On Mon, Jul 6, 2026 at 4:10 AM changfengnan <changfengnan@xxxxxxxxxxxxx> wrote:
> > > >
> > > > > From: "Christoph Hellwig"<hch@xxxxxxxxxxxxx>
> > > > > Date: Fri, Jul 3, 2026, 20:40
> > > > > Subject: Re: [PATCH v6 3/3] iomap: add simple dio path for small direct I/O
> > > > > To: "changfengnan"<changfengnan@xxxxxxxxxxxxx>
> > > > > Cc: "Joanne Koong"<joannelkoong@xxxxxxxxx>, "Christoph Hellwig"<hch@xxxxxxxxxxxxx>, "brauner"<brauner@xxxxxxxxxx>, "djwong"<djwong@xxxxxxxxxx>, "ojaswin"<ojaswin@xxxxxxxxxxxxx>, "dgc"<dgc@xxxxxxxxxx>, "linux-xfs"<linux-xfs@xxxxxxxxxxxxxxx>, "linux-fsdevel"<linux-fsdevel@xxxxxxxxxxxxxxx>, "linux-ext4"<linux-ext4@xxxxxxxxxxxxxxx>, "linux-kernel"<linux-kernel@xxxxxxxxxxxxxxx>, <lidiangang@xxxxxxxxxxxxx>, "pankaj.raghav"<pankaj.raghav@xxxxxxxxx>, "Brian Foster"<bfoster@xxxxxxxxxx>
> > > > > On Fri, Jul 03, 2026 at 05:13:12PM +0800, changfengnan wrote:
> > > > > > taken, which introduced some overhead.
> > > > > > I implemented some optimizations based on Christoph’s suggestions and
> > > > > > made some modifications to `iomap_process`;
> > > > > > Now, XFS performance is essentially unchanged, while ext4 still shows a
> > > > > > 1.5% drop; I will continue to investigate the cause.. I’ve attached the patch.
> > > > > > Actually, I had expected that switching to `iomap_next` would improve
> > > > > > performance, since it avoids an indirect call.
> > > > >
> > > > > I don't think it will for you - the direct I/O readers don't have
> > > > > iomap_end methods, so you still have the same number of indirect
> > > > > calls. What could help is to inline the iterator as seen in Joannes'
> > > > > fist demo series. We could even look into doing that only for simple
> > > > > dio in a first step, shifting the burden to use the simple method
> > > > > to the callers (at least for the POC).
> > > >
> > > > I'm not sure if this is what you had in mind, but I made some
> > > > adjustments referencing Joannes' first patch, and using this approach,
> > > > there was no performance degradation.
> > >
> > > Thanks, Fengnan. This is awesome that it got the degradation down to zero.
> > >
> > > Would you be able to test how this change performs on your benchmarks?:
> >
> > XFS basically has no performance degradation, while ext4 experiences
> > a 1-1.5% performance degradation.
> >
> > Round 1:
> > d67 xfs : 2.04M, 2.05M, 2.04M, 2.04M, 2.04M avg=2.042M
> > d67 ext4: 2.12M, 2.12M, 2.12M, 2.12M, 2.12M avg=2.120M
> > joanne86 xfs : 2.13M, 2.05M, 2.04M, 2.05M, 2.05M avg=2.064M
> > joanne86 ext4: 2.11M, 2.11M, 2.11M, 2.11M, 2.11M avg=2.110M
> >
> > Round 2:
> > d67 xfs : 2.05M, 2.05M, 2.05M, 2.05M, 2.05M avg=2.050M
> > d67 ext4: 2.12M, 2.12M, 2.12M, 2.12M, 2.12M avg=2.120M
> > joanne86 xfs : 2.04M, 2.04M, 2.04M, 2.04M, 2.05M avg=2.042M
> > joanne86 ext4: 2.09M, 2.08M, 2.09M, 2.10M, 2.09M avg=2.090M
> >
> > Round 3:
> > d67 xfs : 2.04M, 2.04M, 2.04M, 2.04M, 2.05M avg=2.042M
> > d67 ext4: 2.12M, 2.12M, 2.11M, 2.12M, 2.12M avg=2.118M
> > joanne86 xfs : 2.04M, 2.05M, 2.04M, 2.04M, 2.04M avg=2.042M
> > joanne86 ext4: 2.09M, 2.09M, 2.08M, 2.09M, 2.09M avg=2.088M
> >
>
> Thanks. I'm not sure why it makes ext4 worse. Maybe it being inlined
> bloats it too much so it leads to more frontend stalls/icache misses
> than what the devirtualization helps with? Looking at the begin
> functions, ext4's is around 2.5x larger than xfs's. It also looks like
> ext4's read_iomap_begin function has 2 callers, so I think that may
> not get automatically inlined whereas xfs's will since
> xfs_read_iomap_begin just has 1 caller, though I don't know if that
> ends up mattering.
I ran two rounds of tests using your patch, and ext4 still showed a 1.5%
performance drop.
However, I think your analysis is correct—it’s likely caused by too many
inline functions. Inlining the simple DIO wrappers expands the filesystem
read_iter hot path and increases frontend/register pressure. Keeping the
wrappers out of line adds only a cheap direct call, while preserving a smaller
and better laid-out filesystem hot path.
So I made some changes, reverting `iomap_dio_rw()` and `iomap_dio_simple()`
to non-inline versions, and performance returned to normal.
Patch based on https://github.com/joannekoong/linux/commits/iomap_simple_dio_iomap_next_ext4/ ;
diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
index bc3b87e57592a..d0f7ef8ac140a 100644
--- a/fs/iomap/direct-io.c
+++ b/fs/iomap/direct-io.c
@@ -968,6 +968,33 @@ static void iomap_dio_simple_end_io(struct bio *bio)
iocb->ki_complete(iocb, iomap_dio_simple_complete(sr));
}
+static bool iomap_dio_simple_supported(struct kiocb *iocb,
+ struct iov_iter *iter,
+ const struct iomap_dio_ops *dops,
+ unsigned int dio_flags,
+ size_t done_before)
+{
+ struct inode *inode = file_inode(iocb->ki_filp);
+ size_t count = iov_iter_count(iter);
+
+ if (dops || done_before)
+ return false;
+ if (iov_iter_rw(iter) != READ)
+ return false;
+ if (!count)
+ return false;
+ if (count > inode->i_sb->s_blocksize)
+ return false;
+ if (dio_flags & (IOMAP_DIO_FORCE_WAIT | IOMAP_DIO_PARTIAL |
+ IOMAP_DIO_BOUNCE))
+ return false;
+ if (iocb->ki_pos + count > i_size_read(inode))
+ return false;
+ if (IS_ENCRYPTED(inode))
+ return false;
+ return true;
+}
+
static inline void
iomap_dio_simple_finish(struct iomap_iter *iomi, iomap_next_fn iomap_next,
size_t written)
@@ -978,7 +1005,7 @@ iomap_dio_simple_finish(struct iomap_iter *iomi, iomap_next_fn iomap_next,
iomap_next(iomi, &iomi->iomap, &iomi->srcmap);
}
-ssize_t
+static ssize_t
__iomap_dio_simple(struct kiocb *iocb, struct iov_iter *iter,
struct iomap_iter *iomi, iomap_next_fn iomap_next,
unsigned int dio_flags)
@@ -1070,7 +1097,97 @@ __iomap_dio_simple(struct kiocb *iocb, struct iov_iter *iter,
inode_dio_end(inode);
return ret;
}
-EXPORT_SYMBOL_GPL(__iomap_dio_simple);
+
+/*
+ * Fast path for small, block-aligned direct I/Os that map to a single
+ * contiguous on-disk extent.
+ *
+ * iomap_dio_simple_supported() enforces the cheap up-front constraints before
+ * entering this path.
+ *
+ * @dops must be NULL: a non-NULL @dops means the caller wants its
+ * ->end_io / ->submit_io hooks invoked, and in particular wants its bios to be
+ * allocated from the filesystem-private @dops->bio_set (whose front_pad sizes a
+ * filesystem-private wrapper around the bio). The fast path instead allocates
+ * from the shared iomap_dio_simple_pool, whose front_pad matches struct
+ * iomap_dio_simple; the two wrappers are not interchangeable, so we must fall
+ * back to __iomap_dio_rw() in that case.
+ *
+ * @done_before must be zero: a non-zero caller-accumulated residual cannot be
+ * carried through a single-bio inline completion.
+ *
+ * @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.
+ *
+ * @dio_flags must not request IOMAP_DIO_FORCE_WAIT, IOMAP_DIO_PARTIAL, or
+ * IOMAP_DIO_BOUNCE: this path does not support forced waiting, partial direct
+ * I/O, or bouncing. The range must also stay within i_size and encrypted
+ * inodes must use the generic iomap direct I/O path. IOMAP_DIO_NO_IOMAP_END is
+ * a fast-path optimization the caller can set if there is no work that needs to
+ * be done after a mapping.
+ *
+ * -ENOTBLK is the private sentinel returned by iomap_dio_simple() when it
+ * decides the request does not fit the fast path. In that case we proceed to
+ * the generic __iomap_dio_rw() slow path. Any other errno is a real result and
+ * is propagated as-is, in particular -EAGAIN for IOCB_NOWAIT must reach the
+ * caller.
+ */
+static ssize_t iomap_dio_simple(struct kiocb *iocb,
+ struct iov_iter *iter, iomap_next_fn iomap_next,
+ void *private, unsigned int dio_flags)
+{
+ struct inode *inode = file_inode(iocb->ki_filp);
+ size_t count = iov_iter_count(iter);
+ struct iomap_iter iomi = {
+ .inode = inode,
+ .pos = iocb->ki_pos,
+ .len = count,
+ .flags = IOMAP_DIRECT,
+ .private = private,
+ };
+ ssize_t ret;
+
+ if (iocb->ki_flags & IOCB_NOWAIT)
+ iomi.flags |= IOMAP_NOWAIT;
+
+ ret = kiocb_write_and_wait(iocb, count);
+ if (ret)
+ return ret;
+
+ inode_dio_begin(inode);
+
+ ret = iomap_next(&iomi, &iomi.iomap, &iomi.srcmap);
+ if (ret <= 0) {
+ inode_dio_end(inode);
+ /* a zero return means no mapping, which should never happen */
+ return ret ? ret : -EFAULT;
+ }
+
+ return __iomap_dio_simple(iocb, iter, &iomi, iomap_next, dio_flags);
+}
+
+ssize_t iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
+ iomap_next_fn iomap_next, const struct iomap_dio_ops *dops,
+ unsigned int dio_flags, void *private, size_t done_before)
+{
+ struct iomap_dio *dio;
+ ssize_t ret;
+
+ if (iomap_dio_simple_supported(iocb, iter, dops, dio_flags, done_before)) {
+ ret = iomap_dio_simple(iocb, iter, iomap_next, private,
+ dio_flags);
+ if (ret != -ENOTBLK)
+ return ret;
+ }
+
+ dio = __iomap_dio_rw(iocb, iter, iomap_next, dops, dio_flags,
+ private, done_before);
+ if (IS_ERR_OR_NULL(dio))
+ return PTR_ERR_OR_ZERO(dio);
+ return iomap_dio_complete(dio);
+}
+EXPORT_SYMBOL_GPL(iomap_dio_rw);
static int __init iomap_dio_init(void)
{
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 6213a3b1a0b93..d6028722c54b2 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -615,126 +615,9 @@ struct iomap_dio *__iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
ssize_t iomap_dio_complete(struct iomap_dio *dio);
void iomap_dio_bio_end_io(struct bio *bio);
-static inline bool
-iomap_dio_simple_supported(struct kiocb *iocb, struct iov_iter *iter,
- const struct iomap_dio_ops *dops,
- unsigned int dio_flags, size_t done_before)
-{
- struct inode *inode = file_inode(iocb->ki_filp);
- size_t count = iov_iter_count(iter);
-
- if (dops || done_before)
- return false;
- if (iov_iter_rw(iter) != READ)
- return false;
- if (!count)
- return false;
- if (count > inode->i_sb->s_blocksize)
- return false;
- if (dio_flags & (IOMAP_DIO_FORCE_WAIT | IOMAP_DIO_PARTIAL |
- IOMAP_DIO_BOUNCE))
- return false;
- if (iocb->ki_pos + count > i_size_read(inode))
- return false;
- if (IS_ENCRYPTED(inode))
- return false;
- return true;
-}
-
-ssize_t __iomap_dio_simple(struct kiocb *iocb, struct iov_iter *iter,
- struct iomap_iter *iomi, iomap_next_fn iomap_next,
- unsigned int dio_flags);
-
-/*
- * Fast path for small, block-aligned direct I/Os that map to a single
- * contiguous on-disk extent.
- *
- * iomap_dio_simple_supported() enforces the cheap up-front constraints before
- * entering this path.
- *
- * @dops must be NULL: a non-NULL @dops means the caller wants its
- * ->end_io / ->submit_io hooks invoked, and in particular wants its bios to be
- * allocated from the filesystem-private @dops->bio_set (whose front_pad sizes a
- * filesystem-private wrapper around the bio). The fast path instead allocates
- * from the shared iomap_dio_simple_pool, whose front_pad matches struct
- * iomap_dio_simple; the two wrappers are not interchangeable, so we must fall
- * back to __iomap_dio_rw() in that case.
- *
- * @done_before must be zero: a non-zero caller-accumulated residual cannot be
- * carried through a single-bio inline completion.
- *
- * @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.
- *
- * @dio_flags must not request IOMAP_DIO_FORCE_WAIT, IOMAP_DIO_PARTIAL, or
- * IOMAP_DIO_BOUNCE: this path does not support forced waiting, partial direct
- * I/O, or bouncing. The range must also stay within i_size and encrypted
- * inodes must use the generic iomap direct I/O path. IOMAP_DIO_NO_IOMAP_END is
- * a fast-path optimization the caller can set if there is no work that needs to
- * be done after a mapping.
- *
- * -ENOTBLK is the private sentinel returned by iomap_dio_simple() when it
- * decides the request does not fit the fast path. In that case we proceed to
- * the generic __iomap_dio_rw() slow path. Any other errno is a real result and
- * is propagated as-is, in particular -EAGAIN for IOCB_NOWAIT must reach the
- * caller.
- */
-static __always_inline ssize_t
-iomap_dio_simple(struct kiocb *iocb, struct iov_iter *iter,
- iomap_next_fn iomap_next, void *private, unsigned int dio_flags)
-{
- struct inode *inode = file_inode(iocb->ki_filp);
- size_t count = iov_iter_count(iter);
- struct iomap_iter iomi = {
- .inode = inode,
- .pos = iocb->ki_pos,
- .len = count,
- .flags = IOMAP_DIRECT,
- .private = private,
- };
- ssize_t ret;
-
- if (iocb->ki_flags & IOCB_NOWAIT)
- iomi.flags |= IOMAP_NOWAIT;
-
- ret = kiocb_write_and_wait(iocb, count);
- if (ret)
- return ret;
-
- inode_dio_begin(inode);
-
- ret = iomap_next(&iomi, &iomi.iomap, &iomi.srcmap);
- if (ret <= 0) {
- inode_dio_end(inode);
- /* a zero return means no mapping, which should never happen */
- return ret ? ret : -EFAULT;
- }
-
- return __iomap_dio_simple(iocb, iter, &iomi, iomap_next, dio_flags);
-}
-
-static __always_inline ssize_t
-iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, iomap_next_fn iomap_next,
- const struct iomap_dio_ops *dops, unsigned int dio_flags,
- void *private, size_t done_before)
-{
- struct iomap_dio *dio;
- ssize_t ret;
-
- if (iomap_dio_simple_supported(iocb, iter, dops, dio_flags, done_before)) {
- ret = iomap_dio_simple(iocb, iter, iomap_next, private,
- dio_flags);
- if (ret != -ENOTBLK)
- return ret;
- }
-
- dio = __iomap_dio_rw(iocb, iter, iomap_next, dops, dio_flags, private,
- done_before);
- if (IS_ERR_OR_NULL(dio))
- return PTR_ERR_OR_ZERO(dio);
- return iomap_dio_complete(dio);
-}
+ssize_t iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
+ iomap_next_fn iomap_next, const struct iomap_dio_ops *dops,
+ unsigned int dio_flags, void *private, size_t done_before);
#ifdef CONFIG_SWAP
struct file;
--
>
> I'm seeing that ext4_iomap_begin is ~1570 bytes vs.
> xfs_read_iomap_begin is ~635 bytes. It looks like though
> ext4_iomap_begin is a combined read + write begin, whereas xfs's is a
> dedicated read-only begin. If we separate ext4_iomap_begin into
> dedicated read vs write begins, then I think this gets
> ext4_read_iomap_begin down to ~230 bytes.
>
> Would you be able to test if this improves the ext4 performance you're seeing?:
>
> change #1: separate out ext4_iomap_begin into ext4_read/write_iomap_begin
>
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 4c30dd8dbec7..54cdecbc7539 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -3771,8 +3771,46 @@ static int ext4_iomap_alloc(struct inode
> *inode, struct ext4_map_blocks *map,
> }
>
>
> -static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
> - unsigned flags, struct iomap *iomap, struct iomap *srcmap)
> +static int ext4_read_iomap_begin(struct inode *inode, loff_t offset,
> + loff_t length, unsigned int flags, struct iomap *iomap,
> + struct iomap *srcmap)
> +{
> + int ret;
> + struct ext4_map_blocks map;
> + u8 blkbits = inode->i_blkbits;
> +
> + if ((offset >> blkbits) > EXT4_MAX_LOGICAL_BLOCK)
> + return -EINVAL;
> +
> + if (WARN_ON_ONCE(ext4_has_inline_data(inode)))
> + return -ERANGE;
> +
> + /*
> + * Calculate the first and last logical blocks respectively.
> + */
> + map.m_lblk = offset >> blkbits;
> + map.m_len = min_t(loff_t, (offset + length - 1) >> blkbits,
> + EXT4_MAX_LOGICAL_BLOCK) - map.m_lblk + 1;
> +
> + ret = ext4_map_blocks(NULL, inode, &map, 0);
> + if (ret < 0)
> + return ret;
> +
> + /*
> + * When inline encryption is enabled, sometimes I/O to an encrypted file
> + * has to be broken up to guarantee DUN contiguity. Handle this by
> + * limiting the length of the mapping returned.
> + */
> + map.m_len = fscrypt_limit_io_blocks(inode, map.m_lblk, map.m_len);
> +
> + ext4_set_iomap(inode, iomap, &map, offset, length, flags);
> +
> + return 0;
> +}
> +
> +static int ext4_write_iomap_begin(struct inode *inode, loff_t offset,
> + loff_t length, unsigned int flags, struct iomap *iomap,
> + struct iomap *srcmap)
> {
> int ret;
> struct ext4_map_blocks map;
> @@ -3793,37 +3831,33 @@ static int ext4_iomap_begin(struct inode
> *inode, loff_t offset, loff_t length,
> EXT4_MAX_LOGICAL_BLOCK) - map.m_lblk + 1;
> orig_mlen = map.m_len;
>
> - if (flags & IOMAP_WRITE) {
> + /*
> + * We check here if the blocks are already allocated, then we
> + * don't need to start a journal txn and we can directly return
> + * the mapping information. This could boost performance
> + * especially in multi-threaded overwrite requests.
> + */
> + if (offset + length <= i_size_read(inode)) {
> + ret = ext4_map_blocks(NULL, inode, &map, 0);
> /*
> - * We check here if the blocks are already allocated, then we
> - * don't need to start a journal txn and we can directly return
> - * the mapping information. This could boost performance
> - * especially in multi-threaded overwrite requests.
> + * For DAX we convert extents to initialized ones before
> + * copying the data, otherwise we do it after I/O so
> + * there's no need to call into ext4_iomap_alloc().
> */
> - if (offset + length <= i_size_read(inode)) {
> - ret = ext4_map_blocks(NULL, inode, &map, 0);
> + if ((map.m_flags & EXT4_MAP_MAPPED) ||
> + (!(flags & IOMAP_DAX) &&
> + (map.m_flags & EXT4_MAP_UNWRITTEN))) {
> /*
> - * For DAX we convert extents to initialized ones before
> - * copying the data, otherwise we do it after I/O so
> - * there's no need to call into ext4_iomap_alloc().
> + * For atomic writes the entire requested
> + * length should be mapped.
> */
> - if ((map.m_flags & EXT4_MAP_MAPPED) ||
> - (!(flags & IOMAP_DAX) &&
> - (map.m_flags & EXT4_MAP_UNWRITTEN))) {
> - /*
> - * For atomic writes the entire requested
> - * length should be mapped.
> - */
> - if (ret == orig_mlen ||
> - (!(flags & IOMAP_ATOMIC) && ret > 0))
> - goto out;
> - }
> - map.m_len = orig_mlen;
> + if (ret == orig_mlen ||
> + (!(flags & IOMAP_ATOMIC) && ret > 0))
> + goto out;
> }
> - ret = ext4_iomap_alloc(inode, &map, flags);
> - } else {
> - ret = ext4_map_blocks(NULL, inode, &map, 0);
> + map.m_len = orig_mlen;
> }
> + ret = ext4_iomap_alloc(inode, &map, flags);
>
> if (ret < 0)
> return ret;
> @@ -3850,6 +3884,16 @@ static int ext4_iomap_begin(struct inode
> *inode, loff_t offset, loff_t length,
> return 0;
> }
>
> +static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
> + unsigned flags, struct iomap *iomap, struct iomap *srcmap)
> +{
> + if (flags & IOMAP_WRITE)
> + return ext4_write_iomap_begin(inode, offset, length, flags,
> + iomap, srcmap);
> + return ext4_read_iomap_begin(inode, offset, length, flags, iomap,
> + srcmap);
> +}
> +
> int ext4_iomap_next(const struct iomap_iter *iter, struct iomap *iomap,
> struct iomap *srcmap)
> {
>
>
> change #2: use ext4_read_iomap_next in iomap_dio_rw
>
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 755fde1baf03..bef2851d5466 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -4006,6 +4006,8 @@ static inline void
> ext4_clear_io_unwritten_flag(ext4_io_end_t *io_end)
>
> int ext4_iomap_next(const struct iomap_iter *iter, struct iomap *iomap,
> struct iomap *srcmap);
> +int ext4_read_iomap_next(const struct iomap_iter *iter, struct iomap *iomap,
> + struct iomap *srcmap);
> int ext4_iomap_next_report(const struct iomap_iter *iter, struct iomap *iomap,
> struct iomap *srcmap);
>
> diff --git a/fs/ext4/file.c b/fs/ext4/file.c
> index bf0c18cf1017..48c6a76a16a1 100644
> --- a/fs/ext4/file.c
> +++ b/fs/ext4/file.c
> @@ -91,7 +91,7 @@ static ssize_t ext4_dio_read_iter(struct kiocb
> *iocb, struct iov_iter *to)
> return generic_file_read_iter(iocb, to);
> }
>
> - ret = iomap_dio_rw(iocb, to, ext4_iomap_next, NULL,
> + ret = iomap_dio_rw(iocb, to, ext4_read_iomap_next, NULL,
> IOMAP_DIO_NO_IOMAP_END, NULL, 0);
> inode_unlock_shared(inode);
>
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 54cdecbc7539..6e7c77276d58 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -3771,9 +3771,9 @@ static int ext4_iomap_alloc(struct inode *inode,
> struct ext4_map_blocks *map,
> }
>
>
> -static int ext4_read_iomap_begin(struct inode *inode, loff_t offset,
> - loff_t length, unsigned int flags, struct iomap *iomap,
> - struct iomap *srcmap)
> +static __always_inline int ext4_read_iomap_begin(struct inode *inode,
> + loff_t offset, loff_t length, unsigned int flags,
> + struct iomap *iomap, struct iomap *srcmap)
> {
> int ret;
> struct ext4_map_blocks map;
> @@ -3900,6 +3900,12 @@ int ext4_iomap_next(const struct iomap_iter
> *iter, struct iomap *iomap,
> return iomap_process(iter, iomap, srcmap, ext4_iomap_begin, NULL);
> }
>
> +int ext4_read_iomap_next(const struct iomap_iter *iter, struct iomap *iomap,
> + struct iomap *srcmap)
> +{
> + return iomap_process(iter, iomap, srcmap, ext4_read_iomap_begin, NULL);
> +}
> +
> static int ext4_iomap_begin_report(struct inode *inode, loff_t offset,
> loff_t length, unsigned int flags,
> struct iomap *iomap, struct iomap *srcmap)
>
> The github link for the changes are in [1].
>
> Thanks for your patience with this!
>
> [1] https://github.com/joannekoong/linux/commits/iomap_simple_dio_iomap_next_ext4/
>