Skip to content

Commit 9b81c02

Browse files
npigginmpe
authored andcommitted
powerpc/64s: make PACA_IRQ_HARD_DIS track MSR[EE] closely
When the masked interrupt handler clears MSR[EE] for an interrupt in the PACA_IRQ_MUST_HARD_MASK set, it does not set PACA_IRQ_HARD_DIS. This makes them get out of synch. With that taken into account, it's only low level irq manipulation (and interrupt entry before reconcile) where they can be out of synch. This makes the code less surprising. It also allows the IRQ replay code to rely on the IRQ_HARD_DIS value and not have to mtmsrd again in this case (e.g., for an external interrupt that has been masked). The bigger benefit might just be that there is not such an element of surprise in these two bits of state. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
1 parent 29e8131 commit 9b81c02

File tree

6 files changed

+52
-16
lines changed

6 files changed

+52
-16
lines changed

arch/powerpc/include/asm/hw_irq.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,14 +253,16 @@ static inline bool lazy_irq_pending(void)
253253

254254
/*
255255
* This is called by asynchronous interrupts to conditionally
256-
* re-enable hard interrupts when soft-disabled after having
257-
* cleared the source of the interrupt
256+
* re-enable hard interrupts after having cleared the source
257+
* of the interrupt. They are kept disabled if there is a different
258+
* soft-masked interrupt pending that requires hard masking.
258259
*/
259260
static inline void may_hard_irq_enable(void)
260261
{
261-
get_paca()->irq_happened &= ~PACA_IRQ_HARD_DIS;
262-
if (!(get_paca()->irq_happened & PACA_IRQ_MUST_HARD_MASK))
262+
if (!(get_paca()->irq_happened & PACA_IRQ_MUST_HARD_MASK)) {
263+
get_paca()->irq_happened &= ~PACA_IRQ_HARD_DIS;
263264
__hard_irq_enable();
265+
}
264266
}
265267

266268
static inline bool arch_irq_disabled_regs(struct pt_regs *regs)

arch/powerpc/kernel/entry_64.S

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
992992
or r4,r4,r3
993993
std r4,_TRAP(r1)
994994

995+
/*
996+
* PACA_IRQ_HARD_DIS won't always be set here, so set it now
997+
* to reconcile the IRQ state. Tracing is already accounted for.
998+
*/
999+
lbz r4,PACAIRQHAPPENED(r13)
1000+
ori r4,r4,PACA_IRQ_HARD_DIS
1001+
stb r4,PACAIRQHAPPENED(r13)
1002+
9951003
/*
9961004
* Then find the right handler and call it. Interrupts are
9971005
* still soft-disabled and we keep them that way.

arch/powerpc/kernel/exceptions-64e.S

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,11 @@ kernel_dbg_exc:
949949

950950
.macro masked_interrupt_book3e paca_irq full_mask
951951
lbz r10,PACAIRQHAPPENED(r13)
952+
.if \full_mask == 1
953+
ori r10,r10,\paca_irq | PACA_IRQ_HARD_DIS
954+
.else
952955
ori r10,r10,\paca_irq
956+
.endif
953957
stb r10,PACAIRQHAPPENED(r13)
954958

955959
.if \full_mask == 1

arch/powerpc/kernel/exceptions-64s.S

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1498,7 +1498,10 @@ masked_##_H##interrupt: \
14981498
mfspr r10,SPRN_##_H##SRR1; \
14991499
xori r10,r10,MSR_EE; /* clear MSR_EE */ \
15001500
mtspr SPRN_##_H##SRR1,r10; \
1501-
2: mtcrf 0x80,r9; \
1501+
ori r11,r11,PACA_IRQ_HARD_DIS; \
1502+
stb r11,PACAIRQHAPPENED(r13); \
1503+
2: /* done */ \
1504+
mtcrf 0x80,r9; \
15021505
std r1,PACAR1(r13); \
15031506
ld r9,PACA_EXGEN+EX_R9(r13); \
15041507
ld r10,PACA_EXGEN+EX_R10(r13); \

arch/powerpc/kernel/idle_book3e.S

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ _GLOBAL(\name)
3636
*/
3737
lbz r3,PACAIRQHAPPENED(r13)
3838
cmpwi cr0,r3,0
39-
bnelr
39+
bne 2f
4040

4141
/* Now we are going to mark ourselves as soft and hard enabled in
4242
* order to be able to take interrupts while asleep. We inform lockdep
@@ -72,6 +72,11 @@ _GLOBAL(\name)
7272
wrteei 1
7373
\loop
7474

75+
2:
76+
lbz r10,PACAIRQHAPPENED(r13)
77+
ori r10,r10,PACA_IRQ_HARD_DIS
78+
stb r10,PACAIRQHAPPENED(r13)
79+
blr
7580
.endm
7681

7782
.macro BOOK3E_IDLE_LOOP

arch/powerpc/kernel/irq.c

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,20 @@ notrace unsigned int __check_irq_replay(void)
145145
trace_hardirqs_on();
146146
trace_hardirqs_off();
147147

148+
/*
149+
* We are always hard disabled here, but PACA_IRQ_HARD_DIS may
150+
* not be set, which means interrupts have only just been hard
151+
* disabled as part of the local_irq_restore or interrupt return
152+
* code. In that case, skip the decrementr check becaus it's
153+
* expensive to read the TB.
154+
*
155+
* HARD_DIS then gets cleared here, but it's reconciled later.
156+
* Either local_irq_disable will replay the interrupt and that
157+
* will reconcile state like other hard interrupts. Or interrupt
158+
* retur will replay the interrupt and in that case it sets
159+
* PACA_IRQ_HARD_DIS by hand (see comments in entry_64.S).
160+
*/
148161
if (happened & PACA_IRQ_HARD_DIS) {
149-
/* Clear bit 0 which we wouldn't clear otherwise */
150162
local_paca->irq_happened &= ~PACA_IRQ_HARD_DIS;
151163

152164
/*
@@ -248,24 +260,26 @@ notrace void arch_local_irq_restore(unsigned long mask)
248260
* cannot have preempted.
249261
*/
250262
irq_happened = get_irq_happened();
251-
if (!irq_happened)
263+
if (!irq_happened) {
264+
#ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG
265+
WARN_ON(!(mfmsr() & MSR_EE));
266+
#endif
252267
return;
268+
}
253269

254270
/*
255271
* We need to hard disable to get a trusted value from
256272
* __check_irq_replay(). We also need to soft-disable
257273
* again to avoid warnings in there due to the use of
258274
* per-cpu variables.
259-
*
260-
* We know that if the value in irq_happened is exactly 0x01
261-
* then we are already hard disabled (there are other less
262-
* common cases that we'll ignore for now), so we skip the
263-
* (expensive) mtmsrd.
264275
*/
265-
if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
276+
if (!(irq_happened & PACA_IRQ_HARD_DIS)) {
277+
#ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG
278+
WARN_ON(!(mfmsr() & MSR_EE));
279+
#endif
266280
__hard_irq_disable();
267281
#ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG
268-
else {
282+
} else {
269283
/*
270284
* We should already be hard disabled here. We had bugs
271285
* where that wasn't the case so let's dbl check it and
@@ -274,8 +288,8 @@ notrace void arch_local_irq_restore(unsigned long mask)
274288
*/
275289
if (WARN_ON(mfmsr() & MSR_EE))
276290
__hard_irq_disable();
277-
}
278291
#endif
292+
}
279293

280294
irq_soft_mask_set(IRQS_ALL_DISABLED);
281295
trace_hardirqs_off();

0 commit comments

Comments
 (0)