Re: [PATCH v3 3/7] livepatch: Support scoped atomic replace using replace_set
Yafang Shao <[email protected]> Tue, 23 Jun 2026 11:08:15 +0800
| Newsgroups | org.kernel.vger.live-patching |
|---|---|
| Message-ID | <CALOAHbBVjTX76u18jMQb3R4H+4=ipT4ybfjxq6tBFQNF4ALNQg@mail.gmail.com> |
On Tue, Jun 23, 2026 at 1:27 AM Joe Lawrence <[email protected]> wrote: > > On Thu, Jun 18, 2026 at 03:03:56PM +0200, Petr Mladek wrote: > > On Wed 2026-06-17 16:06:59, Joe Lawrence wrote: > > > On Wed, Jun 17, 2026 at 03:52:27PM +0200, Petr Mladek wrote: > > > > On Tue 2026-06-16 16:15:17, Joe Lawrence wrote: > > > > > On Thu, Jun 11, 2026 at 02:58:39PM +0200, Petr Mladek wrote: > > > > > > On Tue 2026-06-09 18:00:55, Petr Mladek wrote: > > > > > > > On Sun 2026-06-07 21:16:55, Yafang Shao wrote: > > > > [ ... snip ... ] > > > > > I'm not against supercedes functionality, but continuing the > > > > > brainstorming: what about solution 1 (.replace_set=0 special) with a > > > > > special zero-day overlay? > > > > > > > > I continue with the brainstorming ;-) > > > > > > > > > > Thanks for walking through it with me. Your reply crossed with my note > > > to Yafang at nearly the same time. > > > > > > > [ ... snip ... ] > > > > > So maybe it boils down to: is the supercedes big hammer desired and safe > > > > > enough to deploy? > > > > > > > > I personally like the solution with a zero-terminated array of > > > > replace_sets: > > > > > > > > struct patch { > > > > [...] > > > > unsigned int *replace_sets; > > > > [...], > > > > }; > > > > > > > > , which would allow to build a cumulative livepatch which replaces > > > > known hotfixes out of box. > > > > > > > > > > Question on this at the bottom ... > > > > > > > Note that the hotfix should not be allowed to modify a function or > > > > livepatch state which is modified by another livepatch. It would > > > > be dangerous. We should allow to solve this only by a cumulative > > > > livepatch. > > > > > > > > > > Agreed. > > > > > > > IMHO, the OS vendor should not touch customer specific livepatches > > > > by default. The customer installed them for a reason. We should > > > > just refuse to install two conflicting livepatches. Where > > > > we could reliably compare only the livepatched functions. > > > > But it still is good because most livepatches only modify > > > > functions. > > > > > > > > Plus, I would still allow to resolve the possible conflict by using > > > > the atomic replace. It could be done by a module-specific parameter. > > > > I would call it: override_replace_sets=X[,Y]... or so. > > > > > > > > > > Naming nitpick: "override_replace_sets" sounds like it may override the > > > "replace_sets" value and not supplement it. But that's just an > > > implementation detail to bikeshed later :D > > > > Good point! "supplement_replace_sets" or "add_replace_sets" > > would be better :D > > > > But see below. > > > > > > Finally, I assume that most users will keep using only the default > > > > replace_sets=0 [*]. They will never have to deal with another sets. > > > > > > > > The non-default replace sets will be only for adventurous users > > > > who want to deal with the complexity and accept the risks. > > > > > > > > [*] It we allow the zero-terminated array of replace_sets then > > > > zero should not be the default. Or it could be but it would > > > > be a special set which could never be replaced by anything > > > > else than another zero replace set. > > > > > > > > The zero replace set might be for users who do not want to > > > > deal with the complexity at all. For example, for an os-vendor > > > > who does not want to release separate hotfixes. > > > > > > > > > > Hmm, I do like the default replace_sets=0 not dealing with the > > > complication of the replace sets. > > > > > > But first, back to the larger question I mentioned at the beginning. > > > > > > Originally there was: > > > > > > unsigned int replace_set; /* the set I belong to */ > > > const unsigned int *supersedes; /* other sets I also replace */ > > > > > > and now it's just: > > > > > > unsigned int *replace_sets; /* sets I belong to AND replace? */ > > > > > > Could you trace through a few cycles of cumulative + hotfix releases with > > > this approach? For example: > > > > > > Wed: klp-1a: cumulative (replace_sets={1}) > > > Thu: klp-1b: hotfix (replace_sets={2}) <- coexists with klp-1a > > > Fri: klp-1c: hotfix v2 (replace_sets={2}) <- replaces klp-1b (same set) > > > Mon: klp-2a: cumulative (replace_sets={1,2}) <- replaces klp-1a AND wipes klp-1c * > > > Tue: klp-2b: hotfix (replace_sets={2}) <- coexists with klp-2a > > > > > > [*] After klp-2a loads with {1, 2}, is it permanently in both sets? Or > > > does it just evict set 2 and then only occupy set 1 going forward? The > > > latter makes klp-2b's load straightforward. > > > > > > I can read replace_sets two ways: > > > > > > 1. Positional: { set [, eviction_set ...] } where the first element is > > > the patch's own set and the rest are evicted on load. > > > > > > 2. Flat: the patch belongs to every listed set equally. But then how > > > could klp-2b load into set 2 without replacing the entire > > > cumulative klp-2a that also occupies it? > > > > I understand it a 3rd way (similar to Yafang?) ;-) > > > > 3. Set: the patch replaces the given set of replace_sets. > > Where klp-2a is a cumulative livepatch for two > > replace_sets: 1,2. And klp-2b hotfix would need to > > use a new replace_set, .e.g. 3. > > > > I see "replace_set" as a set of modifications (functions, > > shadow variables, and callbacks) which is supposed to > > replace/update/downgrade the same "replace_set". > > > > It would have the following consequences: > > ----------------------------------------- > > > > First, any newer cumulative livepatch would need to replace all > > older hotfixes. Let's extend your example: > > > > Wed: klp-1a: cumulative (replace_sets={1}) > > Thu: klp-1b: hotfix (replace_sets={2}) <- coexists with klp-1a > > Fri: klp-1c: hotfix v2 (replace_sets={2}) <- replaces klp-1b (same set) > > Mon: klp-2a: cumulative (replace_sets={1,2}) <- replaces klp-1a AND wipes klp-1c > > Tue: klp-2b: hotfix (replace_sets={3}) <- coexists with klp-2a > > Fri: klp-3a: cumulative (replace_sets={1,2,3}) <- replaces klp-2a AND wipes klp-2b > > Fri: klp-4a: cumulative (replace_sets={1,2,3}) <- replaces klp-3a > > Fri: klp-5a: cumulative (replace_sets={1,2,3}) <- replaces klp-4a > > > > This is making sense so far: hotfixes introduce new replace_set(s), > subsequent cumulatives replace all sets that came before it. > > Quick question for you or Yafang based on his thoughts on my other reply: > > > [Yafang] I support the flat mode approach. The hotfix should be assigned to a > > set that is not currently in use — for example, klp-2a should select a > > new set like set 3. The core design idea is to set replace_set > > dynamically. To determine which sets are already occupied, a > > user-space script can inspect /sys/kernel/livepatch/*/replace_set. > > How would Petr's 3rd way handle such dynamic replace_set enumeration? > Using the extended example above, without considering user patches, a > vendor could hardcode the replace_sets values at build time as shown. > > However, if a user patch has already occupied say, replace_set 3, then > klp-2b would need to skip over 3 and set replace_sets=4. But then when > the next cumulative patch, klp-3a, loads, how does it know that it > should replace_sets=1,2,4 and leave 3 alone? Wed: klp-1a: cumulative (replace_sets={1}) Thu: klp-1b: hotfix (replace_sets={2}) <- coexists with klp-1a Fri: klp-1c: hotfix v2 (replace_sets={2}) <- replaces klp-1b (same set) Mon: klp-2a: cumulative (replace_sets={1,2}) <- replaces klp-1a AND wipes klp-1c Mon: klp-user: cumulative (replace_sets={3}) <- coexists with klp-2a Tue: klp-2b: hotfix (replace_sets={4}) <- choose an unused id Wed: klp-3a: cumulative When loading klp-3a, the user can inspect the currently active set IDs by checking /sys/kernel/livepatch/klp-2a/replace_set and /sys/kernel/livepatch/klp-2b/replace_set, then dynamically configure replace_sets to {1, 2, 4} accordingly. However, I don't have a strong opinion on this. If you both prefer the positional mode, I'm happy to implement it that way. > > > Second, it would limit downgrades, for example: > > > > + klp-3a, klp-4a, and klp-5a looks compatible from the replace_set POV. > > The replace_set should not limit replacing each other. > > > > This should be somewhat minimized assuming they are upgraded as part of > the same distro package, each new one replaces the previous one on disk. > But there would be nothing preventing someone from loading klp-3a after > klp-5a if they wanted to. > > > Well, the replacing still might be limited by the states. > > > > Plus the pending patchset adds per-state "block_disable" flag > > which should handle situations where the change (by a callback) > > can't be reverted, see > > https://lore.kernel.org/all/[email protected]/ > > > > > > Hmm, this brings a question how exactly replace_sets and states > > play together and if we need them both. > > > > I did some brainstorming and came with the following definitions: > > ----------------------------------------------------------------- > > > > + Each patch.objs[i].funcs[j] defines a particular > > livepatched function. > > > > + Each patch.states[i] defines either a particular shadow variable > > (same id + state.is_shadow=true) [1] and/or set of callbacks [2] > > > > [1] https://lore.kernel.org/all/[email protected]/ > > [2] https://lore.kernel.org/all/[email protected]/ > > > > + Each shadow variable "id" defines a particular data (type) > > > > + Each set of callback (pre/post/enable/disable) is connected > > either with a particular shadow variable (for its lifetime handling) > > or it can change the system state with a one-time operation. > > > > Why do we need "states"? > > ------------------------ > > > > I see "states" as a definition of shadow variable ids and > > callbacks sets. We need to somehow tell the kernel that the > > livepatch is going to use them. > > > > Aside: IIRC the shadow variable + callback sets were the concrete use > case for states, but technically any special livepatch side effect could > be considered its own state, right? > > > The numeric "id" allows to compare the compatibility of the > > definitions between livepatches "easily". > > > > Note that we do not need states to compare livepatched functions. > > The kernel can compare them by the info in patch.objs[].funcs[].old_name. > > > > > > Why do we need "replace_set" ? > > ------------------------------ > > > > I see "replace_set" as an union of fixes (livepatched functions, > > shadow variables, callbacks) which is supposed to be handled > > using atomic replace. > > > > It defines which livepatches upgrade/downgrade or can be installed > > in parallel with other livepatches. > > > > I see it like "package name" in the RPM package management system. > > The "rpm" tools allows to upgrade/downgrade packages with the same > > name. It can even upgrade/downgrade a package with another name > > when "provides" [*] are defined. > > > > <nod> When listing out the future culumative + hotfix examples, my brain > went there (package mgmt), too. > > > Note that the "replace_set" would allow even downgrade because > > new livepatch might: > > > > + stop modifying some functions,see klp_add_nops(). > > > > + stop using some shadow variables and/or revert changes > > done by some callbacks, until it gets blocked by per-state > > "block_disable", see > > https://lore.kernel.org/all/[email protected]/ > > > > [*] "provides" seems to be better name than "supplements". > > > > > > Has "replace_set" a good name and semantic? > > ------------------------------------------- > > > > I think that we really could find some analogy with the package > > management. > > > > "replace_set" does not exist in the package management terminology. > > Also "replace_sets" is a set of replace sets which sounds a bit ugly. > > Also I sometimes wanted to say "replace a replace set" which > > overwhelming. > > > > Well, we likely do not want to introduce livepatch names. They > > might get confused with module names. > > > > We could not use module names because they must differ. Otherwise, > > kernel could not load both old and newer livepatch in parallel. > > > > I would stay with numbers. They are kind of IDs. But we do want > > to name it patch.id because it might get confused with state.id. > > > > We could call it "patch_id" but "patch.patch_id" is a but ugly. > > > > I did some brainstorming and came up with: > > > > "project_id" > > "changeset_id" > > "provides_id" > > "track_id" > > "fix_id" > > > > and claude-sonnet-4.6 also suggested to use: > > > > "slot" > > > > which has a similar meaning in the Gentoo package management, > > see https://devmanual.gentoo.org/general-concepts/slotting/ > > > > The "slots" name is interesting but they seems to be always > > mutually exclusive. I do not see any concept of merging > > slots in Gentoo. > > > > > > My preferences: > > > > Honestly, I feel a bit lost. I think that I need to sleep over it. > > > > I kind of like: > > > > * @slot: Livepatches with the same slot replace each other. > > Livepatches with different slots might be installed in parallel. > > unsigned long slot; > > > > And I would handle the merging of other slots separately by: > > > > * @merge_slots: Replace livepatches with the given slots. > > unsigned long merge_slots[]; > > > > Plus, a module parameter add_merge_slots=x[,y]... > > > > But I do not have strong opinion. > > > > How about following the package management analogy almost exactly: > > @provides: id < only one active livepatch per id > @obsoletes: { id [, id ...] } < replace given livepatch id(s) > @also_obsoletes: { id [, id ...] } < replace addt'l livepatch id(s) > > Claude offers alternative naming like: family, group, track, series and > absorbs, evicts, replaces, retires. It seems @provides and @obsoletes are easier to understand, but I don't have a strong opinion either way. -- Regards Yafang