mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
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>
This commit is contained in:
parent
cab056f2aa
commit
f653ff7af9
7 changed files with 203 additions and 1 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
158
lib/tests/liveupdate.c
Normal file
158
lib/tests/liveupdate.c
Normal file
|
|
@ -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");
|
||||
Loading…
Add table
Add a link
Reference in a new issue