public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Petr Mladek <pmladek@suse.com>
To: Josh Poimboeuf <jpoimboe@kernel.org>, Miroslav Benes <mbenes@suse.cz>
Cc: Joe Lawrence <joe.lawrence@redhat.com>,
	Nicolai Stange <nstange@suse.de>,
	live-patching@vger.kernel.org, linux-kernel@vger.kernel.org,
	Petr Mladek <pmladek@suse.com>
Subject: [PATCH v1 12/19] selftests/livepatch: Convert selftest with blocked transition
Date: Wed, 15 Jan 2025 09:24:24 +0100	[thread overview]
Message-ID: <20250115082431.5550-13-pmladek@suse.com> (raw)
In-Reply-To: <20250115082431.5550-1-pmladek@suse.com>

The per-object callbacks have been deprecated in favor of per-state
callbacks and will be removed soon.

For the conversion of the self-test of the callbacks with a blocked
transition, use the new module with a virtual speaker.

Implementation Details:

  + The implementation of the blocked function is inspired by the
    module "test_klp_callbacks_busy". Specifically, the blocked function
    is called in a workqueue, synchronization is done using a completion,
    and the replacement of the blocked function is not fully functional[*].

  + The blocked function is queued and called only if the parameter
    "block_doors" is set.

Important Note on Sibling Call Optimization:

Sibling call optimization must be disabled for functions designed to block
transitions. Otherwise, they won't appear on the stack, leading to test
failure. These functions can be livepatched because they are called with
the call instruction. But when an optimized version just jumps to a nested
then the jump instruction obviously doesn't store any return address
on the stack.

[*] The livepatch variant of the blocked function is never called
    because the transition is reverted. It is going to change in a followup
    patch.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 .../selftests/livepatch/test-callbacks.sh     | 73 -------------------
 .../livepatch/test-state-callbacks.sh         | 56 ++++++++++++++
 .../livepatch/test_modules/test_klp_speaker.c | 61 ++++++++++++++++
 .../test_modules/test_klp_speaker_livepatch.c | 28 +++++++
 4 files changed, 145 insertions(+), 73 deletions(-)

diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh
index 1ecd8f08a613..858e8f0b14d5 100755
--- a/tools/testing/selftests/livepatch/test-callbacks.sh
+++ b/tools/testing/selftests/livepatch/test-callbacks.sh
@@ -11,79 +11,6 @@ MOD_TARGET_BUSY=test_klp_callbacks_busy
 
 setup_config
 
-# A similar test as the previous one, but force the "busy" kernel module
-# to block the livepatch transition.
-#
-# The livepatching core will refuse to patch a task that is currently
-# executing a to-be-patched function -- the consistency model stalls the
-# current patch transition until this safety-check is met.  Test a
-# scenario where one of a livepatch's target klp_objects sits on such a
-# function for a long time.  Meanwhile, load and unload other target
-# kernel modules while the livepatch transition is in progress.
-#
-# - Load the "busy" kernel module, this time make its work function loop
-#
-# - Meanwhile, the livepatch is loaded.  Notice that the patch
-#   transition does not complete as the targeted "busy" module is
-#   sitting on a to-be-patched function.
-#
-# - Load a second target module (this one is an ordinary idle kernel
-#   module).  Note that *no* post-patch callbacks will be executed while
-#   the livepatch is still in transition.
-#
-# - Request an unload of the simple kernel module.  The patch is still
-#   transitioning, so its pre-unpatch callbacks are skipped.
-#
-# - Finally the livepatch is disabled.  Since none of the patch's
-#   klp_object's post-patch callbacks executed, the remaining
-#   klp_object's pre-unpatch callbacks are skipped.
-
-start_test "busy target module"
-
-load_mod $MOD_TARGET_BUSY block_transition=Y
-load_lp_nowait $MOD_LIVEPATCH
-
-# Wait until the livepatch reports in-transition state, i.e. that it's
-# stalled on $MOD_TARGET_BUSY::busymod_work_func()
-loop_until 'grep -q '^1$' $SYSFS_KLP_DIR/$MOD_LIVEPATCH/transition' ||
-	die "failed to stall transition"
-
-load_mod $MOD_TARGET
-unload_mod $MOD_TARGET
-disable_lp $MOD_LIVEPATCH
-unload_lp $MOD_LIVEPATCH
-unload_mod $MOD_TARGET_BUSY
-
-check_result "% insmod test_modules/$MOD_TARGET_BUSY.ko block_transition=Y
-$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init
-$MOD_TARGET_BUSY: busymod_work_func enter
-% insmod test_modules/$MOD_LIVEPATCH.ko
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
-$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
-livepatch: '$MOD_LIVEPATCH': starting patching transition
-% insmod test_modules/$MOD_TARGET.ko
-livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
-$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
-$MOD_TARGET: ${MOD_TARGET}_init
-% rmmod $MOD_TARGET
-$MOD_TARGET: ${MOD_TARGET}_exit
-livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
-$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
-% echo 0 > $SYSFS_KLP_DIR/$MOD_LIVEPATCH/enabled
-livepatch: '$MOD_LIVEPATCH': reversing transition from patching to unpatching
-livepatch: '$MOD_LIVEPATCH': starting unpatching transition
-livepatch: '$MOD_LIVEPATCH': completing unpatching transition
-$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
-$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
-livepatch: '$MOD_LIVEPATCH': unpatching complete
-% rmmod $MOD_LIVEPATCH
-% rmmod $MOD_TARGET_BUSY
-$MOD_TARGET_BUSY: busymod_work_func exit
-$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit"
-
-
 # Test loading multiple livepatches.  This test-case is mainly for comparing
 # with the next test-case.
 #
diff --git a/tools/testing/selftests/livepatch/test-state-callbacks.sh b/tools/testing/selftests/livepatch/test-state-callbacks.sh
index 28ef88a2dfc3..043e2062d71c 100755
--- a/tools/testing/selftests/livepatch/test-state-callbacks.sh
+++ b/tools/testing/selftests/livepatch/test-state-callbacks.sh
@@ -90,4 +90,60 @@ $MOD_TARGET: speaker_welcome: Hello, World!
 % rmmod $MOD_TARGET
 $MOD_TARGET: ${MOD_TARGET}_exit"
 
+# Test state callbacks handling with blocked and reverted transitons.
+#
+# The started patching transion never finishes. Only "pre_patch"
+# callback is called.
+#
+# When reading the "welcome" parameter, the livepatched message
+# is printed because it is a new process. But [APPLAUSE] is not
+# printed because the "post_patch" callback has not been called.
+#
+# When the livepatch gets disabled, the current transiton gets
+# reverted instead of starting a new disable transition. Only
+# the "post_unpatch" callback is called.
+start_test "blocked transition"
+
+load_mod $MOD_TARGET block_doors=1
+read_module_param $MOD_TARGET welcome
+
+load_lp_nowait $MOD_LIVEPATCH applause=1
+# Wait until the livepatch reports in-transition state, i.e. that it's
+# stalled because of the process with the waiting speaker
+loop_until 'grep -q '^1$' $SYSFS_KLP_DIR/$MOD_LIVEPATCH/transition' ||
+	die "failed to stall transition"
+read_module_param $MOD_TARGET welcome
+
+disable_lp $MOD_LIVEPATCH
+read_module_param $MOD_TARGET welcome
+
+unload_lp $MOD_LIVEPATCH
+unload_mod $MOD_TARGET
+
+check_result "% insmod test_modules/$MOD_TARGET.ko block_doors=1
+$MOD_TARGET: ${MOD_TARGET}_init
+$MOD_TARGET: block_doors_func: Going to block doors.
+$MOD_TARGET: do_block_doors: Started blocking doors.
+% cat $SYSFS_MODULE_DIR/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% insmod test_modules/$MOD_LIVEPATCH.ko applause=1
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: applause_pre_patch_callback: state 10
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+% cat $SYSFS_MODULE_DIR/$MOD_TARGET/parameters/welcome
+$MOD_LIVEPATCH: lp_speaker_welcome: [] Ladies and gentleman, ...
+% echo 0 > $SYSFS_KLP_DIR/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': reversing transition from patching to unpatching
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: applause_post_unpatch_callback: state 10 (nope)
+$MOD_LIVEPATCH: applause_shadow_dtor: freeing applause [] (nope)
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% cat $SYSFS_MODULE_DIR/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
 exit 0
diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_speaker.c b/tools/testing/selftests/livepatch/test_modules/test_klp_speaker.c
index 92c577addb8e..6dcf15b4154b 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_speaker.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_speaker.c
@@ -5,6 +5,9 @@
 
 #include <linux/module.h>
 #include <linux/printk.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/completion.h>
 
 #ifndef SPEAKER_ID
 #define SPEAKER_ID ""
@@ -21,6 +24,10 @@
  *	welcome_get().
  *
  *    - Reuse the module source for more speakers, see SPEAKER_ID.
+ *
+ *    - Add "block_doors" parameter which could block the livepatch transition.
+ *	The stalled function is offloaded to a workqueue so that it does not
+ *	block the module load.
  */
 
 noinline
@@ -43,16 +50,70 @@ static const struct kernel_param_ops welcome_ops = {
 module_param_cb(welcome, &welcome_ops, NULL, 0400);
 MODULE_PARM_DESC(welcome, "Print speaker's welcome message into the kernel log when reading the value.");
 
+static DECLARE_COMPLETION(started_blocking_doors);
+struct work_struct block_doors_work;
+static bool block_doors;
+static bool show_over;
+
+noinline
+static void do_block_doors(void)
+{
+	pr_info("%s: Started blocking doors.\n", __func__);
+	complete(&started_blocking_doors);
+
+	while (!READ_ONCE(show_over)) {
+		/* Busy-wait until the module gets unloaded. */
+		msleep(20);
+	}
+}
+
+/*
+ * Prevent tail call optimizations to make sure that this function
+ * appears in the backtrace and blocks the transition.
+ */
+__attribute__((__optimize__("no-optimize-sibling-calls")))
+static void block_doors_func(struct work_struct *work)
+{
+	pr_info("%s: Going to block doors%s.\n", __func__, SPEAKER_ID);
+	do_block_doors();
+}
+
+static void block_doors_set(void)
+{
+	init_completion(&started_blocking_doors);
+	INIT_WORK(&block_doors_work, block_doors_func);
+
+	schedule_work(&block_doors_work);
+
+	/*
+	 * To synchronize kernel messages, hold this callback from
+	 * exiting until the work function's entry message has got printed.
+	 */
+	wait_for_completion(&started_blocking_doors);
+
+}
+
+module_param(block_doors, bool, 0400);
+MODULE_PARM_DESC(block_doors, "Block doors so that the audience could not enter. It blocks the livepatch transition. (default=false)");
+
 static int test_klp_speaker_init(void)
 {
 	pr_info("%s\n", __func__);
 
+	if (block_doors)
+		block_doors_set();
+
 	return 0;
 }
 
 static void test_klp_speaker_exit(void)
 {
 	pr_info("%s\n", __func__);
+
+	if (block_doors) {
+		WRITE_ONCE(show_over, true);
+		flush_work(&block_doors_work);
+	}
 }
 
 module_init(test_klp_speaker_init);
diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_speaker_livepatch.c b/tools/testing/selftests/livepatch/test_modules/test_klp_speaker_livepatch.c
index c46c98a3c1e6..76402947c789 100644
--- a/tools/testing/selftests/livepatch/test_modules/test_klp_speaker_livepatch.c
+++ b/tools/testing/selftests/livepatch/test_modules/test_klp_speaker_livepatch.c
@@ -19,6 +19,8 @@
  *	"Ladies and gentleman, ..."
  *
  *    - Support more speaker modules, see __lp_speaker_welcome().
+ *
+ *    - Livepatch block_doors_func() which can block the transition.
  */
 
 #define APPLAUSE_ID 10
@@ -180,11 +182,33 @@ static void applause_shadow_dtor(void *obj, void *shadow_data)
 		__func__, applause);
 }
 
+static void __lp_block_doors_func(struct work_struct *work, const char *caller_func,
+		       const char *speaker_id)
+{
+	/* Just print the message. Never really used. */
+	pr_info("%s: Going to block doors%s (this should never happen).\n",
+		caller_func, speaker_id);
+}
+
+static void lp_block_doors_func(struct work_struct *work)
+{
+	__lp_block_doors_func(work, __func__, "");
+}
+
+static void lp_block_doors_func2(struct work_struct *work)
+{
+	__lp_block_doors_func(work, __func__, "(2)");
+}
+
 static struct klp_func test_klp_speaker_funcs[] = {
 	{
 		.old_name = "speaker_welcome",
 		.new_func = lp_speaker_welcome,
 	},
+	{
+		.old_name = "block_doors_func",
+		.new_func = lp_block_doors_func,
+	},
 	{ }
 };
 
@@ -193,6 +217,10 @@ static struct klp_func test_klp_speaker2_funcs[] = {
 		.old_name = "speaker_welcome",
 		.new_func = lp_speaker2_welcome,
 	},
+	{
+		.old_name = "block_doors_func",
+		.new_func = lp_block_doors_func2,
+	},
 	{ }
 };
 
-- 
2.47.1


  parent reply	other threads:[~2025-01-15  8:26 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-15  8:24 [PATCH v1 00/19] livepatch: Better integrate callbacks and shadow variables with the states API Petr Mladek
2025-01-15  8:24 ` [PATCH v1 01/19] livepatch: Add callbacks for introducing and removing states Petr Mladek
2025-01-15  8:24 ` [PATCH v1 02/19] livepatch: Allow to handle lifetime of shadow variables using the livepatch state Petr Mladek
2025-01-15  8:24 ` [PATCH v1 03/19] selftests/livepatch: Use per-state callbacks in state API tests Petr Mladek
2025-01-15  8:24 ` [PATCH v1 04/19] livepatch: Add "block_disable" flag to per-state API and remove versioning Petr Mladek
2025-01-15  8:24 ` [PATCH v1 05/19] livepatch: Remove "data" from struct klp_state Petr Mladek
2025-01-15  8:24 ` [PATCH v1 06/19] selftests/livepatch: Remove callbacks from sysfs interface testing Petr Mladek
2025-01-15  8:24 ` [PATCH v1 07/19] selftests/livepatch: Substitute hard-coded /sys/module path Petr Mladek
2025-01-15  8:24 ` [PATCH v1 08/19] selftests/livepatch: Move basic tests for livepatching modules Petr Mladek
2025-01-15  8:24 ` [PATCH v1 09/19] selftests/livepatch: Convert testing of multiple target modules Petr Mladek
2025-01-15  8:24 ` [PATCH v1 10/19] selftests/livepatch: Create a simple selftest for state callbacks Petr Mladek
2025-01-15  8:24 ` [PATCH v1 11/19] selftests/livepatch: Convert selftests for failing pre_patch callback Petr Mladek
2025-01-15  8:24 ` Petr Mladek [this message]
2025-01-15  8:24 ` [PATCH v1 13/19] selftests/livepatch: Add more tests for state callbacks with blocked transitions Petr Mladek
2025-01-15  8:24 ` [PATCH v1 14/19] selftests/livepatch: Convert selftests for testing callbacks with more livepatches Petr Mladek
2025-01-15  8:24 ` [PATCH v1 15/19] selftests/livepatch: Do not use a livepatch with the obsolete per-object callbacks in the basic selftests Petr Mladek
2025-01-15  8:24 ` [PATCH v1 16/19] selftests/livepatch: Remove obsolete test modules for per-object callbacks Petr Mladek
2025-01-15  8:24 ` [PATCH v1 17/19] samples/livepatch: Replace sample module with obsolete " Petr Mladek
2025-01-15  8:24 ` [PATCH v1 18/19] Documentation/livepatch: Update documentation for state, callbacks, and shadow variables Petr Mladek
2025-03-06 22:54   ` Joe Lawrence
2025-03-07 12:26     ` Petr Mladek
2025-03-07 15:50       ` Joe Lawrence
2025-03-17 11:17         ` Petr Mladek
2025-01-15  8:24 ` [PATCH v1 19/19] livepatch: Remove obsolete per-object callbacks Petr Mladek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250115082431.5550-13-pmladek@suse.com \
    --to=pmladek@suse.com \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --cc=nstange@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox