Thread (21 messages) flat view 21 messages, 5 authors, 14h ago

Re: [PATCH v9 2/9] selftests/livepatch: Adapt atomic replace tests to provides/obsoletes

From: Miroslav Benes <mbenes@suse.cz>
Date: 2026-09-25 08:54:55

quoted hunk ↗ jump to hunk
The legacy "replace" field in struct klp_patch will be 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.  On kernels without the legacy "replace" support, the
replace-related test cases are skipped with a SKIP message instead
of being run.

The provides/obsoletes based selftests will be added later after the
provides/obsoletes are substituted.

Suggested-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Yafang Shao <redacted>
Acked-by: Song Liu <song@kernel.org>
---
 .../testing/selftests/livepatch/functions.sh  |  30 ++++++
 .../selftests/livepatch/test-callbacks.sh     |  43 +++++---
 .../selftests/livepatch/test-livepatch.sh     | 102 ++++++++++--------
 .../test_modules/test_klp_callbacks_demo2.c   |  16 +++
 .../test_modules/test_klp_meminfo_lp.c        |  16 +++
 .../livepatch/test_modules/test_klp_state.c   |   7 ++
 .../livepatch/test_modules/test_klp_state2.c  |   7 ++
 7 files changed, 162 insertions(+), 59 deletions(-)
diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh
index a65b7b1ac8ad..1d5d8ee890a6 100644
--- a/tools/testing/selftests/livepatch/functions.sh
+++ b/tools/testing/selftests/livepatch/functions.sh
@@ -328,6 +328,15 @@ function start_test {
 	log "===== TEST: $test ====="
 }
 
+function skip_test {
+	local test="$1"
+	local reason="$2"
+
+	echo -n "TEST: $test ... "
+	echo "SKIP ($reason)"
+	log "===== TEST: $test SKIPPED: $reason ====="
+}
We also have skip() in functions.sh which exits a test script
immediately (and is used for detecting necessary prerequisites for
testing). skip_test() is used in conditionals and just prints useful
output. I think that we need a better naming which would distinguish
between the two. I would not mind renaming skip() to skip_exit() and
using skip() for the above but I am not good in naming.
quoted hunk ↗ jump to hunk
 # check_result() - verify dmesg output
 #	TODO - better filter, out of order msgs, etc?
 function check_result {
@@ -365,6 +374,27 @@ function does_sysfs_exist() {
 	[[ -f "$SYSFS_KLP_DIR/$mod/$attr" ]]
 }
 
+# detect_provides_attr() - detect whether the running kernel supports the
+# livepatch "provides" attribute and set HAS_PROVIDES_ATTR accordingly.
+# The provides/obsoletes based tests are only run when HAS_PROVIDES_ATTR
+# is set.
+function detect_provides_attr() {
+	HAS_PROVIDES_ATTR=0
+
+	if [[ -r /proc/config.gz ]] &&
+	   zgrep -q "CONFIG_KLP_HAS_PROVIDES=y" /proc/config.gz 2>/dev/null; then
+		HAS_PROVIDES_ATTR=1
+		return 0
+	fi
+
+	load_lp test_klp_cmdline_lp
+	if does_sysfs_exist test_klp_cmdline_lp "provides"; then
+		HAS_PROVIDES_ATTR=1
+	fi
+	disable_lp test_klp_cmdline_lp
+	unload_lp test_klp_cmdline_lp
+}
Why is there does_sysfs_exist() fallback? I mean, we use that currently
for exactly this purpose. My understanding is that
CONFIG_KLP_HAS_PROVIDES is also used elsewhere in the code so using it
here makes some sense, but do we need both?

Also, would it be a good idea to move CONFIG_KLP_HAS_PROVIDES definition
to this patch with "def_bool n" and just change it later when it is
actually implemented? It might make the whole thing more consistent.
quoted hunk ↗ jump to hunk
 # 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..346248f3488b 100755
--- a/tools/testing/selftests/livepatch/test-callbacks.sh
+++ b/tools/testing/selftests/livepatch/test-callbacks.sh
@@ -10,6 +10,7 @@ MOD_TARGET=test_klp_callbacks_mod
 MOD_TARGET_BUSY=test_klp_callbacks_busy
 
 setup_config
+detect_provides_attr
 
 
 # Test a combination of loading a kernel module and a livepatch that
@@ -458,16 +459,17 @@ $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit"
 #   execute as each patch progresses through its (un)patching
 #   transition.
 
-start_test "multiple livepatches"
+function test_multiple_livepatches() {
+	start_test "multiple livepatches"
 
-load_lp $MOD_LIVEPATCH
-load_lp $MOD_LIVEPATCH2
-disable_lp $MOD_LIVEPATCH2
-disable_lp $MOD_LIVEPATCH
-unload_lp $MOD_LIVEPATCH2
-unload_lp $MOD_LIVEPATCH
+	load_lp $MOD_LIVEPATCH
+	load_lp $MOD_LIVEPATCH2
+	disable_lp $MOD_LIVEPATCH2
+	disable_lp $MOD_LIVEPATCH
+	unload_lp $MOD_LIVEPATCH2
+	unload_lp $MOD_LIVEPATCH
 
-check_result "% insmod test_modules/$MOD_LIVEPATCH.ko
+	check_result "% insmod test_modules/$MOD_LIVEPATCH.ko
 livepatch: enabling patch '$MOD_LIVEPATCH'
 livepatch: '$MOD_LIVEPATCH': initializing patching transition
 $MOD_LIVEPATCH: pre_patch_callback: vmlinux
@@ -499,6 +501,7 @@ $MOD_LIVEPATCH: post_unpatch_callback: vmlinux
 livepatch: '$MOD_LIVEPATCH': unpatching complete
 % rmmod $MOD_LIVEPATCH2
 % rmmod $MOD_LIVEPATCH"
+}
 
 
 # Load multiple livepatches, but the second as an 'atomic-replace'
@@ -515,15 +518,16 @@ livepatch: '$MOD_LIVEPATCH': unpatching complete
 # - Once the atomic replace module is loaded, only its pre and post
 #   unpatch callbacks are executed.
 
-start_test "atomic replace"
+function test_atomic_replace() {
+	start_test "atomic replace"
 
-load_lp $MOD_LIVEPATCH
-load_lp $MOD_LIVEPATCH2 replace=1
-disable_lp $MOD_LIVEPATCH2
-unload_lp $MOD_LIVEPATCH2
-unload_lp $MOD_LIVEPATCH
+	load_lp $MOD_LIVEPATCH
+	load_lp $MOD_LIVEPATCH2 replace=1
+	disable_lp $MOD_LIVEPATCH2
+	unload_lp $MOD_LIVEPATCH2
+	unload_lp $MOD_LIVEPATCH
 
-check_result "% insmod test_modules/$MOD_LIVEPATCH.ko
+	check_result "% insmod test_modules/$MOD_LIVEPATCH.ko
 livepatch: enabling patch '$MOD_LIVEPATCH'
 livepatch: '$MOD_LIVEPATCH': initializing patching transition
 $MOD_LIVEPATCH: pre_patch_callback: vmlinux
@@ -548,6 +552,15 @@ $MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
 livepatch: '$MOD_LIVEPATCH2': unpatching complete
 % rmmod $MOD_LIVEPATCH2
 % rmmod $MOD_LIVEPATCH"
+}
 
 
+if [[ "$HAS_PROVIDES_ATTR" != "1" ]]; then
+	test_multiple_livepatches
+	test_atomic_replace
+else
+	skip_test "multiple livepatches" "legacy replace attribute not present"
+	skip_test "atomic replace" "legacy replace attribute not present"
+fi
Wrapping into functions is not used later anywhere if I am not missing
something. Nothing really important but my preference would be to just
wrap the existing test code with HAS_PROVIDES_ATTR if where appropriate
and be done with it.

if [[ "$HAS_PROVIDES_ATTR" != "1" ]]; then
	start_test "multiple livepatches"
	load_lp $MOD_LIVEPATCH
	load_lp $MOD_LIVEPATCH2
	...
else
	skip_test "multiple livepatches" "legacy replace attribute not present"
fi

and similarly for "atomic replace" test.

What do you think?

[...]
+if [[ "$HAS_PROVIDES_ATTR" != "1" ]]; then
+	test_multiple_livepatches
+	test_atomic_replace_livepatch
+else
+	skip_test "multiple livepatches" "legacy replace attribute not present"
+	skip_test "atomic replace livepatch" "legacy replace attribute not present"
+fi
Same here. 
  
quoted hunk ↗ jump to hunk
 # - load a target module that provides /proc/test_klp_mod_target with
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..de4eabd4b924 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>
 
+#ifndef CONFIG_KLP_HAS_PROVIDES
 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,
+#ifndef CONFIG_KLP_HAS_PROVIDES
 	/* set .replace in the init function below for demo purposes */
+#endif
 };
 
 static int test_klp_callbacks_demo2_init(void)
 {
+#ifndef CONFIG_KLP_HAS_PROVIDES
 	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);
 }
A nit, but since you change #ifndef to #ifdef later, my preference would
be to use #ifdef here right away.

#ifdef CONFIG_KLP_HAS_PROVIDES
	/*
	 * TODO: Set provides/obsoletes from the module parameters
	 * for the provides/obsoletes based tests (to be added later).
	 */
#else
	patch.replace = replace;
#endif

After all you prepare the code for a new feature. What do you think?

Similarly elsewhere.

-- 
Miroslav
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help