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 interfacediff --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 withdiff --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