Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Nicholas Piggin and committed by
Michael Ellerman
9b81c021 29e8131c

+52 -16
+6 -4
arch/powerpc/include/asm/hw_irq.h
··· 253 253 254 254 /* 255 255 * 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. 258 259 */ 259 260 static inline void may_hard_irq_enable(void) 260 261 { 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; 263 264 __hard_irq_enable(); 265 + } 264 266 } 265 267 266 268 static inline bool arch_irq_disabled_regs(struct pt_regs *regs)
+8
arch/powerpc/kernel/entry_64.S
··· 993 993 std r4,_TRAP(r1) 994 994 995 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 + 1003 + /* 996 1004 * Then find the right handler and call it. Interrupts are 997 1005 * still soft-disabled and we keep them that way. 998 1006 */
+4
arch/powerpc/kernel/exceptions-64e.S
··· 949 949 950 950 .macro masked_interrupt_book3e paca_irq full_mask 951 951 lbz r10,PACAIRQHAPPENED(r13) 952 + .if \full_mask == 1 953 + ori r10,r10,\paca_irq | PACA_IRQ_HARD_DIS 954 + .else 952 955 ori r10,r10,\paca_irq 956 + .endif 953 957 stb r10,PACAIRQHAPPENED(r13) 954 958 955 959 .if \full_mask == 1
+4 -1
arch/powerpc/kernel/exceptions-64s.S
··· 1498 1498 mfspr r10,SPRN_##_H##SRR1; \ 1499 1499 xori r10,r10,MSR_EE; /* clear MSR_EE */ \ 1500 1500 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; \ 1502 1505 std r1,PACAR1(r13); \ 1503 1506 ld r9,PACA_EXGEN+EX_R9(r13); \ 1504 1507 ld r10,PACA_EXGEN+EX_R10(r13); \
+6 -1
arch/powerpc/kernel/idle_book3e.S
··· 36 36 */ 37 37 lbz r3,PACAIRQHAPPENED(r13) 38 38 cmpwi cr0,r3,0 39 - bnelr 39 + bne 2f 40 40 41 41 /* Now we are going to mark ourselves as soft and hard enabled in 42 42 * order to be able to take interrupts while asleep. We inform lockdep ··· 72 72 wrteei 1 73 73 \loop 74 74 75 + 2: 76 + lbz r10,PACAIRQHAPPENED(r13) 77 + ori r10,r10,PACA_IRQ_HARD_DIS 78 + stb r10,PACAIRQHAPPENED(r13) 79 + blr 75 80 .endm 76 81 77 82 .macro BOOK3E_IDLE_LOOP
+24 -10
arch/powerpc/kernel/irq.c
··· 145 145 trace_hardirqs_on(); 146 146 trace_hardirqs_off(); 147 147 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 + */ 148 161 if (happened & PACA_IRQ_HARD_DIS) { 149 - /* Clear bit 0 which we wouldn't clear otherwise */ 150 162 local_paca->irq_happened &= ~PACA_IRQ_HARD_DIS; 151 163 152 164 /* ··· 260 248 * cannot have preempted. 261 249 */ 262 250 irq_happened = get_irq_happened(); 263 - if (!irq_happened) 251 + if (!irq_happened) { 252 + #ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG 253 + WARN_ON(!(mfmsr() & MSR_EE)); 254 + #endif 264 255 return; 256 + } 265 257 266 258 /* 267 259 * We need to hard disable to get a trusted value from 268 260 * __check_irq_replay(). We also need to soft-disable 269 261 * again to avoid warnings in there due to the use of 270 262 * per-cpu variables. 271 - * 272 - * We know that if the value in irq_happened is exactly 0x01 273 - * then we are already hard disabled (there are other less 274 - * common cases that we'll ignore for now), so we skip the 275 - * (expensive) mtmsrd. 276 263 */ 277 - if (unlikely(irq_happened != PACA_IRQ_HARD_DIS)) 264 + if (!(irq_happened & PACA_IRQ_HARD_DIS)) { 265 + #ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG 266 + WARN_ON(!(mfmsr() & MSR_EE)); 267 + #endif 278 268 __hard_irq_disable(); 279 269 #ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG 280 - else { 270 + } else { 281 271 /* 282 272 * We should already be hard disabled here. We had bugs 283 273 * where that wasn't the case so let's dbl check it and ··· 288 274 */ 289 275 if (WARN_ON(mfmsr() & MSR_EE)) 290 276 __hard_irq_disable(); 291 - } 292 277 #endif 278 + } 293 279 294 280 irq_soft_mask_set(IRQS_ALL_DISABLED); 295 281 trace_hardirqs_off();