[RFC Patch v2 09/16] kexec: Integrate multikernel instance management with kexec subsystem

Cong Wang
2025-10-19T06:17:08Z

Establish bidirectional integration between the kexec subsystem and
multikernel instance management, enabling proper lifecycle tracking
and resource coordination for multikernel operations.

This commit introduces:

* Enhanced kimage structure with multikernel-specific fields including
mk_id for unique multikernel identification and mk_instance pointer
for cross-referencing with the multikernel instance management
system, enabling proper state synchronization.

* UAPI extensions in include/uapi/linux/kexec.h that define multikernel
ID encoding within kexec flags using KEXEC_MK_ID_MASK and
KEXEC_MK_ID_SHIFT, providing up to 2047 unique multikernel instances
with proper bit field management macros.

* Multikernel image lookup infrastructure through kimage_find_by_id()
that leverages the mk_instance system for efficient image retrieval
by multikernel ID, replacing CPU-based lookup with proper instance
management.

* Refactored multikernel_kexec() interface from CPU-based to ID-based
operation (multikernel_kexec_by_id()) that uses instance CPU
assignments from device tree configuration rather than manual CPU
specification, improving safety and consistency.

* Proper resource lifecycle management in kimage_free() that clears
cross-references, updates instance states, and handles reference
counting when multikernel images are freed, preventing resource
leaks and dangling pointers.

* Updated reboot syscall interface that accepts multikernel instance
IDs instead of CPU numbers in LINUX_REBOOT_CMD_MULTIKERNEL,
providing a more intuitive and safer user interface.

Signed-off-by: Cong Wang <cwang@multikernel.io>
---
include/linux/kexec.h | 10 ++++-
include/linux/multikernel.h | 3 ++
include/uapi/linux/kexec.h | 4 ++
kernel/kexec_core.c | 75 +++++++++++++++++++++++++++++++++----
kernel/reboot.c | 4 +-
5 files changed, 85 insertions(+), 11 deletions(-)

diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 69877db5360b..5e9e9ad1dfeb 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -132,6 +132,7 @@ struct purgatory_info {
};

struct kimage;
+struct mk_instance;

typedef int (kexec_probe_t)(const char *kernel_buf, unsigned long kernel_size);
typedef void *(kexec_load_t)(struct kimage *image, char *kernel_buf,
@@ -434,6 +435,12 @@ struct kimage {

/* For multikernel support: linked list node */
struct list_head list;
+
+ /* Multikernel unique ID (0 = current kernel, >0 = multikernel images) */
+ int mk_id;
+
+ /* Multikernel instance cross-reference */
+ struct mk_instance *mk_instance;
};

/* kexec interface functions */
@@ -441,7 +448,8 @@ extern void machine_kexec(struct kimage *image);
extern int machine_kexec_prepare(struct kimage *image);
extern void machine_kexec_cleanup(struct kimage *image);
extern int kernel_kexec(void);
-extern int multikernel_kexec(int cpu);
+extern int multikernel_kexec_by_id(int mk_id);
+extern struct kimage *kimage_find_by_id(int mk_id);
extern struct page *kimage_alloc_control_pages(struct kimage *image,
unsigned int order);

diff --git a/include/linux/multikernel.h b/include/linux/multikernel.h
index 75cbb316d565..c65d39a66b84 100644
--- a/include/linux/multikernel.h
+++ b/include/linux/multikernel.h
@@ -102,6 +102,9 @@ struct mk_instance {
void *dtb_data; /* Device tree blob data */
size_t dtb_size; /* Size of DTB */

+ /* Kexec integration */
+ struct kimage *kimage; /* Associated kimage object */
+
/* Sysfs representation */
struct kernfs_node *kn; /* Kernfs node for this instance */

diff --git a/include/uapi/linux/kexec.h b/include/uapi/linux/kexec.h
index 346e0ff4e663..82a562ae6ac1 100644
--- a/include/uapi/linux/kexec.h
+++ b/include/uapi/linux/kexec.h
@@ -15,6 +15,10 @@
#define KEXEC_UPDATE_ELFCOREHDR 0x00000004
#define KEXEC_CRASH_HOTPLUG_SUPPORT 0x00000008
#define KEXEC_MULTIKERNEL 0x00000010
+#define KEXEC_MK_ID_MASK 0x0000ffe0
+#define KEXEC_MK_ID_SHIFT 5
+#define KEXEC_MK_ID(id) (((id) << KEXEC_MK_ID_SHIFT) & KEXEC_MK_ID_MASK)
+#define KEXEC_GET_MK_ID(flags) (((flags) & KEXEC_MK_ID_MASK) >> KEXEC_MK_ID_SHIFT)
#define KEXEC_ARCH_MASK 0xffff0000

/*
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 449096060fe8..ed5c97b4531e 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -40,6 +40,7 @@
#include <linux/hugetlb.h>
#include <linux/objtool.h>
#include <linux/kmsg_dump.h>
+#include <linux/multikernel.h>
#include <linux/dma-map-ops.h>
#include <linux/memblock.h>

@@ -255,6 +256,12 @@ struct kimage *do_kimage_alloc_init(void)
/* Initialize the list node for multikernel support */
INIT_LIST_HEAD(&image->list);

+ /* Initialize multikernel ID (0 = current kernel, will be assigned later for multikernel) */
+ image->mk_id = 0;
+
+ /* Initialize multikernel instance cross-reference */
+ image->mk_instance = NULL;
+
#ifdef CONFIG_CRASH_HOTPLUG
image->hp_action = KEXEC_CRASH_HP_NONE;
image->elfcorehdr_index = -1;
@@ -594,6 +601,16 @@ void kimage_free(struct kimage *image)
else if (image == kexec_crash_image)
kimage_update_compat_pointers(NULL, KEXEC_TYPE_CRASH);

+ /* Remove from IDR if it's a multikernel image */
+ if (image->type == KEXEC_TYPE_MULTIKERNEL && image->mk_instance) {
+ /* Clear cross-reference and update state */
+ image->mk_instance->kimage = NULL;
+ mk_instance_set_state(image->mk_instance, MK_STATE_READY);
+ mk_instance_put(image->mk_instance);
+ image->mk_instance = NULL;
+ pr_info("Freed multikernel ID %d\n", image->mk_id);
+ }
+
#ifdef CONFIG_CRASH_DUMP
if (image->vmcoreinfo_data_copy) {
crash_update_vmcoreinfo_safecopy(NULL);
@@ -1393,26 +1410,68 @@ int kernel_kexec(void)
return error;
}

-int multikernel_kexec(int cpu)
+/*
+ * Find a multikernel image by ID using mk_instance lookup
+ */
+struct kimage *kimage_find_by_id(int mk_id)
{
- int rc;
+ struct mk_instance *instance;
+ struct kimage *image = NULL;

- pr_info("multikernel kexec: cpu %d\n", cpu);
+ if (mk_id <= 0)
+ return NULL;

- if (cpu_online(cpu)) {
- pr_err("The CPU is currently running with this kernel instance.");
- return -EBUSY;
+ /* Use mk_instance system to find the associated kimage */
+ instance = mk_instance_find(mk_id);
+ if (instance) {
+ image = instance->kimage;
+ mk_instance_put(instance); /* Release reference from find */
}

+ return image;
+}
+
+int multikernel_kexec_by_id(int mk_id)
+{
+ struct kimage *mk_image;
+ struct mk_instance *instance;
+ int cpu = -1;
+ int rc;
+
if (!kexec_trylock())
return -EBUSY;
- if (!kexec_image) {
+
+ mk_image = kimage_find_by_id(mk_id);
+ if (!mk_image) {
+ pr_err("No multikernel image found with ID %d\n", mk_id);
rc = -EINVAL;
goto unlock;
}

+ instance = mk_image->mk_instance;
+ if (instance->cpus_valid && !cpumask_empty(instance->cpus)) {
+ cpu = cpumask_first(instance->cpus);
+ pr_info("multikernel kexec: using assigned CPU %d from instance cpumask %*pbl\n",
+ cpu, cpumask_pr_args(instance->cpus));
+ } else {
+ pr_err("No CPU assignment found for multikernel instance %d - CPU assignment is required\n",
+ mk_id);
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ if (cpu_online(cpu)) {
+ pr_err("CPU %d is currently online and cannot be used for multikernel instance %d\n",
+ cpu, mk_id);
+ rc = -EBUSY;
+ goto unlock;
+ }
+
+ pr_info("Using multikernel image with ID %d (entry point: 0x%lx) on CPU %d\n",
+ mk_image->mk_id, mk_image->start, cpu);
+
cpus_read_lock();
- rc = multikernel_kick_ap(cpu, kexec_image->start);
+ rc = multikernel_kick_ap(cpu, mk_image->start);
cpus_read_unlock();

unlock:
diff --git a/kernel/reboot.c b/kernel/reboot.c
index f3ac703c4695..bff6d3603a17 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -718,7 +718,7 @@ EXPORT_SYMBOL_GPL(kernel_power_off);
DEFINE_MUTEX(system_transition_mutex);

struct multikernel_boot_args {
- int cpu;
+ int mk_id;
};

/*
@@ -807,7 +807,7 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
case LINUX_REBOOT_CMD_MULTIKERNEL:
if (copy_from_user(&boot_args, arg, sizeof(boot_args)))
return -EFAULT;
- ret = multikernel_kexec(boot_args.cpu);
+ ret = multikernel_kexec_by_id(boot_args.mk_id);
break;
#endif

--
2.34.1