AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/06/29 12:20 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [
    "iomap_dio_simple",
    "iomap_dio_simple_complete",
    "iomap_dio_simple_complete_work",
    "iomap_dio_simple_end_io"
  ],
  "Reasoning": "The patch introduces a new fast path for direct I/O reads (`iomap_dio_simple`) in the iomap subsystem. This is a functional change to core kernel I/O logic that is reachable from userspace via direct I/O system calls on supported filesystems. It should be fuzzed to ensure the new fast path handles various I/O conditions correctly.",
  "WorthFuzzing": true
}

1/1 2026/06/29 12:20 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit a80b68ad6068f08cc7e6f016cba3752a196647a2\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Jun 29 12:20:03 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c\nindex b485e3b191dafa..4dcc9fc766a92e 100644\n--- a/fs/iomap/direct-io.c\n+++ b/fs/iomap/direct-io.c\n@@ -10,6 +10,9 @@\n #include \u003clinux/iomap.h\u003e\n #include \u003clinux/task_io_accounting_ops.h\u003e\n #include \u003clinux/fserror.h\u003e\n+#include \u003clinux/kobject.h\u003e\n+#include \u003clinux/sysfs.h\u003e\n+#include \u003clinux/init.h\u003e\n #include \"internal.h\"\n #include \"trace.h\"\n \n@@ -88,9 +91,9 @@ static inline enum fserror_type iomap_dio_err_type(const struct iomap_dio *dio)\n \treturn FSERR_DIRECTIO_READ;\n }\n \n-static inline bool should_report_dio_fserror(const struct iomap_dio *dio)\n+static inline bool should_report_dio_fserror(int error)\n {\n-\tswitch (dio-\u003eerror) {\n+\tswitch (error) {\n \tcase 0:\n \tcase -EAGAIN:\n \tcase -ENOTBLK:\n@@ -110,7 +113,7 @@ ssize_t iomap_dio_complete(struct iomap_dio *dio)\n \n \tif (dops \u0026\u0026 dops-\u003eend_io)\n \t\tret = dops-\u003eend_io(iocb, dio-\u003esize, ret, dio-\u003eflags);\n-\tif (should_report_dio_fserror(dio))\n+\tif (should_report_dio_fserror(dio-\u003eerror))\n \t\tfserror_report_io(file_inode(iocb-\u003eki_filp),\n \t\t\t\t  iomap_dio_err_type(dio), offset, dio-\u003esize,\n \t\t\t\t  dio-\u003eerror, GFP_NOFS);\n@@ -237,23 +240,29 @@ static void iomap_dio_done(struct iomap_dio *dio)\n \tiomap_dio_complete_work(\u0026dio-\u003eaio.work);\n }\n \n-static void __iomap_dio_bio_end_io(struct bio *bio, bool inline_completion)\n+static inline void iomap_dio_bio_release_pages(struct bio *bio,\n+\t\tunsigned int dio_flags, bool error)\n {\n-\tstruct iomap_dio *dio = bio-\u003ebi_private;\n-\n \tif (bio_integrity(bio))\n \t\tfs_bio_integrity_free(bio);\n \n-\tif (dio-\u003eflags \u0026 IOMAP_DIO_BOUNCE) {\n-\t\tbio_iov_iter_unbounce(bio, !!dio-\u003eerror,\n-\t\t\t\tdio-\u003eflags \u0026 IOMAP_DIO_USER_BACKED);\n+\tif (dio_flags \u0026 IOMAP_DIO_BOUNCE) {\n+\t\tbio_iov_iter_unbounce(bio, error,\n+\t\t\t\tdio_flags \u0026 IOMAP_DIO_USER_BACKED);\n \t\tbio_put(bio);\n-\t} else if (dio-\u003eflags \u0026 IOMAP_DIO_USER_BACKED) {\n+\t} else if (dio_flags \u0026 IOMAP_DIO_USER_BACKED) {\n \t\tbio_check_pages_dirty(bio);\n \t} else {\n \t\tbio_release_pages(bio, false);\n \t\tbio_put(bio);\n \t}\n+}\n+\n+static void __iomap_dio_bio_end_io(struct bio *bio, bool inline_completion)\n+{\n+\tstruct iomap_dio *dio = bio-\u003ebi_private;\n+\n+\tiomap_dio_bio_release_pages(bio, dio-\u003eflags, !!dio-\u003eerror);\n \n \t/* Do not touch bio below, we just gave up our reference. */\n \n@@ -398,6 +407,14 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter,\n \treturn ret;\n }\n \n+static inline unsigned int iomap_dio_alignment(struct inode *inode,\n+\t\tstruct block_device *bdev, unsigned int dio_flags)\n+{\n+\tif (dio_flags \u0026 IOMAP_DIO_FSBLOCK_ALIGNED)\n+\t\treturn i_blocksize(inode);\n+\treturn bdev_logical_block_size(bdev);\n+}\n+\n static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)\n {\n \tconst struct iomap *iomap = \u0026iter-\u003eiomap;\n@@ -416,10 +433,7 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)\n \t * File systems that write out of place and always allocate new blocks\n \t * need each bio to be block aligned as that's the unit of allocation.\n \t */\n-\tif (dio-\u003eflags \u0026 IOMAP_DIO_FSBLOCK_ALIGNED)\n-\t\talignment = fs_block_size;\n-\telse\n-\t\talignment = bdev_logical_block_size(iomap-\u003ebdev);\n+\talignment = iomap_dio_alignment(inode, iomap-\u003ebdev, dio-\u003eflags);\n \n \tif ((pos | length) \u0026 (alignment - 1))\n \t\treturn -EINVAL;\n@@ -888,12 +902,253 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,\n }\n EXPORT_SYMBOL_GPL(__iomap_dio_rw);\n \n+struct iomap_dio_simple {\n+\tstruct kiocb\t\t*iocb;\n+\tsize_t\t\t\tsize;\n+\tunsigned int\t\tdio_flags;\n+\tstruct work_struct\twork;\n+\t/*\n+\t * Align @bio to a cacheline boundary so that, combined with the\n+\t * front_pad passed to bioset_init(), the bio sits at the start of\n+\t * a cacheline in memory returned by the (HWCACHE-aligned) bio\n+\t * slab.  This keeps the hot fields block layer touches on submit\n+\t * and completion (bi_iter, bi_status, ...) within a single line.\n+\t */\n+\tstruct bio\tbio ____cacheline_aligned_in_smp;\n+};\n+\n+static struct bio_set iomap_dio_simple_pool;\n+\n+static ssize_t iomap_dio_simple_complete(struct iomap_dio_simple *sr)\n+{\n+\tstruct bio *bio = \u0026sr-\u003ebio;\n+\tstruct kiocb *iocb = sr-\u003eiocb;\n+\tstruct inode *inode = file_inode(iocb-\u003eki_filp);\n+\tint error = blk_status_to_errno(bio-\u003ebi_status);\n+\tssize_t ret = error;\n+\n+\tif (likely(!ret)) {\n+\t\tret = sr-\u003esize;\n+\t\tiocb-\u003eki_pos += ret;\n+\t} else if (should_report_dio_fserror(ret)) {\n+\t\tfserror_report_io(inode, FSERR_DIRECTIO_READ, iocb-\u003eki_pos,\n+\t\t\t\t  sr-\u003esize, ret, GFP_NOFS);\n+\t}\n+\n+\tiomap_dio_bio_release_pages(bio, sr-\u003edio_flags, ret \u003c 0);\n+\tinode_dio_end(inode);\n+\ttrace_iomap_dio_complete(iocb, error, ret);\n+\treturn ret;\n+}\n+\n+static void iomap_dio_simple_complete_work(struct work_struct *work)\n+{\n+\tstruct iomap_dio_simple *sr =\n+\t\t\tcontainer_of(work, struct iomap_dio_simple, work);\n+\n+\tWRITE_ONCE(sr-\u003eiocb-\u003eprivate, NULL);\n+\tsr-\u003eiocb-\u003eki_complete(sr-\u003eiocb, iomap_dio_simple_complete(sr));\n+}\n+\n+static void iomap_dio_simple_end_io(struct bio *bio)\n+{\n+\tstruct iomap_dio_simple *sr =\n+\t\tcontainer_of(bio, struct iomap_dio_simple, bio);\n+\n+\tif (unlikely(sr-\u003ebio.bi_status)) {\n+\t\tstruct inode *inode = file_inode(sr-\u003eiocb-\u003eki_filp);\n+\n+\t\tINIT_WORK(\u0026sr-\u003ework, iomap_dio_simple_complete_work);\n+\t\tqueue_work(inode-\u003ei_sb-\u003es_dio_done_wq, \u0026sr-\u003ework);\n+\t\treturn;\n+\t}\n+\n+\tsr-\u003eiocb-\u003eki_complete(sr-\u003eiocb, iomap_dio_simple_complete(sr));\n+}\n+\n+static inline bool iomap_dio_simple_supported(struct kiocb *iocb,\n+\t\tstruct iov_iter *iter, unsigned int dio_flags)\n+{\n+\tstruct inode *inode = file_inode(iocb-\u003eki_filp);\n+\tsize_t count = iov_iter_count(iter);\n+\n+\tif (iov_iter_rw(iter) != READ)\n+\t\treturn false;\n+\tif (!count)\n+\t\treturn false;\n+\t/*\n+\t * Simple dio is an optimization for small IO. Filter out large IO\n+\t * early as it's the most common case to fail for typical direct IO\n+\t * workloads.\n+\t */\n+\tif (count \u003e inode-\u003ei_sb-\u003es_blocksize)\n+\t\treturn false;\n+\tif (dio_flags \u0026 (IOMAP_DIO_FORCE_WAIT | IOMAP_DIO_PARTIAL |\n+\t\t\t IOMAP_DIO_BOUNCE))\n+\t\treturn false;\n+\tif (iocb-\u003eki_pos + count \u003e i_size_read(inode))\n+\t\treturn false;\n+\tif (IS_ENCRYPTED(inode))\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+static ssize_t iomap_dio_simple(struct kiocb *iocb,\n+\t\tstruct iov_iter *iter, const struct iomap_ops *ops,\n+\t\tvoid *private, unsigned int dio_flags)\n+{\n+\tstruct inode *inode = file_inode(iocb-\u003eki_filp);\n+\tsize_t count = iov_iter_count(iter);\n+\tstruct iomap_dio_simple *sr;\n+\tunsigned int alignment;\n+\tstruct iomap_iter iomi = {\n+\t\t.inode\t\t= inode,\n+\t\t.pos\t\t= iocb-\u003eki_pos,\n+\t\t.len\t\t= count,\n+\t\t.flags\t\t= IOMAP_DIRECT,\n+\t\t.private\t= private,\n+\t};\n+\tstruct bio *bio;\n+\tbool wait_for_completion = is_sync_kiocb(iocb);\n+\tssize_t ret;\n+\n+\tif (iocb-\u003eki_flags \u0026 IOCB_NOWAIT)\n+\t\tiomi.flags |= IOMAP_NOWAIT;\n+\n+\tret = kiocb_write_and_wait(iocb, count);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tinode_dio_begin(inode);\n+\n+\tret = ops-\u003eiomap_begin(inode, iomi.pos, count, iomi.flags,\n+\t\t\t       \u0026iomi.iomap, \u0026iomi.srcmap);\n+\tif (ret) {\n+\t\tinode_dio_end(inode);\n+\t\treturn ret;\n+\t}\n+\n+\tif (iomi.iomap.type != IOMAP_MAPPED ||\n+\t    iomi.iomap.offset + iomi.iomap.length \u003c iomi.pos + count ||\n+\t    (iomi.iomap.flags \u0026 IOMAP_F_INTEGRITY)) {\n+\t\tret = -ENOTBLK;\n+\t\tgoto out_iomap_end;\n+\t}\n+\n+\talignment = iomap_dio_alignment(inode, iomi.iomap.bdev, dio_flags);\n+\tif ((iomi.pos | count) \u0026 (alignment - 1)) {\n+\t\tret = -EINVAL;\n+\t\tgoto out_iomap_end;\n+\t}\n+\n+\tif (!wait_for_completion \u0026\u0026 unlikely(!inode-\u003ei_sb-\u003es_dio_done_wq)) {\n+\t\tret = sb_init_dio_done_wq(inode-\u003ei_sb);\n+\t\tif (ret \u003c 0)\n+\t\t\tgoto out_iomap_end;\n+\t}\n+\n+\ttrace_iomap_dio_rw_begin(iocb, iter, dio_flags, 0);\n+\n+\tif (user_backed_iter(iter))\n+\t\tdio_flags |= IOMAP_DIO_USER_BACKED;\n+\n+\tbio = bio_alloc_bioset(iomi.iomap.bdev,\n+\t\t\tbio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),\n+\t\t\tREQ_OP_READ,\n+\t\t\tGFP_KERNEL, \u0026iomap_dio_simple_pool);\n+\tsr = container_of(bio, struct iomap_dio_simple, bio);\n+\tsr-\u003eiocb = iocb;\n+\tsr-\u003edio_flags = dio_flags;\n+\n+\tbio-\u003ebi_iter.bi_sector = iomap_sector(\u0026iomi.iomap, iomi.pos);\n+\tbio-\u003ebi_ioprio = iocb-\u003eki_ioprio;\n+\n+\tret = bio_iov_iter_get_pages(bio, iter, alignment - 1);\n+\tif (unlikely(ret))\n+\t\tgoto out_bio_put;\n+\n+\tif (bio-\u003ebi_iter.bi_size != count) {\n+\t\tiov_iter_revert(iter, bio-\u003ebi_iter.bi_size);\n+\t\tret = -ENOTBLK;\n+\t\tgoto out_bio_release_pages;\n+\t}\n+\n+\tsr-\u003esize = bio-\u003ebi_iter.bi_size;\n+\n+\tif (dio_flags \u0026 IOMAP_DIO_USER_BACKED)\n+\t\tbio_set_pages_dirty(bio);\n+\n+\tif (iocb-\u003eki_flags \u0026 IOCB_NOWAIT)\n+\t\tbio-\u003ebi_opf |= REQ_NOWAIT;\n+\tif ((iocb-\u003eki_flags \u0026 IOCB_HIPRI) \u0026\u0026 !wait_for_completion) {\n+\t\tbio-\u003ebi_opf |= REQ_POLLED;\n+\t\tWRITE_ONCE(iocb-\u003eprivate, bio);\n+\t}\n+\n+\tif (ops-\u003eiomap_end)\n+\t\tops-\u003eiomap_end(inode, iomi.pos, count, count, iomi.flags,\n+\t\t\t       \u0026iomi.iomap);\n+\n+\tif (!wait_for_completion) {\n+\t\tbio-\u003ebi_end_io = iomap_dio_simple_end_io;\n+\t\tsubmit_bio(bio);\n+\t\ttrace_iomap_dio_rw_queued(inode, iomi.pos, count);\n+\t\treturn -EIOCBQUEUED;\n+\t}\n+\n+\tsubmit_bio_wait(bio);\n+\treturn iomap_dio_simple_complete(sr);\n+\n+out_bio_release_pages:\n+\tbio_release_pages(bio, false);\n+out_bio_put:\n+\tbio_put(bio);\n+out_iomap_end:\n+\tif (ops-\u003eiomap_end)\n+\t\tops-\u003eiomap_end(inode, iomi.pos, count, 0, iomi.flags,\n+\t\t\t       \u0026iomi.iomap);\n+\tinode_dio_end(inode);\n+\treturn ret;\n+}\n+\n ssize_t\n iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,\n \t\tconst struct iomap_ops *ops, const struct iomap_dio_ops *dops,\n \t\tunsigned int dio_flags, void *private, size_t done_before)\n {\n \tstruct iomap_dio *dio;\n+\tssize_t ret;\n+\n+\t/*\n+\t * Fast path for small, block-aligned direct I/Os that map to a\n+\t * single contiguous on-disk extent.\n+\t *\n+\t * @dops must be NULL: a non-NULL @dops means the caller wants its\n+\t * -\u003eend_io / -\u003esubmit_io hooks invoked, and in particular wants its\n+\t * bios to be allocated from the filesystem-private @dops-\u003ebio_set\n+\t * (whose front_pad sizes a filesystem-private wrapper around the\n+\t * bio).  The fast path instead allocates from the shared\n+\t * iomap_dio_simple_pool, whose front_pad matches\n+\t * struct iomap_dio_simple; the two wrappers are not\n+\t * interchangeable, so we must fall back to __iomap_dio_rw() in\n+\t * that case.\n+\t *\n+\t * @done_before must be zero: a non-zero caller-accumulated residual\n+\t * cannot be carried through a single-bio inline completion.\n+\t *\n+\t * -ENOTBLK is the private sentinel returned by iomap_dio_simple()\n+\t * when it decides the request does not fit the fast path.\n+\t * In that case we proceed to the generic __iomap_dio_rw() slow\n+\t * path.  Any other errno is a real result and is propagated as-is,\n+\t * in particular -EAGAIN for IOCB_NOWAIT must reach the caller.\n+\t */\n+\tif (!dops \u0026\u0026 !done_before \u0026\u0026\n+\t    iomap_dio_simple_supported(iocb, iter, dio_flags)) {\n+\t\tret = iomap_dio_simple(iocb, iter, ops, private, dio_flags);\n+\t\tif (ret != -ENOTBLK)\n+\t\t\treturn ret;\n+\t}\n \n \tdio = __iomap_dio_rw(iocb, iter, ops, dops, dio_flags, private,\n \t\t\t     done_before);\n@@ -902,3 +1157,11 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,\n \treturn iomap_dio_complete(dio);\n }\n EXPORT_SYMBOL_GPL(iomap_dio_rw);\n+\n+static int __init iomap_dio_init(void)\n+{\n+\treturn bioset_init(\u0026iomap_dio_simple_pool, 4,\n+\t\t\t   offsetof(struct iomap_dio_simple, bio),\n+\t\t\t   BIOSET_NEED_BVECS | BIOSET_PERCPU_CACHE);\n+}\n+fs_initcall(iomap_dio_init);\n"
}

2/1 2026/06/29 12:20 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [
    "iomap_dio_simple",
    "iomap_dio_simple_complete",
    "iomap_dio_simple_complete_work",
    "iomap_dio_simple_end_io"
  ],
  "Reasoning": "The patch introduces a new fast path for direct I/O reads (`iomap_dio_simple`) in the iomap subsystem. This is a functional change to core kernel I/O logic that is reachable from userspace via direct I/O system calls on supported filesystems. It should be fuzzed to ensure the new fast path handles various I/O conditions correctly.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit a80b68ad6068f08cc7e6f016cba3752a196647a2
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Jun 29 12:20:03 2026 +0000

    syz-cluster: applied patch under review

diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
index b485e3b191dafa..4dcc9fc766a92e 100644
--- a/fs/iomap/direct-io.c
+++ b/fs/iomap/direct-io.c
@@ -10,6 +10,9 @@
 #include <linux/iomap.h>
 #include <linux/task_io_accounting_ops.h>
 #include <linux/fserror.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/init.h>
 #include "internal.h"
 #include "trace.h"
 
@@ -88,9 +91,9 @@ static inline enum fserror_type iomap_dio_err_type(const struct iomap_dio *dio)
 	return FSERR_DIRECTIO_READ;
 }
 
-static inline bool should_report_dio_fserror(const struct iomap_dio *dio)
+static inline bool should_report_dio_fserror(int error)
 {
-	switch (dio->error) {
+	switch (error) {
 	case 0:
 	case -EAGAIN:
 	case -ENOTBLK:
@@ -110,7 +113,7 @@ ssize_t iomap_dio_complete(struct iomap_dio *dio)
 
 	if (dops && dops->end_io)
 		ret = dops->end_io(iocb, dio->size, ret, dio->flags);
-	if (should_report_dio_fserror(dio))
+	if (should_report_dio_fserror(dio->error))
 		fserror_report_io(file_inode(iocb->ki_filp),
 				  iomap_dio_err_type(dio), offset, dio->size,
 				  dio->error, GFP_NOFS);
@@ -237,23 +240,29 @@ static void iomap_dio_done(struct iomap_dio *dio)
 	iomap_dio_complete_work(&dio->aio.work);
 }
 
-static void __iomap_dio_bio_end_io(struct bio *bio, bool inline_completion)
+static inline void iomap_dio_bio_release_pages(struct bio *bio,
+		unsigned int dio_flags, bool error)
 {
-	struct iomap_dio *dio = bio->bi_private;
-
 	if (bio_integrity(bio))
 		fs_bio_integrity_free(bio);
 
-	if (dio->flags & IOMAP_DIO_BOUNCE) {
-		bio_iov_iter_unbounce(bio, !!dio->error,
-				dio->flags & IOMAP_DIO_USER_BACKED);
+	if (dio_flags & IOMAP_DIO_BOUNCE) {
+		bio_iov_iter_unbounce(bio, error,
+				dio_flags & IOMAP_DIO_USER_BACKED);
 		bio_put(bio);
-	} else if (dio->flags & IOMAP_DIO_USER_BACKED) {
+	} else if (dio_flags & IOMAP_DIO_USER_BACKED) {
 		bio_check_pages_dirty(bio);
 	} else {
 		bio_release_pages(bio, false);
 		bio_put(bio);
 	}
+}
+
+static void __iomap_dio_bio_end_io(struct bio *bio, bool inline_completion)
+{
+	struct iomap_dio *dio = bio->bi_private;
+
+	iomap_dio_bio_release_pages(bio, dio->flags, !!dio->error);
 
 	/* Do not touch bio below, we just gave up our reference. */
 
@@ -398,6 +407,14 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter,
 	return ret;
 }
 
+static inline unsigned int iomap_dio_alignment(struct inode *inode,
+		struct block_device *bdev, unsigned int dio_flags)
+{
+	if (dio_flags & IOMAP_DIO_FSBLOCK_ALIGNED)
+		return i_blocksize(inode);
+	return bdev_logical_block_size(bdev);
+}
+
 static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
 {
 	const struct iomap *iomap = &iter->iomap;
@@ -416,10 +433,7 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
 	 * File systems that write out of place and always allocate new blocks
 	 * need each bio to be block aligned as that's the unit of allocation.
 	 */
-	if (dio->flags & IOMAP_DIO_FSBLOCK_ALIGNED)
-		alignment = fs_block_size;
-	else
-		alignment = bdev_logical_block_size(iomap->bdev);
+	alignment = iomap_dio_alignment(inode, iomap->bdev, dio->flags);
 
 	if ((pos | length) & (alignment - 1))
 		return -EINVAL;
@@ -888,12 +902,253 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
 }
 EXPORT_SYMBOL_GPL(__iomap_dio_rw);
 
+struct iomap_dio_simple {
+	struct kiocb		*iocb;
+	size_t			size;
+	unsigned int		dio_flags;
+	struct work_struct	work;
+	/*
+	 * Align @bio to a cacheline boundary so that, combined with the
+	 * front_pad passed to bioset_init(), the bio sits at the start of
+	 * a cacheline in memory returned by the (HWCACHE-aligned) bio
+	 * slab.  This keeps the hot fields block layer touches on submit
+	 * and completion (bi_iter, bi_status, ...) within a single line.
+	 */
+	struct bio	bio ____cacheline_aligned_in_smp;
+};
+
+static struct bio_set iomap_dio_simple_pool;
+
+static ssize_t iomap_dio_simple_complete(struct iomap_dio_simple *sr)
+{
+	struct bio *bio = &sr->bio;
+	struct kiocb *iocb = sr->iocb;
+	struct inode *inode = file_inode(iocb->ki_filp);
+	int error = blk_status_to_errno(bio->bi_status);
+	ssize_t ret = error;
+
+	if (likely(!ret)) {
+		ret = sr->size;
+		iocb->ki_pos += ret;
+	} else if (should_report_dio_fserror(ret)) {
+		fserror_report_io(inode, FSERR_DIRECTIO_READ, iocb->ki_pos,
+				  sr->size, ret, GFP_NOFS);
+	}
+
+	iomap_dio_bio_release_pages(bio, sr->dio_flags, ret < 0);
+	inode_dio_end(inode);
+	trace_iomap_dio_complete(iocb, error, ret);
+	return ret;
+}
+
+static void iomap_dio_simple_complete_work(struct work_struct *work)
+{
+	struct iomap_dio_simple *sr =
+			container_of(work, struct iomap_dio_simple, work);
+
+	WRITE_ONCE(sr->iocb->private, NULL);
+	sr->iocb->ki_complete(sr->iocb, iomap_dio_simple_complete(sr));
+}
+
+static void iomap_dio_simple_end_io(struct bio *bio)
+{
+	struct iomap_dio_simple *sr =
+		container_of(bio, struct iomap_dio_simple, bio);
+
+	if (unlikely(sr->bio.bi_status)) {
+		struct inode *inode = file_inode(sr->iocb->ki_filp);
+
+		INIT_WORK(&sr->work, iomap_dio_simple_complete_work);
+		queue_work(inode->i_sb->s_dio_done_wq, &sr->work);
+		return;
+	}
+
+	sr->iocb->ki_complete(sr->iocb, iomap_dio_simple_complete(sr));
+}
+
+static inline bool iomap_dio_simple_supported(struct kiocb *iocb,
+		struct iov_iter *iter, unsigned int dio_flags)
+{
+	struct inode *inode = file_inode(iocb->ki_filp);
+	size_t count = iov_iter_count(iter);
+
+	if (iov_iter_rw(iter) != READ)
+		return false;
+	if (!count)
+		return false;
+	/*
+	 * 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 (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 ssize_t iomap_dio_simple(struct kiocb *iocb,
+		struct iov_iter *iter, const struct iomap_ops *ops,
+		void *private, unsigned int dio_flags)
+{
+	struct inode *inode = file_inode(iocb->ki_filp);
+	size_t count = iov_iter_count(iter);
+	struct iomap_dio_simple *sr;
+	unsigned int alignment;
+	struct iomap_iter iomi = {
+		.inode		= inode,
+		.pos		= iocb->ki_pos,
+		.len		= count,
+		.flags		= IOMAP_DIRECT,
+		.private	= private,
+	};
+	struct bio *bio;
+	bool wait_for_completion = is_sync_kiocb(iocb);
+	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 = ops->iomap_begin(inode, iomi.pos, count, iomi.flags,
+			       &iomi.iomap, &iomi.srcmap);
+	if (ret) {
+		inode_dio_end(inode);
+		return ret;
+	}
+
+	if (iomi.iomap.type != IOMAP_MAPPED ||
+	    iomi.iomap.offset + iomi.iomap.length < iomi.pos + count ||
+	    (iomi.iomap.flags & IOMAP_F_INTEGRITY)) {
+		ret = -ENOTBLK;
+		goto out_iomap_end;
+	}
+
+	alignment = iomap_dio_alignment(inode, iomi.iomap.bdev, dio_flags);
+	if ((iomi.pos | count) & (alignment - 1)) {
+		ret = -EINVAL;
+		goto out_iomap_end;
+	}
+
+	if (!wait_for_completion && unlikely(!inode->i_sb->s_dio_done_wq)) {
+		ret = sb_init_dio_done_wq(inode->i_sb);
+		if (ret < 0)
+			goto out_iomap_end;
+	}
+
+	trace_iomap_dio_rw_begin(iocb, iter, dio_flags, 0);
+
+	if (user_backed_iter(iter))
+		dio_flags |= IOMAP_DIO_USER_BACKED;
+
+	bio = bio_alloc_bioset(iomi.iomap.bdev,
+			bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),
+			REQ_OP_READ,
+			GFP_KERNEL, &iomap_dio_simple_pool);
+	sr = container_of(bio, struct iomap_dio_simple, bio);
+	sr->iocb = iocb;
+	sr->dio_flags = dio_flags;
+
+	bio->bi_iter.bi_sector = iomap_sector(&iomi.iomap, iomi.pos);
+	bio->bi_ioprio = iocb->ki_ioprio;
+
+	ret = bio_iov_iter_get_pages(bio, iter, alignment - 1);
+	if (unlikely(ret))
+		goto out_bio_put;
+
+	if (bio->bi_iter.bi_size != count) {
+		iov_iter_revert(iter, bio->bi_iter.bi_size);
+		ret = -ENOTBLK;
+		goto out_bio_release_pages;
+	}
+
+	sr->size = bio->bi_iter.bi_size;
+
+	if (dio_flags & IOMAP_DIO_USER_BACKED)
+		bio_set_pages_dirty(bio);
+
+	if (iocb->ki_flags & IOCB_NOWAIT)
+		bio->bi_opf |= REQ_NOWAIT;
+	if ((iocb->ki_flags & IOCB_HIPRI) && !wait_for_completion) {
+		bio->bi_opf |= REQ_POLLED;
+		WRITE_ONCE(iocb->private, bio);
+	}
+
+	if (ops->iomap_end)
+		ops->iomap_end(inode, iomi.pos, count, count, iomi.flags,
+			       &iomi.iomap);
+
+	if (!wait_for_completion) {
+		bio->bi_end_io = iomap_dio_simple_end_io;
+		submit_bio(bio);
+		trace_iomap_dio_rw_queued(inode, iomi.pos, count);
+		return -EIOCBQUEUED;
+	}
+
+	submit_bio_wait(bio);
+	return iomap_dio_simple_complete(sr);
+
+out_bio_release_pages:
+	bio_release_pages(bio, false);
+out_bio_put:
+	bio_put(bio);
+out_iomap_end:
+	if (ops->iomap_end)
+		ops->iomap_end(inode, iomi.pos, count, 0, iomi.flags,
+			       &iomi.iomap);
+	inode_dio_end(inode);
+	return ret;
+}
+
 ssize_t
 iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
 		const struct iomap_ops *ops, const struct iomap_dio_ops *dops,
 		unsigned int dio_flags, void *private, size_t done_before)
 {
 	struct iomap_dio *dio;
+	ssize_t ret;
+
+	/*
+	 * Fast path for small, block-aligned direct I/Os that map to a
+	 * single contiguous on-disk extent.
+	 *
+	 * @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.
+	 *
+	 * -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.
+	 */
+	if (!dops && !done_before &&
+	    iomap_dio_simple_supported(iocb, iter, dio_flags)) {
+		ret = iomap_dio_simple(iocb, iter, ops, private, dio_flags);
+		if (ret != -ENOTBLK)
+			return ret;
+	}
 
 	dio = __iomap_dio_rw(iocb, iter, ops, dops, dio_flags, private,
 			     done_before);
@@ -902,3 +1157,11 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
 	return iomap_dio_complete(dio);
 }
 EXPORT_SYMBOL_GPL(iomap_dio_rw);
+
+static int __init iomap_dio_init(void)
+{
+	return bioset_init(&iomap_dio_simple_pool, 4,
+			   offsetof(struct iomap_dio_simple, bio),
+			   BIOSET_NEED_BVECS | BIOSET_PERCPU_CACHE);
+}
+fs_initcall(iomap_dio_init);

3/2 2026/06/29 12:20 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6330
							output: 33
							thoughts: 252
						

Thoughts:
**Analyzing a New Direct I/O Fast Path Patch**

Okay, so there's a new patch aimed at accelerating direct I/O reads within `fs/iomap/direct-io.c`. The core idea is a "simple" fast path, handled by a new function, `iomap_dio_simple`. It looks like `iomap_dio_simple` is conditionally called from `iomap_dio_rw` based on the return value of `iomap_dio_simple_supported`. This is a pretty significant functional change, impacting the heart of iomap direct I/O, so fuzzing this is absolutely critical.

Let's dive into the details. I see the patch introduces a suite of new functions: `iomap_dio_simple_supported`, `iomap_dio_simple`, `iomap_dio_simple_complete`, `iomap_dio_simple_complete_work`, and `iomap_dio_simple_end_io`. And, as expected, it modifies `iomap_dio_rw` to actually leverage that new `iomap_dio_simple` fast path.

Before we even think about fuzzing, let's nail down the configuration dependencies. `fs/iomap/direct-io.c` is compiled when `CONFIG_FS_IOMAP` is enabled, which makes sense. I'll need to double-check `fs/iomap/Makefile` or `fs/iomap/Kconfig` to be absolutely certain I understand all the necessary configuration options, but this should be straightforward.



4/2 2026/06/29 12:20 tool grepper
0m Args:
{
  "Expression": "CONFIG_FS_IOMAP",
  "PathPrefix": "fs/iomap/"
}

Results:
{
  "Output": "fs/iomap/Makefile=7=ccflags-y += -I $(src)\t\t# needed for trace events\nfs/iomap/Makefile-8-\nfs/iomap/Makefile:9:obj-$(CONFIG_FS_IOMAP)\t\t+= iomap.o\nfs/iomap/Makefile-10-\n"
}

5/2 2026/06/29 12:20 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7054
							output: 28
							thoughts: 15
						

Thoughts:
Here's a summary of my thought process, as requested:

**Investigating the Configuration for CONFIG_FS_IOMAP**

Okay, so I'm looking at `CONFIG_FS_IOMAP`. What's making this functionality tick? Time to delve into the kernel's configuration options and source code. I need to figure out the dependencies, the underlying assumptions, and any potential conflicts that might arise from enabling or disabling this. I know the general purpose, IOMAP is an important component for managing direct I/O. Now, let's explore this specific configuration item in detail. Understanding the precise relationship between `CONFIG_FS_IOMAP` and other related features – the filesystem layers, the block I/O stack, and any specific driver dependencies – is critical here. I'll need to trace through the relevant Kconfig files and code to see how it all fits together, the usual process when deciphering kernel configurations. Let's start digging.



6/2 2026/06/29 12:20 tool grepper
0m Args:
{
  "Expression": "select FS_IOMAP",
  "PathPrefix": "fs/"
}

Results:
{
  "Output": "fs/Kconfig=58=config FS_DAX\n--\nfs/Kconfig-61-\tdepends on ZONE_DEVICE\nfs/Kconfig:62:\tselect FS_IOMAP\nfs/Kconfig-63-\tselect DAX\n--\nfs/btrfs/Kconfig=3=config BTRFS_FS\n--\nfs/btrfs/Kconfig-14-\tselect ZSTD_DECOMPRESS\nfs/btrfs/Kconfig:15:\tselect FS_IOMAP\nfs/btrfs/Kconfig-16-\tselect RAID6_PQ\n--\nfs/erofs/Kconfig=3=config EROFS_FS\n--\nfs/erofs/Kconfig-8-\tselect CRYPTO_DEFLATE if EROFS_FS_ZIP_ACCEL\nfs/erofs/Kconfig:9:\tselect FS_IOMAP\nfs/erofs/Kconfig-10-\tselect LZ4_DECOMPRESS if EROFS_FS_ZIP\n--\nfs/exfat/Kconfig=3=config EXFAT_FS\n--\nfs/exfat/Kconfig-6-\tselect NLS\nfs/exfat/Kconfig:7:\tselect FS_IOMAP\nfs/exfat/Kconfig-8-\thelp\n--\nfs/ext2/Kconfig=2=config EXT2_FS\n--\nfs/ext2/Kconfig-4-\tselect BUFFER_HEAD\nfs/ext2/Kconfig:5:\tselect FS_IOMAP\nfs/ext2/Kconfig-6-\thelp\n--\nfs/ext4/Kconfig=2=config EXT4_FS\n--\nfs/ext4/Kconfig-7-\tselect CRC32\nfs/ext4/Kconfig:8:\tselect FS_IOMAP\nfs/ext4/Kconfig-9-\tselect FS_ENCRYPTION_ALGS if FS_ENCRYPTION\n--\nfs/f2fs/Kconfig=2=config F2FS_FS\n--\nfs/f2fs/Kconfig-9-\tselect FS_ENCRYPTION_ALGS if FS_ENCRYPTION\nfs/f2fs/Kconfig:10:\tselect FS_IOMAP\nfs/f2fs/Kconfig-11-\tselect LZ4_COMPRESS if F2FS_FS_LZ4\n--\nfs/fuse/Kconfig=2=config FUSE_FS\n--\nfs/fuse/Kconfig-4-\tselect FS_POSIX_ACL\nfs/fuse/Kconfig:5:\tselect FS_IOMAP\nfs/fuse/Kconfig-6-\thelp\n--\nfs/gfs2/Kconfig=2=config GFS2_FS\n--\nfs/gfs2/Kconfig-7-\tselect QUOTACTL\nfs/gfs2/Kconfig:8:\tselect FS_IOMAP\nfs/gfs2/Kconfig-9-\thelp\n--\nfs/hpfs/Kconfig=2=config HPFS_FS\n--\nfs/hpfs/Kconfig-5-\tselect BUFFER_HEAD\nfs/hpfs/Kconfig:6:\tselect FS_IOMAP\nfs/hpfs/Kconfig-7-\thelp\n--\nfs/ntfs/Kconfig=2=config NTFS_FS\n--\nfs/ntfs/Kconfig-4-\tselect NLS\nfs/ntfs/Kconfig:5:\tselect FS_IOMAP\nfs/ntfs/Kconfig-6-\thelp\n--\nfs/xfs/Kconfig=2=config XFS_FS\n--\nfs/xfs/Kconfig-6-\tselect CRC32\nfs/xfs/Kconfig:7:\tselect FS_IOMAP\nfs/xfs/Kconfig-8-\thelp\n--\nfs/zonefs/Kconfig=1=config ZONEFS_FS\n--\nfs/zonefs/Kconfig-4-\tdepends on BLK_DEV_ZONED\nfs/zonefs/Kconfig:5:\tselect FS_IOMAP\nfs/zonefs/Kconfig-6-\tselect CRC32\n"
}

7/2 2026/06/29 12:20 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8278
							output: 28
							thoughts: 556
						

Thoughts:
**Analyzing Direct I/O and iomap for Targeted Fuzzing**

Okay, so I'm thinking about how to effectively target `iomap_dio_simple` and its related functions for fuzzing. Since `FS_IOMAP` is widely used, there's no need to explicitly enable it, but defining specific filesystems like `XFS_FS`, `EXT4_FS`, or `BTRFS_FS` will make sure that iomap is being used, although syzkaller probably already has those covered. I checked for any specific configs related to `iomap_dio_simple`, but it looks like it's just part of `direct-io.c`.

My primary focus should be on the core functions. Here are the ones I want to zero in on: `iomap_dio_simple`, `iomap_dio_simple_complete`, `iomap_dio_simple_complete_work`, and `iomap_dio_simple_end_io`. I initially wondered about `iomap_dio_simple_supported`, since it's an inline function, but I wanted to make sure it was actually inlined.

A quick check confirmed that it is indeed inline: `static inline bool iomap_dio_simple_supported(...)`. Therefore, I'll exclude it from the target list.

I had considered `iomap_dio_rw`, but since it's a heavily used function, I want to avoid it in favor of newly added functions.

So, the key functions for fuzzing are: `iomap_dio_simple`, `iomap_dio_simple_complete`, `iomap_dio_simple_complete_work`, and `iomap_dio_simple_end_io`.

For enabling configs, while `FS_IOMAP` should work, I should specify either `FS_IOMAP` or `EXT4_FS`, `XFS_FS`, or `BTRFS_FS` to make sure we have a filesystem that uses iomap. However, `FS_IOMAP` seems to be a hidden config, so I'll need to double-check `fs/Kconfig` to confirm that.



8/2 2026/06/29 12:20 tool grepper
0m Args:
{
  "Expression": "config FS_IOMAP",
  "PathPrefix": "fs/"
}

Results:
{
  "Output": "fs/Kconfig=12=config VALIDATE_FS_PARSER\n--\nfs/Kconfig-17-\nfs/Kconfig:18:config FS_IOMAP\nfs/Kconfig-19-\tbool\n"
}

9/2 2026/06/29 12:20 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9409
							output: 159
							thoughts: 363
						

Thoughts:
**My Configuration Assessment**

Okay, so this is a hidden config, got it. We could list the filesystem configs like `XFS_FS`, `EXT4_FS`, and `BTRFS_FS` in the `EnableConfigs` list, but those are extremely generic. But the prompt said not to include generic configs, only ones that directly relate to the modified code. Hmm, I could just leave `EnableConfigs` empty or put in those specific filesystems.

Wait, the prompt said to avoid the generic ones since they are already enabled. Since `FS_IOMAP` gets selected for those generic filesystems, I think I'll just leave `EnableConfigs` empty for now.

Let me make sure my reasoning is sound: This patch adds a fast path for direct I/O reads within the iomap subsystem. It's a fundamental change to core kernel I/O logic, accessible from userspace through direct I/O system calls on filesystems like XFS, ext4, and btrfs. It *definitely* needs fuzzing to verify that the new fast path handles various I/O scenarios properly.

Let's double-check quickly for any other functions. Aha, `iomap_dio_bio_release_pages` and `iomap_dio_alignment` are both inline, and `iomap_dio_init` is an initcall, so it only runs once at boot.

So, the critical symbols to focus on are:
*   `iomap_dio_simple`
*   `iomap_dio_simple_complete`
*   `iomap_dio_simple_complete_work`
*   `iomap_dio_simple_end_io`

Okay, no other configs seem relevant. Time to call `set-results`.



10/2 2026/06/29 12:20 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [
    "iomap_dio_simple",
    "iomap_dio_simple_complete",
    "iomap_dio_simple_complete_work",
    "iomap_dio_simple_end_io"
  ],
  "Reasoning": "The patch introduces a new fast path for direct I/O reads (`iomap_dio_simple`) in the iomap subsystem. This is a functional change to core kernel I/O logic that is reachable from userspace via direct I/O system calls on supported filesystems. It should be fuzzed to ensure the new fast path handles various I/O conditions correctly.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [
    "iomap_dio_simple",
    "iomap_dio_simple_complete",
    "iomap_dio_simple_complete_work",
    "iomap_dio_simple_end_io"
  ],
  "Reasoning": "The patch introduces a new fast path for direct I/O reads (`iomap_dio_simple`) in the iomap subsystem. This is a functional change to core kernel I/O logic that is reachable from userspace via direct I/O system calls on supported filesystems. It should be fuzzed to ensure the new fast path handles various I/O conditions correctly.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)