AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/03 03:07 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "EVENTFD_OPT",
    "EVENTFD",
    "EPOLL"
  ],
  "FocusSymbols": [
    "eventfd_signal_mask",
    "eventfd_ctx_remove_wait_queue",
    "eventfd_read",
    "eventfd_write",
    "is_eventfd_file",
    "ep_done_scan",
    "__ep_eventpoll_poll",
    "ep_poll_callback",
    "ep_alloc_epitem",
    "ep_insert",
    "ep_modify",
    "ep_deliver_event"
  ],
  "Reasoning": "The patch introduces a functional optimization for the interaction between eventfd and epoll, adding a lockless fast-path in ep_poll_callback. This modifies core kernel logic in eventfd and eventpoll subsystems, making it highly relevant for fuzzing to ensure no race conditions or lost wakeups occur.",
  "WorthFuzzing": true
}

1/1 2026/08/03 03:07 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 666625a99c5af801ce57a65f486a7f4321273e15\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Aug 3 03:07:19 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/fs/eventfd.c b/fs/eventfd.c\nindex 9d33a02757d52..3d34cd652ac72 100644\n--- a/fs/eventfd.c\n+++ b/fs/eventfd.c\n@@ -43,6 +43,29 @@ struct eventfd_ctx {\n \tint id;\n };\n \n+static inline void eventfd_wake_up_locked_poll(struct eventfd_ctx *ctx,\n+\t\t\t\t\t       __poll_t mask)\n+{\n+\tlockdep_assert_held(\u0026ctx-\u003ewqh.lock);\n+\n+\t/* Protected by ctx-\u003ewqh.lock. */\n+\tif (!waitqueue_active(\u0026ctx-\u003ewqh))\n+\t\treturn;\n+\n+#ifdef CONFIG_EVENTFD_OPT\n+\t/*\n+\t * Eventfd context of the two-variable protocol:\n+\t *\n+\t * W(B) updates ctx-\u003ecount in the caller, then the callback performs\n+\t * R(A) on epitem-\u003enotified.\n+\t *\n+\t * W(B) -\u003e smp_mb() -\u003e R(A)\n+\t */\n+\tsmp_mb();\n+#endif\n+\twake_up_locked_poll(\u0026ctx-\u003ewqh, mask);\n+}\n+\n /**\n  * eventfd_signal_mask - Increment the event counter\n  * @ctx: [in] Pointer to the eventfd context.\n@@ -72,8 +95,7 @@ void eventfd_signal_mask(struct eventfd_ctx *ctx, __poll_t mask)\n \tcurrent-\u003ein_eventfd = 1;\n \tif (ctx-\u003ecount \u003c ULLONG_MAX)\n \t\tctx-\u003ecount++;\n-\tif (waitqueue_active(\u0026ctx-\u003ewqh))\n-\t\twake_up_locked_poll(\u0026ctx-\u003ewqh, EPOLLIN | mask);\n+\teventfd_wake_up_locked_poll(ctx, EPOLLIN | mask);\n \tcurrent-\u003ein_eventfd = 0;\n \tspin_unlock_irqrestore(\u0026ctx-\u003ewqh.lock, flags);\n }\n@@ -203,8 +225,8 @@ int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_entry_t *w\n \tspin_lock_irqsave(\u0026ctx-\u003ewqh.lock, flags);\n \teventfd_ctx_do_read(ctx, cnt);\n \t__remove_wait_queue(\u0026ctx-\u003ewqh, wait);\n-\tif (*cnt != 0 \u0026\u0026 waitqueue_active(\u0026ctx-\u003ewqh))\n-\t\twake_up_locked_poll(\u0026ctx-\u003ewqh, EPOLLOUT);\n+\tif (*cnt != 0)\n+\t\teventfd_wake_up_locked_poll(ctx, EPOLLOUT);\n \tspin_unlock_irqrestore(\u0026ctx-\u003ewqh.lock, flags);\n \n \treturn *cnt != 0 ? 0 : -EAGAIN;\n@@ -234,8 +256,7 @@ static ssize_t eventfd_read(struct kiocb *iocb, struct iov_iter *to)\n \t}\n \teventfd_ctx_do_read(ctx, \u0026ucnt);\n \tcurrent-\u003ein_eventfd = 1;\n-\tif (waitqueue_active(\u0026ctx-\u003ewqh))\n-\t\twake_up_locked_poll(\u0026ctx-\u003ewqh, EPOLLOUT);\n+\teventfd_wake_up_locked_poll(ctx, EPOLLOUT);\n \tcurrent-\u003ein_eventfd = 0;\n \tspin_unlock_irq(\u0026ctx-\u003ewqh.lock);\n \tif (unlikely(copy_to_iter(\u0026ucnt, sizeof(ucnt), to) != sizeof(ucnt)))\n@@ -270,8 +291,7 @@ static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t c\n \tif (likely(res \u003e 0)) {\n \t\tctx-\u003ecount += ucnt;\n \t\tcurrent-\u003ein_eventfd = 1;\n-\t\tif (waitqueue_active(\u0026ctx-\u003ewqh))\n-\t\t\twake_up_locked_poll(\u0026ctx-\u003ewqh, EPOLLIN);\n+\t\teventfd_wake_up_locked_poll(ctx, EPOLLIN);\n \t\tcurrent-\u003ein_eventfd = 0;\n \t}\n \tspin_unlock_irq(\u0026ctx-\u003ewqh.lock);\n@@ -310,6 +330,15 @@ static const struct file_operations eventfd_fops = {\n \t.llseek\t\t= noop_llseek,\n };\n \n+#ifdef CONFIG_EVENTFD_OPT\n+bool is_eventfd_file(struct file *file)\n+{\n+\treturn file-\u003ef_op == \u0026eventfd_fops;\n+}\n+EXPORT_SYMBOL_GPL(is_eventfd_file);\n+\n+#endif\n+\n /**\n  * eventfd_fget - Acquire a reference of an eventfd file descriptor.\n  * @fd: [in] Eventfd file descriptor.\ndiff --git a/fs/eventpoll.c b/fs/eventpoll.c\nindex eed8cecd94e3a..09e9239ce2d5e 100644\n--- a/fs/eventpoll.c\n+++ b/fs/eventpoll.c\n@@ -39,6 +39,9 @@\n #include \u003clinux/rculist.h\u003e\n #include \u003clinux/capability.h\u003e\n #include \u003clinux/seqlock.h\u003e\n+#ifdef CONFIG_EVENTFD_OPT\n+#include \u003clinux/eventfd.h\u003e\n+#endif\n #include \u003cnet/busy_poll.h\u003e\n \n /*\n@@ -285,6 +288,14 @@ struct epitem {\n \n \t/* The structure that describe the interested events and the source fd */\n \tstruct epoll_event event;\n+\n+#ifdef CONFIG_EVENTFD_OPT\n+\t/* Set after queueing, cleared before dequeueing from the ready path. */\n+\tbool notified;\n+\n+\t/* True if the monitored file is an eventfd. */\n+\tbool is_eventfd;\n+#endif\n };\n \n /*\n@@ -620,6 +631,65 @@ static inline bool ep_events_available(struct eventpoll *ep)\n \t\tread_seqcount_retry(\u0026ep-\u003eseq, seq);\n }\n \n+#ifdef CONFIG_EVENTFD_OPT\n+/*\n+ * Eventfd/epoll two-variable communication:\n+ *\n+ *   A = epi-\u003enotified\n+ *   B = eventfd count/readiness\n+ *\n+ *   scanner context: W(A = false) -\u003e smp_mb() -\u003e dequeue -\u003e R(B)\n+ *   eventfd context: W(B)         -\u003e smp_mb() -\u003e R(A)\n+ *\n+ * Therefore R(B) == old and R(A) == true cannot both occur.\n+ */\n+static inline void ep_set_notified(struct epitem *epi)\n+{\n+\tif (epi-\u003eis_eventfd)\n+\t\tWRITE_ONCE(epi-\u003enotified, true);\n+}\n+\n+static inline void ep_eventfd_prepare_repoll(struct epitem *epi)\n+{\n+\tif (!epi-\u003eis_eventfd)\n+\t\treturn;\n+\n+\t/*\n+\t * Stop callbacks from skipping before the scanner drops its ready-list\n+\t * ownership. Keep the full barrier between W(A = false) and R(B); the\n+\t * actual dequeue may happen between the barrier and the readiness read.\n+\t */\n+\tWRITE_ONCE(epi-\u003enotified, false);\n+\t/* Scanner context: W(A = false) -\u003e smp_mb() -\u003e R(B). */\n+\tsmp_mb();\n+}\n+\n+static inline bool ep_eventfd_callback_can_skip(struct epitem *epi,\n+\t\t\t\t\t\t__poll_t pollflags)\n+{\n+\tif (!epi-\u003eis_eventfd)\n+\t\treturn false;\n+\n+\tif (READ_ONCE(epi-\u003eevent.events) \u0026\n+\t    (EPOLLEXCLUSIVE | EPOLLET | EPOLLONESHOT))\n+\t\treturn false;\n+\n+\tif (pollflags \u0026 POLLFREE)\n+\t\treturn false;\n+\n+\t/* Eventfd context: W(B) -\u003e smp_mb() -\u003e R(A). */\n+\treturn READ_ONCE(epi-\u003enotified);\n+}\n+#else\n+static inline void ep_set_notified(struct epitem *epi) { }\n+static inline void ep_eventfd_prepare_repoll(struct epitem *epi) { }\n+static inline bool ep_eventfd_callback_can_skip(struct epitem *epi,\n+\t\t\t\t\t\t__poll_t pollflags)\n+{\n+\treturn false;\n+}\n+#endif\n+\n #ifdef CONFIG_NET_RX_BUSY_POLL\n /**\n  * busy_loop_ep_timeout - check if busy poll has timed out. The timeout value\n@@ -1007,6 +1077,7 @@ static void ep_done_scan(struct eventpoll *ep,\n \t\t\t * reverses the iteration order into FIFO.\n \t\t\t */\n \t\t\tlist_add(\u0026epi-\u003erdllink, \u0026ep-\u003erdllist);\n+\t\t\tep_set_notified(epi);\n \t\t\tep_pm_stay_awake(epi);\n \t\t}\n \t}\n@@ -1303,8 +1374,11 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep\n \tmutex_lock_nested(\u0026ep-\u003emtx, depth);\n \tep_start_scan(ep, \u0026scan_batch);\n \tlist_for_each_entry_safe(epi, tmp, \u0026scan_batch, rdllink) {\n+\t\t/* Clear notified before a possible removal from txlist. */\n+\t\tep_eventfd_prepare_repoll(epi);\n \t\tif (ep_item_poll(epi, \u0026pt, depth + 1)) {\n \t\t\tres = EPOLLIN | EPOLLRDNORM;\n+\t\t\tep_set_notified(epi);\n \t\t\tbreak;\n \t\t} else {\n \t\t\t/*\n@@ -1497,6 +1571,9 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v\n \tunsigned long flags;\n \tint ewake = 0;\n \n+\tif (ep_eventfd_callback_can_skip(epi, pollflags))\n+\t\treturn 1;\n+\n \tspin_lock_irqsave(\u0026ep-\u003elock, flags);\n \n \tep_set_busy_poll_napi_id(epi);\n@@ -1529,11 +1606,13 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v\n \t\tif (!epi_on_ovflist(epi)) {\n \t\t\tepi-\u003eovflist_next = READ_ONCE(ep-\u003eovflist);\n \t\t\tWRITE_ONCE(ep-\u003eovflist, epi);\n+\t\t\tep_set_notified(epi);\n \t\t\tep_pm_stay_awake_rcu(epi);\n \t\t}\n \t} else if (!ep_is_linked(epi)) {\n \t\t/* In the usual case, add event to ready list. */\n \t\tlist_add_tail(\u0026epi-\u003erdllink, \u0026ep-\u003erdllist);\n+\t\tep_set_notified(epi);\n \t\tep_pm_stay_awake_rcu(epi);\n \t}\n \n@@ -1840,6 +1919,9 @@ static struct epitem *ep_alloc_epitem(struct eventpoll *ep,\n \tepi-\u003effd = *tf;\n \tepi-\u003eevent = *event;\n \tepi_clear_ovflist(epi);\n+#ifdef CONFIG_EVENTFD_OPT\n+\tepi-\u003eis_eventfd = is_eventfd_file(tfile);\n+#endif\n \n \treturn epi;\n }\n@@ -1956,6 +2038,7 @@ static int ep_insert(struct ep_ctl_ctx *ctx, struct eventpoll *ep,\n \n \tif (revents \u0026\u0026 !ep_is_linked(epi)) {\n \t\tlist_add_tail(\u0026epi-\u003erdllink, \u0026ep-\u003erdllist);\n+\t\tep_set_notified(epi);\n \t\tep_pm_stay_awake(epi);\n \n \t\tif (waitqueue_active(\u0026ep-\u003ewq))\n@@ -2031,6 +2114,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,\n \t\tspin_lock_irq(\u0026ep-\u003elock);\n \t\tif (!ep_is_linked(epi)) {\n \t\t\tlist_add_tail(\u0026epi-\u003erdllink, \u0026ep-\u003erdllist);\n+\t\t\tep_set_notified(epi);\n \t\t\tep_pm_stay_awake(epi);\n \n \t\t\t/* Notify waiting tasks that events are available */\n@@ -2084,6 +2168,12 @@ static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi,\n \t\t__pm_relax(ws);\n \t}\n \n+\t/*\n+\t * Clear notified while epi is still on txlist. A callback that\n+\t * races with the following dequeue must take the slow path and\n+\t * publish the event through ovflist.\n+\t */\n+\tep_eventfd_prepare_repoll(epi);\n \tlist_del_init(\u0026epi-\u003erdllink);\n \n \t/*\n@@ -2104,6 +2194,7 @@ static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi,\n \t\t * attempt.\n \t\t */\n \t\tlist_add(\u0026epi-\u003erdllink, scan_batch);\n+\t\tep_set_notified(epi);\n \t\tep_pm_stay_awake(epi);\n \t\treturn -EFAULT;\n \t}\n@@ -2120,6 +2211,7 @@ static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi,\n \t\t * during scans.\n \t\t */\n \t\tlist_add_tail(\u0026epi-\u003erdllink, \u0026ep-\u003erdllist);\n+\t\tep_set_notified(epi);\n \t\tep_pm_stay_awake(epi);\n \t}\n \treturn 1;\ndiff --git a/include/linux/eventfd.h b/include/linux/eventfd.h\nindex e32bee4345fb9..0f5d0f589d924 100644\n--- a/include/linux/eventfd.h\n+++ b/include/linux/eventfd.h\n@@ -40,6 +40,10 @@ int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_entry_t *w\n \t\t\t\t  __u64 *cnt);\n void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt);\n \n+#ifdef CONFIG_EVENTFD_OPT\n+bool is_eventfd_file(struct file *file);\n+#endif\n+\n static inline bool eventfd_signal_allowed(void)\n {\n \treturn !current-\u003ein_eventfd;\n@@ -82,6 +86,13 @@ static inline void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt)\n \n }\n \n+#ifdef CONFIG_EVENTFD_OPT\n+static inline bool is_eventfd_file(struct file *file)\n+{\n+\treturn false;\n+}\n+#endif\n+\n #endif\n \n static inline void eventfd_signal(struct eventfd_ctx *ctx)\ndiff --git a/init/Kconfig b/init/Kconfig\nindex 10f2013b53216..c734dfe3490c7 100644\n--- a/init/Kconfig\n+++ b/init/Kconfig\n@@ -1896,6 +1896,18 @@ config EVENTFD\n \n \t  If unsure, say Y.\n \n+config EVENTFD_OPT\n+\tbool \"Optimize eventfd/epoll interaction\" if EXPERT\n+\tdepends on EVENTFD \u0026\u0026 EPOLL\n+\tdefault n\n+\thelp\n+\t  Enables a lockless fast-path in ep_poll_callback for eventfd\n+\t  files, reducing ep-\u003elock contention under concurrent workloads.\n+\t  Full memory barriers prevent lost wakeups when eventfd updates\n+\t  race with epoll re-polling.\n+\n+\t  If unsure, say N.\n+\n config SHMEM\n \tbool \"Use full shmem filesystem\" if EXPERT\n \tdefault y\n"
}

2/1 2026/08/03 03:07 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "EVENTFD_OPT",
    "EVENTFD",
    "EPOLL"
  ],
  "FocusSymbols": [
    "eventfd_signal_mask",
    "eventfd_ctx_remove_wait_queue",
    "eventfd_read",
    "eventfd_write",
    "is_eventfd_file",
    "ep_done_scan",
    "__ep_eventpoll_poll",
    "ep_poll_callback",
    "ep_alloc_epitem",
    "ep_insert",
    "ep_modify",
    "ep_deliver_event"
  ],
  "Reasoning": "The patch introduces a functional optimization for the interaction between eventfd and epoll, adding a lockless fast-path in ep_poll_callback. This modifies core kernel logic in eventfd and eventpoll subsystems, making it highly relevant for fuzzing to ensure no race conditions or lost wakeups occur.",
  "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).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

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.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
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.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. 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 666625a99c5af801ce57a65f486a7f4321273e15
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 3 03:07:19 2026 +0000

    syz-cluster: applied patch under review

diff --git a/fs/eventfd.c b/fs/eventfd.c
index 9d33a02757d52..3d34cd652ac72 100644
--- a/fs/eventfd.c
+++ b/fs/eventfd.c
@@ -43,6 +43,29 @@ struct eventfd_ctx {
 	int id;
 };
 
+static inline void eventfd_wake_up_locked_poll(struct eventfd_ctx *ctx,
+					       __poll_t mask)
+{
+	lockdep_assert_held(&ctx->wqh.lock);
+
+	/* Protected by ctx->wqh.lock. */
+	if (!waitqueue_active(&ctx->wqh))
+		return;
+
+#ifdef CONFIG_EVENTFD_OPT
+	/*
+	 * Eventfd context of the two-variable protocol:
+	 *
+	 * W(B) updates ctx->count in the caller, then the callback performs
+	 * R(A) on epitem->notified.
+	 *
+	 * W(B) -> smp_mb() -> R(A)
+	 */
+	smp_mb();
+#endif
+	wake_up_locked_poll(&ctx->wqh, mask);
+}
+
 /**
  * eventfd_signal_mask - Increment the event counter
  * @ctx: [in] Pointer to the eventfd context.
@@ -72,8 +95,7 @@ void eventfd_signal_mask(struct eventfd_ctx *ctx, __poll_t mask)
 	current->in_eventfd = 1;
 	if (ctx->count < ULLONG_MAX)
 		ctx->count++;
-	if (waitqueue_active(&ctx->wqh))
-		wake_up_locked_poll(&ctx->wqh, EPOLLIN | mask);
+	eventfd_wake_up_locked_poll(ctx, EPOLLIN | mask);
 	current->in_eventfd = 0;
 	spin_unlock_irqrestore(&ctx->wqh.lock, flags);
 }
@@ -203,8 +225,8 @@ int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_entry_t *w
 	spin_lock_irqsave(&ctx->wqh.lock, flags);
 	eventfd_ctx_do_read(ctx, cnt);
 	__remove_wait_queue(&ctx->wqh, wait);
-	if (*cnt != 0 && waitqueue_active(&ctx->wqh))
-		wake_up_locked_poll(&ctx->wqh, EPOLLOUT);
+	if (*cnt != 0)
+		eventfd_wake_up_locked_poll(ctx, EPOLLOUT);
 	spin_unlock_irqrestore(&ctx->wqh.lock, flags);
 
 	return *cnt != 0 ? 0 : -EAGAIN;
@@ -234,8 +256,7 @@ static ssize_t eventfd_read(struct kiocb *iocb, struct iov_iter *to)
 	}
 	eventfd_ctx_do_read(ctx, &ucnt);
 	current->in_eventfd = 1;
-	if (waitqueue_active(&ctx->wqh))
-		wake_up_locked_poll(&ctx->wqh, EPOLLOUT);
+	eventfd_wake_up_locked_poll(ctx, EPOLLOUT);
 	current->in_eventfd = 0;
 	spin_unlock_irq(&ctx->wqh.lock);
 	if (unlikely(copy_to_iter(&ucnt, sizeof(ucnt), to) != sizeof(ucnt)))
@@ -270,8 +291,7 @@ static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t c
 	if (likely(res > 0)) {
 		ctx->count += ucnt;
 		current->in_eventfd = 1;
-		if (waitqueue_active(&ctx->wqh))
-			wake_up_locked_poll(&ctx->wqh, EPOLLIN);
+		eventfd_wake_up_locked_poll(ctx, EPOLLIN);
 		current->in_eventfd = 0;
 	}
 	spin_unlock_irq(&ctx->wqh.lock);
@@ -310,6 +330,15 @@ static const struct file_operations eventfd_fops = {
 	.llseek		= noop_llseek,
 };
 
+#ifdef CONFIG_EVENTFD_OPT
+bool is_eventfd_file(struct file *file)
+{
+	return file->f_op == &eventfd_fops;
+}
+EXPORT_SYMBOL_GPL(is_eventfd_file);
+
+#endif
+
 /**
  * eventfd_fget - Acquire a reference of an eventfd file descriptor.
  * @fd: [in] Eventfd file descriptor.
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index eed8cecd94e3a..09e9239ce2d5e 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -39,6 +39,9 @@
 #include <linux/rculist.h>
 #include <linux/capability.h>
 #include <linux/seqlock.h>
+#ifdef CONFIG_EVENTFD_OPT
+#include <linux/eventfd.h>
+#endif
 #include <net/busy_poll.h>
 
 /*
@@ -285,6 +288,14 @@ struct epitem {
 
 	/* The structure that describe the interested events and the source fd */
 	struct epoll_event event;
+
+#ifdef CONFIG_EVENTFD_OPT
+	/* Set after queueing, cleared before dequeueing from the ready path. */
+	bool notified;
+
+	/* True if the monitored file is an eventfd. */
+	bool is_eventfd;
+#endif
 };
 
 /*
@@ -620,6 +631,65 @@ static inline bool ep_events_available(struct eventpoll *ep)
 		read_seqcount_retry(&ep->seq, seq);
 }
 
+#ifdef CONFIG_EVENTFD_OPT
+/*
+ * Eventfd/epoll two-variable communication:
+ *
+ *   A = epi->notified
+ *   B = eventfd count/readiness
+ *
+ *   scanner context: W(A = false) -> smp_mb() -> dequeue -> R(B)
+ *   eventfd context: W(B)         -> smp_mb() -> R(A)
+ *
+ * Therefore R(B) == old and R(A) == true cannot both occur.
+ */
+static inline void ep_set_notified(struct epitem *epi)
+{
+	if (epi->is_eventfd)
+		WRITE_ONCE(epi->notified, true);
+}
+
+static inline void ep_eventfd_prepare_repoll(struct epitem *epi)
+{
+	if (!epi->is_eventfd)
+		return;
+
+	/*
+	 * Stop callbacks from skipping before the scanner drops its ready-list
+	 * ownership. Keep the full barrier between W(A = false) and R(B); the
+	 * actual dequeue may happen between the barrier and the readiness read.
+	 */
+	WRITE_ONCE(epi->notified, false);
+	/* Scanner context: W(A = false) -> smp_mb() -> R(B). */
+	smp_mb();
+}
+
+static inline bool ep_eventfd_callback_can_skip(struct epitem *epi,
+						__poll_t pollflags)
+{
+	if (!epi->is_eventfd)
+		return false;
+
+	if (READ_ONCE(epi->event.events) &
+	    (EPOLLEXCLUSIVE | EPOLLET | EPOLLONESHOT))
+		return false;
+
+	if (pollflags & POLLFREE)
+		return false;
+
+	/* Eventfd context: W(B) -> smp_mb() -> R(A). */
+	return READ_ONCE(epi->notified);
+}
+#else
+static inline void ep_set_notified(struct epitem *epi) { }
+static inline void ep_eventfd_prepare_repoll(struct epitem *epi) { }
+static inline bool ep_eventfd_callback_can_skip(struct epitem *epi,
+						__poll_t pollflags)
+{
+	return false;
+}
+#endif
+
 #ifdef CONFIG_NET_RX_BUSY_POLL
 /**
  * busy_loop_ep_timeout - check if busy poll has timed out. The timeout value
@@ -1007,6 +1077,7 @@ static void ep_done_scan(struct eventpoll *ep,
 			 * reverses the iteration order into FIFO.
 			 */
 			list_add(&epi->rdllink, &ep->rdllist);
+			ep_set_notified(epi);
 			ep_pm_stay_awake(epi);
 		}
 	}
@@ -1303,8 +1374,11 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep
 	mutex_lock_nested(&ep->mtx, depth);
 	ep_start_scan(ep, &scan_batch);
 	list_for_each_entry_safe(epi, tmp, &scan_batch, rdllink) {
+		/* Clear notified before a possible removal from txlist. */
+		ep_eventfd_prepare_repoll(epi);
 		if (ep_item_poll(epi, &pt, depth + 1)) {
 			res = EPOLLIN | EPOLLRDNORM;
+			ep_set_notified(epi);
 			break;
 		} else {
 			/*
@@ -1497,6 +1571,9 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
 	unsigned long flags;
 	int ewake = 0;
 
+	if (ep_eventfd_callback_can_skip(epi, pollflags))
+		return 1;
+
 	spin_lock_irqsave(&ep->lock, flags);
 
 	ep_set_busy_poll_napi_id(epi);
@@ -1529,11 +1606,13 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
 		if (!epi_on_ovflist(epi)) {
 			epi->ovflist_next = READ_ONCE(ep->ovflist);
 			WRITE_ONCE(ep->ovflist, epi);
+			ep_set_notified(epi);
 			ep_pm_stay_awake_rcu(epi);
 		}
 	} else if (!ep_is_linked(epi)) {
 		/* In the usual case, add event to ready list. */
 		list_add_tail(&epi->rdllink, &ep->rdllist);
+		ep_set_notified(epi);
 		ep_pm_stay_awake_rcu(epi);
 	}
 
@@ -1840,6 +1919,9 @@ static struct epitem *ep_alloc_epitem(struct eventpoll *ep,
 	epi->ffd = *tf;
 	epi->event = *event;
 	epi_clear_ovflist(epi);
+#ifdef CONFIG_EVENTFD_OPT
+	epi->is_eventfd = is_eventfd_file(tfile);
+#endif
 
 	return epi;
 }
@@ -1956,6 +2038,7 @@ static int ep_insert(struct ep_ctl_ctx *ctx, struct eventpoll *ep,
 
 	if (revents && !ep_is_linked(epi)) {
 		list_add_tail(&epi->rdllink, &ep->rdllist);
+		ep_set_notified(epi);
 		ep_pm_stay_awake(epi);
 
 		if (waitqueue_active(&ep->wq))
@@ -2031,6 +2114,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,
 		spin_lock_irq(&ep->lock);
 		if (!ep_is_linked(epi)) {
 			list_add_tail(&epi->rdllink, &ep->rdllist);
+			ep_set_notified(epi);
 			ep_pm_stay_awake(epi);
 
 			/* Notify waiting tasks that events are available */
@@ -2084,6 +2168,12 @@ static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi,
 		__pm_relax(ws);
 	}
 
+	/*
+	 * Clear notified while epi is still on txlist. A callback that
+	 * races with the following dequeue must take the slow path and
+	 * publish the event through ovflist.
+	 */
+	ep_eventfd_prepare_repoll(epi);
 	list_del_init(&epi->rdllink);
 
 	/*
@@ -2104,6 +2194,7 @@ static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi,
 		 * attempt.
 		 */
 		list_add(&epi->rdllink, scan_batch);
+		ep_set_notified(epi);
 		ep_pm_stay_awake(epi);
 		return -EFAULT;
 	}
@@ -2120,6 +2211,7 @@ static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi,
 		 * during scans.
 		 */
 		list_add_tail(&epi->rdllink, &ep->rdllist);
+		ep_set_notified(epi);
 		ep_pm_stay_awake(epi);
 	}
 	return 1;
diff --git a/include/linux/eventfd.h b/include/linux/eventfd.h
index e32bee4345fb9..0f5d0f589d924 100644
--- a/include/linux/eventfd.h
+++ b/include/linux/eventfd.h
@@ -40,6 +40,10 @@ int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_entry_t *w
 				  __u64 *cnt);
 void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt);
 
+#ifdef CONFIG_EVENTFD_OPT
+bool is_eventfd_file(struct file *file);
+#endif
+
 static inline bool eventfd_signal_allowed(void)
 {
 	return !current->in_eventfd;
@@ -82,6 +86,13 @@ static inline void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt)
 
 }
 
+#ifdef CONFIG_EVENTFD_OPT
+static inline bool is_eventfd_file(struct file *file)
+{
+	return false;
+}
+#endif
+
 #endif
 
 static inline void eventfd_signal(struct eventfd_ctx *ctx)
diff --git a/init/Kconfig b/init/Kconfig
index 10f2013b53216..c734dfe3490c7 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1896,6 +1896,18 @@ config EVENTFD
 
 	  If unsure, say Y.
 
+config EVENTFD_OPT
+	bool "Optimize eventfd/epoll interaction" if EXPERT
+	depends on EVENTFD && EPOLL
+	default n
+	help
+	  Enables a lockless fast-path in ep_poll_callback for eventfd
+	  files, reducing ep->lock contention under concurrent workloads.
+	  Full memory barriers prevent lost wakeups when eventfd updates
+	  race with epoll re-polling.
+
+	  If unsure, say N.
+
 config SHMEM
 	bool "Use full shmem filesystem" if EXPERT
 	default y

3/2 2026/08/03 03:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5950
							output: 207
							thoughts: 1654
						

Thoughts:
**Fuzzing Analysis: Eventfd/Epoll Optimization Patch**

Okay, so we've got a patch here that's introducing a new config, `CONFIG_EVENTFD_OPT`, which is all about optimizing the interplay between `eventfd` and `epoll`. Specifically, this involves a lockless fast-path in `ep_poll_callback` for `eventfd` files. This is definitely a functional change in core kernel logic, so fuzzing is a must.

First, let's look at what's been changed: `fs/eventfd.c` and `fs/eventpoll.c` are the main targets.  `eventfd.c` now includes an `eventfd_wake_up_locked_poll` (with an `smp_mb()` if the new config is enabled), and introduces `is_eventfd_file`. `eventpoll.c` gets some new functions too, like `ep_set_notified`, `ep_eventfd_prepare_repoll`, and `ep_eventfd_callback_can_skip`, which are all part of epoll's scanning and callback routines. There's also a declaration for `is_eventfd_file` in the `eventfd.h` header and the Kconfig has been updated.

Right, so we need to identify the key functions to focus on for fuzzing. Ideally, we want non-static, non-inlined entry points.  I started by listing everything that was touched. So, from the files, we have:

*   `eventfd_wake_up_locked_poll` (static inline)
*   `eventfd_signal_mask`
*   `eventfd_ctx_remove_wait_queue`
*   `eventfd_read`
*   `eventfd_write`
*   `ep_poll_callback`
*   `ep_done_scan`
*   `__ep_eventpoll_poll`
*   `ep_alloc_epitem`
*   `ep_insert`
*   `ep_modify`
*   `ep_deliver_event`
*   `is_eventfd_file`

Okay, a couple of those are static inline so they won't have unique symbol addresses. But `eventfd_signal_mask`, `eventfd_ctx_remove_wait_queue`, and `is_eventfd_file` are all good candidates since they are non-static. The file operations `eventfd_read` and `eventfd_write` are static, but they're still actual syscall implementations, so we need to include them. The remaining epoll-related functions, `ep_poll_callback`, `ep_insert`, `ep_modify`, and `ep_deliver_event` are static, but they aren't inlined, and are likely to be critical in triggering the optimization's logic. So that will be our list:

*   `eventfd_signal_mask`
*   `eventfd_ctx_remove_wait_queue`
*   `eventfd_read`
*   `eventfd_write`
*   `is_eventfd_file`
*   `ep_done_scan`
*   `__ep_eventpoll_poll`
*   `ep_poll_callback`
*   `ep_alloc_epitem`
*   `ep_insert`
*   `ep_modify`
*   `ep_deliver_event`

Now for the configuration options. The patch introduces `EVENTFD_OPT`, which depends on `EVENTFD` and `EPOLL`.  So, we'll need to enable all of those, with `EVENTFD_OPT` being the critical one for this fuzzing effort.

Looks like we have what we need, let's call set-results.



4/2 2026/08/03 03:07 tool set-results
0m Args:
{
  "EnableConfigs": [
    "EVENTFD_OPT",
    "EVENTFD",
    "EPOLL"
  ],
  "FocusSymbols": [
    "eventfd_signal_mask",
    "eventfd_ctx_remove_wait_queue",
    "eventfd_read",
    "eventfd_write",
    "is_eventfd_file",
    "ep_done_scan",
    "__ep_eventpoll_poll",
    "ep_poll_callback",
    "ep_alloc_epitem",
    "ep_insert",
    "ep_modify",
    "ep_deliver_event"
  ],
  "Reasoning": "The patch introduces a functional optimization for the interaction between eventfd and epoll, adding a lockless fast-path in ep_poll_callback. This modifies core kernel logic in eventfd and eventpoll subsystems, making it highly relevant for fuzzing to ensure no race conditions or lost wakeups occur.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "EVENTFD_OPT",
    "EVENTFD",
    "EPOLL"
  ],
  "FocusSymbols": [
    "eventfd_signal_mask",
    "eventfd_ctx_remove_wait_queue",
    "eventfd_read",
    "eventfd_write",
    "is_eventfd_file",
    "ep_done_scan",
    "__ep_eventpoll_poll",
    "ep_poll_callback",
    "ep_alloc_epitem",
    "ep_insert",
    "ep_modify",
    "ep_deliver_event"
  ],
  "Reasoning": "The patch introduces a functional optimization for the interaction between eventfd and epoll, adding a lockless fast-path in ep_poll_callback. This modifies core kernel logic in eventfd and eventpoll subsystems, making it highly relevant for fuzzing to ensure no race conditions or lost wakeups occur.",
  "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)