Skip to content

Commit c65a548

Browse files
atsushi-nemotoralfbaechle
authored andcommitted
[MIPS] Fix potential latency problem due to non-atomic cpu_wait.
If an interrupt happened between checking of NEED_RESCHED and WAIT instruction, adjust EPC to restart from checking of NEED_RESCHED. Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
1 parent 6657fe0 commit c65a548

File tree

3 files changed

+55
-20
lines changed

3 files changed

+55
-20
lines changed

arch/mips/kernel/cpu-probe.c

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,7 @@ static void r39xx_wait(void)
4545
local_irq_enable();
4646
}
4747

48-
/*
49-
* There is a race when WAIT instruction executed with interrupt
50-
* enabled.
51-
* But it is implementation-dependent wheter the pipelie restarts when
52-
* a non-enabled interrupt is requested.
53-
*/
54-
static void r4k_wait(void)
55-
{
56-
__asm__(" .set mips3 \n"
57-
" wait \n"
58-
" .set mips0 \n");
59-
}
48+
extern void r4k_wait(void);
6049

6150
/*
6251
* This variant is preferable as it allows testing need_resched and going to
@@ -128,7 +117,7 @@ static int __init wait_disable(char *s)
128117

129118
__setup("nowait", wait_disable);
130119

131-
static inline void check_wait(void)
120+
void __init check_wait(void)
132121
{
133122
struct cpuinfo_mips *c = &current_cpu_data;
134123

@@ -242,7 +231,6 @@ static inline void check_errata(void)
242231

243232
void __init check_bugs32(void)
244233
{
245-
check_wait();
246234
check_errata();
247235
}
248236

arch/mips/kernel/genex.S

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <asm/stackframe.h>
2121
#include <asm/war.h>
2222
#include <asm/page.h>
23+
#include <asm/thread_info.h>
2324

2425
#define PANIC_PIC(msg) \
2526
.set push; \
@@ -126,7 +127,42 @@ handle_vcei:
126127

127128
__FINIT
128129

130+
.align 5 /* 32 byte rollback region */
131+
LEAF(r4k_wait)
132+
.set push
133+
.set noreorder
134+
/* start of rollback region */
135+
LONG_L t0, TI_FLAGS($28)
136+
nop
137+
andi t0, _TIF_NEED_RESCHED
138+
bnez t0, 1f
139+
nop
140+
nop
141+
nop
142+
.set mips3
143+
wait
144+
/* end of rollback region (the region size must be power of two) */
145+
.set pop
146+
1:
147+
jr ra
148+
END(r4k_wait)
149+
150+
.macro BUILD_ROLLBACK_PROLOGUE handler
151+
FEXPORT(rollback_\handler)
152+
.set push
153+
.set noat
154+
MFC0 k0, CP0_EPC
155+
PTR_LA k1, r4k_wait
156+
ori k0, 0x1f /* 32 byte rollback region */
157+
xori k0, 0x1f
158+
bne k0, k1, 9f
159+
MTC0 k0, CP0_EPC
160+
9:
161+
.set pop
162+
.endm
163+
129164
.align 5
165+
BUILD_ROLLBACK_PROLOGUE handle_int
130166
NESTED(handle_int, PT_SIZE, sp)
131167
#ifdef CONFIG_TRACE_IRQFLAGS
132168
/*
@@ -201,6 +237,7 @@ NESTED(except_vec_ejtag_debug, 0, sp)
201237
* This prototype is copied to ebase + n*IntCtl.VS and patched
202238
* to invoke the handler
203239
*/
240+
BUILD_ROLLBACK_PROLOGUE except_vec_vi
204241
NESTED(except_vec_vi, 0, sp)
205242
SAVE_SOME
206243
SAVE_AT

arch/mips/kernel/traps.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
#include <asm/types.h>
4747
#include <asm/stacktrace.h>
4848

49+
extern void check_wait(void);
50+
extern asmlinkage void r4k_wait(void);
51+
extern asmlinkage void rollback_handle_int(void);
4952
extern asmlinkage void handle_int(void);
5053
extern asmlinkage void handle_tlbm(void);
5154
extern asmlinkage void handle_tlbl(void);
@@ -1251,18 +1254,21 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
12511254

12521255
extern char except_vec_vi, except_vec_vi_lui;
12531256
extern char except_vec_vi_ori, except_vec_vi_end;
1257+
extern char rollback_except_vec_vi;
1258+
char *vec_start = (cpu_wait == r4k_wait) ?
1259+
&rollback_except_vec_vi : &except_vec_vi;
12541260
#ifdef CONFIG_MIPS_MT_SMTC
12551261
/*
12561262
* We need to provide the SMTC vectored interrupt handler
12571263
* not only with the address of the handler, but with the
12581264
* Status.IM bit to be masked before going there.
12591265
*/
12601266
extern char except_vec_vi_mori;
1261-
const int mori_offset = &except_vec_vi_mori - &except_vec_vi;
1267+
const int mori_offset = &except_vec_vi_mori - vec_start;
12621268
#endif /* CONFIG_MIPS_MT_SMTC */
1263-
const int handler_len = &except_vec_vi_end - &except_vec_vi;
1264-
const int lui_offset = &except_vec_vi_lui - &except_vec_vi;
1265-
const int ori_offset = &except_vec_vi_ori - &except_vec_vi;
1269+
const int handler_len = &except_vec_vi_end - vec_start;
1270+
const int lui_offset = &except_vec_vi_lui - vec_start;
1271+
const int ori_offset = &except_vec_vi_ori - vec_start;
12661272

12671273
if (handler_len > VECTORSPACING) {
12681274
/*
@@ -1272,7 +1278,7 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
12721278
panic("VECTORSPACING too small");
12731279
}
12741280

1275-
memcpy(b, &except_vec_vi, handler_len);
1281+
memcpy(b, vec_start, handler_len);
12761282
#ifdef CONFIG_MIPS_MT_SMTC
12771283
BUG_ON(n > 7); /* Vector index %d exceeds SMTC maximum. */
12781284

@@ -1554,6 +1560,10 @@ void __init trap_init(void)
15541560
extern char except_vec3_generic, except_vec3_r4000;
15551561
extern char except_vec4;
15561562
unsigned long i;
1563+
int rollback;
1564+
1565+
check_wait();
1566+
rollback = (cpu_wait == r4k_wait);
15571567

15581568
#if defined(CONFIG_KGDB)
15591569
if (kgdb_early_setup)
@@ -1618,7 +1628,7 @@ void __init trap_init(void)
16181628
if (board_be_init)
16191629
board_be_init();
16201630

1621-
set_except_vector(0, handle_int);
1631+
set_except_vector(0, rollback ? rollback_handle_int : handle_int);
16221632
set_except_vector(1, handle_tlbm);
16231633
set_except_vector(2, handle_tlbl);
16241634
set_except_vector(3, handle_tlbs);

0 commit comments

Comments
 (0)