EXC_COMMON_BEGIN(performance_monitor_common)
        GEN_COMMON performance_monitor
        addi    r3,r1,STACK_FRAME_OVERHEAD
-       bl      performance_monitor_exception
+       lbz     r4,PACAIRQSOFTMASK(r13)
+       cmpdi   r4,IRQS_ENABLED
+       bne     1f
+       bl      performance_monitor_exception_async
        b       interrupt_return_srr
+1:
+       bl      performance_monitor_exception_nmi
+       /* Clear MSR_RI before setting SRR0 and SRR1. */
+       li      r9,0
+       mtmsrd  r9,1
 
+       kuap_kernel_restore r9, r10
+
+       EXCEPTION_RESTORE_REGS hsrr=0
+       RFI_TO_KERNEL
 
 /**
  * Interrupt 0xf20 - Vector Unavailable Interrupt.
 
         * CT_WARN_ON comes here via program_check_exception, so avoid
         * recursion.
         *
-        * Skip the assertion on PMIs to work around a problem caused by NMI
-        * PMIs incorrectly taking this interrupt return path, it's possible
-        * for this to hit after interrupt exit to user switches context to
-        * user. See also the comment in the performance monitor handler in
-        * exceptions-64e/s.S
+        * Skip the assertion on PMIs on 64e to work around a problem caused
+        * by NMI PMIs incorrectly taking this interrupt return path, it's
+        * possible for this to hit after interrupt exit to user switches
+        * context to user. See also the comment in the performance monitor
+        * handler in exceptions-64e.S
         */
-       if (TRAP(regs) != INTERRUPT_PROGRAM && TRAP(regs) != INTERRUPT_PERFMON)
+       if (!IS_ENABLED(CONFIG_PPC_BOOK3E_64) &&
+           TRAP(regs) != INTERRUPT_PROGRAM &&
+           TRAP(regs) != INTERRUPT_PERFMON)
                CT_WARN_ON(ct_state() == CONTEXT_USER);
 
        kuap = kuap_get_and_assert_locked();