summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2026-07-29 12:27:18 -0700
committerChristian Brauner <brauner@kernel.org>2026-07-31 12:28:49 +0200
commit335d4b6201ac317d906e6a694f07de0792325fee (patch)
treea00852b8dc7da3e53154cbc55458eae327086519 /include/linux
parent19eb9f6ab5ce1d15c7f5e48ca16804a6d7740084 (diff)
iomap: decouple simple direct I/O reads from iomap_dio_rw
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. 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. Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Fengnan Chang <changfengnan@bytedance.com> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Christoph Hellwig <hch@lst.de> Link: https://patch.msgid.link/20260729192737.3190206-4-joannelkoong@gmail.com Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/iomap.h66
1 files changed, 66 insertions, 0 deletions
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 36490c08d6e9..80832edc7ec2 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -10,6 +10,7 @@
#include <linux/mm_types.h>
#include <linux/blkdev.h>
#include <linux/folio_batch.h>
+#include <linux/pagemap.h>
struct address_space;
struct fiemap_extent_info;
@@ -675,6 +676,71 @@ 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);
+/*
+ * 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 indicates 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_read_simple(struct kiocb *iocb, struct iov_iter *iter,
+ struct iomap_iter *iomi);
+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) {
+ inode_dio_end(iomi.inode);
+ return ret;
+ }
+
+ return __iomap_dio_read_simple(iocb, iter, &iomi);
+}
+
#ifdef CONFIG_SWAP
struct file;
struct swap_info_struct;