Skip to content

Commit 4025c78

Browse files
npigginmpe
authored andcommitted
powerpc/64s: prevent recursive replay_soft_interrupts causing superfluous interrupt
When an asynchronous interrupt calls irq_exit, it checks for softirqs that may have been created, and runs them. Running softirqs enables local irqs, which can replay pending interrupts causing recursion in replay_soft_interrupts. This abridged trace shows how this can occur: ! NIP replay_soft_interrupts LR interrupt_exit_kernel_prepare Call Trace: interrupt_exit_kernel_prepare (unreliable) interrupt_return --- interrupt: ea0 at __rb_reserve_next NIP __rb_reserve_next LR __rb_reserve_next Call Trace: ring_buffer_lock_reserve trace_function function_trace_call ftrace_call __do_softirq irq_exit timer_interrupt ! replay_soft_interrupts interrupt_exit_kernel_prepare interrupt_return --- interrupt: ea0 at arch_local_irq_restore This can not be prevented easily, because softirqs must not block hard irqs, so it has to be dealt with. The recursion is bounded by design in the softirq code because softirq replay disables softirqs and loops around again to check for new softirqs created while it ran, so that's not a problem. However it does mess up interrupt replay state, causing superfluous interrupts when the second replay_soft_interrupts clears a pending interrupt, leaving it still set in the first call in the 'happened' local variable. Fix this by not caching a copy of irqs_happened across interrupt handler calls. Fixes: 3282a3d ("powerpc/64: Implement soft interrupt replay in C") Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20210123061244.2076145-1-npiggin@gmail.com
1 parent 08685be commit 4025c78

1 file changed

Lines changed: 16 additions & 12 deletions

File tree

arch/powerpc/kernel/irq.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,18 @@ void notrace restore_interrupts(void)
180180

181181
void replay_soft_interrupts(void)
182182
{
183+
struct pt_regs regs;
184+
183185
/*
184-
* We use local_paca rather than get_paca() to avoid all
185-
* the debug_smp_processor_id() business in this low level
186-
* function
186+
* Be careful here, calling these interrupt handlers can cause
187+
* softirqs to be raised, which they may run when calling irq_exit,
188+
* which will cause local_irq_enable() to be run, which can then
189+
* recurse into this function. Don't keep any state across
190+
* interrupt handler calls which may change underneath us.
191+
*
192+
* We use local_paca rather than get_paca() to avoid all the
193+
* debug_smp_processor_id() business in this low level function.
187194
*/
188-
unsigned char happened = local_paca->irq_happened;
189-
struct pt_regs regs;
190195

191196
ppc_save_regs(&regs);
192197
regs.softe = IRQS_ENABLED;
@@ -209,31 +214,31 @@ void replay_soft_interrupts(void)
209214
* This is a higher priority interrupt than the others, so
210215
* replay it first.
211216
*/
212-
if (IS_ENABLED(CONFIG_PPC_BOOK3S) && (happened & PACA_IRQ_HMI)) {
217+
if (IS_ENABLED(CONFIG_PPC_BOOK3S) && (local_paca->irq_happened & PACA_IRQ_HMI)) {
213218
local_paca->irq_happened &= ~PACA_IRQ_HMI;
214219
regs.trap = 0xe60;
215220
handle_hmi_exception(&regs);
216221
if (!(local_paca->irq_happened & PACA_IRQ_HARD_DIS))
217222
hard_irq_disable();
218223
}
219224

220-
if (happened & PACA_IRQ_DEC) {
225+
if (local_paca->irq_happened & PACA_IRQ_DEC) {
221226
local_paca->irq_happened &= ~PACA_IRQ_DEC;
222227
regs.trap = 0x900;
223228
timer_interrupt(&regs);
224229
if (!(local_paca->irq_happened & PACA_IRQ_HARD_DIS))
225230
hard_irq_disable();
226231
}
227232

228-
if (happened & PACA_IRQ_EE) {
233+
if (local_paca->irq_happened & PACA_IRQ_EE) {
229234
local_paca->irq_happened &= ~PACA_IRQ_EE;
230235
regs.trap = 0x500;
231236
do_IRQ(&regs);
232237
if (!(local_paca->irq_happened & PACA_IRQ_HARD_DIS))
233238
hard_irq_disable();
234239
}
235240

236-
if (IS_ENABLED(CONFIG_PPC_DOORBELL) && (happened & PACA_IRQ_DBELL)) {
241+
if (IS_ENABLED(CONFIG_PPC_DOORBELL) && (local_paca->irq_happened & PACA_IRQ_DBELL)) {
237242
local_paca->irq_happened &= ~PACA_IRQ_DBELL;
238243
if (IS_ENABLED(CONFIG_PPC_BOOK3E))
239244
regs.trap = 0x280;
@@ -245,16 +250,15 @@ void replay_soft_interrupts(void)
245250
}
246251

247252
/* Book3E does not support soft-masking PMI interrupts */
248-
if (IS_ENABLED(CONFIG_PPC_BOOK3S) && (happened & PACA_IRQ_PMI)) {
253+
if (IS_ENABLED(CONFIG_PPC_BOOK3S) && (local_paca->irq_happened & PACA_IRQ_PMI)) {
249254
local_paca->irq_happened &= ~PACA_IRQ_PMI;
250255
regs.trap = 0xf00;
251256
performance_monitor_exception(&regs);
252257
if (!(local_paca->irq_happened & PACA_IRQ_HARD_DIS))
253258
hard_irq_disable();
254259
}
255260

256-
happened = local_paca->irq_happened;
257-
if (happened & ~PACA_IRQ_HARD_DIS) {
261+
if (local_paca->irq_happened & ~PACA_IRQ_HARD_DIS) {
258262
/*
259263
* We are responding to the next interrupt, so interrupt-off
260264
* latencies should be reset here.

0 commit comments

Comments
 (0)