linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together
@ 2023-11-10 17:04 Petr Mladek
  2023-11-10 17:04 ` [POC 1/7] livepatch: Add callbacks for introducing and removing states Petr Mladek
                   ` (7 more replies)
  0 siblings, 8 replies; 12+ messages in thread
From: Petr Mladek @ 2023-11-10 17:04 UTC (permalink / raw)
  To: Josh Poimboeuf, Miroslav Benes
  Cc: Joe Lawrence, Nicolai Stange, live-patching, linux-kernel, Petr Mladek

This POC is a material for the discussion "Simplify Livepatch Callbacks,
Shadow Variables, and States handling" at LPC 2013, see
https://lpc.events/event/17/contributions/1541/

It obsoletes the patchset adding the garbage collection of shadow
variables. This new solution is based on ideas from Nicolai Stange.
And it should also be in sync with Josh's ideas mentioned into
the thread about the garbage collection, see
https://lore.kernel.org/r/20230204235910.4j4ame5ntqogqi7m@treble

What is this all about?

There are three features provided by the kernel livepatching support:

  + callbacks:

       They allow doing system modifications where the "simple"
       redirection to the fixed code is not enough. For example,
       allocate some data and allow to use them when all processes
       are patched.

       There are four optional callbacks which might be called
       either when the livepatch or the livepatched object is loaded.
       It depends who is loaded first.

       The are called at different stages of the livepatch transition:
       pre_enable, post_enable, pre_disable, post_disable.

       Only callbacks from the new livepatch are called during atomic
       replace. The motivation was that new livepatches should know
       how to handle the existing changes correctly. Also it
       simplified the semantic because it would be horrible when
       both callbacks from the old and new livepatch are called.
       The later one might break changes done by the earlier one.

       They are defined per-object. The idea was that they might
       be needed when a livepatched module is loaded or unloaded.


   + shadow variables:

      They allow attaching extra data to any existing data.
      For example, they allow to extend a structure. Or they
      allow to create a spin lock which might stay even
      when the livepatch gets atomically replaced.

      They are defined per-patch but there is no real connection.
      There is just an allocate/get/free API.


   + states:

      They were introduced to manage the life-cycle of changes
      done by the callbacks and shadow variables.

      They should help especially when atomic replace is used.
      The new livepatch need to know what changes have already
      been done or which need to be reverted.

      The states are defined per-patch. There was proposal
      to make them per-object but it was decided that it
      was not worth the complexity.

      Each state might have a version which allows to maintain
      compatibility between the livepatches. Otherwise, there
      is no connection with the other features. The is just an API
      to check whether the state was in the previous patch so that
      the callbacks might do an informed decisions.


Observation:

   + States were supposed to help with the life-time of changes
     done by callbacks. But states are per-patch and callbacks
     are per-object. Also the API is hard to use.

   + Shadow variables were not connected with the states at all.
     It needs to be done by callbacks.

   + The decision that only the callbacks from the new livepatch
     gets called during atomic replace make downgrades complicated.


Better solution implemented by this POC:

   + Transform per-object callbacks to per-state callbacks
     so that the state might really control the life-cycle
     of the changes.

   + Change the semantic of the callbacks, so that they
     are called when the state is introduced or removed.

     No callbacks are called when the state is just transferred
     during the atomic replace.


   + The disable/remove callbacks from the old livepatch are
     called from the old livepatch when the new one does
     not support them.

     These callbacks have to be there anyway so that the livepatch
     can get disabled.

     This nicely solves the problem with downgrades while keeping
     simple semantic.


   + A state might be associated with a shadow variable with
     the same ID.

     It helps to maintain the life-cycle of the shadow variable.

     The variable is automatically freed when the state is not longer
     supported during atomic replace or when the livepatch gets disabled.

     Also the state callbacks might help to allocate the variable
     do do some checks before the transition starts. But it can
     be enabled only after all processes are transitioned.

     It would prevent loading the livepatch when the shadow variable
     could not be used and the livepatch could cause problems.


   + State version is replaced with "block_disable" flag.

     The versions are too generic and make things complicated.

     In practice, the main question is whether the changes introduced
     by the state (callbacks) can be reverted or not. The livepatch
     could not be disabled or downgraded when the revert (state disable)
     is not supported.


What is done in this POC:

   + All changes in livepatch code are implemented.
   + The existing selftests are migrated [*]


What is missing:

   + The documentation is not updated.
   + More selftest might be needed [**]


[*] There is some mystery in a selftest when the migration gets
    blocked, see the comments in the 5th patch.

[**] In fact, many selftests would deserve some cleanup and 
     better split into categories.


Petr Mladek (7):
  livepatch: Add callbacks for introducing and removing states
  livepatch: Allow to handle lifetime of shadow variables using the
    livepatch state
  livepatch: Use per-state callbacks in state API tests
  livepatch: Do not use callbacks when testing sysfs interface
  livepatch: Convert klp module callbacks tests into livepatch module
    tests
  livepatch: Remove the obsolete per-object callbacks
  livepatching: Remove per-state version

 Documentation/livepatch/callbacks.rst         | 133 -----
 Documentation/livepatch/index.rst             |   1 -
 include/linux/livepatch.h                     |  75 ++-
 kernel/livepatch/core.c                       |  61 +-
 kernel/livepatch/core.h                       |  33 --
 kernel/livepatch/state.c                      | 151 ++++-
 kernel/livepatch/state.h                      |  10 +
 kernel/livepatch/transition.c                 |  13 +-
 lib/livepatch/Makefile                        |   8 +-
 lib/livepatch/test_klp_callbacks_busy.c       |  70 ---
 lib/livepatch/test_klp_callbacks_demo.c       | 121 ----
 lib/livepatch/test_klp_callbacks_demo2.c      |  93 ---
 lib/livepatch/test_klp_callbacks_mod.c        |  24 -
 lib/livepatch/test_klp_speaker.c              | 185 ++++++
 lib/livepatch/test_klp_speaker_livepatch.c    | 253 ++++++++
 lib/livepatch/test_klp_state.c                | 178 ++++--
 lib/livepatch/test_klp_state2.c               | 190 +-----
 lib/livepatch/test_klp_state3.c               |   2 +-
 samples/livepatch/Makefile                    |   3 -
 .../livepatch/livepatch-callbacks-busymod.c   |  60 --
 samples/livepatch/livepatch-callbacks-demo.c  | 196 -------
 samples/livepatch/livepatch-callbacks-mod.c   |  41 --
 tools/testing/selftests/livepatch/Makefile    |   2 +-
 .../testing/selftests/livepatch/functions.sh  |  46 ++
 .../selftests/livepatch/test-callbacks.sh     | 553 ------------------
 .../selftests/livepatch/test-modules.sh       | 539 +++++++++++++++++
 .../testing/selftests/livepatch/test-state.sh |  80 ++-
 .../testing/selftests/livepatch/test-sysfs.sh |  48 +-
 28 files changed, 1436 insertions(+), 1733 deletions(-)
 delete mode 100644 Documentation/livepatch/callbacks.rst
 delete mode 100644 lib/livepatch/test_klp_callbacks_busy.c
 delete mode 100644 lib/livepatch/test_klp_callbacks_demo.c
 delete mode 100644 lib/livepatch/test_klp_callbacks_demo2.c
 delete mode 100644 lib/livepatch/test_klp_callbacks_mod.c
 create mode 100644 lib/livepatch/test_klp_speaker.c
 create mode 100644 lib/livepatch/test_klp_speaker_livepatch.c
 delete mode 100644 samples/livepatch/livepatch-callbacks-busymod.c
 delete mode 100644 samples/livepatch/livepatch-callbacks-demo.c
 delete mode 100644 samples/livepatch/livepatch-callbacks-mod.c
 delete mode 100755 tools/testing/selftests/livepatch/test-callbacks.sh
 create mode 100755 tools/testing/selftests/livepatch/test-modules.sh

-- 
2.35.3


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [POC 1/7] livepatch: Add callbacks for introducing and removing states
  2023-11-10 17:04 [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Petr Mladek
@ 2023-11-10 17:04 ` Petr Mladek
  2023-11-11  0:54   ` kernel test robot
  2023-11-11  3:19   ` kernel test robot
  2023-11-10 17:04 ` [POC 2/7] livepatch: Allow to handle lifetime of shadow variables using the livepatch state Petr Mladek
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 12+ messages in thread
From: Petr Mladek @ 2023-11-10 17:04 UTC (permalink / raw)
  To: Josh Poimboeuf, Miroslav Benes
  Cc: Joe Lawrence, Nicolai Stange, live-patching, linux-kernel, Petr Mladek

The basic livepatch functionality is to redirect problematic functions
to a fixed or improved variants. In addition, there are two features
helping with more problematic situations:

  + pre_patch(), post_patch(), pre_unpatch(), post_unpatch() callbacks
    might be called before and after the respective transitions.
    For example, post_patch() callback might enable some functionality
    at the end of the transition when the entire system is using
    the new code.

  + Shadow variables allow to add new items into structures or other
    data objects.

The practice has shown that these features were hard to use with the atomic
replace feature. The new livepatch usually just adds more fixes. But it
might also remove problematic ones.

Originally, any version of the livepatch was allowed to replace any older
or newer version of the patch. It was not clear how to handle the extra
features. The new patch did not know whether to run the callbacks or
if the changes were already done by the current livepatch. Or if it has
to revert some changes or free shadow variables whey they would not longer
be supported.

It was even more complicated because only the callbacks from the newly
installed livepatch were called. It means that older livepatch might
not be able to revert changes supported only by newer livepatches.

The above problems were supposed to be solved by adding livepatch
states. Each livepatch might define which states are supported. The states
are versioned. The livepatch core checks if the newly installed livepatch
is able to handle all states used by the currently installed livepatch.

Though the practice has shown that the states API was not easy to use
either. It was not well connected with the callbacks and shadow variables.
The states are per-patch. The callbacks are per-object. The livepatch
does not know about the supported shadow variables at all.

As a first step, new per-state callbacks are introduced:

  + "setup" is called before the livepatch is applied but only when
      the state is new.

      It might be used to allocate some memory. Or it might
      check if the state change is safe on the running system.

      If it fails, the patch will not be enabled.

  + "enable" is called after the livepatch is applied but only when
      the state is new.

      It might be used to enable using some functionality provided by
      the livepatch after the entire system is livepatched.

  + "disable" is called before the livepatch is disabled or replaced.

      When replacing, the callback is called only when the new livepatch
      does not support the related state. And it uses the implementation
      from the to-be-replaced livepatch. The to-be-replaced livepatch
      needed the callback to allow disabling the livepatch anyway.
      The new livepatch does not need to know anything about the state.

      It might be used to disable using some functionality which will
      not longer be supported after the livepatch gets disabled.

  + "release" is called after the livepatch was disabled or replaced.
     There are the same rules for replacement as for "disable" callback.

      It might be used for freeing some memory or unused shadow variables.

These callbacks are going to replace the existing ones. It would cause
some changes:

   + The new callbacks are not called when a livepatched object is
     loaded or removed later.

     The practice shows that per-object callbacks are not worth
     supporting. In a rare case, when a per-object callback is needed.
     the livepatch might register a custom module notifier.

   + The new callbacks are called only when the state is introduced
     or removed. It does not handle the situation when the newly
     installed livepatch continues using an existing state.

     The practice shows that this is exactly what is needed. In the rare
     case when this is not enough, an extra takeover might be done in
     the module->init() callback.

The per-state callbacks are called in similar code paths as the per-object
ones. Especially, the ordering against the other operations is the same.
Though, there are some obvious and less obvious changes:

  + The per-state callbacks are called for the entire patch instead
    of loaded object. It means that they called outside the for-each-object
    cycle.

  + The per-state callbacks are called when a state is introduced
    or obsoleted. Both variants might happen when the atomic replace
    is used.

  + In __klp_enable_patch(), the per-state callbacks are called before
    the smp_wmb() while the per-object ones are called later.

    The new location makes more sense. The setup of the state should
    be ready before processes start being transferred.

    The per-object callbacks were called after the barrier. They were
    using and already existing for-cycle. And nobody did mind about
    the ordering.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 include/linux/livepatch.h     |  28 ++++++++
 kernel/livepatch/core.c       |  19 +++++-
 kernel/livepatch/state.c      | 118 ++++++++++++++++++++++++++++++++++
 kernel/livepatch/state.h      |   9 +++
 kernel/livepatch/transition.c |   8 +++
 5 files changed, 180 insertions(+), 2 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 9b9b38e89563..c2a39e5f5b66 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -129,15 +129,43 @@ struct klp_object {
 	bool patched;
 };
 
+struct klp_patch;
+struct klp_state;
+
+/**
+ * struct klp_state_callbacks - callbacks manipulating the state
+ * @setup:	executed before code patching when the state is added
+ * @enable:	executed after code patching when the state is added
+ * @disable:	executed before code unpatching when the state is removed
+ * @release:	executed after code unpatching when the state is removed
+ * @setup_succeeded: internal state used by a rollback on error
+ *
+ * All callbacks are optional.
+ *
+ * @setup callback returns 0 on success and an error code otherwise.
+ * Any error prevents enabling the livepatch. @disable() callbacks
+ * are then called to rollback @enable callbacks which has already
+ * succeeded before.
+ */
+struct klp_state_callbacks {
+	int (*setup)(struct klp_patch *patch, struct klp_state *state);
+	void (*enable)(struct klp_patch *patch, struct klp_state *state);
+	void (*disable)(struct klp_patch *patch, struct klp_state *state);
+	void (*release)(struct klp_patch *patch, struct klp_state *state);
+	bool setup_succeeded;
+};
+
 /**
  * struct klp_state - state of the system modified by the livepatch
  * @id:		system state identifier (non-zero)
  * @version:	version of the change
+ * @callbacks:	optional callbacks used when introducing or removing the state
  * @data:	custom data
  */
 struct klp_state {
 	unsigned long id;
 	unsigned int version;
+	struct klp_state_callbacks callbacks;
 	void *data;
 };
 
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 61328328c474..a4a3fe7907ad 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -975,6 +975,8 @@ static int __klp_disable_patch(struct klp_patch *patch)
 
 	klp_init_transition(patch, KLP_UNPATCHED);
 
+	klp_disable_states(patch);
+
 	klp_for_each_object(patch, obj)
 		if (obj->patched)
 			klp_pre_unpatch_callback(obj);
@@ -1010,6 +1012,13 @@ static int __klp_enable_patch(struct klp_patch *patch)
 
 	klp_init_transition(patch, KLP_PATCHED);
 
+	ret = klp_setup_states(patch);
+	if (ret)
+		goto err;
+
+	if (patch->replace)
+		klp_disable_obsolete_states(patch);
+
 	/*
 	 * Enforce the order of the func->transition writes in
 	 * klp_init_transition() and the ops->func_stack writes in
@@ -1027,14 +1036,14 @@ static int __klp_enable_patch(struct klp_patch *patch)
 		if (ret) {
 			pr_warn("pre-patch callback failed for object '%s'\n",
 				klp_is_module(obj) ? obj->name : "vmlinux");
-			goto err;
+			goto err_states;
 		}
 
 		ret = klp_patch_object(obj);
 		if (ret) {
 			pr_warn("failed to patch object '%s'\n",
 				klp_is_module(obj) ? obj->name : "vmlinux");
-			goto err;
+			goto err_states;
 		}
 	}
 
@@ -1043,6 +1052,12 @@ static int __klp_enable_patch(struct klp_patch *patch)
 	klp_try_complete_transition();
 
 	return 0;
+
+err_states:
+	if (patch->replace)
+		klp_enable_obsolete_states(patch);
+
+	klp_release_states(patch);
 err:
 	pr_warn("failed to enable patch '%s'\n", patch->mod->name);
 
diff --git a/kernel/livepatch/state.c b/kernel/livepatch/state.c
index 2565d039ade0..6693d808106b 100644
--- a/kernel/livepatch/state.c
+++ b/kernel/livepatch/state.c
@@ -117,3 +117,121 @@ bool klp_is_patch_compatible(struct klp_patch *patch)
 
 	return true;
 }
+
+bool is_state_in_other_patches(struct klp_patch *patch, struct klp_state *state)
+{
+	struct klp_patch *old_patch;
+	struct klp_state *old_state;
+
+	klp_for_each_patch(old_patch) {
+		if (old_patch == patch)
+			continue;
+
+		klp_for_each_state(old_patch, old_state) {
+			if (old_state->id == state->id)
+				return true;
+		}
+	}
+
+	return false;
+}
+
+int klp_setup_states(struct klp_patch *patch)
+{
+	struct klp_state *state;
+	int err;
+
+	klp_for_each_state(patch, state) {
+		if (!is_state_in_other_patches(patch, state) &&
+		    state->callbacks.setup) {
+
+			err = state->callbacks.setup(patch, state);
+			if (err)
+				goto err;
+		}
+
+		state->callbacks.setup_succeeded = true;
+	}
+
+	return 0;
+
+err:
+	klp_release_states(patch);
+	return err;
+}
+
+void klp_enable_states(struct klp_patch *patch)
+{
+	struct klp_state *state;
+
+	klp_for_each_state(patch, state) {
+		if (is_state_in_other_patches(patch, state))
+			continue;
+
+		if (!state->callbacks.enable)
+			continue;
+
+		state->callbacks.enable(patch, state);
+	}
+}
+
+void klp_disable_states(struct klp_patch *patch)
+{
+	struct klp_state *state;
+
+	klp_for_each_state(patch, state) {
+		if (is_state_in_other_patches(patch, state))
+			continue;
+
+		if (!state->callbacks.disable)
+			continue;
+
+		state->callbacks.disable(patch, state);
+	}
+}
+
+void klp_release_states(struct klp_patch *patch)
+{
+	struct klp_state *state;
+
+	klp_for_each_state(patch, state) {
+		if (is_state_in_other_patches(patch, state))
+			continue;
+
+		if (!state->callbacks.release)
+			continue;
+
+		if (state->callbacks.setup_succeeded)
+			state->callbacks.release(patch, state);
+	}
+}
+
+void klp_enable_obsolete_states(struct klp_patch *patch)
+{
+	struct klp_patch *old_patch;
+
+	klp_for_each_patch(old_patch) {
+		if (old_patch != patch)
+			klp_enable_states(old_patch);
+	}
+}
+
+void klp_disable_obsolete_states(struct klp_patch *patch)
+{
+	struct klp_patch *old_patch;
+
+	klp_for_each_patch(old_patch) {
+		if (old_patch != patch)
+			klp_disable_states(old_patch);
+	}
+}
+
+void klp_release_obsolete_states(struct klp_patch *patch)
+{
+	struct klp_patch *old_patch;
+
+	klp_for_each_patch(old_patch) {
+		if (old_patch != patch)
+			klp_release_states(old_patch);
+	}
+}
diff --git a/kernel/livepatch/state.h b/kernel/livepatch/state.h
index 49d9c16e8762..e9940e7f00dd 100644
--- a/kernel/livepatch/state.h
+++ b/kernel/livepatch/state.h
@@ -5,5 +5,14 @@
 #include <linux/livepatch.h>
 
 bool klp_is_patch_compatible(struct klp_patch *patch);
+int klp_setup_states(struct klp_patch *patch);
+void klp_enable_states(struct klp_patch *patch);
+void klp_disable_states(struct klp_patch *patch);
+void klp_release_states(struct klp_patch *patch);
+
+void klp_enable_obsolete_states(struct klp_patch *patch);
+void klp_disable_obsolete_states(struct klp_patch *patch);
+void klp_release_obsolete_states(struct klp_patch *patch);
+
 
 #endif /* _LIVEPATCH_STATE_H */
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
index e54c3d60a904..cfa1ab10feb7 100644
--- a/kernel/livepatch/transition.c
+++ b/kernel/livepatch/transition.c
@@ -12,6 +12,7 @@
 #include <linux/static_call.h>
 #include "core.h"
 #include "patch.h"
+#include "state.h"
 #include "transition.h"
 
 #define MAX_STACK_ENTRIES  100
@@ -101,6 +102,7 @@ static void klp_complete_transition(void)
 	if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) {
 		klp_unpatch_replaced_patches(klp_transition_patch);
 		klp_discard_nops(klp_transition_patch);
+		klp_release_obsolete_states(klp_transition_patch);
 	}
 
 	if (klp_target_state == KLP_UNPATCHED) {
@@ -140,6 +142,12 @@ static void klp_complete_transition(void)
 		task->patch_state = KLP_UNDEFINED;
 	}
 
+	if (klp_target_state == KLP_PATCHED) {
+		klp_enable_states(klp_transition_patch);
+	} else if (klp_target_state == KLP_UNPATCHED) {
+		klp_release_states(klp_transition_patch);
+	}
+
 	klp_for_each_object(klp_transition_patch, obj) {
 		if (!klp_is_object_loaded(obj))
 			continue;
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [POC 2/7] livepatch: Allow to handle lifetime of shadow variables using the livepatch state
  2023-11-10 17:04 [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Petr Mladek
  2023-11-10 17:04 ` [POC 1/7] livepatch: Add callbacks for introducing and removing states Petr Mladek
@ 2023-11-10 17:04 ` Petr Mladek
  2023-11-10 17:04 ` [POC 3/7] livepatch: Use per-state callbacks in state API tests Petr Mladek
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Petr Mladek @ 2023-11-10 17:04 UTC (permalink / raw)
  To: Josh Poimboeuf, Miroslav Benes
  Cc: Joe Lawrence, Nicolai Stange, live-patching, linux-kernel, Petr Mladek

The handling of the lifetime of the shadow variables is not easy
when the atomic replace is used. The new patch does not know
if a shadow variable has already been used by the previous livepatch.
Or if there is a shadow variable which will not longer be used.

Shadow variables are almost always used together with callbacks.
At least @post_unpatch callback is used to free not longer used shadow
variables. And sometimes @post_patch and @pre_unpatch callbacks
are used to enable or disable the use of the shadow variables.
It is needed when the shadow variable can be used only when
the entire system is able to handle them.

All this gets even more complicated because the original callbacks
are called only from the new livepatch when atomic replace is used.
Newly created livepatches might be able to handle obsolete shadow
variables so the upgrade would work. But older livepatches do not know
anything about later introduced shadow variables. They might leak
during downgrade. And they might contain outdated information when
another upgrade would start using them again.

All problems are better solved with the new callbacks associated with
a livepatch state. They are called when the state is first introduced
and when it gets obsolete. Also the callbacks are called from the patch
where the state was supported. So that even downgrade might be safe.

Let's make it official. A shadow variable might be associated with
a livepatch state by setting the new "state.is_shadow" flag and
using the same "id" in both struct klp_shadow and struct klp_state.

The shadow variable will then have the same lifetime as the livepatch
state. It allows to free obsolete shadow variables automatically
without the need to add a callback.

The generic callback will free the shadow variables using
state->callbacks.shadow_dtor callback when provided.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 include/linux/livepatch.h | 15 ++++++++++-----
 kernel/livepatch/state.c  | 14 ++++++++++----
 2 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index c2a39e5f5b66..189ec7c6a89f 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -132,12 +132,18 @@ struct klp_object {
 struct klp_patch;
 struct klp_state;
 
+typedef int (*klp_shadow_ctor_t)(void *obj,
+				 void *shadow_data,
+				 void *ctor_data);
+typedef void (*klp_shadow_dtor_t)(void *obj, void *shadow_data);
+
 /**
  * struct klp_state_callbacks - callbacks manipulating the state
  * @setup:	executed before code patching when the state is added
  * @enable:	executed after code patching when the state is added
  * @disable:	executed before code unpatching when the state is removed
  * @release:	executed after code unpatching when the state is removed
+ * @shadow_dtor: destructor for the related shadow variable
  * @setup_succeeded: internal state used by a rollback on error
  *
  * All callbacks are optional.
@@ -152,6 +158,7 @@ struct klp_state_callbacks {
 	void (*enable)(struct klp_patch *patch, struct klp_state *state);
 	void (*disable)(struct klp_patch *patch, struct klp_state *state);
 	void (*release)(struct klp_patch *patch, struct klp_state *state);
+	klp_shadow_dtor_t shadow_dtor;
 	bool setup_succeeded;
 };
 
@@ -160,12 +167,15 @@ struct klp_state_callbacks {
  * @id:		system state identifier (non-zero)
  * @version:	version of the change
  * @callbacks:	optional callbacks used when introducing or removing the state
+ * @is_shadow:  the state handles lifetime of a shadow variable
+ *		with the same @id
  * @data:	custom data
  */
 struct klp_state {
 	unsigned long id;
 	unsigned int version;
 	struct klp_state_callbacks callbacks;
+	bool is_shadow;
 	void *data;
 };
 
@@ -240,11 +250,6 @@ static inline bool klp_have_reliable_stack(void)
 	       IS_ENABLED(CONFIG_HAVE_RELIABLE_STACKTRACE);
 }
 
-typedef int (*klp_shadow_ctor_t)(void *obj,
-				 void *shadow_data,
-				 void *ctor_data);
-typedef void (*klp_shadow_dtor_t)(void *obj, void *shadow_data);
-
 void *klp_shadow_get(void *obj, unsigned long id);
 void *klp_shadow_alloc(void *obj, unsigned long id,
 		       size_t size, gfp_t gfp_flags,
diff --git a/kernel/livepatch/state.c b/kernel/livepatch/state.c
index 6693d808106b..4ec65afe3a43 100644
--- a/kernel/livepatch/state.c
+++ b/kernel/livepatch/state.c
@@ -198,11 +198,17 @@ void klp_release_states(struct klp_patch *patch)
 		if (is_state_in_other_patches(patch, state))
 			continue;
 
-		if (!state->callbacks.release)
-			continue;
-
-		if (state->callbacks.setup_succeeded)
+		if (state->callbacks.release && state->callbacks.setup_succeeded)
 			state->callbacks.release(patch, state);
+
+		if (state->is_shadow)
+			klp_shadow_free_all(state->id, state->callbacks.shadow_dtor);
+
+		/*
+		 * The @release callback is supposed to restore the original
+		 * state before the @setup callback was called.
+		 */
+		state->callbacks.setup_succeeded = 0;
 	}
 }
 
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [POC 3/7] livepatch: Use per-state callbacks in state API tests
  2023-11-10 17:04 [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Petr Mladek
  2023-11-10 17:04 ` [POC 1/7] livepatch: Add callbacks for introducing and removing states Petr Mladek
  2023-11-10 17:04 ` [POC 2/7] livepatch: Allow to handle lifetime of shadow variables using the livepatch state Petr Mladek
@ 2023-11-10 17:04 ` Petr Mladek
  2023-11-10 17:04 ` [POC 4/7] livepatch: Do not use callbacks when testing sysfs interface Petr Mladek
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Petr Mladek @ 2023-11-10 17:04 UTC (permalink / raw)
  To: Josh Poimboeuf, Miroslav Benes
  Cc: Joe Lawrence, Nicolai Stange, live-patching, linux-kernel, Petr Mladek

Recent changes in the livepatch core have allowed to connect states,
shadow variables, and callbacks. Use these new features in
the state tests.

Use the shadow variable API to store the original loglevel. It is
better suited for this purpose than directly accessing the .data
pointer in state klp_state.

Another big advantage is that the shadow variable is preserved
when the current patch is replaced by a new version. As a result,
there is not need to copy the pointer.

Finally, the lifetime of the shadow variable is connected with
the lifetime of the state. It is freed automatically when
it is not longer supported.

This results into the following changes in the code:

  + Rename CONSOLE_LOGLEVEL_STATE -> CONSOLE_LOGLEVEL_FIX_ID
    because it will be used also the for shadow variable

  + Remove the extra code for module coming and going states
    because the new callback are per-state.

  + Remove callbacks needed to transfer the pointer between
    states.

  + Keep the versioning of the state to prevent downgrade.
    The problem is artificial because no callbacks are
    needed to transfer or free the shadow variable anymore.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 lib/livepatch/test_klp_state.c                | 113 +++++------
 lib/livepatch/test_klp_state2.c               | 190 +-----------------
 .../testing/selftests/livepatch/test-state.sh |  44 ++--
 3 files changed, 76 insertions(+), 271 deletions(-)

diff --git a/lib/livepatch/test_klp_state.c b/lib/livepatch/test_klp_state.c
index 57a4253acb01..b3d1ee48dfcc 100644
--- a/lib/livepatch/test_klp_state.c
+++ b/lib/livepatch/test_klp_state.c
@@ -9,108 +9,109 @@
 #include <linux/printk.h>
 #include <linux/livepatch.h>
 
-#define CONSOLE_LOGLEVEL_STATE 1
-/* Version 1 does not support migration. */
-#define CONSOLE_LOGLEVEL_STATE_VERSION 1
+#define CONSOLE_LOGLEVEL_FIX_ID 1
 
-static const char *const module_state[] = {
-	[MODULE_STATE_LIVE]	= "[MODULE_STATE_LIVE] Normal state",
-	[MODULE_STATE_COMING]	= "[MODULE_STATE_COMING] Full formed, running module_init",
-	[MODULE_STATE_GOING]	= "[MODULE_STATE_GOING] Going away",
-	[MODULE_STATE_UNFORMED]	= "[MODULE_STATE_UNFORMED] Still setting it up",
-};
-
-static void callback_info(const char *callback, struct klp_object *obj)
-{
-	if (obj->mod)
-		pr_info("%s: %s -> %s\n", callback, obj->mod->name,
-			module_state[obj->mod->state]);
-	else
-		pr_info("%s: vmlinux\n", callback);
-}
+/*
+ * Version of the state which defines compatibility of livepaches.
+ * The value is artificial. It set just for testing the compatibility
+ * checks. In reality, all versions are compatible because all
+ * the callbacks do nothing and the shadow variable clean up
+ * is done by the core.
+ */
+#ifndef CONSOLE_LOGLEVEL_FIX_VERSION
+#define CONSOLE_LOGLEVEL_FIX_VERSION 1
+#endif
 
 static struct klp_patch patch;
 
 static int allocate_loglevel_state(void)
 {
-	struct klp_state *loglevel_state;
+	int *shadow_console_loglevel;
 
-	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
-	if (!loglevel_state)
-		return -EINVAL;
+	/* Make sure that the shadow variable does not exist yet. */
+	shadow_console_loglevel =
+		klp_shadow_alloc(&console_loglevel, CONSOLE_LOGLEVEL_FIX_ID,
+				 sizeof(*shadow_console_loglevel), GFP_KERNEL,
+				 NULL, NULL);
 
-	loglevel_state->data = kzalloc(sizeof(console_loglevel), GFP_KERNEL);
-	if (!loglevel_state->data)
+	if (!shadow_console_loglevel) {
+		pr_err("%s: failed to allocated shadow variable for storing original loglevel\n",
+		       __func__);
 		return -ENOMEM;
+	}
 
 	pr_info("%s: allocating space to store console_loglevel\n",
 		__func__);
+
 	return 0;
 }
 
 static void fix_console_loglevel(void)
 {
-	struct klp_state *loglevel_state;
+	int *shadow_console_loglevel;
 
-	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
-	if (!loglevel_state)
+	shadow_console_loglevel =
+		(int *)klp_shadow_get(&console_loglevel, CONSOLE_LOGLEVEL_FIX_ID);
+	if (!shadow_console_loglevel)
 		return;
 
 	pr_info("%s: fixing console_loglevel\n", __func__);
-	*(int *)loglevel_state->data = console_loglevel;
+	*shadow_console_loglevel = console_loglevel;
 	console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
 }
 
 static void restore_console_loglevel(void)
 {
-	struct klp_state *loglevel_state;
+	int *shadow_console_loglevel;
 
-	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
-	if (!loglevel_state)
+	shadow_console_loglevel =
+		(int *)klp_shadow_get(&console_loglevel, CONSOLE_LOGLEVEL_FIX_ID);
+	if (!shadow_console_loglevel)
 		return;
 
 	pr_info("%s: restoring console_loglevel\n", __func__);
-	console_loglevel = *(int *)loglevel_state->data;
+	console_loglevel = *shadow_console_loglevel;
 }
 
 static void free_loglevel_state(void)
 {
-	struct klp_state *loglevel_state;
+	int *shadow_console_loglevel;
 
-	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
-	if (!loglevel_state)
+	shadow_console_loglevel =
+		(int *)klp_shadow_get(&console_loglevel, CONSOLE_LOGLEVEL_FIX_ID);
+	if (!shadow_console_loglevel)
 		return;
 
 	pr_info("%s: freeing space for the stored console_loglevel\n",
 		__func__);
-	kfree(loglevel_state->data);
+	klp_shadow_free(&console_loglevel, CONSOLE_LOGLEVEL_FIX_ID, NULL);
 }
 
-/* Executed on object patching (ie, patch enablement) */
-static int pre_patch_callback(struct klp_object *obj)
+/* Executed before patching when the state is new. */
+static int setup_state_callback(struct klp_patch *patch, struct klp_state *state)
 {
-	callback_info(__func__, obj);
+	pr_info("%s: state %lu\n", __func__, state->id);
 	return allocate_loglevel_state();
 }
 
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_patch_callback(struct klp_object *obj)
+/* Executed after patching when the state is new. */
+static void enable_state_callback(struct klp_patch *patch, struct klp_state *state)
 {
-	callback_info(__func__, obj);
+	pr_info("%s: state %lu\n", __func__, state->id);
 	fix_console_loglevel();
 }
 
-/* Executed on object unpatching (ie, patch disablement) */
-static void pre_unpatch_callback(struct klp_object *obj)
+/* Executed before unpatching when the state is obsoleted. */
+static void disable_state_callback(struct klp_patch *patch, struct klp_state *state)
 {
-	callback_info(__func__, obj);
+	pr_info("%s: state %lu\n", __func__, state->id);
 	restore_console_loglevel();
 }
 
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_unpatch_callback(struct klp_object *obj)
+/* Executed after unpatching when the state is obsoleted. */
+static void release_state_callback(struct klp_patch *patch, struct klp_state *state)
 {
-	callback_info(__func__, obj);
+	pr_info("%s: state %lu\n", __func__, state->id);
 	free_loglevel_state();
 }
 
@@ -122,19 +123,19 @@ static struct klp_object objs[] = {
 	{
 		.name = NULL,	/* vmlinux */
 		.funcs = no_funcs,
-		.callbacks = {
-			.pre_patch = pre_patch_callback,
-			.post_patch = post_patch_callback,
-			.pre_unpatch = pre_unpatch_callback,
-			.post_unpatch = post_unpatch_callback,
-		},
 	}, { }
 };
 
 static struct klp_state states[] = {
 	{
-		.id = CONSOLE_LOGLEVEL_STATE,
-		.version = CONSOLE_LOGLEVEL_STATE_VERSION,
+		.id = CONSOLE_LOGLEVEL_FIX_ID,
+		.version = CONSOLE_LOGLEVEL_FIX_VERSION,
+		.callbacks = {
+			.setup = setup_state_callback,
+			.enable = enable_state_callback,
+			.disable = disable_state_callback,
+			.release = release_state_callback,
+		},
 	}, { }
 };
 
diff --git a/lib/livepatch/test_klp_state2.c b/lib/livepatch/test_klp_state2.c
index c978ea4d5e67..128855764bf8 100644
--- a/lib/livepatch/test_klp_state2.c
+++ b/lib/livepatch/test_klp_state2.c
@@ -1,191 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 // Copyright (C) 2019 SUSE
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define CONSOLE_LOGLEVEL_FIX_VERSION 2
 
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/printk.h>
-#include <linux/livepatch.h>
-
-#define CONSOLE_LOGLEVEL_STATE 1
-/* Version 2 supports migration. */
-#define CONSOLE_LOGLEVEL_STATE_VERSION 2
-
-static const char *const module_state[] = {
-	[MODULE_STATE_LIVE]	= "[MODULE_STATE_LIVE] Normal state",
-	[MODULE_STATE_COMING]	= "[MODULE_STATE_COMING] Full formed, running module_init",
-	[MODULE_STATE_GOING]	= "[MODULE_STATE_GOING] Going away",
-	[MODULE_STATE_UNFORMED]	= "[MODULE_STATE_UNFORMED] Still setting it up",
-};
-
-static void callback_info(const char *callback, struct klp_object *obj)
-{
-	if (obj->mod)
-		pr_info("%s: %s -> %s\n", callback, obj->mod->name,
-			module_state[obj->mod->state]);
-	else
-		pr_info("%s: vmlinux\n", callback);
-}
-
-static struct klp_patch patch;
-
-static int allocate_loglevel_state(void)
-{
-	struct klp_state *loglevel_state, *prev_loglevel_state;
-
-	prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
-	if (prev_loglevel_state) {
-		pr_info("%s: space to store console_loglevel already allocated\n",
-		__func__);
-		return 0;
-	}
-
-	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
-	if (!loglevel_state)
-		return -EINVAL;
-
-	loglevel_state->data = kzalloc(sizeof(console_loglevel), GFP_KERNEL);
-	if (!loglevel_state->data)
-		return -ENOMEM;
-
-	pr_info("%s: allocating space to store console_loglevel\n",
-		__func__);
-	return 0;
-}
-
-static void fix_console_loglevel(void)
-{
-	struct klp_state *loglevel_state, *prev_loglevel_state;
-
-	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
-	if (!loglevel_state)
-		return;
-
-	prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
-	if (prev_loglevel_state) {
-		pr_info("%s: taking over the console_loglevel change\n",
-		__func__);
-		loglevel_state->data = prev_loglevel_state->data;
-		return;
-	}
-
-	pr_info("%s: fixing console_loglevel\n", __func__);
-	*(int *)loglevel_state->data = console_loglevel;
-	console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
-}
-
-static void restore_console_loglevel(void)
-{
-	struct klp_state *loglevel_state, *prev_loglevel_state;
-
-	prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
-	if (prev_loglevel_state) {
-		pr_info("%s: passing the console_loglevel change back to the old livepatch\n",
-		__func__);
-		return;
-	}
-
-	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
-	if (!loglevel_state)
-		return;
-
-	pr_info("%s: restoring console_loglevel\n", __func__);
-	console_loglevel = *(int *)loglevel_state->data;
-}
-
-static void free_loglevel_state(void)
-{
-	struct klp_state *loglevel_state, *prev_loglevel_state;
-
-	prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
-	if (prev_loglevel_state) {
-		pr_info("%s: keeping space to store console_loglevel\n",
-		__func__);
-		return;
-	}
-
-	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
-	if (!loglevel_state)
-		return;
-
-	pr_info("%s: freeing space for the stored console_loglevel\n",
-		__func__);
-	kfree(loglevel_state->data);
-}
-
-/* Executed on object patching (ie, patch enablement) */
-static int pre_patch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-	return allocate_loglevel_state();
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_patch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-	fix_console_loglevel();
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void pre_unpatch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-	restore_console_loglevel();
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_unpatch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-	free_loglevel_state();
-}
-
-static struct klp_func no_funcs[] = {
-	{}
-};
-
-static struct klp_object objs[] = {
-	{
-		.name = NULL,	/* vmlinux */
-		.funcs = no_funcs,
-		.callbacks = {
-			.pre_patch = pre_patch_callback,
-			.post_patch = post_patch_callback,
-			.pre_unpatch = pre_unpatch_callback,
-			.post_unpatch = post_unpatch_callback,
-		},
-	}, { }
-};
-
-static struct klp_state states[] = {
-	{
-		.id = CONSOLE_LOGLEVEL_STATE,
-		.version = CONSOLE_LOGLEVEL_STATE_VERSION,
-	}, { }
-};
-
-static struct klp_patch patch = {
-	.mod = THIS_MODULE,
-	.objs = objs,
-	.states = states,
-	.replace = true,
-};
-
-static int test_klp_callbacks_demo_init(void)
-{
-	return klp_enable_patch(&patch);
-}
-
-static void test_klp_callbacks_demo_exit(void)
-{
-}
-
-module_init(test_klp_callbacks_demo_init);
-module_exit(test_klp_callbacks_demo_exit);
-MODULE_LICENSE("GPL");
-MODULE_INFO(livepatch, "Y");
-MODULE_AUTHOR("Petr Mladek <pmladek@suse.com>");
-MODULE_DESCRIPTION("Livepatch test: system state modification");
+/* The console loglevel fix is the same in the next cumulative patch. */
+#include "test_klp_state.c"
diff --git a/tools/testing/selftests/livepatch/test-state.sh b/tools/testing/selftests/livepatch/test-state.sh
index 38656721c958..a3c933ea96fc 100755
--- a/tools/testing/selftests/livepatch/test-state.sh
+++ b/tools/testing/selftests/livepatch/test-state.sh
@@ -22,20 +22,20 @@ unload_lp $MOD_LIVEPATCH
 check_result "% modprobe $MOD_LIVEPATCH
 livepatch: enabling patch '$MOD_LIVEPATCH'
 livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+$MOD_LIVEPATCH: setup_state_callback: state 1
 $MOD_LIVEPATCH: allocate_loglevel_state: allocating space to store console_loglevel
 livepatch: '$MOD_LIVEPATCH': starting patching transition
 livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
+$MOD_LIVEPATCH: enable_state_callback: state 1
 $MOD_LIVEPATCH: fix_console_loglevel: fixing console_loglevel
 livepatch: '$MOD_LIVEPATCH': patching complete
 % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
 livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
+$MOD_LIVEPATCH: disable_state_callback: state 1
 $MOD_LIVEPATCH: restore_console_loglevel: restoring console_loglevel
 livepatch: '$MOD_LIVEPATCH': starting unpatching transition
 livepatch: '$MOD_LIVEPATCH': completing unpatching transition
-$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+$MOD_LIVEPATCH: release_state_callback: state 1
 $MOD_LIVEPATCH: free_loglevel_state: freeing space for the stored console_loglevel
 livepatch: '$MOD_LIVEPATCH': unpatching complete
 % rmmod $MOD_LIVEPATCH"
@@ -54,31 +54,27 @@ unload_lp $MOD_LIVEPATCH2
 check_result "% modprobe $MOD_LIVEPATCH
 livepatch: enabling patch '$MOD_LIVEPATCH'
 livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+$MOD_LIVEPATCH: setup_state_callback: state 1
 $MOD_LIVEPATCH: allocate_loglevel_state: allocating space to store console_loglevel
 livepatch: '$MOD_LIVEPATCH': starting patching transition
 livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
+$MOD_LIVEPATCH: enable_state_callback: state 1
 $MOD_LIVEPATCH: fix_console_loglevel: fixing console_loglevel
 livepatch: '$MOD_LIVEPATCH': patching complete
 % modprobe $MOD_LIVEPATCH2
 livepatch: enabling patch '$MOD_LIVEPATCH2'
 livepatch: '$MOD_LIVEPATCH2': initializing patching transition
-$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
-$MOD_LIVEPATCH2: allocate_loglevel_state: space to store console_loglevel already allocated
 livepatch: '$MOD_LIVEPATCH2': starting patching transition
 livepatch: '$MOD_LIVEPATCH2': completing patching transition
-$MOD_LIVEPATCH2: post_patch_callback: vmlinux
-$MOD_LIVEPATCH2: fix_console_loglevel: taking over the console_loglevel change
 livepatch: '$MOD_LIVEPATCH2': patching complete
 % rmmod $MOD_LIVEPATCH
 % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
 livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
-$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: disable_state_callback: state 1
 $MOD_LIVEPATCH2: restore_console_loglevel: restoring console_loglevel
 livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
 livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
-$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: release_state_callback: state 1
 $MOD_LIVEPATCH2: free_loglevel_state: freeing space for the stored console_loglevel
 livepatch: '$MOD_LIVEPATCH2': unpatching complete
 % rmmod $MOD_LIVEPATCH2"
@@ -99,41 +95,33 @@ unload_lp $MOD_LIVEPATCH3
 check_result "% modprobe $MOD_LIVEPATCH2
 livepatch: enabling patch '$MOD_LIVEPATCH2'
 livepatch: '$MOD_LIVEPATCH2': initializing patching transition
-$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
+$MOD_LIVEPATCH2: setup_state_callback: state 1
 $MOD_LIVEPATCH2: allocate_loglevel_state: allocating space to store console_loglevel
 livepatch: '$MOD_LIVEPATCH2': starting patching transition
 livepatch: '$MOD_LIVEPATCH2': completing patching transition
-$MOD_LIVEPATCH2: post_patch_callback: vmlinux
+$MOD_LIVEPATCH2: enable_state_callback: state 1
 $MOD_LIVEPATCH2: fix_console_loglevel: fixing console_loglevel
 livepatch: '$MOD_LIVEPATCH2': patching complete
 % modprobe $MOD_LIVEPATCH3
 livepatch: enabling patch '$MOD_LIVEPATCH3'
 livepatch: '$MOD_LIVEPATCH3': initializing patching transition
-$MOD_LIVEPATCH3: pre_patch_callback: vmlinux
-$MOD_LIVEPATCH3: allocate_loglevel_state: space to store console_loglevel already allocated
 livepatch: '$MOD_LIVEPATCH3': starting patching transition
 livepatch: '$MOD_LIVEPATCH3': completing patching transition
-$MOD_LIVEPATCH3: post_patch_callback: vmlinux
-$MOD_LIVEPATCH3: fix_console_loglevel: taking over the console_loglevel change
 livepatch: '$MOD_LIVEPATCH3': patching complete
 % rmmod $MOD_LIVEPATCH2
 % modprobe $MOD_LIVEPATCH2
 livepatch: enabling patch '$MOD_LIVEPATCH2'
 livepatch: '$MOD_LIVEPATCH2': initializing patching transition
-$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
-$MOD_LIVEPATCH2: allocate_loglevel_state: space to store console_loglevel already allocated
 livepatch: '$MOD_LIVEPATCH2': starting patching transition
 livepatch: '$MOD_LIVEPATCH2': completing patching transition
-$MOD_LIVEPATCH2: post_patch_callback: vmlinux
-$MOD_LIVEPATCH2: fix_console_loglevel: taking over the console_loglevel change
 livepatch: '$MOD_LIVEPATCH2': patching complete
 % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
 livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
-$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: disable_state_callback: state 1
 $MOD_LIVEPATCH2: restore_console_loglevel: restoring console_loglevel
 livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
 livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
-$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: release_state_callback: state 1
 $MOD_LIVEPATCH2: free_loglevel_state: freeing space for the stored console_loglevel
 livepatch: '$MOD_LIVEPATCH2': unpatching complete
 % rmmod $MOD_LIVEPATCH2
@@ -152,11 +140,11 @@ unload_lp $MOD_LIVEPATCH2
 check_result "% modprobe $MOD_LIVEPATCH2
 livepatch: enabling patch '$MOD_LIVEPATCH2'
 livepatch: '$MOD_LIVEPATCH2': initializing patching transition
-$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
+$MOD_LIVEPATCH2: setup_state_callback: state 1
 $MOD_LIVEPATCH2: allocate_loglevel_state: allocating space to store console_loglevel
 livepatch: '$MOD_LIVEPATCH2': starting patching transition
 livepatch: '$MOD_LIVEPATCH2': completing patching transition
-$MOD_LIVEPATCH2: post_patch_callback: vmlinux
+$MOD_LIVEPATCH2: enable_state_callback: state 1
 $MOD_LIVEPATCH2: fix_console_loglevel: fixing console_loglevel
 livepatch: '$MOD_LIVEPATCH2': patching complete
 % modprobe $MOD_LIVEPATCH
@@ -164,11 +152,11 @@ livepatch: Livepatch patch ($MOD_LIVEPATCH) is not compatible with the already i
 modprobe: ERROR: could not insert '$MOD_LIVEPATCH': Invalid argument
 % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
 livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
-$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: disable_state_callback: state 1
 $MOD_LIVEPATCH2: restore_console_loglevel: restoring console_loglevel
 livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
 livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
-$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: release_state_callback: state 1
 $MOD_LIVEPATCH2: free_loglevel_state: freeing space for the stored console_loglevel
 livepatch: '$MOD_LIVEPATCH2': unpatching complete
 % rmmod $MOD_LIVEPATCH2"
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [POC 4/7] livepatch: Do not use callbacks when testing sysfs interface
  2023-11-10 17:04 [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Petr Mladek
                   ` (2 preceding siblings ...)
  2023-11-10 17:04 ` [POC 3/7] livepatch: Use per-state callbacks in state API tests Petr Mladek
@ 2023-11-10 17:04 ` Petr Mladek
  2023-11-10 17:04 ` [POC 5/7] livepatch: Convert klp module callbacks tests into livepatch module tests Petr Mladek
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Petr Mladek @ 2023-11-10 17:04 UTC (permalink / raw)
  To: Josh Poimboeuf, Miroslav Benes
  Cc: Joe Lawrence, Nicolai Stange, live-patching, linux-kernel, Petr Mladek

The per-object callbacks have been obsoleted by per-state callbacks.
As a result, the callbacks test modules have been obsoleted
by updated klp state tests.

The callbacks test modules are re-used in the sysfs selftests.
It would be possible to replace them by klp state test modules
but the newly generated logs are hard to review because
there is a lot of noise caused by the callbacks.

Instead, introduce a simple test module in a "Hello World"
style and corresponding livepatch. The expected log can be
reviewed easily.

The test module might be later extended to provide more functionality
which might be used in more tests. It would allow to create tests
focusing on some particular feature with an easier output.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 lib/livepatch/Makefile                        |  2 +
 lib/livepatch/test_klp_speaker.c              | 34 +++++++++++++
 lib/livepatch/test_klp_speaker_livepatch.c    | 50 +++++++++++++++++++
 .../testing/selftests/livepatch/test-sysfs.sh | 48 ++++++++----------
 4 files changed, 106 insertions(+), 28 deletions(-)
 create mode 100644 lib/livepatch/test_klp_speaker.c
 create mode 100644 lib/livepatch/test_klp_speaker_livepatch.c

diff --git a/lib/livepatch/Makefile b/lib/livepatch/Makefile
index dcc912b3478f..a8a5f6597633 100644
--- a/lib/livepatch/Makefile
+++ b/lib/livepatch/Makefile
@@ -9,6 +9,8 @@ obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
 				test_klp_callbacks_mod.o \
 				test_klp_livepatch.o \
 				test_klp_shadow_vars.o \
+				test_klp_speaker.o \
+				test_klp_speaker_livepatch.o \
 				test_klp_state.o \
 				test_klp_state2.o \
 				test_klp_state3.o
diff --git a/lib/livepatch/test_klp_speaker.c b/lib/livepatch/test_klp_speaker.c
new file mode 100644
index 000000000000..d2d31072639a
--- /dev/null
+++ b/lib/livepatch/test_klp_speaker.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2023 SUSE
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#ifndef _VER_NAME
+#define _VER_NAME(name) name
+#endif
+
+#include <linux/module.h>
+#include <linux/printk.h>
+
+noinline
+void speaker_welcome(void)
+{
+	pr_info("%s: Hello, World!\n", __func__);
+}
+
+static int test_klp_speaker_init(void)
+{
+	pr_info("%s\n", __func__);
+
+	return 0;
+}
+
+static void test_klp_speaker_exit(void)
+{
+	pr_info("%s\n", __func__);
+}
+
+module_init(test_klp_speaker_init);
+module_exit(test_klp_speaker_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Livepatch test: test functions");
diff --git a/lib/livepatch/test_klp_speaker_livepatch.c b/lib/livepatch/test_klp_speaker_livepatch.c
new file mode 100644
index 000000000000..0317a4937b78
--- /dev/null
+++ b/lib/livepatch/test_klp_speaker_livepatch.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include <linux/init.h>
+
+
+void livepatch_speaker_welcome(void)
+{
+	pr_info("%s: Ladies and gentleman, ...\n", __func__);
+}
+
+
+static struct klp_func test_klp_speaker_funcs[] = {
+	{
+		.old_name = "speaker_welcome",
+		.new_func = livepatch_speaker_welcome,
+	},
+	{ }
+};
+
+static struct klp_object objs[] = {
+	{
+		.name = "test_klp_speaker",
+		.funcs = test_klp_speaker_funcs,
+	},
+	{ }
+};
+
+static struct klp_patch patch = {
+	.mod = THIS_MODULE,
+	.objs = objs,
+};
+
+static int test_klp_speaker_livepatch_init(void)
+{
+	return klp_enable_patch(&patch);
+}
+
+static void test_klp_speaker_livepatch_exit(void)
+{
+}
+
+module_init(test_klp_speaker_livepatch_init);
+module_exit(test_klp_speaker_livepatch_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
diff --git a/tools/testing/selftests/livepatch/test-sysfs.sh b/tools/testing/selftests/livepatch/test-sysfs.sh
index 7f76f280189a..424b6af32c99 100755
--- a/tools/testing/selftests/livepatch/test-sysfs.sh
+++ b/tools/testing/selftests/livepatch/test-sysfs.sh
@@ -42,8 +42,8 @@ livepatch: '$MOD_LIVEPATCH': unpatching complete
 
 start_test "sysfs test object/patched"
 
-MOD_LIVEPATCH=test_klp_callbacks_demo
-MOD_TARGET=test_klp_callbacks_mod
+MOD_LIVEPATCH=test_klp_speaker_livepatch
+MOD_TARGET=test_klp_speaker
 load_lp $MOD_LIVEPATCH
 
 # check the "patch" file changes as target module loads/unloads
@@ -56,31 +56,23 @@ check_sysfs_value  "$MOD_LIVEPATCH" "$MOD_TARGET/patched" "0"
 disable_lp $MOD_LIVEPATCH
 unload_lp $MOD_LIVEPATCH
 
-check_result "% modprobe test_klp_callbacks_demo
-livepatch: enabling patch 'test_klp_callbacks_demo'
-livepatch: 'test_klp_callbacks_demo': initializing patching transition
-test_klp_callbacks_demo: pre_patch_callback: vmlinux
-livepatch: 'test_klp_callbacks_demo': starting patching transition
-livepatch: 'test_klp_callbacks_demo': completing patching transition
-test_klp_callbacks_demo: post_patch_callback: vmlinux
-livepatch: 'test_klp_callbacks_demo': patching complete
-% modprobe test_klp_callbacks_mod
-livepatch: applying patch 'test_klp_callbacks_demo' to loading module 'test_klp_callbacks_mod'
-test_klp_callbacks_demo: pre_patch_callback: test_klp_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
-test_klp_callbacks_demo: post_patch_callback: test_klp_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
-test_klp_callbacks_mod: test_klp_callbacks_mod_init
-% rmmod test_klp_callbacks_mod
-test_klp_callbacks_mod: test_klp_callbacks_mod_exit
-test_klp_callbacks_demo: pre_unpatch_callback: test_klp_callbacks_mod -> [MODULE_STATE_GOING] Going away
-livepatch: reverting patch 'test_klp_callbacks_demo' on unloading module 'test_klp_callbacks_mod'
-test_klp_callbacks_demo: post_unpatch_callback: test_klp_callbacks_mod -> [MODULE_STATE_GOING] Going away
-% echo 0 > /sys/kernel/livepatch/test_klp_callbacks_demo/enabled
-livepatch: 'test_klp_callbacks_demo': initializing unpatching transition
-test_klp_callbacks_demo: pre_unpatch_callback: vmlinux
-livepatch: 'test_klp_callbacks_demo': starting unpatching transition
-livepatch: 'test_klp_callbacks_demo': completing unpatching transition
-test_klp_callbacks_demo: post_unpatch_callback: vmlinux
-livepatch: 'test_klp_callbacks_demo': unpatching complete
-% rmmod test_klp_callbacks_demo"
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_TARGET
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
+$MOD_TARGET: ${MOD_TARGET}_init
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
 
 exit 0
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [POC 5/7] livepatch: Convert klp module callbacks tests into livepatch module tests
  2023-11-10 17:04 [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Petr Mladek
                   ` (3 preceding siblings ...)
  2023-11-10 17:04 ` [POC 4/7] livepatch: Do not use callbacks when testing sysfs interface Petr Mladek
@ 2023-11-10 17:04 ` Petr Mladek
  2023-11-11  1:15   ` kernel test robot
  2023-11-10 17:04 ` [POC 6/7] livepatch: Remove the obsolete per-object callbacks Petr Mladek
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 12+ messages in thread
From: Petr Mladek @ 2023-11-10 17:04 UTC (permalink / raw)
  To: Josh Poimboeuf, Miroslav Benes
  Cc: Joe Lawrence, Nicolai Stange, live-patching, linux-kernel, Petr Mladek

The original livepatch callbacks has been obsoleted by the livepatch
state callbacks.

The important difference is that the original callbacks were associated
with the livepatched object. The state callbacks are associated with
the livepatch states which are associated with the livepatch.

As a result, some per-object callbacks functionality could not be
preserved with the new per-state callbacks. Namely, it is not
possible to change a state when a livepatched module is loaded
or unloaded. This usecase it considered rare and not worth
the complexity.

Instead, the callback self-tests are transformed into testing
the new state callbacks. Some tests do not use the callbacks
any longer. But they still test some basic scenarios which
is not tested by other tests.

Many features are added to the test_klp_speaker module:

  - The parameter "welcome" allows to call the livepatched
    function speaker_welcome.

  - The parameter "waiting_welcome" allows to a trigger
    a workqueue work which might busy wait in a livepatched
    function and block the transition.

    It requires storing pointer to speaker_wait_and_welcome()
    so that the original one could be called from the livepatch.

    Alternative solution would be to livepatch
    speaker_wait_and_welcome() but it would require passing
    pointer to the completion structure from the livepatched module.

    Another tricky part is the initialization of the work structure.
    It must be initialized in the module init() callback so that it can
    be flushed in the exit() callback even when it was not used. But
    it must be initialized also in waiting_welcome_set() because
    the module parameter callbacks are proceed before calling
    the module init() callback.

  - The same sources are used to build two target modules.
    The livepatched functions have versioned names. It makes it easier
    to distinguish messages printed by this functions and the livepatched
    variants.

    It is solved by using macro for definition of the functions.

Many features are added also into the livepatch module:

   - The parameter "add_applause" allows to add "[APPLAUSE " string
     into the message printed by speaker_welcome(). The string
     is stored in a shadow variable which is:

       - associated with a state.
       - allocated and initialized to "[] " in setup() state callback.
       - set to "[APPLAUSE ]" in enable() state callback.
       - set back to "[] " in disable() state callback.
       - freed in release() state callback.

     Allows testing state callbacks. It even allows to distinguish
     the state after setup() and enable() callbacks.

   - The parameter "setup_ret" allow to force a negative return
     value to the callback allocation the shadow variable.

   - The parameter "noreplace" allows to disable patch.replace
     parameter and install the livepatch in parallel with
     others.

   - The same sources are used for building two livepatches.
     It allows writing tests with more livepatches.

     The livepatched functions are versioned. Both livepatches
     can livepatch both speaker test modules.

     But they use the same shadow variable for storing
     the "add_applause" feature. It allows testing passing
     the states and shadow variables with atomic replace.

     But obviously, "add_applause" parameter could no be
     used for both livepatches when they are loaded in parallel
     using the "noreplace" parameter.

Finally, remove the obsolete and unused "callback" test modules
and livepatches.

TODO:

   - Debug why "waiting_welcome" does not block the transition.
     The livepatched function is not on the stack from unknown
     reasons. See the comments in test-module.h for more details.

   - Better organize the tests. test-module.sh combines
     tests of various aspects which might better be suited
     somewhere else.

     As a first step, test-callbacks.sh has been renamed
     to test-modules.sh. But there still might be a better name.

   - Split this huge patch. Add the various features and tests
     in more steps.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 lib/livepatch/Makefile                        |   6 +-
 lib/livepatch/test_klp_callbacks_busy.c       |  70 ---
 lib/livepatch/test_klp_callbacks_demo.c       | 121 ----
 lib/livepatch/test_klp_callbacks_demo2.c      |  93 ---
 lib/livepatch/test_klp_callbacks_mod.c        |  24 -
 lib/livepatch/test_klp_speaker.c              | 153 ++++-
 lib/livepatch/test_klp_speaker_livepatch.c    | 209 ++++++-
 tools/testing/selftests/livepatch/Makefile    |   2 +-
 .../testing/selftests/livepatch/functions.sh  |  29 +
 .../selftests/livepatch/test-callbacks.sh     | 553 ------------------
 .../selftests/livepatch/test-modules.sh       | 539 +++++++++++++++++
 11 files changed, 929 insertions(+), 870 deletions(-)
 delete mode 100644 lib/livepatch/test_klp_callbacks_busy.c
 delete mode 100644 lib/livepatch/test_klp_callbacks_demo.c
 delete mode 100644 lib/livepatch/test_klp_callbacks_demo2.c
 delete mode 100644 lib/livepatch/test_klp_callbacks_mod.c
 delete mode 100755 tools/testing/selftests/livepatch/test-callbacks.sh
 create mode 100755 tools/testing/selftests/livepatch/test-modules.sh

diff --git a/lib/livepatch/Makefile b/lib/livepatch/Makefile
index a8a5f6597633..e85e0c0f3875 100644
--- a/lib/livepatch/Makefile
+++ b/lib/livepatch/Makefile
@@ -3,14 +3,12 @@
 # Makefile for livepatch test code.
 
 obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
-				test_klp_callbacks_demo.o \
-				test_klp_callbacks_demo2.o \
-				test_klp_callbacks_busy.o \
-				test_klp_callbacks_mod.o \
 				test_klp_livepatch.o \
 				test_klp_shadow_vars.o \
 				test_klp_speaker.o \
+				test_klp_speaker2.o \
 				test_klp_speaker_livepatch.o \
+				test_klp_speaker_livepatch2.o \
 				test_klp_state.o \
 				test_klp_state2.o \
 				test_klp_state3.o
diff --git a/lib/livepatch/test_klp_callbacks_busy.c b/lib/livepatch/test_klp_callbacks_busy.c
deleted file mode 100644
index 133929e0ce8f..000000000000
--- a/lib/livepatch/test_klp_callbacks_busy.c
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-
-/* load/run-time control from sysfs writer  */
-static bool block_transition;
-module_param(block_transition, bool, 0644);
-MODULE_PARM_DESC(block_transition, "block_transition (default=false)");
-
-static void busymod_work_func(struct work_struct *work);
-static DECLARE_WORK(work, busymod_work_func);
-static DECLARE_COMPLETION(busymod_work_started);
-
-static void busymod_work_func(struct work_struct *work)
-{
-	pr_info("%s enter\n", __func__);
-	complete(&busymod_work_started);
-
-	while (READ_ONCE(block_transition)) {
-		/*
-		 * Busy-wait until the sysfs writer has acknowledged a
-		 * blocked transition and clears the flag.
-		 */
-		msleep(20);
-	}
-
-	pr_info("%s exit\n", __func__);
-}
-
-static int test_klp_callbacks_busy_init(void)
-{
-	pr_info("%s\n", __func__);
-	schedule_work(&work);
-
-	/*
-	 * To synchronize kernel messages, hold the init function from
-	 * exiting until the work function's entry message has printed.
-	 */
-	wait_for_completion(&busymod_work_started);
-
-	if (!block_transition) {
-		/*
-		 * Serialize output: print all messages from the work
-		 * function before returning from init().
-		 */
-		flush_work(&work);
-	}
-
-	return 0;
-}
-
-static void test_klp_callbacks_busy_exit(void)
-{
-	WRITE_ONCE(block_transition, false);
-	flush_work(&work);
-	pr_info("%s\n", __func__);
-}
-
-module_init(test_klp_callbacks_busy_init);
-module_exit(test_klp_callbacks_busy_exit);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
-MODULE_DESCRIPTION("Livepatch test: busy target module");
diff --git a/lib/livepatch/test_klp_callbacks_demo.c b/lib/livepatch/test_klp_callbacks_demo.c
deleted file mode 100644
index 3fd8fe1cd1cc..000000000000
--- a/lib/livepatch/test_klp_callbacks_demo.c
+++ /dev/null
@@ -1,121 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/livepatch.h>
-
-static int pre_patch_ret;
-module_param(pre_patch_ret, int, 0644);
-MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)");
-
-static const char *const module_state[] = {
-	[MODULE_STATE_LIVE]	= "[MODULE_STATE_LIVE] Normal state",
-	[MODULE_STATE_COMING]	= "[MODULE_STATE_COMING] Full formed, running module_init",
-	[MODULE_STATE_GOING]	= "[MODULE_STATE_GOING] Going away",
-	[MODULE_STATE_UNFORMED]	= "[MODULE_STATE_UNFORMED] Still setting it up",
-};
-
-static void callback_info(const char *callback, struct klp_object *obj)
-{
-	if (obj->mod)
-		pr_info("%s: %s -> %s\n", callback, obj->mod->name,
-			module_state[obj->mod->state]);
-	else
-		pr_info("%s: vmlinux\n", callback);
-}
-
-/* Executed on object patching (ie, patch enablement) */
-static int pre_patch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-	return pre_patch_ret;
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_patch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void pre_unpatch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_unpatch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-}
-
-static void patched_work_func(struct work_struct *work)
-{
-	pr_info("%s\n", __func__);
-}
-
-static struct klp_func no_funcs[] = {
-	{}
-};
-
-static struct klp_func busymod_funcs[] = {
-	{
-		.old_name = "busymod_work_func",
-		.new_func = patched_work_func,
-	}, {}
-};
-
-static struct klp_object objs[] = {
-	{
-		.name = NULL,	/* vmlinux */
-		.funcs = no_funcs,
-		.callbacks = {
-			.pre_patch = pre_patch_callback,
-			.post_patch = post_patch_callback,
-			.pre_unpatch = pre_unpatch_callback,
-			.post_unpatch = post_unpatch_callback,
-		},
-	},	{
-		.name = "test_klp_callbacks_mod",
-		.funcs = no_funcs,
-		.callbacks = {
-			.pre_patch = pre_patch_callback,
-			.post_patch = post_patch_callback,
-			.pre_unpatch = pre_unpatch_callback,
-			.post_unpatch = post_unpatch_callback,
-		},
-	},	{
-		.name = "test_klp_callbacks_busy",
-		.funcs = busymod_funcs,
-		.callbacks = {
-			.pre_patch = pre_patch_callback,
-			.post_patch = post_patch_callback,
-			.pre_unpatch = pre_unpatch_callback,
-			.post_unpatch = post_unpatch_callback,
-		},
-	}, { }
-};
-
-static struct klp_patch patch = {
-	.mod = THIS_MODULE,
-	.objs = objs,
-};
-
-static int test_klp_callbacks_demo_init(void)
-{
-	return klp_enable_patch(&patch);
-}
-
-static void test_klp_callbacks_demo_exit(void)
-{
-}
-
-module_init(test_klp_callbacks_demo_init);
-module_exit(test_klp_callbacks_demo_exit);
-MODULE_LICENSE("GPL");
-MODULE_INFO(livepatch, "Y");
-MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
-MODULE_DESCRIPTION("Livepatch test: livepatch demo");
diff --git a/lib/livepatch/test_klp_callbacks_demo2.c b/lib/livepatch/test_klp_callbacks_demo2.c
deleted file mode 100644
index 5417573e80af..000000000000
--- a/lib/livepatch/test_klp_callbacks_demo2.c
+++ /dev/null
@@ -1,93 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/livepatch.h>
-
-static int replace;
-module_param(replace, int, 0644);
-MODULE_PARM_DESC(replace, "replace (default=0)");
-
-static const char *const module_state[] = {
-	[MODULE_STATE_LIVE]	= "[MODULE_STATE_LIVE] Normal state",
-	[MODULE_STATE_COMING]	= "[MODULE_STATE_COMING] Full formed, running module_init",
-	[MODULE_STATE_GOING]	= "[MODULE_STATE_GOING] Going away",
-	[MODULE_STATE_UNFORMED]	= "[MODULE_STATE_UNFORMED] Still setting it up",
-};
-
-static void callback_info(const char *callback, struct klp_object *obj)
-{
-	if (obj->mod)
-		pr_info("%s: %s -> %s\n", callback, obj->mod->name,
-			module_state[obj->mod->state]);
-	else
-		pr_info("%s: vmlinux\n", callback);
-}
-
-/* Executed on object patching (ie, patch enablement) */
-static int pre_patch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-	return 0;
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_patch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void pre_unpatch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_unpatch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-}
-
-static struct klp_func no_funcs[] = {
-	{ }
-};
-
-static struct klp_object objs[] = {
-	{
-		.name = NULL,	/* vmlinux */
-		.funcs = no_funcs,
-		.callbacks = {
-			.pre_patch = pre_patch_callback,
-			.post_patch = post_patch_callback,
-			.pre_unpatch = pre_unpatch_callback,
-			.post_unpatch = post_unpatch_callback,
-		},
-	}, { }
-};
-
-static struct klp_patch patch = {
-	.mod = THIS_MODULE,
-	.objs = objs,
-	/* set .replace in the init function below for demo purposes */
-};
-
-static int test_klp_callbacks_demo2_init(void)
-{
-	patch.replace = replace;
-	return klp_enable_patch(&patch);
-}
-
-static void test_klp_callbacks_demo2_exit(void)
-{
-}
-
-module_init(test_klp_callbacks_demo2_init);
-module_exit(test_klp_callbacks_demo2_exit);
-MODULE_LICENSE("GPL");
-MODULE_INFO(livepatch, "Y");
-MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
-MODULE_DESCRIPTION("Livepatch test: livepatch demo2");
diff --git a/lib/livepatch/test_klp_callbacks_mod.c b/lib/livepatch/test_klp_callbacks_mod.c
deleted file mode 100644
index 8fbe645b1c2c..000000000000
--- a/lib/livepatch/test_klp_callbacks_mod.c
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-
-static int test_klp_callbacks_mod_init(void)
-{
-	pr_info("%s\n", __func__);
-	return 0;
-}
-
-static void test_klp_callbacks_mod_exit(void)
-{
-	pr_info("%s\n", __func__);
-}
-
-module_init(test_klp_callbacks_mod_init);
-module_exit(test_klp_callbacks_mod_exit);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
-MODULE_DESCRIPTION("Livepatch test: target module");
diff --git a/lib/livepatch/test_klp_speaker.c b/lib/livepatch/test_klp_speaker.c
index d2d31072639a..d8e42267f5cd 100644
--- a/lib/livepatch/test_klp_speaker.c
+++ b/lib/livepatch/test_klp_speaker.c
@@ -9,23 +9,174 @@
 
 #include <linux/module.h>
 #include <linux/printk.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+
+#include "test_klp_speaker.h"
 
 noinline
-void speaker_welcome(void)
+void _VER_NAME(speaker_welcome)(void)
 {
 	pr_info("%s: Hello, World!\n", __func__);
 }
 
+static int welcome_get(char *buffer, const struct kernel_param *kp)
+{
+	_VER_NAME(speaker_welcome)();
+
+	return 0;
+}
+
+static const struct kernel_param_ops welcome_ops = {
+	.get	= welcome_get,
+};
+
+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.");
+
+noinline
+void speaker_wait_and_welcome(struct speaker *speaker)
+{
+	pr_info("%s: Speaker started waiting.\n", __func__);
+	complete(&speaker->started_waiting);
+	speaker->started_waiting_param = true;
+
+	while (READ_ONCE(speaker->is_waiting)) {
+		/*
+		 * Busy-wait until the sysfs writer has acknowledged a
+		 * blocked transition and clears the flag.
+		 */
+		msleep(20);
+	}
+
+	speaker->welcome();
+}
+
+noinline
+void _VER_NAME(call_speaker)(struct speaker *speaker)
+{
+	pr_info("%s: Calling speaker.\n", __func__);
+	speaker->wait_and_welcome(speaker);
+}
+
+static struct speaker test_klp_speaker = {
+	.call = _VER_NAME(call_speaker),
+	.welcome = _VER_NAME(speaker_welcome),
+	.wait_and_welcome = speaker_wait_and_welcome,
+};
+
+static void speaker_func(struct work_struct *work)
+{
+	struct speaker *speaker = container_of(work, struct speaker, work);
+
+	speaker->call(speaker);
+}
+
+/*
+ * The work must be initialized when "waiting_welcome" parameter is proceed
+ * during the module load. Which is done before calling the module init
+ * callback.
+ *
+ * Also it must be initialized also when the parameter was not used because
+ * the work must be flushed in the module exit callback.
+ */
+static void speaker_work_init(struct speaker *speaker)
+{
+	static bool speaker_work_initialized;
+
+	if (speaker_work_initialized)
+		return;
+
+	INIT_WORK(&speaker->work, speaker_func);
+	speaker_work_initialized = true;
+}
+
+static int waiting_welcome_get(char *buffer, const struct kernel_param *kp)
+{
+	if (test_klp_speaker.is_waiting)
+		pr_info("Speaker is waiting.\n");
+	else
+		pr_info("Speaker is not waiting.\n");
+
+	return 0;
+}
+
+static int waiting_welcome_set(const char *val, const struct kernel_param *kp)
+{
+	bool wait;
+	int ret;
+
+	ret = kstrtobool(val, &wait);
+	if (ret)
+		return ret;
+
+	if (wait) {
+		if (test_klp_speaker.is_waiting) {
+			pr_err("%s: Speaker is already waiting.\n", __func__);
+			return -EBUSY;
+		}
+
+		test_klp_speaker.started_waiting_param = false;
+		init_completion(&test_klp_speaker.started_waiting);
+		speaker_work_init(&test_klp_speaker);
+
+		WRITE_ONCE(test_klp_speaker.is_waiting, true);
+		schedule_work(&test_klp_speaker.work);
+
+		/*
+		 * To synchronize kernel messages, hold this callback from
+		 * exiting until the work function's entry message has printed.
+		 */
+		wait_for_completion(&test_klp_speaker.started_waiting);
+	} else {
+		if (!test_klp_speaker.is_waiting) {
+			pr_err("%s: Speaker has not been waiting.\n", __func__);
+			return -EINVAL;
+		}
+
+		WRITE_ONCE(test_klp_speaker.is_waiting, false);
+		flush_work(&test_klp_speaker.work);
+	}
+
+	return 0;
+}
+
+static const struct kernel_param_ops waiting_welcome_ops = {
+	.set	= waiting_welcome_set,
+	.get	= waiting_welcome_get,
+};
+
+module_param_cb(waiting_welcome, &waiting_welcome_ops, NULL, 0600);
+MODULE_PARM_DESC(waiting_welcome, "Speaker will start waiting when set and will say welcome message when cleared.");
+
+static int started_waiting_get(char *buffer, const struct kernel_param *kp)
+{
+	return sysfs_emit(buffer, test_klp_speaker.started_waiting_param ? "1" : "0");
+}
+
+static const struct kernel_param_ops started_waiting_ops = {
+	.get	= started_waiting_get,
+};
+
+module_param_cb(started_waiting, &started_waiting_ops, NULL, 0400);
+MODULE_PARM_DESC(started_waiting, "Read only parameter to check whether the asynchronously started speaker already started waiting.");
+
 static int test_klp_speaker_init(void)
 {
 	pr_info("%s\n", __func__);
 
+	speaker_work_init(&test_klp_speaker);
+
 	return 0;
 }
 
 static void test_klp_speaker_exit(void)
 {
 	pr_info("%s\n", __func__);
+
+	/* Make sure that wait_funtion() is not running. */
+	WRITE_ONCE(test_klp_speaker.is_waiting, false);
+	flush_work(&test_klp_speaker.work);
 }
 
 module_init(test_klp_speaker_init);
diff --git a/lib/livepatch/test_klp_speaker_livepatch.c b/lib/livepatch/test_klp_speaker_livepatch.c
index 0317a4937b78..54bd32ee24ea 100644
--- a/lib/livepatch/test_klp_speaker_livepatch.c
+++ b/lib/livepatch/test_klp_speaker_livepatch.c
@@ -7,18 +7,182 @@
 #include <linux/livepatch.h>
 #include <linux/init.h>
 
+#include "test_klp_speaker.h"
 
-void livepatch_speaker_welcome(void)
-{
-	pr_info("%s: Ladies and gentleman, ...\n", __func__);
+#define APPLAUSE_ID 1
+#define APPLAUSE_SIZE 64
+
+/* associate the shadow variable with NULL address */;
+void *shadow_object = NULL;
+
+/* load/run-time control from sysfs writer  */
+static bool add_applause;
+module_param(add_applause, bool, 0600);
+MODULE_PARM_DESC(add_applause, "Use shadow variable to add applause (default=false)");
+
+/* load/run-time control from sysfs writer  */
+static int setup_ret;
+module_param(setup_ret, int, 0644);
+MODULE_PARM_DESC(setup_ret, "Allow to force failure for the setup callback (default=0)");
+
+/* load/run-time control from sysfs writer  */
+static bool noreplace;
+module_param(noreplace, bool, 0600);
+MODULE_PARM_DESC(noreplace, "Allow to install the livepatch together with other livepatches. (default=false)");
+
+#define LIVEPATCH_SPEAKER_WELCOME_FN(fn_name)					\
+noinline									\
+void fn_name(void)								\
+{										\
+	const char *applause;							\
+										\
+	applause = (char *)klp_shadow_get(shadow_object, APPLAUSE_ID);		\
+										\
+	if (!applause)								\
+		applause = "";							\
+										\
+	pr_info("%s: %sLadies and gentleman, ...\n", __func__, applause);	\
 }
 
+LIVEPATCH_SPEAKER_WELCOME_FN(livepatch_speaker_welcome)
+LIVEPATCH_SPEAKER_WELCOME_FN(livepatch_speaker_welcome2)
+
+static int allocate_applause(void)
+{
+	char *applause;
+
+	/*
+	 * Attach the shadow variable to some well known address it stays
+	 * even when the livepatch gets replaced with a newer version.
+	 *
+	 * Make sure that the shadow variable does not exist yet.
+	 */
+	applause = (char *)klp_shadow_alloc(shadow_object, APPLAUSE_ID,
+					   APPLAUSE_SIZE, GFP_KERNEL,
+					   NULL, NULL);
+
+	if (!applause) {
+		pr_err("%s: failed to allocated shadow variable for storing an applause description\n",
+		       __func__);
+		return -ENOMEM;
+	}
+
+	/*
+	 * Fill the shadow target with an empty brackets before all processes
+	 * get livepatched.
+	 */
+	strscpy(applause, "[] ", APPLAUSE_SIZE);
+
+	return 0;
+}
+
+static void set_applause(void)
+{
+	char *applause;
+
+	applause = (char *)klp_shadow_get(shadow_object, APPLAUSE_ID);
+	if (!applause) {
+		pr_err("%s: failed to get shadow variable with the applause description: %d\n",
+		       __func__, APPLAUSE_ID);
+		return;
+	}
+
+	strscpy(applause, "[APPLAUSE] ", APPLAUSE_SIZE);
+}
+
+static void unset_applause(void)
+{
+	char *applause;
+
+	applause = (char *)klp_shadow_get(shadow_object, APPLAUSE_ID);
+	if (!applause) {
+		pr_err("%s: failed to get shadow variable with the applause description: %d\n",
+		       __func__, APPLAUSE_ID);
+		return;
+	}
+
+	applause[0] = '\0';
+}
+
+static void free_applause(void)
+{
+	char *applause;
+
+	applause = (char *)klp_shadow_get(shadow_object, APPLAUSE_ID);
+	if (!applause) {
+		pr_err("%s: failed to get shadow variable with the applause description: %d\n",
+		       __func__, APPLAUSE_ID);
+		return;
+	}
+
+	klp_shadow_free(shadow_object, APPLAUSE_ID, NULL);
+}
+
+/* Executed before patching when the state is new. */
+static int setup_applause_callback(struct klp_patch *patch, struct klp_state *state)
+{
+	pr_info("%s: state %lu\n", __func__, state->id);
+
+	if (setup_ret) {
+		pr_err("%s: forcing err: %pe\n", __func__, ERR_PTR(setup_ret));
+		return setup_ret;
+	}
+
+	return allocate_applause();
+}
+
+/* Executed after patching when the state is new. */
+static void enable_applause_callback(struct klp_patch *patch, struct klp_state *state)
+{
+	pr_info("%s: state %lu\n", __func__, state->id);
+	set_applause();
+}
+
+/* Executed before unpatching when the state is obsoleted. */
+static void disable_applause_callback(struct klp_patch *patch, struct klp_state *state)
+{
+	pr_info("%s: state %lu\n", __func__, state->id);
+	unset_applause();
+}
+
+/* Executed after unpatching when the state is obsoleted. */
+static void release_applause_callback(struct klp_patch *patch, struct klp_state *state)
+{
+	pr_info("%s: state %lu\n", __func__, state->id);
+	free_applause();
+}
+
+#define LIVEPATCH_CALL_SPEAKER_FN(fn_name)			\
+void fn_name(struct speaker *speaker)		\
+{								\
+	pr_info("%s: Calling speaker (fixed).\n", __func__);	\
+	speaker->wait_and_welcome(speaker);			\
+}
+
+LIVEPATCH_CALL_SPEAKER_FN(livepatch_call_speaker)
+LIVEPATCH_CALL_SPEAKER_FN(livepatch_call_speaker2)
 
 static struct klp_func test_klp_speaker_funcs[] = {
 	{
 		.old_name = "speaker_welcome",
 		.new_func = livepatch_speaker_welcome,
 	},
+	{
+		.old_name = "call_speaker",
+		.new_func = livepatch_call_speaker,
+	},
+	{ }
+};
+
+static struct klp_func test_klp_speaker2_funcs[] = {
+	{
+		.old_name = "speaker_welcome2",
+		.new_func = livepatch_speaker_welcome2,
+	},
+	{
+		.old_name = "call_speaker2",
+		.new_func = livepatch_call_speaker2,
+	},
 	{ }
 };
 
@@ -27,16 +191,55 @@ static struct klp_object objs[] = {
 		.name = "test_klp_speaker",
 		.funcs = test_klp_speaker_funcs,
 	},
+	{
+		.name = "test_klp_speaker2",
+		.funcs = test_klp_speaker2_funcs,
+	},
 	{ }
 };
 
+static struct klp_state states[] = {
+	{
+		.id = APPLAUSE_ID,
+		.is_shadow = true,
+		.callbacks = {
+			.setup = setup_applause_callback,
+			.enable = enable_applause_callback,
+			.disable = disable_applause_callback,
+			.release = release_applause_callback,
+		},
+	},
+	{ }
+};
+
+/*
+ * Use the atomic replace by default so that the APPLAUSE state
+ * is correctly transferred when another version of the speaker
+ * livepatch gets loaded.
+ *
+ * Can be overridden by "noreplace=1" parameter. But it can't
+ * be used together with the "add_applause=1" parameter when
+ * another speaker livepatch is already loaded with
+ * the "add_applause=1" parameter.
+ */
 static struct klp_patch patch = {
 	.mod = THIS_MODULE,
 	.objs = objs,
+	.replace = true,
 };
 
 static int test_klp_speaker_livepatch_init(void)
 {
+	if (add_applause)
+		patch.states = states;
+
+	if (noreplace) {
+		if (add_applause)
+			pr_warn("The speaker livepatch can't be loaded when both \"add_applause\" and \"noreplace\" are used and another speaker livepatch is already loaded with \"add_aplause\"\n");
+
+		patch.replace = false;
+	}
+
 	return klp_enable_patch(&patch);
 }
 
diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile
index 02fadc9d55e0..18c52b9e8f46 100644
--- a/tools/testing/selftests/livepatch/Makefile
+++ b/tools/testing/selftests/livepatch/Makefile
@@ -3,7 +3,7 @@
 TEST_PROGS_EXTENDED := functions.sh
 TEST_PROGS := \
 	test-livepatch.sh \
-	test-callbacks.sh \
+	test-modules.sh \
 	test-shadow-vars.sh \
 	test-state.sh \
 	test-ftrace.sh \
diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh
index c8416c54b463..a962119f053e 100644
--- a/tools/testing/selftests/livepatch/functions.sh
+++ b/tools/testing/selftests/livepatch/functions.sh
@@ -277,6 +277,20 @@ function set_pre_patch_ret {
 		die "failed to set pre_patch_ret parameter for $mod module"
 }
 
+# read_module_param(modname, param)
+#	modname - module name which provides the given parameter
+#	param - parameter name to be read
+function read_module_param {
+	local mod="$1"; shift
+	local param="$1"
+
+	log "% cat /sys/module/$mod/parameters/$param"
+	ret=$(cat /sys/module/$mod/parameters/$param 2>&1)
+	if [[ "$ret" != "" ]]; then
+		die "$ret"
+	fi
+}
+
 function start_test {
 	local test="$1"
 
@@ -336,9 +350,24 @@ function check_sysfs_value() {
 	local rel_path="$1"; shift
 	local expected_value="$1"; shift
 
+#	echo "mod=$mod"
+#	echo "rel_path=$rel_path"
+#	echo "expected_value=$expected_value"
 	local path="$KLP_SYSFS_DIR/$mod/$rel_path"
 	local value=`cat $path`
 	if test "$value" != "$expected_value" ; then
 		die "Unexpected value in $path: $expected_value vs. $value"
 	fi
 }
+
+# check_object_patched(livepatch_module, objname, expected_value)
+#	livepatch_module - livepatch module creating the sysfs interface
+#	objname - livepatched object to be checked
+#	expected_value - expected value read from the file
+function check_object_patched() {
+	local livepatch_module="$1"; shift
+	local objname="$1"; shift
+	local expected_value="$1"; shift
+
+	check_sysfs_value "$livepatch_module" "$objname/patched" "$expected_value"
+}
diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh
deleted file mode 100755
index 90b26dbb2626..000000000000
--- a/tools/testing/selftests/livepatch/test-callbacks.sh
+++ /dev/null
@@ -1,553 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-# Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
-
-. $(dirname $0)/functions.sh
-
-MOD_LIVEPATCH=test_klp_callbacks_demo
-MOD_LIVEPATCH2=test_klp_callbacks_demo2
-MOD_TARGET=test_klp_callbacks_mod
-MOD_TARGET_BUSY=test_klp_callbacks_busy
-
-setup_config
-
-
-# Test a combination of loading a kernel module and a livepatch that
-# patches a function in the first module.  Load the target module
-# before the livepatch module.  Unload them in the same order.
-#
-# - On livepatch enable, before the livepatch transition starts,
-#   pre-patch callbacks are executed for vmlinux and $MOD_TARGET (those
-#   klp_objects currently loaded).  After klp_objects are patched
-#   according to the klp_patch, their post-patch callbacks run and the
-#   transition completes.
-#
-# - Similarly, on livepatch disable, pre-patch callbacks run before the
-#   unpatching transition starts.  klp_objects are reverted, post-patch
-#   callbacks execute and the transition completes.
-
-start_test "target module before livepatch"
-
-load_mod $MOD_TARGET
-load_lp $MOD_LIVEPATCH
-disable_lp $MOD_LIVEPATCH
-unload_lp $MOD_LIVEPATCH
-unload_mod $MOD_TARGET
-
-check_result "% modprobe $MOD_TARGET
-$MOD_TARGET: ${MOD_TARGET}_init
-% modprobe $MOD_LIVEPATCH
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
-$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
-livepatch: '$MOD_LIVEPATCH': starting patching transition
-livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
-$MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
-livepatch: '$MOD_LIVEPATCH': patching complete
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
-livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
-$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
-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 -> [MODULE_STATE_LIVE] Normal state
-livepatch: '$MOD_LIVEPATCH': unpatching complete
-% rmmod $MOD_LIVEPATCH
-% rmmod $MOD_TARGET
-$MOD_TARGET: ${MOD_TARGET}_exit"
-
-
-# This test is similar to the previous test, but (un)load the livepatch
-# module before the target kernel module.  This tests the livepatch
-# core's module_coming handler.
-#
-# - On livepatch enable, only pre/post-patch callbacks are executed for
-#   currently loaded klp_objects, in this case, vmlinux.
-#
-# - When a targeted module is subsequently loaded, only its
-#   pre/post-patch callbacks are executed.
-#
-# - On livepatch disable, all currently loaded klp_objects' (vmlinux and
-#   $MOD_TARGET) pre/post-unpatch callbacks are executed.
-
-start_test "module_coming notifier"
-
-load_lp $MOD_LIVEPATCH
-load_mod $MOD_TARGET
-disable_lp $MOD_LIVEPATCH
-unload_lp $MOD_LIVEPATCH
-unload_mod $MOD_TARGET
-
-check_result "% modprobe $MOD_LIVEPATCH
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting patching transition
-livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': patching complete
-% modprobe $MOD_TARGET
-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_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
-$MOD_TARGET: ${MOD_TARGET}_init
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
-livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
-$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
-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 -> [MODULE_STATE_LIVE] Normal state
-livepatch: '$MOD_LIVEPATCH': unpatching complete
-% rmmod $MOD_LIVEPATCH
-% rmmod $MOD_TARGET
-$MOD_TARGET: ${MOD_TARGET}_exit"
-
-
-# Test loading the livepatch after a targeted kernel module, then unload
-# the kernel module before disabling the livepatch.  This tests the
-# livepatch core's module_going handler.
-#
-# - First load a target module, then the livepatch.
-#
-# - When a target module is unloaded, the livepatch is only reverted
-#   from that klp_object ($MOD_TARGET).  As such, only its pre and
-#   post-unpatch callbacks are executed when this occurs.
-#
-# - When the livepatch is disabled, pre and post-unpatch callbacks are
-#   run for the remaining klp_object, vmlinux.
-
-start_test "module_going notifier"
-
-load_mod $MOD_TARGET
-load_lp $MOD_LIVEPATCH
-unload_mod $MOD_TARGET
-disable_lp $MOD_LIVEPATCH
-unload_lp $MOD_LIVEPATCH
-
-check_result "% modprobe $MOD_TARGET
-$MOD_TARGET: ${MOD_TARGET}_init
-% modprobe $MOD_LIVEPATCH
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
-$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
-livepatch: '$MOD_LIVEPATCH': starting patching transition
-livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
-$MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
-livepatch: '$MOD_LIVEPATCH': patching complete
-% rmmod $MOD_TARGET
-$MOD_TARGET: ${MOD_TARGET}_exit
-$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
-livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
-$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
-livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting unpatching transition
-livepatch: '$MOD_LIVEPATCH': completing unpatching transition
-$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': unpatching complete
-% rmmod $MOD_LIVEPATCH"
-
-
-# This test is similar to the previous test, however the livepatch is
-# loaded first.  This tests the livepatch core's module_coming and
-# module_going handlers.
-#
-# - First load the livepatch.
-#
-# - When a targeted kernel module is subsequently loaded, only its
-#   pre/post-patch callbacks are executed.
-#
-# - When the target module is unloaded, the livepatch is only reverted
-#   from the $MOD_TARGET klp_object.  As such, only pre and
-#   post-unpatch callbacks are executed when this occurs.
-
-start_test "module_coming and module_going notifiers"
-
-load_lp $MOD_LIVEPATCH
-load_mod $MOD_TARGET
-unload_mod $MOD_TARGET
-disable_lp $MOD_LIVEPATCH
-unload_lp $MOD_LIVEPATCH
-
-check_result "% modprobe $MOD_LIVEPATCH
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting patching transition
-livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': patching complete
-% modprobe $MOD_TARGET
-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_LIVEPATCH: post_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
-$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
-livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
-$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
-livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting unpatching transition
-livepatch: '$MOD_LIVEPATCH': completing unpatching transition
-$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': unpatching complete
-% rmmod $MOD_LIVEPATCH"
-
-
-# A simple test of loading a livepatch without one of its patch target
-# klp_objects ever loaded ($MOD_TARGET).
-#
-# - Load the livepatch.
-#
-# - As expected, only pre/post-(un)patch handlers are executed for
-#   vmlinux.
-
-start_test "target module not present"
-
-load_lp $MOD_LIVEPATCH
-disable_lp $MOD_LIVEPATCH
-unload_lp $MOD_LIVEPATCH
-
-check_result "% modprobe $MOD_LIVEPATCH
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting patching transition
-livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': patching complete
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
-livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting unpatching transition
-livepatch: '$MOD_LIVEPATCH': completing unpatching transition
-$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': unpatching complete
-% rmmod $MOD_LIVEPATCH"
-
-
-# Test a scenario where a vmlinux pre-patch callback returns a non-zero
-# status (ie, failure).
-#
-# - First load a target module.
-#
-# - Load the livepatch module, setting its 'pre_patch_ret' value to -19
-#   (-ENODEV).  When its vmlinux pre-patch callback executes, this
-#   status code will propagate back to the module-loading subsystem.
-#   The result is that the insmod command refuses to load the livepatch
-#   module.
-
-start_test "pre-patch callback -ENODEV"
-
-load_mod $MOD_TARGET
-load_failing_mod $MOD_LIVEPATCH pre_patch_ret=-19
-unload_mod $MOD_TARGET
-
-check_result "% modprobe $MOD_TARGET
-$MOD_TARGET: ${MOD_TARGET}_init
-% modprobe $MOD_LIVEPATCH pre_patch_ret=-19
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-test_klp_callbacks_demo: pre_patch_callback: vmlinux
-livepatch: pre-patch callback failed for object 'vmlinux'
-livepatch: failed to enable patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': canceling patching transition, going to unpatch
-livepatch: '$MOD_LIVEPATCH': completing unpatching transition
-livepatch: '$MOD_LIVEPATCH': unpatching complete
-modprobe: ERROR: could not insert '$MOD_LIVEPATCH': No such device
-% rmmod $MOD_TARGET
-$MOD_TARGET: ${MOD_TARGET}_exit"
-
-
-# Similar to the previous test, setup a livepatch such that its vmlinux
-# pre-patch callback returns success.  However, when a targeted kernel
-# module is later loaded, have the livepatch return a failing status
-# code.
-#
-# - Load the livepatch, vmlinux pre-patch callback succeeds.
-#
-# - Set a trap so subsequent pre-patch callbacks to this livepatch will
-#   return -ENODEV.
-#
-# - The livepatch pre-patch callback for subsequently loaded target
-#   modules will return failure, so the module loader refuses to load
-#   the kernel module.  No post-patch or pre/post-unpatch callbacks are
-#   executed for this klp_object.
-#
-# - Pre/post-unpatch callbacks are run for the vmlinux klp_object.
-
-start_test "module_coming + pre-patch callback -ENODEV"
-
-load_lp $MOD_LIVEPATCH
-set_pre_patch_ret $MOD_LIVEPATCH -19
-load_failing_mod $MOD_TARGET
-disable_lp $MOD_LIVEPATCH
-unload_lp $MOD_LIVEPATCH
-
-check_result "% modprobe $MOD_LIVEPATCH
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting patching transition
-livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': patching complete
-% echo -19 > /sys/module/$MOD_LIVEPATCH/parameters/pre_patch_ret
-% modprobe $MOD_TARGET
-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
-livepatch: pre-patch callback failed for object '$MOD_TARGET'
-livepatch: patch '$MOD_LIVEPATCH' failed for module '$MOD_TARGET', refusing to load module '$MOD_TARGET'
-modprobe: ERROR: could not insert '$MOD_TARGET': No such device
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
-livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting unpatching transition
-livepatch: '$MOD_LIVEPATCH': completing unpatching transition
-$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': unpatching complete
-% rmmod $MOD_LIVEPATCH"
-
-
-# Test loading multiple targeted kernel modules.  This test-case is
-# mainly for comparing with the next test-case.
-#
-# - Load a target "busy" kernel module which kicks off a worker function
-#   that immediately exits.
-#
-# - Proceed with loading the livepatch and another ordinary target
-#   module.  Post-patch callbacks are executed and the transition
-#   completes quickly.
-
-start_test "multiple target modules"
-
-load_mod $MOD_TARGET_BUSY block_transition=N
-load_lp $MOD_LIVEPATCH
-load_mod $MOD_TARGET
-unload_mod $MOD_TARGET
-disable_lp $MOD_LIVEPATCH
-unload_lp $MOD_LIVEPATCH
-unload_mod $MOD_TARGET_BUSY
-
-check_result "% modprobe $MOD_TARGET_BUSY block_transition=N
-$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init
-$MOD_TARGET_BUSY: busymod_work_func enter
-$MOD_TARGET_BUSY: busymod_work_func exit
-% modprobe $MOD_LIVEPATCH
-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
-livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
-$MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
-livepatch: '$MOD_LIVEPATCH': patching complete
-% modprobe $MOD_TARGET
-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_LIVEPATCH: post_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
-$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
-livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
-$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
-livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
-$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
-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: ${MOD_TARGET_BUSY}_exit"
-
-
-# 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$' /sys/kernel/livepatch/$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 "% modprobe $MOD_TARGET_BUSY block_transition=Y
-$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init
-$MOD_TARGET_BUSY: busymod_work_func enter
-% modprobe $MOD_LIVEPATCH
-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
-% modprobe $MOD_TARGET
-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 > /sys/kernel/livepatch/$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.
-#
-# - Load and unload two livepatches, pre and post (un)patch callbacks
-#   execute as each patch progresses through its (un)patching
-#   transition.
-
-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
-
-check_result "% modprobe $MOD_LIVEPATCH
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting patching transition
-livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': patching complete
-% modprobe $MOD_LIVEPATCH2
-livepatch: enabling patch '$MOD_LIVEPATCH2'
-livepatch: '$MOD_LIVEPATCH2': initializing patching transition
-$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH2': starting patching transition
-livepatch: '$MOD_LIVEPATCH2': completing patching transition
-$MOD_LIVEPATCH2: post_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH2': patching complete
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
-livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
-$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
-livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
-$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH2': unpatching complete
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
-livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting unpatching transition
-livepatch: '$MOD_LIVEPATCH': completing unpatching transition
-$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'
-# patch.  When the latter loads, the original livepatch should be
-# disabled and *none* of its pre/post-unpatch callbacks executed.  On
-# the other hand, when the atomic-replace livepatch is disabled, its
-# pre/post-unpatch callbacks *should* be executed.
-#
-# - Load and unload two livepatches, the second of which has its
-#   .replace flag set true.
-#
-# - Pre and post patch callbacks are executed for both livepatches.
-#
-# - Once the atomic replace module is loaded, only its pre and post
-#   unpatch callbacks are executed.
-
-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
-
-check_result "% modprobe $MOD_LIVEPATCH
-livepatch: enabling patch '$MOD_LIVEPATCH'
-livepatch: '$MOD_LIVEPATCH': initializing patching transition
-$MOD_LIVEPATCH: pre_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': starting patching transition
-livepatch: '$MOD_LIVEPATCH': completing patching transition
-$MOD_LIVEPATCH: post_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH': patching complete
-% modprobe $MOD_LIVEPATCH2 replace=1
-livepatch: enabling patch '$MOD_LIVEPATCH2'
-livepatch: '$MOD_LIVEPATCH2': initializing patching transition
-$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH2': starting patching transition
-livepatch: '$MOD_LIVEPATCH2': completing patching transition
-$MOD_LIVEPATCH2: post_patch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH2': patching complete
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
-livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
-$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
-livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
-$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
-livepatch: '$MOD_LIVEPATCH2': unpatching complete
-% rmmod $MOD_LIVEPATCH2
-% rmmod $MOD_LIVEPATCH"
-
-
-exit 0
diff --git a/tools/testing/selftests/livepatch/test-modules.sh b/tools/testing/selftests/livepatch/test-modules.sh
new file mode 100755
index 000000000000..1f7b502db173
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test-modules.sh
@@ -0,0 +1,539 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
+
+. $(dirname $0)/functions.sh
+
+MOD_LIVEPATCH=test_klp_speaker_livepatch
+MOD_LIVEPATCH2=test_klp_speaker_livepatch2
+MOD_TARGET=test_klp_speaker
+MOD_TARGET2=test_klp_speaker2
+
+setup_config
+
+# Test basic livepatch enable/disable functionality when livepatching
+# modules.
+#
+# Load the target module before the livepatch module. Unload them
+# in the reverse order.
+#
+# The expected state is checked by reading "welcome" parameter
+# of the target module. The livepatched variant should be printed
+# when both the target and livepatch modules are loaded.
+
+start_test "module enable/disable livepatch"
+
+load_mod $MOD_TARGET
+read_module_param $MOD_TARGET welcome
+
+load_lp $MOD_LIVEPATCH
+read_module_param $MOD_TARGET welcome
+
+disable_lp $MOD_LIVEPATCH
+unload_lp $MOD_LIVEPATCH
+read_module_param $MOD_TARGET welcome
+
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_LIVEPATCH: livepatch_speaker_welcome: Ladies and gentleman, ...
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
+
+# Test the module coming hook in the module loader.
+#
+# Load the livepatch before the target module. Unload them in
+# the same order.
+#
+# The livepatch hook in the module loader should print a message
+# about applying the livepatch to the target module.
+#
+# The expected state is checked by reading "welcome" parameter
+# of the target module. The livepatched variant should be printed
+# when both the target and livepatch modules are loaded.
+
+start_test "module coming hook"
+
+load_lp $MOD_LIVEPATCH
+load_mod $MOD_TARGET
+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 "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_TARGET
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
+$MOD_TARGET: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_LIVEPATCH: livepatch_speaker_welcome: Ladies and gentleman, ...
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
+
+# Test the module going hook in the module loader.
+#
+# The livepatch hook in the module loader should print a message
+# about reverting the livepatch to the target module.
+#
+# The expected state is checked by reading "welcome" parameter
+# of the target module. The livepatched variant should be printed
+# when both the target and livepatch modules are loaded.
+
+start_test "module going hook"
+
+load_mod $MOD_TARGET
+read_module_param $MOD_TARGET welcome
+
+load_lp $MOD_LIVEPATCH
+read_module_param $MOD_TARGET welcome
+check_object_patched $MOD_LIVEPATCH $MOD_TARGET "1"
+
+unload_mod $MOD_TARGET
+check_object_patched $MOD_LIVEPATCH $MOD_TARGET "0"
+
+disable_lp $MOD_LIVEPATCH
+unload_lp $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_LIVEPATCH: livepatch_speaker_welcome: Ladies and gentleman, ...
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
+
+# Test the module coming and going hooks in the module loader.
+#
+# Load the livepatch before the target module. Unload them in the reverse order.
+#
+# Both livepatch hooks in the module loader should print a message
+# about applying resp. reverting the livepatch to the target module.
+#
+# The expected state is checked by reading "welcome" parameter
+# of the target module. The livepatched variant should be printed
+# when both the target and livepatch modules are loaded.
+
+start_test "module coming and going hooks"
+
+load_lp $MOD_LIVEPATCH
+load_mod $MOD_TARGET
+read_module_param $MOD_TARGET welcome
+
+unload_mod $MOD_TARGET
+disable_lp $MOD_LIVEPATCH
+unload_lp $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_TARGET
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
+$MOD_TARGET: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_LIVEPATCH: livepatch_speaker_welcome: Ladies and gentleman, ...
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
+
+
+# Use shadow variables, state, and callbacks to add "[APPLAUSE] "
+# into the message printed by "welcome" parameter.
+
+start_test "livepatch state callbacks"
+
+load_mod $MOD_TARGET
+read_module_param $MOD_TARGET welcome
+
+load_lp $MOD_LIVEPATCH add_applause=1
+read_module_param $MOD_TARGET welcome
+
+disable_lp $MOD_LIVEPATCH
+unload_lp $MOD_LIVEPATCH
+read_module_param $MOD_TARGET welcome
+
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% modprobe $MOD_LIVEPATCH add_applause=1
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: setup_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: enable_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': patching complete
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_LIVEPATCH: livepatch_speaker_welcome: [APPLAUSE] Ladies and gentleman, ...
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: disable_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: release_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
+# Use shadow variables, state, and callbacks to add "[APPLAUSE] "
+# into the message printed by "welcome" parameter.
+#
+# BUT make the "setup" callback fail.
+#
+# The livepatch should not get loaded. The test module should
+# should stay unpatched which is checked by reading the "welcome"
+# parameter.
+
+start_test "failing livepatch setup callback with -ENODEV"
+
+load_mod $MOD_TARGET
+read_module_param $MOD_TARGET welcome
+
+load_failing_mod $MOD_LIVEPATCH add_applause=1 setup_ret=-19
+read_module_param $MOD_TARGET welcome
+
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% modprobe $MOD_LIVEPATCH add_applause=1 setup_ret=-19
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: setup_applause_callback: state 1
+$MOD_LIVEPATCH: setup_applause_callback: forcing err: -ENODEV
+livepatch: failed to enable patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': canceling patching transition, going to unpatch
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+modprobe: ERROR: could not insert '$MOD_LIVEPATCH': No such device
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
+# Test loading multiple targeted kernel modules.  This test-case is
+# mainly for comparing with the next test-case.
+#
+# The livepatch gets loaded between two target modules. It adds
+# a livepatch state, callbacks, and shadow variable which would
+# add "[APPLAUSE] " into the message printed when reading
+# the "welcome" parameters of the two target modules.
+#
+# All four state callbacks should get called. And the message
+# "[APPLAUSE] Ladies and gentleman, ..." should be printed when
+# reading the "welcome" parameter while the livepatch is enabled.
+
+start_test "multiple target modules"
+
+load_mod $MOD_TARGET
+read_module_param $MOD_TARGET welcome
+
+load_lp $MOD_LIVEPATCH add_applause=1
+read_module_param $MOD_TARGET welcome
+
+load_mod $MOD_TARGET2
+read_module_param $MOD_TARGET2 welcome
+
+unload_mod $MOD_TARGET2
+disable_lp $MOD_LIVEPATCH
+read_module_param $MOD_TARGET welcome
+
+unload_lp $MOD_LIVEPATCH
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% modprobe $MOD_LIVEPATCH add_applause=1
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: setup_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: enable_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': patching complete
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_LIVEPATCH: livepatch_speaker_welcome: [APPLAUSE] Ladies and gentleman, ...
+% modprobe $MOD_TARGET2
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET2'
+$MOD_TARGET2: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET2/parameters/welcome
+$MOD_LIVEPATCH: livepatch_speaker_welcome2: [APPLAUSE] Ladies and gentleman, ...
+% rmmod $MOD_TARGET2
+$MOD_TARGET2: ${MOD_TARGET}_exit
+livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET2'
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: disable_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: release_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
+# Test loading multiple livepatches.  This test-case is mainly for comparing
+# with the next test-case.
+#
+# The patching and unpatching transition should be done for both livepatches.
+
+start_test "multiple livepatches in parallel"
+
+load_lp $MOD_LIVEPATCH
+load_lp $MOD_LIVEPATCH2 noreplace=1
+disable_lp $MOD_LIVEPATCH2
+disable_lp $MOD_LIVEPATCH
+unload_lp $MOD_LIVEPATCH2
+unload_lp $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_LIVEPATCH2 noreplace=1
+livepatch: enabling patch '$MOD_LIVEPATCH2'
+livepatch: '$MOD_LIVEPATCH2': initializing patching transition
+livepatch: '$MOD_LIVEPATCH2': starting patching transition
+livepatch: '$MOD_LIVEPATCH2': completing patching transition
+livepatch: '$MOD_LIVEPATCH2': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
+livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH2': unpatching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH2
+% rmmod $MOD_LIVEPATCH"
+
+
+# Load multiple livepatches, but the second as an 'atomic-replace'
+# patch.
+#
+# The 2nd livepatch will replace the 1st one. As a result, the 1s patch
+# can be removed wihtout the unpatch transition.
+
+start_test "atomic replace"
+
+load_lp $MOD_LIVEPATCH
+load_lp $MOD_LIVEPATCH2
+disable_lp $MOD_LIVEPATCH2
+unload_lp $MOD_LIVEPATCH2
+unload_lp $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_LIVEPATCH2
+livepatch: enabling patch '$MOD_LIVEPATCH2'
+livepatch: '$MOD_LIVEPATCH2': initializing patching transition
+livepatch: '$MOD_LIVEPATCH2': starting patching transition
+livepatch: '$MOD_LIVEPATCH2': completing patching transition
+livepatch: '$MOD_LIVEPATCH2': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
+livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH2': unpatching complete
+% rmmod $MOD_LIVEPATCH2
+% rmmod $MOD_LIVEPATCH"
+
+exit 0
+
+# FIXME:
+#
+#    The test below does not work and I do not know why.
+#
+#    It seems that the livepatched function call_speaker()
+#    is not on the stack of the workqueue worker which
+#    is processing the speaker work.
+#
+#    Even though, there is a worker which is waiting in
+#    speaker_wait_and_welcome(). But the stack looks like:
+#
+#      [<0>] msleep+0x36/0x40
+#      [<0>] speaker_wait_and_welcome+0x40/0x60 [test_klp_speaker]
+#      [<0>] process_scheduled_works+0x2b4/0x530
+#      [<0>] worker_thread+0x174/0x340
+#      [<0>] kthread+0x100/0x130
+#      [<0>] ret_from_fork+0x2d/0x50
+#      [<0>] ret_from_fork_asm+0x1b/0x30
+#
+#    Note that I have got the same stack by adding show_stack()
+#    directly into speaker_wait_and_welcome() function.
+#
+#    Also note that the speaker must be waiting when the livepatch
+#    gets loaded. The speaker work  is queued when
+#    the "waiting_speaker=1" parameter is proceed. And
+#    waiting_welcome_set() waits until the worker started waiting.
+#
+#    And the function is supposed to be on the stack. It is called
+#    via the speaker->call() callback. And it is marked as noinline.
+#
+#    Note that "call_speaker() did not appear on the stack even
+#    when I tried to call it directly from speaker_func().
+#
+#    BTW: speaker_func() is not on the stack either. And it can't
+#         be inlined because it is callback for the workqueue work.
+#
+######################################################################
+#
+# 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.
+#
+# Note:
+#
+#   - The started patching transion never finishes. Only "setup"
+#     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 "enable" callback has not been called.
+#
+#   - When the livepatch gets disabled, the current transiton gets
+#     reverted instead of starting a new disable transition. Only
+#     the "remove" callback is called.
+start_test "busy target module"
+
+load_mod $MOD_TARGET waiting_welcome=1
+# Wait until the asynchronous speaker started waiting.
+loop_until 'grep -q '^1$' /sys/module/$MOD_TARGET/parameters/started_waiting' ||
+	die "failed to stall transition"
+read_module_param $MOD_TARGET welcome
+
+load_lp_nowait $MOD_LIVEPATCH add_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$' /sys/kernel/livepatch/$MOD_LIVEPATCH/transition' ||
+	die "failed to stall transition"
+read_module_param $MOD_TARGET welcome
+
+load_mod $MOD_TARGET2
+read_module_param $MOD_TARGET2 welcome
+
+unload_mod $MOD_TARGET2
+disable_lp $MOD_LIVEPATCH
+read_module_param $MOD_TARGET welcome
+
+unload_lp $MOD_LIVEPATCH
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET waiting_welcome=1
+$MOD_TARGET: call_speaker: Calling speaker.
+$MOD_TARGET: speaker_wait_and_welcome: Speaker started waiting.
+$MOD_TARGET: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% modprobe $MOD_LIVEPATCH add_applause=1
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: setup_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_LIVEPATCH: livepatch_speaker_welcome: [] Ladies and gentleman, ...
+% modprobe $MOD_TARGET2
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET2'
+$MOD_TARGET2: ${MOD_TARGET}_init
+% cat /sys/module/$MOD_TARGET2/parameters/welcome
+$MOD_LIVEPATCH: livepatch_speaker_welcome2: [] Ladies and gentleman, ...
+% rmmod $MOD_TARGET2
+$MOD_TARGET2: ${MOD_TARGET}_exit
+livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET2'
+% echo 0 > /sys/kernel/livepatch/$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: release_applause_callback: state 1
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% cat /sys/module/$MOD_TARGET/parameters/welcome
+$MOD_TARGET: speaker_welcome: Hello, World!
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+$MOD_TARGET: speaker_welcome: Hello, World!"
+
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [POC 6/7] livepatch: Remove the obsolete per-object callbacks
  2023-11-10 17:04 [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Petr Mladek
                   ` (4 preceding siblings ...)
  2023-11-10 17:04 ` [POC 5/7] livepatch: Convert klp module callbacks tests into livepatch module tests Petr Mladek
@ 2023-11-10 17:04 ` Petr Mladek
  2023-11-10 17:04 ` [POC 7/7] livepatching: Remove per-state version Petr Mladek
  2023-11-10 21:33 ` [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Josh Poimboeuf
  7 siblings, 0 replies; 12+ messages in thread
From: Petr Mladek @ 2023-11-10 17:04 UTC (permalink / raw)
  To: Josh Poimboeuf, Miroslav Benes
  Cc: Joe Lawrence, Nicolai Stange, live-patching, linux-kernel, Petr Mladek

All selftests have been migrated to the new per-state callbacks.
And the obsoleted per-object callbacks can be safely removed.

FIXME:

This patch is removing the sample module and documentation
without any replacement. They obviously have to be converted
for the state-callbacks.

It has been postponed until the approach has been approved
in the POC stage.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 Documentation/livepatch/callbacks.rst         | 133 ------------
 Documentation/livepatch/index.rst             |   1 -
 include/linux/livepatch.h                     |  25 ---
 kernel/livepatch/core.c                       |  29 ---
 kernel/livepatch/core.h                       |  33 ---
 kernel/livepatch/transition.c                 |   9 -
 samples/livepatch/Makefile                    |   3 -
 .../livepatch/livepatch-callbacks-busymod.c   |  60 ------
 samples/livepatch/livepatch-callbacks-demo.c  | 196 ------------------
 samples/livepatch/livepatch-callbacks-mod.c   |  41 ----
 10 files changed, 530 deletions(-)
 delete mode 100644 Documentation/livepatch/callbacks.rst
 delete mode 100644 samples/livepatch/livepatch-callbacks-busymod.c
 delete mode 100644 samples/livepatch/livepatch-callbacks-demo.c
 delete mode 100644 samples/livepatch/livepatch-callbacks-mod.c

diff --git a/Documentation/livepatch/callbacks.rst b/Documentation/livepatch/callbacks.rst
deleted file mode 100644
index 470944aa8658..000000000000
--- a/Documentation/livepatch/callbacks.rst
+++ /dev/null
@@ -1,133 +0,0 @@
-======================
-(Un)patching Callbacks
-======================
-
-Livepatch (un)patch-callbacks provide a mechanism for livepatch modules
-to execute callback functions when a kernel object is (un)patched.  They
-can be considered a **power feature** that **extends livepatching abilities**
-to include:
-
-  - Safe updates to global data
-
-  - "Patches" to init and probe functions
-
-  - Patching otherwise unpatchable code (i.e. assembly)
-
-In most cases, (un)patch callbacks will need to be used in conjunction
-with memory barriers and kernel synchronization primitives, like
-mutexes/spinlocks, or even stop_machine(), to avoid concurrency issues.
-
-1. Motivation
-=============
-
-Callbacks differ from existing kernel facilities:
-
-  - Module init/exit code doesn't run when disabling and re-enabling a
-    patch.
-
-  - A module notifier can't stop a to-be-patched module from loading.
-
-Callbacks are part of the klp_object structure and their implementation
-is specific to that klp_object.  Other livepatch objects may or may not
-be patched, irrespective of the target klp_object's current state.
-
-2. Callback types
-=================
-
-Callbacks can be registered for the following livepatch actions:
-
-  * Pre-patch
-                 - before a klp_object is patched
-
-  * Post-patch
-                 - after a klp_object has been patched and is active
-                   across all tasks
-
-  * Pre-unpatch
-                 - before a klp_object is unpatched (ie, patched code is
-                   active), used to clean up post-patch callback
-                   resources
-
-  * Post-unpatch
-                 - after a klp_object has been patched, all code has
-                   been restored and no tasks are running patched code,
-                   used to cleanup pre-patch callback resources
-
-3. How it works
-===============
-
-Each callback is optional, omitting one does not preclude specifying any
-other.  However, the livepatching core executes the handlers in
-symmetry: pre-patch callbacks have a post-unpatch counterpart and
-post-patch callbacks have a pre-unpatch counterpart.  An unpatch
-callback will only be executed if its corresponding patch callback was
-executed.  Typical use cases pair a patch handler that acquires and
-configures resources with an unpatch handler tears down and releases
-those same resources.
-
-A callback is only executed if its host klp_object is loaded.  For
-in-kernel vmlinux targets, this means that callbacks will always execute
-when a livepatch is enabled/disabled.  For patch target kernel modules,
-callbacks will only execute if the target module is loaded.  When a
-module target is (un)loaded, its callbacks will execute only if the
-livepatch module is enabled.
-
-The pre-patch callback, if specified, is expected to return a status
-code (0 for success, -ERRNO on error).  An error status code indicates
-to the livepatching core that patching of the current klp_object is not
-safe and to stop the current patching request.  (When no pre-patch
-callback is provided, the transition is assumed to be safe.)  If a
-pre-patch callback returns failure, the kernel's module loader will:
-
-  - Refuse to load a livepatch, if the livepatch is loaded after
-    targeted code.
-
-    or:
-
-  - Refuse to load a module, if the livepatch was already successfully
-    loaded.
-
-No post-patch, pre-unpatch, or post-unpatch callbacks will be executed
-for a given klp_object if the object failed to patch, due to a failed
-pre_patch callback or for any other reason.
-
-If a patch transition is reversed, no pre-unpatch handlers will be run
-(this follows the previously mentioned symmetry -- pre-unpatch callbacks
-will only occur if their corresponding post-patch callback executed).
-
-If the object did successfully patch, but the patch transition never
-started for some reason (e.g., if another object failed to patch),
-only the post-unpatch callback will be called.
-
-4. Use cases
-============
-
-Sample livepatch modules demonstrating the callback API can be found in
-samples/livepatch/ directory.  These samples were modified for use in
-kselftests and can be found in the lib/livepatch directory.
-
-Global data update
-------------------
-
-A pre-patch callback can be useful to update a global variable.  For
-example, 75ff39ccc1bd ("tcp: make challenge acks less predictable")
-changes a global sysctl, as well as patches the tcp_send_challenge_ack()
-function.
-
-In this case, if we're being super paranoid, it might make sense to
-patch the data *after* patching is complete with a post-patch callback,
-so that tcp_send_challenge_ack() could first be changed to read
-sysctl_tcp_challenge_ack_limit with READ_ONCE.
-
-__init and probe function patches support
------------------------------------------
-
-Although __init and probe functions are not directly livepatch-able, it
-may be possible to implement similar updates via pre/post-patch
-callbacks.
-
-The commit ``48900cb6af42 ("virtio-net: drop NETIF_F_FRAGLIST")`` change the way that
-virtnet_probe() initialized its driver's net_device features.  A
-pre/post-patch callback could iterate over all such devices, making a
-similar change to their hw_features value.  (Client functions of the
-value may need to be updated accordingly.)
diff --git a/Documentation/livepatch/index.rst b/Documentation/livepatch/index.rst
index cebf1c71d4a5..997b5ddf4779 100644
--- a/Documentation/livepatch/index.rst
+++ b/Documentation/livepatch/index.rst
@@ -8,7 +8,6 @@ Kernel Livepatching
     :maxdepth: 1
 
     livepatch
-    callbacks
     cumulative-patches
     module-elf-format
     shadow-vars
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 189ec7c6a89f..3807c7bb0281 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -77,30 +77,6 @@ struct klp_func {
 	bool transition;
 };
 
-struct klp_object;
-
-/**
- * struct klp_callbacks - pre/post live-(un)patch callback structure
- * @pre_patch:		executed before code patching
- * @post_patch:		executed after code patching
- * @pre_unpatch:	executed before code unpatching
- * @post_unpatch:	executed after code unpatching
- * @post_unpatch_enabled:	flag indicating if post-unpatch callback
- * 				should run
- *
- * All callbacks are optional.  Only the pre-patch callback, if provided,
- * will be unconditionally executed.  If the parent klp_object fails to
- * patch for any reason, including a non-zero error status returned from
- * the pre-patch callback, no further callbacks will be executed.
- */
-struct klp_callbacks {
-	int (*pre_patch)(struct klp_object *obj);
-	void (*post_patch)(struct klp_object *obj);
-	void (*pre_unpatch)(struct klp_object *obj);
-	void (*post_unpatch)(struct klp_object *obj);
-	bool post_unpatch_enabled;
-};
-
 /**
  * struct klp_object - kernel object structure for live patching
  * @name:	module name (or NULL for vmlinux)
@@ -118,7 +94,6 @@ struct klp_object {
 	/* external */
 	const char *name;
 	struct klp_func *funcs;
-	struct klp_callbacks callbacks;
 
 	/* internal */
 	struct kobject kobj;
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index a4a3fe7907ad..d982365777f1 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -965,8 +965,6 @@ static int klp_init_patch(struct klp_patch *patch)
 
 static int __klp_disable_patch(struct klp_patch *patch)
 {
-	struct klp_object *obj;
-
 	if (WARN_ON(!patch->enabled))
 		return -EINVAL;
 
@@ -977,10 +975,6 @@ static int __klp_disable_patch(struct klp_patch *patch)
 
 	klp_disable_states(patch);
 
-	klp_for_each_object(patch, obj)
-		if (obj->patched)
-			klp_pre_unpatch_callback(obj);
-
 	/*
 	 * Enforce the order of the func->transition writes in
 	 * klp_init_transition() and the TIF_PATCH_PENDING writes in
@@ -1032,13 +1026,6 @@ static int __klp_enable_patch(struct klp_patch *patch)
 		if (!klp_is_object_loaded(obj))
 			continue;
 
-		ret = klp_pre_patch_callback(obj);
-		if (ret) {
-			pr_warn("pre-patch callback failed for object '%s'\n",
-				klp_is_module(obj) ? obj->name : "vmlinux");
-			goto err_states;
-		}
-
 		ret = klp_patch_object(obj);
 		if (ret) {
 			pr_warn("failed to patch object '%s'\n",
@@ -1214,14 +1201,10 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
 			if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
 				continue;
 
-			if (patch != klp_transition_patch)
-				klp_pre_unpatch_callback(obj);
-
 			pr_notice("reverting patch '%s' on unloading module '%s'\n",
 				  patch->mod->name, obj->mod->name);
 			klp_unpatch_object(obj);
 
-			klp_post_unpatch_callback(obj);
 			klp_clear_object_relocs(patch, obj);
 			klp_free_object_loaded(obj);
 			break;
@@ -1268,25 +1251,13 @@ int klp_module_coming(struct module *mod)
 			pr_notice("applying patch '%s' to loading module '%s'\n",
 				  patch->mod->name, obj->mod->name);
 
-			ret = klp_pre_patch_callback(obj);
-			if (ret) {
-				pr_warn("pre-patch callback failed for object '%s'\n",
-					obj->name);
-				goto err;
-			}
-
 			ret = klp_patch_object(obj);
 			if (ret) {
 				pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
 					patch->mod->name, obj->mod->name, ret);
-
-				klp_post_unpatch_callback(obj);
 				goto err;
 			}
 
-			if (patch != klp_transition_patch)
-				klp_post_patch_callback(obj);
-
 			break;
 		}
 	}
diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h
index 38209c7361b6..02b8364f6779 100644
--- a/kernel/livepatch/core.h
+++ b/kernel/livepatch/core.h
@@ -23,37 +23,4 @@ static inline bool klp_is_object_loaded(struct klp_object *obj)
 	return !obj->name || obj->mod;
 }
 
-static inline int klp_pre_patch_callback(struct klp_object *obj)
-{
-	int ret = 0;
-
-	if (obj->callbacks.pre_patch)
-		ret = (*obj->callbacks.pre_patch)(obj);
-
-	obj->callbacks.post_unpatch_enabled = !ret;
-
-	return ret;
-}
-
-static inline void klp_post_patch_callback(struct klp_object *obj)
-{
-	if (obj->callbacks.post_patch)
-		(*obj->callbacks.post_patch)(obj);
-}
-
-static inline void klp_pre_unpatch_callback(struct klp_object *obj)
-{
-	if (obj->callbacks.pre_unpatch)
-		(*obj->callbacks.pre_unpatch)(obj);
-}
-
-static inline void klp_post_unpatch_callback(struct klp_object *obj)
-{
-	if (obj->callbacks.post_unpatch_enabled &&
-	    obj->callbacks.post_unpatch)
-		(*obj->callbacks.post_unpatch)(obj);
-
-	obj->callbacks.post_unpatch_enabled = false;
-}
-
 #endif /* _LIVEPATCH_CORE_H */
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
index cfa1ab10feb7..1826e08a31dd 100644
--- a/kernel/livepatch/transition.c
+++ b/kernel/livepatch/transition.c
@@ -148,15 +148,6 @@ static void klp_complete_transition(void)
 		klp_release_states(klp_transition_patch);
 	}
 
-	klp_for_each_object(klp_transition_patch, obj) {
-		if (!klp_is_object_loaded(obj))
-			continue;
-		if (klp_target_state == KLP_PATCHED)
-			klp_post_patch_callback(obj);
-		else if (klp_target_state == KLP_UNPATCHED)
-			klp_post_unpatch_callback(obj);
-	}
-
 	pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name,
 		  klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
 
diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile
index 9f853eeb6140..5ad205c61a67 100644
--- a/samples/livepatch/Makefile
+++ b/samples/livepatch/Makefile
@@ -3,6 +3,3 @@ obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o
 obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o
 obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o
 obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o
-obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo.o
-obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-mod.o
-obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-busymod.o
diff --git a/samples/livepatch/livepatch-callbacks-busymod.c b/samples/livepatch/livepatch-callbacks-busymod.c
deleted file mode 100644
index 378e2d40271a..000000000000
--- a/samples/livepatch/livepatch-callbacks-busymod.c
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
- */
-
-/*
- * livepatch-callbacks-busymod.c - (un)patching callbacks demo support module
- *
- *
- * Purpose
- * -------
- *
- * Simple module to demonstrate livepatch (un)patching callbacks.
- *
- *
- * Usage
- * -----
- *
- * This module is not intended to be standalone.  See the "Usage"
- * section of livepatch-callbacks-mod.c.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-
-static int sleep_secs;
-module_param(sleep_secs, int, 0644);
-MODULE_PARM_DESC(sleep_secs, "sleep_secs (default=0)");
-
-static void busymod_work_func(struct work_struct *work);
-static DECLARE_DELAYED_WORK(work, busymod_work_func);
-
-static void busymod_work_func(struct work_struct *work)
-{
-	pr_info("%s, sleeping %d seconds ...\n", __func__, sleep_secs);
-	msleep(sleep_secs * 1000);
-	pr_info("%s exit\n", __func__);
-}
-
-static int livepatch_callbacks_mod_init(void)
-{
-	pr_info("%s\n", __func__);
-	schedule_delayed_work(&work,
-		msecs_to_jiffies(1000 * 0));
-	return 0;
-}
-
-static void livepatch_callbacks_mod_exit(void)
-{
-	cancel_delayed_work_sync(&work);
-	pr_info("%s\n", __func__);
-}
-
-module_init(livepatch_callbacks_mod_init);
-module_exit(livepatch_callbacks_mod_exit);
-MODULE_LICENSE("GPL");
diff --git a/samples/livepatch/livepatch-callbacks-demo.c b/samples/livepatch/livepatch-callbacks-demo.c
deleted file mode 100644
index 11c3f4357812..000000000000
--- a/samples/livepatch/livepatch-callbacks-demo.c
+++ /dev/null
@@ -1,196 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
- */
-
-/*
- * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo
- *
- *
- * Purpose
- * -------
- *
- * Demonstration of registering livepatch (un)patching callbacks.
- *
- *
- * Usage
- * -----
- *
- * Step 1 - load the simple module
- *
- *   insmod samples/livepatch/livepatch-callbacks-mod.ko
- *
- *
- * Step 2 - load the demonstration livepatch (with callbacks)
- *
- *   insmod samples/livepatch/livepatch-callbacks-demo.ko
- *
- *
- * Step 3 - cleanup
- *
- *   echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- *   rmmod livepatch_callbacks_demo
- *   rmmod livepatch_callbacks_mod
- *
- * Watch dmesg output to see livepatch enablement, callback execution
- * and patching operations for both vmlinux and module targets.
- *
- * NOTE: swap the insmod order of livepatch-callbacks-mod.ko and
- *       livepatch-callbacks-demo.ko to observe what happens when a
- *       target module is loaded after a livepatch with callbacks.
- *
- * NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch
- *       callback return status.  Try setting up a non-zero status
- *       such as -19 (-ENODEV):
- *
- *       # Load demo livepatch, vmlinux is patched
- *       insmod samples/livepatch/livepatch-callbacks-demo.ko
- *
- *       # Setup next pre-patch callback to return -ENODEV
- *       echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret
- *
- *       # Module loader refuses to load the target module
- *       insmod samples/livepatch/livepatch-callbacks-mod.ko
- *       insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device
- *
- * NOTE: There is a second target module,
- *       livepatch-callbacks-busymod.ko, available for experimenting
- *       with livepatch (un)patch callbacks.  This module contains
- *       a 'sleep_secs' parameter that parks the module on one of the
- *       functions that the livepatch demo module wants to patch.
- *       Modifying this value and tweaking the order of module loads can
- *       effectively demonstrate stalled patch transitions:
- *
- *       # Load a target module, let it park on 'busymod_work_func' for
- *       # thirty seconds
- *       insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30
- *
- *       # Meanwhile load the livepatch
- *       insmod samples/livepatch/livepatch-callbacks-demo.ko
- *
- *       # ... then load and unload another target module while the
- *       # transition is in progress
- *       insmod samples/livepatch/livepatch-callbacks-mod.ko
- *       rmmod samples/livepatch/livepatch-callbacks-mod.ko
- *
- *       # Finally cleanup
- *       echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- *       rmmod samples/livepatch/livepatch-callbacks-demo.ko
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/livepatch.h>
-
-static int pre_patch_ret;
-module_param(pre_patch_ret, int, 0644);
-MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)");
-
-static const char *const module_state[] = {
-	[MODULE_STATE_LIVE]	= "[MODULE_STATE_LIVE] Normal state",
-	[MODULE_STATE_COMING]	= "[MODULE_STATE_COMING] Full formed, running module_init",
-	[MODULE_STATE_GOING]	= "[MODULE_STATE_GOING] Going away",
-	[MODULE_STATE_UNFORMED]	= "[MODULE_STATE_UNFORMED] Still setting it up",
-};
-
-static void callback_info(const char *callback, struct klp_object *obj)
-{
-	if (obj->mod)
-		pr_info("%s: %s -> %s\n", callback, obj->mod->name,
-			module_state[obj->mod->state]);
-	else
-		pr_info("%s: vmlinux\n", callback);
-}
-
-/* Executed on object patching (ie, patch enablement) */
-static int pre_patch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-	return pre_patch_ret;
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_patch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void pre_unpatch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-}
-
-/* Executed on object unpatching (ie, patch disablement) */
-static void post_unpatch_callback(struct klp_object *obj)
-{
-	callback_info(__func__, obj);
-}
-
-static void patched_work_func(struct work_struct *work)
-{
-	pr_info("%s\n", __func__);
-}
-
-static struct klp_func no_funcs[] = {
-	{ }
-};
-
-static struct klp_func busymod_funcs[] = {
-	{
-		.old_name = "busymod_work_func",
-		.new_func = patched_work_func,
-	}, { }
-};
-
-static struct klp_object objs[] = {
-	{
-		.name = NULL,	/* vmlinux */
-		.funcs = no_funcs,
-		.callbacks = {
-			.pre_patch = pre_patch_callback,
-			.post_patch = post_patch_callback,
-			.pre_unpatch = pre_unpatch_callback,
-			.post_unpatch = post_unpatch_callback,
-		},
-	},	{
-		.name = "livepatch_callbacks_mod",
-		.funcs = no_funcs,
-		.callbacks = {
-			.pre_patch = pre_patch_callback,
-			.post_patch = post_patch_callback,
-			.pre_unpatch = pre_unpatch_callback,
-			.post_unpatch = post_unpatch_callback,
-		},
-	},	{
-		.name = "livepatch_callbacks_busymod",
-		.funcs = busymod_funcs,
-		.callbacks = {
-			.pre_patch = pre_patch_callback,
-			.post_patch = post_patch_callback,
-			.pre_unpatch = pre_unpatch_callback,
-			.post_unpatch = post_unpatch_callback,
-		},
-	}, { }
-};
-
-static struct klp_patch patch = {
-	.mod = THIS_MODULE,
-	.objs = objs,
-};
-
-static int livepatch_callbacks_demo_init(void)
-{
-	return klp_enable_patch(&patch);
-}
-
-static void livepatch_callbacks_demo_exit(void)
-{
-}
-
-module_init(livepatch_callbacks_demo_init);
-module_exit(livepatch_callbacks_demo_exit);
-MODULE_LICENSE("GPL");
-MODULE_INFO(livepatch, "Y");
diff --git a/samples/livepatch/livepatch-callbacks-mod.c b/samples/livepatch/livepatch-callbacks-mod.c
deleted file mode 100644
index 2a074f422a51..000000000000
--- a/samples/livepatch/livepatch-callbacks-mod.c
+++ /dev/null
@@ -1,41 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
- */
-
-/*
- * livepatch-callbacks-mod.c - (un)patching callbacks demo support module
- *
- *
- * Purpose
- * -------
- *
- * Simple module to demonstrate livepatch (un)patching callbacks.
- *
- *
- * Usage
- * -----
- *
- * This module is not intended to be standalone.  See the "Usage"
- * section of livepatch-callbacks-demo.c.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-
-static int livepatch_callbacks_mod_init(void)
-{
-	pr_info("%s\n", __func__);
-	return 0;
-}
-
-static void livepatch_callbacks_mod_exit(void)
-{
-	pr_info("%s\n", __func__);
-}
-
-module_init(livepatch_callbacks_mod_init);
-module_exit(livepatch_callbacks_mod_exit);
-MODULE_LICENSE("GPL");
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [POC 7/7] livepatching: Remove per-state version
  2023-11-10 17:04 [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Petr Mladek
                   ` (5 preceding siblings ...)
  2023-11-10 17:04 ` [POC 6/7] livepatch: Remove the obsolete per-object callbacks Petr Mladek
@ 2023-11-10 17:04 ` Petr Mladek
  2023-11-10 21:33 ` [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Josh Poimboeuf
  7 siblings, 0 replies; 12+ messages in thread
From: Petr Mladek @ 2023-11-10 17:04 UTC (permalink / raw)
  To: Josh Poimboeuf, Miroslav Benes
  Cc: Joe Lawrence, Nicolai Stange, live-patching, linux-kernel, Petr Mladek

The livepatch state API was added to help with maintaining:

   + changes done by livepatch callbasks
   + lifetime of shadow variables

The original API was hard to use. Both objectives are better handled
by the new per-state callbacks. They are called when the state is
introduced or removed. There is also support for automatically freeing
obsolete shadow variables.

The new callbacks changed the view of compatibility.  The livepatch
can be replaced to any older one as long the current livepatch is
able to disable the obsolete state.

As a result, the new patch does not need to support the currently
used states. The current patch will be able to disable them.

The remaining question is what to do with the per-state version.
It was supposed to allow doing more modifications on an existing
state. The experience shows that it is not needed in practice.

Well, it still might make sense to prevent downgrade when the state
could not be disabled easily or when the author does not want to
deal with it.

Replace the per-state version with per-state block_disable flag.
It allows to handle several scenarios:

  + prevent disabling the livepatch when it does not support
    a particular state disablement.

  + prevent replacing the livepatch when the state is not supported
    by the new patch and the current patch does not support
    a particular state disablement.

  + allow to replace the livepatch with a new one which would
    support the particular state disablement.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 include/linux/livepatch.h                     |   7 +-
 kernel/livepatch/core.c                       |  17 ++-
 kernel/livepatch/state.c                      |  27 +++--
 kernel/livepatch/state.h                      |   1 +
 lib/livepatch/test_klp_state.c                | 101 +++++++++++++-----
 lib/livepatch/test_klp_state2.c               |   2 -
 lib/livepatch/test_klp_state3.c               |   2 +-
 .../testing/selftests/livepatch/functions.sh  |  17 +++
 .../testing/selftests/livepatch/test-state.sh |  40 ++++---
 9 files changed, 158 insertions(+), 56 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 3807c7bb0281..c7c2c013b380 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -140,16 +140,16 @@ struct klp_state_callbacks {
 /**
  * struct klp_state - state of the system modified by the livepatch
  * @id:		system state identifier (non-zero)
- * @version:	version of the change
  * @callbacks:	optional callbacks used when introducing or removing the state
+ * @block_disable: the state disablement is not supported
  * @is_shadow:  the state handles lifetime of a shadow variable
  *		with the same @id
  * @data:	custom data
  */
 struct klp_state {
 	unsigned long id;
-	unsigned int version;
 	struct klp_state_callbacks callbacks;
+	bool block_disable;
 	bool is_shadow;
 	void *data;
 };
@@ -159,7 +159,9 @@ struct klp_state {
  * @mod:	reference to the live patch module
  * @objs:	object entries for kernel objects to be patched
  * @states:	system states that can get modified
+ * version:	livepatch version (optional)
  * @replace:	replace all actively used patches
+ *
  * @list:	list node for global list of actively used patches
  * @kobj:	kobject for sysfs resources
  * @obj_list:	dynamic list of the object entries
@@ -173,6 +175,7 @@ struct klp_patch {
 	struct module *mod;
 	struct klp_object *objs;
 	struct klp_state *states;
+	unsigned int version;
 	bool replace;
 
 	/* internal */
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index d982365777f1..81fbe0778742 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -373,6 +373,13 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
 		goto out;
 	}
 
+	if (patch->enabled && klp_patch_disable_blocked(patch)) {
+		pr_err("The livepatch '%s' does not support disable\n",
+		       patch->mod->name);
+		ret = -EINVAL;
+		goto out;
+	}
+
 	/*
 	 * Allow to reverse a pending transition in both ways. It might be
 	 * necessary to complete the transition without forcing and breaking
@@ -1097,10 +1104,10 @@ int klp_enable_patch(struct klp_patch *patch)
 
 	if (!klp_is_patch_compatible(patch)) {
 		pr_err("Livepatch patch (%s) is not compatible with the already installed livepatches.\n",
-			patch->mod->name);
+		       patch->mod->name);
 		mutex_unlock(&klp_mutex);
 		return -EINVAL;
-	}
+       }
 
 	if (!try_module_get(patch->mod)) {
 		mutex_unlock(&klp_mutex);
@@ -1111,17 +1118,17 @@ int klp_enable_patch(struct klp_patch *patch)
 
 	ret = klp_init_patch(patch);
 	if (ret)
-		goto err;
+		goto unlock_free;
 
 	ret = __klp_enable_patch(patch);
 	if (ret)
-		goto err;
+		goto unlock_free;
 
 	mutex_unlock(&klp_mutex);
 
 	return 0;
 
-err:
+unlock_free:
 	klp_free_patch_start(patch);
 
 	mutex_unlock(&klp_mutex);
diff --git a/kernel/livepatch/state.c b/kernel/livepatch/state.c
index 4ec65afe3a43..0647cc688f38 100644
--- a/kernel/livepatch/state.c
+++ b/kernel/livepatch/state.c
@@ -91,23 +91,26 @@ static bool klp_is_state_compatible(struct klp_patch *patch,
 
 	state = klp_get_state(patch, old_state->id);
 
-	/* A cumulative livepatch must handle all already modified states. */
-	if (!state)
-		return !patch->replace;
+	if (!state && old_state->block_disable)
+		return false;
 
-	return state->version >= old_state->version;
+	return true;
 }
 
 /*
  * Check that the new livepatch will not break the existing system states.
- * Cumulative patches must handle all already modified states.
- * Non-cumulative patches can touch already modified states.
+ * The patch could replace existing patches only when the obsolete
+ * states can be disabled.
  */
 bool klp_is_patch_compatible(struct klp_patch *patch)
 {
 	struct klp_patch *old_patch;
 	struct klp_state *old_state;
 
+	/* Non-cumulative patches are always compatible. */
+	if (!patch->replace)
+		return true;
+
 	klp_for_each_patch(old_patch) {
 		klp_for_each_state(old_patch, old_state) {
 			if (!klp_is_state_compatible(patch, old_state))
@@ -118,6 +121,18 @@ bool klp_is_patch_compatible(struct klp_patch *patch)
 	return true;
 }
 
+bool klp_patch_disable_blocked(struct klp_patch *patch)
+{
+	struct klp_state *state;
+
+	klp_for_each_state(patch, state) {
+		if (state->block_disable)
+			return true;
+	}
+
+	return false;
+}
+
 bool is_state_in_other_patches(struct klp_patch *patch, struct klp_state *state)
 {
 	struct klp_patch *old_patch;
diff --git a/kernel/livepatch/state.h b/kernel/livepatch/state.h
index e9940e7f00dd..74cd9d8f63c5 100644
--- a/kernel/livepatch/state.h
+++ b/kernel/livepatch/state.h
@@ -5,6 +5,7 @@
 #include <linux/livepatch.h>
 
 bool klp_is_patch_compatible(struct klp_patch *patch);
+bool klp_patch_disable_blocked(struct klp_patch *patch);
 int klp_setup_states(struct klp_patch *patch);
 void klp_enable_states(struct klp_patch *patch);
 void klp_disable_states(struct klp_patch *patch);
diff --git a/lib/livepatch/test_klp_state.c b/lib/livepatch/test_klp_state.c
index b3d1ee48dfcc..ec9bf4f37b26 100644
--- a/lib/livepatch/test_klp_state.c
+++ b/lib/livepatch/test_klp_state.c
@@ -11,17 +11,6 @@
 
 #define CONSOLE_LOGLEVEL_FIX_ID 1
 
-/*
- * Version of the state which defines compatibility of livepaches.
- * The value is artificial. It set just for testing the compatibility
- * checks. In reality, all versions are compatible because all
- * the callbacks do nothing and the shadow variable clean up
- * is done by the core.
- */
-#ifndef CONSOLE_LOGLEVEL_FIX_VERSION
-#define CONSOLE_LOGLEVEL_FIX_VERSION 1
-#endif
-
 static struct klp_patch patch;
 
 static int allocate_loglevel_state(void)
@@ -115,6 +104,80 @@ static void release_state_callback(struct klp_patch *patch, struct klp_state *st
 	free_loglevel_state();
 }
 
+static struct klp_state states[] = {
+	{
+		.id = CONSOLE_LOGLEVEL_FIX_ID,
+		.callbacks = {
+			.setup = setup_state_callback,
+			.enable = enable_state_callback,
+			.disable = disable_state_callback,
+			.release = release_state_callback,
+		},
+	}, { }
+};
+
+static int block_state_disable_get(char *buffer, const struct kernel_param *kp)
+{
+	pr_info("%s: Disable transition is %s by state: %lu\n",
+		__func__,
+		states[0].block_disable ? "not supported" : "supported",
+		states[0].id);
+
+	return 0;
+}
+
+static int block_state_disable_set(const char *val, const struct kernel_param *kp)
+{
+	bool block;
+	int ret;
+
+	ret = kstrtobool(val, &block);
+	if (ret)
+		return ret;
+
+	states[0].block_disable = block;
+
+	return 0;
+}
+
+static const struct kernel_param_ops block_state_disable_ops = {
+	.get	= block_state_disable_get,
+	.set	= block_state_disable_set,
+};
+
+module_param_cb(block_state_disable, &block_state_disable_ops, NULL, 0600);
+MODULE_PARM_DESC(block_state_disable, "Set to 1 to pretend that the state does not support disable operation (default = 0).");
+
+bool no_state;
+
+static int no_state_get(char *buffer, const struct kernel_param *kp)
+{
+	return sysfs_emit("%s", no_state ? "1" : "0");
+}
+
+static int no_state_set(const char *val, const struct kernel_param *kp)
+{
+	bool no;
+	int ret;
+
+	ret = kstrtobool(val, &no);
+	if (ret)
+		return ret;
+
+	no_state = no;
+
+	return 0;
+}
+
+static const struct kernel_param_ops no_state_ops = {
+	.get	= no_state_get,
+	.set	= no_state_set,
+};
+
+module_param_cb(no_state, &no_state_ops, NULL, 0400);
+MODULE_PARM_DESC(no_state, "Set to 1 when the livepatch should not support the state. (default = 0).");
+
+
 static struct klp_func no_funcs[] = {
 	{}
 };
@@ -126,19 +189,6 @@ static struct klp_object objs[] = {
 	}, { }
 };
 
-static struct klp_state states[] = {
-	{
-		.id = CONSOLE_LOGLEVEL_FIX_ID,
-		.version = CONSOLE_LOGLEVEL_FIX_VERSION,
-		.callbacks = {
-			.setup = setup_state_callback,
-			.enable = enable_state_callback,
-			.disable = disable_state_callback,
-			.release = release_state_callback,
-		},
-	}, { }
-};
-
 static struct klp_patch patch = {
 	.mod = THIS_MODULE,
 	.objs = objs,
@@ -148,6 +198,9 @@ static struct klp_patch patch = {
 
 static int test_klp_callbacks_demo_init(void)
 {
+	if (no_state)
+		patch.states = NULL;
+
 	return klp_enable_patch(&patch);
 }
 
diff --git a/lib/livepatch/test_klp_state2.c b/lib/livepatch/test_klp_state2.c
index 128855764bf8..b8fe1ca2d802 100644
--- a/lib/livepatch/test_klp_state2.c
+++ b/lib/livepatch/test_klp_state2.c
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
 // Copyright (C) 2019 SUSE
 
-#define CONSOLE_LOGLEVEL_FIX_VERSION 2
-
 /* The console loglevel fix is the same in the next cumulative patch. */
 #include "test_klp_state.c"
diff --git a/lib/livepatch/test_klp_state3.c b/lib/livepatch/test_klp_state3.c
index 9226579d10c5..b8fe1ca2d802 100644
--- a/lib/livepatch/test_klp_state3.c
+++ b/lib/livepatch/test_klp_state3.c
@@ -2,4 +2,4 @@
 // Copyright (C) 2019 SUSE
 
 /* The console loglevel fix is the same in the next cumulative patch. */
-#include "test_klp_state2.c"
+#include "test_klp_state.c"
diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh
index a962119f053e..5e2569bff126 100644
--- a/tools/testing/selftests/livepatch/functions.sh
+++ b/tools/testing/selftests/livepatch/functions.sh
@@ -277,6 +277,23 @@ function set_pre_patch_ret {
 		die "failed to set pre_patch_ret parameter for $mod module"
 }
 
+# set_module_param(modname, param, val)
+#	modname - module name to set
+#	param - name of the parameter to set
+#       val - value to set
+function set_module_param {
+	local mod="$1"; shift
+	local param="$1"; shift
+	local val="$1"
+
+	log "% echo $val > /sys/module/$mod/parameters/$param"
+	echo "$val" > "/sys/module/$mod/parameters/$param"
+
+	# Wait for sysfs value to hold ...
+	loop_until '[[ $(cat "/sys/module/$mod/parameters/$param") == "$val" ]]' ||
+		die "failed to set parameter $param for $mod module to the value $val"
+}
+
 # read_module_param(modname, param)
 #	modname - module name which provides the given parameter
 #	param - parameter name to be read
diff --git a/tools/testing/selftests/livepatch/test-state.sh b/tools/testing/selftests/livepatch/test-state.sh
index a3c933ea96fc..72387b5775c6 100755
--- a/tools/testing/selftests/livepatch/test-state.sh
+++ b/tools/testing/selftests/livepatch/test-state.sh
@@ -132,12 +132,14 @@ livepatch: '$MOD_LIVEPATCH2': unpatching complete
 
 start_test "incompatible cumulative livepatches"
 
-load_lp $MOD_LIVEPATCH2
-load_failing_mod $MOD_LIVEPATCH
-disable_lp $MOD_LIVEPATCH2
-unload_lp $MOD_LIVEPATCH2
+load_lp $MOD_LIVEPATCH2 block_state_disable=1
+load_failing_mod $MOD_LIVEPATCH no_state=1
+# load the livepatch again with default features (state and disable supported)
+load_lp $MOD_LIVEPATCH
+disable_lp $MOD_LIVEPATCH
+unload_lp $MOD_LIVEPATCH
 
-check_result "% modprobe $MOD_LIVEPATCH2
+check_result "% modprobe $MOD_LIVEPATCH2 block_state_disable=1
 livepatch: enabling patch '$MOD_LIVEPATCH2'
 livepatch: '$MOD_LIVEPATCH2': initializing patching transition
 $MOD_LIVEPATCH2: setup_state_callback: state 1
@@ -147,18 +149,24 @@ livepatch: '$MOD_LIVEPATCH2': completing patching transition
 $MOD_LIVEPATCH2: enable_state_callback: state 1
 $MOD_LIVEPATCH2: fix_console_loglevel: fixing console_loglevel
 livepatch: '$MOD_LIVEPATCH2': patching complete
-% modprobe $MOD_LIVEPATCH
+% modprobe $MOD_LIVEPATCH no_state=1
 livepatch: Livepatch patch ($MOD_LIVEPATCH) is not compatible with the already installed livepatches.
 modprobe: ERROR: could not insert '$MOD_LIVEPATCH': Invalid argument
-% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
-livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
-$MOD_LIVEPATCH2: disable_state_callback: state 1
-$MOD_LIVEPATCH2: restore_console_loglevel: restoring console_loglevel
-livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
-livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
-$MOD_LIVEPATCH2: release_state_callback: state 1
-$MOD_LIVEPATCH2: free_loglevel_state: freeing space for the stored console_loglevel
-livepatch: '$MOD_LIVEPATCH2': unpatching complete
-% rmmod $MOD_LIVEPATCH2"
+% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: disable_state_callback: state 1
+$MOD_LIVEPATCH: restore_console_loglevel: restoring console_loglevel
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: release_state_callback: state 1
+$MOD_LIVEPATCH: free_loglevel_state: freeing space for the stored console_loglevel
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
 
 exit 0
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together
  2023-11-10 17:04 [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Petr Mladek
                   ` (6 preceding siblings ...)
  2023-11-10 17:04 ` [POC 7/7] livepatching: Remove per-state version Petr Mladek
@ 2023-11-10 21:33 ` Josh Poimboeuf
  7 siblings, 0 replies; 12+ messages in thread
From: Josh Poimboeuf @ 2023-11-10 21:33 UTC (permalink / raw)
  To: Petr Mladek
  Cc: Miroslav Benes, Joe Lawrence, Nicolai Stange, live-patching,
	linux-kernel

On Fri, Nov 10, 2023 at 06:04:21PM +0100, Petr Mladek wrote:
> This POC is a material for the discussion "Simplify Livepatch Callbacks,
> Shadow Variables, and States handling" at LPC 2013, see
> https://lpc.events/event/17/contributions/1541/
> 
> It obsoletes the patchset adding the garbage collection of shadow
> variables. This new solution is based on ideas from Nicolai Stange.
> And it should also be in sync with Josh's ideas mentioned into
> the thread about the garbage collection, see
> https://lore.kernel.org/r/20230204235910.4j4ame5ntqogqi7m@treble

Nice!  I like how it brings the "features" together and makes them easy
to use.  This looks like a vast improvement.

Was there a reason to change the naming?  I'm thinking

  setup / enable / disable / release

is less precise than

  pre_patch / post_patch / pre_unpatch / post_unpatch.

Also, I'm thinking "replaced" instead of "obsolete" would be more
consistent with the existing terminology.

For example, in __klp_enable_patch():

	ret = klp_setup_states(patch);
	if (ret)
		goto err;

	if (patch->replace)
		klp_disable_obsolete_states(patch);

it's not immediately clear why "disable obsolete" would be the "replace"
counterpart to "setup".

Similarly, in klp_complete_transition():

	if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) {
		klp_unpatch_replaced_patches(klp_transition_patch);
		klp_discard_nops(klp_transition_patch);
		klp_release_obsolete_states(klp_transition_patch);
	}

it's a little jarring to have "unpatch replaced" followed by "release
obsolete".

So instead of:

  int  klp_setup_states(struct klp_patch *patch);
  void klp_enable_states(struct klp_patch *patch);
  void klp_disable_states(struct klp_patch *patch);
  void klp_release_states(struct klp_patch *patch);

  void klp_enable_obsolete_states(struct klp_patch *patch);
  void klp_disable_obsolete_states(struct klp_patch *patch);
  void klp_release_obsolete_states(struct klp_patch *patch);

how about something like:

  int  klp_states_pre_patch(void);
  void klp_states_post_patch(void);
  void klp_states_pre_unpatch(void);
  void klp_states_post_unpatch(void);

  void klp_states_post_patch_replaced(void);
  void klp_states_pre_unpatch_replaced(void);
  void klp_states_post_unpatch_replaced(void);

?

(note that passing klp_transition_patch might be optional since state.c
already has visibility to it anyway)

-- 
Josh

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [POC 1/7] livepatch: Add callbacks for introducing and removing states
  2023-11-10 17:04 ` [POC 1/7] livepatch: Add callbacks for introducing and removing states Petr Mladek
@ 2023-11-11  0:54   ` kernel test robot
  2023-11-11  3:19   ` kernel test robot
  1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2023-11-11  0:54 UTC (permalink / raw)
  To: Petr Mladek, Josh Poimboeuf, Miroslav Benes
  Cc: oe-kbuild-all, Joe Lawrence, Nicolai Stange, live-patching,
	linux-kernel, Petr Mladek

Hi Petr,

kernel test robot noticed the following build warnings:

[auto build test WARNING on shuah-kselftest/next]
[also build test WARNING on shuah-kselftest/fixes linus/master v6.6 next-20231110]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Petr-Mladek/livepatch-Add-callbacks-for-introducing-and-removing-states/20231111-014906
base:   https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git next
patch link:    https://lore.kernel.org/r/20231110170428.6664-2-pmladek%40suse.com
patch subject: [POC 1/7] livepatch: Add callbacks for introducing and removing states
config: x86_64-randconfig-006-20231111 (https://download.01.org/0day-ci/archive/20231111/202311110829.UKC9GiUG-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231111/202311110829.UKC9GiUG-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311110829.UKC9GiUG-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> kernel/livepatch/state.c:121:6: warning: no previous prototype for 'is_state_in_other_patches' [-Wmissing-prototypes]
     121 | bool is_state_in_other_patches(struct klp_patch *patch, struct klp_state *state)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~


vim +/is_state_in_other_patches +121 kernel/livepatch/state.c

   120	
 > 121	bool is_state_in_other_patches(struct klp_patch *patch, struct klp_state *state)
   122	{
   123		struct klp_patch *old_patch;
   124		struct klp_state *old_state;
   125	
   126		klp_for_each_patch(old_patch) {
   127			if (old_patch == patch)
   128				continue;
   129	
   130			klp_for_each_state(old_patch, old_state) {
   131				if (old_state->id == state->id)
   132					return true;
   133			}
   134		}
   135	
   136		return false;
   137	}
   138	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [POC 5/7] livepatch: Convert klp module callbacks tests into livepatch module tests
  2023-11-10 17:04 ` [POC 5/7] livepatch: Convert klp module callbacks tests into livepatch module tests Petr Mladek
@ 2023-11-11  1:15   ` kernel test robot
  0 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2023-11-11  1:15 UTC (permalink / raw)
  To: Petr Mladek, Josh Poimboeuf, Miroslav Benes
  Cc: oe-kbuild-all, Joe Lawrence, Nicolai Stange, live-patching,
	linux-kernel, Petr Mladek

Hi Petr,

kernel test robot noticed the following build errors:

[auto build test ERROR on shuah-kselftest/next]
[also build test ERROR on shuah-kselftest/fixes linus/master v6.6 next-20231110]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Petr-Mladek/livepatch-Add-callbacks-for-introducing-and-removing-states/20231111-014906
base:   https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git next
patch link:    https://lore.kernel.org/r/20231110170428.6664-6-pmladek%40suse.com
patch subject: [POC 5/7] livepatch: Convert klp module callbacks tests into livepatch module tests
config: s390-defconfig (https://download.01.org/0day-ci/archive/20231111/202311110928.Ui5NizhT-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231111/202311110928.Ui5NizhT-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311110928.Ui5NizhT-lkp@intel.com/

All errors (new ones prefixed by >>):

>> make[5]: *** No rule to make target 'lib/livepatch/test_klp_speaker2.o', needed by 'lib/livepatch/'.
>> make[5]: *** No rule to make target 'lib/livepatch/test_klp_speaker_livepatch2.o', needed by 'lib/livepatch/'.
   make[5]: *** [scripts/Makefile.build:243: lib/livepatch/test_klp_speaker.o] Error 1
   make[5]: *** [scripts/Makefile.build:243: lib/livepatch/test_klp_speaker_livepatch.o] Error 1
   make[5]: Target 'lib/livepatch/' not remade because of errors.

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [POC 1/7] livepatch: Add callbacks for introducing and removing states
  2023-11-10 17:04 ` [POC 1/7] livepatch: Add callbacks for introducing and removing states Petr Mladek
  2023-11-11  0:54   ` kernel test robot
@ 2023-11-11  3:19   ` kernel test robot
  1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2023-11-11  3:19 UTC (permalink / raw)
  To: Petr Mladek, Josh Poimboeuf, Miroslav Benes
  Cc: llvm, oe-kbuild-all, Joe Lawrence, Nicolai Stange, live-patching,
	linux-kernel, Petr Mladek

Hi Petr,

kernel test robot noticed the following build warnings:

[auto build test WARNING on shuah-kselftest/next]
[also build test WARNING on shuah-kselftest/fixes linus/master v6.6 next-20231110]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Petr-Mladek/livepatch-Add-callbacks-for-introducing-and-removing-states/20231111-014906
base:   https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git next
patch link:    https://lore.kernel.org/r/20231110170428.6664-2-pmladek%40suse.com
patch subject: [POC 1/7] livepatch: Add callbacks for introducing and removing states
config: x86_64-rhel-8.3-rust (https://download.01.org/0day-ci/archive/20231111/202311111107.avVIpRi2-lkp@intel.com/config)
compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231111/202311111107.avVIpRi2-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311111107.avVIpRi2-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> kernel/livepatch/state.c:121:6: warning: no previous prototype for function 'is_state_in_other_patches' [-Wmissing-prototypes]
   bool is_state_in_other_patches(struct klp_patch *patch, struct klp_state *state)
        ^
   kernel/livepatch/state.c:121:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   bool is_state_in_other_patches(struct klp_patch *patch, struct klp_state *state)
   ^
   static 
   1 warning generated.


vim +/is_state_in_other_patches +121 kernel/livepatch/state.c

   120	
 > 121	bool is_state_in_other_patches(struct klp_patch *patch, struct klp_state *state)
   122	{
   123		struct klp_patch *old_patch;
   124		struct klp_state *old_state;
   125	
   126		klp_for_each_patch(old_patch) {
   127			if (old_patch == patch)
   128				continue;
   129	
   130			klp_for_each_state(old_patch, old_state) {
   131				if (old_state->id == state->id)
   132					return true;
   133			}
   134		}
   135	
   136		return false;
   137	}
   138	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2023-11-11  3:20 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-10 17:04 [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Petr Mladek
2023-11-10 17:04 ` [POC 1/7] livepatch: Add callbacks for introducing and removing states Petr Mladek
2023-11-11  0:54   ` kernel test robot
2023-11-11  3:19   ` kernel test robot
2023-11-10 17:04 ` [POC 2/7] livepatch: Allow to handle lifetime of shadow variables using the livepatch state Petr Mladek
2023-11-10 17:04 ` [POC 3/7] livepatch: Use per-state callbacks in state API tests Petr Mladek
2023-11-10 17:04 ` [POC 4/7] livepatch: Do not use callbacks when testing sysfs interface Petr Mladek
2023-11-10 17:04 ` [POC 5/7] livepatch: Convert klp module callbacks tests into livepatch module tests Petr Mladek
2023-11-11  1:15   ` kernel test robot
2023-11-10 17:04 ` [POC 6/7] livepatch: Remove the obsolete per-object callbacks Petr Mladek
2023-11-10 17:04 ` [POC 7/7] livepatching: Remove per-state version Petr Mladek
2023-11-10 21:33 ` [POC 0/7] livepatch: Make livepatch states, callbacks, and shadow variables work together Josh Poimboeuf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).