浏览代码

sh: Fix occasional FPU register corruption under preempt.

Presently with preempt enabled there's the possibility to be preempted
after the TIF_USEDFPU test and the register save, leading to bogus
state post-__switch_to(). Use an explicit preempt_disable()/enable()
pair around unlazy_fpu()/clear_fpu() to avoid this. Follows the x86
change.

Reported-by: Takuo Koguchi <takuo.koguchi.sw@hitachi.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Paul Mundt 17 年之前
父节点
当前提交
9bbafce2ee

+ 1 - 0
arch/sh/kernel/cpu/sh2a/fpu.c

@@ -13,6 +13,7 @@
 #include <linux/signal.h>
 #include <linux/signal.h>
 #include <asm/processor.h>
 #include <asm/processor.h>
 #include <asm/io.h>
 #include <asm/io.h>
+#include <asm/fpu.h>
 
 
 /* The PR (precision) bit in the FP Status Register must be clear when
 /* The PR (precision) bit in the FP Status Register must be clear when
  * an frchg instruction is executed, otherwise the instruction is undefined.
  * an frchg instruction is executed, otherwise the instruction is undefined.

+ 1 - 0
arch/sh/kernel/cpu/sh4/fpu.c

@@ -16,6 +16,7 @@
 #include <asm/cpu/fpu.h>
 #include <asm/cpu/fpu.h>
 #include <asm/processor.h>
 #include <asm/processor.h>
 #include <asm/system.h>
 #include <asm/system.h>
+#include <asm/fpu.h>
 
 
 /* The PR (precision) bit in the FP Status Register must be clear when
 /* The PR (precision) bit in the FP Status Register must be clear when
  * an frchg instruction is executed, otherwise the instruction is undefined.
  * an frchg instruction is executed, otherwise the instruction is undefined.

+ 1 - 0
arch/sh/kernel/cpu/sh5/fpu.c

@@ -17,6 +17,7 @@
 #include <asm/processor.h>
 #include <asm/processor.h>
 #include <asm/user.h>
 #include <asm/user.h>
 #include <asm/io.h>
 #include <asm/io.h>
+#include <asm/fpu.h>
 
 
 /*
 /*
  * Initially load the FPU with signalling NANS.  This bit pattern
  * Initially load the FPU with signalling NANS.  This bit pattern

+ 1 - 0
arch/sh/kernel/dump_task.c

@@ -1,5 +1,6 @@
 #include <linux/elfcore.h>
 #include <linux/elfcore.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
+#include <asm/fpu.h>
 
 
 /*
 /*
  * Capture the user space registers if the task is not running (in user space)
  * Capture the user space registers if the task is not running (in user space)

+ 1 - 0
arch/sh/kernel/process_32.c

@@ -25,6 +25,7 @@
 #include <asm/pgalloc.h>
 #include <asm/pgalloc.h>
 #include <asm/system.h>
 #include <asm/system.h>
 #include <asm/ubc.h>
 #include <asm/ubc.h>
+#include <asm/fpu.h>
 
 
 static int hlt_counter;
 static int hlt_counter;
 int ubc_usercnt = 0;
 int ubc_usercnt = 0;

+ 1 - 0
arch/sh/kernel/signal_32.c

@@ -29,6 +29,7 @@
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/pgtable.h>
 #include <asm/cacheflush.h>
 #include <asm/cacheflush.h>
+#include <asm/fpu.h>
 
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 

+ 18 - 14
include/asm-sh/fpu.h

@@ -1,9 +1,8 @@
 #ifndef __ASM_SH_FPU_H
 #ifndef __ASM_SH_FPU_H
 #define __ASM_SH_FPU_H
 #define __ASM_SH_FPU_H
 
 
-#define SR_FD    0x00008000
-
 #ifndef __ASSEMBLY__
 #ifndef __ASSEMBLY__
+#include <linux/preempt.h>
 #include <asm/ptrace.h>
 #include <asm/ptrace.h>
 
 
 #ifdef CONFIG_SH_FPU
 #ifdef CONFIG_SH_FPU
@@ -28,18 +27,23 @@ extern void save_fpu(struct task_struct *__tsk, struct pt_regs *regs);
 
 
 extern int do_fpu_inst(unsigned short, struct pt_regs *);
 extern int do_fpu_inst(unsigned short, struct pt_regs *);
 
 
-#define unlazy_fpu(tsk, regs) do {			\
-	if (test_tsk_thread_flag(tsk, TIF_USEDFPU)) {	\
-		save_fpu(tsk, regs);			\
-	}						\
-} while (0)
-
-#define clear_fpu(tsk, regs) do {				\
-	if (test_tsk_thread_flag(tsk, TIF_USEDFPU)) {		\
-		clear_tsk_thread_flag(tsk, TIF_USEDFPU);	\
-		release_fpu(regs);				\
-	}							\
-} while (0)
+static inline void unlazy_fpu(struct task_struct *tsk, struct pt_regs *regs)
+{
+	preempt_disable();
+	if (test_tsk_thread_flag(tsk, TIF_USEDFPU))
+		save_fpu(tsk, regs);
+	preempt_enable();
+}
+
+static inline void clear_fpu(struct task_struct *tsk, struct pt_regs *regs)
+{
+	preempt_disable();
+	if (test_tsk_thread_flag(tsk, TIF_USEDFPU)) {
+		clear_tsk_thread_flag(tsk, TIF_USEDFPU);
+		release_fpu(regs);
+	}
+	preempt_enable();
+}
 
 
 #endif /* __ASSEMBLY__ */
 #endif /* __ASSEMBLY__ */
 
 

+ 0 - 1
include/asm-sh/processor.h

@@ -2,7 +2,6 @@
 #define __ASM_SH_PROCESSOR_H
 #define __ASM_SH_PROCESSOR_H
 
 
 #include <asm/cpu-features.h>
 #include <asm/cpu-features.h>
-#include <asm/fpu.h>
 
 
 #ifndef __ASSEMBLY__
 #ifndef __ASSEMBLY__
 /*
 /*

+ 1 - 0
include/asm-sh/processor_32.h

@@ -70,6 +70,7 @@ extern struct sh_cpuinfo cpu_data[];
  */
  */
 #define SR_DSP		0x00001000
 #define SR_DSP		0x00001000
 #define SR_IMASK	0x000000f0
 #define SR_IMASK	0x000000f0
+#define SR_FD		0x00008000
 
 
 /*
 /*
  * FPU structure and data
  * FPU structure and data

+ 1 - 0
include/asm-sh/processor_64.h

@@ -112,6 +112,7 @@ extern struct sh_cpuinfo cpu_data[];
 #endif
 #endif
 
 
 #define SR_IMASK 0x000000f0
 #define SR_IMASK 0x000000f0
+#define SR_FD    0x00008000
 #define SR_SSTEP 0x08000000
 #define SR_SSTEP 0x08000000
 
 
 #ifndef __ASSEMBLY__
 #ifndef __ASSEMBLY__