All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/2] x86/crash: fix kexec_file_load(2) -EINVAL on machines with many possible CPUs
@ 2026-08-26  7:35 Ionut Nechita (Wind River)
  2026-08-26  7:35 ` [PATCH v3 1/2] x86/crash: reserve elfcorehdr for CONFIG_NR_CPUS, not CONFIG_NR_CPUS_DEFAULT Ionut Nechita (Wind River)
  2026-08-26  7:35 ` [PATCH v3 2/2] crash: update stale NR_CPUS_DEFAULT references in elfcorehdr sizing docs Ionut Nechita (Wind River)
  0 siblings, 2 replies; 4+ messages in thread
From: Ionut Nechita (Wind River) @ 2026-08-26  7:35 UTC (permalink / raw)
  To: x86, kexec
  Cc: tglx, mingo, bp, dave.hansen, hpa, akpm, baoquan.he, rppt,
	pasha.tatashin, pratyush, ruirui.yang, eric.devolder, hbathini,
	sourabhjain, ruanjinjie, include, linux-kernel

From: Ionut Nechita <ionut.nechita@windriver.com>

Hi,

On x86 with CONFIG_CRASH_HOTPLUG=y and CONFIG_MEMORY_HOTPLUG=n, the
crash elfcorehdr segment is reserved for

    nr_mem_ranges + 2 + CONFIG_NR_CPUS_DEFAULT

program headers, while the header that crash_prepare_elf64_headers()
actually builds carries

    nr_mem_ranges + 2 + num_possible_cpus()

of them.  num_possible_cpus() is bounded by CONFIG_NR_CPUS, not by
CONFIG_NR_CPUS_DEFAULT, so on configs that raise CONFIG_NR_CPUS above
the arch default without MAXSMP the header outgrows its reservation once
the excess exceeds the page padding, and kexec_file_load(2) is rejected
by sanity_check_segment_list() with -EINVAL.

Patch 1 reserves 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.  It also fixes the same shortfall in
arch_crash_get_elfcorehdr_size(), which feeds
/sys/kernel/crash_elfcorehdr_size and therefore the kexec_load(2) path.

Patch 2 is a documentation-only follow-up requested during v1 review: it
updates the two remaining places that still name NR_CPUS_DEFAULT when
describing the elfcorehdr sizing (the comment above
crash_handle_hotplug_event() and the CRASH_MAX_MEMORY_RANGES help text).

Based on linux-next (next-20260825, base-commit a8406e6c0b79b).

Verified on a single-socket Xeon 6776P running a PREEMPT_RT kernel with:

    # CONFIG_MAXSMP is not set
    # CONFIG_MEMORY_HOTPLUG is not set
    CONFIG_NR_CPUS_RANGE_BEGIN=2
    CONFIG_NR_CPUS_RANGE_END=512
    CONFIG_NR_CPUS_DEFAULT=64
    CONFIG_NR_CPUS=256
    CONFIG_CRASH_MAX_MEMORY_RANGES=8192

- 144 possible CPUs: excess is (144 - 64) * 56 = 4480 bytes, more than
  the 4096 bytes of page padding, so 'kexec -p -s' fails with
  "kexec_file_load failed: Invalid argument"
- 72 possible CPUs (reduced via firmware): excess is (72 - 64) * 56 =
  448 bytes, still absorbed by the page rounding, and the load succeeds

Changes since v2:
- No code changes.  The diff is byte-identical to v2; only the patch 1/2
  changelog and the collected tags differ.
- Patch 1/2: corrected the claim that kexec_load(2) is "unaffected".
  As Sourabh Jain pointed out, with crash hotplug enabled kexec-tools
  sizes the elfcorehdr segment from /sys/kernel/crash_elfcorehdr_size in
  load_crashdump_segments(), i.e. from arch_crash_get_elfcorehdr_size(),
  which uses the same CONFIG_NR_CPUS_DEFAULT; add_segment_phys_virt()
  then clamps bufsz to memsz.  So that path does not fail the load, it
  silently truncates the elfcorehdr, which shows up later as a bad dump.
  Switching arch_crash_get_elfcorehdr_size() to CONFIG_NR_CPUS - which
  this patch already did - fixes that path as well.  The changelog now
  says so instead of claiming immunity.
- Patch 1/2: picked up Reviewed-by from Sourabh Jain and Acked-by from
  Baoquan He.
- Patch 2/2: picked up Reviewed-by from Bradley Morgan.
- Both patches: added an Assisted-by: LLM trailer, per
  Documentation/process/coding-assistants.rst.  An LLM was used for the
  root cause analysis and the changelog wording; the bug itself was
  found and reproduced on real hardware, the fix was reviewed by me, and
  the Signed-off-by is mine.  This should have been present in v1 and
  v2, and was not - my oversight.
- Rebased from next-20260824 onto next-20260825.

The two further issues Sourabh raised on the v2 thread - the
add_segment_phys_virt() bufsz truncation in kexec-tools, and having
crash_load_segments() skip the kbuf.memsz update when the prepared
buffer is already larger than the statically computed size - are
deliberately left out of this series and are worth handling separately,
as he suggested.

On the num_possible_cpus() alternative raised by Jinjie in v1: it would
give a tighter reservation and powerpc's arch_crash_get_elfcorehdr_size()
already does exactly that.  I kept CONFIG_NR_CPUS here because it is the
minimal, easy-to-backport fix - it just swaps the wrong compile-time
constant for the correct compile-time upper bound.  I am happy to respin
with num_possible_cpus() if maintainers prefer that.

v1: https://lore.kernel.org/lkml/20260812170433.533845-1-ionut.nechita@windriver.com/
v2: https://lore.kernel.org/lkml/20260825075043.42041-1-ionut.nechita@windriver.com/

Ionut Nechita (2):
  x86/crash: reserve elfcorehdr for CONFIG_NR_CPUS, not
    CONFIG_NR_CPUS_DEFAULT
  crash: update stale NR_CPUS_DEFAULT references in elfcorehdr sizing
    docs

 arch/x86/kernel/crash.c | 6 +++---
 kernel/Kconfig.kexec    | 2 +-
 kernel/crash_core.c     | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)


base-commit: a8406e6c0b793ce0788019683837c40855b55995
--
2.55.0



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v3 1/2] x86/crash: reserve elfcorehdr for CONFIG_NR_CPUS, not CONFIG_NR_CPUS_DEFAULT
  2026-08-26  7:35 [PATCH v3 0/2] x86/crash: fix kexec_file_load(2) -EINVAL on machines with many possible CPUs Ionut Nechita (Wind River)
@ 2026-08-26  7:35 ` Ionut Nechita (Wind River)
  2026-08-26 13:33   ` Dave Hansen
  2026-08-26  7:35 ` [PATCH v3 2/2] crash: update stale NR_CPUS_DEFAULT references in elfcorehdr sizing docs Ionut Nechita (Wind River)
  1 sibling, 1 reply; 4+ messages in thread
From: Ionut Nechita (Wind River) @ 2026-08-26  7:35 UTC (permalink / raw)
  To: x86, kexec
  Cc: tglx, mingo, bp, dave.hansen, hpa, akpm, baoquan.he, rppt,
	pasha.tatashin, pratyush, ruirui.yang, eric.devolder, hbathini,
	sourabhjain, ruanjinjie, include, linux-kernel

From: Ionut Nechita <ionut.nechita@windriver.com>

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.

With CONFIG_CRASH_HOTPLUG=y the elfcorehdr segment is over-allocated so
it can be updated in place on CPU/memory hotplug.  On the
!CONFIG_MEMORY_HOTPLUG path, crash_load_segments() sizes that
reservation as:

	ret = crash_prepare_headers(..., &kbuf.bufsz, &pnum);
	...
	pnum += 2 + CONFIG_NR_CPUS_DEFAULT;

The value that lands in @pnum is crash_prepare_headers()'s
@nr_mem_ranges out parameter, i.e. cmem->nr_ranges - the number of
memory ranges only, not a phdr count.  The header that
crash_prepare_elf64_headers() actually builds adds one phdr per
*possible* CPU on top of those ranges:

	nr_phdr = nr_cpus + 1;		/* + vmcoreinfo */
	nr_phdr += mem->nr_ranges;
	nr_phdr++;			/* + kernel text map */

So the reservation covers

	nr_ranges + 2 + CONFIG_NR_CPUS_DEFAULT

phdrs while the buffer holds

	nr_ranges + 2 + num_possible_cpus()

phdrs, and the buffer exceeds the reservation by

	(num_possible_cpus() - CONFIG_NR_CPUS_DEFAULT) * sizeof(Elf64_Phdr)

bytes as soon as num_possible_cpus() grows past CONFIG_NR_CPUS_DEFAULT.
num_possible_cpus() is bounded by CONFIG_NR_CPUS, not by
CONFIG_NR_CPUS_DEFAULT, so this is reachable on any config that raises
CONFIG_NR_CPUS above the arch default without CONFIG_MAXSMP.

crash_prepare_elf64_headers() rounds bufsz up to ELF_CORE_HEADER_ALIGN
and kexec_add_buffer() rounds memsz up to PAGE_SIZE (both 4096), so the
excess is invisible until it outgrows that padding.  Once it does,
sanity_check_segment_list() rejects the image:

	if (image->segment[i].bufsz > image->segment[i].memsz)
		return -EINVAL;

kexec_load(2) does not report an error on the same machine, but it is
not unaffected either.  With crash hotplug enabled, kexec-tools sizes
the elfcorehdr segment from /sys/kernel/crash_elfcorehdr_size in
load_crashdump_segments(), i.e. from arch_crash_get_elfcorehdr_size(),
which derives its answer from the very same CONFIG_NR_CPUS_DEFAULT.
add_segment_phys_virt() then clamps the buffer to that segment:

	if (bufsz > memsz) {
		bufsz = memsz;
	}

so on the kexec_load(2) path the undersized reservation silently
truncates the elfcorehdr instead of failing the load, which surfaces
later as a bad or unusable dump rather than as a load failure.
Switching arch_crash_get_elfcorehdr_size() to CONFIG_NR_CPUS below fixes
that path as well.

Observed on a single-socket Xeon 6776P (144 possible CPUs) running a
PREEMPT_RT kernel with:

	# CONFIG_MAXSMP is not set
	# CONFIG_MEMORY_HOTPLUG is not set
	CONFIG_NR_CPUS_RANGE_BEGIN=2
	CONFIG_NR_CPUS_RANGE_END=512
	CONFIG_NR_CPUS_DEFAULT=64
	CONFIG_NR_CPUS=256

At 144 possible CPUs the buffer exceeds the reservation by
(144 - 64) * 56 = 4480 bytes.  That is more than the 4096 bytes of page
padding, so the overflow is guaranteed and kexec -p -s fails with
"kexec_file_load failed: Invalid argument".  Reducing the possible CPU
count to 72 leaves an excess of (72 - 64) * 56 = 448 bytes, which the
page rounding still absorbs, and the load succeeds - 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.

The CONFIG_MEMORY_HOTPLUG=y path discards @pnum and reserves
2 + CONFIG_NR_CPUS_DEFAULT + CONFIG_CRASH_MAX_MEMORY_RANGES phdrs
instead.  With the default CONFIG_CRASH_MAX_MEMORY_RANGES=8192 the
memory range allowance dwarfs the CPU shortfall, so that path does not
fail in practice; it is switched to CONFIG_NR_CPUS as well for
consistency and to stay correct for small CONFIG_CRASH_MAX_MEMORY_RANGES
values.

This does not change the reservation for defconfig-like builds, since
CONFIG_NR_CPUS defaults to CONFIG_NR_CPUS_DEFAULT.  Only configs that
raise CONFIG_NR_CPUS reserve more, and the worst case is bounded by the
top of the range (CONFIG_NR_CPUS=8192 with CONFIG_CPUMASK_OFFSTACK=y),
which is exactly what CONFIG_MAXSMP already reserves today.

Fixes: ea53ad9cf73b ("x86/crash: add x86 crash hotplug support")
Assisted-by: LLM
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
Reviewed-by: Jinjie Ruan <ruanjinjie@huawei.com>
Reviewed-by: Bradley Morgan <include@grrlz.net>
Reviewed-by: Sourabh Jain <sourabhjain@linux.ibm.com>
Acked-by: Baoquan He <baoquan.he@linux.dev>
---
 arch/x86/kernel/crash.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
index e681ec9cf1dc..e6f23933a6df 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);

base-commit: a8406e6c0b793ce0788019683837c40855b55995
-- 
2.55.0



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v3 2/2] crash: update stale NR_CPUS_DEFAULT references in elfcorehdr sizing docs
  2026-08-26  7:35 [PATCH v3 0/2] x86/crash: fix kexec_file_load(2) -EINVAL on machines with many possible CPUs Ionut Nechita (Wind River)
  2026-08-26  7:35 ` [PATCH v3 1/2] x86/crash: reserve elfcorehdr for CONFIG_NR_CPUS, not CONFIG_NR_CPUS_DEFAULT Ionut Nechita (Wind River)
@ 2026-08-26  7:35 ` Ionut Nechita (Wind River)
  1 sibling, 0 replies; 4+ messages in thread
From: Ionut Nechita (Wind River) @ 2026-08-26  7:35 UTC (permalink / raw)
  To: x86, kexec
  Cc: tglx, mingo, bp, dave.hansen, hpa, akpm, baoquan.he, rppt,
	pasha.tatashin, pratyush, ruirui.yang, eric.devolder, hbathini,
	sourabhjain, ruanjinjie, include, linux-kernel

From: Ionut Nechita <ionut.nechita@windriver.com>

The elfcorehdr over-allocation for crash hotplug is now computed from
CONFIG_NR_CPUS rather than CONFIG_NR_CPUS_DEFAULT, but two pieces of
documentation still name the old symbol: the comment above
crash_handle_hotplug_event() and the CRASH_MAX_MEMORY_RANGES help text.

Update both so they describe what the code actually does and do not
mislead people sizing CRASH_MAX_MEMORY_RANGES.

Documentation only, no functional change.

Suggested-by: Jinjie Ruan <ruanjinjie@huawei.com>
Suggested-by: Bradley Morgan <include@grrlz.net>
Assisted-by: LLM
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
Reviewed-by: Bradley Morgan <include@grrlz.net>
---
 kernel/Kconfig.kexec | 2 +-
 kernel/crash_core.c  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
index 15632358bcf7..a97ed9605602 100644
--- a/kernel/Kconfig.kexec
+++ b/kernel/Kconfig.kexec
@@ -167,7 +167,7 @@ config CRASH_MAX_MEMORY_RANGES
 	  memory regions that the elfcorehdr buffer/segment can accommodate.
 	  These regions are obtained via walk_system_ram_res(); eg. the
 	  'System RAM' entries in /proc/iomem.
-	  This value is combined with NR_CPUS_DEFAULT and multiplied by
+	  This value is combined with NR_CPUS and multiplied by
 	  sizeof(Elf64_Phdr) to determine the final elfcorehdr memory buffer/
 	  segment size.
 	  The value 8192, for example, covers a (sparsely populated) 1TiB system
diff --git a/kernel/crash_core.c b/kernel/crash_core.c
index 2b36aa9fade0..d0bd2d0cf899 100644
--- a/kernel/crash_core.c
+++ b/kernel/crash_core.c
@@ -648,7 +648,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
  * excluded from SHA verification by default if the architecture
  * supports crash hotplug.
  */
-- 
2.55.0



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 1/2] x86/crash: reserve elfcorehdr for CONFIG_NR_CPUS, not CONFIG_NR_CPUS_DEFAULT
  2026-08-26  7:35 ` [PATCH v3 1/2] x86/crash: reserve elfcorehdr for CONFIG_NR_CPUS, not CONFIG_NR_CPUS_DEFAULT Ionut Nechita (Wind River)
@ 2026-08-26 13:33   ` Dave Hansen
  0 siblings, 0 replies; 4+ messages in thread
From: Dave Hansen @ 2026-08-26 13:33 UTC (permalink / raw)
  To: Ionut Nechita (Wind River), x86, kexec
  Cc: tglx, mingo, bp, dave.hansen, hpa, akpm, baoquan.he, rppt,
	pasha.tatashin, pratyush, ruirui.yang, eric.devolder, hbathini,
	sourabhjain, ruanjinjie, include, linux-kernel

On 8/26/26 00:35, Ionut Nechita (Wind River) wrote:
> Assisted-by: LLM

"LLM" went a little wild on the changelogs here. Shouldn't this just be
something like:

NR_CPUS_DEFAULT is *purely* a Kconfig thing. Its entire purpose in life
is to start NR_CPUS at a sane value. There is precisely one (buggy)
reference to it outside of Kconfig in the whole kernel: the x86 crash code.

That code will undersize a reservation if NR_CPUS exceeds
NR_CPUS_DEFAULT. This causes kexec_load() silently truncate the
elfcorehdr, which surfaces later as a bad or unusable dump.

Size the elfcorehdr reservation with NR_CPUS instead.

---

Does it need more than that?


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-08-26 13:33 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26  7:35 [PATCH v3 0/2] x86/crash: fix kexec_file_load(2) -EINVAL on machines with many possible CPUs Ionut Nechita (Wind River)
2026-08-26  7:35 ` [PATCH v3 1/2] x86/crash: reserve elfcorehdr for CONFIG_NR_CPUS, not CONFIG_NR_CPUS_DEFAULT Ionut Nechita (Wind River)
2026-08-26 13:33   ` Dave Hansen
2026-08-26  7:35 ` [PATCH v3 2/2] crash: update stale NR_CPUS_DEFAULT references in elfcorehdr sizing docs Ionut Nechita (Wind River)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.