linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] panic: call hardlockup_detector_perf_stop in panic
@ 2025-07-30  3:06 Wang Jinchao
  2025-08-19 15:01 ` Petr Mladek
  0 siblings, 1 reply; 5+ messages in thread
From: Wang Jinchao @ 2025-07-30  3:06 UTC (permalink / raw)
  To: Petr Mladek, John Ogness, Thomas Gleixner, Joel Granados,
	Dave Jiang, Josh Poimboeuf, Sravan Kumar Gundu, Ryo Takakura
  Cc: linux-kernel, Wang Jinchao, Wei Liu, Jason Gunthorpe

When a panic happens, it blocks the cpu, which may
trigger the hardlockup detector if some dump is slow.
So call hardlockup_detector_perf_stop() to disable
hardlockup dector.

Signed-off-by: Wang Jinchao <wangjinchao600@gmail.com>
---
 kernel/panic.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/panic.c b/kernel/panic.c
index b0b9a8bf4560..52a1ac4ad447 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -339,6 +339,7 @@ void panic(const char *fmt, ...)
 	 */
 	local_irq_disable();
 	preempt_disable_notrace();
+	hardlockup_detector_perf_stop();
 
 	/*
 	 * It's possible to come here directly from a panic-assertion and
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] panic: call hardlockup_detector_perf_stop in panic
  2025-07-30  3:06 [PATCH] panic: call hardlockup_detector_perf_stop in panic Wang Jinchao
@ 2025-08-19 15:01 ` Petr Mladek
  2025-08-20  6:22   ` Jinchao Wang
  0 siblings, 1 reply; 5+ messages in thread
From: Petr Mladek @ 2025-08-19 15:01 UTC (permalink / raw)
  To: Wang Jinchao
  Cc: John Ogness, Thomas Gleixner, Joel Granados, Dave Jiang,
	Josh Poimboeuf, Sravan Kumar Gundu, Ryo Takakura, linux-kernel,
	Wei Liu, Jason Gunthorpe

On Wed 2025-07-30 11:06:33, Wang Jinchao wrote:
> When a panic happens, it blocks the cpu, which may
> trigger the hardlockup detector if some dump is slow.
> So call hardlockup_detector_perf_stop() to disable
> hardlockup dector.

Could you please provide more details, especially the log showing
the problem?

I wonder if this is similar to
https://lore.kernel.org/all/SN6PR02MB4157A4C5E8CB219A75263A17D46DA@SN6PR02MB4157.namprd02.prod.outlook.com/

There was a problem that a non-panic CPU might get stuck in
pl011_console_write_thread() or any other con->write_thread()
callback because nbcon_reacquire_nobuf(wctxt) ended in an infinite
loop.

It was a real lockup. It has got recently fixed in 6.17-rc1 by
the commit 571c1ea91a73db56bd94 ("printk: nbcon: Allow reacquire
during panic"), see
https://patch.msgid.link/20250606185549.900611-1-john.ogness@linutronix.de
It is possible that it fixed your problem as well.

That said, it might make sense to disable the hardlockup
detector during panic. But I do not like the proposed way,
see below.

> --- a/kernel/panic.c
> +++ b/kernel/panic.c
> @@ -339,6 +339,7 @@ void panic(const char *fmt, ...)
>  	 */
>  	local_irq_disable();
>  	preempt_disable_notrace();
> +	hardlockup_detector_perf_stop();

I see the following in kernel/watchdog_perf.c:

/**
 * hardlockup_detector_perf_stop - Globally stop watchdog events
 *
 * Special interface for x86 to handle the perf HT bug.
 */
void __init hardlockup_detector_perf_stop(void)
{
[...]
	lockdep_assert_cpus_held();
[...]
}

1. It is suspicious to see an x86-specific "hacky" function called in
   the generic panic().

   Is this safe?
   What about other hardlockup detectors?


2. I expect that lockdep_assert_cpus_held() would complain
   when CONFIG_LOCKDEP was enabled.


Anyway, it does not look safe. panic() might be called in any context,
including NMI, and I see:

 + hardlockup_detector_perf_stop()
   + perf_event_disable()
     + perf_event_ctx_lock()
       + mutex_lock_nested()

This might cause deadlock when called in NMI, definitely.

Alternative:

A conservative approach would be to update watchdog_hardlockup_check()
so that it does nothing when panic_in_progress() returns true. It
would even work for both hardlockup detectors implementation.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] panic: call hardlockup_detector_perf_stop in panic
  2025-08-19 15:01 ` Petr Mladek
@ 2025-08-20  6:22   ` Jinchao Wang
  2025-08-20 10:22     ` Petr Mladek
  0 siblings, 1 reply; 5+ messages in thread
From: Jinchao Wang @ 2025-08-20  6:22 UTC (permalink / raw)
  To: Petr Mladek
  Cc: John Ogness, Thomas Gleixner, Joel Granados, Dave Jiang,
	Josh Poimboeuf, Sravan Kumar Gundu, Ryo Takakura, linux-kernel,
	Wei Liu, Jason Gunthorpe

On 8/19/25 23:01, Petr Mladek wrote:
> On Wed 2025-07-30 11:06:33, Wang Jinchao wrote:
>> When a panic happens, it blocks the cpu, which may
>> trigger the hardlockup detector if some dump is slow.
>> So call hardlockup_detector_perf_stop() to disable
>> hardlockup dector.
> 
> Could you please provide more details, especially the log showing
> the problem?

Here's what happened: I configured the kernel to use efi-pstore for kdump
logging while enabling the perf hard lockup detector (NMI). Perhaps the
efi-pstore was slow and there were too many logs. When the first panic was
triggered, the pstore dump callback in kmsg_dump()->dumper->dump() took a
long time, which triggered the NMI watchdog. Then emergency_restart()
triggered the machine restart before the efi-pstore operation finished.
The function call flow looked like this:

```c
real panic() {
	kmsg_dump() {
		...
		pstore_dump() {
			start_dump();
			... // long time operation triggers NMI watchdog
			nmi panic() {
				...
				emergency_restart(); //pstore unfinished
			}
			...
			finish_dump(); // never reached
		}
	}
}
```

This created a nested panic situation where the second panic interrupted
the crash dump process, causing the loss of the original panic information.
> 
> I wonder if this is similar to
> https://lore.kernel.org/all/SN6PR02MB4157A4C5E8CB219A75263A17D46DA@SN6PR02MB4157.namprd02.prod.outlook.com/
> 
> There was a problem that a non-panic CPU might get stuck in
> pl011_console_write_thread() or any other con->write_thread()
> callback because nbcon_reacquire_nobuf(wctxt) ended in an infinite
> loop.
> 
> It was a real lockup. It has got recently fixed in 6.17-rc1 by
> the commit 571c1ea91a73db56bd94 ("printk: nbcon: Allow reacquire
> during panic"), see
> https://patch.msgid.link/20250606185549.900611-1-john.ogness@linutronix.de
> It is possible that it fixed your problem as well.
> 
> That said, it might make sense to disable the hardlockup
> detector during panic. But I do not like the proposed way,
> see below.
> 
>> --- a/kernel/panic.c
>> +++ b/kernel/panic.c
>> @@ -339,6 +339,7 @@ void panic(const char *fmt, ...)
>>   	 */
>>   	local_irq_disable();
>>   	preempt_disable_notrace();
>> +	hardlockup_detector_perf_stop();
> 
> I see the following in kernel/watchdog_perf.c:
> 
> /**
>   * hardlockup_detector_perf_stop - Globally stop watchdog events
>   *
>   * Special interface for x86 to handle the perf HT bug.
>   */
> void __init hardlockup_detector_perf_stop(void)
> {
> [...]
> 	lockdep_assert_cpus_held();
> [...]
> }
> 
> 1. It is suspicious to see an x86-specific "hacky" function called in
>     the generic panic().
> 
>     Is this safe?
>     What about other hardlockup detectors?
> 
> 
> 2. I expect that lockdep_assert_cpus_held() would complain
>     when CONFIG_LOCKDEP was enabled.
> 
> 
> Anyway, it does not look safe. panic() might be called in any context,
> including NMI, and I see:
> 
>   + hardlockup_detector_perf_stop()
>     + perf_event_disable()
>       + perf_event_ctx_lock()
>         + mutex_lock_nested()
> 
> This might cause deadlock when called in NMI, definitely.
> 
> Alternative:
> 
> A conservative approach would be to update watchdog_hardlockup_check()
> so that it does nothing when panic_in_progress() returns true. It
> would even work for both hardlockup detectors implementation.
Yes, I think it is a better solution.
I didn't find panic_in_progress() but found 
hardlockup_detector_perf_stop() available instead :)
I will send another patch.

> 
> Best Regards,
> Petr


-- 
Best regards,
Jinchao

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] panic: call hardlockup_detector_perf_stop in panic
  2025-08-20  6:22   ` Jinchao Wang
@ 2025-08-20 10:22     ` Petr Mladek
  2025-08-21  1:35       ` Jinchao Wang
  0 siblings, 1 reply; 5+ messages in thread
From: Petr Mladek @ 2025-08-20 10:22 UTC (permalink / raw)
  To: Jinchao Wang, Thomas Gleixner, Peter Zijlstra, Josh Poimboeuf
  Cc: John Ogness, Joel Granados, Dave Jiang, Sravan Kumar Gundu,
	Ryo Takakura, linux-kernel, Wei Liu, Jason Gunthorpe

Adding Peter Zijlstra into Cc.

The nested panic() should return. But panic() was never supposed to
return. It seems that it is not marked as noreturn but I am not sure
whether some tricks are not hidden somewhere, in objtool, or...

On Wed 2025-08-20 14:22:52, Jinchao Wang wrote:
> On 8/19/25 23:01, Petr Mladek wrote:
> > On Wed 2025-07-30 11:06:33, Wang Jinchao wrote:
> > > When a panic happens, it blocks the cpu, which may
> > > trigger the hardlockup detector if some dump is slow.
> > > So call hardlockup_detector_perf_stop() to disable
> > > hardlockup dector.
> > 
> > Could you please provide more details, especially the log showing
> > the problem?
> 
> Here's what happened: I configured the kernel to use efi-pstore for kdump
> logging while enabling the perf hard lockup detector (NMI). Perhaps the
> efi-pstore was slow and there were too many logs. When the first panic was
> triggered, the pstore dump callback in kmsg_dump()->dumper->dump() took a
> long time, which triggered the NMI watchdog. Then emergency_restart()
> triggered the machine restart before the efi-pstore operation finished.
> The function call flow looked like this:
> 
> ```c
> real panic() {
> 	kmsg_dump() {
> 		...
> 		pstore_dump() {
> 			start_dump();
> 			... // long time operation triggers NMI watchdog
> 			nmi panic() {
> 				...
> 				emergency_restart(); //pstore unfinished
> 			}
> 			...
> 			finish_dump(); // never reached
> 		}
> 	}
> }
> ```
> 
> This created a nested panic situation where the second panic interrupted
> the crash dump process, causing the loss of the original panic information.

I believe that we should prevent the nested panic() in the first
place. There already is the following code:

void vpanic(const char *fmt, va_list args)
{
[...]
	 * Only one CPU is allowed to execute the panic code from here. For
	 * multiple parallel invocations of panic, all other CPUs either
	 * stop themself or will wait until they are stopped by the 1st CPU
	 * with smp_send_stop().
	 *
	 * cmpxchg success means this is the 1st CPU which comes here,
	 * so go ahead.
	 * `old_cpu == this_cpu' means we came from nmi_panic() which sets
	 * panic_cpu to this CPU.  In this case, this is also the 1st CPU.
	 */
	old_cpu = PANIC_CPU_INVALID;
	this_cpu = raw_smp_processor_id();

	/* atomic_try_cmpxchg updates old_cpu on failure */
	if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) {
		/* go ahead */
	} else if (old_cpu != this_cpu)
		panic_smp_self_stop();


We should improve it to detect nested panic() call as well,
something like:

	this_cpu = raw_smp_processor_id();
	/* Bail out in a nested panic(). Let the outer one finish the job. */
	if (this_cpu == atomic_read(&panic_cpu))
		return;

	/* atomic_try_cmpxchg updates old_cpu on failure */
	old_cpu = PANIC_CPU_INVALID;
	if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) {
		/* go ahead */
	} else if (old_cpu != this_cpu)
		panic_smp_self_stop();


> > That said, it might make sense to disable the hardlockup
> > detector during panic. But I do not like the proposed way,
> > see below.
> > 
> > > --- a/kernel/panic.c
> > > +++ b/kernel/panic.c
> > > @@ -339,6 +339,7 @@ void panic(const char *fmt, ...)
> > >   	 */
> > >   	local_irq_disable();
> > >   	preempt_disable_notrace();
> > > +	hardlockup_detector_perf_stop();
> > 
> > Anyway, it does not look safe. panic() might be called in any context,
> > including NMI, and I see:
> > 
> >   + hardlockup_detector_perf_stop()
> >     + perf_event_disable()
> >       + perf_event_ctx_lock()
> >         + mutex_lock_nested()
> > 
> > This might cause deadlock when called in NMI, definitely.
> > 
> > Alternative:
> > 
> > A conservative approach would be to update watchdog_hardlockup_check()
> > so that it does nothing when panic_in_progress() returns true. It
> > would even work for both hardlockup detectors implementation.
> Yes, I think it is a better solution.
> I didn't find panic_in_progress() but found hardlockup_detector_perf_stop()
> available instead :)
> I will send another patch.

OK.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] panic: call hardlockup_detector_perf_stop in panic
  2025-08-20 10:22     ` Petr Mladek
@ 2025-08-21  1:35       ` Jinchao Wang
  0 siblings, 0 replies; 5+ messages in thread
From: Jinchao Wang @ 2025-08-21  1:35 UTC (permalink / raw)
  To: Petr Mladek, Thomas Gleixner, Peter Zijlstra, Josh Poimboeuf
  Cc: John Ogness, Joel Granados, Dave Jiang, Sravan Kumar Gundu,
	Ryo Takakura, linux-kernel, Wei Liu, Jason Gunthorpe

On 8/20/25 18:22, Petr Mladek wrote:
> Adding Peter Zijlstra into Cc.
> 
> The nested panic() should return. But panic() was never supposed to
> return. It seems that it is not marked as noreturn but I am not sure
> whether some tricks are not hidden somewhere, in objtool, or...
> 
> On Wed 2025-08-20 14:22:52, Jinchao Wang wrote:
>> On 8/19/25 23:01, Petr Mladek wrote:
>>> On Wed 2025-07-30 11:06:33, Wang Jinchao wrote:
>>>> When a panic happens, it blocks the cpu, which may
>>>> trigger the hardlockup detector if some dump is slow.
>>>> So call hardlockup_detector_perf_stop() to disable
>>>> hardlockup dector.
>>>
>>> Could you please provide more details, especially the log showing
>>> the problem?
>>
>> Here's what happened: I configured the kernel to use efi-pstore for kdump
>> logging while enabling the perf hard lockup detector (NMI). Perhaps the
>> efi-pstore was slow and there were too many logs. When the first panic was
>> triggered, the pstore dump callback in kmsg_dump()->dumper->dump() took a
>> long time, which triggered the NMI watchdog. Then emergency_restart()
>> triggered the machine restart before the efi-pstore operation finished.
>> The function call flow looked like this:
>>
>> ```c
>> real panic() {
>> 	kmsg_dump() {
>> 		...
>> 		pstore_dump() {
>> 			start_dump();
>> 			... // long time operation triggers NMI watchdog
>> 			nmi panic() {
>> 				...
>> 				emergency_restart(); //pstore unfinished
>> 			}
>> 			...
>> 			finish_dump(); // never reached
>> 		}
>> 	}
>> }
>> ```
>>
>> This created a nested panic situation where the second panic interrupted
>> the crash dump process, causing the loss of the original panic information.
> 
> I believe that we should prevent the nested panic() in the first
> place. There already is the following code:
> 
> void vpanic(const char *fmt, va_list args)
> {
> [...]
> 	 * Only one CPU is allowed to execute the panic code from here. For
> 	 * multiple parallel invocations of panic, all other CPUs either
> 	 * stop themself or will wait until they are stopped by the 1st CPU
> 	 * with smp_send_stop().
> 	 *
> 	 * cmpxchg success means this is the 1st CPU which comes here,
> 	 * so go ahead.
> 	 * `old_cpu == this_cpu' means we came from nmi_panic() which sets
> 	 * panic_cpu to this CPU.  In this case, this is also the 1st CPU.
> 	 */
> 	old_cpu = PANIC_CPU_INVALID;
> 	this_cpu = raw_smp_processor_id();
> 
> 	/* atomic_try_cmpxchg updates old_cpu on failure */
> 	if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) {
> 		/* go ahead */
> 	} else if (old_cpu != this_cpu)
> 		panic_smp_self_stop();
> 
> 
> We should improve it to detect nested panic() call as well,
> something like:
> 
> 	this_cpu = raw_smp_processor_id();
> 	/* Bail out in a nested panic(). Let the outer one finish the job. */
> 	if (this_cpu == atomic_read(&panic_cpu))
> 		return;
> 
> 	/* atomic_try_cmpxchg updates old_cpu on failure */
> 	old_cpu = PANIC_CPU_INVALID;
> 	if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) {
> 		/* go ahead */
> 	} else if (old_cpu != this_cpu)
> 		panic_smp_self_stop();
> 
Agree with you.
Please see my patchset of "panic: introduce panic status function family"
https://lore.kernel.org/all/20250820091702.512524-1-wangjinchao600@gmail.com/

For this nested panic problem, see patch 9.

> 
>>> That said, it might make sense to disable the hardlockup
>>> detector during panic. But I do not like the proposed way,
>>> see below.
>>>
>>>> --- a/kernel/panic.c
>>>> +++ b/kernel/panic.c
>>>> @@ -339,6 +339,7 @@ void panic(const char *fmt, ...)
>>>>    	 */
>>>>    	local_irq_disable();
>>>>    	preempt_disable_notrace();
>>>> +	hardlockup_detector_perf_stop();
>>>
>>> Anyway, it does not look safe. panic() might be called in any context,
>>> including NMI, and I see:
>>>
>>>    + hardlockup_detector_perf_stop()
>>>      + perf_event_disable()
>>>        + perf_event_ctx_lock()
>>>          + mutex_lock_nested()
>>>
>>> This might cause deadlock when called in NMI, definitely.
>>>
>>> Alternative:
>>>
>>> A conservative approach would be to update watchdog_hardlockup_check()
>>> so that it does nothing when panic_in_progress() returns true. It
>>> would even work for both hardlockup detectors implementation.
>> Yes, I think it is a better solution.
>> I didn't find panic_in_progress() but found hardlockup_detector_perf_stop()
>> available instead :)
>> I will send another patch.
> 
> OK.
> 
> Best Regards,
> Petr


-- 
Best regards,
Jinchao

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-08-21  1:35 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-30  3:06 [PATCH] panic: call hardlockup_detector_perf_stop in panic Wang Jinchao
2025-08-19 15:01 ` Petr Mladek
2025-08-20  6:22   ` Jinchao Wang
2025-08-20 10:22     ` Petr Mladek
2025-08-21  1:35       ` Jinchao Wang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).