Re: [PATCH 1/1] x86/crash: reserve elfcorehdr for CONFIG_NR_CPUS, not CONFIG_NR_CPUS_DEFAULT
From: Bradley Morgan
Date: Mon Aug 17 2026 - 19:16:35 EST
On 17 August 2026 02:18:59 BST, Jinjie Ruan <ruanjinjie@xxxxxxxxxx> wrote:
>
>
>在 2026/8/13 1:04, Ionut Nechita (Wind River) 写道:
>> From: Ionut Nechita <ionut.nechita@xxxxxxxxxxxxx>
>>
>> kexec_file_load(2) fails with -EINVAL when loading a crash kernel on a
>> machine whose number of possible CPUs exceeds CONFIG_NR_CPUS_DEFAULT,
>> even though the classic kexec_load(2) path succeeds on the same machine.
>>
>> With CONFIG_CRASH_HOTPLUG=y the elfcorehdr segment is over-allocated so
>> it can be updated in place on CPU/memory hotplug. crash_load_segments()
>> and arch_crash_get_elfcorehdr_size() compute that reservation from
>> CONFIG_NR_CPUS_DEFAULT:
>>
>> if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
>> pnum = 2 + CONFIG_NR_CPUS_DEFAULT + CONFIG_CRASH_MAX_MEMORY_RANGES;
>> else
>> pnum += 2 + CONFIG_NR_CPUS_DEFAULT;
>>
>> However the actual header produced by crash_prepare_elf64_headers()
>> contains one PT_NOTE per *possible* CPU, i.e. num_possible_cpus(), which
>> is bounded by CONFIG_NR_CPUS, not by CONFIG_NR_CPUS_DEFAULT. On configs
>> that raise CONFIG_NR_CPUS above the arch default while leaving
>> CONFIG_NR_CPUS_DEFAULT untouched (e.g. CONFIG_NR_CPUS=256,
>> CONFIG_NR_CPUS_DEFAULT=64 on x86_64 without MAXSMP), a system with more
>> than ~CONFIG_NR_CPUS_DEFAULT possible CPUs builds a header whose bufsz
>> exceeds the reserved, page-aligned memsz. sanity_check_segment_list()
>> then rejects the image:
>>
>> if (image->segment[i].bufsz > image->segment[i].memsz)
>> return -EINVAL;
>>
>> kexec_load(2) is unaffected because user space builds the elfcorehdr
>> without the hotplug over-allocation.
>>
>> Observed on a single-socket Xeon 6776P (144 possible CPUs) running a
>> PREEMPT_RT kernel with:
>>
>> # CONFIG_MAXSMP is not set
>> CONFIG_NR_CPUS_RANGE_BEGIN=2
>> CONFIG_NR_CPUS_RANGE_END=512
>> CONFIG_NR_CPUS_DEFAULT=64
>> CONFIG_NR_CPUS=256
>>
>> kexec -p -s fails with "kexec_file_load failed: Invalid argument".
>> Reducing the possible CPU count below the page-rounding threshold
>> (e.g. 72 via firmware) makes it succeed, confirming the reservation is
>> the limiting factor.
>>
>> Reserve the elfcorehdr for CONFIG_NR_CPUS, the compile-time upper bound
>> of num_possible_cpus(), so the reservation always covers the header that
>> is actually generated.
>>
>> Fixes: a72bbec70da2 ("crash: hotplug support for kexec_load()")
>> Signed-off-by: Ionut Nechita <ionut.nechita@xxxxxxxxxxxxx>
>> ---
>> arch/x86/kernel/crash.c | 6 +++---
>
>We should also update the comment in crash_handle_hotplug_event() to
>avoid misleading people.
>
>diff --git a/kernel/crash_core.c b/kernel/crash_core.c
>index 4f21fc3b108b..b046c1682aad 100644
>--- a/kernel/crash_core.c
>+++ b/kernel/crash_core.c
>@@ -565,7 +565,7 @@ int crash_check_hotplug_support(void)
> * new list of CPUs and memory. To make changes to the elfcorehdr, it
> * should be large enough to permit a growing number of CPU and Memory
> * resources. One can estimate the elfcorehdr memory size based on
>- * NR_CPUS_DEFAULT and CRASH_MAX_MEMORY_RANGES. The elfcorehdr is
>+ * NR_CPUS and CRASH_MAX_MEMORY_RANGES. The elfcorehdr is
One more spot carries the same stale name though:
kernel/Kconfig.kexec: "This value is combined with NR_CPUS_DEFAULT
and multiplied by sizeof(Elf64_Phdr) to determine the final
elfcorehdr memory buffer/segment size."
The CRASH_MAX_MEMORY_RANGES help text needs the same
s/NR_CPUS_DEFAULT/NR_CPUS/, either here or in Ionut's v2.
Reviewed-by: Bradley Morgan <include@xxxxxxxxx>
> * excluded from SHA verification by default if the architecture
> * supports crash hotplug.
> */
>
>
>> 1 file changed, 3 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
>> index e681ec9cf1dc8..e6f23933a6df2 100644
>> --- a/arch/x86/kernel/crash.c
>> +++ b/arch/x86/kernel/crash.c
>> @@ -369,9 +369,9 @@ int crash_load_segments(struct kimage *image)
>> * maximum CPUs and maximum memory ranges.
>> */
>> if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
>> - pnum = 2 + CONFIG_NR_CPUS_DEFAULT + CONFIG_CRASH_MAX_MEMORY_RANGES;
>> + pnum = 2 + CONFIG_NR_CPUS + CONFIG_CRASH_MAX_MEMORY_RANGES;
>> else
>> - pnum += 2 + CONFIG_NR_CPUS_DEFAULT;
>> + pnum += 2 + CONFIG_NR_CPUS;
>>
>> if (pnum < (unsigned long)PN_XNUM) {
>> kbuf.memsz = pnum * sizeof(Elf64_Phdr);
>> @@ -430,7 +430,7 @@ unsigned int arch_crash_get_elfcorehdr_size(void)
>> unsigned int sz;
>>
>> /* kernel_map, VMCOREINFO and maximum CPUs */
>> - sz = 2 + CONFIG_NR_CPUS_DEFAULT;
>> + sz = 2 + CONFIG_NR_CPUS;
>> if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
>> sz += CONFIG_CRASH_MAX_MEMORY_RANGES;
>> sz *= sizeof(Elf64_Phdr);
>
>
>
Thanks!