Skip to content

Commit 1da5351

Browse files
npigginmpe
authored andcommitted
powerpc/64/irq: tidy soft-masked irq replay and improve documentation
irq replay is quite complicated because of softirq processing which itself enables and disables irqs. Several considerations need to be accounted for due to this, and they are not clearly documented. Refactor the irq replay code a bit to tidy and deduplicate some common functions. Add comments, debug checks. This has a minor functional change that irq tracing enable/disable is done after each interrupt replayed, rather than after a batch. It also re-sets state to IRQS_ALL_DISABLED after an interrupt, which doesn't matter much because interrupts are hard disabled at this point, but it is more consistent with how interrupt handlers are called. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20220926054305.2671436-8-npiggin@gmail.com
1 parent f7bff6e commit 1da5351

File tree

1 file changed

+61
-32
lines changed

1 file changed

+61
-32
lines changed

arch/powerpc/kernel/irq_64.c

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,35 @@
6868

6969
int 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+
71100
void 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

90129
again:
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

Comments
 (0)