[RFC PATCH 03/10] liveupdate: do early initialization before hugepages are allocated

Pratyush Yadav
2025-12-06T23:03:23Z

To support hugepage preservation using LUO, the hugetlb subsystem needs
to get liveupdate data when it allocates the hugepages to find out how
many pages are coming from live update.

Move early LUO init from early_initcall to mm_core_init(). This is where
gigantic hugepages are allocated on ARM64. On x86, they are allocated in
setup_arch(), so have a call there as well. Keep track of whether the
function was already called to avoid double-init.

liveupdate_early_init() only gets the KHO subtree and validates the data
to ensure it is valid and understood. These are read-only operations and
do not need much from the system, so it is safe to call early in boot.

Signed-off-by: Pratyush Yadav <pratyush@kernel.org>
---
arch/x86/kernel/setup.c | 7 +++++++
include/linux/liveupdate.h | 6 ++++++
kernel/liveupdate/luo_core.c | 30 ++++++++++++++++++++++++++----
kernel/liveupdate/luo_internal.h | 2 ++
mm/mm_init.c | 7 +++++++
5 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 9bf00287c408..e2ec779afc2c 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -26,6 +26,7 @@
#include <linux/tboot.h>
#include <linux/usb/xhci-dbgp.h>
#include <linux/vmalloc.h>
+#include <linux/liveupdate.h>

#include <uapi/linux/mount.h>

@@ -1216,6 +1217,12 @@ void __init setup_arch(char **cmdline_p)

kho_memory_init();

+ /*
+ * Hugepages might be preserved from a liveupdate. Make sure it is
+ * initialized so hugetlb can query its state.
+ */
+ liveupdate_early_init();
+
if (boot_cpu_has(X86_FEATURE_GBPAGES)) {
hugetlb_cma_reserve(PUD_SHIFT - PAGE_SHIFT);
hugetlb_bootmem_alloc();
diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
index ed81e7b31a9f..78e8c529e4e7 100644
--- a/include/linux/liveupdate.h
+++ b/include/linux/liveupdate.h
@@ -214,6 +214,8 @@ struct liveupdate_flb {

#ifdef CONFIG_LIVEUPDATE

+void __init liveupdate_early_init(void);
+
/* Return true if live update orchestrator is enabled */
bool liveupdate_enabled(void);

@@ -233,6 +235,10 @@ int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp);

#else /* CONFIG_LIVEUPDATE */

+static inline void liveupdate_early_init(void)
+{
+}
+
static inline bool liveupdate_enabled(void)
{
return false;
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 7a9ef16b37d8..2c740ecad8e6 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -69,6 +69,13 @@ static struct {
u64 liveupdate_num;
} luo_global;

+static bool __luo_early_initialized __initdata;
+
+bool __init luo_early_initialized(void)
+{
+ return __luo_early_initialized;
+}
+
static int __init early_liveupdate_param(char *buf)
{
return kstrtobool(buf, &luo_global.enabled);
@@ -133,20 +140,35 @@ static int __init luo_early_startup(void)
return err;
}

-static int __init liveupdate_early_init(void)
+/*
+ * This should only be called after KHO FDT is known. It gets the LUO subtree
+ * and does initial validation, making early boot read-only access possible.
+ */
+void __init liveupdate_early_init(void)
{
int err;

+ /*
+ * HugeTLB needs LUO to be initialized early in boot, before gigantic
+ * hugepages are allocated. On x86, that happens in setup_arch(), but on
+ * ARM64 (and other architectures) that happens in mm_core_init().
+ *
+ * Since the code in mm_core_init() is shared between all architectures,
+ * this can lead to the init being called twice. Skip if initialization
+ * was already done.
+ */
+ if (__luo_early_initialized)
+ return;
+
+ __luo_early_initialized = true;
+
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)
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index 6115d6a4054d..171c54af7b38 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -114,6 +114,8 @@ int __init luo_flb_setup_outgoing(void *fdt);
int __init luo_flb_setup_incoming(void *fdt);
void luo_flb_serialize(void);

+bool __init luo_early_initialized(void);
+
#ifdef CONFIG_LIVEUPDATE_TEST
void liveupdate_test_register(struct liveupdate_file_handler *fh);
void liveupdate_test_unregister(struct liveupdate_file_handler *fh);
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 93cec06c1c8a..9a5b06a93622 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -31,6 +31,7 @@
#include <linux/execmem.h>
#include <linux/vmstat.h>
#include <linux/kexec_handover.h>
+#include <linux/liveupdate.h>
#include <linux/hugetlb.h>
#include "internal.h"
#include "slab.h"
@@ -2681,6 +2682,12 @@ void __init mm_core_init(void)
arch_mm_preinit();

kho_memory_init();
+ /*
+ * Hugepages might be preserved from a liveupdate. Make sure it is
+ * initialized so hugetlb can query its state.
+ */
+ liveupdate_early_init();
+
hugetlb_bootmem_alloc();

/* Initializations relying on SMP setup */
--
2.43.0