* [PATCH] selftests/livepatch: add test for module function patching @ 2026-03-20 20:11 Pablo Hugen 2026-03-24 14:22 ` Miroslav Benes 2026-03-26 14:34 ` Petr Mladek 0 siblings, 2 replies; 7+ messages in thread From: Pablo Hugen @ 2026-03-20 20:11 UTC (permalink / raw) To: live-patching, linux-kselftest, linux-kernel Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah, Pablo Alessandro Santos Hugen From: Pablo Alessandro Santos Hugen <phugen@redhat.com> Add a target module and livepatch pair that verify module function patching via a proc entry. Two test cases cover both the klp_enable_patch path (target loaded before livepatch) and the klp_module_coming path (livepatch loaded before target). Signed-off-by: Pablo Alessandro Santos Hugen <phugen@redhat.com> --- .../selftests/livepatch/test-livepatch.sh | 100 ++++++++++++++++++ .../selftests/livepatch/test_modules/Makefile | 2 + .../test_modules/test_klp_mod_patch.c | 53 ++++++++++ .../test_modules/test_klp_mod_target.c | 39 +++++++ 4 files changed, 194 insertions(+) create mode 100644 tools/testing/selftests/livepatch/test_modules/test_klp_mod_patch.c create mode 100644 tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c diff --git a/tools/testing/selftests/livepatch/test-livepatch.sh b/tools/testing/selftests/livepatch/test-livepatch.sh index 6673023d2b66..c44c5341a2f1 100755 --- a/tools/testing/selftests/livepatch/test-livepatch.sh +++ b/tools/testing/selftests/livepatch/test-livepatch.sh @@ -8,6 +8,8 @@ MOD_LIVEPATCH1=test_klp_livepatch MOD_LIVEPATCH2=test_klp_syscall MOD_LIVEPATCH3=test_klp_callbacks_demo MOD_REPLACE=test_klp_atomic_replace +MOD_TARGET=test_klp_mod_target +MOD_TARGET_PATCH=test_klp_mod_patch setup_config @@ -196,4 +198,102 @@ livepatch: '$MOD_REPLACE': unpatching complete % rmmod $MOD_REPLACE" +# - load a target module that provides /proc/test_klp_mod_target with +# original output +# - load a livepatch that patches the target module's show function +# - verify the proc entry returns livepatched output +# - disable and unload the livepatch +# - verify the proc entry returns original output again +# - unload the target module + +start_test "module function patching" + +load_mod $MOD_TARGET + +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET: original output" ]] ; then + echo -e "FAIL\n\n" + die "livepatch kselftest(s) failed" +fi + +load_lp $MOD_TARGET_PATCH + +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET_PATCH: this has been live patched" ]] ; then + echo -e "FAIL\n\n" + die "livepatch kselftest(s) failed" +fi + +disable_lp $MOD_TARGET_PATCH +unload_lp $MOD_TARGET_PATCH + +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET: original output" ]] ; then + echo -e "FAIL\n\n" + die "livepatch kselftest(s) failed" +fi + +unload_mod $MOD_TARGET + +check_result "% insmod test_modules/$MOD_TARGET.ko +$MOD_TARGET: test_klp_mod_target_init +% insmod test_modules/$MOD_TARGET_PATCH.ko +livepatch: enabling patch '$MOD_TARGET_PATCH' +livepatch: '$MOD_TARGET_PATCH': initializing patching transition +livepatch: '$MOD_TARGET_PATCH': starting patching transition +livepatch: '$MOD_TARGET_PATCH': completing patching transition +livepatch: '$MOD_TARGET_PATCH': patching complete +% echo 0 > $SYSFS_KLP_DIR/$MOD_TARGET_PATCH/enabled +livepatch: '$MOD_TARGET_PATCH': initializing unpatching transition +livepatch: '$MOD_TARGET_PATCH': starting unpatching transition +livepatch: '$MOD_TARGET_PATCH': completing unpatching transition +livepatch: '$MOD_TARGET_PATCH': unpatching complete +% rmmod $MOD_TARGET_PATCH +% rmmod $MOD_TARGET +$MOD_TARGET: test_klp_mod_target_exit" + + +# - load a livepatch that targets a not-yet-loaded module +# - load the target module: klp_module_coming patches it immediately +# - verify the proc entry returns livepatched output +# - disable and unload the livepatch +# - verify the proc entry returns original output again +# - unload the target module + +start_test "module function patching (livepatch first)" + +load_lp $MOD_TARGET_PATCH +load_mod $MOD_TARGET + +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET_PATCH: this has been live patched" ]] ; then + echo -e "FAIL\n\n" + die "livepatch kselftest(s) failed" +fi + +disable_lp $MOD_TARGET_PATCH +unload_lp $MOD_TARGET_PATCH + +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET: original output" ]] ; then + echo -e "FAIL\n\n" + die "livepatch kselftest(s) failed" +fi + +unload_mod $MOD_TARGET + +check_result "% insmod test_modules/$MOD_TARGET_PATCH.ko +livepatch: enabling patch '$MOD_TARGET_PATCH' +livepatch: '$MOD_TARGET_PATCH': initializing patching transition +livepatch: '$MOD_TARGET_PATCH': starting patching transition +livepatch: '$MOD_TARGET_PATCH': completing patching transition +livepatch: '$MOD_TARGET_PATCH': patching complete +% insmod test_modules/$MOD_TARGET.ko +livepatch: applying patch '$MOD_TARGET_PATCH' to loading module '$MOD_TARGET' +$MOD_TARGET: test_klp_mod_target_init +% echo 0 > $SYSFS_KLP_DIR/$MOD_TARGET_PATCH/enabled +livepatch: '$MOD_TARGET_PATCH': initializing unpatching transition +livepatch: '$MOD_TARGET_PATCH': starting unpatching transition +livepatch: '$MOD_TARGET_PATCH': completing unpatching transition +livepatch: '$MOD_TARGET_PATCH': unpatching complete +% rmmod $MOD_TARGET_PATCH +% rmmod $MOD_TARGET +$MOD_TARGET: test_klp_mod_target_exit" + + exit 0 diff --git a/tools/testing/selftests/livepatch/test_modules/Makefile b/tools/testing/selftests/livepatch/test_modules/Makefile index 939230e571f5..a13d398585dc 100644 --- a/tools/testing/selftests/livepatch/test_modules/Makefile +++ b/tools/testing/selftests/livepatch/test_modules/Makefile @@ -8,6 +8,8 @@ obj-m += test_klp_atomic_replace.o \ test_klp_callbacks_mod.o \ test_klp_kprobe.o \ test_klp_livepatch.o \ + test_klp_mod_patch.o \ + test_klp_mod_target.o \ test_klp_shadow_vars.o \ test_klp_state.o \ test_klp_state2.o \ diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_mod_patch.c b/tools/testing/selftests/livepatch/test_modules/test_klp_mod_patch.c new file mode 100644 index 000000000000..6725b4720365 --- /dev/null +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_mod_patch.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Pablo Hugen <phugen@redhat.com> + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/livepatch.h> +#include <linux/seq_file.h> + +static int livepatch_mod_target_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s: %s\n", THIS_MODULE->name, + "this has been live patched"); + return 0; +} + +static struct klp_func funcs[] = { + { + .old_name = "test_klp_mod_target_show", + .new_func = livepatch_mod_target_show, + }, + {}, +}; + +static struct klp_object objs[] = { + { + .name = "test_klp_mod_target", + .funcs = funcs, + }, + {}, +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int test_klp_mod_patch_init(void) +{ + return klp_enable_patch(&patch); +} + +static void test_klp_mod_patch_exit(void) +{ +} + +module_init(test_klp_mod_patch_init); +module_exit(test_klp_mod_patch_exit); +MODULE_LICENSE("GPL"); +MODULE_INFO(livepatch, "Y"); +MODULE_AUTHOR("Pablo Hugen <phugen@redhat.com>"); +MODULE_DESCRIPTION("Livepatch test: patch for module-provided function"); diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c b/tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c new file mode 100644 index 000000000000..9643984d2402 --- /dev/null +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Pablo Hugen <phugen@redhat.com> + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +static struct proc_dir_entry *pde; + +static noinline int test_klp_mod_target_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s: %s\n", THIS_MODULE->name, "original output"); + return 0; +} + +static int test_klp_mod_target_init(void) +{ + pr_info("%s\n", __func__); + pde = proc_create_single("test_klp_mod_target", 0, NULL, + test_klp_mod_target_show); + if (!pde) + return -ENOMEM; + return 0; +} + +static void test_klp_mod_target_exit(void) +{ + pr_info("%s\n", __func__); + proc_remove(pde); +} + +module_init(test_klp_mod_target_init); +module_exit(test_klp_mod_target_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pablo Hugen <phugen@redhat.com>"); +MODULE_DESCRIPTION("Livepatch test: target module with proc entry"); -- 2.53.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] selftests/livepatch: add test for module function patching 2026-03-20 20:11 [PATCH] selftests/livepatch: add test for module function patching Pablo Hugen @ 2026-03-24 14:22 ` Miroslav Benes 2026-03-24 14:45 ` Joe Lawrence 2026-03-26 14:34 ` Petr Mladek 1 sibling, 1 reply; 7+ messages in thread From: Miroslav Benes @ 2026-03-24 14:22 UTC (permalink / raw) To: Pablo Hugen Cc: live-patching, linux-kselftest, linux-kernel, jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah On Fri, 20 Mar 2026 17:11:17 -0300, Pablo Hugen <phugen@redhat.com> wrote: > Add a target module and livepatch pair that verify module function > patching via a proc entry. Two test cases cover both the > klp_enable_patch path (target loaded before livepatch) and the > klp_module_coming path (livepatch loaded before target). We sort of test the same in test-callbacks.sh. Just using different means. I think I would not mind having this as well. Petr, Joe, what do you think? > > > diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c b/tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c > new file mode 100644 > index 000000000000..9643984d2402 > --- /dev/null > +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c > @@ -0,0 +1,39 @@ > [ ... skip 11 lines ... ] > + > +static noinline int test_klp_mod_target_show(struct seq_file *m, void *v) > +{ > + seq_printf(m, "%s: %s\n", THIS_MODULE->name, "original output"); > + return 0; > +} A nit but is 'noinline' keyword needed here? proc_create_single() below takes a function pointer so hopefully test_klp_mod_target_show() stays even without it? > + > +static int test_klp_mod_target_init(void) > +{ > + pr_info("%s\n", __func__); > + pde = proc_create_single("test_klp_mod_target", 0, NULL, > + test_klp_mod_target_show); ...here. Otherwise it looks good to me. -- Miroslav ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] selftests/livepatch: add test for module function patching 2026-03-24 14:22 ` Miroslav Benes @ 2026-03-24 14:45 ` Joe Lawrence 2026-03-25 8:45 ` Miroslav Benes 0 siblings, 1 reply; 7+ messages in thread From: Joe Lawrence @ 2026-03-24 14:45 UTC (permalink / raw) To: Miroslav Benes Cc: Pablo Hugen, live-patching, linux-kselftest, linux-kernel, jpoimboe, jikos, pmladek, shuah On Tue, Mar 24, 2026 at 03:22:27PM +0100, Miroslav Benes wrote: > On Fri, 20 Mar 2026 17:11:17 -0300, Pablo Hugen <phugen@redhat.com> wrote: > > Add a target module and livepatch pair that verify module function > > patching via a proc entry. Two test cases cover both the > > klp_enable_patch path (target loaded before livepatch) and the > > klp_module_coming path (livepatch loaded before target). > > We sort of test the same in test-callbacks.sh. Just using different > means. I think I would not mind having this as well. > > Petr, Joe, what do you think? > I was *just* in the middle of replying to the patch when yours came in, so I'll just move over here. I had noticed the same thing re: test-callbacks.sh despite originally suggested writing this test to Pablo (and forgot about the callbacks test module). With that, I agree that it's a nice basic sanity check that's obvious about what it's testing. > > > > > > diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c b/tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c > > new file mode 100644 > > index 000000000000..9643984d2402 > > --- /dev/null > > +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_mod_target.c > > @@ -0,0 +1,39 @@ > > [ ... skip 11 lines ... ] > > + > > +static noinline int test_klp_mod_target_show(struct seq_file *m, void *v) > > +{ > > + seq_printf(m, "%s: %s\n", THIS_MODULE->name, "original output"); > > + return 0; > > +} > > A nit but is 'noinline' keyword needed here? proc_create_single() below > takes a function pointer so hopefully test_klp_mod_target_show() stays > even without it? > No strong preference either way. I won't fault a livepatch developer for being paranoid w/respect to the compiler :D Acked-by: Joe Lawrence <joe.lawrence@redhat.com> -- Joe ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] selftests/livepatch: add test for module function patching 2026-03-24 14:45 ` Joe Lawrence @ 2026-03-25 8:45 ` Miroslav Benes 0 siblings, 0 replies; 7+ messages in thread From: Miroslav Benes @ 2026-03-25 8:45 UTC (permalink / raw) To: Joe Lawrence Cc: Miroslav Benes, Pablo Hugen, live-patching, linux-kselftest, linux-kernel, jpoimboe, jikos, pmladek, shuah > > A nit but is 'noinline' keyword needed here? proc_create_single() below > > takes a function pointer so hopefully test_klp_mod_target_show() stays > > even without it? > > No strong preference either way. I won't fault a livepatch developer > for being paranoid w/respect to the compiler :D True :) Either way Acked-by: Miroslav Benes <mbenes@suse.cz> ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] selftests/livepatch: add test for module function patching 2026-03-20 20:11 [PATCH] selftests/livepatch: add test for module function patching Pablo Hugen 2026-03-24 14:22 ` Miroslav Benes @ 2026-03-26 14:34 ` Petr Mladek 2026-03-26 20:41 ` Joe Lawrence 1 sibling, 1 reply; 7+ messages in thread From: Petr Mladek @ 2026-03-26 14:34 UTC (permalink / raw) To: Pablo Hugen Cc: live-patching, linux-kselftest, linux-kernel, jpoimboe, jikos, mbenes, joe.lawrence, shuah On Fri 2026-03-20 17:11:17, Pablo Hugen wrote: > From: Pablo Alessandro Santos Hugen <phugen@redhat.com> > > Add a target module and livepatch pair that verify module function > patching via a proc entry. Two test cases cover both the > klp_enable_patch path (target loaded before livepatch) and the > klp_module_coming path (livepatch loaded before target). First, thanks for the test. Second, I am a bit biased because I am working on a patchset which would obsolete this patch, see https://lore.kernel.org/all/20250115082431.5550-1-pmladek@suse.com/ That said, I have sent an RFC a year ago. I worked on v1 when time permitted but it is still not ready. And it might take another many months or year to finish it. Your test might be perfectly fine in the meantime. Just see few notes below. > --- a/tools/testing/selftests/livepatch/test-livepatch.sh > +++ b/tools/testing/selftests/livepatch/test-livepatch.sh > @@ -8,6 +8,8 @@ MOD_LIVEPATCH1=test_klp_livepatch > MOD_LIVEPATCH2=test_klp_syscall > MOD_LIVEPATCH3=test_klp_callbacks_demo > MOD_REPLACE=test_klp_atomic_replace > +MOD_TARGET=test_klp_mod_target > +MOD_TARGET_PATCH=test_klp_mod_patch > > setup_config > > @@ -196,4 +198,102 @@ livepatch: '$MOD_REPLACE': unpatching complete > % rmmod $MOD_REPLACE" > > > +# - load a target module that provides /proc/test_klp_mod_target with > +# original output > +# - load a livepatch that patches the target module's show function > +# - verify the proc entry returns livepatched output > +# - disable and unload the livepatch > +# - verify the proc entry returns original output again > +# - unload the target module > + > +start_test "module function patching" > + > +load_mod $MOD_TARGET > + > +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET: original output" ]] ; then > + echo -e "FAIL\n\n" > + die "livepatch kselftest(s) failed" > +fi This code is repeated several times. It might be worth creating a helper function in tools/testing/selftests/livepatch/functions.sh. > +load_lp $MOD_TARGET_PATCH > + > +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET_PATCH: this has been live patched" ]] ; then > + echo -e "FAIL\n\n" > + die "livepatch kselftest(s) failed" > +fi When I was working on the above mentioned patchset, I realized that "die" in the middle of the test was not practical because it did not do any clean up. As a result, "make run_tests" continued with other tests but they typically failed as well. And I had to manually remove the test modules to be able to try "fixed" tests again. I thought about two solutions: 1. Remember loaded modules and try to remove them in a clean up code. 2. Report the failure into the kernel log but keep the test running so that they calls the disable_lp/unload_lp/unload_mod functions. The test will do the clean up and will fail later in check_result(). While the 1st approach might be easier in the end, I choose the 2nd approach in my RFC, see below. > +disable_lp $MOD_TARGET_PATCH > +unload_lp $MOD_TARGET_PATCH > + > +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET: original output" ]] ; then > + echo -e "FAIL\n\n" > + die "livepatch kselftest(s) failed" > +fi > + > +unload_mod $MOD_TARGET > + > +check_result "% insmod test_modules/$MOD_TARGET.ko > +$MOD_TARGET: test_klp_mod_target_init > +% insmod test_modules/$MOD_TARGET_PATCH.ko Note that the existing helper functions log the userspace commands in the kernel log. It helps to understand the kernel logs. In my RFC, I created a helper module which implemented a person (speaker) which would come on the stage and welcome the audience. I am not sure if it was a good idea. But it became a bit confusing when everything (module name, sysfs interface, function name, message) included the same strings like (livepatch, callback, shadow_var). Anyway, my tests produced messages like these: +% cat $SYSFS_MODULE_DIR/$MOD_TARGET/parameters/welcome +$MOD_TARGET: speaker_welcome: Hello, World! , see https://lore.kernel.org/all/20250115082431.5550-9-pmladek@suse.com/ There were even tests which blocked the transition. They tested shadow variables which added an applause to the message. They did something like: <paste> All four callbacks are used as follows: + pre_patch() allocates a shadow variable with a string and fills it with "[]". + post_patch() fills the string with "[APPLAUSE]". + pre_unpatch() reverts the string back to "[]". + post_unpatch() releases the shadow variable. The welcome message printed by the livepatched function allows us to distinguish between the transition and the completed transition. Specifically, the speaker's welcome message appears as: + Not patched system: "Hello, World!" + Transition (unpatched task): "[] Hello, World!" + Transition (patched task): "[] Ladies and gentlemen, ..." + Patched system: "[APPLAUSE] Ladies and gentlemen, ..." </paste> , see https://lore.kernel.org/all/20250115082431.5550-11-pmladek@suse.com/ Sigh, I have done many changes in the tests for v1. But they still need some love (and rebasing) for sending. > +livepatch: enabling patch '$MOD_TARGET_PATCH' > +livepatch: '$MOD_TARGET_PATCH': initializing patching transition > +livepatch: '$MOD_TARGET_PATCH': starting patching transition > +livepatch: '$MOD_TARGET_PATCH': completing patching transition > +livepatch: '$MOD_TARGET_PATCH': patching complete > +% echo 0 > $SYSFS_KLP_DIR/$MOD_TARGET_PATCH/enabled > +livepatch: '$MOD_TARGET_PATCH': initializing unpatching transition > +livepatch: '$MOD_TARGET_PATCH': starting unpatching transition > +livepatch: '$MOD_TARGET_PATCH': completing unpatching transition > +livepatch: '$MOD_TARGET_PATCH': unpatching complete > +% rmmod $MOD_TARGET_PATCH > +% rmmod $MOD_TARGET > +$MOD_TARGET: test_klp_mod_target_exit" Summary: IMHO, this patch is perfectly fine as is if we accept that it will get eventually obsoleted by my patchset (hopefully in a year or two). On the other hand, this patch would deserve some clean up, (helper functions, don't die in the middle of the test) if you planned to work on more tests. It would help to maintain the tests. Best Regards, Petr ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] selftests/livepatch: add test for module function patching 2026-03-26 14:34 ` Petr Mladek @ 2026-03-26 20:41 ` Joe Lawrence 2026-03-27 10:46 ` Miroslav Benes 0 siblings, 1 reply; 7+ messages in thread From: Joe Lawrence @ 2026-03-26 20:41 UTC (permalink / raw) To: Petr Mladek Cc: Pablo Hugen, live-patching, linux-kselftest, linux-kernel, jpoimboe, jikos, mbenes, shuah On Thu, Mar 26, 2026 at 03:34:36PM +0100, Petr Mladek wrote: > On Fri 2026-03-20 17:11:17, Pablo Hugen wrote: > > From: Pablo Alessandro Santos Hugen <phugen@redhat.com> > > > > Add a target module and livepatch pair that verify module function > > patching via a proc entry. Two test cases cover both the > > klp_enable_patch path (target loaded before livepatch) and the > > klp_module_coming path (livepatch loaded before target). > > First, thanks for the test. > > Second, I am a bit biased because I am working on a patchset which would > obsolete this patch, see > https://lore.kernel.org/all/20250115082431.5550-1-pmladek@suse.com/ > > That said, I have sent an RFC a year ago. I worked on v1 when time > permitted but it is still not ready. And it might take another > many months or year to finish it. > Hi Petr, I remember that patch as a "grand, unified" theory for livepatching API and look forward to the next iteration when you find the time. > Your test might be perfectly fine in the meantime. Just see few > notes below. > > > --- a/tools/testing/selftests/livepatch/test-livepatch.sh > > +++ b/tools/testing/selftests/livepatch/test-livepatch.sh > > @@ -8,6 +8,8 @@ MOD_LIVEPATCH1=test_klp_livepatch > > MOD_LIVEPATCH2=test_klp_syscall > > MOD_LIVEPATCH3=test_klp_callbacks_demo > > MOD_REPLACE=test_klp_atomic_replace > > +MOD_TARGET=test_klp_mod_target > > +MOD_TARGET_PATCH=test_klp_mod_patch > > > > setup_config > > > > @@ -196,4 +198,102 @@ livepatch: '$MOD_REPLACE': unpatching complete > > % rmmod $MOD_REPLACE" > > > > > > +# - load a target module that provides /proc/test_klp_mod_target with > > +# original output > > +# - load a livepatch that patches the target module's show function > > +# - verify the proc entry returns livepatched output > > +# - disable and unload the livepatch > > +# - verify the proc entry returns original output again > > +# - unload the target module > > + > > +start_test "module function patching" > > + > > +load_mod $MOD_TARGET > > + > > +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET: original output" ]] ; then > > + echo -e "FAIL\n\n" > > + die "livepatch kselftest(s) failed" > > +fi > > This code is repeated several times. It might be worth creating a > helper function in tools/testing/selftests/livepatch/functions.sh. > > > +load_lp $MOD_TARGET_PATCH > > + > > +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET_PATCH: this has been live patched" ]] ; then > > + echo -e "FAIL\n\n" > > + die "livepatch kselftest(s) failed" > > +fi > > When I was working on the above mentioned patchset, I realized that > "die" in the middle of the test was not practical because it > did not do any clean up. IIRC it was an original design intention so that if you were to run the test script directly, the system would be left in that state for debugging. That said ... > As a result, "make run_tests" > continued with other tests but they typically failed as well. > And I had to manually remove the test modules to be able to > try "fixed" tests again. ... D'oh, that's pretty annoying behavior to wade through, like triaging compiler failures (i.e. the Nth error is probably just a result of the first N-1 error(s).) Since most people probably run via the run_tests target, my previous preference for a debuggable state probably isn't realistic anymore as the test car continues careening down the cliff, accumulating new, interesting damage with each subsequent test failure. > > I thought about two solutions: > > 1. Remember loaded modules and try to remove them in a clean up code. > At the time of creating the selftests, I remember resisting this effort, afraid of the Pareto Principle and never getting that last 20% correct (is the livepatch transition stuck, broken, how I can cleanly unwind from a test gone rogue, etc.) > 2. Report the failure into the kernel log but keep the test > running so that they calls the disable_lp/unload_lp/unload_mod > functions. The test will do the clean up and will fail > later in check_result(). > > > While the 1st approach might be easier in the end, I choose > the 2nd approach in my RFC, see below. > > > > +disable_lp $MOD_TARGET_PATCH > > +unload_lp $MOD_TARGET_PATCH > > + > > +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET: original output" ]] ; then > > + echo -e "FAIL\n\n" > > + die "livepatch kselftest(s) failed" > > +fi > > + > > +unload_mod $MOD_TARGET > > + > > +check_result "% insmod test_modules/$MOD_TARGET.ko > > +$MOD_TARGET: test_klp_mod_target_init > > +% insmod test_modules/$MOD_TARGET_PATCH.ko > So following this technique, all the other tests with command sequences would need to be re-written as '&&' chains, e.g. the "patch getpid syscall while being heavily hammered" one like: pid_list=$(echo "${pids[@]}" | tr ' ' ',') && \ load_lp $MOD_SYSCALL klp_pids=$pid_list && \ loop_until "grep -q '^0$' $SYSFS_KERNEL_DIR/$MOD_SYSCALL/npids" && \ log "$MOD_SYSCALL: Remaining not livepatched processes: $(cat $SYSFS_KERNEL_DIR/$MOD_SYSCALL/npids)" so that we only continue down a particular test for as long as it's successful, then the cleanup code is unconditional: pending_pids=$(cat $SYSFS_KERNEL_DIR/$MOD_SYSCALL/npids) log "$MOD_SYSCALL: Remaining not livepatched processes: $pending_pids" for pid in ${pids[@]}; do kill $pid || true done disable_lp $MOD_SYSCALL unload_lp $MOD_SYSCALL check_result <- flags a problem Yeah, may be that's not so bad. The functions.sh helpers may need to be hardened a little (can they cancel / bust a transition? it's been a while since I've looked.) Or maybe ... ugh, bash is not a programming language ... each test is split into its own script, the die calls can remain as they are, but we move the cleanup logic into a trap EXIT handler so it always runs? Or test-code and test-cleanup-code are split into their own functions, so tests can exit early, but their cleanup is always called? (Just brainstorming here.) > Note that the existing helper functions log the userspace commands > in the kernel log. It helps to understand the kernel logs. > > In my RFC, I created a helper module which implemented a person > (speaker) which would come on the stage and welcome the audience. > I am not sure if it was a good idea. But it became a bit confusing > when everything (module name, sysfs interface, function name, message) > included the same strings like (livepatch, callback, shadow_var). > > Anyway, my tests produced messages like these: > > +% cat $SYSFS_MODULE_DIR/$MOD_TARGET/parameters/welcome > +$MOD_TARGET: speaker_welcome: Hello, World! > > , see https://lore.kernel.org/all/20250115082431.5550-9-pmladek@suse.com/ > > > There were even tests which blocked the transition. They tested shadow > variables which added an applause to the message. They did something like: > > <paste> > All four callbacks are used as follows: > > + pre_patch() allocates a shadow variable with a string and fills > it with "[]". > + post_patch() fills the string with "[APPLAUSE]". > + pre_unpatch() reverts the string back to "[]". > + post_unpatch() releases the shadow variable. > > The welcome message printed by the livepatched function allows us to > distinguish between the transition and the completed transition. > Specifically, the speaker's welcome message appears as: > > + Not patched system: "Hello, World!" > + Transition (unpatched task): "[] Hello, World!" > + Transition (patched task): "[] Ladies and gentlemen, ..." > + Patched system: "[APPLAUSE] Ladies and gentlemen, ..." > </paste> > > , see https://lore.kernel.org/all/20250115082431.5550-11-pmladek@suse.com/ > > Sigh, I have done many changes in the tests for v1. But they still > need some love (and rebasing) for sending. > If these can be pulled out independently from the v1 patch, perhaps Pablo would want to hack on that in a follow up series? > > +livepatch: enabling patch '$MOD_TARGET_PATCH' > > +livepatch: '$MOD_TARGET_PATCH': initializing patching transition > > +livepatch: '$MOD_TARGET_PATCH': starting patching transition > > +livepatch: '$MOD_TARGET_PATCH': completing patching transition > > +livepatch: '$MOD_TARGET_PATCH': patching complete > > +% echo 0 > $SYSFS_KLP_DIR/$MOD_TARGET_PATCH/enabled > > +livepatch: '$MOD_TARGET_PATCH': initializing unpatching transition > > +livepatch: '$MOD_TARGET_PATCH': starting unpatching transition > > +livepatch: '$MOD_TARGET_PATCH': completing unpatching transition > > +livepatch: '$MOD_TARGET_PATCH': unpatching complete > > +% rmmod $MOD_TARGET_PATCH > > +% rmmod $MOD_TARGET > > +$MOD_TARGET: test_klp_mod_target_exit" > > Summary: > > IMHO, this patch is perfectly fine as is if we accept that it will get > eventually obsoleted by my patchset (hopefully in a year or two). > > On the other hand, this patch would deserve some clean up, > (helper functions, don't die in the middle of the test) if > you planned to work on more tests. It would help to maintain > the tests. > Right, I think this was a good intro patch for Pablo and that the revised execution flow would be a great follow on series, if he is interested. How about that? Regards, -- Joe ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] selftests/livepatch: add test for module function patching 2026-03-26 20:41 ` Joe Lawrence @ 2026-03-27 10:46 ` Miroslav Benes 0 siblings, 0 replies; 7+ messages in thread From: Miroslav Benes @ 2026-03-27 10:46 UTC (permalink / raw) To: Joe Lawrence Cc: Petr Mladek, Pablo Hugen, live-patching, linux-kselftest, linux-kernel, jpoimboe, jikos, shuah > > > +disable_lp $MOD_TARGET_PATCH > > > +unload_lp $MOD_TARGET_PATCH > > > + > > > +if [[ "$(cat /proc/$MOD_TARGET)" != "$MOD_TARGET: original output" ]] ; then > > > + echo -e "FAIL\n\n" > > > + die "livepatch kselftest(s) failed" > > > +fi > > > + > > > +unload_mod $MOD_TARGET > > > + > > > +check_result "% insmod test_modules/$MOD_TARGET.ko > > > +$MOD_TARGET: test_klp_mod_target_init > > > +% insmod test_modules/$MOD_TARGET_PATCH.ko > > > > So following this technique, all the other tests with command sequences > would need to be re-written as '&&' chains, e.g. the "patch getpid > syscall while being heavily hammered" one like: > > pid_list=$(echo "${pids[@]}" | tr ' ' ',') && \ > load_lp $MOD_SYSCALL klp_pids=$pid_list && \ > loop_until "grep -q '^0$' $SYSFS_KERNEL_DIR/$MOD_SYSCALL/npids" && \ > log "$MOD_SYSCALL: Remaining not livepatched processes: $(cat $SYSFS_KERNEL_DIR/$MOD_SYSCALL/npids)" > > so that we only continue down a particular test for as long as it's > successful, then the cleanup code is unconditional: > > pending_pids=$(cat $SYSFS_KERNEL_DIR/$MOD_SYSCALL/npids) > log "$MOD_SYSCALL: Remaining not livepatched processes: $pending_pids" > > for pid in ${pids[@]}; do > kill $pid || true > done > > disable_lp $MOD_SYSCALL > unload_lp $MOD_SYSCALL > > check_result <- flags a problem > > Yeah, may be that's not so bad. The functions.sh helpers may need to be > hardened a little (can they cancel / bust a transition? it's been a > while since I've looked.) > > Or maybe ... ugh, bash is not a programming language ... each test is > split into its own script, the die calls can remain as they are, but we > move the cleanup logic into a trap EXIT handler so it always runs? We use this technique in OOT https://github.com/SUSE/qa_test_klp/ tests (slowly being upstreamed). See https://github.com/SUSE/qa_test_klp/blob/master/klp_tc_functions.sh. Mainly klp_tc_init(), klp_tc_exit() and klp_tc_abort(). Different tests then use what you proposed above... caching pids and modules to clean up. Miroslav ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-03-27 10:46 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-20 20:11 [PATCH] selftests/livepatch: add test for module function patching Pablo Hugen 2026-03-24 14:22 ` Miroslav Benes 2026-03-24 14:45 ` Joe Lawrence 2026-03-25 8:45 ` Miroslav Benes 2026-03-26 14:34 ` Petr Mladek 2026-03-26 20:41 ` Joe Lawrence 2026-03-27 10:46 ` Miroslav Benes
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox