[PATCH v0 2/3] livepatch: update documentation/samples for callbacks

From: Joe Lawrence
Date: Fri Feb 23 2018 - 16:34:05 EST


Update livepatch callback documentation and samples with respect to new
atomic replace / cumulative patch functionality.

Signed-off-by: Joe Lawrence <joe.lawrence@xxxxxxxxxx>
---
Documentation/livepatch/callbacks.txt | 102 ++++++++++++++++
samples/livepatch/Makefile | 1 +
samples/livepatch/livepatch-callbacks-demo2.c | 162 ++++++++++++++++++++++++++
3 files changed, 265 insertions(+)
create mode 100644 samples/livepatch/livepatch-callbacks-demo2.c

diff --git a/Documentation/livepatch/callbacks.txt b/Documentation/livepatch/callbacks.txt
index c9776f48e458..b5e67975c5a9 100644
--- a/Documentation/livepatch/callbacks.txt
+++ b/Documentation/livepatch/callbacks.txt
@@ -86,6 +86,13 @@ 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.

+If a livepatch is replaced by a cumulative patch, then only the
+callbacks belonging to the cumulative patch will be executed. This
+simplifies the livepatching core for it is the responsibility of the
+cumulative patch to safely revert whatever needs to be reverted. See
+Documentation/livepatch/cumulative.txt for more information on such
+patches.
+

Example Use-cases
=================
@@ -603,3 +610,98 @@ pre-unpatch callbacks are skipped:
% rmmod samples/livepatch/livepatch-callbacks-busymod.ko
[ 141.279111] livepatch_callbacks_busymod: busymod_work_func exit
[ 141.279760] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit
+
+
+Test 10
+-------
+
+Test loading multiple livepatch modules containing callback routines.
+The livepatching core executes callbacks for all modules.
+
+- load livepatch
+- load second livepatch
+- disable livepatch
+- disable second livepatch
+- unload livepatch
+- unload second livepatch
+
+ % insmod samples/livepatch/livepatch-callbacks-demo.ko
+ [ 216.448208] livepatch: enabling patch 'livepatch_callbacks_demo'
+ [ 216.448211] livepatch: 'livepatch_callbacks_demo': initializing patching transition
+ [ 216.448330] livepatch_callbacks_demo: pre_patch_callback: vmlinux
+ [ 216.448341] livepatch: 'livepatch_callbacks_demo': starting patching transition
+ [ 218.720099] livepatch: 'livepatch_callbacks_demo': completing patching transition
+ [ 218.720179] livepatch_callbacks_demo: post_patch_callback: vmlinux
+ [ 218.720180] livepatch: 'livepatch_callbacks_demo': patching complete
+
+ % insmod samples/livepatch/livepatch-callbacks-demo2.ko
+ [ 220.126552] livepatch: enabling patch 'livepatch_callbacks_demo2'
+ [ 220.126554] livepatch: 'livepatch_callbacks_demo2': initializing patching transition
+ [ 220.126592] livepatch_callbacks_demo2: pre_patch_callback: vmlinux
+ [ 220.126593] livepatch: 'livepatch_callbacks_demo2': starting patching transition
+ [ 221.728091] livepatch: 'livepatch_callbacks_demo2': completing patching transition
+ [ 221.728254] livepatch_callbacks_demo2: post_patch_callback: vmlinux
+ [ 221.728255] livepatch: 'livepatch_callbacks_demo2': patching complete
+
+ % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo2/enabled
+ [ 223.434556] livepatch: 'livepatch_callbacks_demo2': initializing unpatching transition
+ [ 223.434616] livepatch_callbacks_demo2: pre_unpatch_callback: vmlinux
+ [ 223.434617] livepatch: 'livepatch_callbacks_demo2': starting unpatching transition
+ [ 224.736159] livepatch: 'livepatch_callbacks_demo2': completing unpatching transition
+ [ 224.736660] livepatch_callbacks_demo2: post_unpatch_callback: vmlinux
+ [ 224.736662] livepatch: 'livepatch_callbacks_demo2': unpatching complete
+
+ % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
+ [ 227.284070] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition
+ [ 227.284111] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux
+ [ 227.284112] livepatch: 'livepatch_callbacks_demo': starting unpatching transition
+ [ 228.704142] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
+ [ 228.704215] livepatch_callbacks_demo: post_unpatch_callback: vmlinux
+ [ 228.704216] livepatch: 'livepatch_callbacks_demo': unpatching complete
+
+ % rmmod samples/livepatch/livepatch-callbacks-demo2.ko
+ % rmmod samples/livepatch/livepatch-callbacks-demo.ko
+
+
+Test 11
+-------
+
+A similar test as the previous one, except this time load the second
+callback demo module as a cumulative (ie, replacement) patch. The
+livepatching core will only execute klp_object callbacks for the latest
+cumulative patch on the patch stack.
+
+- load livepatch
+- load second livepatch (atomic replace)
+- disable livepatch
+- disable second livepatch
+- unload livepatch
+- unload second livepatch
+
+ % insmod samples/livepatch/livepatch-callbacks-demo.ko
+ [16435.711175] livepatch: enabling patch 'livepatch_callbacks_demo'
+ [16435.711185] livepatch: 'livepatch_callbacks_demo': initializing patching transition
+ [16435.711271] livepatch_callbacks_demo: pre_patch_callback: vmlinux
+ [16435.711297] livepatch: 'livepatch_callbacks_demo': starting patching transition
+ [16436.704092] livepatch: 'livepatch_callbacks_demo': completing patching transition
+ [16436.704363] livepatch_callbacks_demo: post_patch_callback: vmlinux
+ [16436.704364] livepatch: 'livepatch_callbacks_demo': patching complete
+
+ % insmod samples/livepatch/livepatch-callbacks-demo2.ko replace=1
+ [16442.760963] livepatch: enabling patch 'livepatch_callbacks_demo2'
+ [16442.760966] livepatch: 'livepatch_callbacks_demo2': initializing patching transition
+ [16442.761018] livepatch_callbacks_demo2: pre_patch_callback: vmlinux
+ [16442.761018] livepatch: 'livepatch_callbacks_demo2': starting patching transition
+ [16444.704092] livepatch: 'livepatch_callbacks_demo2': completing patching transition
+ [16444.704181] livepatch_callbacks_demo2: post_patch_callback: vmlinux
+ [16444.704181] livepatch: 'livepatch_callbacks_demo2': patching complete
+
+ % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo2/enabled
+ [16448.434672] livepatch: 'livepatch_callbacks_demo2': initializing unpatching transition
+ [16448.434712] livepatch: 'livepatch_callbacks_demo2': starting unpatching transition
+ [16449.760134] livepatch: 'livepatch_callbacks_demo2': completing unpatching transition
+ [16449.760338] livepatch: 'livepatch_callbacks_demo2': unpatching complete
+ ** TODO ** where are the demo2 unpatch callbacks?
+
+ % rmmod samples/livepatch/livepatch-callbacks-demo2.ko
+ % rmmod samples/livepatch/livepatch-callbacks-demo.ko
diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile
index dd0e2a8af1af..9fb4d9b845bb 100644
--- a/samples/livepatch/Makefile
+++ b/samples/livepatch/Makefile
@@ -3,6 +3,7 @@ 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-demo2.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-mod.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-busymod.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-cumulative.o
diff --git a/samples/livepatch/livepatch-callbacks-demo2.c b/samples/livepatch/livepatch-callbacks-demo2.c
new file mode 100644
index 000000000000..bc07ba7e0568
--- /dev/null
+++ b/samples/livepatch/livepatch-callbacks-demo2.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 Joe Lawrence <joe.lawrence@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * livepatch-callbacks-demo2.c - (un)patching callbacks livepatch demo
+ *
+ *
+ * Purpose
+ * -------
+ *
+ * Demonstration of registering livepatch (un)patching callbacks and
+ * their behavior in cumulative patches.
+ *
+ *
+ * Usage
+ * -----
+ *
+ * Step 1 - load two livepatch callback demos (default behavior)
+ *
+ * insmod samples/livepatch/livepatch-callbacks-demo.ko
+ * insmod samples/livepatch/livepatch-callbacks-demo2.ko replace=0
+ * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo2/enabled
+ * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
+ *
+ * Watch dmesg output to see pre and post (un)patch callbacks made for
+ * both livepatch-callbacks-demo and livepatch-callbacks-demo2.
+ *
+ * Remove the modules to prepare for the next step:
+ *
+ * rmmod samples/livepatch/livepatch-callbacks-demo2.ko
+ * rmmod samples/livepatch/livepatch-callbacks-demo.ko
+ *
+ * Step 1 - load two livepatch callback demos (cumulative behavior)
+ *
+ * insmod samples/livepatch/livepatch-callbacks-demo.ko
+ * insmod samples/livepatch/livepatch-callbacks-demo2.ko replace=1
+ * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo2/enabled
+ * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
+ *
+ * Check dmesg output again and notice that when a cumulative patch is
+ * loaded, only its pre and post unpatch callbacks are executed.
+ *
+ * Final cleanup:
+ *
+ * rmmod samples/livepatch/livepatch-callbacks-demo2.ko
+ * 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 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,
+};
+
+static int livepatch_callbacks_demo2_init(void)
+{
+ int ret;
+
+ patch.replace = replace;
+
+ ret = klp_register_patch(&patch);
+ if (ret)
+ return ret;
+ ret = klp_enable_patch(&patch);
+ if (ret) {
+ WARN_ON(klp_unregister_patch(&patch));
+ return ret;
+ }
+ return 0;
+}
+
+static void livepatch_callbacks_demo2_exit(void)
+{
+ WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(livepatch_callbacks_demo2_init);
+module_exit(livepatch_callbacks_demo2_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
--
1.8.3.1