[POC 6/7] livepatch: Remove the obsolete per-object callbacks

From: Petr Mladek
Date: Fri Nov 10 2023 - 12:42:12 EST


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@xxxxxxxx>
---
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@xxxxxxxxxx>
- */
-
-/*
- * 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@xxxxxxxxxx>
- */
-
-/*
- * 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@xxxxxxxxxx>
- */
-
-/*
- * 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