diff options
author | Atsushi Nemoto <anemo@mba.ocn.ne.jp> | 2007-03-09 11:07:45 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-03-16 21:03:26 -0400 |
commit | 53dc80287da43b75df2fe2658651d3c5160dad8e (patch) | |
tree | 3c4c97534c379709cd2a1dae5b90df626349f21d | |
parent | c6a2f4679331206ef5d353fc9a6cda2fa4aef8c6 (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>
-rw-r--r-- | arch/mips/kernel/r2300_switch.S | 10 | ||||
-rw-r--r-- | arch/mips/kernel/r4k_switch.S | 10 | ||||
-rw-r--r-- | arch/mips/kernel/signal.c | 29 | ||||
-rw-r--r-- | arch/mips/kernel/signal32.c | 29 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 84 | ||||
-rw-r--r-- | arch/mips/math-emu/kernel_linkage.c | 8 | ||||
-rw-r--r-- | include/asm-mips/cpu-features.h | 3 | ||||
-rw-r--r-- | include/asm-mips/cpu-info.h | 1 | ||||
-rw-r--r-- | include/asm-mips/fpu.h | 54 | ||||
-rw-r--r-- | include/asm-mips/thread_info.h | 1 |
10 files changed, 125 insertions, 104 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 | ||
80 | 1: | 81 | 1: |
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 |
79 | 1: | 80 | 1: |
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, ¤t->thread.fpu, 1); | 627 | sig = fpu_emulator_cop1Handler (regs, ¤t->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 | ¤t->thread.fpu, 0); | 800 | ¤t->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 | */ |
1262 | asmlinkage int (*save_fp_context)(struct sigcontext *sc); | 1252 | asmlinkage int (*save_fp_context)(struct sigcontext __user *sc); |
1263 | asmlinkage int (*restore_fp_context)(struct sigcontext *sc); | 1253 | asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc); |
1264 | 1254 | ||
1265 | extern asmlinkage int _save_fp_context(struct sigcontext *sc); | 1255 | extern asmlinkage int _save_fp_context(struct sigcontext __user *sc); |
1266 | extern asmlinkage int _restore_fp_context(struct sigcontext *sc); | 1256 | extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc); |
1267 | 1257 | ||
1268 | extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc); | 1258 | extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc); |
1269 | extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc); | 1259 | extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc); |
1270 | 1260 | ||
1271 | #ifdef CONFIG_SMP | 1261 | #ifdef CONFIG_SMP |
1272 | static int smp_save_fp_context(struct sigcontext *sc) | 1262 | static 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 | ||
1279 | static int smp_restore_fp_context(struct sigcontext *sc) | 1269 | static 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 | */ |
1309 | asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc); | 1299 | asmlinkage int (*save_fp_context32)(struct sigcontext32 __user *sc); |
1310 | asmlinkage int (*restore_fp_context32)(struct sigcontext32 *sc); | 1300 | asmlinkage int (*restore_fp_context32)(struct sigcontext32 __user *sc); |
1311 | 1301 | ||
1312 | extern asmlinkage int _save_fp_context32(struct sigcontext32 *sc); | 1302 | extern asmlinkage int _save_fp_context32(struct sigcontext32 __user *sc); |
1313 | extern asmlinkage int _restore_fp_context32(struct sigcontext32 *sc); | 1303 | extern asmlinkage int _restore_fp_context32(struct sigcontext32 __user *sc); |
1314 | 1304 | ||
1315 | extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 *sc); | 1305 | extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 __user *sc); |
1316 | extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 *sc); | 1306 | extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 __user *sc); |
1317 | 1307 | ||
1318 | static inline void signal32_init(void) | 1308 | static 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 | ||
54 | int fpu_emulator_save_context(struct sigcontext *sc) | 54 | int 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 | ||
68 | int fpu_emulator_restore_context(struct sigcontext *sc) | 68 | int 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 | ||
87 | int fpu_emulator_save_context32(struct sigcontext32 *sc) | 87 | int 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 | ||
101 | int fpu_emulator_restore_context32(struct sigcontext32 *sc) | 101 | int fpu_emulator_restore_context32(struct sigcontext32 __user *sc) |
102 | { | 102 | { |
103 | int i; | 103 | int i; |
104 | int err = 0; | 104 | int err = 0; |
diff --git a/include/asm-mips/cpu-features.h b/include/asm-mips/cpu-features.h index eadca266f159..5e4bed123b48 100644 --- a/include/asm-mips/cpu-features.h +++ b/include/asm-mips/cpu-features.h | |||
@@ -40,6 +40,9 @@ | |||
40 | #endif | 40 | #endif |
41 | #ifndef cpu_has_fpu | 41 | #ifndef cpu_has_fpu |
42 | #define cpu_has_fpu (current_cpu_data.options & MIPS_CPU_FPU) | 42 | #define cpu_has_fpu (current_cpu_data.options & MIPS_CPU_FPU) |
43 | #define raw_cpu_has_fpu (raw_current_cpu_data.options & MIPS_CPU_FPU) | ||
44 | #else | ||
45 | #define raw_cpu_has_fpu cpu_has_fpu | ||
43 | #endif | 46 | #endif |
44 | #ifndef cpu_has_32fpr | 47 | #ifndef cpu_has_32fpr |
45 | #define cpu_has_32fpr (cpu_data[0].options & MIPS_CPU_32FPR) | 48 | #define cpu_has_32fpr (cpu_data[0].options & MIPS_CPU_32FPR) |
diff --git a/include/asm-mips/cpu-info.h b/include/asm-mips/cpu-info.h index 610d0cdeaa9e..22fe8453fcc7 100644 --- a/include/asm-mips/cpu-info.h +++ b/include/asm-mips/cpu-info.h | |||
@@ -87,6 +87,7 @@ struct cpuinfo_mips { | |||
87 | 87 | ||
88 | extern struct cpuinfo_mips cpu_data[]; | 88 | extern struct cpuinfo_mips cpu_data[]; |
89 | #define current_cpu_data cpu_data[smp_processor_id()] | 89 | #define current_cpu_data cpu_data[smp_processor_id()] |
90 | #define raw_current_cpu_data cpu_data[raw_smp_processor_id()] | ||
90 | 91 | ||
91 | extern void cpu_probe(void); | 92 | extern void cpu_probe(void); |
92 | extern void cpu_report(void); | 93 | extern void cpu_report(void); |
diff --git a/include/asm-mips/fpu.h b/include/asm-mips/fpu.h index efef843b93f0..4e12d1f9534f 100644 --- a/include/asm-mips/fpu.h +++ b/include/asm-mips/fpu.h | |||
@@ -27,11 +27,11 @@ | |||
27 | struct sigcontext; | 27 | struct sigcontext; |
28 | struct sigcontext32; | 28 | struct sigcontext32; |
29 | 29 | ||
30 | extern asmlinkage int (*save_fp_context)(struct sigcontext *sc); | 30 | extern asmlinkage int (*save_fp_context)(struct sigcontext __user *sc); |
31 | extern asmlinkage int (*restore_fp_context)(struct sigcontext *sc); | 31 | extern asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc); |
32 | 32 | ||
33 | extern asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc); | 33 | extern asmlinkage int (*save_fp_context32)(struct sigcontext32 __user *sc); |
34 | extern asmlinkage int (*restore_fp_context32)(struct sigcontext32 *sc); | 34 | extern asmlinkage int (*restore_fp_context32)(struct sigcontext32 __user *sc); |
35 | 35 | ||
36 | extern void fpu_emulator_init_fpu(void); | 36 | extern void fpu_emulator_init_fpu(void); |
37 | extern void _init_fpu(void); | 37 | extern void _init_fpu(void); |
@@ -68,6 +68,8 @@ do { \ | |||
68 | /* We don't care about the c0 hazard here */ \ | 68 | /* We don't care about the c0 hazard here */ \ |
69 | } while (0) | 69 | } while (0) |
70 | 70 | ||
71 | #define __fpu_enabled() (read_c0_status() & ST0_CU1) | ||
72 | |||
71 | #define enable_fpu() \ | 73 | #define enable_fpu() \ |
72 | do { \ | 74 | do { \ |
73 | if (cpu_has_fpu) \ | 75 | if (cpu_has_fpu) \ |
@@ -93,31 +95,47 @@ static inline int is_fpu_owner(void) | |||
93 | return cpu_has_fpu && __is_fpu_owner(); | 95 | return cpu_has_fpu && __is_fpu_owner(); |
94 | } | 96 | } |
95 | 97 | ||
96 | static inline void own_fpu(void) | 98 | static inline void __own_fpu(void) |
97 | { | 99 | { |
98 | if (cpu_has_fpu) { | 100 | __enable_fpu(); |
99 | __enable_fpu(); | 101 | KSTK_STATUS(current) |= ST0_CU1; |
100 | KSTK_STATUS(current) |= ST0_CU1; | 102 | set_thread_flag(TIF_USEDFPU); |
101 | set_thread_flag(TIF_USEDFPU); | 103 | } |
104 | |||
105 | static inline void own_fpu(int restore) | ||
106 | { | ||
107 | preempt_disable(); | ||
108 | if (cpu_has_fpu && !__is_fpu_owner()) { | ||
109 | __own_fpu(); | ||
110 | if (restore) | ||
111 | _restore_fp(current); | ||
102 | } | 112 | } |
113 | preempt_enable(); | ||
103 | } | 114 | } |
104 | 115 | ||
105 | static inline void lose_fpu(void) | 116 | static inline void lose_fpu(int save) |
106 | { | 117 | { |
107 | if (cpu_has_fpu) { | 118 | preempt_disable(); |
119 | if (is_fpu_owner()) { | ||
120 | if (save) | ||
121 | _save_fp(current); | ||
108 | KSTK_STATUS(current) &= ~ST0_CU1; | 122 | KSTK_STATUS(current) &= ~ST0_CU1; |
109 | clear_thread_flag(TIF_USEDFPU); | 123 | clear_thread_flag(TIF_USEDFPU); |
110 | __disable_fpu(); | 124 | __disable_fpu(); |
111 | } | 125 | } |
126 | preempt_enable(); | ||
112 | } | 127 | } |
113 | 128 | ||
114 | static inline void init_fpu(void) | 129 | static inline void init_fpu(void) |
115 | { | 130 | { |
131 | preempt_disable(); | ||
116 | if (cpu_has_fpu) { | 132 | if (cpu_has_fpu) { |
133 | __own_fpu(); | ||
117 | _init_fpu(); | 134 | _init_fpu(); |
118 | } else { | 135 | } else { |
119 | fpu_emulator_init_fpu(); | 136 | fpu_emulator_init_fpu(); |
120 | } | 137 | } |
138 | preempt_enable(); | ||
121 | } | 139 | } |
122 | 140 | ||
123 | static inline void save_fp(struct task_struct *tsk) | 141 | static inline void save_fp(struct task_struct *tsk) |
@@ -144,4 +162,18 @@ static inline fpureg_t *get_fpu_regs(struct task_struct *tsk) | |||
144 | return tsk->thread.fpu.fpr; | 162 | return tsk->thread.fpu.fpr; |
145 | } | 163 | } |
146 | 164 | ||
165 | static inline void enable_fp_in_kernel(void) | ||
166 | { | ||
167 | set_thread_flag(TIF_ALLOW_FP_IN_KERNEL); | ||
168 | /* make sure CU1 and FPU ownership are consistent */ | ||
169 | if (!__is_fpu_owner() && __fpu_enabled()) | ||
170 | __disable_fpu(); | ||
171 | } | ||
172 | |||
173 | static inline void disable_fp_in_kernel(void) | ||
174 | { | ||
175 | BUG_ON(!__is_fpu_owner() && __fpu_enabled()); | ||
176 | clear_thread_flag(TIF_ALLOW_FP_IN_KERNEL); | ||
177 | } | ||
178 | |||
147 | #endif /* _ASM_FPU_H */ | 179 | #endif /* _ASM_FPU_H */ |
diff --git a/include/asm-mips/thread_info.h b/include/asm-mips/thread_info.h index fbcda8204473..6cf05f4a4e7e 100644 --- a/include/asm-mips/thread_info.h +++ b/include/asm-mips/thread_info.h | |||
@@ -119,6 +119,7 @@ register struct thread_info *__current_thread_info __asm__("$28"); | |||
119 | #define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */ | 119 | #define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */ |
120 | #define TIF_MEMDIE 18 | 120 | #define TIF_MEMDIE 18 |
121 | #define TIF_FREEZE 19 | 121 | #define TIF_FREEZE 19 |
122 | #define TIF_ALLOW_FP_IN_KERNEL 20 | ||
122 | #define TIF_SYSCALL_TRACE 31 /* syscall trace active */ | 123 | #define TIF_SYSCALL_TRACE 31 /* syscall trace active */ |
123 | 124 | ||
124 | #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) | 125 | #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) |