Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Atsushi Nemoto and committed by
Ralf Baechle
53dc8028 c6a2f467

+126 -105
+6 -4
arch/mips/kernel/r2300_switch.S
··· 49 49 #ifndef CONFIG_CPU_HAS_LLSC 50 50 sw zero, ll_bit 51 51 #endif 52 - mfc0 t1, CP0_STATUS 53 - sw t1, THREAD_STATUS(a0) 52 + mfc0 t2, CP0_STATUS 54 53 cpu_save_nonscratch a0 55 54 sw ra, THREAD_REG31(a0) 56 55 ··· 59 60 lw t3, TASK_THREAD_INFO(a0) 60 61 lw t0, TI_FLAGS(t3) 61 62 li t1, _TIF_USEDFPU 62 - and t2, t0, t1 63 - beqz t2, 1f 63 + and t1, t0 64 + beqz t1, 1f 64 65 nor t1, zero, t1 65 66 66 67 and t0, t0, t1 ··· 73 74 li t1, ~ST0_CU1 74 75 and t0, t0, t1 75 76 sw t0, ST_OFF(t3) 77 + /* clear thread_struct CU1 bit */ 78 + and t2, t1 76 79 77 80 fpu_save_single a0, t0 # clobbers t0 78 81 79 82 1: 83 + sw t2, THREAD_STATUS(a0) 80 84 /* 81 85 * The order of restoring the registers takes care of the race 82 86 * updating $28, $29 and kernelsp without disabling ints.
+6 -4
arch/mips/kernel/r4k_switch.S
··· 48 48 #ifndef CONFIG_CPU_HAS_LLSC 49 49 sw zero, ll_bit 50 50 #endif 51 - mfc0 t1, CP0_STATUS 52 - LONG_S t1, THREAD_STATUS(a0) 51 + mfc0 t2, CP0_STATUS 53 52 cpu_save_nonscratch a0 54 53 LONG_S ra, THREAD_REG31(a0) 55 54 ··· 58 59 PTR_L t3, TASK_THREAD_INFO(a0) 59 60 LONG_L t0, TI_FLAGS(t3) 60 61 li t1, _TIF_USEDFPU 61 - and t2, t0, t1 62 - beqz t2, 1f 62 + and t1, t0 63 + beqz t1, 1f 63 64 nor t1, zero, t1 64 65 65 66 and t0, t0, t1 ··· 72 73 li t1, ~ST0_CU1 73 74 and t0, t0, t1 74 75 LONG_S t0, ST_OFF(t3) 76 + /* clear thread_struct CU1 bit */ 77 + and t2, t1 75 78 76 79 fpu_save_double a0 t0 t1 # c0_status passed in t0 77 80 # clobbers t1 78 81 1: 82 + LONG_S t2, THREAD_STATUS(a0) 79 83 80 84 /* 81 85 * The order of restoring the registers takes care of the race
+12 -17
arch/mips/kernel/signal.c
··· 82 82 { 83 83 int err = 0; 84 84 int i; 85 + unsigned int used_math; 85 86 86 87 err |= __put_user(regs->cp0_epc, &sc->sc_pc); 87 88 ··· 105 104 err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); 106 105 } 107 106 108 - err |= __put_user(!!used_math(), &sc->sc_used_math); 107 + used_math = !!used_math(); 108 + err |= __put_user(used_math, &sc->sc_used_math); 109 109 110 - if (used_math()) { 110 + if (used_math) { 111 111 /* 112 112 * Save FPU state to signal context. Signal handler 113 113 * will "inherit" current FPU state. 114 114 */ 115 - preempt_disable(); 116 - 117 - if (!is_fpu_owner()) { 118 - own_fpu(); 119 - restore_fp(current); 120 - } 115 + own_fpu(1); 116 + enable_fp_in_kernel(); 121 117 err |= save_fp_context(sc); 122 - 123 - preempt_enable(); 118 + disable_fp_in_kernel(); 124 119 } 125 120 return err; 126 121 } ··· 185 188 err |= __get_user(used_math, &sc->sc_used_math); 186 189 conditional_used_math(used_math); 187 190 188 - preempt_disable(); 189 - 190 - if (used_math()) { 191 + if (used_math) { 191 192 /* restore fpu context if we have used it before */ 192 - own_fpu(); 193 + own_fpu(0); 194 + enable_fp_in_kernel(); 193 195 if (!err) 194 196 err = check_and_restore_fp_context(sc); 197 + disable_fp_in_kernel(); 195 198 } else { 196 199 /* signal handler may have used FPU. Give it up. */ 197 - lose_fpu(); 200 + lose_fpu(0); 198 201 } 199 - 200 - preempt_enable(); 201 202 202 203 return err; 203 204 }
+12 -17
arch/mips/kernel/signal32.c
··· 181 181 { 182 182 int err = 0; 183 183 int i; 184 + u32 used_math; 184 185 185 186 err |= __put_user(regs->cp0_epc, &sc->sc_pc); 186 187 ··· 201 200 err |= __put_user(mflo3(), &sc->sc_lo3); 202 201 } 203 202 204 - err |= __put_user(!!used_math(), &sc->sc_used_math); 203 + used_math = !!used_math(); 204 + err |= __put_user(used_math, &sc->sc_used_math); 205 205 206 - if (used_math()) { 206 + if (used_math) { 207 207 /* 208 208 * Save FPU state to signal context. Signal handler 209 209 * will "inherit" current FPU state. 210 210 */ 211 - preempt_disable(); 212 - 213 - if (!is_fpu_owner()) { 214 - own_fpu(); 215 - restore_fp(current); 216 - } 211 + own_fpu(1); 212 + enable_fp_in_kernel(); 217 213 err |= save_fp_context32(sc); 218 - 219 - preempt_enable(); 214 + disable_fp_in_kernel(); 220 215 } 221 216 return err; 222 217 } ··· 259 262 err |= __get_user(used_math, &sc->sc_used_math); 260 263 conditional_used_math(used_math); 261 264 262 - preempt_disable(); 263 - 264 - if (used_math()) { 265 + if (used_math) { 265 266 /* restore fpu context if we have used it before */ 266 - own_fpu(); 267 + own_fpu(0); 268 + enable_fp_in_kernel(); 267 269 if (!err) 268 270 err = check_and_restore_fp_context32(sc); 271 + disable_fp_in_kernel(); 269 272 } else { 270 273 /* signal handler may have used FPU. Give it up. */ 271 - lose_fpu(); 274 + lose_fpu(0); 272 275 } 273 - 274 - preempt_enable(); 275 276 276 277 return err; 277 278 }
+37 -47
arch/mips/kernel/traps.c
··· 610 610 if (fcr31 & FPU_CSR_UNI_X) { 611 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 614 * Unimplemented operation exception. If we've got the full 625 615 * software emulator on-board, let's use it... ··· 620 630 * register operands before invoking the emulator, which seems 621 631 * a bit extreme for what should be an infrequent event. 622 632 */ 623 - save_fp(current); 624 633 /* Ensure 'resume' not overwrite saved fp context again. */ 625 - lose_fpu(); 626 - 627 - preempt_enable(); 634 + lose_fpu(1); 628 635 629 636 /* Run the emulator */ 630 637 sig = fpu_emulator_cop1Handler (regs, &current->thread.fpu, 1); 631 638 632 - preempt_disable(); 633 - 634 - own_fpu(); /* Using the FPU again. */ 635 639 /* 636 640 * We can't allow the emulated instruction to leave any of 637 641 * the cause bit set in $fcr31. ··· 633 649 current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; 634 650 635 651 /* Restore the hardware register state */ 636 - restore_fp(current); 637 - 638 - preempt_enable(); 652 + own_fpu(1); /* Using the FPU again. */ 639 653 640 654 /* If something went wrong, signal */ 641 655 if (sig) ··· 757 775 { 758 776 unsigned int cpid; 759 777 760 - die_if_kernel("do_cpu invoked from kernel context!", regs); 761 - 762 778 cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; 763 779 764 780 switch (cpid) { 765 781 case 0: 782 + die_if_kernel("do_cpu invoked from kernel context!", regs); 766 783 if (!cpu_has_llsc) 767 784 if (!simulate_llsc(regs)) 768 785 return; ··· 772 791 break; 773 792 774 793 case 1: 775 - preempt_disable(); 776 - 777 - own_fpu(); 778 - if (used_math()) { /* Using the FPU again. */ 779 - restore_fp(current); 780 - } else { /* First time FPU user. */ 794 + if (!test_thread_flag(TIF_ALLOW_FP_IN_KERNEL)) 795 + die_if_kernel("do_cpu invoked from kernel context!", 796 + regs); 797 + if (used_math()) /* Using the FPU again. */ 798 + own_fpu(1); 799 + else { /* First time FPU user. */ 781 800 init_fpu(); 782 801 set_used_math(); 783 802 } 784 803 785 - if (cpu_has_fpu) { 786 - preempt_enable(); 804 + if (raw_cpu_has_fpu) { 805 + if (test_thread_flag(TIF_ALLOW_FP_IN_KERNEL)) { 806 + local_irq_disable(); 807 + if (cpu_has_fpu) 808 + regs->cp0_status |= ST0_CU1; 809 + /* 810 + * We must return without enabling 811 + * interrupts to ensure keep FPU 812 + * ownership until resume. 813 + */ 814 + return; 815 + } 787 816 } else { 788 817 int sig; 789 - preempt_enable(); 790 818 sig = fpu_emulator_cop1Handler(regs, 791 819 &current->thread.fpu, 0); 792 820 if (sig) ··· 1249 1259 /* 1250 1260 * This is used by native signal handling 1251 1261 */ 1252 - asmlinkage int (*save_fp_context)(struct sigcontext *sc); 1253 - asmlinkage int (*restore_fp_context)(struct sigcontext *sc); 1262 + asmlinkage int (*save_fp_context)(struct sigcontext __user *sc); 1263 + asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc); 1254 1264 1255 - extern asmlinkage int _save_fp_context(struct sigcontext *sc); 1256 - extern asmlinkage int _restore_fp_context(struct sigcontext *sc); 1265 + extern asmlinkage int _save_fp_context(struct sigcontext __user *sc); 1266 + extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc); 1257 1267 1258 - extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc); 1259 - extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc); 1268 + extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc); 1269 + extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc); 1260 1270 1261 1271 #ifdef CONFIG_SMP 1262 - static int smp_save_fp_context(struct sigcontext *sc) 1272 + static int smp_save_fp_context(struct sigcontext __user *sc) 1263 1273 { 1264 - return cpu_has_fpu 1274 + return raw_cpu_has_fpu 1265 1275 ? _save_fp_context(sc) 1266 1276 : fpu_emulator_save_context(sc); 1267 1277 } 1268 1278 1269 - static int smp_restore_fp_context(struct sigcontext *sc) 1279 + static int smp_restore_fp_context(struct sigcontext __user *sc) 1270 1280 { 1271 - return cpu_has_fpu 1281 + return raw_cpu_has_fpu 1272 1282 ? _restore_fp_context(sc) 1273 1283 : fpu_emulator_restore_context(sc); 1274 1284 } ··· 1296 1306 /* 1297 1307 * This is used by 32-bit signal stuff on the 64-bit kernel 1298 1308 */ 1299 - asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc); 1300 - asmlinkage int (*restore_fp_context32)(struct sigcontext32 *sc); 1309 + asmlinkage int (*save_fp_context32)(struct sigcontext32 __user *sc); 1310 + asmlinkage int (*restore_fp_context32)(struct sigcontext32 __user *sc); 1301 1311 1302 - extern asmlinkage int _save_fp_context32(struct sigcontext32 *sc); 1303 - extern asmlinkage int _restore_fp_context32(struct sigcontext32 *sc); 1312 + extern asmlinkage int _save_fp_context32(struct sigcontext32 __user *sc); 1313 + extern asmlinkage int _restore_fp_context32(struct sigcontext32 __user *sc); 1304 1314 1305 - extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 *sc); 1306 - extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 *sc); 1315 + extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 __user *sc); 1316 + extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 __user *sc); 1307 1317 1308 1318 static inline void signal32_init(void) 1309 1319 {
+4 -4
arch/mips/math-emu/kernel_linkage.c
··· 51 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 56 int i; 57 57 int err = 0; ··· 65 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 70 int i; 71 71 int err = 0; ··· 84 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 89 int i; 90 90 int err = 0; ··· 98 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 103 int i; 104 104 int err = 0;
+3
include/asm-mips/cpu-features.h
··· 40 40 #endif 41 41 #ifndef cpu_has_fpu 42 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 46 #endif 44 47 #ifndef cpu_has_32fpr 45 48 #define cpu_has_32fpr (cpu_data[0].options & MIPS_CPU_32FPR)
+1
include/asm-mips/cpu-info.h
··· 87 87 88 88 extern struct cpuinfo_mips cpu_data[]; 89 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 92 extern void cpu_probe(void); 92 93 extern void cpu_report(void);
+44 -12
include/asm-mips/fpu.h
··· 27 27 struct sigcontext; 28 28 struct sigcontext32; 29 29 30 - extern asmlinkage int (*save_fp_context)(struct sigcontext *sc); 31 - extern asmlinkage int (*restore_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 __user *sc); 32 32 33 - extern asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc); 34 - extern asmlinkage int (*restore_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 __user *sc); 35 35 36 36 extern void fpu_emulator_init_fpu(void); 37 37 extern void _init_fpu(void); ··· 68 68 /* We don't care about the c0 hazard here */ \ 69 69 } while (0) 70 70 71 + #define __fpu_enabled() (read_c0_status() & ST0_CU1) 72 + 71 73 #define enable_fpu() \ 72 74 do { \ 73 75 if (cpu_has_fpu) \ ··· 95 93 return cpu_has_fpu && __is_fpu_owner(); 96 94 } 97 95 98 - static inline void own_fpu(void) 96 + static inline void __own_fpu(void) 99 97 { 100 - if (cpu_has_fpu) { 101 - __enable_fpu(); 102 - KSTK_STATUS(current) |= ST0_CU1; 103 - set_thread_flag(TIF_USEDFPU); 104 - } 98 + __enable_fpu(); 99 + KSTK_STATUS(current) |= ST0_CU1; 100 + set_thread_flag(TIF_USEDFPU); 105 101 } 106 102 107 - static inline void lose_fpu(void) 103 + static inline void own_fpu(int restore) 108 104 { 109 - if (cpu_has_fpu) { 105 + preempt_disable(); 106 + if (cpu_has_fpu && !__is_fpu_owner()) { 107 + __own_fpu(); 108 + if (restore) 109 + _restore_fp(current); 110 + } 111 + preempt_enable(); 112 + } 113 + 114 + static inline void lose_fpu(int save) 115 + { 116 + preempt_disable(); 117 + if (is_fpu_owner()) { 118 + if (save) 119 + _save_fp(current); 110 120 KSTK_STATUS(current) &= ~ST0_CU1; 111 121 clear_thread_flag(TIF_USEDFPU); 112 122 __disable_fpu(); 113 123 } 124 + preempt_enable(); 114 125 } 115 126 116 127 static inline void init_fpu(void) 117 128 { 129 + preempt_disable(); 118 130 if (cpu_has_fpu) { 131 + __own_fpu(); 119 132 _init_fpu(); 120 133 } else { 121 134 fpu_emulator_init_fpu(); 122 135 } 136 + preempt_enable(); 123 137 } 124 138 125 139 static inline void save_fp(struct task_struct *tsk) ··· 160 142 } 161 143 162 144 return tsk->thread.fpu.fpr; 145 + } 146 + 147 + static inline void enable_fp_in_kernel(void) 148 + { 149 + set_thread_flag(TIF_ALLOW_FP_IN_KERNEL); 150 + /* make sure CU1 and FPU ownership are consistent */ 151 + if (!__is_fpu_owner() && __fpu_enabled()) 152 + __disable_fpu(); 153 + } 154 + 155 + static inline void disable_fp_in_kernel(void) 156 + { 157 + BUG_ON(!__is_fpu_owner() && __fpu_enabled()); 158 + clear_thread_flag(TIF_ALLOW_FP_IN_KERNEL); 163 159 } 164 160 165 161 #endif /* _ASM_FPU_H */
+1
include/asm-mips/thread_info.h
··· 119 119 #define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */ 120 120 #define TIF_MEMDIE 18 121 121 #define TIF_FREEZE 19 122 + #define TIF_ALLOW_FP_IN_KERNEL 20 122 123 #define TIF_SYSCALL_TRACE 31 /* syscall trace active */ 123 124 124 125 #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)