From 405c00e43a9c72a0b9b318cc2d2e298386ffb58e Mon Sep 17 00:00:00 2001 From: DAI RENJIE Date: Fri, 21 Aug 2026 14:08:17 +0000 Subject: resource: fix lost wakeup when waiting for a muxed region A task waiting for a muxed region can sleep forever in TASK_UNINTERRUPTIBLE even though the region it waits for is already free. __request_region_locked() queues itself on muxed_resource_wait and drops resource_lock before setting TASK_UNINTERRUPTIBLE, while __release_region() wakes the queue after dropping the same lock. A wakeup landing in between finds TASK_RUNNING, does not match TASK_NORMAL and is discarded; callers hold a muxed region only across a bounded transaction, so no further release is coming. The task is unkillable and its caller never returns. The window is one store wide, but an interrupt is enough to hold the waiter in it, and the machine this was seen on runs PREEMPT_DYNAMIC in its voluntary default. Since v6.11 spd5118 exports the DDR5 sensors of AMD boards through i2c-piix4, which takes a muxed region per SMBus transaction; a third of the in-tree users of request_muxed_region() are hwmon drivers, so reading a world-readable attribute is all an unprivileged user needs to drive the contention. The blocked task sleeps holding the i2c adapter bus lock, and 27 more piled up behind it. Reproduced by building a kernel with the two orderings selectable at runtime and a 2ms delay inside the window. Switching only that knob, a two-thread barriered reproducer loses the wakeup 200 times out of 200 before the fix and 0 out of 200 after it; without the delay it goes 20000 times through the wait path and loses none. Fix it by setting the task state before dropping resource_lock, as prepare_to_wait() does: the releasing side needs resource_lock to unlink the resource, so it cannot reach the wakeup before the state is published. Link: https://lore.kernel.org/20260821-b4-resource-muxed-lost-wakeup-v1-1-37eb6473a76c@gmail.com Fixes: 8b6d043b7ee2 ("resource: shared I/O region support") Signed-off-by: DAI RENJIE Signed-off-by: Andrew Morton Reviewed-by: Bradley Morgan Reviewed-by: Andrew Morton Assisted-by: Claude:claude-opus-5 Cc: Mark Brown Cc: Kees Cook Cc: Bjorn Helgaas Cc: --- kernel/resource.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/resource.c b/kernel/resource.c index e60539a55541..54d7199695fb 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -1350,8 +1350,8 @@ static int __request_region_locked(struct resource *res, struct resource *parent } if (conflict->flags & flags & IORESOURCE_MUXED) { add_wait_queue(&muxed_resource_wait, &wait); - write_unlock(&resource_lock); set_current_state(TASK_UNINTERRUPTIBLE); + write_unlock(&resource_lock); schedule(); remove_wait_queue(&muxed_resource_wait, &wait); write_lock(&resource_lock); -- cgit v1.2.3 From e614d360f3877bca8ef3d821fbef828f3628f40d Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Sun, 4 Oct 2026 12:36:09 -0400 Subject: hung_task: reset warning budget when problem gets resolved Patch series "hung_task: Improve warning budget handling and task reporting", v11. The hung_task watchdog detects tasks stuck in TASK_UNINTERRUPTIBLE (D) state for longer than CONFIG_DEFAULT_HUNG_TASK_TIMEOUT seconds. To prevent log spam during system spikes, sysctl_hung_task_warnings enforces a budget on the number of logged warnings. However, the current implementation has two major limitations: 1. Permanent exhaustion of warning budget sysctl_hung_task_warnings is decremented directly when printing warnings. Once this budget hits zero, no further warnings are reported until an administrator manually updates the sysctl value or reboots the system. Consequently, a single temporary hang episode permanently blinds the kernel watchdog to any subsequent hung tasks after system recovery. 2. Total log suppression when budget is exhausted Once the warning budget reaches zero, hung_task_info() completely suppresses all output, including the basic single-line alert. While suppressing verbose stack dumps and lock debugging is desirable to prevent dmesg flooding, hiding basic task alerts leaves administrators entirely unaware that tasks are hanging. This patch series resolves both limitations by decoupling the configured warning limit from the active runtime budget, automatically resetting the budget upon system recovery or sysctl updates, and emitting a single aggregate summary line when hung tasks are detected under an exhausted warning budget. Patch 1 separates the configured sysctl hung_task_warnings from the runtime budget, making khungtaskd the sole owner of runtime budget updates. The budget is reloaded directly when a scan finds zero hung tasks, or via an atomic reset request published on sysctl write. Patch 2 prevents dmesg flooding during system-wide hangs by keeping non-panic per-task stack dumps budgeted, while providing ongoing visibility by logging a single aggregate summary line at the end of each scan iteration when the warning budget is exhausted. This patch (of 2): The sysctl hung_task_warnings currently holds both the configured warning limit and the remaining budget. Each detailed report decrements the sysctl, so once it reaches zero, the configured limit is lost and cannot be restored automatically. Keep sysctl_hung_task_warnings as the configured warning limit and make khungtaskd the sole owner of the remaining budget. A check that finds no hung tasks reloads the budget directly from the configured limit. A successful sysctl write publishes an atomic reset request, which khungtaskd consumes at the start of the next check. Link: https://lore.kernel.org/20261004163610.413506-1-atomlin@atomlin.com Link: https://lore.kernel.org/20261004163610.413506-2-atomlin@atomlin.com Signed-off-by: Aaron Tomlin Signed-off-by: Andrew Morton Suggested-by: Petr Mladek Suggested-by: Lance Yang Tested-by: Lance Yang Reviewed-by: Lance Yang Reviewed-by: Bradley Morgan Cc: Masami Hiramatsu Cc: David Laight Cc: Daniel Vacek Cc: Sean Ashe Cc: Cc: Cc: Cc: --- Documentation/admin-guide/sysctl/kernel.rst | 5 +-- kernel/hung_task.c | 49 ++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 10 deletions(-) (limited to 'kernel') diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index ffea61d448eb..c03369e234a9 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -459,8 +459,9 @@ hung_task_warnings ================== The maximum number of warnings to report. During a check interval -if a hung task is detected, this value is decreased by 1. -When this value reaches 0, no more warnings will be reported. +if a hung task is detected, the internal warning budget is decreased by 1. +When this budget reaches 0, no more detailed warnings will be reported. The +warning budget is reset to the configured limit when no hung task is found. This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. -1: report an infinite number of warnings. diff --git a/kernel/hung_task.c b/kernel/hung_task.c index 6fcc94ce4ca9..a5043188456d 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -57,8 +57,20 @@ unsigned long __read_mostly sysctl_hung_task_timeout_secs = CONFIG_DEFAULT_HUNG_ */ static unsigned long __read_mostly sysctl_hung_task_check_interval_secs; +/* + * Limit the number of printed hung tasks to prevent printing + * the same or similar backtraces repeatedly. + */ static int __read_mostly sysctl_hung_task_warnings = 10; +/* + * The number of hung tasks which still can be reported. + * The budget gets restored to the original limit when + * the previous stall is resolved. + */ +static int hung_task_warnings_budget = 10; +static atomic_t reset_hung_task_warnings = ATOMIC_INIT(0); + static int __read_mostly did_panic; static bool hung_task_call_panic; @@ -245,11 +257,11 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout, /* * The given task did not get scheduled for more than * CONFIG_DEFAULT_HUNG_TASK_TIMEOUT. Therefore, complain - * accordingly + * accordingly with full details if the budget is not exhausted. */ - if (sysctl_hung_task_warnings || hung_task_call_panic) { - if (sysctl_hung_task_warnings > 0) - sysctl_hung_task_warnings--; + if (hung_task_warnings_budget || hung_task_call_panic) { + if (hung_task_warnings_budget > 0) + hung_task_warnings_budget--; pr_err("INFO: task %s:%d blocked%s for more than %ld seconds.\n", t->comm, t->pid, t->in_iowait ? " in I/O wait" : "", (jiffies - t->last_switch_time) / HZ); @@ -264,7 +276,7 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout, sched_show_task(t); debug_show_blocker(t, timeout); - if (!sysctl_hung_task_warnings) + if (!hung_task_warnings_budget) pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n"); } @@ -304,7 +316,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) unsigned long last_break = jiffies; struct task_struct *g, *t; unsigned long this_round_count; - int need_warning = sysctl_hung_task_warnings; + int need_warning; unsigned long si_mask = hung_task_si_mask; /* @@ -314,6 +326,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) if (test_taint(TAINT_DIE) || did_panic) return; + if (atomic_xchg_acquire(&reset_hung_task_warnings, 0)) + hung_task_warnings_budget = + READ_ONCE(sysctl_hung_task_warnings); + need_warning = hung_task_warnings_budget; + this_round_count = 0; rcu_read_lock(); for_each_process_thread(g, t) { @@ -340,8 +357,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) unlock: rcu_read_unlock(); - if (!this_round_count) + if (!this_round_count) { + hung_task_warnings_budget = + READ_ONCE(sysctl_hung_task_warnings); return; + } if (need_warning || hung_task_call_panic) { si_mask |= SYS_INFO_LOCKS; @@ -425,6 +445,19 @@ static int proc_dohung_task_timeout_secs(const struct ctl_table *table, int writ return ret; } +static int proc_dohung_task_warnings(const struct ctl_table *table, int write, + void *buffer, + size_t *lenp, loff_t *ppos) +{ + int ret; + + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + if (!ret && write) + atomic_set_release(&reset_hung_task_warnings, 1); + + return ret; +} + /* * This is needed for proc_doulongvec_minmax of sysctl_hung_task_timeout_secs * and hung_task_check_interval_secs @@ -480,7 +513,7 @@ static const struct ctl_table hung_task_sysctls[] = { .data = &sysctl_hung_task_warnings, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_dohung_task_warnings, .extra1 = SYSCTL_NEG_ONE, }, { -- cgit v1.2.3 From 36dd57d0ae9605cf7c91e6de04296400f8e2ef27 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Sun, 4 Oct 2026 12:36:10 -0400 Subject: hung_task: log summary line when warning budget is exhausted Once the warning budget is exhausted, hung_task_info() normally stops printing per-task details. When panic is triggered, full details are still printed so diagnostics remain available before panic. To retain visibility without restoring per-task output after budget exhaustion, emit a single aggregate summary line at the end of each watchdog scan that detects hung tasks with an exhausted budget. This keeps non-panic per-task reports budgeted during system-wide hangs. Link: https://lore.kernel.org/20261004163610.413506-3-atomlin@atomlin.com Signed-off-by: Aaron Tomlin Signed-off-by: Andrew Morton Suggested-by: Petr Mladek Suggested-by: Lance Yang Reviewed-by: Petr Mladek Reviewed-by: Lance Yang Reviewed-by: Bradley Morgan Cc: Masami Hiramatsu Cc: David Laight Cc: Daniel Vacek Cc: Sean Ashe Cc: Cc: Cc: Cc: --- kernel/hung_task.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/hung_task.c b/kernel/hung_task.c index a5043188456d..b8554aedc589 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -276,8 +276,8 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout, sched_show_task(t); debug_show_blocker(t, timeout); - if (!hung_task_warnings_budget) - pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n"); + if (!hung_task_warnings_budget && !hung_task_call_panic) + pr_info("hung_task: further per-task details suppressed until warning budget is reset (see sysctl kernel.hung_task_warnings)\n"); } touch_nmi_watchdog(); @@ -363,6 +363,10 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) return; } + if (!hung_task_warnings_budget && !hung_task_call_panic) + pr_info("hung_task: %lu hung tasks detected, warning budget exhausted\n", + this_round_count); + if (need_warning || hung_task_call_panic) { si_mask |= SYS_INFO_LOCKS; -- cgit v1.2.3 From aabe047f8b8055b66cf55a88c688c6660a3f6aca Mon Sep 17 00:00:00 2001 From: Hrushiraj Gandhi Date: Mon, 31 Aug 2026 20:19:53 +0530 Subject: gcov: use strscpy() instead of strcpy() in init_node() node->name is a flexible array member sized to exactly strlen(name) + 1 bytes at allocation time in new_node(), so this copy can never actually overflow. Still, prefer the bounded strscpy() over strcpy() on general principle; pass the same strlen(name) + 1 bound the allocation used, since sizeof() cannot be applied to a flexible array member. No functional change. Link: https://lore.kernel.org/20260831144953.324441-1-hrushirajg23@gmail.com Signed-off-by: Hrushiraj Gandhi Signed-off-by: Andrew Morton Reviewed-by: Bradley Morgan Cc: Peter Oberparleiter --- kernel/gcov/fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/gcov/fs.c b/kernel/gcov/fs.c index 1d19b1be207a..764918570de1 100644 --- a/kernel/gcov/fs.c +++ b/kernel/gcov/fs.c @@ -529,7 +529,7 @@ static void init_node(struct gcov_node *node, struct gcov_info *info, } node->parent = parent; if (name) - strcpy(node->name, name); + strscpy(node->name, name, strlen(name) + 1); } /* -- cgit v1.2.3 From 97c358198e73c4d3feff561ebf813e84cfb1ef5a Mon Sep 17 00:00:00 2001 From: Mete Durlu Date: Mon, 31 Aug 2026 11:57:15 +0200 Subject: panic: introduce arch_do_panic Patch series "Introduce arch_do_panic", v6. Replace architecture-specific ifdef sections in vpanic() with a clean arch_do_panic() hook. Currently s390 and sparc embed their panic handlers directly in vpanic() using preprocessor conditionals, making the common code path harder to maintain. Introduce arch_do_panic() as an architecture extension point called at the end of vpanic(). Architectures can use this hook to implement their specific panic handling without polluting the generic panic code. Remove s390s ifdef block in vpanic() and move the corresponding code block to s390s own arch_do_panic() implementation in architecture specific code. Move sparc panic handling from ifdef blocks to arch_do_panic(). Remove the preprocessor conditionals from vpanic() and place the Stop-A enablement code in architecture-specific files where it belongs. Stop-A enablement markers are now printed after "end Kernel panic" line. To me, there are no better alternatives other than setup.c to put sparc's arch_do_panic() implementation. The other files under arch/sparc/kernel are either divided to *_32.c and *_64.c variants, which mean code duplication, or unrelated. The cleanup reduces vpanic() complexity and establishes a pattern for other architectures needing custom panic behavior. No functional changes, only minor print order changes. This patch (of 3): Introduce a hook for architectures to put their specific panic handlers. s390 and sparc already have ifdef preprocessor checks to execute architecture specific code. Pave the way for vpanic() cleanup. Link: https://lore.kernel.org/20260831-arch_do_panic-v6-1-a1e170a9e7fd@linux.ibm.com Link: https://lore.kernel.org/all/20260730-arch_do_panic-v3-0-d5401e683cdb@linux.ibm.com/ [1] Signed-off-by: Mete Durlu Signed-off-by: Andrew Morton Reviewed-by: Bradley Morgan Suggested-by: Sven Schnelle Reviewed-by: Andrew Morton Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Heiko Carstens Cc: Petr Mladek Cc: Vasily Gorbik --- include/linux/panic.h | 2 ++ kernel/panic.c | 3 +++ 2 files changed, 5 insertions(+) (limited to 'kernel') diff --git a/include/linux/panic.h b/include/linux/panic.h index f1dd417e54b2..98dd7dfd27de 100644 --- a/include/linux/panic.h +++ b/include/linux/panic.h @@ -110,4 +110,6 @@ extern void add_taint(unsigned flag, enum lockdep_ok); extern int test_taint(unsigned flag); extern unsigned long get_taint(void); +void arch_do_panic(void); + #endif /* _LINUX_PANIC_H */ diff --git a/kernel/panic.c b/kernel/panic.c index 213725b612aa..726a97842232 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -567,6 +567,8 @@ static void panic_other_cpus_shutdown(bool crash_kexec) crash_smp_send_stop(); } +void __weak arch_do_panic(void) {} + /** * vpanic - halt the system * @fmt: The text string to print @@ -756,6 +758,7 @@ void vpanic(const char *fmt, va_list args) #endif pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf); + arch_do_panic(); /* Do not scroll important messages printed above */ suppress_printk = 1; -- cgit v1.2.3 From ed26d6fcf5631330a06ad120d6e8a262efa65dde Mon Sep 17 00:00:00 2001 From: Mete Durlu Date: Mon, 31 Aug 2026 11:57:16 +0200 Subject: s390: implement arch_do_panic Implement s390 specific arch_do_panic() instead of using s390 specific ifdef sections in vpanic() code. disabled_wait() is now called after "end Kernel panic" marker. No functional changes. Link: https://lore.kernel.org/20260831-arch_do_panic-v6-2-a1e170a9e7fd@linux.ibm.com Signed-off-by: Mete Durlu Signed-off-by: Andrew Morton Acked-by: Heiko Carstens Reviewed-by: Bradley Morgan Reviewed-by: Andrew Morton Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Petr Mladek Cc: Sven Schnelle Cc: Vasily Gorbik --- arch/s390/kernel/traps.c | 7 +++++++ kernel/panic.c | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index b6ba4465f59d..115cb3373247 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include "entry.h" struct pgm_stat { @@ -283,6 +285,11 @@ static void monitor_event_exception(struct pt_regs *regs) } } +void arch_do_panic(void) +{ + disabled_wait(); +} + void kernel_stack_invalid(struct pt_regs *regs) { /* diff --git a/kernel/panic.c b/kernel/panic.c index 726a97842232..ee6e3f9e3900 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -752,9 +752,6 @@ void vpanic(const char *fmt, va_list args) pr_emerg("Press Stop-A (L1-A) from sun keyboard or send break\n" "twice on console to return to the boot prom\n"); } -#endif -#if defined(CONFIG_S390) - disabled_wait(); #endif pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf); -- cgit v1.2.3 From 1d4408578567d1b3c7086be37986164982791f19 Mon Sep 17 00:00:00 2001 From: Mete Durlu Date: Mon, 31 Aug 2026 11:57:17 +0200 Subject: sparc: implement arch_do_panic Implement sparc specific arch_do_panic() instead of using sparc specific ifdef sections in vpanic() code. Reorder arch specific panic handling, sparc's Stop-A messages are now printed after "end Kernel panic" marker. Link: https://lore.kernel.org/20260831-arch_do_panic-v6-3-a1e170a9e7fd@linux.ibm.com Signed-off-by: Mete Durlu Signed-off-by: Andrew Morton Reviewed-by: Bradley Morgan Reviewed-by: Andrew Morton Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Heiko Carstens Cc: Petr Mladek Cc: Sven Schnelle Cc: Vasily Gorbik --- arch/sparc/kernel/setup.c | 9 +++++++++ kernel/panic.c | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'kernel') diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 4975867d9001..5f43cef80638 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -2,6 +2,8 @@ #include #include +#include +#include static const struct ctl_table sparc_sysctl_table[] = { { @@ -36,6 +38,13 @@ static const struct ctl_table sparc_sysctl_table[] = { #endif }; +void arch_do_panic(void) +{ + /* Make sure the user can actually press Stop-A (L1-A) */ + stop_a_enabled = 1; + pr_emerg("Press Stop-A (L1-A) from sun keyboard or send break\n" + "twice on console to return to the boot prom\n"); +} static int __init init_sparc_sysctls(void) { diff --git a/kernel/panic.c b/kernel/panic.c index ee6e3f9e3900..7dda841c16f9 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -744,15 +744,6 @@ void vpanic(const char *fmt, va_list args) reboot_mode = panic_reboot_mode; emergency_restart(); } -#ifdef __sparc__ - { - extern int stop_a_enabled; - /* Make sure the user can actually press Stop-A (L1-A) */ - stop_a_enabled = 1; - pr_emerg("Press Stop-A (L1-A) from sun keyboard or send break\n" - "twice on console to return to the boot prom\n"); - } -#endif pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf); arch_do_panic(); -- cgit v1.2.3 From 9decb604dc064099a6249150f0c4909525ed657c Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Fri, 10 Jul 2026 14:39:57 +0200 Subject: fork: honor task_struct's declared alignment Since commit cb7ca40a3882 ("x86/fpu: Make task_struct::thread constant size"), struct task_struct is declared __attribute__((aligned(64))) on all architectures. But fork_init() still sets the task_struct slab cache's alignment to align = max(L1_CACHE_BYTES, ARCH_MIN_TASKALIGN) which is smaller than 64 on architectures whose cache lines are below 64 bytes: e.g. 32 on ARMv5. In practice plain SLUB happens to hand out 64-byte-aligned objects anyway. With CONFIG_SLUB_DEBUG_ON the red-zone padding shifts objects to the requested alignment. With CONFIG_UBSAN_ALIGNMENT=y a boot on QEMU versatilepb (ARM926EJ-S, v7.2-rc2, gcc 13.3) floods the console with reports like: UBSAN: misaligned-access in include/linux/sched.h:2087:9 member access within misaligned address c295d7e0 for type 'struct task_struct' which requires 64 byte alignment CPU: 0 UID: 0 PID: 15 Comm: pr/ttyAMA-1 Not tainted 7.2.0-rc2 #1 VOLUNTARY Set the slab alignment to at least the type's declared alignment. Link: https://lore.kernel.org/20260710123957.31774-1-kmehltretter@gmail.com Fixes: cb7ca40a3882 ("x86/fpu: Make task_struct::thread constant size") Signed-off-by: Karl Mehltretter Signed-off-by: Andrew Morton Reviewed-by: Bradley Morgan Assisted-by: Claude:claude-fable-5 Cc: Ingo Molnar Cc: Kees Cook Cc: Peter Zijlstra Cc: Vlastimil Babka Cc: --- kernel/fork.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/fork.c b/kernel/fork.c index 10f2d05d816a..4558150fd133 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -858,7 +858,8 @@ void __init fork_init(void) #ifndef ARCH_MIN_TASKALIGN #define ARCH_MIN_TASKALIGN 0 #endif - int align = max_t(int, L1_CACHE_BYTES, ARCH_MIN_TASKALIGN); + int align = max3(L1_CACHE_BYTES, ARCH_MIN_TASKALIGN, + __alignof__(struct task_struct)); unsigned long useroffset, usersize; /* create a slab on which task_structs can be allocated */ -- cgit v1.2.3 From cc2356e17ff2320ed9cd9970317fb2265a633d9e Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Sat, 1 Aug 2026 22:58:44 +0000 Subject: taskstats: fold the two pid/tgid handlers into one cmd_attr_pid() and cmd_attr_tgid() are copy paste. fold them into one handler that takes the attr type and fill function as parameters, same pattern as the cpumask fold in 59c0bc949c5e. No functional change. Link: https://lore.kernel.org/20260801225845.23855-1-include@grrlz.net Signed-off-by: Bradley Morgan Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Cc: Balbir Singh Cc: Balbir Singh --- kernel/taskstats.c | 47 ++++++++++++----------------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) (limited to 'kernel') diff --git a/kernel/taskstats.c b/kernel/taskstats.c index f31df72f0e9d..5edd759a163e 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -473,7 +473,8 @@ static size_t taskstats_packet_size(void) return size; } -static int cmd_attr_pid(struct genl_info *info) +static int cmd_attr_pid_tgid(struct genl_info *info, int attr, + int (*fill)(pid_t, struct taskstats *)) { struct taskstats *stats; struct sk_buff *rep_skb; @@ -488,41 +489,15 @@ static int cmd_attr_pid(struct genl_info *info) return rc; rc = -EINVAL; - pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); - stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid); + pid = nla_get_u32(info->attrs[attr]); + stats = mk_reply(rep_skb, + attr == TASKSTATS_CMD_ATTR_PID + ? TASKSTATS_TYPE_PID : TASKSTATS_TYPE_TGID, + pid); if (!stats) goto err; - rc = fill_stats_for_pid(pid, stats); - if (rc < 0) - goto err; - return send_reply(rep_skb, info); -err: - nlmsg_free(rep_skb); - return rc; -} - -static int cmd_attr_tgid(struct genl_info *info) -{ - struct taskstats *stats; - struct sk_buff *rep_skb; - size_t size; - u32 tgid; - int rc; - - size = taskstats_packet_size(); - - rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); - if (rc < 0) - return rc; - - rc = -EINVAL; - tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); - stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid); - if (!stats) - goto err; - - rc = fill_stats_for_tgid(tgid, stats); + rc = fill(pid, stats); if (rc < 0) goto err; return send_reply(rep_skb, info); @@ -542,9 +517,11 @@ static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, DEREGISTER); else if (info->attrs[TASKSTATS_CMD_ATTR_PID]) - return cmd_attr_pid(info); + return cmd_attr_pid_tgid(info, TASKSTATS_CMD_ATTR_PID, + fill_stats_for_pid); else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) - return cmd_attr_tgid(info); + return cmd_attr_pid_tgid(info, TASKSTATS_CMD_ATTR_TGID, + fill_stats_for_tgid); else return -EINVAL; } -- cgit v1.2.3 From 033abd1e9bf99f3004ed1932d4880c3c941c3834 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Wed, 2 Sep 2026 19:48:51 +0800 Subject: panic: remove the unneeded panic_print_get() panic_print_get() was introduced in commit 2683df6539cb ("panic: add note that 'panic_print' parameter is deprecated") to print out warning message of the deprecation of 'panic_print' on read access. Since commit 90f3c123247e ("panic: only warn about deprecated panic_print on write access"), panic_print_get() wrapper is not needed anymore for read access, so remove it and use param_get_ulong() instead. Link: https://lore.kernel.org/20260902114851.77062-1-feng.tang@linux.alibaba.com Signed-off-by: Feng Tang Signed-off-by: Andrew Morton Reviewed-by: Bradley Morgan Reviewed-by: Andrew Morton Reviewed-by: Petr Mladek --- kernel/panic.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'kernel') diff --git a/kernel/panic.c b/kernel/panic.c index 7dda841c16f9..50715f14cf04 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -1216,14 +1216,9 @@ static int panic_print_set(const char *val, const struct kernel_param *kp) return param_set_ulong(val, kp); } -static int panic_print_get(char *val, const struct kernel_param *kp) -{ - return param_get_ulong(val, kp); -} - static const struct kernel_param_ops panic_print_ops = { .set = panic_print_set, - .get = panic_print_get, + .get = param_get_ulong, }; __core_param_cb(panic_print, &panic_print_ops, &panic_print, 0644); -- cgit v1.2.3 From 025ba24e7509082b62eb9f655046174f6bb50f51 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Tue, 15 Sep 2026 18:06:23 +0000 Subject: taskstats: drop the unused CPU_DONT_CARE enum member CPU_DONT_CARE was added by the original listener cpumask code in 2006 (f9fd8914c1ac) and has never had a single user, not even in the commit that introduced it. It survived every refactor of the listener path since, including the recent handler folds. Kill it, twenty years is enough. Link: https://lore.kernel.org/20260915180623.22058-1-brads@mainlining.org Signed-off-by: Bradley Morgan Signed-off-by: Andrew Morton Reviewed-by: Balbir Singh --- kernel/taskstats.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 5edd759a163e..bf517788b840 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -59,8 +59,7 @@ static DEFINE_PER_CPU(struct listener_list, listener_array); enum actions { REGISTER, - DEREGISTER, - CPU_DONT_CARE + DEREGISTER }; static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp, -- cgit v1.2.3 From 31b38ab685cc318a272ae0ccd218e7cc8f820a6a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 15 Sep 2026 10:53:53 +0200 Subject: resource: replace open coded resource_overlaps() In iomem_map_sanity_check() a piece of code resembles the content of the resource_overlaps(). Replace open coded piece with the call to the existing helper. Link: https://lore.kernel.org/20260915085353.3518153-1-andriy.shevchenko@linux.intel.com Signed-off-by: Andy Shevchenko Signed-off-by: Andrew Morton Reviewed-by: Bradley Morgan Cc: Bjorn Helgaas --- kernel/resource.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'kernel') diff --git a/kernel/resource.c b/kernel/resource.c index 54d7199695fb..cfc1a00e86aa 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -1833,7 +1833,7 @@ __setup("reserve=", reserve_setup); */ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) { - resource_size_t end = addr + size - 1; + struct resource mem = DEFINE_RES_MEM(addr, size); struct resource *p; int err = 0; @@ -1843,12 +1843,10 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) * We can probably skip the resources without * IORESOURCE_IO attribute? */ - if (p->start > end) + if (!resource_overlaps(p, &mem)) continue; - if (p->end < addr) - continue; - if (PFN_DOWN(p->start) <= PFN_DOWN(addr) && - PFN_DOWN(p->end) >= PFN_DOWN(end)) + if (PFN_DOWN(p->start) <= PFN_DOWN(mem.start) && + PFN_DOWN(p->end) >= PFN_DOWN(mem.end)) continue; /* * if a resource is "BUSY", it's not a hardware resource @@ -1859,8 +1857,8 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) if (p->flags & IORESOURCE_BUSY) continue; - pr_debug("resource sanity check: requesting [mem %pa-%pa], which spans more than %s %pR\n", - &addr, &end, p->name, p); + pr_debug("resource sanity check: requesting %pR, which spans more than %s %pR\n", + &mem, p->name, p); err = -1; break; } -- cgit v1.2.3 From c05e2c32cd322ee2899501feda5b746c14207c0e Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Wed, 16 Sep 2026 18:29:52 +0000 Subject: panic: fix redirect CPU race in panic_try_force_cpu() Patch series "panic: fix panic_force_cpu= redirect races and NMI bypass", v7. The panic_force_cpu= parameter redirects a panic to a specific CPU so the crash kernel runs there. The redirect code in panic_try_force_cpu() had two races and an NMI bypass, all found by Sashiko. This series closes them and kills one more hack that the rework turned up. Panic from NMI still goes through the redirect path first, so the crash kernel ends up on the CPU the admin asked for. The redirect buffer is a static 1KB now, no initcall, no fallback message. This patch (of 6): The cmpxchg() in panic_try_force_cpu() makes sure that only one CPU tries to redirect panic() to the requested CPU. It is similar to the cmpxchg() in panic_try_start() which makes sure that only one CPU does the panic(). In both situations, only the winner of cmpxchg() should proceed further. Other CPUs should go offline. There is a bug because the cmpxchg loser returns false and falls through into vpanic(). Two non-target CPUs A and B panic, the requested CPU is C: cpu A cpu B ---------- ---------- panic() panic() vpanic() vpanic() panic_try_force_cpu() panic_try_force_cpu() cmpxchg wins cmpxchg fails redirect = A old_cpu = A IPI -> C return false <- BUG return true panic_try_start() wins panic_smp_self_stop() __crash_kexec() on B (A stops) (target C bypassed) The loser must stop, not fall through. It cannot just return true, though. A CPU that already won the redirect cmpxchg can reenter panic_try_force_cpu() on the same CPU, for example a nested NMI during the message formatting, before the IPI is sent: cpu A (1st) cpu A (nested) ---------- ---------- panic() vpanic() panic_try_force_cpu() cmpxchg wins (redirect = A) vsnprintf(msg) ... <-- NMI, nested panic --> panic() vpanic() panic_try_force_cpu() cmpxchg fails old_cpu == A (this CPU) return true <- would halt panic_smp_self_stop() (IPI never sent, panic abandoned) Check old_cpu against this_cpu so a second call from the same CPU returns false and falls through to panic_try_start() instead. Also fix the panic_in_progress() check. We must not redirect when panic_cpu is already assigned. Return true to stop when the panic is on another CPU, false to proceed when it is this one. Update the panic_try_force_cpu() doc comment for the new return value semantics. Link: https://lore.kernel.org/20260916182957.7788-1-brads@mainlining.org Link: https://lore.kernel.org/20260916182957.7788-2-brads@mainlining.org Fixes: 2e171ab29f91 ("panic: add panic_force_cpu= parameter to redirect panic to a specific CPU") Signed-off-by: Bradley Morgan Signed-off-by: Andrew Morton Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260705164123.18746-1-include@grrlz.net Closes: https://sashiko.dev/#/patchset/20260707172252.4842-1-include@grrlz.net Reviewed-by: Petr Mladek Cc: Guenetr Roeck Cc: Wang Jinchao Cc: Wim Van Sebroeck Cc: --- kernel/panic.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'kernel') diff --git a/kernel/panic.c b/kernel/panic.c index 50715f14cf04..08072bfae422 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -371,8 +371,9 @@ int __weak panic_smp_redirect_cpu(int target_cpu, void *msg) * for the crash kernel to function correctly. This function redirects * panic handling to the CPU specified via the panic_force_cpu= boot parameter. * - * Returns false if panic should proceed on current CPU. - * Returns true if panic was redirected. + * Returns true when this CPU must stop: the panic was redirected or is + * already running on another CPU. + * Returns false when panic() should proceed on this CPU. */ __printf(1, 0) static bool panic_try_force_cpu(const char *fmt, va_list args) @@ -396,16 +397,20 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) return false; } - /* Another panic already in progress */ + /* + * Don't redirect when a panic is already in progress. Stop this + * CPU when it's another one, proceed when it's this one. + */ if (panic_in_progress()) - return false; + return panic_on_other_cpu(); /* - * Only one CPU can do the redirect. Use atomic cmpxchg to ensure - * we don't race with another CPU also trying to redirect. + * Only one CPU can do the redirection. Others should go offline. + * Continue with panic() when we already tried the redirection + * from this CPU before, for example via nmi_panic(). */ if (!atomic_try_cmpxchg(&panic_redirect_cpu, &old_cpu, this_cpu)) - return false; + return old_cpu != this_cpu; /* * Use dynamically allocated buffer if available, otherwise -- cgit v1.2.3 From 50ce3620bee380cd2e136803ff66cc8c997d6d7c Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Wed, 16 Sep 2026 18:29:53 +0000 Subject: panic: flatten nmi_panic control flow panic() is __noreturn, so the else after panic_try_start() is dead. Drop it so the force_cpu path can be added cleanly on top. Link: https://lore.kernel.org/20260916182957.7788-3-brads@mainlining.org Signed-off-by: Bradley Morgan Signed-off-by: Andrew Morton Reviewed-by: Petr Mladek Cc: Guenetr Roeck Cc: Sashiko Cc: Wang Jinchao Cc: Wim Van Sebroeck Cc: --- kernel/panic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/panic.c b/kernel/panic.c index 08072bfae422..5646d4fb82b7 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -517,7 +517,8 @@ void nmi_panic(struct pt_regs *regs, const char *msg) { if (panic_try_start()) panic("%s", msg); - else if (panic_on_other_cpu()) + + if (panic_on_other_cpu()) nmi_panic_self_stop(regs); } EXPORT_SYMBOL(nmi_panic); -- cgit v1.2.3 From e97834b034a3d4de1f3c305a6dfcef7bb14989ad Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Wed, 16 Sep 2026 18:29:54 +0000 Subject: panic: fix va_list reuse in panic_try_force_cpu() vsnprintf() consumes the caller's va_list. When the redirect fails, vpanic() reuses it for the panic message, which is undefined behavior. Use va_copy(). Link: https://lore.kernel.org/20260916182957.7788-4-brads@mainlining.org Fixes: 2e171ab29f91 ("panic: add panic_force_cpu= parameter to redirect panic to a specific CPU") Signed-off-by: Bradley Morgan Signed-off-by: Andrew Morton Reviewed-by: Petr Mladek Cc: Guenetr Roeck Cc: Sashiko Cc: Wang Jinchao Cc: Wim Van Sebroeck Cc: --- kernel/panic.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/panic.c b/kernel/panic.c index 5646d4fb82b7..7388eb81a1c4 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -417,7 +417,12 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) * fall back to static message for early boot panics or allocation failure. */ if (panic_force_buf) { - vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, args); + va_list ap; + + /* Do not consume args, the caller reuses it if we fail */ + va_copy(ap, args); + vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, ap); + va_end(ap); msg = panic_force_buf; } else { msg = "Redirected panic (buffer unavailable)"; -- cgit v1.2.3 From 9cbe4c3344734464263ac4561fe4f639d332fc6e Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Wed, 16 Sep 2026 18:29:55 +0000 Subject: panic: restore variable arguments to nmi_panic() nmi_panic() used to accept variable arguments until commit ebc41f20d77f ("panic: change nmi_panic from macro to function") flattened it to a final message string. vpanic() did not exist back then, so the function had to format through panic("%s", msg). Bring the variable arguments back and format with vpanic() directly. The next patch makes nmi_panic() try the panic_force_cpu= redirect before claiming panic_cpu. panic_try_force_cpu() needs to print the message but it is used also by panic() which accepts variable argument list. The string could be formatted only when a CPU gets assigned to process the redirection. Otherwise, there might be a race when writing to the `panic_force_buf`. No current caller passes a string with format specifiers. The closest one is hpwdt_pretimeout(), which builds panic_msg with hex_byte_pack() and has only two variants, both plain strings. But the new __printf() annotation on nmi_panic() would warn with -Wformat-security there because the buffer is passed directly as the format argument, so switch it to nmi_panic(regs, "%s", panic_msg). [pmladek@suse.com: changelog fix] Link: https://lore.kernel.org/arJxFybmtD7OBIcL@pathway.suse.cz Link: https://lore.kernel.org/20260916182957.7788-5-brads@mainlining.org Signed-off-by: Bradley Morgan Signed-off-by: Andrew Morton Suggested-by: Petr Mladek Reviewed-by: Petr Mladek Cc: Guenetr Roeck Cc: Sashiko Cc: Wang Jinchao Cc: Wim Van Sebroeck Cc: --- drivers/watchdog/hpwdt.c | 2 +- include/linux/panic.h | 3 ++- kernel/panic.c | 10 ++++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 8af1fad2de0b..78227d200afe 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -199,7 +199,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) } hex_byte_pack(panic_msg, nmistat); - nmi_panic(regs, panic_msg); + nmi_panic(regs, "%s", panic_msg); return NMI_HANDLED; } diff --git a/include/linux/panic.h b/include/linux/panic.h index 98dd7dfd27de..17e61b61c45f 100644 --- a/include/linux/panic.h +++ b/include/linux/panic.h @@ -13,7 +13,8 @@ __printf(1, 2) void panic(const char *fmt, ...) __noreturn __cold; __printf(1, 0) void vpanic(const char *fmt, va_list args) __noreturn __cold; -void nmi_panic(struct pt_regs *regs, const char *msg); +__printf(2, 3) +void nmi_panic(struct pt_regs *regs, const char *fmt, ...); void check_panic_on_warn(const char *origin); extern void oops_enter(void); extern void oops_exit(void); diff --git a/kernel/panic.c b/kernel/panic.c index 7388eb81a1c4..e240ca06faab 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -518,13 +518,19 @@ EXPORT_SYMBOL(panic_on_other_cpu); * nmi_panic_self_stop() which can provide architecture dependent code such * as saving register state for crash dump. */ -void nmi_panic(struct pt_regs *regs, const char *msg) +void nmi_panic(struct pt_regs *regs, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + if (panic_try_start()) - panic("%s", msg); + vpanic(fmt, args); if (panic_on_other_cpu()) nmi_panic_self_stop(regs); + + va_end(args); } EXPORT_SYMBOL(nmi_panic); -- cgit v1.2.3 From 9e3a9fd1f684bd59b70ced64b6709c11e13bd4c7 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Wed, 16 Sep 2026 18:29:56 +0000 Subject: panic: allow force_cpu redirect from an NMI nmi_panic() claims panic_cpu via panic_try_start() before calling panic(). When the panic later reaches panic_try_force_cpu(), the panic_in_progress() check sees panic_cpu set and refuses to redirect. The crash kernel runs on the CPU that took the NMI instead of the CPU requested with panic_force_cpu=: nmi_panic() panic_try_start() wins, panic_cpu = X panic("%s", msg) vpanic() panic_try_force_cpu() panic_in_progress() true, panic_cpu is X return false redirect bypassed panic_try_start() already won __crash_kexec() on X, not the requested CPU Try the redirect before claiming panic_cpu instead, as suggested by Petr Mladek. nmi_panic() now calls panic_try_force_cpu() first and claims panic_cpu only when no redirect happened. The requested CPU claims panic_cpu itself when it runs panic(), so panic_cpu does not need to be handed off. panic_try_force_cpu() copies the arguments before formatting (patch 3), so nmi_panic() can pass them to vpanic() again when no redirect happens. The redirect IPI is sent with smp_call_function_single_async(), which is not guaranteed to work from NMI context. Treat it as best effort. It is worth the risk because the redirection is only used when the crash kernel would not work on the panicking CPU anyway. Keep returning when the panic is already running on this CPU. A nested NMI, for example with unknown_nmi_panic while this CPU is inside panic(), must return and let the interrupted panic() continue instead of parking the CPU in nmi_panic_self_stop(). Mark the redirecting CPU offline before stopping it, like vpanic() does, so that panic_other_cpus_shutdown() on the target CPU does not wait for it. Link: https://lore.kernel.org/20260916182957.7788-6-brads@mainlining.org Fixes: 2e171ab29f91 ("panic: add panic_force_cpu= parameter to redirect panic to a specific CPU") Signed-off-by: Bradley Morgan Signed-off-by: Andrew Morton Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260708164312.19044-1-include@grrlz.net Reviewed-by: Petr Mladek Cc: Guenetr Roeck Cc: Wang Jinchao Cc: Wim Van Sebroeck Cc: --- kernel/panic.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/kernel/panic.c b/kernel/panic.c index e240ca06faab..29c981926f5f 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -513,10 +513,11 @@ bool panic_on_other_cpu(void) EXPORT_SYMBOL(panic_on_other_cpu); /* - * A variant of panic() called from NMI context. We return if we've already - * panicked on this CPU. If another CPU already panicked, loop in - * nmi_panic_self_stop() which can provide architecture dependent code such - * as saving register state for crash dump. + * A variant of panic() called from NMI context. The panic is first + * redirected to the CPU requested via panic_force_cpu=, when configured. + * We return if we've already panicked on this CPU. If another CPU already + * panicked, loop in nmi_panic_self_stop() which can provide architecture + * dependent code for saving register state for crash dump. */ void nmi_panic(struct pt_regs *regs, const char *fmt, ...) { @@ -524,6 +525,16 @@ void nmi_panic(struct pt_regs *regs, const char *fmt, ...) va_start(args, fmt); + /* Try to redirect to the requested CPU before claiming panic_cpu. */ + if (panic_try_force_cpu(fmt, args)) { + /* + * Mark ourselves offline so panic_other_cpus_shutdown() won't + * wait for us on architectures that check num_online_cpus(). + */ + set_cpu_online(raw_smp_processor_id(), false); + nmi_panic_self_stop(regs); + } + if (panic_try_start()) vpanic(fmt, args); -- cgit v1.2.3 From 56de113da14706d6a1b28648e9ff5c261f536201 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Wed, 16 Sep 2026 18:29:57 +0000 Subject: panic: kill the "buffer unavailable" redirect fallback The redirect buffer is a disgusting terrible hack. panic_force_buf is kmalloc'ed in a late_initcall, and until then the redirect delivers this as the panic message: Redirected panic (buffer unavailable) The whole point of the redirect is to hand the panic message to the target CPU, so the crash kernel boots knowing it panicked and not why. And that window is the entire boot, from the early_param to the late_initcall, which is exactly when you most want the message. Make it a static 1KB buffer and kill the initcall. The cost is 1KB of .bss in SMP crash dump builds, and it is only ever touched when panic_force_cpu= is set anyway. The local msg variable is gone too, the buffer goes directly to panic_smp_redirect_cpu(). Link: https://lore.kernel.org/20260916182957.7788-7-brads@mainlining.org Signed-off-by: Bradley Morgan Signed-off-by: Andrew Morton Suggested-by: Petr Mladek Reviewed-by: Petr Mladek Cc: Guenetr Roeck Cc: Sashiko Cc: Wang Jinchao Cc: Wim Van Sebroeck Cc: --- kernel/panic.c | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) (limited to 'kernel') diff --git a/kernel/panic.c b/kernel/panic.c index 29c981926f5f..170744163fc2 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -307,7 +307,7 @@ atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); atomic_t panic_redirect_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); #if defined(CONFIG_SMP) && defined(CONFIG_CRASH_DUMP) -static char *panic_force_buf; +static char panic_force_buf[PANIC_MSG_BUFSZ]; static int __init panic_force_cpu_setup(char *str) { @@ -326,17 +326,6 @@ static int __init panic_force_cpu_setup(char *str) } early_param("panic_force_cpu", panic_force_cpu_setup); -static int __init panic_force_cpu_late_init(void) -{ - if (panic_force_cpu < 0) - return 0; - - panic_force_buf = kmalloc(PANIC_MSG_BUFSZ, GFP_KERNEL); - - return 0; -} -late_initcall(panic_force_cpu_late_init); - static void do_panic_on_target_cpu(void *info) { panic("%s", (char *)info); @@ -380,7 +369,7 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) { int this_cpu = raw_smp_processor_id(); int old_cpu = PANIC_CPU_INVALID; - const char *msg; + va_list ap; /* Feature not enabled via boot parameter */ if (panic_force_cpu < 0) @@ -413,20 +402,11 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) return old_cpu != this_cpu; /* - * Use dynamically allocated buffer if available, otherwise - * fall back to static message for early boot panics or allocation failure. + * Do not consume args, the caller reuses them if we fail. */ - if (panic_force_buf) { - va_list ap; - - /* Do not consume args, the caller reuses it if we fail */ - va_copy(ap, args); - vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, ap); - va_end(ap); - msg = panic_force_buf; - } else { - msg = "Redirected panic (buffer unavailable)"; - } + va_copy(ap, args); + vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, ap); + va_end(ap); console_verbose(); bust_spinlocks(1); @@ -441,7 +421,7 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) dump_stack(); } - if (panic_smp_redirect_cpu(panic_force_cpu, (void *)msg) != 0) { + if (panic_smp_redirect_cpu(panic_force_cpu, panic_force_buf) != 0) { atomic_set(&panic_redirect_cpu, PANIC_CPU_INVALID); pr_warn("panic: failed to redirect to CPU %d, continuing on CPU %d\n", panic_force_cpu, this_cpu); -- cgit v1.2.3