6868
6969int distribute_irqs = 1 ;
7070
71+ static inline void next_interrupt (struct pt_regs * regs )
72+ {
73+ /*
74+ * Softirq processing can enable/disable irqs, which will leave
75+ * MSR[EE] enabled and the soft mask set to IRQS_DISABLED. Fix
76+ * this up.
77+ */
78+ if (!(local_paca -> irq_happened & PACA_IRQ_HARD_DIS ))
79+ hard_irq_disable ();
80+ else
81+ irq_soft_mask_set (IRQS_ALL_DISABLED );
82+
83+ /*
84+ * We are responding to the next interrupt, so interrupt-off
85+ * latencies should be reset here.
86+ */
87+ trace_hardirqs_on ();
88+ trace_hardirqs_off ();
89+ }
90+
91+ static inline bool irq_happened_test_and_clear (u8 irq )
92+ {
93+ if (local_paca -> irq_happened & irq ) {
94+ local_paca -> irq_happened &= ~irq ;
95+ return true;
96+ }
97+ return false;
98+ }
99+
71100void replay_soft_interrupts (void )
72101{
73102 struct pt_regs regs ;
@@ -79,18 +108,25 @@ void replay_soft_interrupts(void)
79108 * recurse into this function. Don't keep any state across
80109 * interrupt handler calls which may change underneath us.
81110 *
111+ * Softirqs can not be disabled over replay to stop this recursion
112+ * because interrupts taken in idle code may require RCU softirq
113+ * to run in the irq RCU tracking context. This is a hard problem
114+ * to fix without changes to the softirq or idle layer.
115+ *
82116 * We use local_paca rather than get_paca() to avoid all the
83117 * debug_smp_processor_id() business in this low level function.
84118 */
85119
120+ if (IS_ENABLED (CONFIG_PPC_IRQ_SOFT_MASK_DEBUG )) {
121+ WARN_ON_ONCE (mfmsr () & MSR_EE );
122+ WARN_ON (!(local_paca -> irq_happened & PACA_IRQ_HARD_DIS ));
123+ }
124+
86125 ppc_save_regs (& regs );
87126 regs .softe = IRQS_ENABLED ;
88127 regs .msr |= MSR_EE ;
89128
90129again :
91- if (IS_ENABLED (CONFIG_PPC_IRQ_SOFT_MASK_DEBUG ))
92- WARN_ON_ONCE (mfmsr () & MSR_EE );
93-
94130 /*
95131 * Force the delivery of pending soft-disabled interrupts on PS3.
96132 * Any HV call will have this side effect.
@@ -105,56 +141,47 @@ void replay_soft_interrupts(void)
105141 * This is a higher priority interrupt than the others, so
106142 * replay it first.
107143 */
108- if (IS_ENABLED (CONFIG_PPC_BOOK3S ) && ( local_paca -> irq_happened & PACA_IRQ_HMI )) {
109- local_paca -> irq_happened &= ~ PACA_IRQ_HMI ;
144+ if (IS_ENABLED (CONFIG_PPC_BOOK3S ) &&
145+ irq_happened_test_and_clear ( PACA_IRQ_HMI )) {
110146 regs .trap = INTERRUPT_HMI ;
111147 handle_hmi_exception (& regs );
112- if (!(local_paca -> irq_happened & PACA_IRQ_HARD_DIS ))
113- hard_irq_disable ();
148+ next_interrupt (& regs );
114149 }
115150
116- if (local_paca -> irq_happened & PACA_IRQ_DEC ) {
117- local_paca -> irq_happened &= ~PACA_IRQ_DEC ;
151+ if (irq_happened_test_and_clear (PACA_IRQ_DEC )) {
118152 regs .trap = INTERRUPT_DECREMENTER ;
119153 timer_interrupt (& regs );
120- if (!(local_paca -> irq_happened & PACA_IRQ_HARD_DIS ))
121- hard_irq_disable ();
154+ next_interrupt (& regs );
122155 }
123156
124- if (local_paca -> irq_happened & PACA_IRQ_EE ) {
125- local_paca -> irq_happened &= ~PACA_IRQ_EE ;
157+ if (irq_happened_test_and_clear (PACA_IRQ_EE )) {
126158 regs .trap = INTERRUPT_EXTERNAL ;
127159 do_IRQ (& regs );
128- if (!(local_paca -> irq_happened & PACA_IRQ_HARD_DIS ))
129- hard_irq_disable ();
160+ next_interrupt (& regs );
130161 }
131162
132- if (IS_ENABLED (CONFIG_PPC_DOORBELL ) && ( local_paca -> irq_happened & PACA_IRQ_DBELL )) {
133- local_paca -> irq_happened &= ~ PACA_IRQ_DBELL ;
163+ if (IS_ENABLED (CONFIG_PPC_DOORBELL ) &&
164+ irq_happened_test_and_clear ( PACA_IRQ_DBELL )) {
134165 regs .trap = INTERRUPT_DOORBELL ;
135166 doorbell_exception (& regs );
136- if (!(local_paca -> irq_happened & PACA_IRQ_HARD_DIS ))
137- hard_irq_disable ();
167+ next_interrupt (& regs );
138168 }
139169
140170 /* Book3E does not support soft-masking PMI interrupts */
141- if (IS_ENABLED (CONFIG_PPC_BOOK3S ) && ( local_paca -> irq_happened & PACA_IRQ_PMI )) {
142- local_paca -> irq_happened &= ~ PACA_IRQ_PMI ;
171+ if (IS_ENABLED (CONFIG_PPC_BOOK3S ) &&
172+ irq_happened_test_and_clear ( PACA_IRQ_PMI )) {
143173 regs .trap = INTERRUPT_PERFMON ;
144174 performance_monitor_exception (& regs );
145- if (!(local_paca -> irq_happened & PACA_IRQ_HARD_DIS ))
146- hard_irq_disable ();
175+ next_interrupt (& regs );
147176 }
148177
149- if (local_paca -> irq_happened & ~PACA_IRQ_HARD_DIS ) {
150- /*
151- * We are responding to the next interrupt, so interrupt-off
152- * latencies should be reset here.
153- */
154- trace_hardirqs_on ();
155- trace_hardirqs_off ();
178+ /*
179+ * Softirq processing can enable and disable interrupts, which can
180+ * result in new irqs becoming pending. Must keep looping until we
181+ * have cleared out all pending interrupts.
182+ */
183+ if (local_paca -> irq_happened & ~PACA_IRQ_HARD_DIS )
156184 goto again ;
157- }
158185}
159186
160187#if defined(CONFIG_PPC_BOOK3S_64 ) && defined(CONFIG_PPC_KUAP )
@@ -270,10 +297,12 @@ notrace void arch_local_irq_restore(unsigned long mask)
270297 trace_hardirqs_off ();
271298
272299 replay_soft_interrupts_irqrestore ();
273- local_paca -> irq_happened = 0 ;
274300
275301 trace_hardirqs_on ();
276302 irq_soft_mask_set (IRQS_ENABLED );
303+ if (IS_ENABLED (CONFIG_PPC_IRQ_SOFT_MASK_DEBUG ))
304+ WARN_ON (local_paca -> irq_happened != PACA_IRQ_HARD_DIS );
305+ local_paca -> irq_happened = 0 ;
277306 __hard_irq_enable ();
278307 preempt_enable ();
279308}
0 commit comments