[PATCH v1 1/2] eventfd: luo: luo support for preserving eventfd

From: Chenghao Duan

Date: Thu Jun 25 2026 - 01:50:14 EST


This patch adds support for preserving eventfd file descriptors across
kexec live updates using the Live Update Orchestrator (LUO) framework.
Userspace applications using eventfd for event notification can now
maintain their state across kernel updates.

Preserved State:
The following properties of the eventfd are preserved across kexec:
- Counter Value: The current 64-bit counter value, including any pending
events that have been signaled but not yet consumed by readers.
- File Flags: The creation flags (EFD_SEMAPHORE, EFD_CLOEXEC, EFD_NONBLOCK)
are preserved.

Non-Preserved State:
- File Descriptor Number: The eventfd will be assigned a new fd number
in the target process after restore.
- Wait Queue State: Any processes blocked on read() operations will be
woken up and need to re-establish their blocking state.
- All other internal state is reset to default.

Changes:
- fs/eventfd.c: Add eventfd_luo_get_state() to safely read eventfd state
(count and flags), and eventfd_create() helper function.
- fs/eventfd_luo.c: New file implementing LUO file operations:
preserve, freeze, unpreserve, retrieve, and finish callbacks.
- include/linux/eventfd.h: Export new functions.
- include/linux/kho/abi/eventfd.h: Define the ABI contract with
eventfd_luo_ser structure for serialization.

Signed-off-by: Chenghao Duan <duanchenghao@xxxxxxxxxx>
---
fs/Makefile | 1 +
fs/eventfd.c | 40 +++++
fs/eventfd_luo.c | 250 ++++++++++++++++++++++++++++++++
include/linux/eventfd.h | 2 +
include/linux/kho/abi/eventfd.h | 39 +++++
kernel/liveupdate/Kconfig | 16 ++
6 files changed, 348 insertions(+)
create mode 100644 fs/eventfd_luo.c
create mode 100644 include/linux/kho/abi/eventfd.h

diff --git a/fs/Makefile b/fs/Makefile
index 89a8a9d207d1..36d568e6cfc7 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -27,6 +27,7 @@ obj-y += anon_inodes.o
obj-$(CONFIG_SIGNALFD) += signalfd.o
obj-$(CONFIG_TIMERFD) += timerfd.o
obj-$(CONFIG_EVENTFD) += eventfd.o
+obj-$(CONFIG_LIVEUPDATE_EVENTFD)+= eventfd_luo.o
obj-$(CONFIG_AIO) += aio.o
obj-$(CONFIG_FS_DAX) += dax.o
obj-$(CONFIG_FS_ENCRYPTION) += crypto/
diff --git a/fs/eventfd.c b/fs/eventfd.c
index 9d33a02757d5..9b76cf06135a 100644
--- a/fs/eventfd.c
+++ b/fs/eventfd.c
@@ -376,6 +376,40 @@ struct eventfd_ctx *eventfd_ctx_fileget(struct file *file)
}
EXPORT_SYMBOL_GPL(eventfd_ctx_fileget);

+/**
+ * eventfd_luo_get_state - Get eventfd state (count and flags) for LUO
+ * @file: Eventfd file
+ * @count: Output parameter for count value
+ * @flags: Output parameter for flags value
+ *
+ * This function is exported for use by LUO to safely read eventfd state.
+ * Since struct eventfd_ctx is defined in this file, we can access its
+ * members directly here. The function uses the wait queue lock to ensure
+ * atomic access to count.
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+int eventfd_luo_get_state(struct file *file, __u64 *count, unsigned int *flags)
+{
+ struct eventfd_ctx *ctx;
+ unsigned long irq_flags;
+
+ ctx = eventfd_ctx_fileget(file);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ /* Read count with lock (flags don't need lock) */
+ spin_lock_irqsave(&ctx->wqh.lock, irq_flags);
+ *count = ctx->count;
+ spin_unlock_irqrestore(&ctx->wqh.lock, irq_flags);
+
+ *flags = ctx->flags;
+
+ eventfd_ctx_put(ctx);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(eventfd_luo_get_state);
+
static int do_eventfd(unsigned int count, int flags)
{
struct eventfd_ctx *ctx __free(kfree) = NULL;
@@ -411,6 +445,12 @@ static int do_eventfd(unsigned int count, int flags)
return fd_publish(fdf);
}

+int eventfd_create(__u64 count, unsigned int flags)
+{
+ return do_eventfd(count, flags);
+}
+EXPORT_SYMBOL_GPL(eventfd_create);
+
SYSCALL_DEFINE2(eventfd2, unsigned int, count, int, flags)
{
return do_eventfd(count, flags);
diff --git a/fs/eventfd_luo.c b/fs/eventfd_luo.c
new file mode 100644
index 000000000000..781d90635c52
--- /dev/null
+++ b/fs/eventfd_luo.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 KylinSoft Corporation.
+ * Author: Chenghao Duan <duanchenghao@xxxxxxxxxx>
+ */
+
+/**
+ * DOC: Eventfd Preservation via LUO
+ *
+ * Overview
+ * ========
+ *
+ * Event file descriptors (eventfd) can be preserved over a kexec using the Live
+ * Update Orchestrator (LUO) file preservation. This allows userspace applications
+ * that use eventfd for event notification to maintain their state across kernel
+ * updates.
+ *
+ * Eventfd is a simple notification mechanism that uses a 64-bit counter for
+ * signaling events between userspace processes or between userspace and kernel.
+ * The preservation ensures that pending events and configuration are not lost
+ * during kexec.
+ *
+ * The preservation is not intended to be transparent. Only select properties of
+ * the eventfd are preserved. All others are reset to default. The preserved
+ * properties are described below.
+ *
+ * Preserved Properties
+ * ====================
+ *
+ * The following properties of the eventfd are preserved across kexec:
+ *
+ * Counter Value
+ * The current 64-bit counter value is preserved. This includes any pending
+ * events that have been signaled but not yet consumed by readers.
+ *
+ * File Flags
+ * The creation flags (EFD_SEMAPHORE, EFD_CLOEXEC, EFD_NONBLOCK) are preserved.
+ * These control the behavior of read/write operations and file descriptor
+ * inheritance.
+ *
+ * Non-Preserved Properties
+ * ========================
+ *
+ * All properties which are not preserved must be assumed to be reset to
+ * default. This section describes some of those properties which may be more of
+ * note.
+ *
+ * File Descriptor Number
+ * The file descriptor number itself is not preserved. After restore, the
+ * eventfd will be assigned a new file descriptor number in the target process.
+ *
+ * Wait Queue State
+ * Any processes currently blocked on read() operations will be woken up and
+ * need to re-establish their blocking state if desired.
+ *
+ * File Position
+ * Eventfd files don't have a traditional file position, but any internal
+ * state related to the file descriptor is reset.
+ */
+
+#include <linux/err.h>
+#include <linux/file.h>
+#include <linux/io.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/eventfd.h>
+#include <linux/liveupdate.h>
+#include <linux/module.h>
+#include <linux/eventfd.h>
+#include <linux/anon_inodes.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/kref.h>
+#include <linux/fdtable.h>
+
+static int eventfd_luo_preserve(struct liveupdate_file_op_args *args)
+{
+ struct eventfd_luo_ser *ser;
+ u64 count;
+ unsigned int flags;
+ int err = 0;
+
+ /* Get eventfd state safely */
+ err = eventfd_luo_get_state(args->file, &count, &flags);
+ if (err) {
+ pr_err("Failed to get eventfd state: %d\n", err);
+ return err;
+ }
+
+ ser = kho_alloc_preserve(sizeof(*ser));
+ if (IS_ERR(ser)) {
+ err = PTR_ERR(ser);
+ pr_err("Failed to allocate preserve memory: %d\n", err);
+ return err;
+ }
+
+ /* Save eventfd state */
+ ser->count = count;
+ ser->flags = flags;
+
+ pr_debug("Preserved eventfd: count=%llu, flags=0x%x\n",
+ ser->count, ser->flags);
+
+ /* Return physical address of serialization structure */
+ args->serialized_data = virt_to_phys(ser);
+
+ return 0;
+}
+
+static int eventfd_luo_freeze(struct liveupdate_file_op_args *args)
+{
+ struct eventfd_luo_ser *ser;
+ u64 count;
+ unsigned int flags;
+ int err;
+
+ if (WARN_ON_ONCE(!args->serialized_data))
+ return -EINVAL;
+
+ ser = phys_to_virt(args->serialized_data);
+
+ /* Get current state and update if changed */
+ err = eventfd_luo_get_state(args->file, &count, &flags);
+ if (err)
+ return err;
+
+ if (ser->count != count) {
+ pr_debug("WARNING: Count changed during preserve->freeze! old=%llu, new=%llu\n",
+ ser->count, count);
+ }
+
+ ser->count = count;
+
+ return 0;
+}
+
+static void eventfd_luo_unpreserve(struct liveupdate_file_op_args *args)
+{
+ struct eventfd_luo_ser *ser;
+
+ if (WARN_ON_ONCE(!args->serialized_data))
+ return;
+
+ ser = phys_to_virt(args->serialized_data);
+ kho_unpreserve_free(ser);
+}
+
+static int eventfd_luo_retrieve(struct liveupdate_file_op_args *args)
+{
+ struct eventfd_luo_ser *ser;
+ struct eventfd_ctx *ctx;
+ struct file *file = NULL;
+ int eventfd;
+
+ ser = phys_to_virt(args->serialized_data);
+ if (!ser)
+ return -EINVAL;
+
+ /* Create a new eventfd with the preserved count and flags */
+ eventfd = eventfd_create(ser->count, ser->flags);
+ if (eventfd < 0) {
+ pr_err("Failed to create eventfd: %d\n", eventfd);
+ return eventfd;
+ }
+
+ file = fget(eventfd);
+ if (!file) {
+ pr_err("Failed to get file from fd\n");
+ close_fd(eventfd);
+ return -EBADF;
+ }
+
+ close_fd(eventfd);
+
+ /* Verify the created file has correct internal state */
+ ctx = eventfd_ctx_fileget(file);
+ if (IS_ERR(ctx)) {
+ pr_err("Failed to get context from file\n");
+ fput(file);
+ return PTR_ERR(ctx);
+ }
+
+ eventfd_ctx_put(ctx);
+
+ args->file = file;
+ return 0;
+}
+
+static void eventfd_luo_finish(struct liveupdate_file_op_args *args)
+{
+ struct eventfd_luo_ser *ser;
+
+ if (args->retrieve_status)
+ return;
+
+ if (!args->serialized_data)
+ return;
+
+ ser = phys_to_virt(args->serialized_data);
+ if (!ser)
+ return;
+
+ kho_restore_free(ser);
+}
+
+static bool eventfd_luo_can_preserve(struct liveupdate_file_handler *handler,
+ struct file *file)
+{
+ struct eventfd_ctx *ctx;
+
+ if (!file->f_op)
+ return false;
+
+ /* Try to get eventfd context - this will fail if not an eventfd */
+ ctx = eventfd_ctx_fileget(file);
+ if (IS_ERR(ctx))
+ return false;
+
+ eventfd_ctx_put(ctx);
+ return true;
+}
+
+static const struct liveupdate_file_ops eventfd_luo_file_ops = {
+ .preserve = eventfd_luo_preserve,
+ .unpreserve = eventfd_luo_unpreserve,
+ .freeze = eventfd_luo_freeze,
+ .retrieve = eventfd_luo_retrieve,
+ .finish = eventfd_luo_finish,
+ .can_preserve = eventfd_luo_can_preserve,
+ .owner = THIS_MODULE,
+};
+
+static struct liveupdate_file_handler eventfd_luo_handler = {
+ .ops = &eventfd_luo_file_ops,
+ .compatible = EVENTFD_LUO_FH_COMPATIBLE,
+};
+
+static int __init eventfd_luo_init(void)
+{
+ int err = liveupdate_register_file_handler(&eventfd_luo_handler);
+
+ if (err && err != -EOPNOTSUPP) {
+ pr_err("Could not register eventfd LUO handler: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ return 0;
+}
+late_initcall(eventfd_luo_init);
diff --git a/include/linux/eventfd.h b/include/linux/eventfd.h
index e32bee4345fb..703e1a126c4d 100644
--- a/include/linux/eventfd.h
+++ b/include/linux/eventfd.h
@@ -35,6 +35,8 @@ void eventfd_ctx_put(struct eventfd_ctx *ctx);
struct file *eventfd_fget(int fd);
struct eventfd_ctx *eventfd_ctx_fdget(int fd);
struct eventfd_ctx *eventfd_ctx_fileget(struct file *file);
+int eventfd_luo_get_state(struct file *file, __u64 *count, unsigned int *flags);
+int eventfd_create(__u64 count, unsigned int flags);
void eventfd_signal_mask(struct eventfd_ctx *ctx, __poll_t mask);
int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_entry_t *wait,
__u64 *cnt);
diff --git a/include/linux/kho/abi/eventfd.h b/include/linux/kho/abi/eventfd.h
new file mode 100644
index 000000000000..148beac6bcc7
--- /dev/null
+++ b/include/linux/kho/abi/eventfd.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 KylinSoft Corporation.
+ * Author: Chenghao Duan <duanchenghao@xxxxxxxxxx>
+ */
+
+#ifndef _LINUX_KHO_ABI_EVENTFD_H
+#define _LINUX_KHO_ABI_EVENTFD_H
+
+#include <linux/types.h>
+
+/*
+ * Eventfd Live Update ABI
+ *
+ * This header defines the ABI for preserving eventfd state across kexec.
+ *
+ * The state is serialized into a packed structure `struct eventfd_luo_ser`
+ * which is handed over to the next kernel via the KHO mechanism.
+ *
+ */
+
+/**
+ * struct eventfd_luo_ser - Serialized state of an eventfd
+ * @count: The current counter value
+ * @flags: File flags (EFD_SEMAPHORE, EFD_CLOEXEC, EFD_NONBLOCK)
+ *
+ * This structure contains the minimal state needed to restore an eventfd
+ * after kexec. The count represents the current value of the event counter,
+ * and flags represent the file creation flags.
+ */
+struct eventfd_luo_ser {
+ __u64 count;
+ unsigned int flags;
+} __packed;
+
+/* The compatibility string for eventfd file handler */
+#define EVENTFD_LUO_FH_COMPATIBLE "eventfd-v1"
+
+#endif /* _LINUX_KHO_ABI_EVENTFD_H */
diff --git a/kernel/liveupdate/Kconfig b/kernel/liveupdate/Kconfig
index c13af38ba23a..1361b5733f41 100644
--- a/kernel/liveupdate/Kconfig
+++ b/kernel/liveupdate/Kconfig
@@ -86,4 +86,20 @@ config LIVEUPDATE_MEMFD

If unsure, say N.

+config LIVEUPDATE_EVENTFD
+ bool "Eventfd Live Update Orchestrator support"
+ depends on EVENTFD
+ depends on LIVEUPDATE
+ help
+ Enable Live Update Orchestrator support for eventfd file descriptors.
+ This allows eventfd files to be preserved and restored across kexec
+ operations, maintaining their counter values and flags.
+
+ Eventfd files are commonly used for event notification between
+ userspace processes or between userspace and kernel. With this
+ option enabled, eventfd state can be handed over to a new kernel
+ during live update operations.
+
+ If unsure, say N.
+
endmenu
--
2.25.1