[RFC PATCH 31/36] arm64: nmi: Add handling of superpriority interrupts as NMIs
Jinjie Ruan
ruanjinjie at huawei.com
Wed Jul 15 05:07:27 PDT 2026
On 7/15/2026 12:24 AM, Vladimir Murzin wrote:
> On 7/10/26 11:13, Jinjie Ruan wrote:
>>> diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
>>> index a13653b228b7..de71d5a3a6a1 100644
>>> --- a/arch/arm64/kernel/entry-common.c
>>> +++ b/arch/arm64/kernel/entry-common.c
>>> @@ -525,8 +525,8 @@ asmlinkage void noinstr el1h_64_sync_handler(struct pt_regs *regs)
>>> arm64_debug_exc_context(CRITICAL_CONTEXT);
>>> }
>>>
>>> -static __always_inline void __el1_pnmi(struct pt_regs *regs,
>>> - void (*handler)(struct pt_regs *))
>>> +static __always_inline void __el1_nmi(struct pt_regs *regs,
>>> + void (*handler)(struct pt_regs *))
>>> {
>>> arm64_exc_hwstate_t hwstate;
>>> irqentry_state_t state;
>>> @@ -545,7 +545,10 @@ static __always_inline void __el1_irq(struct pt_regs *regs,
>>>
>>> state = arm64_enter_from_kernel_mode(regs);
>>>
>>> - arm64_unmask_exc_context(NONMI_CONTEXT);
>>> + if (system_uses_nmi())
>>> + arm64_unmask_exc_context(NOIRQ_CONTEXT);
>>> + else
>>> + arm64_unmask_exc_context(NONMI_CONTEXT);
>> This looks strange; whether it's a pseudo NMI or FEAT_NMI, the behavior
>> should be consistent.
>
> Here is my understanding of how things behave.
>
> No NMI support
>
> NONMI_CONTEXT and NOIRQ_CONTEXT are the same, so we cannot take any
> IRQ while handling an IRQ. On return from the handler, we remain in
> NOIRQ_CONTEXT. Easy.
Yes, you are completely right.
>
> pNMI
>
> We cannot distinguish between an NMI and an IRQ on exception entry,
Yes, only after ack the interrupt can we determine whether the interrupt
priority is that of a pseudo-NMI interrupt.
> so we have to postpone unmasking the IF bits until the interrupt
> handler. In other words, we enter the interrupt handler in
> NONMI_CONTEXT.
Yes, we set DAIF.IF to mask IRQ and pNMI at first.
>
> In the handler:
>
> 1. For an NMI, we handle the NMI and then drop to NOIRQ_CONTEXT.
yes. I think what you said is the following code.
855 static void __gic_handle_irq_from_irqson(struct pt_regs *regs)
856 {
857 >-------bool is_nmi;
858 >-------u32 irqnr;
859
860 >-------irqnr = gic_read_iar();
861
862 >-------is_nmi = gic_rpr_is_nmi_prio();
863
864 >-------if (is_nmi) {
865 >------->-------nmi_enter();
866 >------->-------__gic_handle_nmi(irqnr, regs);
867 >------->-------nmi_exit();
868 >-------}
869
870 >-------if (gic_prio_masking_enabled()) {
871 >------->-------gic_pmr_mask_irqs();
872 >------->-------gic_arch_enable_irqs();
873 >-------}
We drop to NOIRQ_CONTEXT here by clear DAIF.IF and set pmr to IRQOFF.
874
875 >-------if (!is_nmi)
876 >------->-------__gic_handle_irq(irqnr, regs);
>
> 2. For an IRQ, we drop to NOIRQ_CONTEXT (allowing NMIs to preempt
> the IRQ handler) and then handle the IRQ.
Yes, it corresponds to 874~876 line. We need to unmask the pseudo NMI
before handling the regular IRQ, but keep the IRQs masked to prevent
interrupt nesting.
>
> In both cases, we return from the handler in NOIRQ_CONTEXT.
Yes
>
> FEAT_NMI
>
> We can distinguish between an NMI and an IRQ on exception
> entry. Thus, for an IRQ, we can immediately drop to NOIRQ_CONTEXT
> before passing control to the IRQ handler, allowing NMIs to preempt
> the IRQ handler. This corresponds to case 2 of the pNMI flow above.
>
I think this is not quite correct. When entering the __el1_irq()
function, if only system_uses_nmi() is true, it does not necessarily
mean it is NOIRQ_CONTEXT.
I believe the unmasking of allint, or NOIRQ_CONTEXT, should be delayed
until gic_handle_irq() actually before we processes the interrupt as
gic_unmask_pnmis() do it. At this point, it is still NONMI_CONTEXT to
keep hardware NMI masked as isr_el1.IS is not set.
41 static __always_inline void __el1_irq(struct pt_regs *regs,
542 >------->------->------->------- void (*handler)(struct
pt_regs *))
543 {
544 >-------irqentry_state_t state;
545
546 >-------state = arm64_enter_from_kernel_mode(regs);
547
548 >-------if (system_uses_nmi())
549 >------->-------arm64_unmask_exc_context(NOIRQ_CONTEXT);
550 >-------else
551 >------->-------arm64_unmask_exc_context(NONMI_CONTEXT);
568 static void noinstr el1_interrupt(struct pt_regs *regs,
569 >------->------->------->------- void (*handler)(struct pt_regs *))
570 {
571 >-------/* Is there a NMI to handle? */
572 >-------if (regs_irqs_disabled(regs))
573 >------->-------__el1_nmi(regs, handler);
574 >-------else if (system_uses_nmi() && (read_sysreg(isr_el1) &
(ISR_EL1_IS | ISR_EL1_FS)))
575 >------->-------__el1_nmi(regs, handler);
576 >-------else
577 >------->-------__el1_irq(regs, handler);
>
> Perhaps above could be better expressed with:
>
> if (gic_supports_pseudo_nmis())
> arm64_unmask_exc_context(NONMI_CONTEXT);
> else
> arm64_unmask_exc_context(NOIRQ_CONTEXT);
>
>
> Have I missed anything?
>
> Cheers
> Vladimir
>
More information about the linux-arm-kernel
mailing list