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

crash: hotplug support for kexec_load()

The hotplug support for kexec_load() requires changes to the userspace
kexec-tools and a little extra help from the kernel.

Given a kdump capture kernel loaded via kexec_load(), and a subsequent
hotplug event, the crash hotplug handler finds the elfcorehdr and rewrites
it to reflect the hotplug change. That is the desired outcome, however,
at kernel panic time, the purgatory integrity check fails (because the
elfcorehdr changed), and the capture kernel does not boot and no vmcore is
generated.

Therefore, the userspace kexec-tools/kexec must indicate to the kernel
that the elfcorehdr can be modified (because the kexec excluded the
elfcorehdr from the digest, and sized the elfcorehdr memory buffer
appropriately).

To facilitate hotplug support with kexec_load():
- a new kexec flag KEXEC_UPATE_ELFCOREHDR indicates that it is
safe for the kernel to modify the kexec_load()'d elfcorehdr
- the /sys/kernel/crash_elfcorehdr_size node communicates the
preferred size of the elfcorehdr memory buffer
- The sysfs crash_hotplug nodes (ie.
/sys/devices/system/[cpu|memory]/crash_hotplug) dynamically
take into account kexec_file_load() vs kexec_load() and
KEXEC_UPDATE_ELFCOREHDR.
This is critical so that the udev rule processing of crash_hotplug
is all that is needed to determine if the userspace unload-then-load
of the kdump image is to be skipped, or not. The proposed udev
rule change looks like:
# The kernel updates the crash elfcorehdr for CPU and memory changes
SUBSYSTEM=="cpu", ATTRS{crash_hotplug}=="1", GOTO="kdump_reload_end"
SUBSYSTEM=="memory", ATTRS{crash_hotplug}=="1", GOTO="kdump_reload_end"

The table below indicates the behavior of kexec_load()'d kdump image
updates (with the new udev crash_hotplug rule in place):

Kernel |Kexec
-------+-----+----
Old |Old |New
| a | a
-------+-----+----
New | a | b
-------+-----+----

where kexec 'old' and 'new' delineate kexec-tools has the needed
modifications for the crash hotplug feature, and kernel 'old' and 'new'
delineate the kernel supports this crash hotplug feature.

Behavior 'a' indicates the unload-then-reload of the entire kdump image.
For the kexec 'old' column, the unload-then-reload occurs due to the
missing flag KEXEC_UPDATE_ELFCOREHDR. An 'old' kernel (with 'new' kexec)
does not present the crash_hotplug sysfs node, which leads to the
unload-then-reload of the kdump image.

Behavior 'b' indicates the desired optimized behavior of the kernel
directly modifying the elfcorehdr and avoiding the unload-then-reload of
the kdump image.

If the udev rule is not updated with crash_hotplug node check, then no
matter any combination of kernel or kexec is new or old, the kdump image
continues to be unload-then-reload on hotplug changes.

To fully support crash hotplug feature, there needs to be a rollout of
kernel, kexec-tools and udev rule changes. However, the order of the
rollout of these pieces does not matter; kexec_load()'d kdump images still
function for hotplug as-is.

Link: https://lkml.kernel.org/r/20230814214446.6659-7-eric.devolder@oracle.com
Signed-off-by: Eric DeVolder <eric.devolder@oracle.com>
Suggested-by: Hari Bathini <hbathini@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: Sourabh Jain <sourabhjain@linux.ibm.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>

authored by

Eric DeVolder and committed by
Andrew Morton
a72bbec7 ea53ad9c

+102 -6
+7 -4
arch/x86/include/asm/kexec.h
··· 214 214 #define arch_crash_handle_hotplug_event arch_crash_handle_hotplug_event 215 215 216 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 217 + int arch_crash_hotplug_cpu_support(void); 218 + #define crash_hotplug_cpu_support arch_crash_hotplug_cpu_support 219 219 #endif 220 220 221 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 222 + int arch_crash_hotplug_memory_support(void); 223 + #define crash_hotplug_memory_support arch_crash_hotplug_memory_support 224 224 #endif 225 + 226 + unsigned int arch_crash_get_elfcorehdr_size(void); 227 + #define crash_get_elfcorehdr_size arch_crash_get_elfcorehdr_size 225 228 #endif 226 229 227 230 #endif /* __ASSEMBLY__ */
+27
arch/x86/kernel/crash.c
··· 429 429 #undef pr_fmt 430 430 #define pr_fmt(fmt) "crash hp: " fmt 431 431 432 + /* These functions provide the value for the sysfs crash_hotplug nodes */ 433 + #ifdef CONFIG_HOTPLUG_CPU 434 + int arch_crash_hotplug_cpu_support(void) 435 + { 436 + return crash_check_update_elfcorehdr(); 437 + } 438 + #endif 439 + 440 + #ifdef CONFIG_MEMORY_HOTPLUG 441 + int arch_crash_hotplug_memory_support(void) 442 + { 443 + return crash_check_update_elfcorehdr(); 444 + } 445 + #endif 446 + 447 + unsigned int arch_crash_get_elfcorehdr_size(void) 448 + { 449 + unsigned int sz; 450 + 451 + /* kernel_map, VMCOREINFO and maximum CPUs */ 452 + sz = 2 + CONFIG_NR_CPUS_DEFAULT; 453 + if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) 454 + sz += CONFIG_CRASH_MAX_MEMORY_RANGES; 455 + sz *= sizeof(Elf64_Phdr); 456 + return sz; 457 + } 458 + 432 459 /** 433 460 * arch_crash_handle_hotplug_event() - Handle hotplug elfcorehdr changes 434 461 * @image: a pointer to kexec_crash_image
+12 -2
include/linux/kexec.h
··· 320 320 unsigned int preserve_context : 1; 321 321 /* If set, we are using file mode kexec syscall */ 322 322 unsigned int file_mode:1; 323 + #ifdef CONFIG_CRASH_HOTPLUG 324 + /* If set, allow changes to elfcorehdr of kexec_load'd image */ 325 + unsigned int update_elfcorehdr:1; 326 + #endif 323 327 324 328 #ifdef ARCH_HAS_KIMAGE_ARCH 325 329 struct kimage_arch arch; ··· 400 396 401 397 /* List of defined/legal kexec flags */ 402 398 #ifndef CONFIG_KEXEC_JUMP 403 - #define KEXEC_FLAGS KEXEC_ON_CRASH 399 + #define KEXEC_FLAGS (KEXEC_ON_CRASH | KEXEC_UPDATE_ELFCOREHDR) 404 400 #else 405 - #define KEXEC_FLAGS (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT) 401 + #define KEXEC_FLAGS (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT | KEXEC_UPDATE_ELFCOREHDR) 406 402 #endif 407 403 408 404 /* List of defined/legal kexec file flags */ ··· 490 486 static inline void arch_crash_handle_hotplug_event(struct kimage *image) { } 491 487 #endif 492 488 489 + int crash_check_update_elfcorehdr(void); 490 + 493 491 #ifndef crash_hotplug_cpu_support 494 492 static inline int crash_hotplug_cpu_support(void) { return 0; } 495 493 #endif 496 494 497 495 #ifndef crash_hotplug_memory_support 498 496 static inline int crash_hotplug_memory_support(void) { return 0; } 497 + #endif 498 + 499 + #ifndef crash_get_elfcorehdr_size 500 + static inline unsigned int crash_get_elfcorehdr_size(void) { return 0; } 499 501 #endif 500 502 501 503 #else /* !CONFIG_KEXEC_CORE */
+1
include/uapi/linux/kexec.h
··· 12 12 /* kexec flags for different usage scenarios */ 13 13 #define KEXEC_ON_CRASH 0x00000001 14 14 #define KEXEC_PRESERVE_CONTEXT 0x00000002 15 + #define KEXEC_UPDATE_ELFCOREHDR 0x00000004 15 16 #define KEXEC_ARCH_MASK 0xffff0000 16 17 17 18 /*
+4
kernel/Kconfig.kexec
··· 143 143 memory buffer/segment size under 1MiB. This represents a sane choice 144 144 to accommodate both baremetal and virtual machine configurations. 145 145 146 + For the kexec_load() syscall path, CRASH_MAX_MEMORY_RANGES is part of 147 + the computation behind the value provided through the 148 + /sys/kernel/crash_elfcorehdr_size attribute. 149 + 146 150 endmenu
+31
kernel/crash_core.c
··· 740 740 #ifdef CONFIG_CRASH_HOTPLUG 741 741 #undef pr_fmt 742 742 #define pr_fmt(fmt) "crash hp: " fmt 743 + 744 + /* 745 + * This routine utilized when the crash_hotplug sysfs node is read. 746 + * It reflects the kernel's ability/permission to update the crash 747 + * elfcorehdr directly. 748 + */ 749 + int crash_check_update_elfcorehdr(void) 750 + { 751 + int rc = 0; 752 + 753 + /* Obtain lock while reading crash information */ 754 + if (!kexec_trylock()) { 755 + pr_info("kexec_trylock() failed, elfcorehdr may be inaccurate\n"); 756 + return 0; 757 + } 758 + if (kexec_crash_image) { 759 + if (kexec_crash_image->file_mode) 760 + rc = 1; 761 + else 762 + rc = kexec_crash_image->update_elfcorehdr; 763 + } 764 + /* Release lock now that update complete */ 765 + kexec_unlock(); 766 + 767 + return rc; 768 + } 769 + 743 770 /* 744 771 * To accurately reflect hot un/plug changes of cpu and memory resources 745 772 * (including onling and offlining of those resources), the elfcorehdr ··· 796 769 goto out; 797 770 798 771 image = kexec_crash_image; 772 + 773 + /* Check that updating elfcorehdr is permitted */ 774 + if (!(image->file_mode || image->update_elfcorehdr)) 775 + goto out; 799 776 800 777 if (hp_action == KEXEC_CRASH_HP_ADD_CPU || 801 778 hp_action == KEXEC_CRASH_HP_REMOVE_CPU)
+5
kernel/kexec.c
··· 129 129 if (flags & KEXEC_PRESERVE_CONTEXT) 130 130 image->preserve_context = 1; 131 131 132 + #ifdef CONFIG_CRASH_HOTPLUG 133 + if (flags & KEXEC_UPDATE_ELFCOREHDR) 134 + image->update_elfcorehdr = 1; 135 + #endif 136 + 132 137 ret = machine_kexec_prepare(image); 133 138 if (ret) 134 139 goto out;
+15
kernel/ksysfs.c
··· 165 165 } 166 166 KERNEL_ATTR_RO(vmcoreinfo); 167 167 168 + #ifdef CONFIG_CRASH_HOTPLUG 169 + static ssize_t crash_elfcorehdr_size_show(struct kobject *kobj, 170 + struct kobj_attribute *attr, char *buf) 171 + { 172 + unsigned int sz = crash_get_elfcorehdr_size(); 173 + 174 + return sysfs_emit(buf, "%u\n", sz); 175 + } 176 + KERNEL_ATTR_RO(crash_elfcorehdr_size); 177 + 178 + #endif 179 + 168 180 #endif /* CONFIG_CRASH_CORE */ 169 181 170 182 /* whether file capabilities are enabled */ ··· 267 255 #endif 268 256 #ifdef CONFIG_CRASH_CORE 269 257 &vmcoreinfo_attr.attr, 258 + #ifdef CONFIG_CRASH_HOTPLUG 259 + &crash_elfcorehdr_size_attr.attr, 260 + #endif 270 261 #endif 271 262 #ifndef CONFIG_TINY_RCU 272 263 &rcu_expedited_attr.attr,