[PATCH v10 7/9] selftests/livepatch: Add provides/obsoletes test scenarios
WARM1d
From: Yafang Shao <hidden>
Date: 2026-09-30 03:04:16
Subsystem:
kernel selftest framework, live patching, the rest · Maintainers:
Shuah Khan, Shuah Khan, Josh Poimboeuf, Jiri Kosina, Miroslav Benes, Petr Mladek, Linus Torvalds
Revision v10 of 3 in this series.
Revisions (3)
- v8 [diff vs current]
- v9 [diff vs current]
- v10 current
Now that the legacy "replace" field has been replaced by the provides/obsoletes mechanism, add test scenarios to verify the new behavior. The test modules gain provides/obsoletes module parameters, and test-provides-obsoletes.sh covers the new replace semantics: provides=0 replacement, same-provides replacement, obsoletes-based replacement and coexistence of different provides ids. These tests are skipped on kernels that still support the legacy "replace" attribute. Assisted-by: LLM Signed-off-by: Yafang Shao <redacted> --- tools/testing/selftests/livepatch/Makefile | 3 +- .../livepatch/test-provides-obsoletes.sh | 221 ++++++++++++++++++ .../test_modules/test_klp_callbacks_demo2.c | 12 +- .../test_modules/test_klp_cmdline_lp.c | 11 +- .../test_modules/test_klp_meminfo_lp.c | 22 +- .../livepatch/test_modules/test_klp_state.c | 5 +- .../livepatch/test_modules/test_klp_state2.c | 5 +- 7 files changed, 253 insertions(+), 26 deletions(-) create mode 100755 tools/testing/selftests/livepatch/test-provides-obsoletes.sh
diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile
index a080eb54a215..38f98594d883 100644
--- a/tools/testing/selftests/livepatch/Makefile
+++ b/tools/testing/selftests/livepatch/Makefile@@ -11,7 +11,8 @@ TEST_PROGS := \ test-ftrace.sh \ test-sysfs.sh \ test-syscall.sh \ - test-kprobe.sh + test-kprobe.sh \ + test-provides-obsoletes.sh TEST_FILES := settings
diff --git a/tools/testing/selftests/livepatch/test-provides-obsoletes.sh b/tools/testing/selftests/livepatch/test-provides-obsoletes.sh
new file mode 100755
index 000000000000..a1c0ebb9ed44
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test-provides-obsoletes.sh@@ -0,0 +1,221 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2026 Yafang Shao <laoar.shao@gmail.com> + +. $(dirname $0)/functions.sh + +MOD_MEMINFO=test_klp_meminfo_lp +MOD_CMDLINE=test_klp_cmdline_lp + +setup_config +detect_provides_attr + +# The provides/obsoletes based tests only run on kernels that no longer +# support the legacy "replace" attribute. +if [[ "$HAS_PROVIDES_ATTR" != "1" ]]; then + skip_exit "kernel still supports the legacy replace attribute" +fi + + +# - load a livepatch with provides=0 (cmdline), then another with +# provides=0 (meminfo). The second replaces the first (same provides). +# - unload the remaining livepatch + +start_test "provides 0 replaces provides 0" + +load_lp $MOD_CMDLINE provides=0 +load_lp $MOD_MEMINFO provides=0 + +check_sysfs_value "$MOD_MEMINFO" "enabled" "1" + +disable_lp $MOD_MEMINFO +unload_lp $MOD_MEMINFO +unload_lp $MOD_CMDLINE + +check_result "% insmod test_modules/$MOD_CMDLINE.ko provides=0 +livepatch: enabling patch '$MOD_CMDLINE' +livepatch: '$MOD_CMDLINE': initializing patching transition +livepatch: '$MOD_CMDLINE': starting patching transition +livepatch: '$MOD_CMDLINE': completing patching transition +livepatch: '$MOD_CMDLINE': patching complete +% insmod test_modules/$MOD_MEMINFO.ko provides=0 +livepatch: enabling patch '$MOD_MEMINFO' +livepatch: '$MOD_MEMINFO': initializing patching transition +livepatch: '$MOD_MEMINFO': starting patching transition +livepatch: '$MOD_MEMINFO': completing patching transition +livepatch: '$MOD_MEMINFO': patching complete +% echo 0 > $SYSFS_KLP_DIR/$MOD_MEMINFO/enabled +livepatch: '$MOD_MEMINFO': initializing unpatching transition +livepatch: '$MOD_MEMINFO': starting unpatching transition +livepatch: '$MOD_MEMINFO': completing unpatching transition +livepatch: '$MOD_MEMINFO': unpatching complete +% rmmod $MOD_MEMINFO +% rmmod $MOD_CMDLINE" + + +# - load a livepatch with provides=1 (cmdline), then another with +# provides=1 (meminfo). The second replaces the first (same provides). +# - unload the remaining livepatch + +start_test "same provides replaces" + +load_lp $MOD_CMDLINE provides=1 +load_lp $MOD_MEMINFO provides=1 + +check_sysfs_value "$MOD_MEMINFO" "enabled" "1" + +disable_lp $MOD_MEMINFO +unload_lp $MOD_MEMINFO +unload_lp $MOD_CMDLINE + +check_result "% insmod test_modules/$MOD_CMDLINE.ko provides=1 +livepatch: enabling patch '$MOD_CMDLINE' +livepatch: '$MOD_CMDLINE': initializing patching transition +livepatch: '$MOD_CMDLINE': starting patching transition +livepatch: '$MOD_CMDLINE': completing patching transition +livepatch: '$MOD_CMDLINE': patching complete +% insmod test_modules/$MOD_MEMINFO.ko provides=1 +livepatch: enabling patch '$MOD_MEMINFO' +livepatch: '$MOD_MEMINFO': initializing patching transition +livepatch: '$MOD_MEMINFO': starting patching transition +livepatch: '$MOD_MEMINFO': completing patching transition +livepatch: '$MOD_MEMINFO': patching complete +% echo 0 > $SYSFS_KLP_DIR/$MOD_MEMINFO/enabled +livepatch: '$MOD_MEMINFO': initializing unpatching transition +livepatch: '$MOD_MEMINFO': starting unpatching transition +livepatch: '$MOD_MEMINFO': completing unpatching transition +livepatch: '$MOD_MEMINFO': unpatching complete +% rmmod $MOD_MEMINFO +% rmmod $MOD_CMDLINE" + + +# - load a livepatch with provides=1 (cmdline), then another with +# provides=2 and obsoletes=[1] (meminfo). The second replaces the +# first (obsoletes contains the first patch's provides id). +# - verify the provides/obsoletes sysfs values +# - unload the remaining livepatch + +start_test "obsoletes replaces" + +load_lp $MOD_CMDLINE provides=1 +load_lp $MOD_MEMINFO provides=2 obsoletes=1 + +check_sysfs_value "$MOD_MEMINFO" "provides" "2" +check_sysfs_value "$MOD_MEMINFO" "obsoletes" "1" + +disable_lp $MOD_MEMINFO +unload_lp $MOD_MEMINFO +unload_lp $MOD_CMDLINE + +check_result "% insmod test_modules/$MOD_CMDLINE.ko provides=1 +livepatch: enabling patch '$MOD_CMDLINE' +livepatch: '$MOD_CMDLINE': initializing patching transition +livepatch: '$MOD_CMDLINE': starting patching transition +livepatch: '$MOD_CMDLINE': completing patching transition +livepatch: '$MOD_CMDLINE': patching complete +% insmod test_modules/$MOD_MEMINFO.ko provides=2 obsoletes=1 +livepatch: enabling patch '$MOD_MEMINFO' +livepatch: '$MOD_MEMINFO': initializing patching transition +livepatch: '$MOD_MEMINFO': starting patching transition +livepatch: '$MOD_MEMINFO': completing patching transition +livepatch: '$MOD_MEMINFO': patching complete +% echo 0 > $SYSFS_KLP_DIR/$MOD_MEMINFO/enabled +livepatch: '$MOD_MEMINFO': initializing unpatching transition +livepatch: '$MOD_MEMINFO': starting unpatching transition +livepatch: '$MOD_MEMINFO': completing unpatching transition +livepatch: '$MOD_MEMINFO': unpatching complete +% rmmod $MOD_MEMINFO +% rmmod $MOD_CMDLINE" + + +# - load two livepatches with different provides ids that modify +# different functions and verify that they coexist +# - in particular, verify that provides=0 does NOT replace a patch +# with a different provides id +# - unload both livepatches + +start_test "provides 0 coexists with provides 1" + +load_lp $MOD_CMDLINE provides=0 +load_lp $MOD_MEMINFO provides=1 + +grep 'live patched' /proc/cmdline > /dev/kmsg +grep 'live patched' /proc/meminfo > /dev/kmsg + +disable_lp $MOD_CMDLINE +unload_lp $MOD_CMDLINE +disable_lp $MOD_MEMINFO +unload_lp $MOD_MEMINFO + +check_result "% insmod test_modules/$MOD_CMDLINE.ko provides=0 +livepatch: enabling patch '$MOD_CMDLINE' +livepatch: '$MOD_CMDLINE': initializing patching transition +livepatch: '$MOD_CMDLINE': starting patching transition +livepatch: '$MOD_CMDLINE': completing patching transition +livepatch: '$MOD_CMDLINE': patching complete +% insmod test_modules/$MOD_MEMINFO.ko provides=1 +livepatch: enabling patch '$MOD_MEMINFO' +livepatch: '$MOD_MEMINFO': initializing patching transition +livepatch: '$MOD_MEMINFO': starting patching transition +livepatch: '$MOD_MEMINFO': completing patching transition +livepatch: '$MOD_MEMINFO': patching complete +$MOD_CMDLINE: this has been live patched +$MOD_MEMINFO: this has been live patched +% echo 0 > $SYSFS_KLP_DIR/$MOD_CMDLINE/enabled +livepatch: '$MOD_CMDLINE': initializing unpatching transition +livepatch: '$MOD_CMDLINE': starting unpatching transition +livepatch: '$MOD_CMDLINE': completing unpatching transition +livepatch: '$MOD_CMDLINE': unpatching complete +% rmmod $MOD_CMDLINE +% echo 0 > $SYSFS_KLP_DIR/$MOD_MEMINFO/enabled +livepatch: '$MOD_MEMINFO': initializing unpatching transition +livepatch: '$MOD_MEMINFO': starting unpatching transition +livepatch: '$MOD_MEMINFO': completing unpatching transition +livepatch: '$MOD_MEMINFO': unpatching complete +% rmmod $MOD_MEMINFO" + + +# - load a livepatch with provides=2 and obsoletes=[1] (cmdline), +# then another with provides=1 (meminfo). Although the second patch +# does not list provides=2 in its own obsoletes list, it still +# replaces the first one, because the obsoletes relationship is +# symmetric: the first patch's obsoletes list contains the second +# patch's provides id. +# - verify the provides sysfs value of the replacing patch +# - unload the remaining livepatch +# +# MOD_MEMINFO(provides=2, obsoletes=[1]) -- loaded first +# MOD_CMDLINE(provides=1) -- loaded second, replaces above + +start_test "symmetric obsoletes replaces" + +load_lp $MOD_MEMINFO provides=2 obsoletes=1 +load_lp $MOD_CMDLINE provides=1 + +check_sysfs_value "$MOD_CMDLINE" "provides" "1" + +disable_lp $MOD_CMDLINE +unload_lp $MOD_CMDLINE +unload_lp $MOD_MEMINFO + +check_result "% insmod test_modules/$MOD_MEMINFO.ko provides=2 obsoletes=1 +livepatch: enabling patch '$MOD_MEMINFO' +livepatch: '$MOD_MEMINFO': initializing patching transition +livepatch: '$MOD_MEMINFO': starting patching transition +livepatch: '$MOD_MEMINFO': completing patching transition +livepatch: '$MOD_MEMINFO': patching complete +% insmod test_modules/$MOD_CMDLINE.ko provides=1 +livepatch: enabling patch '$MOD_CMDLINE' +livepatch: '$MOD_CMDLINE': initializing patching transition +livepatch: '$MOD_CMDLINE': starting patching transition +livepatch: '$MOD_CMDLINE': completing patching transition +livepatch: '$MOD_CMDLINE': patching complete +% echo 0 > $SYSFS_KLP_DIR/$MOD_CMDLINE/enabled +livepatch: '$MOD_CMDLINE': initializing unpatching transition +livepatch: '$MOD_CMDLINE': starting unpatching transition +livepatch: '$MOD_CMDLINE': completing unpatching transition +livepatch: '$MOD_CMDLINE': unpatching complete +% rmmod $MOD_CMDLINE +% rmmod $MOD_MEMINFO" + +exit 0
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 6c46ce575f5c..93789470fe18 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@@ -8,10 +8,9 @@ #include <linux/livepatch.h> #ifdef CONFIG_KLP_HAS_PROVIDES -/* - * TODO: Add provides/obsoletes module parameters for the - * provides/obsoletes based tests (to be added later). - */ +static unsigned int provides; +module_param(provides, uint, 0444); +MODULE_PARM_DESC(provides, "provides id (default=0)"); #else static int replace; module_param(replace, int, 0644);
@@ -87,10 +86,7 @@ static struct klp_patch patch = { static int test_klp_callbacks_demo2_init(void) { #ifdef CONFIG_KLP_HAS_PROVIDES - /* - * TODO: Set provides/obsoletes from the module parameters - * for the provides/obsoletes based tests (to be added later). - */ + patch.provides = provides; #else patch.replace = replace; #endif
diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_cmdline_lp.c b/tools/testing/selftests/livepatch/test_modules/test_klp_cmdline_lp.c
index 88c6aed925fb..8a93f005a383 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_cmdline_lp.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_cmdline_lp.c@@ -6,8 +6,14 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/livepatch.h> - #include <linux/seq_file.h> + +#ifdef CONFIG_KLP_HAS_PROVIDES +static unsigned int provides; +module_param(provides, uint, 0444); +MODULE_PARM_DESC(provides, "provides id (default=0)"); +#endif + static int livepatch_cmdline_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%s: %s\n", THIS_MODULE->name,
@@ -36,6 +42,9 @@ static struct klp_patch patch = { static int test_klp_cmdline_lp_init(void) { +#ifdef CONFIG_KLP_HAS_PROVIDES + patch.provides = provides; +#endif return klp_enable_patch(&patch); }
diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_meminfo_lp.c b/tools/testing/selftests/livepatch/test_modules/test_klp_meminfo_lp.c
index 0c434a27672d..a3b433385148 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_meminfo_lp.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_meminfo_lp.c@@ -8,10 +8,15 @@ #include <linux/livepatch.h> #ifdef CONFIG_KLP_HAS_PROVIDES -/* - * TODO: Add provides/obsoletes module parameters for the - * provides/obsoletes based tests (to be added later). - */ +static unsigned int provides; +module_param(provides, uint, 0444); +MODULE_PARM_DESC(provides, "provides id (default=0)"); + +#define KLP_MAX_OBSOLETES 16 +static unsigned int obsoletes[KLP_MAX_OBSOLETES]; +static int nr_obsoletes; +module_param_array(obsoletes, uint, &nr_obsoletes, 0444); +MODULE_PARM_DESC(obsoletes, "obsoletes provides ids"); #else static int replace; module_param(replace, int, 0644);
@@ -51,10 +56,11 @@ static struct klp_patch patch = { static int test_klp_meminfo_lp_init(void) { #ifdef CONFIG_KLP_HAS_PROVIDES - /* - * TODO: Set provides/obsoletes from the module parameters - * for the provides/obsoletes based tests (to be added later). - */ + patch.provides = provides; + if (nr_obsoletes > 0) { + patch.obsoletes = obsoletes; + patch.nr_obsoletes = nr_obsoletes; + } #else patch.replace = replace; #endif
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 4d419a58cf65..f53eead9d55b 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_state.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_state.c@@ -143,10 +143,7 @@ static struct klp_patch patch = { .objs = objs, .states = states, #ifdef CONFIG_KLP_HAS_PROVIDES - /* - * TODO: Add provides/obsoletes module parameters for the - * provides/obsoletes based tests (to be added later). - */ + /* provides=0 by default, replaces all provides=0 patches */ #else .replace = true, #endif
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 4f70f0619967..5f07fe1be2b9 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_state2.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_state2.c@@ -172,10 +172,7 @@ static struct klp_patch patch = { .objs = objs, .states = states, #ifdef CONFIG_KLP_HAS_PROVIDES - /* - * TODO: Add provides/obsoletes module parameters for the - * provides/obsoletes based tests (to be added later). - */ + /* provides=0 by default, replaces all provides=0 patches */ #else .replace = true, #endif
--
2.52.0