The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v2] panic: allow force_cpu redirect from an NMI
@ 2026-07-08 16:43 Bradley Morgan
  0 siblings, 0 replies; only message in thread
From: Bradley Morgan @ 2026-07-08 16:43 UTC (permalink / raw)
  To: akpm
  Cc: pmladek, feng.tang, tglx, peterz, rostedt, linux-kernel, include,
	Sashiko

nmi_panic() calls panic_try_start() before panic(), so it claims
panic_cpu first. When the panic then reaches panic_try_force_cpu(),
panic_in_progress() sees panic_cpu set and returns false, so the
redirect to the requested CPU never happens. The crash kernel runs
on the CPU that took the NMI instead.

smp_call_function_single_async() is safe from NMI context, so move
the redirect first. nmi_panic() now calls panic_try_force_cpu()
before panic_try_start(), and only claims panic_cpu when no redirect
is done. The requested CPU then claims panic_cpu itself.

Also use panic_on_other_cpu() in place of the open coded check and
return true to stop directly.

Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260707183253.9793-1-include@grrlz.net

Signed-off-by: Bradley Morgan <include@grrlz.net>
---
 kernel/panic.c | 38 ++++++++++++++++----------------------
 1 file changed, 16 insertions(+), 22 deletions(-)

Changes since v1:
  - Dropped the atomic_set panic_cpu hand off, it was racy.
  - Restructured nmi_panic() to call panic_try_force_cpu() first.
  - Used panic_on_other_cpu() instead of an open coded check.
  - panic_try_force_cpu() now takes a formatted message string.

diff --git a/kernel/panic.c b/kernel/panic.c
index ebdc46af6aa9..b039bb6f1d19 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -364,8 +364,7 @@ int __weak panic_smp_redirect_cpu(int target_cpu, void *msg)
 
 /**
  * panic_try_force_cpu - Redirect panic to a specific CPU for crash kernel
- * @fmt: panic message format string
- * @args: arguments for format string
+ * @msg: already formatted panic message
  *
  * Some platforms require panic handling to occur on a specific CPU
  * for the crash kernel to function correctly. This function redirects
@@ -374,12 +373,10 @@ int __weak panic_smp_redirect_cpu(int target_cpu, void *msg)
  * Returns false if panic should proceed on current CPU.
  * Returns true if panic was redirected.
  */
-__printf(1, 0)
-static bool panic_try_force_cpu(const char *fmt, va_list args)
+static bool panic_try_force_cpu(const char *msg)
 {
 	int this_cpu = raw_smp_processor_id();
 	int old_cpu = PANIC_CPU_INVALID;
-	const char *msg;
 
 	/* Feature not enabled via boot parameter */
 	if (panic_force_cpu < 0)
@@ -396,9 +393,9 @@ static bool panic_try_force_cpu(const char *fmt, va_list args)
 		return false;
 	}
 
-	/* Another panic already in progress */
-	if (panic_in_progress())
-		return false;
+	/* Stop this CPU when the panic is already proceeding elsewhere. */
+	if (panic_on_other_cpu())
+		return true;
 
 	/*
 	 * Only one CPU can do the redirection. Others should go
@@ -412,12 +409,7 @@ 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) {
-		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);
+		strscpy(panic_force_buf, msg, PANIC_MSG_BUFSZ);
 		msg = panic_force_buf;
 	} else {
 		msg = "Redirected panic (buffer unavailable)";
@@ -447,8 +439,7 @@ static bool panic_try_force_cpu(const char *fmt, va_list args)
 	return true;
 }
 #else
-__printf(1, 0)
-static inline bool panic_try_force_cpu(const char *fmt, va_list args)
+static inline bool panic_try_force_cpu(const char *msg)
 {
 	return false;
 }
@@ -515,7 +506,9 @@ EXPORT_SYMBOL(panic_on_other_cpu);
  */
 void nmi_panic(struct pt_regs *regs, const char *msg)
 {
-	if (panic_try_start())
+	if (panic_try_force_cpu(msg))
+		nmi_panic_self_stop(regs);
+	else if (panic_try_start())
 		panic("%s", msg);
 	else if (panic_on_other_cpu())
 		nmi_panic_self_stop(regs);
@@ -609,8 +602,13 @@ void vpanic(const char *fmt, va_list args)
 	local_irq_disable();
 	preempt_disable_notrace();
 
+	/* Format the message once; reused for the log and any redirect IPI. */
+	len = vscnprintf(buf, sizeof(buf), fmt, args);
+	if (len && buf[len - 1] == '\n')
+		buf[len - 1] = '\0';
+
 	/* Redirect panic to target CPU if configured via panic_force_cpu=. */
-	if (panic_try_force_cpu(fmt, args)) {
+	if (panic_try_force_cpu(buf)) {
 		/*
 		 * Mark ourselves offline so panic_other_cpus_shutdown() won't wait
 		 * for us on architectures that check num_online_cpus().
@@ -641,10 +639,6 @@ void vpanic(const char *fmt, va_list args)
 
 	console_verbose();
 	bust_spinlocks(1);
-	len = vscnprintf(buf, sizeof(buf), fmt, args);
-
-	if (len && buf[len - 1] == '\n')
-		buf[len - 1] = '\0';
 
 	pr_emerg("Kernel panic - not syncing: %s\n", buf);
 	/*
-- 
2.53.0


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-07-08 16:43 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-08 16:43 [PATCH v2] panic: allow force_cpu redirect from an NMI Bradley Morgan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox