[PATCH v6 5/8] selftests: livepatch: Adapt atomic replace tests to provides/obsoletes
From: Yafang Shao <hidden>
Date: 2026-08-24 03:28:00
Subsystem:
kernel selftest framework, live patching, the rest · Maintainers:
Shuah Khan, Shuah Khan, Josh Poimboeuf, Jiri Kosina, Miroslav Benes, Petr Mladek, Linus Torvalds
The legacy "replace" field in struct klp_patch was replaced by the
provides/obsoletes mechanism. As a result, the atomic replace
selftests fail to build against kernels that only support
provides/obsoletes.
Adapt the selftests so that they build and run on both old and new
kernels:
- test_modules/Makefile detects whether the kernel header still
declares the "replace" field. If so, it passes KLP_HAS_REPLACE to
the modules via ccflags-y.
- test_klp_atomic_replace.c and other source files using .replace guard
their replace module parameter and .replace assignment with
#ifdef KLP_HAS_REPLACE. The #else branches are left as TODO
placeholders for the upcoming provides/obsoletes based tests.
- functions.sh adds detect_replace_attr(), which loads a temporary
livepatch and checks for the /sys/kernel/livepatch/<patch>/replace
attribute, setting HAS_REPLACE_ATTR accordingly.
- test-livepatch.sh and test-callbacks.sh run the tests that depend
on multiple livepatches coexisting or on the atomic replace
behavior ("multiple livepatches" and "atomic replace") only when
HAS_REPLACE_ATTR is set. These scenarios do not apply to the new
kernel, where provides=0 replaces any other livepatch by default.
The provides/obsoletes based selftests will be added later.
Assisted-by: Comagic:DeepSeek-V4-Flash
Signed-off-by: Yafang Shao <redacted>
---
tools/testing/selftests/livepatch/functions.sh | 15 +++++++++++++++
.../selftests/livepatch/test-callbacks.sh | 6 ++++++
.../selftests/livepatch/test-livepatch.sh | 6 ++++++
.../selftests/livepatch/test_modules/Makefile | 14 ++++++++++++++
.../test_modules/test_klp_atomic_replace.c | 16 ++++++++++++++++
.../test_modules/test_klp_callbacks_demo2.c | 16 ++++++++++++++++
.../livepatch/test_modules/test_klp_state.c | 7 +++++++
.../livepatch/test_modules/test_klp_state2.c | 7 +++++++
8 files changed, 87 insertions(+)
diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh
index 1eba371f9d4d..5e6ed57c3df2 100644
--- a/tools/testing/selftests/livepatch/functions.sh
+++ b/tools/testing/selftests/livepatch/functions.sh@@ -357,6 +357,21 @@ function does_sysfs_exist() { [[ -f "$SYSFS_KLP_DIR/$mod/$attr" ]] } +# detect_replace_attr() - detect whether the running kernel still exposes +# the legacy "replace" sysfs attribute and set HAS_REPLACE_ATTR +# accordingly. The atomic replace tests are only run when the attribute +# is present. The provides/obsoletes based tests will be added later. +function detect_replace_attr() { + HAS_REPLACE_ATTR=0 + + load_lp test_klp_livepatch + if does_sysfs_exist test_klp_livepatch "replace"; then + HAS_REPLACE_ATTR=1 + fi + disable_lp test_klp_livepatch + unload_lp test_klp_livepatch +} + # check_sysfs_rights(modname, rel_path, expected_rights) - check sysfs # path permissions # modname - livepatch module creating the sysfs interface
diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh
index 2a03deb26a12..e9b2dc6ea099 100755
--- a/tools/testing/selftests/livepatch/test-callbacks.sh
+++ b/tools/testing/selftests/livepatch/test-callbacks.sh@@ -11,6 +11,8 @@ MOD_TARGET_BUSY=test_klp_callbacks_busy setup_config +detect_replace_attr + # Test a combination of loading a kernel module and a livepatch that # patches a function in the first module. Load the target module
@@ -451,6 +453,7 @@ $MOD_TARGET_BUSY: busymod_work_func exit $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit" +if [[ "$HAS_REPLACE_ATTR" == "1" ]]; then # Test loading multiple livepatches. This test-case is mainly for comparing # with the next test-case. #
@@ -499,8 +502,10 @@ $MOD_LIVEPATCH: post_unpatch_callback: vmlinux livepatch: '$MOD_LIVEPATCH': unpatching complete % rmmod $MOD_LIVEPATCH2 % rmmod $MOD_LIVEPATCH" +fi +if [[ "$HAS_REPLACE_ATTR" == "1" ]]; then # Load multiple livepatches, but the second as an 'atomic-replace' # patch. When the latter loads, the original livepatch should be # disabled and *none* of its pre/post-unpatch callbacks executed. On
@@ -548,6 +553,7 @@ $MOD_LIVEPATCH2: post_unpatch_callback: vmlinux livepatch: '$MOD_LIVEPATCH2': unpatching complete % rmmod $MOD_LIVEPATCH2 % rmmod $MOD_LIVEPATCH" +fi exit 0
diff --git a/tools/testing/selftests/livepatch/test-livepatch.sh b/tools/testing/selftests/livepatch/test-livepatch.sh
index c44c5341a2f1..b3b7cd81e3d0 100755
--- a/tools/testing/selftests/livepatch/test-livepatch.sh
+++ b/tools/testing/selftests/livepatch/test-livepatch.sh@@ -13,6 +13,8 @@ MOD_TARGET_PATCH=test_klp_mod_patch setup_config +detect_replace_attr + # - load a livepatch that modifies the output from /proc/cmdline and # verify correct behavior
@@ -49,6 +51,7 @@ livepatch: '$MOD_LIVEPATCH1': unpatching complete % rmmod $MOD_LIVEPATCH1" +if [[ "$HAS_REPLACE_ATTR" == "1" ]]; then # - load a livepatch that modifies the output from /proc/cmdline and # verify correct behavior # - load another livepatch and verify that both livepatches are active
@@ -107,8 +110,10 @@ livepatch: '$MOD_LIVEPATCH1': starting unpatching transition livepatch: '$MOD_LIVEPATCH1': completing unpatching transition livepatch: '$MOD_LIVEPATCH1': unpatching complete % rmmod $MOD_LIVEPATCH1" +fi +if [[ "$HAS_REPLACE_ATTR" == "1" ]]; then # - load a livepatch that modifies the output from /proc/cmdline and # verify correct behavior # - load two additional livepatches and check the number of livepatch modules
@@ -196,6 +201,7 @@ livepatch: '$MOD_REPLACE': starting unpatching transition livepatch: '$MOD_REPLACE': completing unpatching transition livepatch: '$MOD_REPLACE': unpatching complete % rmmod $MOD_REPLACE" +fi # - load a target module that provides /proc/test_klp_mod_target with
diff --git a/tools/testing/selftests/livepatch/test_modules/Makefile b/tools/testing/selftests/livepatch/test_modules/Makefile
index a13d398585dc..a4fe469a6501 100644
--- a/tools/testing/selftests/livepatch/test_modules/Makefile
+++ b/tools/testing/selftests/livepatch/test_modules/Makefile@@ -16,6 +16,20 @@ obj-m += test_klp_atomic_replace.o \ test_klp_state3.o \ test_klp_syscall.o +# The legacy "replace" field was replaced by provides/obsoletes. When the +# kernel (KDIR) still supports "replace", pass KLP_HAS_REPLACE to the +# modules so that they can #ifdef the legacy "replace" code. Otherwise +# the modules are built with the provides/obsoletes based code (to be +# added later). +KLP_SRC := $(if $(wildcard $(srctree)/include/linux/livepatch.h), \ + $(srctree), $(KDIR)) +KLP_HAS_REPLACE := $(shell grep -q 'bool replace' \ + $(KLP_SRC)/include/linux/livepatch.h 2>/dev/null \ + && echo y) +ifeq ($(KLP_HAS_REPLACE),y) +ccflags-y += -DKLP_HAS_REPLACE +endif + # Ensure that KDIR exists, otherwise skip the compilation modules: ifneq ("$(wildcard $(KDIR))", "")
diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_atomic_replace.c b/tools/testing/selftests/livepatch/test_modules/test_klp_atomic_replace.c
index 5af7093ca00c..4b3ed17886e7 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_atomic_replace.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_atomic_replace.c@@ -7,9 +7,16 @@ #include <linux/kernel.h> #include <linux/livepatch.h> +#ifdef KLP_HAS_REPLACE static int replace; module_param(replace, int, 0644); MODULE_PARM_DESC(replace, "replace (default=0)"); +#else +/* + * TODO: Add provides/obsoletes module parameters for the + * provides/obsoletes based tests (to be added later). + */ +#endif #include <linux/seq_file.h> static int livepatch_meminfo_proc_show(struct seq_file *m, void *v)
@@ -36,12 +43,21 @@ static struct klp_object objs[] = { static struct klp_patch patch = { .mod = THIS_MODULE, .objs = objs, +#ifdef KLP_HAS_REPLACE /* set .replace in the init function below for demo purposes */ +#endif }; static int test_klp_atomic_replace_init(void) { +#ifdef KLP_HAS_REPLACE patch.replace = replace; +#else + /* + * TODO: Set provides/obsoletes from the module parameters + * for the provides/obsoletes based tests (to be added later). + */ +#endif return klp_enable_patch(&patch); }
diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_callbacks_demo2.c b/tools/testing/selftests/livepatch/test_modules/test_klp_callbacks_demo2.c
index 5417573e80af..135da6d4d882 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_callbacks_demo2.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_callbacks_demo2.c@@ -7,9 +7,16 @@ #include <linux/kernel.h> #include <linux/livepatch.h> +#ifdef KLP_HAS_REPLACE static int replace; module_param(replace, int, 0644); MODULE_PARM_DESC(replace, "replace (default=0)"); +#else +/* + * TODO: Add provides/obsoletes module parameters for the + * provides/obsoletes based tests (to be added later). + */ +#endif static const char *const module_state[] = { [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",
@@ -72,12 +79,21 @@ static struct klp_object objs[] = { static struct klp_patch patch = { .mod = THIS_MODULE, .objs = objs, +#ifdef KLP_HAS_REPLACE /* set .replace in the init function below for demo purposes */ +#endif }; static int test_klp_callbacks_demo2_init(void) { +#ifdef KLP_HAS_REPLACE patch.replace = replace; +#else + /* + * TODO: Set provides/obsoletes from the module parameters + * for the provides/obsoletes based tests (to be added later). + */ +#endif return klp_enable_patch(&patch); }
diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_state.c b/tools/testing/selftests/livepatch/test_modules/test_klp_state.c
index 57a4253acb01..3492472e5d07 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_state.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_state.c@@ -142,7 +142,14 @@ static struct klp_patch patch = { .mod = THIS_MODULE, .objs = objs, .states = states, +#ifdef KLP_HAS_REPLACE .replace = true, +#else +/* + * TODO: Add provides/obsoletes module parameters for the + * provides/obsoletes based tests (to be added later). + */ +#endif }; static int test_klp_callbacks_demo_init(void)
diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_state2.c b/tools/testing/selftests/livepatch/test_modules/test_klp_state2.c
index c978ea4d5e67..8160b565a659 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_state2.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_state2.c@@ -171,7 +171,14 @@ static struct klp_patch patch = { .mod = THIS_MODULE, .objs = objs, .states = states, +#ifdef KLP_HAS_REPLACE .replace = true, +#else +/* + * TODO: Add provides/obsoletes module parameters for the + * provides/obsoletes based tests (to be added later). + */ +#endif }; static int test_klp_callbacks_demo_init(void)
--
2.52.0