Integrate the LUO with the KHO framework to enable passing LUO state
across a kexec reboot.
This patch implements the lifecycle integration with KHO:
1. Incoming State: During early boot (`early_initcall`), LUO checks if
KHO is active. If so, it retrieves the "LUO" subtree, verifies the
"luo-v1" compatibility string, and reads the `liveupdate-number` to
track the update count.
2. Outgoing State: During late initialization (`late_initcall`), LUO
allocates a new FDT for the next kernel, populates it with the basic
header (compatible string and incremented update number), and
registers it with KHO (`kho_add_subtree`).
3. Finalization: The `liveupdate_reboot()` notifier is updated to invoke
`kho_finalize()`. This ensures that all memory segments marked for
preservation are properly serialized before the kexec jump.
LUO now depends on `CONFIG_KEXEC_HANDOVER`.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/kho/abi/luo.h | 54 +++++++++++
kernel/liveupdate/luo_core.c | 154 ++++++++++++++++++++++++++++++-
kernel/liveupdate/luo_internal.h | 22 +++++
3 files changed, 229 insertions(+), 1 deletion(-)
create mode 100644 include/linux/kho/abi/luo.h
create mode 100644 kernel/liveupdate/luo_internal.h
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
new file mode 100644
index 000000000000..8523b3ff82d1
--- /dev/null
+++ b/include/linux/kho/abi/luo.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (c) 2025, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+
+/**
+ * DOC: Live Update Orchestrator ABI
+ *
+ * This header defines the stable Application Binary Interface used by the
+ * Live Update Orchestrator to pass state from a pre-update kernel to a
+ * post-update kernel. The ABI is built upon the Kexec HandOver framework
+ * and uses a Flattened Device Tree to describe the preserved data.
+ *
+ * This interface is a contract. Any modification to the FDT structure, node
+ * properties, compatible strings, or the layout of the `__packed` serialization
+ * structures defined here constitutes a breaking change. Such changes require
+ * incrementing the version number in the relevant `_COMPATIBLE` string to
+ * prevent a new kernel from misinterpreting data from an old kernel.
+ *
+ * FDT Structure Overview:
+ * The entire LUO state is encapsulated within a single KHO entry named "LUO".
+ * This entry contains an FDT with the following layout:
+ *
+ * .. code-block:: none
+ *
+ * / {
+ * compatible = "luo-v1";
+ * liveupdate-number = <...>;
+ * };
+ *
+ * Main LUO Node (/):
+ *
+ * - compatible: "luo-v1"
+ * Identifies the overall LUO ABI version.
+ * - liveupdate-number: u64
+ * A counter tracking the number of successful live updates performed.
+ */
+
+#ifndef _LINUX_KHO_ABI_LUO_H
+#define _LINUX_KHO_ABI_LUO_H
+
+/*
+ * The LUO FDT hooks all LUO state for sessions, fds, etc.
+ * In the root it also carries "liveupdate-number" 64-bit property that
+ * corresponds to the number of live-updates performed on this machine.
+ */
+#define LUO_FDT_SIZE PAGE_SIZE
+#define LUO_FDT_KHO_ENTRY_NAME "LUO"
+#define LUO_FDT_COMPATIBLE "luo-v1"
+#define LUO_FDT_LIVEUPDATE_NUM "liveupdate-number"
+
+#endif /* _LINUX_KHO_ABI_LUO_H */
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 30ad8836360b..9f9fe9a81b29 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -41,12 +41,26 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/io.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/luo.h>
#include <linux/kobject.h>
+#include <linux/libfdt.h>
#include <linux/liveupdate.h>
#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/sizes.h>
+#include <linux/string.h>
+#include <linux/unaligned.h>
+
+#include "kexec_handover_internal.h"
+#include "luo_internal.h"
static struct {
bool enabled;
+ void *fdt_out;
+ void *fdt_in;
+ u64 liveupdate_num;
} luo_global;
static int __init early_liveupdate_param(char *buf)
@@ -55,6 +69,129 @@ static int __init early_liveupdate_param(char *buf)
}
early_param("liveupdate", early_liveupdate_param);
+static int __init luo_early_startup(void)
+{
+ phys_addr_t fdt_phys;
+ int err, ln_size;
+ const void *ptr;
+
+ if (!kho_is_enabled()) {
+ if (liveupdate_enabled())
+ pr_warn("Disabling liveupdate because KHO is disabled\n");
+ luo_global.enabled = false;
+ return 0;
+ }
+
+ /* Retrieve LUO subtree, and verify its format. */
+ err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys);
+ if (err) {
+ if (err != -ENOENT) {
+ pr_err("failed to retrieve FDT '%s' from KHO: %pe\n",
+ LUO_FDT_KHO_ENTRY_NAME, ERR_PTR(err));
+ return err;
+ }
+
+ return 0;
+ }
+
+ luo_global.fdt_in = phys_to_virt(fdt_phys);
+ err = fdt_node_check_compatible(luo_global.fdt_in, 0,
+ LUO_FDT_COMPATIBLE);
+ if (err) {
+ pr_err("FDT '%s' is incompatible with '%s' [%d]\n",
+ LUO_FDT_KHO_ENTRY_NAME, LUO_FDT_COMPATIBLE, err);
+
+ return -EINVAL;
+ }
+
+ ln_size = 0;
+ ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_LIVEUPDATE_NUM,
+ &ln_size);
+ if (!ptr || ln_size != sizeof(luo_global.liveupdate_num)) {
+ pr_err("Unable to get live update number '%s' [%d]\n",
+ LUO_FDT_LIVEUPDATE_NUM, ln_size);
+
+ return -EINVAL;
+ }
+
+ luo_global.liveupdate_num = get_unaligned((u64 *)ptr);
+ pr_info("Retrieved live update data, liveupdate number: %lld\n",
+ luo_global.liveupdate_num);
+
+ return 0;
+}
+
+static int __init liveupdate_early_init(void)
+{
+ int err;
+
+ err = luo_early_startup();
+ if (err) {
+ luo_global.enabled = false;
+ luo_restore_fail("The incoming tree failed to initialize properly [%pe], disabling live update\n",
+ ERR_PTR(err));
+ }
+
+ return err;
+}
+early_initcall(liveupdate_early_init);
+
+/* Called during boot to create outgoing LUO fdt tree */
+static int __init luo_fdt_setup(void)
+{
+ const u64 ln = luo_global.liveupdate_num + 1;
+ void *fdt_out;
+ int err;
+
+ fdt_out = kho_alloc_preserve(LUO_FDT_SIZE);
+ if (IS_ERR(fdt_out)) {
+ pr_err("failed to allocate/preserve FDT memory\n");
+ return PTR_ERR(fdt_out);
+ }
+
+ err = fdt_create(fdt_out, LUO_FDT_SIZE);
+ err |= fdt_finish_reservemap(fdt_out);
+ err |= fdt_begin_node(fdt_out, "");
+ err |= fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE);
+ err |= fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln));
+ err |= fdt_end_node(fdt_out);
+ err |= fdt_finish(fdt_out);
+ if (err)
+ goto exit_free;
+
+ err = kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out);
+ if (err)
+ goto exit_free;
+ luo_global.fdt_out = fdt_out;
+
+ return 0;
+
+exit_free:
+ kho_unpreserve_free(fdt_out);
+ pr_err("failed to prepare LUO FDT: %d\n", err);
+
+ return err;
+}
+
+/*
+ * late initcall because it initializes the outgoing tree that is needed only
+ * once userspace starts using /dev/liveupdate.
+ */
+static int __init luo_late_startup(void)
+{
+ int err;
+
+ if (!liveupdate_enabled())
+ return 0;
+
+ err = luo_fdt_setup();
+ if (err)
+ luo_global.enabled = false;
+
+ return err;
+}
+late_initcall(luo_late_startup);
+
/* Public Functions */
/**
@@ -69,7 +206,22 @@ early_param("liveupdate", early_liveupdate_param);
*/
int liveupdate_reboot(void)
{
- return 0;
+ int err;
+
+ if (!liveupdate_enabled())
+ return 0;
+
+ err = kho_finalize();
+ if (err) {
+ pr_err("kho_finalize failed %d\n", err);
+ /*
+ * kho_finalize() may return libfdt errors, to aboid passing to
+ * userspace unknown errors, change this to EAGAIN.
+ */
+ err = -EAGAIN;
+ }
+
+ return err;
}
/**
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
new file mode 100644
index 000000000000..8612687b2000
--- /dev/null
+++ b/kernel/liveupdate/luo_internal.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (c) 2025, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+
+#ifndef _LINUX_LUO_INTERNAL_H
+#define _LINUX_LUO_INTERNAL_H
+
+#include <linux/liveupdate.h>
+
+/*
+ * Handles a deserialization failure: devices and memory is in unpredictable
+ * state.
+ *
+ * Continuing the boot process after a failure is dangerous because it could
+ * lead to leaks of private data.
+ */
+#define luo_restore_fail(__fmt, ...) panic(__fmt, ##__VA_ARGS__)
+
+#endif /* _LINUX_LUO_INTERNAL_H */
--
2.52.0.rc2.455.g230fcf2819-goog
On Sat, Nov 22 2025, Pasha Tatashin wrote: > Integrate the LUO with the KHO framework to enable passing LUO state > across a kexec reboot. > > This patch implements the lifecycle integration with KHO: > > 1. Incoming State: During early boot (`early_initcall`), LUO checks if > KHO is active. If so, it retrieves the "LUO" subtree, verifies the > "luo-v1" compatibility string, and reads the `liveupdate-number` to > track the update count. > > 2. Outgoing State: During late initialization (`late_initcall`), LUO > allocates a new FDT for the next kernel, populates it with the basic > header (compatible string and incremented update number), and > registers it with KHO (`kho_add_subtree`). > > 3. Finalization: The `liveupdate_reboot()` notifier is updated to invoke > `kho_finalize()`. This ensures that all memory segments marked for > preservation are properly serialized before the kexec jump. > > LUO now depends on `CONFIG_KEXEC_HANDOVER`. Nit: This patch does not add the dependency. That is done by patch 1. I guess that change needs to be moved here or the comment removed? Other than this, Reviewed-by: Pratyush Yadav <pratyush@kernel.org> [...] -- Regards, Pratyush Yadav
On Mon, Nov 24, 2025 at 9:21 AM Pratyush Yadav <pratyush@kernel.org> wrote: > > On Sat, Nov 22 2025, Pasha Tatashin wrote: > > > Integrate the LUO with the KHO framework to enable passing LUO state > > across a kexec reboot. > > > > This patch implements the lifecycle integration with KHO: > > > > 1. Incoming State: During early boot (`early_initcall`), LUO checks if > > KHO is active. If so, it retrieves the "LUO" subtree, verifies the > > "luo-v1" compatibility string, and reads the `liveupdate-number` to > > track the update count. > > > > 2. Outgoing State: During late initialization (`late_initcall`), LUO > > allocates a new FDT for the next kernel, populates it with the basic > > header (compatible string and incremented update number), and > > registers it with KHO (`kho_add_subtree`). > > > > 3. Finalization: The `liveupdate_reboot()` notifier is updated to invoke > > `kho_finalize()`. This ensures that all memory segments marked for > > preservation are properly serialized before the kexec jump. > > > > LUO now depends on `CONFIG_KEXEC_HANDOVER`. > > Nit: This patch does not add the dependency. That is done by patch 1. I > guess that change needs to be moved here or the comment removed? > > Other than this, > > Reviewed-by: Pratyush Yadav <pratyush@kernel.org> Done, thank you for review! > > [...] > > -- > Regards, > Pratyush Yadav
On Sat, Nov 22, 2025 at 05:23:29PM -0500, Pasha Tatashin wrote:
> Integrate the LUO with the KHO framework to enable passing LUO state
> across a kexec reboot.
>
> This patch implements the lifecycle integration with KHO:
>
> 1. Incoming State: During early boot (`early_initcall`), LUO checks if
> KHO is active. If so, it retrieves the "LUO" subtree, verifies the
> "luo-v1" compatibility string, and reads the `liveupdate-number` to
> track the update count.
>
> 2. Outgoing State: During late initialization (`late_initcall`), LUO
> allocates a new FDT for the next kernel, populates it with the basic
> header (compatible string and incremented update number), and
> registers it with KHO (`kho_add_subtree`).
>
> 3. Finalization: The `liveupdate_reboot()` notifier is updated to invoke
> `kho_finalize()`. This ensures that all memory segments marked for
> preservation are properly serialized before the kexec jump.
>
> LUO now depends on `CONFIG_KEXEC_HANDOVER`.
>
> Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> ---
> include/linux/kho/abi/luo.h | 54 +++++++++++
> kernel/liveupdate/luo_core.c | 154 ++++++++++++++++++++++++++++++-
> kernel/liveupdate/luo_internal.h | 22 +++++
> 3 files changed, 229 insertions(+), 1 deletion(-)
> create mode 100644 include/linux/kho/abi/luo.h
> create mode 100644 kernel/liveupdate/luo_internal.h
>
> diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
> new file mode 100644
> index 000000000000..8523b3ff82d1
> --- /dev/null
> +++ b/include/linux/kho/abi/luo.h
> @@ -0,0 +1,54 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/*
> + * Copyright (c) 2025, Google LLC.
> + * Pasha Tatashin <pasha.tatashin@soleen.com>
> + */
> +
> +/**
> + * DOC: Live Update Orchestrator ABI
> + *
> + * This header defines the stable Application Binary Interface used by the
> + * Live Update Orchestrator to pass state from a pre-update kernel to a
> + * post-update kernel. The ABI is built upon the Kexec HandOver framework
> + * and uses a Flattened Device Tree to describe the preserved data.
> + *
> + * This interface is a contract. Any modification to the FDT structure, node
> + * properties, compatible strings, or the layout of the `__packed` serialization
> + * structures defined here constitutes a breaking change. Such changes require
> + * incrementing the version number in the relevant `_COMPATIBLE` string to
> + * prevent a new kernel from misinterpreting data from an old kernel.
From v6 thread:
> > I'd add a sentence that stresses that ABI changes are possible as long they
> > include changes to the FDT version.
> > This is indeed implied by the last paragraph, but I think it's worth
> > spelling it explicitly.
> >
> > Another thing that I think this should mention is that compatibility is
> > only guaranteed for the kernels that use the same ABI version.
>
> Sure, I will add both.
Looks like it fell between the cracks :/
> +static int __init liveupdate_early_init(void)
> +{
> + int err;
> +
> + err = luo_early_startup();
> + if (err) {
> + luo_global.enabled = false;
> + luo_restore_fail("The incoming tree failed to initialize properly [%pe], disabling live update\n",
> + ERR_PTR(err));
What's wrong with a plain panic()?
> + }
> +
> + return err;
> +}
> +early_initcall(liveupdate_early_init);
> +
...
> int liveupdate_reboot(void)
> {
> - return 0;
> + int err;
> +
> + if (!liveupdate_enabled())
> + return 0;
> +
> + err = kho_finalize();
> + if (err) {
> + pr_err("kho_finalize failed %d\n", err);
Nit: why not %pe?
> + /*
> + * kho_finalize() may return libfdt errors, to aboid passing to
> + * userspace unknown errors, change this to EAGAIN.
> + */
> + err = -EAGAIN;
> + }
> +
> + return err;
> }
>
> /**
> diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
> new file mode 100644
> index 000000000000..8612687b2000
> --- /dev/null
> +++ b/kernel/liveupdate/luo_internal.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/*
> + * Copyright (c) 2025, Google LLC.
> + * Pasha Tatashin <pasha.tatashin@soleen.com>
> + */
> +
> +#ifndef _LINUX_LUO_INTERNAL_H
> +#define _LINUX_LUO_INTERNAL_H
> +
> +#include <linux/liveupdate.h>
> +
> +/*
> + * Handles a deserialization failure: devices and memory is in unpredictable
> + * state.
> + *
> + * Continuing the boot process after a failure is dangerous because it could
> + * lead to leaks of private data.
> + */
> +#define luo_restore_fail(__fmt, ...) panic(__fmt, ##__VA_ARGS__)
Let's add this when we have more than a single callsite.
Just use panic() in liveupdate_early_init() and add the comment there.
> +
> +#endif /* _LINUX_LUO_INTERNAL_H */
> --
> 2.52.0.rc2.455.g230fcf2819-goog
>
--
Sincerely yours,
Mike.
On Sun, Nov 23, 2025 at 6:27 AM Mike Rapoport <rppt@kernel.org> wrote:
>
> On Sat, Nov 22, 2025 at 05:23:29PM -0500, Pasha Tatashin wrote:
> > Integrate the LUO with the KHO framework to enable passing LUO state
> > across a kexec reboot.
> >
> > This patch implements the lifecycle integration with KHO:
> >
> > 1. Incoming State: During early boot (`early_initcall`), LUO checks if
> > KHO is active. If so, it retrieves the "LUO" subtree, verifies the
> > "luo-v1" compatibility string, and reads the `liveupdate-number` to
> > track the update count.
> >
> > 2. Outgoing State: During late initialization (`late_initcall`), LUO
> > allocates a new FDT for the next kernel, populates it with the basic
> > header (compatible string and incremented update number), and
> > registers it with KHO (`kho_add_subtree`).
> >
> > 3. Finalization: The `liveupdate_reboot()` notifier is updated to invoke
> > `kho_finalize()`. This ensures that all memory segments marked for
> > preservation are properly serialized before the kexec jump.
> >
> > LUO now depends on `CONFIG_KEXEC_HANDOVER`.
> >
> > Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> > ---
> > include/linux/kho/abi/luo.h | 54 +++++++++++
> > kernel/liveupdate/luo_core.c | 154 ++++++++++++++++++++++++++++++-
> > kernel/liveupdate/luo_internal.h | 22 +++++
> > 3 files changed, 229 insertions(+), 1 deletion(-)
> > create mode 100644 include/linux/kho/abi/luo.h
> > create mode 100644 kernel/liveupdate/luo_internal.h
> >
> > diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
> > new file mode 100644
> > index 000000000000..8523b3ff82d1
> > --- /dev/null
> > +++ b/include/linux/kho/abi/luo.h
> > @@ -0,0 +1,54 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +
> > +/*
> > + * Copyright (c) 2025, Google LLC.
> > + * Pasha Tatashin <pasha.tatashin@soleen.com>
> > + */
> > +
> > +/**
> > + * DOC: Live Update Orchestrator ABI
> > + *
> > + * This header defines the stable Application Binary Interface used by the
> > + * Live Update Orchestrator to pass state from a pre-update kernel to a
> > + * post-update kernel. The ABI is built upon the Kexec HandOver framework
> > + * and uses a Flattened Device Tree to describe the preserved data.
> > + *
> > + * This interface is a contract. Any modification to the FDT structure, node
> > + * properties, compatible strings, or the layout of the `__packed` serialization
> > + * structures defined here constitutes a breaking change. Such changes require
> > + * incrementing the version number in the relevant `_COMPATIBLE` string to
> > + * prevent a new kernel from misinterpreting data from an old kernel.
>
> From v6 thread:
>
> > > I'd add a sentence that stresses that ABI changes are possible as long they
> > > include changes to the FDT version.
> > > This is indeed implied by the last paragraph, but I think it's worth
> > > spelling it explicitly.
> > >
> > > Another thing that I think this should mention is that compatibility is
> > > only guaranteed for the kernels that use the same ABI version.
> >
> > Sure, I will add both.
>
> Looks like it fell between the cracks :/
Hm, when I was updating the patches, I included the first part, and
then re-read the content, and I think it covers all points:
1. Changes are possible
This interface is a contract. Any modification to the FDT structure, node
* properties, compatible strings, or the layout of the `__packed` serialization
* structures defined here constitutes a breaking change. Such changes require
* incrementing the version number in the relevant `_COMPATIBLE` string
So, change as long as you update versioning number
2. Breaking if version is different:
to prevent a new kernel from misinterpreting data from an old kernel.
So, the next kernel can interpret only if the version is the same.
Which point do you think is not covered?
>
> > +static int __init liveupdate_early_init(void)
> > +{
> > + int err;
> > +
> > + err = luo_early_startup();
> > + if (err) {
> > + luo_global.enabled = false;
> > + luo_restore_fail("The incoming tree failed to initialize properly [%pe], disabling live update\n",
> > + ERR_PTR(err));
>
> What's wrong with a plain panic()?
Jason suggested using the luo_restore_fail() function instead of
inserting panic() right in code somewhere in LUOv3 or earlier. It
helps avoid sprinkling panics in different places, and also in case if
we add the maintenance mode that we have discussed in LUOv6, we could
update this function as a place where that mode would be switched on.
> > + }
> > +
> > + return err;
> > +}
> > +early_initcall(liveupdate_early_init);
> > +
>
> ...
>
> > int liveupdate_reboot(void)
> > {
> > - return 0;
> > + int err;
> > +
> > + if (!liveupdate_enabled())
> > + return 0;
> > +
> > + err = kho_finalize();
> > + if (err) {
> > + pr_err("kho_finalize failed %d\n", err);
>
> Nit: why not %pe?
I believe, before my last clean-up of KHO it could return FDT error in
addition to standard errno; but anyways, this code is going to be
removed soon with stateless KHO, keeping err instead of %pe is fine (I
can change this if I update this patch).
> > + /*
> > + * kho_finalize() may return libfdt errors, to aboid passing to
> > + * userspace unknown errors, change this to EAGAIN.
> > + */
> > + err = -EAGAIN;
> > + }
> > +
> > + return err;
> > }
> >
> > /**
> > diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
> > new file mode 100644
> > index 000000000000..8612687b2000
> > --- /dev/null
> > +++ b/kernel/liveupdate/luo_internal.h
> > @@ -0,0 +1,22 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +
> > +/*
> > + * Copyright (c) 2025, Google LLC.
> > + * Pasha Tatashin <pasha.tatashin@soleen.com>
> > + */
> > +
> > +#ifndef _LINUX_LUO_INTERNAL_H
> > +#define _LINUX_LUO_INTERNAL_H
> > +
> > +#include <linux/liveupdate.h>
> > +
> > +/*
> > + * Handles a deserialization failure: devices and memory is in unpredictable
> > + * state.
> > + *
> > + * Continuing the boot process after a failure is dangerous because it could
> > + * lead to leaks of private data.
> > + */
> > +#define luo_restore_fail(__fmt, ...) panic(__fmt, ##__VA_ARGS__)
>
> Let's add this when we have more than a single callsite.
> Just use panic() in liveupdate_early_init() and add the comment there.
https://lore.kernel.org/all/CA+CK2bBEX6C6v63DrK-Fx2sE7fvLTZM=HX0y_j4aVDYcfrCXOg@mail.gmail.com/
This is the reason I added this function. I like the current approach.
Pasha
On Sun, Nov 23, 2025 at 07:03:19AM -0500, Pasha Tatashin wrote:
> On Sun, Nov 23, 2025 at 6:27 AM Mike Rapoport <rppt@kernel.org> wrote:
> >
> > On Sat, Nov 22, 2025 at 05:23:29PM -0500, Pasha Tatashin wrote:
> > > Integrate the LUO with the KHO framework to enable passing LUO state
> > > across a kexec reboot.
> > >
> > > This patch implements the lifecycle integration with KHO:
> > >
> > > 1. Incoming State: During early boot (`early_initcall`), LUO checks if
> > > KHO is active. If so, it retrieves the "LUO" subtree, verifies the
> > > "luo-v1" compatibility string, and reads the `liveupdate-number` to
> > > track the update count.
> > >
> > > 2. Outgoing State: During late initialization (`late_initcall`), LUO
> > > allocates a new FDT for the next kernel, populates it with the basic
> > > header (compatible string and incremented update number), and
> > > registers it with KHO (`kho_add_subtree`).
> > >
> > > 3. Finalization: The `liveupdate_reboot()` notifier is updated to invoke
> > > `kho_finalize()`. This ensures that all memory segments marked for
> > > preservation are properly serialized before the kexec jump.
> > >
> > > LUO now depends on `CONFIG_KEXEC_HANDOVER`.
> > >
> > > Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> > > ---
> > > include/linux/kho/abi/luo.h | 54 +++++++++++
> > > kernel/liveupdate/luo_core.c | 154 ++++++++++++++++++++++++++++++-
> > > kernel/liveupdate/luo_internal.h | 22 +++++
> > > 3 files changed, 229 insertions(+), 1 deletion(-)
> > > create mode 100644 include/linux/kho/abi/luo.h
> > > create mode 100644 kernel/liveupdate/luo_internal.h
> > >
> > > diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
> > > new file mode 100644
> > > index 000000000000..8523b3ff82d1
> > > --- /dev/null
> > > +++ b/include/linux/kho/abi/luo.h
> > > @@ -0,0 +1,54 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > +
> > > +/*
> > > + * Copyright (c) 2025, Google LLC.
> > > + * Pasha Tatashin <pasha.tatashin@soleen.com>
> > > + */
> > > +
> > > +/**
> > > + * DOC: Live Update Orchestrator ABI
> > > + *
> > > + * This header defines the stable Application Binary Interface used by the
> > > + * Live Update Orchestrator to pass state from a pre-update kernel to a
> > > + * post-update kernel. The ABI is built upon the Kexec HandOver framework
> > > + * and uses a Flattened Device Tree to describe the preserved data.
> > > + *
> > > + * This interface is a contract. Any modification to the FDT structure, node
> > > + * properties, compatible strings, or the layout of the `__packed` serialization
> > > + * structures defined here constitutes a breaking change. Such changes require
> > > + * incrementing the version number in the relevant `_COMPATIBLE` string to
> > > + * prevent a new kernel from misinterpreting data from an old kernel.
> >
> > From v6 thread:
> >
> > > > I'd add a sentence that stresses that ABI changes are possible as long they
> > > > include changes to the FDT version.
> > > > This is indeed implied by the last paragraph, but I think it's worth
> > > > spelling it explicitly.
> > > >
> > > > Another thing that I think this should mention is that compatibility is
> > > > only guaranteed for the kernels that use the same ABI version.
> > >
> > > Sure, I will add both.
> >
> > Looks like it fell between the cracks :/
>
> Hm, when I was updating the patches, I included the first part, and
> then re-read the content, and I think it covers all points:
>
> 1. Changes are possible
> This interface is a contract. Any modification to the FDT structure, node
> * properties, compatible strings, or the layout of the `__packed` serialization
> * structures defined here constitutes a breaking change. Such changes require
> * incrementing the version number in the relevant `_COMPATIBLE` string
>
> So, change as long as you update versioning number
>
> 2. Breaking if version is different:
> to prevent a new kernel from misinterpreting data from an old kernel.
>
> So, the next kernel can interpret only if the version is the same.
>
> Which point do you think is not covered?
As I said, it's covered, but it's implied. I'd prefer these stated
explicitly.
> > > +static int __init liveupdate_early_init(void)
> > > +{
> > > + int err;
> > > +
> > > + err = luo_early_startup();
> > > + if (err) {
> > > + luo_global.enabled = false;
> > > + luo_restore_fail("The incoming tree failed to initialize properly [%pe], disabling live update\n",
> > > + ERR_PTR(err));
> >
> > What's wrong with a plain panic()?
>
> Jason suggested using the luo_restore_fail() function instead of
> inserting panic() right in code somewhere in LUOv3 or earlier. It
> helps avoid sprinkling panics in different places, and also in case if
> we add the maintenance mode that we have discussed in LUOv6, we could
> update this function as a place where that mode would be switched on.
I'd agree if we were to have a bunch of panic()s sprinkled in the code.
With a single one it's easier to parse panic() than lookup what
luo_restore_fail() means.
> > > + }
> > > +
> > > + return err;
> > > +}
> > > +early_initcall(liveupdate_early_init);
> > > +
> >
> > ...
> >
> > > int liveupdate_reboot(void)
> > > {
> > > - return 0;
> > > + int err;
> > > +
> > > + if (!liveupdate_enabled())
> > > + return 0;
> > > +
> > > + err = kho_finalize();
> > > + if (err) {
> > > + pr_err("kho_finalize failed %d\n", err);
> >
> > Nit: why not %pe?
>
> I believe, before my last clean-up of KHO it could return FDT error in
> addition to standard errno; but anyways, this code is going to be
> removed soon with stateless KHO, keeping err instead of %pe is fine (I
> can change this if I update this patch).
Nah, %d is ok.
> > > + /*
> > > + * kho_finalize() may return libfdt errors, to aboid passing to
> > > + * userspace unknown errors, change this to EAGAIN.
> > > + */
> > > + err = -EAGAIN;
> > > + }
> > > +
> > > + return err;
> > > }
> > >
> > > /**
> > > diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
> > > new file mode 100644
> > > index 000000000000..8612687b2000
> > > --- /dev/null
> > > +++ b/kernel/liveupdate/luo_internal.h
> > > @@ -0,0 +1,22 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > +
> > > +/*
> > > + * Copyright (c) 2025, Google LLC.
> > > + * Pasha Tatashin <pasha.tatashin@soleen.com>
> > > + */
> > > +
> > > +#ifndef _LINUX_LUO_INTERNAL_H
> > > +#define _LINUX_LUO_INTERNAL_H
> > > +
> > > +#include <linux/liveupdate.h>
> > > +
> > > +/*
> > > + * Handles a deserialization failure: devices and memory is in unpredictable
> > > + * state.
> > > + *
> > > + * Continuing the boot process after a failure is dangerous because it could
> > > + * lead to leaks of private data.
> > > + */
> > > +#define luo_restore_fail(__fmt, ...) panic(__fmt, ##__VA_ARGS__)
> >
> > Let's add this when we have more than a single callsite.
> > Just use panic() in liveupdate_early_init() and add the comment there.
>
> https://lore.kernel.org/all/CA+CK2bBEX6C6v63DrK-Fx2sE7fvLTZM=HX0y_j4aVDYcfrCXOg@mail.gmail.com/
>
> This is the reason I added this function. I like the current approach.
v2 had way more than a single panic(), then it made sense
> Pasha
--
Sincerely yours,
Mike.
On Sun, Nov 23, 2025 at 9:17 AM Mike Rapoport <rppt@kernel.org> wrote:
>
> On Sun, Nov 23, 2025 at 07:03:19AM -0500, Pasha Tatashin wrote:
> > On Sun, Nov 23, 2025 at 6:27 AM Mike Rapoport <rppt@kernel.org> wrote:
> > >
> > > On Sat, Nov 22, 2025 at 05:23:29PM -0500, Pasha Tatashin wrote:
> > > > Integrate the LUO with the KHO framework to enable passing LUO state
> > > > across a kexec reboot.
> > > >
> > > > This patch implements the lifecycle integration with KHO:
> > > >
> > > > 1. Incoming State: During early boot (`early_initcall`), LUO checks if
> > > > KHO is active. If so, it retrieves the "LUO" subtree, verifies the
> > > > "luo-v1" compatibility string, and reads the `liveupdate-number` to
> > > > track the update count.
> > > >
> > > > 2. Outgoing State: During late initialization (`late_initcall`), LUO
> > > > allocates a new FDT for the next kernel, populates it with the basic
> > > > header (compatible string and incremented update number), and
> > > > registers it with KHO (`kho_add_subtree`).
> > > >
> > > > 3. Finalization: The `liveupdate_reboot()` notifier is updated to invoke
> > > > `kho_finalize()`. This ensures that all memory segments marked for
> > > > preservation are properly serialized before the kexec jump.
> > > >
> > > > LUO now depends on `CONFIG_KEXEC_HANDOVER`.
> > > >
> > > > Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> > > > ---
> > > > include/linux/kho/abi/luo.h | 54 +++++++++++
> > > > kernel/liveupdate/luo_core.c | 154 ++++++++++++++++++++++++++++++-
> > > > kernel/liveupdate/luo_internal.h | 22 +++++
> > > > 3 files changed, 229 insertions(+), 1 deletion(-)
> > > > create mode 100644 include/linux/kho/abi/luo.h
> > > > create mode 100644 kernel/liveupdate/luo_internal.h
> > > >
> > > > diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
> > > > new file mode 100644
> > > > index 000000000000..8523b3ff82d1
> > > > --- /dev/null
> > > > +++ b/include/linux/kho/abi/luo.h
> > > > @@ -0,0 +1,54 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > > +
> > > > +/*
> > > > + * Copyright (c) 2025, Google LLC.
> > > > + * Pasha Tatashin <pasha.tatashin@soleen.com>
> > > > + */
> > > > +
> > > > +/**
> > > > + * DOC: Live Update Orchestrator ABI
> > > > + *
> > > > + * This header defines the stable Application Binary Interface used by the
> > > > + * Live Update Orchestrator to pass state from a pre-update kernel to a
> > > > + * post-update kernel. The ABI is built upon the Kexec HandOver framework
> > > > + * and uses a Flattened Device Tree to describe the preserved data.
> > > > + *
> > > > + * This interface is a contract. Any modification to the FDT structure, node
> > > > + * properties, compatible strings, or the layout of the `__packed` serialization
> > > > + * structures defined here constitutes a breaking change. Such changes require
> > > > + * incrementing the version number in the relevant `_COMPATIBLE` string to
> > > > + * prevent a new kernel from misinterpreting data from an old kernel.
> > >
> > > From v6 thread:
> > >
> > > > > I'd add a sentence that stresses that ABI changes are possible as long they
> > > > > include changes to the FDT version.
> > > > > This is indeed implied by the last paragraph, but I think it's worth
> > > > > spelling it explicitly.
> > > > >
> > > > > Another thing that I think this should mention is that compatibility is
> > > > > only guaranteed for the kernels that use the same ABI version.
> > > >
> > > > Sure, I will add both.
> > >
> > > Looks like it fell between the cracks :/
> >
> > Hm, when I was updating the patches, I included the first part, and
> > then re-read the content, and I think it covers all points:
> >
> > 1. Changes are possible
> > This interface is a contract. Any modification to the FDT structure, node
> > * properties, compatible strings, or the layout of the `__packed` serialization
> > * structures defined here constitutes a breaking change. Such changes require
> > * incrementing the version number in the relevant `_COMPATIBLE` string
> >
> > So, change as long as you update versioning number
> >
> > 2. Breaking if version is different:
> > to prevent a new kernel from misinterpreting data from an old kernel.
> >
> > So, the next kernel can interpret only if the version is the same.
> >
> > Which point do you think is not covered?
>
> As I said, it's covered, but it's implied. I'd prefer these stated
> explicitly.
Added, thanks.
>
> > > > +static int __init liveupdate_early_init(void)
> > > > +{
> > > > + int err;
> > > > +
> > > > + err = luo_early_startup();
> > > > + if (err) {
> > > > + luo_global.enabled = false;
> > > > + luo_restore_fail("The incoming tree failed to initialize properly [%pe], disabling live update\n",
> > > > + ERR_PTR(err));
> > >
> > > What's wrong with a plain panic()?
> >
> > Jason suggested using the luo_restore_fail() function instead of
> > inserting panic() right in code somewhere in LUOv3 or earlier. It
> > helps avoid sprinkling panics in different places, and also in case if
> > we add the maintenance mode that we have discussed in LUOv6, we could
> > update this function as a place where that mode would be switched on.
>
> I'd agree if we were to have a bunch of panic()s sprinkled in the code.
> With a single one it's easier to parse panic() than lookup what
> luo_restore_fail() means.
The issue is that removing luo_restore_fail() removes the only
dependency on luo_internal.h in this patch. This would require me to
move the introduction of that header file to a later patch in the
series, which is difficult to handle via a simple fix-up.
Additionally, I still believe the abstraction is cleaner for future
extensibility (like the maintenance mode), even if it currently wraps
a single panic (which is actually a good thing, I have cleaned-up
things substantially to have a single point of panic since v2).
Therefore, it is my preference to keep it as is, unless a full series
is needed to be re-sent.
Pasha
On Sun, Nov 23, 2025 at 01:23:51PM -0500, Pasha Tatashin wrote:
> On Sun, Nov 23, 2025 at 9:17 AM Mike Rapoport <rppt@kernel.org> wrote:
> > > > > +static int __init liveupdate_early_init(void)
> > > > > +{
> > > > > + int err;
> > > > > +
> > > > > + err = luo_early_startup();
> > > > > + if (err) {
> > > > > + luo_global.enabled = false;
> > > > > + luo_restore_fail("The incoming tree failed to initialize properly [%pe], disabling live update\n",
> > > > > + ERR_PTR(err));
> > > >
> > > > What's wrong with a plain panic()?
> > >
> > > Jason suggested using the luo_restore_fail() function instead of
> > > inserting panic() right in code somewhere in LUOv3 or earlier. It
> > > helps avoid sprinkling panics in different places, and also in case if
> > > we add the maintenance mode that we have discussed in LUOv6, we could
> > > update this function as a place where that mode would be switched on.
> >
> > I'd agree if we were to have a bunch of panic()s sprinkled in the code.
> > With a single one it's easier to parse panic() than lookup what
> > luo_restore_fail() means.
>
> The issue is that removing luo_restore_fail() removes the only
> dependency on luo_internal.h in this patch. This would require me to
> move the introduction of that header file to a later patch in the
> series, which is difficult to handle via a simple fix-up.
>
> Additionally, I still believe the abstraction is cleaner for future
> extensibility (like the maintenance mode), even if it currently wraps
> a single panic (which is actually a good thing, I have cleaned-up
> things substantially to have a single point of panic since v2).
> Therefore, it is my preference to keep it as is, unless a full series
> is needed to be re-sent.
Well, let's keep it. If we won't see new users or extensions to
luo_restore_fail() we can kill it later.
> Pasha
--
Sincerely yours,
Mike.
On Tue, Nov 25, 2025 at 8:08 AM Mike Rapoport <rppt@kernel.org> wrote:
>
> On Sun, Nov 23, 2025 at 01:23:51PM -0500, Pasha Tatashin wrote:
> > On Sun, Nov 23, 2025 at 9:17 AM Mike Rapoport <rppt@kernel.org> wrote:
> > > > > > +static int __init liveupdate_early_init(void)
> > > > > > +{
> > > > > > + int err;
> > > > > > +
> > > > > > + err = luo_early_startup();
> > > > > > + if (err) {
> > > > > > + luo_global.enabled = false;
> > > > > > + luo_restore_fail("The incoming tree failed to initialize properly [%pe], disabling live update\n",
> > > > > > + ERR_PTR(err));
> > > > >
> > > > > What's wrong with a plain panic()?
> > > >
> > > > Jason suggested using the luo_restore_fail() function instead of
> > > > inserting panic() right in code somewhere in LUOv3 or earlier. It
> > > > helps avoid sprinkling panics in different places, and also in case if
> > > > we add the maintenance mode that we have discussed in LUOv6, we could
> > > > update this function as a place where that mode would be switched on.
> > >
> > > I'd agree if we were to have a bunch of panic()s sprinkled in the code.
> > > With a single one it's easier to parse panic() than lookup what
> > > luo_restore_fail() means.
> >
> > The issue is that removing luo_restore_fail() removes the only
> > dependency on luo_internal.h in this patch. This would require me to
> > move the introduction of that header file to a later patch in the
> > series, which is difficult to handle via a simple fix-up.
> >
> > Additionally, I still believe the abstraction is cleaner for future
> > extensibility (like the maintenance mode), even if it currently wraps
> > a single panic (which is actually a good thing, I have cleaned-up
> > things substantially to have a single point of panic since v2).
> > Therefore, it is my preference to keep it as is, unless a full series
> > is needed to be re-sent.
>
> Well, let's keep it. If we won't see new users or extensions to
> luo_restore_fail() we can kill it later.
SGTM.
>
> > Pasha
>
> --
> Sincerely yours,
> Mike.
© 2016 - 2025 Red Hat, Inc.