Live Patching
 help / color / mirror / Atom feed
From: Petr Mladek <pmladek@suse.com>
To: Yafang Shao <laoar.shao@gmail.com>
Cc: jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz,
	joe.lawrence@redhat.com, song@kernel.org,
	live-patching@vger.kernel.org
Subject: code review: was: Re: [PATCH v3 3/7] livepatch: Support scoped atomic replace using replace_set
Date: Wed, 10 Jun 2026 16:45:57 +0200	[thread overview]
Message-ID: <ail4pUt-t-0AB2wi@pathway.suse.cz> (raw)
In-Reply-To: <20260607131659.29281-4-laoar.shao@gmail.com>

On Sun 2026-06-07 21:16:55, Yafang Shao wrote:
> Convert the replace attribute from a boolean to a u32 to function as a
> "replace set." A newly loaded livepatch will now atomically replace any
> existing patch belonging to the same set. There can only ever be one active
> livepatch for a given replace_set number.
> 
> This change currently supports function replacement only. Livepatches that
> belong to different replace sets cannot modify the same function. If a new
> livepatch attempts to modify a function already modified by an older
> livepatch from a different replace_set, the loading of the new livepatch
> will be refused.
> 
> Similarly, for the KLP state, livepatches belonging to different replace
> sets cannot use the same state ID. The system will refuse to load a new
> livepatch if it uses a state ID already in use by an older livepatch from
> a different replace_set.
> 
> For the KLP shadow variable mechanism, developers must assign unique shadow
> IDs to livepatches that belong to different replace sets.
> 
> Support for replace_set compatibility with KLP state and shadow variables
> will be implemented after Petr's KLP state transfer work is completed [0].
> 
> Other user-visible changes include:
> - The non-replace model is now deprecated
> - /sys/kernel/livepatch/livepatch_XXX/replace attribute is replaced by
>   /sys/kernel/livepatch/livepatch_XXX/replace_set
> 
> --- a/Documentation/ABI/testing/sysfs-kernel-livepatch
> +++ b/Documentation/ABI/testing/sysfs-kernel-livepatch
> @@ -47,13 +47,12 @@ Description:
>  		disabled when the feature is used. See
>  		Documentation/livepatch/livepatch.rst for more information.
>  
> -What:		/sys/kernel/livepatch/<patch>/replace
> +What:		/sys/kernel/livepatch/<patch>/replace_set
>  Date:		Jun 2024
>  KernelVersion:	6.11.0

The Date and KernelVersion should be upsated as well.
It is a new attribute...

>  Contact:	live-patching@vger.kernel.org
>  Description:
> -		An attribute which indicates whether the patch supports
> -		atomic-replace.
> +		An attribute to show the replace_set of this livepatch.
>  
>  What:		/sys/kernel/livepatch/<patch>/stack_order
>  Date:		Jan 2025
> diff --git a/Documentation/livepatch/cumulative-patches.rst b/Documentation/livepatch/cumulative-patches.rst
> index 1931f318976a..0361adb12f6d 100644
> --- a/Documentation/livepatch/cumulative-patches.rst
> +++ b/Documentation/livepatch/cumulative-patches.rst
> @@ -17,18 +17,20 @@ from all older livepatches and completely replace them in one transition.

We need to update also the introduction part above. I propose
the following text:

<proposal>
Livepatches are used to fix kernel bugs. New fixes need to be added over time.
The fixes might be independent, but they might also depend on each other. This
brings a challenge of how to keep the livepatched system safe and consistent.

Part of the solution is the "Atomic Replace" feature, which allows the kernel to
atomically replace an existing livepatch with another one. These newer
livepatches are designed as "Cumulative Patches". They include all wanted
changes from all older livepatches and completely replace them in one
transition.

The second part of the solution is the newly introduced ``replace_set`` feature,
which allows the installation of multiple livepatches in parallel. A livepatch
will only replace other livepatches that share the same ``replace_set`` number.
This might be used to fix independent problems separately, for example, the
livepatches might be prepared by separate teams focusing on particular
functionality or a subsystem.

It should be emphasized that the preferred and most secure way is to always use
the default ``replace_set = 0``. In this mode, any livepatch replaces any other
livepatch, preventing any unexpected interactions between incompatible
livepatches.
</proposal>

>  Usage
>  -----

I would split the "Usage" section into two new sections, see below.

> -The atomic replace can be enabled by setting "replace" flag in struct klp_patch,
> -for example::
> +The "replace_set" attribute in ``struct klp_patch`` acts as a **replace_set**,
> +defining the scope of the replacement. By default, the replace_set is 0.
> +
> +For example::
>  
>  	static struct klp_patch patch = {
>  		.mod = THIS_MODULE,
>  		.objs = objs,
> -		.replace = true,
> +		.replace_set = 0,
>  	};

I would use the above text in a new section called "Replace Set":

<proposal>
Replace Set
-----------

The "replace_set" attribute in ``struct klp_patch`` acts as a **replace_set**,
defining the scope of the replacement. By default, the replace_set is 0.

For example::

	static struct klp_patch patch = {
		.mod = THIS_MODULE,
		.objs = objs,
		.replace_set = 0,
	};

Any ``replace_set`` value might be associated with a set of livepatched symbols,
callbacks, shadow variables, and state IDs. By definition, there can only ever
be one active livepatch for a given ``replace_set`` number.

On the contrary, livepatches with a different ``replace_set`` number must not
modify the same function, or use the state with the same ID. Any attempt to
load an incompatible livepatch will be rejected by the kernel.
</proposal>

And the rest would be in a new section called:

Atomic Replace
--------------

>  All processes are then migrated to use the code only from the new patch.
> -Once the transition is finished, all older patches are automatically
> -disabled.
> +Once the transition is finished, all older patches with the same replace
> +set are automatically disabled. Patches with different tags remain active.

There always could be only one livepatch with the given replace set.
I would write something like:

<proposal>
A livepatch with a given ``replace_set`` number is replaced by another
livepatch using the same number. All processes are migrated to use
the code only from the new patch. Once the transition is finished,
the older patch. Patches with different replace sets are not affected
and remain active.
</proposal>

>  Ftrace handlers are transparently removed from functions that are no
>  longer modified by the new cumulative patch.
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -600,6 +600,8 @@ static int klp_add_nops(struct klp_patch *patch)
>  		klp_for_each_object(old_patch, old_obj) {
>  			int err;
>  
> +			if (patch->replace_set != old_patch->replace_set)
> +				continue;

This should be checked on the klp_for_each_patch(old_patch) level.
The result is the same for each old_obj from the given old_patch...

>  			err = klp_add_object_nops(patch, old_obj);
>  			if (err)
>  				return err;
> @@ -1174,6 +1176,8 @@ void klp_unpatch_replaced_patches(struct klp_patch *new_patch)
>  		if (old_patch == new_patch)
>  			return;
>  

Nit: Remove the empty line here.

> +		if (old_patch->replace_set != new_patch->replace_set)
> +			continue;

Nit: And put it here instead. To separate if/return/continue part from
     the changes done for the eligible old_patch.

>  		old_patch->enabled = false;
>  		klp_unpatch_objects(old_patch);
>  	}
> diff --git a/kernel/livepatch/state.c b/kernel/livepatch/state.c
> index 2565d039ade0..a1ac46637336 100644
> --- a/kernel/livepatch/state.c
> +++ b/kernel/livepatch/state.c
> @@ -85,34 +85,65 @@ EXPORT_SYMBOL_GPL(klp_get_prev_state);
>  
>  /* Check if the patch is able to deal with the existing system state. */
>  static bool klp_is_state_compatible(struct klp_patch *patch,
> +				    struct klp_patch *old_patch,
>  				    struct klp_state *old_state)
>  {
>  	struct klp_state *state;
>  
>  	state = klp_get_state(patch, old_state->id);
> +	if (patch->replace_set == old_patch->replace_set) {
> +		/*
> +		 * If the new livepatch shares a state set with an existing
> +		 * one, it must maintain compatibility with all states
> +		 * modified by the old patch.
> +		 */
> +		if (!state)
> +			return false;
> +		return state->version >= old_state->version;
>  
> -	/* A cumulative livepatch must handle all already modified states. */
> -	if (!state)
> -		return !patch->replace;
> +	}
>  
> -	return state->version >= old_state->version;
> +	/*
> +	 * Two livepatches with a different "replace_set" must _not_ use
> +	 * the same "state->id.
> +	 */
> +	return !state;
>  }
>  
> -/*
> - * 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.
> - */
> +/* Check that the new livepatch will not break the existing system states. */
>  bool klp_is_patch_compatible(struct klp_patch *patch)
>  {
> +	struct klp_object *obj, *old_obj;
>  	struct klp_patch *old_patch;
>  	struct klp_state *old_state;
> +	struct klp_func *func;
>  
>  	klp_for_each_patch(old_patch) {
>  		klp_for_each_state(old_patch, old_state) {
> -			if (!klp_is_state_compatible(patch, old_state))
> +			if (!klp_is_state_compatible(patch, old_patch, old_state))
>  				return false;
>  		}
> +
> +		if (old_patch->replace_set == patch->replace_set)
> +			continue;
> +
> +		/*
> +		 * Refuse loading a livepatch which would want to modify a
> +		 * function which is already livepatched with the livepatch
> +		 * with another "replace_set".
> +		 */
> +		klp_for_each_object_static(patch, obj) {
> +			klp_for_each_object(old_patch, old_obj) {
> +				if (!!obj->name != !!old_obj->name)
> +					continue;
> +				if (obj->name && strcmp(obj->name, old_obj->name))
> +					continue;
> +				klp_for_each_func_static(obj, func) {
> +					if (klp_find_func(old_obj, func))
> +						return false;
> +				}

The combination of for_each_() and for_each_*_static() variants look
correct but it is a bit scarry.

I think about calling klp_init_patch_early() before
klp_is_patch_compatible() so that we could use the non-static
for_each_*() variants even for the new patch. It would make the life
easier. But I do not have strong opinion about it.

> +			}

Anyway, please, put the new check into a separate function, something
like:

bool klp_has_function_conflict(struct klp_patch *patch,
			       struct klp_patch *old_patch);


> +		}
>  	}
>  
>  	return true;

The rest looks good.

Best Regards,
Petr

  parent reply	other threads:[~2026-06-10 14:46 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-07 13:16 [PATCH v3 0/7] livepatch: Introduce replace set support Yafang Shao
2026-06-07 13:16 ` [PATCH v3 1/7] livepatch: Fix NULL pointer dereference in klp_find_func() Yafang Shao
2026-06-09 13:27   ` Petr Mladek
2026-06-10  3:00     ` Yafang Shao
2026-06-07 13:16 ` [PATCH v3 2/7] livepatch: Move klp_find_func() into core.h Yafang Shao
2026-06-09 15:28   ` Petr Mladek
2026-06-10  3:01     ` Yafang Shao
2026-06-07 13:16 ` [PATCH v3 3/7] livepatch: Support scoped atomic replace using replace_set Yafang Shao
2026-06-07 13:33   ` sashiko-bot
2026-06-07 14:00     ` Yafang Shao
2026-06-09 16:00   ` Petr Mladek
2026-06-10  3:24     ` Yafang Shao
2026-06-10  9:48       ` Petr Mladek
2026-06-10 14:45   ` Petr Mladek [this message]
2026-06-07 13:16 ` [PATCH v3 4/7] livepatch: Deprecate stack_order Yafang Shao
2026-06-07 13:31   ` sashiko-bot
2026-06-10 15:11   ` Petr Mladek
2026-06-07 13:16 ` [PATCH v3 5/7] selftests/livepatch: Update tests for replace_set Yafang Shao
2026-06-07 13:29   ` sashiko-bot
2026-06-07 13:16 ` [PATCH v3 6/7] selftests/livepatch: Add test for state ID conflict across replace_sets Yafang Shao
2026-06-07 13:16 ` [PATCH v3 7/7] selftests/livepatch: Add test for function " 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=ail4pUt-t-0AB2wi@pathway.suse.cz \
    --to=pmladek@suse.com \
    --cc=jikos@kernel.org \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@kernel.org \
    --cc=laoar.shao@gmail.com \
    --cc=live-patching@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --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