Live Patching
 help / color / mirror / Atom feed
From: Yafang Shao <laoar.shao@gmail.com>
To: jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz,
	pmladek@suse.com, joe.lawrence@redhat.com, song@kernel.org
Cc: live-patching@vger.kernel.org, Yafang Shao <laoar.shao@gmail.com>
Subject: [RFC PATCH 5/6] livepatch: Remove obsolete per-object callbacks
Date: Wed, 13 May 2026 22:33:20 +0800	[thread overview]
Message-ID: <20260513143321.26185-6-laoar.shao@gmail.com> (raw)
In-Reply-To: <20260513143321.26185-1-laoar.shao@gmail.com>

This commit removes the obsolete per-object callbacks from the livepatch
framework.  All selftests have been migrated to the new per-state
callbacks, making the per-object callbacks redundant.

Instead, use the new per-state callbacks. They offer improved semantics
by associating callbacks and shadow variables with a specific state,
enabling better lifetime management of changes.

Originally-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
---
 include/linux/livepatch.h                | 40 ---------------
 include/linux/livepatch_external.h       | 62 +++++++++++++++---------
 kernel/livepatch/core.c                  | 29 -----------
 kernel/livepatch/core.h                  | 33 -------------
 kernel/livepatch/transition.c            |  9 ----
 scripts/livepatch/init.c                 |  2 -
 tools/include/linux/livepatch_external.h | 62 +++++++++++++++---------
 tools/objtool/klp-diff.c                 | 16 +++---
 8 files changed, 84 insertions(+), 169 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 340b04a0de83..221f176f1f51 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -95,7 +95,6 @@ struct klp_object {
 	/* external */
 	const char *name;
 	struct klp_func *funcs;
-	struct klp_callbacks callbacks;
 
 	/* internal */
 	struct kobject kobj;
@@ -106,45 +105,6 @@ struct klp_object {
 	bool patched;
 };
 
-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
- * @pre_patch:		 executed only when the state is being enabled
- *			 before code patching
- * @post_patch:		 executed only when the state is being enabled
- *			 after code patching
- * @pre_unpatch:	 executed only when the state is being disabled
- *			 before code unpatching
- * @post_unpatch:	 executed only when the state is being disabled
- *			 after code unpatching
- * @shadow_dtor:	 destructor for the related shadow variable
- * @pre_patch_succeeded: internal state used by a rollback on error
- *
- * All callbacks are optional.
- *
- * @pre_patch callback returns 0 on success and an error code otherwise.
- *
- * Any error prevents enabling the livepatch. @post_unpatch() callbacks are
- * then called to rollback @pre_patch callbacks which has already succeeded
- * before. Also @post_patch callbacks are called for to-be-removed states
- * to rollback pre_unpatch() callbacks when they were called.
- */
-struct klp_state_callbacks {
-	int (*pre_patch)(struct klp_patch *patch, struct klp_state *state);
-	void (*post_patch)(struct klp_patch *patch, struct klp_state *state);
-	void (*pre_unpatch)(struct klp_patch *patch, struct klp_state *state);
-	void (*post_unpatch)(struct klp_patch *patch, struct klp_state *state);
-	klp_shadow_dtor_t shadow_dtor;
-	bool pre_patch_succeeded;
-};
-
 /**
  * struct klp_state - state of the system modified by the livepatch
  * @id:		system state identifier (non-zero)
diff --git a/include/linux/livepatch_external.h b/include/linux/livepatch_external.h
index 138af19b0f5c..d9123d0c5dff 100644
--- a/include/linux/livepatch_external.h
+++ b/include/linux/livepatch_external.h
@@ -21,33 +21,48 @@
 #define KLP_PRE_UNPATCH_PREFIX		__stringify(__KLP_PRE_UNPATCH_PREFIX)
 #define KLP_POST_UNPATCH_PREFIX		__stringify(__KLP_POST_UNPATCH_PREFIX)
 
-struct klp_object;
-
-typedef int (*klp_pre_patch_t)(struct klp_object *obj);
-typedef void (*klp_post_patch_t)(struct klp_object *obj);
-typedef void (*klp_pre_unpatch_t)(struct klp_object *obj);
-typedef void (*klp_post_unpatch_t)(struct klp_object *obj);
+struct klp_state;
+struct klp_patch;
+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_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
+ * struct klp_state_callbacks - callbacks manipulating the state
+ * @pre_patch:		 executed only when the state is being enabled
+ *			 before code patching
+ * @post_patch:		 executed only when the state is being enabled
+ *			 after code patching
+ * @pre_unpatch:	 executed only when the state is being disabled
+ *			 before code unpatching
+ * @post_unpatch:	 executed only when the state is being disabled
+ *			 after code unpatching
+ * @shadow_dtor:	 destructor for the related shadow variable
+ * @pre_patch_succeeded: internal state used by a rollback on error
+ *
+ * All callbacks are optional.
+ *
+ * @pre_patch callback returns 0 on success and an error code otherwise.
  *
- * 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.
+ * Any error prevents enabling the livepatch. @post_unpatch() callbacks are
+ * then called to rollback @pre_patch callbacks which has already succeeded
+ * before. Also @post_patch callbacks are called for to-be-removed states
+ * to rollback pre_unpatch() callbacks when they were called.
  */
-struct klp_callbacks {
-	klp_pre_patch_t		pre_patch;
-	klp_post_patch_t	post_patch;
-	klp_pre_unpatch_t	pre_unpatch;
-	klp_post_unpatch_t	post_unpatch;
-	bool post_unpatch_enabled;
+struct klp_state_callbacks {
+	int (*pre_patch)(struct klp_patch *patch, struct klp_state *state);
+	void (*post_patch)(struct klp_patch *patch, struct klp_state *state);
+	void (*pre_unpatch)(struct klp_patch *patch, struct klp_state *state);
+	void (*post_unpatch)(struct klp_patch *patch, struct klp_state *state);
+	klp_shadow_dtor_t shadow_dtor;
+	bool pre_patch_succeeded;
+};
+
+struct klp_state_ext {
+	unsigned long id;
+	unsigned int version;
+	struct klp_state_callbacks callbacks;
 };
 
 /*
@@ -69,7 +84,6 @@ struct klp_func_ext {
 struct klp_object_ext {
 	const char *name;
 	struct klp_func_ext *funcs;
-	struct klp_callbacks callbacks;
 	unsigned int nr_funcs;
 };
 
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 95c099a8f594..eae807916ca0 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -1009,8 +1009,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;
 
@@ -1021,10 +1019,6 @@ static int __klp_disable_patch(struct klp_patch *patch)
 
 	klp_states_pre_unpatch(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
@@ -1075,13 +1069,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;
-		}
-
 		ret = klp_patch_object(obj);
 		if (ret) {
 			pr_warn("failed to patch object '%s'\n",
@@ -1253,14 +1240,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;
@@ -1307,25 +1290,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 1a2b11be7b5a..f844283b5423 100644
--- a/kernel/livepatch/transition.c
+++ b/kernel/livepatch/transition.c
@@ -145,15 +145,6 @@ static void klp_complete_transition(void)
 		klp_states_post_unpatch(klp_transition_patch);
 	}
 
-	klp_for_each_object(klp_transition_patch, obj) {
-		if (!klp_is_object_loaded(obj))
-			continue;
-		if (klp_target_state == KLP_TRANSITION_PATCHED)
-			klp_post_patch_callback(obj);
-		else if (klp_target_state == KLP_TRANSITION_UNPATCHED)
-			klp_post_unpatch_callback(obj);
-	}
-
 	pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name,
 		  klp_target_state == KLP_TRANSITION_PATCHED ? "patching" : "unpatching");
 
diff --git a/scripts/livepatch/init.c b/scripts/livepatch/init.c
index 659db21a5b53..04e8d20bab2a 100644
--- a/scripts/livepatch/init.c
+++ b/scripts/livepatch/init.c
@@ -63,8 +63,6 @@ static int __init livepatch_mod_init(void)
 
 		obj->name = obj_ext->name;
 		obj->funcs = funcs;
-
-		memcpy(&obj->callbacks, &obj_ext->callbacks, sizeof(struct klp_callbacks));
 	}
 
 	patch->mod = THIS_MODULE;
diff --git a/tools/include/linux/livepatch_external.h b/tools/include/linux/livepatch_external.h
index 138af19b0f5c..d9123d0c5dff 100644
--- a/tools/include/linux/livepatch_external.h
+++ b/tools/include/linux/livepatch_external.h
@@ -21,33 +21,48 @@
 #define KLP_PRE_UNPATCH_PREFIX		__stringify(__KLP_PRE_UNPATCH_PREFIX)
 #define KLP_POST_UNPATCH_PREFIX		__stringify(__KLP_POST_UNPATCH_PREFIX)
 
-struct klp_object;
-
-typedef int (*klp_pre_patch_t)(struct klp_object *obj);
-typedef void (*klp_post_patch_t)(struct klp_object *obj);
-typedef void (*klp_pre_unpatch_t)(struct klp_object *obj);
-typedef void (*klp_post_unpatch_t)(struct klp_object *obj);
+struct klp_state;
+struct klp_patch;
+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_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
+ * struct klp_state_callbacks - callbacks manipulating the state
+ * @pre_patch:		 executed only when the state is being enabled
+ *			 before code patching
+ * @post_patch:		 executed only when the state is being enabled
+ *			 after code patching
+ * @pre_unpatch:	 executed only when the state is being disabled
+ *			 before code unpatching
+ * @post_unpatch:	 executed only when the state is being disabled
+ *			 after code unpatching
+ * @shadow_dtor:	 destructor for the related shadow variable
+ * @pre_patch_succeeded: internal state used by a rollback on error
+ *
+ * All callbacks are optional.
+ *
+ * @pre_patch callback returns 0 on success and an error code otherwise.
  *
- * 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.
+ * Any error prevents enabling the livepatch. @post_unpatch() callbacks are
+ * then called to rollback @pre_patch callbacks which has already succeeded
+ * before. Also @post_patch callbacks are called for to-be-removed states
+ * to rollback pre_unpatch() callbacks when they were called.
  */
-struct klp_callbacks {
-	klp_pre_patch_t		pre_patch;
-	klp_post_patch_t	post_patch;
-	klp_pre_unpatch_t	pre_unpatch;
-	klp_post_unpatch_t	post_unpatch;
-	bool post_unpatch_enabled;
+struct klp_state_callbacks {
+	int (*pre_patch)(struct klp_patch *patch, struct klp_state *state);
+	void (*post_patch)(struct klp_patch *patch, struct klp_state *state);
+	void (*pre_unpatch)(struct klp_patch *patch, struct klp_state *state);
+	void (*post_unpatch)(struct klp_patch *patch, struct klp_state *state);
+	klp_shadow_dtor_t shadow_dtor;
+	bool pre_patch_succeeded;
+};
+
+struct klp_state_ext {
+	unsigned long id;
+	unsigned int version;
+	struct klp_state_callbacks callbacks;
 };
 
 /*
@@ -69,7 +84,6 @@ struct klp_func_ext {
 struct klp_object_ext {
 	const char *name;
 	struct klp_func_ext *funcs;
-	struct klp_callbacks callbacks;
 	unsigned int nr_funcs;
 };
 
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index c2c4e4968bc2..128fbe054417 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1606,8 +1606,8 @@ static int create_klp_sections(struct elfs *e)
 		reloc = find_reloc_by_dest(e->out, sym->sec, sym->offset);
 
 		if (!elf_create_reloc(e->out, obj_sec,
-				      offsetof(struct klp_object_ext, callbacks) +
-				      offsetof(struct klp_callbacks, pre_patch),
+				      offsetof(struct klp_state_ext, callbacks) +
+				      offsetof(struct klp_state_callbacks, pre_patch),
 				      reloc->sym, reloc_addend(reloc), R_ABS64))
 			return -1;
 	}
@@ -1622,8 +1622,8 @@ static int create_klp_sections(struct elfs *e)
 		reloc = find_reloc_by_dest(e->out, sym->sec, sym->offset);
 
 		if (!elf_create_reloc(e->out, obj_sec,
-				      offsetof(struct klp_object_ext, callbacks) +
-				      offsetof(struct klp_callbacks, post_patch),
+				      offsetof(struct klp_state_ext, callbacks) +
+				      offsetof(struct klp_state_callbacks, post_patch),
 				      reloc->sym, reloc_addend(reloc), R_ABS64))
 			return -1;
 	}
@@ -1638,8 +1638,8 @@ static int create_klp_sections(struct elfs *e)
 		reloc = find_reloc_by_dest(e->out, sym->sec, sym->offset);
 
 		if (!elf_create_reloc(e->out, obj_sec,
-				      offsetof(struct klp_object_ext, callbacks) +
-				      offsetof(struct klp_callbacks, pre_unpatch),
+				      offsetof(struct klp_state_ext, callbacks) +
+				      offsetof(struct klp_state_callbacks, pre_unpatch),
 				      reloc->sym, reloc_addend(reloc), R_ABS64))
 			return -1;
 	}
@@ -1654,8 +1654,8 @@ static int create_klp_sections(struct elfs *e)
 		reloc = find_reloc_by_dest(e->out, sym->sec, sym->offset);
 
 		if (!elf_create_reloc(e->out, obj_sec,
-				      offsetof(struct klp_object_ext, callbacks) +
-				      offsetof(struct klp_callbacks, post_unpatch),
+				      offsetof(struct klp_state_ext, callbacks) +
+				      offsetof(struct klp_state_callbacks, post_unpatch),
 				      reloc->sym, reloc_addend(reloc), R_ABS64))
 			return -1;
 	}
-- 
2.47.3


  parent reply	other threads:[~2026-05-13 14:34 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-13 14:33 [RFC PATCH 0/6] livepatch: Introduce replace set support Yafang Shao
2026-05-13 14:33 ` [RFC PATCH 1/6] livepatch: Support scoped atomic replace using replace set Yafang Shao
2026-05-13 14:33 ` [RFC PATCH 2/6] livepatch: Add callbacks for introducing and removing states Yafang Shao
2026-05-13 14:33 ` [RFC PATCH 3/6] livepatch: Allow to handle lifetime of shadow variables using the livepatch state Yafang Shao
2026-05-13 14:33 ` [RFC PATCH 4/6] livepatch: Remove "data" from struct klp_state Yafang Shao
2026-05-13 14:33 ` Yafang Shao [this message]
2026-05-13 14:33 ` [RFC PATCH 6/6] livepatch: Support replace_set in shadow variable API Yafang Shao

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=20260513143321.26185-6-laoar.shao@gmail.com \
    --to=laoar.shao@gmail.com \
    --cc=jikos@kernel.org \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --cc=pmladek@suse.com \
    --cc=song@kernel.org \
    /path/to/YOUR_REPLY

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

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