aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips
diff options
context:
space:
mode:
authorAtsushi Nemoto <anemo@mba.ocn.ne.jp>2007-03-09 11:07:45 -0500
committerRalf Baechle <ralf@linux-mips.org>2007-03-16 21:03:26 -0400
commit53dc80287da43b75df2fe2658651d3c5160dad8e (patch)
tree3c4c97534c379709cd2a1dae5b90df626349f21d /arch/mips
parentc6a2f4679331206ef5d353fc9a6cda2fa4aef8c6 (diff)
[MIPS] FPU ownership management & preemption fixes
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips')
-rw-r--r--arch/mips/kernel/r2300_switch.S10
-rw-r--r--arch/mips/kernel/r4k_switch.S10
-rw-r--r--arch/mips/kernel/signal.c29
-rw-r--r--arch/mips/kernel/signal32.c29
-rw-r--r--arch/mips/kernel/traps.c84
-rw-r--r--arch/mips/math-emu/kernel_linkage.c8
6 files changed, 77 insertions, 93 deletions
diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S
index 656bde2e11b1..28c2e2e6af73 100644
--- a/arch/mips/kernel/r2300_switch.S
+++ b/arch/mips/kernel/r2300_switch.S
@@ -49,8 +49,7 @@ LEAF(resume)
49#ifndef CONFIG_CPU_HAS_LLSC 49#ifndef CONFIG_CPU_HAS_LLSC
50 sw zero, ll_bit 50 sw zero, ll_bit
51#endif 51#endif
52 mfc0 t1, CP0_STATUS 52 mfc0 t2, CP0_STATUS
53 sw t1, THREAD_STATUS(a0)
54 cpu_save_nonscratch a0 53 cpu_save_nonscratch a0
55 sw ra, THREAD_REG31(a0) 54 sw ra, THREAD_REG31(a0)
56 55
@@ -60,8 +59,8 @@ LEAF(resume)
60 lw t3, TASK_THREAD_INFO(a0) 59 lw t3, TASK_THREAD_INFO(a0)
61 lw t0, TI_FLAGS(t3) 60 lw t0, TI_FLAGS(t3)
62 li t1, _TIF_USEDFPU 61 li t1, _TIF_USEDFPU
63 and t2, t0, t1 62 and t1, t0
64 beqz t2, 1f 63 beqz t1, 1f
65 nor t1, zero, t1 64 nor t1, zero, t1
66 65
67 and t0, t0, t1 66 and t0, t0, t1
@@ -74,10 +73,13 @@ LEAF(resume)
74 li t1, ~ST0_CU1 73 li t1, ~ST0_CU1
75 and t0, t0, t1 74 and t0, t0, t1
76 sw t0, ST_OFF(t3) 75 sw t0, ST_OFF(t3)
76 /* clear thread_struct CU1 bit */
77 and t2, t1
77 78
78 fpu_save_single a0, t0 # clobbers t0 79 fpu_save_single a0, t0 # clobbers t0
79 80
801: 811:
82 sw t2, THREAD_STATUS(a0)
81 /* 83 /*
82 * The order of restoring the registers takes care of the race 84 * The order of restoring the registers takes care of the race
83 * updating $28, $29 and kernelsp without disabling ints. 85 * updating $28, $29 and kernelsp without disabling ints.
diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S
index cc566cf12246..c7698fd9955c 100644
--- a/arch/mips/kernel/r4k_switch.S
+++ b/arch/mips/kernel/r4k_switch.S
@@ -48,8 +48,7 @@
48#ifndef CONFIG_CPU_HAS_LLSC 48#ifndef CONFIG_CPU_HAS_LLSC
49 sw zero, ll_bit 49 sw zero, ll_bit
50#endif 50#endif
51 mfc0 t1, CP0_STATUS 51 mfc0 t2, CP0_STATUS
52 LONG_S t1, THREAD_STATUS(a0)
53 cpu_save_nonscratch a0 52 cpu_save_nonscratch a0
54 LONG_S ra, THREAD_REG31(a0) 53 LONG_S ra, THREAD_REG31(a0)
55 54
@@ -59,8 +58,8 @@
59 PTR_L t3, TASK_THREAD_INFO(a0) 58 PTR_L t3, TASK_THREAD_INFO(a0)
60 LONG_L t0, TI_FLAGS(t3) 59 LONG_L t0, TI_FLAGS(t3)
61 li t1, _TIF_USEDFPU 60 li t1, _TIF_USEDFPU
62 and t2, t0, t1 61 and t1, t0
63 beqz t2, 1f 62 beqz t1, 1f
64 nor t1, zero, t1 63 nor t1, zero, t1
65 64
66 and t0, t0, t1 65 and t0, t0, t1
@@ -73,10 +72,13 @@
73 li t1, ~ST0_CU1 72 li t1, ~ST0_CU1
74 and t0, t0, t1 73 and t0, t0, t1
75 LONG_S t0, ST_OFF(t3) 74 LONG_S t0, ST_OFF(t3)
75 /* clear thread_struct CU1 bit */
76 and t2, t1
76 77
77 fpu_save_double a0 t0 t1 # c0_status passed in t0 78 fpu_save_double a0 t0 t1 # c0_status passed in t0
78 # clobbers t1 79 # clobbers t1
791: 801:
81 LONG_S t2, THREAD_STATUS(a0)
80 82
81 /* 83 /*
82 * The order of restoring the registers takes care of the race 84 * The order of restoring the registers takes care of the race
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c
index bf094fc4c7eb..8c3c5a5789b0 100644
--- a/arch/mips/kernel/signal.c
+++ b/arch/mips/kernel/signal.c
@@ -82,6 +82,7 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
82{ 82{
83 int err = 0; 83 int err = 0;
84 int i; 84 int i;
85 unsigned int used_math;
85 86
86 err |= __put_user(regs->cp0_epc, &sc->sc_pc); 87 err |= __put_user(regs->cp0_epc, &sc->sc_pc);
87 88
@@ -104,22 +105,18 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
104 err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); 105 err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
105 } 106 }
106 107
107 err |= __put_user(!!used_math(), &sc->sc_used_math); 108 used_math = !!used_math();
109 err |= __put_user(used_math, &sc->sc_used_math);
108 110
109 if (used_math()) { 111 if (used_math) {
110 /* 112 /*
111 * Save FPU state to signal context. Signal handler 113 * Save FPU state to signal context. Signal handler
112 * will "inherit" current FPU state. 114 * will "inherit" current FPU state.
113 */ 115 */
114 preempt_disable(); 116 own_fpu(1);
115 117 enable_fp_in_kernel();
116 if (!is_fpu_owner()) {
117 own_fpu();
118 restore_fp(current);
119 }
120 err |= save_fp_context(sc); 118 err |= save_fp_context(sc);
121 119 disable_fp_in_kernel();
122 preempt_enable();
123 } 120 }
124 return err; 121 return err;
125} 122}
@@ -188,20 +185,18 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
188 err |= __get_user(used_math, &sc->sc_used_math); 185 err |= __get_user(used_math, &sc->sc_used_math);
189 conditional_used_math(used_math); 186 conditional_used_math(used_math);
190 187
191 preempt_disable(); 188 if (used_math) {
192
193 if (used_math()) {
194 /* restore fpu context if we have used it before */ 189 /* restore fpu context if we have used it before */
195 own_fpu(); 190 own_fpu(0);
191 enable_fp_in_kernel();
196 if (!err) 192 if (!err)
197 err = check_and_restore_fp_context(sc); 193 err = check_and_restore_fp_context(sc);
194 disable_fp_in_kernel();
198 } else { 195 } else {
199 /* signal handler may have used FPU. Give it up. */ 196 /* signal handler may have used FPU. Give it up. */
200 lose_fpu(); 197 lose_fpu(0);
201 } 198 }
202 199
203 preempt_enable();
204
205 return err; 200 return err;
206} 201}
207 202
diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c
index 20013b6fe725..151fd2f0893a 100644
--- a/arch/mips/kernel/signal32.c
+++ b/arch/mips/kernel/signal32.c
@@ -181,6 +181,7 @@ static int setup_sigcontext32(struct pt_regs *regs,
181{ 181{
182 int err = 0; 182 int err = 0;
183 int i; 183 int i;
184 u32 used_math;
184 185
185 err |= __put_user(regs->cp0_epc, &sc->sc_pc); 186 err |= __put_user(regs->cp0_epc, &sc->sc_pc);
186 187
@@ -200,22 +201,18 @@ static int setup_sigcontext32(struct pt_regs *regs,
200 err |= __put_user(mflo3(), &sc->sc_lo3); 201 err |= __put_user(mflo3(), &sc->sc_lo3);
201 } 202 }
202 203
203 err |= __put_user(!!used_math(), &sc->sc_used_math); 204 used_math = !!used_math();
205 err |= __put_user(used_math, &sc->sc_used_math);
204 206
205 if (used_math()) { 207 if (used_math) {
206 /* 208 /*
207 * Save FPU state to signal context. Signal handler 209 * Save FPU state to signal context. Signal handler
208 * will "inherit" current FPU state. 210 * will "inherit" current FPU state.
209 */ 211 */
210 preempt_disable(); 212 own_fpu(1);
211 213 enable_fp_in_kernel();
212 if (!is_fpu_owner()) {
213 own_fpu();
214 restore_fp(current);
215 }
216 err |= save_fp_context32(sc); 214 err |= save_fp_context32(sc);
217 215 disable_fp_in_kernel();
218 preempt_enable();
219 } 216 }
220 return err; 217 return err;
221} 218}
@@ -262,20 +259,18 @@ static int restore_sigcontext32(struct pt_regs *regs,
262 err |= __get_user(used_math, &sc->sc_used_math); 259 err |= __get_user(used_math, &sc->sc_used_math);
263 conditional_used_math(used_math); 260 conditional_used_math(used_math);
264 261
265 preempt_disable(); 262 if (used_math) {
266
267 if (used_math()) {
268 /* restore fpu context if we have used it before */ 263 /* restore fpu context if we have used it before */
269 own_fpu(); 264 own_fpu(0);
265 enable_fp_in_kernel();
270 if (!err) 266 if (!err)
271 err = check_and_restore_fp_context32(sc); 267 err = check_and_restore_fp_context32(sc);
268 disable_fp_in_kernel();
272 } else { 269 } else {
273 /* signal handler may have used FPU. Give it up. */ 270 /* signal handler may have used FPU. Give it up. */
274 lose_fpu(); 271 lose_fpu(0);
275 } 272 }
276 273
277 preempt_enable();
278
279 return err; 274 return err;
280} 275}
281 276
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 18f56a9dbcfa..7d76a85422b2 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -610,16 +610,6 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
610 if (fcr31 & FPU_CSR_UNI_X) { 610 if (fcr31 & FPU_CSR_UNI_X) {
611 int sig; 611 int sig;
612 612
613 preempt_disable();
614
615#ifdef CONFIG_PREEMPT
616 if (!is_fpu_owner()) {
617 /* We might lose fpu before disabling preempt... */
618 own_fpu();
619 BUG_ON(!used_math());
620 restore_fp(current);
621 }
622#endif
623 /* 613 /*
624 * Unimplemented operation exception. If we've got the full 614 * Unimplemented operation exception. If we've got the full
625 * software emulator on-board, let's use it... 615 * software emulator on-board, let's use it...
@@ -630,18 +620,12 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
630 * register operands before invoking the emulator, which seems 620 * register operands before invoking the emulator, which seems
631 * a bit extreme for what should be an infrequent event. 621 * a bit extreme for what should be an infrequent event.
632 */ 622 */
633 save_fp(current);
634 /* Ensure 'resume' not overwrite saved fp context again. */ 623 /* Ensure 'resume' not overwrite saved fp context again. */
635 lose_fpu(); 624 lose_fpu(1);
636
637 preempt_enable();
638 625
639 /* Run the emulator */ 626 /* Run the emulator */
640 sig = fpu_emulator_cop1Handler (regs, &current->thread.fpu, 1); 627 sig = fpu_emulator_cop1Handler (regs, &current->thread.fpu, 1);
641 628
642 preempt_disable();
643
644 own_fpu(); /* Using the FPU again. */
645 /* 629 /*
646 * We can't allow the emulated instruction to leave any of 630 * We can't allow the emulated instruction to leave any of
647 * the cause bit set in $fcr31. 631 * the cause bit set in $fcr31.
@@ -649,9 +633,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
649 current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; 633 current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
650 634
651 /* Restore the hardware register state */ 635 /* Restore the hardware register state */
652 restore_fp(current); 636 own_fpu(1); /* Using the FPU again. */
653
654 preempt_enable();
655 637
656 /* If something went wrong, signal */ 638 /* If something went wrong, signal */
657 if (sig) 639 if (sig)
@@ -775,12 +757,11 @@ asmlinkage void do_cpu(struct pt_regs *regs)
775{ 757{
776 unsigned int cpid; 758 unsigned int cpid;
777 759
778 die_if_kernel("do_cpu invoked from kernel context!", regs);
779
780 cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; 760 cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;
781 761
782 switch (cpid) { 762 switch (cpid) {
783 case 0: 763 case 0:
764 die_if_kernel("do_cpu invoked from kernel context!", regs);
784 if (!cpu_has_llsc) 765 if (!cpu_has_llsc)
785 if (!simulate_llsc(regs)) 766 if (!simulate_llsc(regs))
786 return; 767 return;
@@ -791,21 +772,30 @@ asmlinkage void do_cpu(struct pt_regs *regs)
791 break; 772 break;
792 773
793 case 1: 774 case 1:
794 preempt_disable(); 775 if (!test_thread_flag(TIF_ALLOW_FP_IN_KERNEL))
795 776 die_if_kernel("do_cpu invoked from kernel context!",
796 own_fpu(); 777 regs);
797 if (used_math()) { /* Using the FPU again. */ 778 if (used_math()) /* Using the FPU again. */
798 restore_fp(current); 779 own_fpu(1);
799 } else { /* First time FPU user. */ 780 else { /* First time FPU user. */
800 init_fpu(); 781 init_fpu();
801 set_used_math(); 782 set_used_math();
802 } 783 }
803 784
804 if (cpu_has_fpu) { 785 if (raw_cpu_has_fpu) {
805 preempt_enable(); 786 if (test_thread_flag(TIF_ALLOW_FP_IN_KERNEL)) {
787 local_irq_disable();
788 if (cpu_has_fpu)
789 regs->cp0_status |= ST0_CU1;
790 /*
791 * We must return without enabling
792 * interrupts to ensure keep FPU
793 * ownership until resume.
794 */
795 return;
796 }
806 } else { 797 } else {
807 int sig; 798 int sig;
808 preempt_enable();
809 sig = fpu_emulator_cop1Handler(regs, 799 sig = fpu_emulator_cop1Handler(regs,
810 &current->thread.fpu, 0); 800 &current->thread.fpu, 0);
811 if (sig) 801 if (sig)
@@ -1259,26 +1249,26 @@ static inline void mips_srs_init(void)
1259/* 1249/*
1260 * This is used by native signal handling 1250 * This is used by native signal handling
1261 */ 1251 */
1262asmlinkage int (*save_fp_context)(struct sigcontext *sc); 1252asmlinkage int (*save_fp_context)(struct sigcontext __user *sc);
1263asmlinkage int (*restore_fp_context)(struct sigcontext *sc); 1253asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc);
1264 1254
1265extern asmlinkage int _save_fp_context(struct sigcontext *sc); 1255extern asmlinkage int _save_fp_context(struct sigcontext __user *sc);
1266extern asmlinkage int _restore_fp_context(struct sigcontext *sc); 1256extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc);
1267 1257
1268extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc); 1258extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc);
1269extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc); 1259extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc);
1270 1260
1271#ifdef CONFIG_SMP 1261#ifdef CONFIG_SMP
1272static int smp_save_fp_context(struct sigcontext *sc) 1262static int smp_save_fp_context(struct sigcontext __user *sc)
1273{ 1263{
1274 return cpu_has_fpu 1264 return raw_cpu_has_fpu
1275 ? _save_fp_context(sc) 1265 ? _save_fp_context(sc)
1276 : fpu_emulator_save_context(sc); 1266 : fpu_emulator_save_context(sc);
1277} 1267}
1278 1268
1279static int smp_restore_fp_context(struct sigcontext *sc) 1269static int smp_restore_fp_context(struct sigcontext __user *sc)
1280{ 1270{
1281 return cpu_has_fpu 1271 return raw_cpu_has_fpu
1282 ? _restore_fp_context(sc) 1272 ? _restore_fp_context(sc)
1283 : fpu_emulator_restore_context(sc); 1273 : fpu_emulator_restore_context(sc);
1284} 1274}
@@ -1306,14 +1296,14 @@ static inline void signal_init(void)
1306/* 1296/*
1307 * This is used by 32-bit signal stuff on the 64-bit kernel 1297 * This is used by 32-bit signal stuff on the 64-bit kernel
1308 */ 1298 */
1309asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc); 1299asmlinkage int (*save_fp_context32)(struct sigcontext32 __user *sc);
1310asmlinkage int (*restore_fp_context32)(struct sigcontext32 *sc); 1300asmlinkage int (*restore_fp_context32)(struct sigcontext32 __user *sc);
1311 1301
1312extern asmlinkage int _save_fp_context32(struct sigcontext32 *sc); 1302extern asmlinkage int _save_fp_context32(struct sigcontext32 __user *sc);
1313extern asmlinkage int _restore_fp_context32(struct sigcontext32 *sc); 1303extern asmlinkage int _restore_fp_context32(struct sigcontext32 __user *sc);
1314 1304
1315extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 *sc); 1305extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 __user *sc);
1316extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 *sc); 1306extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 __user *sc);
1317 1307
1318static inline void signal32_init(void) 1308static inline void signal32_init(void)
1319{ 1309{
diff --git a/arch/mips/math-emu/kernel_linkage.c b/arch/mips/math-emu/kernel_linkage.c
index 5b3390f64917..ed49ef01ac53 100644
--- a/arch/mips/math-emu/kernel_linkage.c
+++ b/arch/mips/math-emu/kernel_linkage.c
@@ -51,7 +51,7 @@ void fpu_emulator_init_fpu(void)
51 * with appropriate macros from uaccess.h 51 * with appropriate macros from uaccess.h
52 */ 52 */
53 53
54int fpu_emulator_save_context(struct sigcontext *sc) 54int fpu_emulator_save_context(struct sigcontext __user *sc)
55{ 55{
56 int i; 56 int i;
57 int err = 0; 57 int err = 0;
@@ -65,7 +65,7 @@ int fpu_emulator_save_context(struct sigcontext *sc)
65 return err; 65 return err;
66} 66}
67 67
68int fpu_emulator_restore_context(struct sigcontext *sc) 68int fpu_emulator_restore_context(struct sigcontext __user *sc)
69{ 69{
70 int i; 70 int i;
71 int err = 0; 71 int err = 0;
@@ -84,7 +84,7 @@ int fpu_emulator_restore_context(struct sigcontext *sc)
84 * This is the o32 version 84 * This is the o32 version
85 */ 85 */
86 86
87int fpu_emulator_save_context32(struct sigcontext32 *sc) 87int fpu_emulator_save_context32(struct sigcontext32 __user *sc)
88{ 88{
89 int i; 89 int i;
90 int err = 0; 90 int err = 0;
@@ -98,7 +98,7 @@ int fpu_emulator_save_context32(struct sigcontext32 *sc)
98 return err; 98 return err;
99} 99}
100 100
101int fpu_emulator_restore_context32(struct sigcontext32 *sc) 101int fpu_emulator_restore_context32(struct sigcontext32 __user *sc)
102{ 102{
103 int i; 103 int i;
104 int err = 0; 104 int err = 0;