Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

kho: kexec-metadata: track previous kernel chain

Use Kexec Handover (KHO) to pass the previous kernel's version string and
the number of kexec reboots since the last cold boot to the next kernel,
and print it at boot time.

Example output:
[ 0.000000] KHO: exec from: 6.19.0-rc4-next-20260107 (count 1)

Motivation
==========

Bugs that only reproduce when kexecing from specific kernel versions are
difficult to diagnose. These issues occur when a buggy kernel kexecs into
a new kernel, with the bug manifesting only in the second kernel.

Recent examples include the following commits:

* commit eb2266312507 ("x86/boot: Fix page table access in
5-level to 4-level paging transition")
* commit 77d48d39e991 ("efistub/tpm: Use ACPI reclaim memory
for event log to avoid corruption")
* commit 64b45dd46e15 ("x86/efi: skip memattr table on kexec
boot")

As kexec-based reboots become more common, these version-dependent bugs
are appearing more frequently. At scale, correlating crashes to the
previous kernel version is challenging, especially when issues only occur
in specific transition scenarios.

Implementation
==============

The kexec metadata is stored as a plain C struct (struct
kho_kexec_metadata) rather than FDT format, for simplicity and direct
field access. It is registered via kho_add_subtree() as a separate
subtree, keeping it independent from the core KHO ABI. This design
choice:

- Keeps the core KHO ABI minimal and stable
- Allows the metadata format to evolve independently
- Avoids requiring version bumps for all KHO consumers (LUO, etc.)
when the metadata format changes

The struct kho_kexec_metadata contains two fields:
- previous_release: The kernel version that initiated the kexec
- kexec_count: Number of kexec boots since last cold boot

On cold boot, kexec_count starts at 0 and increments with each kexec. The
count helps identify issues that only manifest after multiple consecutive
kexec reboots.

[leitao@debian.org: call kho_kexec_metadata_init() for both boot paths]
Link: https://lore.kernel.org/all/20260309-kho-v8-5-c3abcf4ac750@debian.org/ [1]
Link: https://lore.kernel.org/20260409-kho_fix_merge_issue-v1-1-710c84ceaa85@debian.org
Link: https://lore.kernel.org/20260316-kho-v9-5-ed6dcd951988@debian.org
Signed-off-by: Breno Leitao <leitao@debian.org>
Acked-by: SeongJae Park <sj@kernel.org>
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
Cc: Alexander Graf <graf@amazon.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: "Liam R. Howlett" <Liam.Howlett@oracle.com>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Pasha Tatashin <pasha.tatashin@soleen.com>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Breno Leitao and committed by
Andrew Morton
76aa46b9 062dd306

+144
+46
include/linux/kho/abi/kexec_metadata.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + 3 + /** 4 + * DOC: Kexec Metadata ABI 5 + * 6 + * The "kexec-metadata" subtree stores optional metadata about the kexec chain. 7 + * It is registered via kho_add_subtree(), keeping it independent from the core 8 + * KHO ABI. This allows the metadata format to evolve without affecting other 9 + * KHO consumers. 10 + * 11 + * The metadata is stored as a plain C struct rather than FDT format for 12 + * simplicity and direct field access. 13 + * 14 + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. 15 + * Copyright (c) 2026 Breno Leitao <leitao@debian.org> 16 + */ 17 + 18 + #ifndef _LINUX_KHO_ABI_KEXEC_METADATA_H 19 + #define _LINUX_KHO_ABI_KEXEC_METADATA_H 20 + 21 + #include <linux/types.h> 22 + #include <linux/utsname.h> 23 + 24 + #define KHO_KEXEC_METADATA_VERSION 1 25 + 26 + /** 27 + * struct kho_kexec_metadata - Kexec metadata passed between kernels 28 + * @version: ABI version of this struct (must be first field) 29 + * @previous_release: Kernel version string that initiated the kexec 30 + * @kexec_count: Number of kexec boots since last cold boot 31 + * 32 + * This structure is preserved across kexec and allows the new kernel to 33 + * identify which kernel it was booted from and how many kexec reboots 34 + * have occurred. 35 + * 36 + * __NEW_UTS_LEN is part of uABI, so it safe to use it in here. 37 + */ 38 + struct kho_kexec_metadata { 39 + u32 version; 40 + char previous_release[__NEW_UTS_LEN + 1]; 41 + u32 kexec_count; 42 + } __packed; 43 + 44 + #define KHO_METADATA_NODE_NAME "kexec-metadata" 45 + 46 + #endif /* _LINUX_KHO_ABI_KEXEC_METADATA_H */
+98
kernel/liveupdate/kexec_handover.c
··· 18 18 #include <linux/kexec.h> 19 19 #include <linux/kexec_handover.h> 20 20 #include <linux/kho_radix_tree.h> 21 + #include <linux/utsname.h> 21 22 #include <linux/kho/abi/kexec_handover.h> 23 + #include <linux/kho/abi/kexec_metadata.h> 22 24 #include <linux/libfdt.h> 23 25 #include <linux/list.h> 24 26 #include <linux/memblock.h> ··· 1270 1268 struct kho_in { 1271 1269 phys_addr_t fdt_phys; 1272 1270 phys_addr_t scratch_phys; 1271 + char previous_release[__NEW_UTS_LEN + 1]; 1272 + u32 kexec_count; 1273 1273 struct kho_debugfs dbg; 1274 1274 }; 1275 1275 ··· 1396 1392 return err; 1397 1393 } 1398 1394 1395 + static void __init kho_in_kexec_metadata(void) 1396 + { 1397 + struct kho_kexec_metadata *metadata; 1398 + phys_addr_t metadata_phys; 1399 + size_t blob_size; 1400 + int err; 1401 + 1402 + err = kho_retrieve_subtree(KHO_METADATA_NODE_NAME, &metadata_phys, 1403 + &blob_size); 1404 + if (err) 1405 + /* This is fine, previous kernel didn't export metadata */ 1406 + return; 1407 + 1408 + /* Check that, at least, "version" is present */ 1409 + if (blob_size < sizeof(u32)) { 1410 + pr_warn("kexec-metadata blob too small (%zu bytes)\n", 1411 + blob_size); 1412 + return; 1413 + } 1414 + 1415 + metadata = phys_to_virt(metadata_phys); 1416 + 1417 + if (metadata->version != KHO_KEXEC_METADATA_VERSION) { 1418 + pr_warn("kexec-metadata version %u not supported (expected %u)\n", 1419 + metadata->version, KHO_KEXEC_METADATA_VERSION); 1420 + return; 1421 + } 1422 + 1423 + if (blob_size < sizeof(*metadata)) { 1424 + pr_warn("kexec-metadata blob too small for v%u (%zu < %zu)\n", 1425 + metadata->version, blob_size, sizeof(*metadata)); 1426 + return; 1427 + } 1428 + 1429 + /* 1430 + * Copy data to the kernel structure that will persist during 1431 + * kernel lifetime. 1432 + */ 1433 + kho_in.kexec_count = metadata->kexec_count; 1434 + strscpy(kho_in.previous_release, metadata->previous_release, 1435 + sizeof(kho_in.previous_release)); 1436 + 1437 + pr_info("exec from: %s (count %u)\n", 1438 + kho_in.previous_release, kho_in.kexec_count); 1439 + } 1440 + 1441 + /* 1442 + * Create kexec metadata to pass kernel version and boot count to the 1443 + * next kernel. This keeps the core KHO ABI minimal and allows the 1444 + * metadata format to evolve independently. 1445 + */ 1446 + static __init int kho_out_kexec_metadata(void) 1447 + { 1448 + struct kho_kexec_metadata *metadata; 1449 + int err; 1450 + 1451 + metadata = kho_alloc_preserve(sizeof(*metadata)); 1452 + if (IS_ERR(metadata)) 1453 + return PTR_ERR(metadata); 1454 + 1455 + metadata->version = KHO_KEXEC_METADATA_VERSION; 1456 + strscpy(metadata->previous_release, init_uts_ns.name.release, 1457 + sizeof(metadata->previous_release)); 1458 + /* kho_in.kexec_count is set to 0 on cold boot */ 1459 + metadata->kexec_count = kho_in.kexec_count + 1; 1460 + 1461 + err = kho_add_subtree(KHO_METADATA_NODE_NAME, metadata, 1462 + sizeof(*metadata)); 1463 + if (err) 1464 + kho_unpreserve_free(metadata); 1465 + 1466 + return err; 1467 + } 1468 + 1469 + static int __init kho_kexec_metadata_init(const void *fdt) 1470 + { 1471 + int err; 1472 + 1473 + if (fdt) 1474 + kho_in_kexec_metadata(); 1475 + 1476 + /* Populate kexec metadata for the possible next kexec */ 1477 + err = kho_out_kexec_metadata(); 1478 + if (err) 1479 + pr_warn("failed to initialize kexec-metadata subtree: %d\n", 1480 + err); 1481 + 1482 + return err; 1483 + } 1484 + 1399 1485 static __init int kho_init(void) 1400 1486 { 1401 1487 struct kho_radix_tree *tree = &kho_out.radix_tree; ··· 1516 1422 goto err_free_fdt; 1517 1423 1518 1424 err = kho_out_fdt_setup(); 1425 + if (err) 1426 + goto err_free_fdt; 1427 + 1428 + err = kho_kexec_metadata_init(fdt); 1519 1429 if (err) 1520 1430 goto err_free_fdt; 1521 1431