riscv: crash: Add crash hotplug support

When CPUs or memory are hot-plugged on a running system, the crash
dump's elfcorehdr -- which describes the available CPUs and memory to
the capture kernel -- must be kept in sync, otherwise a crash after a
hotplug event would produce a vmcore that does not reflect the current
system topology.

Enable crash hotplug for RISC-V by selecting
ARCH_SUPPORTS_CRASH_HOTPLUG and providing the three architecture hooks
invoked through the override macros in <asm/kexec.h>:

  arch_crash_hotplug_support() reports whether an image supports the
  mechanism; arch_crash_get_elfcorehdr_size() sizes the segment for
  NR_CPUS and, with memory hotplug, CRASH_MAX_MEMORY_RANGES;
  arch_crash_handle_hotplug_event() rebuilds the elfcorehdr through the
  crash_prepare_headers() helper and writes it over the old segment.

The elfcorehdr is copied with memcpy_flushcache(), matching the x86
implementation; on RISC-V this currently expands to a plain memcpy()
and will benefit automatically once the architecture provides a
flushcache backend. The implementation otherwise follows the x86 crash
hotplug model.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Tested-by: Rui Qi <qirui.001@bytedance.com>
Link: https://patch.msgid.link/20260910032619.2477279-1-qirui.001@bytedance.com
Signed-off-by: Paul Walmsley <pjw@kernel.org>
This commit is contained in:
Rui Qi 2026-10-05 19:02:53 -06:00 committed by Paul Walmsley
parent f89e5ee4d8
commit 8be4655bc9
4 changed files with 130 additions and 0 deletions

View File

@ -1133,6 +1133,9 @@ config ARCH_DEFAULT_CRASH_DUMP
config ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION
def_bool CRASH_RESERVE
config ARCH_SUPPORTS_CRASH_HOTPLUG
def_bool y
config COMPAT
bool "Kernel support for 32-bit U-mode"
default 64BIT

View File

@ -75,4 +75,17 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start,
unsigned long cmdline_len);
#endif
#ifdef CONFIG_CRASH_HOTPLUG
struct kimage;
void arch_crash_handle_hotplug_event(struct kimage *image, void *arg);
#define arch_crash_handle_hotplug_event arch_crash_handle_hotplug_event
int arch_crash_hotplug_support(struct kimage *image, unsigned long kexec_flags);
#define arch_crash_hotplug_support arch_crash_hotplug_support
unsigned int arch_crash_get_elfcorehdr_size(void);
#define crash_get_elfcorehdr_size arch_crash_get_elfcorehdr_size
#endif
#endif

View File

@ -111,6 +111,7 @@ obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_KEXEC_CORE) += kexec_relocate.o crash_save_regs.o machine_kexec.o
obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o kexec_image.o machine_kexec_file.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_CRASH_HOTPLUG) += crash.o
obj-$(CONFIG_VMCORE_INFO) += vmcore_info.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o

113
arch/riscv/kernel/crash.c Normal file
View File

@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* RISC-V crash hotplug support
*
* Copyright (C) 2025 Bytedance Ltd.
*/
#include <linux/kexec.h>
#include <linux/crash_core.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_CRASH_HOTPLUG
#undef pr_fmt
#define pr_fmt(fmt) "crash hp: " fmt
int arch_crash_hotplug_support(struct kimage *image, unsigned long kexec_flags)
{
#ifdef CONFIG_KEXEC_FILE
if (image->file_mode)
return 1;
#endif
/*
* For the kexec_load() syscall path, the user space kexec tool
* needs to indicate that the elfcorehdr segment is excluded from
* SHA verification by setting the appropriate flags.
*/
return (kexec_flags & KEXEC_UPDATE_ELFCOREHDR ||
kexec_flags & KEXEC_CRASH_HOTPLUG_SUPPORT);
}
unsigned int arch_crash_get_elfcorehdr_size(void)
{
unsigned int sz;
/* kernel_map, VMCOREINFO and maximum CPUs */
sz = 2 + CONFIG_NR_CPUS;
if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
sz += CONFIG_CRASH_MAX_MEMORY_RANGES;
sz *= sizeof(Elf64_Phdr);
return sz;
}
/**
* arch_crash_handle_hotplug_event() - Handle hotplug elfcorehdr changes
* @image: a pointer to kexec_crash_image
* @arg: struct memory_notify handler for memory hotplug case and
* NULL for CPU hotplug case.
*
* Prepare the new elfcorehdr and replace the existing elfcorehdr.
*/
void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
{
void *elfbuf = NULL, *old_elfcorehdr;
unsigned long mem, memsz;
unsigned long elfsz = 0;
/*
* As crash_prepare_elf64_headers() has already described all
* possible CPUs, there is no need to update the elfcorehdr
* for additional CPU changes.
*/
if ((image->file_mode || image->elfcorehdr_updated) &&
((image->hp_action == KEXEC_CRASH_HP_ADD_CPU) ||
(image->hp_action == KEXEC_CRASH_HP_REMOVE_CPU)))
return;
/*
* Create the new elfcorehdr reflecting the changes to CPU and/or
* memory resources.
*/
if (crash_prepare_headers(true, &elfbuf, &elfsz, NULL)) {
pr_err("unable to create new elfcorehdr");
goto out;
}
/*
* Obtain address and size of the elfcorehdr segment, and
* check it against the new elfcorehdr buffer.
*/
mem = image->segment[image->elfcorehdr_index].mem;
memsz = image->segment[image->elfcorehdr_index].memsz;
if (elfsz > memsz) {
pr_err("update elfcorehdr elfsz %lu > memsz %lu",
elfsz, memsz);
goto out;
}
/*
* Copy new elfcorehdr over the old elfcorehdr at destination.
*/
old_elfcorehdr = kmap_local_page(pfn_to_page(mem >> PAGE_SHIFT));
if (!old_elfcorehdr) {
pr_err("mapping elfcorehdr segment failed\n");
goto out;
}
/*
* Temporarily invalidate the crash image while the
* elfcorehdr is updated.
*/
xchg(&kexec_crash_image, NULL);
memcpy_flushcache(old_elfcorehdr, elfbuf, elfsz);
xchg(&kexec_crash_image, image);
kunmap_local(old_elfcorehdr);
pr_debug("updated elfcorehdr\n");
out:
vfree(elfbuf);
}
#endif /* CONFIG_CRASH_HOTPLUG */