Skip to content

Commit f653ff7

Browse files
soleenakpm00
authored andcommitted
tests/liveupdate: add in-kernel liveupdate test
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>
1 parent cab056f commit f653ff7

7 files changed

Lines changed: 203 additions & 1 deletion

File tree

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14652,6 +14652,7 @@ F: include/linux/liveupdate.h
1465214652
F: include/linux/liveupdate/
1465314653
F: include/uapi/linux/liveupdate.h
1465414654
F: kernel/liveupdate/
14655+
F: lib/tests/liveupdate.c
1465514656
F: mm/memfd_luo.c
1465614657
F: tools/testing/selftests/liveupdate/
1465714658

include/linux/kho/abi/luo.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,9 @@ struct luo_flb_ser {
239239
u64 count;
240240
} __packed;
241241

242+
/* Kernel Live Update Test ABI */
243+
#ifdef CONFIG_LIVEUPDATE_TEST
244+
#define LIVEUPDATE_TEST_FLB_COMPATIBLE(i) "liveupdate-test-flb-v" #i
245+
#endif
246+
242247
#endif /* _LINUX_KHO_ABI_LUO_H */

kernel/liveupdate/luo_file.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,8 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
864864
list_add_tail(&ACCESS_PRIVATE(fh, list), &luo_file_handler_list);
865865
luo_session_resume();
866866

867+
liveupdate_test_register(fh);
868+
867869
return 0;
868870

869871
err_resume:
@@ -895,8 +897,10 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
895897
if (!liveupdate_enabled())
896898
return -EOPNOTSUPP;
897899

900+
liveupdate_test_unregister(fh);
901+
898902
if (!luo_session_quiesce())
899-
return -EBUSY;
903+
goto err_register;
900904

901905
if (!list_empty(&ACCESS_PRIVATE(fh, flb_list)))
902906
goto err_resume;
@@ -909,5 +913,7 @@ int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
909913

910914
err_resume:
911915
luo_session_resume();
916+
err_register:
917+
liveupdate_test_register(fh);
912918
return err;
913919
}

kernel/liveupdate/luo_internal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,12 @@ int __init luo_flb_setup_outgoing(void *fdt);
107107
int __init luo_flb_setup_incoming(void *fdt);
108108
void luo_flb_serialize(void);
109109

110+
#ifdef CONFIG_LIVEUPDATE_TEST
111+
void liveupdate_test_register(struct liveupdate_file_handler *fh);
112+
void liveupdate_test_unregister(struct liveupdate_file_handler *fh);
113+
#else
114+
static inline void liveupdate_test_register(struct liveupdate_file_handler *fh) { }
115+
static inline void liveupdate_test_unregister(struct liveupdate_file_handler *fh) { }
116+
#endif
117+
110118
#endif /* _LINUX_LUO_INTERNAL_H */

lib/Kconfig.debug

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2825,6 +2825,29 @@ config LINEAR_RANGES_TEST
28252825

28262826
If unsure, say N.
28272827

2828+
config LIVEUPDATE_TEST
2829+
bool "Live Update Kernel Test"
2830+
default n
2831+
depends on LIVEUPDATE
2832+
help
2833+
Enable a built-in kernel test module for the Live Update
2834+
Orchestrator.
2835+
2836+
This module validates the File-Lifecycle-Bound subsystem by
2837+
registering a set of mock FLB objects with any real file handlers
2838+
that support live update (such as the memfd handler).
2839+
2840+
When live update operations are performed, this test module will
2841+
output messages to the kernel log (dmesg), confirming that its
2842+
registration and various callback functions (preserve, retrieve,
2843+
finish, etc.) are being invoked correctly.
2844+
2845+
This is a debugging and regression testing tool for developers
2846+
working on the Live Update subsystem. It should not be enabled in
2847+
production kernels.
2848+
2849+
If unsure, say N
2850+
28282851
config CMDLINE_KUNIT_TEST
28292852
tristate "KUnit test for cmdline API" if !KUNIT_ALL_TESTS
28302853
depends on KUNIT

lib/tests/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ obj-$(CONFIG_LIST_PRIVATE_KUNIT_TEST) += list-private-test.o
3030
obj-$(CONFIG_KFIFO_KUNIT_TEST) += kfifo_kunit.o
3131
obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o
3232
obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
33+
obj-$(CONFIG_LIVEUPDATE_TEST) += liveupdate.o
3334

3435
CFLAGS_longest_symbol_kunit.o += $(call cc-disable-warning, missing-prototypes)
3536
obj-$(CONFIG_LONGEST_SYM_KUNIT_TEST) += longest_symbol_kunit.o

lib/tests/liveupdate.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/*
4+
* Copyright (c) 2025, Google LLC.
5+
* Pasha Tatashin <pasha.tatashin@soleen.com>
6+
*/
7+
8+
#define pr_fmt(fmt) KBUILD_MODNAME " test: " fmt
9+
10+
#include <linux/cleanup.h>
11+
#include <linux/errno.h>
12+
#include <linux/init.h>
13+
#include <linux/liveupdate.h>
14+
#include <linux/module.h>
15+
#include "../../kernel/liveupdate/luo_internal.h"
16+
17+
static const struct liveupdate_flb_ops test_flb_ops;
18+
#define DEFINE_TEST_FLB(i) { \
19+
.ops = &test_flb_ops, \
20+
.compatible = LIVEUPDATE_TEST_FLB_COMPATIBLE(i), \
21+
}
22+
23+
/* Number of Test FLBs to register with every file handler */
24+
#define TEST_NFLBS 3
25+
static struct liveupdate_flb test_flbs[TEST_NFLBS] = {
26+
DEFINE_TEST_FLB(0),
27+
DEFINE_TEST_FLB(1),
28+
DEFINE_TEST_FLB(2),
29+
};
30+
31+
#define TEST_FLB_MAGIC_BASE 0xFEEDF00DCAFEBEE0ULL
32+
33+
static int test_flb_preserve(struct liveupdate_flb_op_args *argp)
34+
{
35+
ptrdiff_t index = argp->flb - test_flbs;
36+
37+
pr_info("%s: preserve was triggered\n", argp->flb->compatible);
38+
argp->data = TEST_FLB_MAGIC_BASE + index;
39+
40+
return 0;
41+
}
42+
43+
static void test_flb_unpreserve(struct liveupdate_flb_op_args *argp)
44+
{
45+
pr_info("%s: unpreserve was triggered\n", argp->flb->compatible);
46+
}
47+
48+
static int test_flb_retrieve(struct liveupdate_flb_op_args *argp)
49+
{
50+
ptrdiff_t index = argp->flb - test_flbs;
51+
u64 expected_data = TEST_FLB_MAGIC_BASE + index;
52+
53+
if (argp->data == expected_data) {
54+
pr_info("%s: found flb data from the previous boot\n",
55+
argp->flb->compatible);
56+
argp->obj = (void *)argp->data;
57+
} else {
58+
pr_err("%s: ERROR - incorrect data handle: %llx, expected %llx\n",
59+
argp->flb->compatible, argp->data, expected_data);
60+
return -EINVAL;
61+
}
62+
63+
return 0;
64+
}
65+
66+
static void test_flb_finish(struct liveupdate_flb_op_args *argp)
67+
{
68+
ptrdiff_t index = argp->flb - test_flbs;
69+
void *expected_obj = (void *)(TEST_FLB_MAGIC_BASE + index);
70+
71+
if (argp->obj == expected_obj) {
72+
pr_info("%s: finish was triggered\n", argp->flb->compatible);
73+
} else {
74+
pr_err("%s: ERROR - finish called with invalid object\n",
75+
argp->flb->compatible);
76+
}
77+
}
78+
79+
static const struct liveupdate_flb_ops test_flb_ops = {
80+
.preserve = test_flb_preserve,
81+
.unpreserve = test_flb_unpreserve,
82+
.retrieve = test_flb_retrieve,
83+
.finish = test_flb_finish,
84+
.owner = THIS_MODULE,
85+
};
86+
87+
static void liveupdate_test_init(void)
88+
{
89+
static DEFINE_MUTEX(init_lock);
90+
static bool initialized;
91+
int i;
92+
93+
guard(mutex)(&init_lock);
94+
95+
if (initialized)
96+
return;
97+
98+
for (i = 0; i < TEST_NFLBS; i++) {
99+
struct liveupdate_flb *flb = &test_flbs[i];
100+
void *obj;
101+
int err;
102+
103+
err = liveupdate_flb_get_incoming(flb, &obj);
104+
if (err && err != -ENODATA && err != -ENOENT) {
105+
pr_err("liveupdate_flb_get_incoming for %s failed: %pe\n",
106+
flb->compatible, ERR_PTR(err));
107+
}
108+
}
109+
initialized = true;
110+
}
111+
112+
void liveupdate_test_register(struct liveupdate_file_handler *fh)
113+
{
114+
int err, i;
115+
116+
liveupdate_test_init();
117+
118+
for (i = 0; i < TEST_NFLBS; i++) {
119+
struct liveupdate_flb *flb = &test_flbs[i];
120+
121+
err = liveupdate_register_flb(fh, flb);
122+
if (err) {
123+
pr_err("Failed to register %s %pe\n",
124+
flb->compatible, ERR_PTR(err));
125+
}
126+
}
127+
128+
err = liveupdate_register_flb(fh, &test_flbs[0]);
129+
if (!err || err != -EEXIST) {
130+
pr_err("Failed: %s should be already registered, but got err: %pe\n",
131+
test_flbs[0].compatible, ERR_PTR(err));
132+
}
133+
134+
pr_info("Registered %d FLBs with file handler: [%s]\n",
135+
TEST_NFLBS, fh->compatible);
136+
}
137+
138+
void liveupdate_test_unregister(struct liveupdate_file_handler *fh)
139+
{
140+
int err, i;
141+
142+
for (i = 0; i < TEST_NFLBS; i++) {
143+
struct liveupdate_flb *flb = &test_flbs[i];
144+
145+
err = liveupdate_unregister_flb(fh, flb);
146+
if (err) {
147+
pr_err("Failed to unregister %s %pe\n",
148+
flb->compatible, ERR_PTR(err));
149+
}
150+
}
151+
152+
pr_info("Unregistered %d FLBs from file handler: [%s]\n",
153+
TEST_NFLBS, fh->compatible);
154+
}
155+
156+
MODULE_LICENSE("GPL");
157+
MODULE_AUTHOR("Pasha Tatashin <pasha.tatashin@soleen.com>");
158+
MODULE_DESCRIPTION("In-kernel test for LUO mechanism");

0 commit comments

Comments
 (0)