]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
tests/liveupdate: add in-kernel liveupdate test
authorPasha Tatashin <pasha.tatashin@soleen.com>
Thu, 18 Dec 2025 15:57:52 +0000 (10:57 -0500)
committerAndrew Morton <akpm@linux-foundation.org>
Sun, 8 Feb 2026 08:13:33 +0000 (00:13 -0800)
Introduce an in-kernel test module to validate the core logic of the Live
Update Orchestrator's File-Lifecycle-Bound feature.  This provides a
low-level, controlled environment to test FLB registration and callback
invocation without requiring userspace interaction or actual kexec
reboots.

The test is enabled by the CONFIG_LIVEUPDATE_TEST Kconfig option.

Link: https://lkml.kernel.org/r/20251218155752.3045808-6-pasha.tatashin@soleen.com
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Cc: Alexander Graf <graf@amazon.com>
Cc: David Gow <davidgow@google.com>
Cc: David Matlack <dmatlack@google.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Kees Cook <kees@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Pratyush Yadav <pratyush@kernel.org>
Cc: Samiullah Khawaja <skhawaja@google.com>
Cc: Tamir Duberstein <tamird@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
MAINTAINERS
include/linux/kho/abi/luo.h
kernel/liveupdate/luo_file.c
kernel/liveupdate/luo_internal.h
lib/Kconfig.debug
lib/tests/Makefile
lib/tests/liveupdate.c [new file with mode: 0644]

index 92b377cd131bb93f64023205847511eb870b3bce..a2a4cfd19fad337bd6038c9e254380c56c799593 100644 (file)
@@ -14652,6 +14652,7 @@ F:      include/linux/liveupdate.h
 F:     include/linux/liveupdate/
 F:     include/uapi/linux/liveupdate.h
 F:     kernel/liveupdate/
+F:     lib/tests/liveupdate.c
 F:     mm/memfd_luo.c
 F:     tools/testing/selftests/liveupdate/
 
index a44010aafb5e8bda48bfca994359b21af90c2dd4..46750a0ddf88e487ccd61463dd2d4660b7be1ed4 100644 (file)
@@ -239,4 +239,9 @@ struct luo_flb_ser {
        u64 count;
 } __packed;
 
+/* Kernel Live Update Test ABI */
+#ifdef CONFIG_LIVEUPDATE_TEST
+#define LIVEUPDATE_TEST_FLB_COMPATIBLE(i)      "liveupdate-test-flb-v" #i
+#endif
+
 #endif /* _LINUX_KHO_ABI_LUO_H */
index cade273c50c9c3e45ed9dd9055b4128bd832f322..35d2a8b1a0df24e4a04eeaab581e6137d9312f80 100644 (file)
@@ -864,6 +864,8 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
        list_add_tail(&ACCESS_PRIVATE(fh, list), &luo_file_handler_list);
        luo_session_resume();
 
+       liveupdate_test_register(fh);
+
        return 0;
 
 err_resume:
@@ -895,8 +897,10 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
        if (!liveupdate_enabled())
                return -EOPNOTSUPP;
 
+       liveupdate_test_unregister(fh);
+
        if (!luo_session_quiesce())
-               return -EBUSY;
+               goto err_register;
 
        if (!list_empty(&ACCESS_PRIVATE(fh, flb_list)))
                goto err_resume;
@@ -909,5 +913,7 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
 
 err_resume:
        luo_session_resume();
+err_register:
+       liveupdate_test_register(fh);
        return err;
 }
index 99db13d9953078988ce54ada2a98f1f4f6b85471..8083d8739b093daa5406dfbcef70f49bb816c093 100644 (file)
@@ -107,4 +107,12 @@ int __init luo_flb_setup_outgoing(void *fdt);
 int __init luo_flb_setup_incoming(void *fdt);
 void luo_flb_serialize(void);
 
+#ifdef CONFIG_LIVEUPDATE_TEST
+void liveupdate_test_register(struct liveupdate_file_handler *fh);
+void liveupdate_test_unregister(struct liveupdate_file_handler *fh);
+#else
+static inline void liveupdate_test_register(struct liveupdate_file_handler *fh) { }
+static inline void liveupdate_test_unregister(struct liveupdate_file_handler *fh) { }
+#endif
+
 #endif /* _LINUX_LUO_INTERNAL_H */
index 234b73f9baf7c7e862b66b6a3fe86d3fac2ab408..ef201f1cc4980fa1fc8af249f09d8378493ea749 100644 (file)
@@ -2825,6 +2825,29 @@ config LINEAR_RANGES_TEST
 
          If unsure, say N.
 
+config LIVEUPDATE_TEST
+       bool "Live Update Kernel Test"
+       default n
+       depends on LIVEUPDATE
+       help
+         Enable a built-in kernel test module for the Live Update
+         Orchestrator.
+
+         This module validates the File-Lifecycle-Bound subsystem by
+         registering a set of mock FLB objects with any real file handlers
+         that support live update (such as the memfd handler).
+
+         When live update operations are performed, this test module will
+         output messages to the kernel log (dmesg), confirming that its
+         registration and various callback functions (preserve, retrieve,
+         finish, etc.) are being invoked correctly.
+
+         This is a debugging and regression testing tool for developers
+         working on the Live Update subsystem. It should not be enabled in
+         production kernels.
+
+         If unsure, say N
+
 config CMDLINE_KUNIT_TEST
        tristate "KUnit test for cmdline API" if !KUNIT_ALL_TESTS
        depends on KUNIT
index f740b0a26750e3f38e56271a7281f160cb3e5c7b..436b7b7a65f040a7375ce47e7a650aeda93e98cd 100644 (file)
@@ -30,6 +30,7 @@ obj-$(CONFIG_LIST_PRIVATE_KUNIT_TEST) += list-private-test.o
 obj-$(CONFIG_KFIFO_KUNIT_TEST) += kfifo_kunit.o
 obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o
 obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
+obj-$(CONFIG_LIVEUPDATE_TEST) += liveupdate.o
 
 CFLAGS_longest_symbol_kunit.o += $(call cc-disable-warning, missing-prototypes)
 obj-$(CONFIG_LONGEST_SYM_KUNIT_TEST) += longest_symbol_kunit.o
diff --git a/lib/tests/liveupdate.c b/lib/tests/liveupdate.c
new file mode 100644 (file)
index 0000000..496d6ef
--- /dev/null
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2025, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME " test: " fmt
+
+#include <linux/cleanup.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/liveupdate.h>
+#include <linux/module.h>
+#include "../../kernel/liveupdate/luo_internal.h"
+
+static const struct liveupdate_flb_ops test_flb_ops;
+#define DEFINE_TEST_FLB(i) {                                           \
+       .ops = &test_flb_ops,                                           \
+       .compatible = LIVEUPDATE_TEST_FLB_COMPATIBLE(i),                \
+}
+
+/* Number of Test FLBs to register with every file handler */
+#define TEST_NFLBS 3
+static struct liveupdate_flb test_flbs[TEST_NFLBS] = {
+       DEFINE_TEST_FLB(0),
+       DEFINE_TEST_FLB(1),
+       DEFINE_TEST_FLB(2),
+};
+
+#define TEST_FLB_MAGIC_BASE 0xFEEDF00DCAFEBEE0ULL
+
+static int test_flb_preserve(struct liveupdate_flb_op_args *argp)
+{
+       ptrdiff_t index = argp->flb - test_flbs;
+
+       pr_info("%s: preserve was triggered\n", argp->flb->compatible);
+       argp->data = TEST_FLB_MAGIC_BASE + index;
+
+       return 0;
+}
+
+static void test_flb_unpreserve(struct liveupdate_flb_op_args *argp)
+{
+       pr_info("%s: unpreserve was triggered\n", argp->flb->compatible);
+}
+
+static int test_flb_retrieve(struct liveupdate_flb_op_args *argp)
+{
+       ptrdiff_t index = argp->flb - test_flbs;
+       u64 expected_data = TEST_FLB_MAGIC_BASE + index;
+
+       if (argp->data == expected_data) {
+               pr_info("%s: found flb data from the previous boot\n",
+                       argp->flb->compatible);
+               argp->obj = (void *)argp->data;
+       } else {
+               pr_err("%s: ERROR - incorrect data handle: %llx, expected %llx\n",
+                      argp->flb->compatible, argp->data, expected_data);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void test_flb_finish(struct liveupdate_flb_op_args *argp)
+{
+       ptrdiff_t index = argp->flb - test_flbs;
+       void *expected_obj = (void *)(TEST_FLB_MAGIC_BASE + index);
+
+       if (argp->obj == expected_obj) {
+               pr_info("%s: finish was triggered\n", argp->flb->compatible);
+       } else {
+               pr_err("%s: ERROR - finish called with invalid object\n",
+                      argp->flb->compatible);
+       }
+}
+
+static const struct liveupdate_flb_ops test_flb_ops = {
+       .preserve       = test_flb_preserve,
+       .unpreserve     = test_flb_unpreserve,
+       .retrieve       = test_flb_retrieve,
+       .finish         = test_flb_finish,
+       .owner          = THIS_MODULE,
+};
+
+static void liveupdate_test_init(void)
+{
+       static DEFINE_MUTEX(init_lock);
+       static bool initialized;
+       int i;
+
+       guard(mutex)(&init_lock);
+
+       if (initialized)
+               return;
+
+       for (i = 0; i < TEST_NFLBS; i++) {
+               struct liveupdate_flb *flb = &test_flbs[i];
+               void *obj;
+               int err;
+
+               err = liveupdate_flb_get_incoming(flb, &obj);
+               if (err && err != -ENODATA && err != -ENOENT) {
+                       pr_err("liveupdate_flb_get_incoming for %s failed: %pe\n",
+                              flb->compatible, ERR_PTR(err));
+               }
+       }
+       initialized = true;
+}
+
+void liveupdate_test_register(struct liveupdate_file_handler *fh)
+{
+       int err, i;
+
+       liveupdate_test_init();
+
+       for (i = 0; i < TEST_NFLBS; i++) {
+               struct liveupdate_flb *flb = &test_flbs[i];
+
+               err = liveupdate_register_flb(fh, flb);
+               if (err) {
+                       pr_err("Failed to register %s %pe\n",
+                              flb->compatible, ERR_PTR(err));
+               }
+       }
+
+       err = liveupdate_register_flb(fh, &test_flbs[0]);
+       if (!err || err != -EEXIST) {
+               pr_err("Failed: %s should be already registered, but got err: %pe\n",
+                      test_flbs[0].compatible, ERR_PTR(err));
+       }
+
+       pr_info("Registered %d FLBs with file handler: [%s]\n",
+               TEST_NFLBS, fh->compatible);
+}
+
+void liveupdate_test_unregister(struct liveupdate_file_handler *fh)
+{
+       int err, i;
+
+       for (i = 0; i < TEST_NFLBS; i++) {
+               struct liveupdate_flb *flb = &test_flbs[i];
+
+               err = liveupdate_unregister_flb(fh, flb);
+               if (err) {
+                       pr_err("Failed to unregister %s %pe\n",
+                              flb->compatible, ERR_PTR(err));
+               }
+       }
+
+       pr_info("Unregistered %d FLBs from file handler: [%s]\n",
+               TEST_NFLBS, fh->compatible);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pasha Tatashin <pasha.tatashin@soleen.com>");
+MODULE_DESCRIPTION("In-kernel test for LUO mechanism");