Skip to content

Commit ea53ad9

Browse files
Eric DeVolderakpm00
authored andcommitted
x86/crash: add x86 crash hotplug support
When CPU or memory is hot un/plugged, or off/onlined, the crash elfcorehdr, which describes the CPUs and memory in the system, must also be updated. A new elfcorehdr is generated from the available CPUs and memory and replaces the existing elfcorehdr. The segment containing the elfcorehdr is identified at run-time in crash_core:crash_handle_hotplug_event(). No modifications to purgatory (see 'kexec: exclude elfcorehdr from the segment digest') or boot_params (as the elfcorehdr= capture kernel command line parameter pointer remains unchanged and correct) are needed, just elfcorehdr. For kexec_file_load(), the elfcorehdr segment size is based on NR_CPUS and CRASH_MAX_MEMORY_RANGES in order to accommodate a growing number of CPU and memory resources. For kexec_load(), the userspace kexec utility needs to size the elfcorehdr segment in the same/similar manner. To accommodate kexec_load() syscall in the absence of kexec_file_load() syscall support, prepare_elf_headers() and dependents are moved outside of CONFIG_KEXEC_FILE. [eric.devolder@oracle.com: correct unused function build error] Link: https://lkml.kernel.org/r/20230821182644.2143-1-eric.devolder@oracle.com Link: https://lkml.kernel.org/r/20230814214446.6659-6-eric.devolder@oracle.com Signed-off-by: Eric DeVolder <eric.devolder@oracle.com> Reviewed-by: Sourabh Jain <sourabhjain@linux.ibm.com> Acked-by: Hari Bathini <hbathini@linux.ibm.com> Acked-by: Baoquan He <bhe@redhat.com> Cc: Akhil Raj <lf32.dev@gmail.com> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Borislav Petkov (AMD) <bp@alien8.de> Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: Dave Young <dyoung@redhat.com> Cc: David Hildenbrand <david@redhat.com> Cc: Eric W. Biederman <ebiederm@xmission.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Cc: Mimi Zohar <zohar@linux.ibm.com> Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> Cc: Oscar Salvador <osalvador@suse.de> Cc: "Rafael J. Wysocki" <rafael@kernel.org> Cc: Sean Christopherson <seanjc@google.com> Cc: Takashi Iwai <tiwai@suse.de> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Thomas Weißschuh <linux@weissschuh.net> Cc: Valentin Schneider <vschneid@redhat.com> Cc: Vivek Goyal <vgoyal@redhat.com> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 88a6f89 commit ea53ad9

File tree

3 files changed

+116
-7
lines changed

3 files changed

+116
-7
lines changed

arch/x86/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,9 @@ config ARCH_SUPPORTS_KEXEC_JUMP
20692069
config ARCH_SUPPORTS_CRASH_DUMP
20702070
def_bool X86_64 || (X86_32 && HIGHMEM)
20712071

2072+
config ARCH_SUPPORTS_CRASH_HOTPLUG
2073+
def_bool y
2074+
20722075
config PHYSICAL_START
20732076
hex "Physical address where the kernel is loaded" if (EXPERT || CRASH_DUMP)
20742077
default "0x1000000"

arch/x86/include/asm/kexec.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,21 @@ typedef void crash_vmclear_fn(void);
209209
extern crash_vmclear_fn __rcu *crash_vmclear_loaded_vmcss;
210210
extern void kdump_nmi_shootdown_cpus(void);
211211

212+
#ifdef CONFIG_CRASH_HOTPLUG
213+
void arch_crash_handle_hotplug_event(struct kimage *image);
214+
#define arch_crash_handle_hotplug_event arch_crash_handle_hotplug_event
215+
216+
#ifdef CONFIG_HOTPLUG_CPU
217+
static inline int crash_hotplug_cpu_support(void) { return 1; }
218+
#define crash_hotplug_cpu_support crash_hotplug_cpu_support
219+
#endif
220+
221+
#ifdef CONFIG_MEMORY_HOTPLUG
222+
static inline int crash_hotplug_memory_support(void) { return 1; }
223+
#define crash_hotplug_memory_support crash_hotplug_memory_support
224+
#endif
225+
#endif
226+
212227
#endif /* __ASSEMBLY__ */
213228

214229
#endif /* _ASM_X86_KEXEC_H */

arch/x86/kernel/crash.c

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,7 @@ void native_machine_crash_shutdown(struct pt_regs *regs)
158158
crash_save_cpu(regs, safe_smp_processor_id());
159159
}
160160

161-
#ifdef CONFIG_KEXEC_FILE
162-
161+
#if defined(CONFIG_KEXEC_FILE) || defined(CONFIG_CRASH_HOTPLUG)
163162
static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
164163
{
165164
unsigned int *nr_ranges = arg;
@@ -231,7 +230,7 @@ static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
231230

232231
/* Prepare elf headers. Return addr and size */
233232
static int prepare_elf_headers(struct kimage *image, void **addr,
234-
unsigned long *sz)
233+
unsigned long *sz, unsigned long *nr_mem_ranges)
235234
{
236235
struct crash_mem *cmem;
237236
int ret;
@@ -249,14 +248,19 @@ static int prepare_elf_headers(struct kimage *image, void **addr,
249248
if (ret)
250249
goto out;
251250

251+
/* Return the computed number of memory ranges, for hotplug usage */
252+
*nr_mem_ranges = cmem->nr_ranges;
253+
252254
/* By default prepare 64bit headers */
253255
ret = crash_prepare_elf64_headers(cmem, IS_ENABLED(CONFIG_X86_64), addr, sz);
254256

255257
out:
256258
vfree(cmem);
257259
return ret;
258260
}
261+
#endif
259262

263+
#ifdef CONFIG_KEXEC_FILE
260264
static int add_e820_entry(struct boot_params *params, struct e820_entry *entry)
261265
{
262266
unsigned int nr_e820_entries;
@@ -371,18 +375,42 @@ int crash_setup_memmap_entries(struct kimage *image, struct boot_params *params)
371375
int crash_load_segments(struct kimage *image)
372376
{
373377
int ret;
378+
unsigned long pnum = 0;
374379
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
375380
.buf_max = ULONG_MAX, .top_down = false };
376381

377382
/* Prepare elf headers and add a segment */
378-
ret = prepare_elf_headers(image, &kbuf.buffer, &kbuf.bufsz);
383+
ret = prepare_elf_headers(image, &kbuf.buffer, &kbuf.bufsz, &pnum);
379384
if (ret)
380385
return ret;
381386

382-
image->elf_headers = kbuf.buffer;
383-
image->elf_headers_sz = kbuf.bufsz;
387+
image->elf_headers = kbuf.buffer;
388+
image->elf_headers_sz = kbuf.bufsz;
389+
kbuf.memsz = kbuf.bufsz;
390+
391+
#ifdef CONFIG_CRASH_HOTPLUG
392+
/*
393+
* The elfcorehdr segment size accounts for VMCOREINFO, kernel_map,
394+
* maximum CPUs and maximum memory ranges.
395+
*/
396+
if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
397+
pnum = 2 + CONFIG_NR_CPUS_DEFAULT + CONFIG_CRASH_MAX_MEMORY_RANGES;
398+
else
399+
pnum += 2 + CONFIG_NR_CPUS_DEFAULT;
400+
401+
if (pnum < (unsigned long)PN_XNUM) {
402+
kbuf.memsz = pnum * sizeof(Elf64_Phdr);
403+
kbuf.memsz += sizeof(Elf64_Ehdr);
404+
405+
image->elfcorehdr_index = image->nr_segments;
406+
407+
/* Mark as usable to crash kernel, else crash kernel fails on boot */
408+
image->elf_headers_sz = kbuf.memsz;
409+
} else {
410+
pr_err("number of Phdrs %lu exceeds max\n", pnum);
411+
}
412+
#endif
384413

385-
kbuf.memsz = kbuf.bufsz;
386414
kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
387415
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
388416
ret = kexec_add_buffer(&kbuf);
@@ -395,3 +423,66 @@ int crash_load_segments(struct kimage *image)
395423
return ret;
396424
}
397425
#endif /* CONFIG_KEXEC_FILE */
426+
427+
#ifdef CONFIG_CRASH_HOTPLUG
428+
429+
#undef pr_fmt
430+
#define pr_fmt(fmt) "crash hp: " fmt
431+
432+
/**
433+
* arch_crash_handle_hotplug_event() - Handle hotplug elfcorehdr changes
434+
* @image: a pointer to kexec_crash_image
435+
*
436+
* Prepare the new elfcorehdr and replace the existing elfcorehdr.
437+
*/
438+
void arch_crash_handle_hotplug_event(struct kimage *image)
439+
{
440+
void *elfbuf = NULL, *old_elfcorehdr;
441+
unsigned long nr_mem_ranges;
442+
unsigned long mem, memsz;
443+
unsigned long elfsz = 0;
444+
445+
/*
446+
* Create the new elfcorehdr reflecting the changes to CPU and/or
447+
* memory resources.
448+
*/
449+
if (prepare_elf_headers(image, &elfbuf, &elfsz, &nr_mem_ranges)) {
450+
pr_err("unable to create new elfcorehdr");
451+
goto out;
452+
}
453+
454+
/*
455+
* Obtain address and size of the elfcorehdr segment, and
456+
* check it against the new elfcorehdr buffer.
457+
*/
458+
mem = image->segment[image->elfcorehdr_index].mem;
459+
memsz = image->segment[image->elfcorehdr_index].memsz;
460+
if (elfsz > memsz) {
461+
pr_err("update elfcorehdr elfsz %lu > memsz %lu",
462+
elfsz, memsz);
463+
goto out;
464+
}
465+
466+
/*
467+
* Copy new elfcorehdr over the old elfcorehdr at destination.
468+
*/
469+
old_elfcorehdr = kmap_local_page(pfn_to_page(mem >> PAGE_SHIFT));
470+
if (!old_elfcorehdr) {
471+
pr_err("mapping elfcorehdr segment failed\n");
472+
goto out;
473+
}
474+
475+
/*
476+
* Temporarily invalidate the crash image while the
477+
* elfcorehdr is updated.
478+
*/
479+
xchg(&kexec_crash_image, NULL);
480+
memcpy_flushcache(old_elfcorehdr, elfbuf, elfsz);
481+
xchg(&kexec_crash_image, image);
482+
kunmap_local(old_elfcorehdr);
483+
pr_debug("updated elfcorehdr\n");
484+
485+
out:
486+
vfree(elfbuf);
487+
}
488+
#endif

0 commit comments

Comments
 (0)