Skip to content

Commit 5665112

Browse files
DispatchCodetsbogend
authored andcommitted
MIPS: Fix idle VS timer enqueue
MIPS re-enables interrupts on its idle routine and performs a TIF_NEED_RESCHED check afterwards before putting the CPU to sleep. The IRQs firing between the check and the 'wait' instruction may set the TIF_NEED_RESCHED flag. In order to deal with this possible race, IRQs interrupting __r4k_wait() rollback their return address to the beginning of __r4k_wait() so that TIF_NEED_RESCHED is checked again before going back to sleep. However idle IRQs can also queue timers that may require a tick reprogramming through a new generic idle loop iteration but those timers would go unnoticed here because __r4k_wait() only checks TIF_NEED_RESCHED. It doesn't check for pending timers. Fix this with fast-forwarding idle IRQs return address to the end of the idle routine instead of the beginning, so that the generic idle loop handles both TIF_NEED_RESCHED and pending timers. CONFIG_CPU_MICROMIPS has been removed along with the nop instructions. There, NOPs are 2 byte in size, so change the code with 3 _ssnop which are always 4 byte and remove the ifdef. Added ehb to make sure the hazard is always cleared. Fixes: c65a548 ("[MIPS] Fix potential latency problem due to non-atomic cpu_wait.") Signed-off-by: Marco Crivellari <marco.crivellari@suse.com> Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk> Acked-by: Frederic Weisbecker <frederic@kernel.org> Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
1 parent 0af2f6b commit 5665112

File tree

3 files changed

+37
-35
lines changed

3 files changed

+37
-35
lines changed

arch/mips/include/asm/idle.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
#include <linux/linkage.h>
77

88
extern void (*cpu_wait)(void);
9-
extern void r4k_wait(void);
10-
extern asmlinkage void __r4k_wait(void);
9+
extern asmlinkage void r4k_wait(void);
1110
extern void r4k_wait_irqoff(void);
1211

1312
static inline int using_rollback_handler(void)

arch/mips/kernel/genex.S

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -104,42 +104,52 @@ handle_vcei:
104104

105105
__FINIT
106106

107-
.align 5 /* 32 byte rollback region */
108-
LEAF(__r4k_wait)
109-
.set push
110-
.set noreorder
111-
/* start of rollback region */
112-
LONG_L t0, TI_FLAGS($28)
113-
nop
114-
andi t0, _TIF_NEED_RESCHED
115-
bnez t0, 1f
116-
nop
117-
nop
118-
nop
119-
#ifdef CONFIG_CPU_MICROMIPS
120-
nop
121-
nop
122-
nop
123-
nop
124-
#endif
107+
/* Align to 32 bytes for the maximum idle interrupt region size. */
108+
.align 5
109+
LEAF(r4k_wait)
110+
/* Keep the ISA bit clear for calculations on local labels here. */
111+
0: .fill 0
112+
/* Start of idle interrupt region. */
113+
local_irq_enable
114+
/*
115+
* If an interrupt lands here, before going idle on the next
116+
* instruction, we must *NOT* go idle since the interrupt could
117+
* have set TIF_NEED_RESCHED or caused a timer to need resched.
118+
* Fall through -- see rollback_handler below -- and have the
119+
* idle loop take care of things.
120+
*/
121+
1: .fill 0
122+
/* The R2 EI/EHB sequence takes 8 bytes, otherwise pad up. */
123+
.if 1b - 0b > 32
124+
.error "overlong idle interrupt region"
125+
.elseif 1b - 0b > 8
126+
.align 4
127+
.endif
128+
2: .fill 0
129+
.equ r4k_wait_idle_size, 2b - 0b
130+
/* End of idle interrupt region; size has to be a power of 2. */
125131
.set MIPS_ISA_ARCH_LEVEL_RAW
132+
r4k_wait_insn:
126133
wait
127-
/* end of rollback region (the region size must be power of two) */
128-
1:
134+
r4k_wait_exit:
135+
.set mips0
136+
local_irq_disable
129137
jr ra
130-
nop
131-
.set pop
132-
END(__r4k_wait)
138+
END(r4k_wait)
139+
.previous
133140

134141
.macro BUILD_ROLLBACK_PROLOGUE handler
135142
FEXPORT(rollback_\handler)
136143
.set push
137144
.set noat
138145
MFC0 k0, CP0_EPC
139-
PTR_LA k1, __r4k_wait
140-
ori k0, 0x1f /* 32 byte rollback region */
141-
xori k0, 0x1f
146+
/* Subtract/add 2 to let the ISA bit propagate through the mask. */
147+
PTR_LA k1, r4k_wait_insn - 2
148+
ori k0, r4k_wait_idle_size - 2
149+
.set noreorder
142150
bne k0, k1, \handler
151+
PTR_ADDIU k0, r4k_wait_exit - r4k_wait_insn + 2
152+
.set reorder
143153
MTC0 k0, CP0_EPC
144154
.set pop
145155
.endm

arch/mips/kernel/idle.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,6 @@ static void __cpuidle r3081_wait(void)
3535
write_c0_conf(cfg | R30XX_CONF_HALT);
3636
}
3737

38-
void __cpuidle r4k_wait(void)
39-
{
40-
raw_local_irq_enable();
41-
__r4k_wait();
42-
raw_local_irq_disable();
43-
}
44-
4538
/*
4639
* This variant is preferable as it allows testing need_resched and going to
4740
* sleep depending on the outcome atomically. Unfortunately the "It is

0 commit comments

Comments
 (0)