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

MIPS: Simplify FP context initialization

MIPS has up until now had 3 different ways for a task's floating point
context to be initialized:

- If the task's first use of FP involves it gaining ownership of an
FPU then _init_fpu() is used to initialize the FPU's registers such
that they all contain ~0, and the FPU registers will be stored to
struct thread_info later (eg. when context switching).

- If the task first uses FP on a CPU without an associated FPU then
fpu_emulator_init_fpu() initializes the task's floating point
register state in struct thread_info such that all floating point
register contain the bit pattern 0x7ff800007ff80000, different to
the _init_fpu() behaviour.

- If a task's floating point context is first accessed via ptrace then
init_fp_ctx() initializes the floating point register state in
struct thread_info to ~0, giving equivalent state to _init_fpu().

The _init_fpu() path has 2 separate implementations - one for r2k/r3k
style systems & one for r4k style systems. The _init_fpu() path also
requires that we be careful to clear & restore the value of the
Config5.FRE bit on modern systems in order to avoid inadvertently
triggering floating point exceptions.

None of this code is in a performance critical hot path - it runs only
the first time a task uses floating point. As such it doesn't seem to
warrant the complications of maintaining the _init_fpu() path.

Remove _init_fpu() & fpu_emulator_init_fpu(), instead using
init_fp_ctx() consistently to initialize floating point register state
in struct thread_info. Upon a task's first use of floating point this
will typically mean that we initialize state in memory & then load it
into FPU registers using _restore_fp() just as we would on a context
switch. For other paths such as __compute_return_epc_for_insn() or
mipsr2_decoder() this results in a significant simplification of the
work to be done.

Signed-off-by: Paul Burton <paul.burton@mips.com>
Patchwork: https://patchwork.linux-mips.org/patch/21002/
Cc: linux-mips@linux-mips.org

+38 -292
+25 -32
arch/mips/include/asm/fpu.h
··· 33 33 struct sigcontext; 34 34 struct sigcontext32; 35 35 36 - extern void _init_fpu(unsigned int); 37 36 extern void _save_fp(struct task_struct *); 38 37 extern void _restore_fp(struct task_struct *); 39 38 ··· 197 198 preempt_enable(); 198 199 } 199 200 200 - static inline int init_fpu(void) 201 + /** 202 + * init_fp_ctx() - Initialize task FP context 203 + * @target: The task whose FP context should be initialized. 204 + * 205 + * Initializes the FP context of the target task to sane default values if that 206 + * target task does not already have valid FP context. Once the context has 207 + * been initialized, the task will be marked as having used FP & thus having 208 + * valid FP context. 209 + * 210 + * Returns: true if context is initialized, else false. 211 + */ 212 + static inline bool init_fp_ctx(struct task_struct *target) 201 213 { 202 - unsigned int fcr31 = current->thread.fpu.fcr31; 203 - int ret = 0; 214 + /* If FP has been used then the target already has context */ 215 + if (tsk_used_math(target)) 216 + return false; 204 217 205 - if (cpu_has_fpu) { 206 - unsigned int config5; 218 + /* Begin with data registers set to all 1s... */ 219 + memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr)); 207 220 208 - ret = __own_fpu(); 209 - if (ret) 210 - return ret; 221 + /* FCSR has been preset by `mips_set_personality_nan'. */ 211 222 212 - if (!cpu_has_fre) { 213 - _init_fpu(fcr31); 223 + /* 224 + * Record that the target has "used" math, such that the context 225 + * just initialised, and any modifications made by the caller, 226 + * aren't discarded. 227 + */ 228 + set_stopped_child_used_math(target); 214 229 215 - return 0; 216 - } 217 - 218 - /* 219 - * Ensure FRE is clear whilst running _init_fpu, since 220 - * single precision FP instructions are used. If FRE 221 - * was set then we'll just end up initialising all 32 222 - * 64b registers. 223 - */ 224 - config5 = clear_c0_config5(MIPS_CONF5_FRE); 225 - enable_fpu_hazard(); 226 - 227 - _init_fpu(fcr31); 228 - 229 - /* Restore FRE */ 230 - write_c0_config5(config5); 231 - enable_fpu_hazard(); 232 - } else 233 - fpu_emulator_init_fpu(); 234 - 235 - return ret; 230 + return true; 236 231 } 237 232 238 233 static inline void save_fp(struct task_struct *tsk)
-11
arch/mips/include/asm/fpu_emulator.h
··· 188 188 int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 189 189 unsigned long *contpc); 190 190 191 - #define SIGNALLING_NAN 0x7ff800007ff80000LL 192 - 193 - static inline void fpu_emulator_init_fpu(void) 194 - { 195 - struct task_struct *t = current; 196 - int i; 197 - 198 - for (i = 0; i < 32; i++) 199 - set_fpr64(&t->thread.fpu.fpr[i], 0, SIGNALLING_NAN); 200 - } 201 - 202 191 /* 203 192 * Mask the FCSR Cause bits according to the Enable bits, observing 204 193 * that Unimplemented is always enabled.
+2 -10
arch/mips/kernel/branch.c
··· 674 674 if (cpu_has_mips_r6 && 675 675 ((insn.i_format.rs == bc1eqz_op) || 676 676 (insn.i_format.rs == bc1nez_op))) { 677 - if (!used_math()) { /* First time FPU user */ 678 - ret = init_fpu(); 679 - if (ret && NO_R6EMU) { 680 - ret = -ret; 681 - break; 682 - } 683 - ret = 0; 684 - set_used_math(); 685 - } 686 - lose_fpu(1); /* Save FPU state for the emulator. */ 677 + if (!init_fp_ctx(current)) 678 + lose_fpu(1); 687 679 reg = insn.i_format.rt; 688 680 bit = get_fpr32(&current->thread.fpu.fpr[reg], 0) & 0x1; 689 681 if (insn.i_format.rs == bc1eqz_op)
+3 -7
arch/mips/kernel/mips-r2-to-r6-emul.c
··· 1174 1174 fpu_emul: 1175 1175 regs->regs[31] = r31; 1176 1176 regs->cp0_epc = epc; 1177 - if (!used_math()) { /* First time FPU user. */ 1178 - preempt_disable(); 1179 - err = init_fpu(); 1180 - preempt_enable(); 1181 - set_used_math(); 1182 - } 1183 - lose_fpu(1); /* Save FPU state for the emulator. */ 1177 + 1178 + if (!init_fp_ctx(current)) 1179 + lose_fpu(1); 1184 1180 1185 1181 err = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0, 1186 1182 &fault_addr);
-19
arch/mips/kernel/ptrace.c
··· 50 50 #define CREATE_TRACE_POINTS 51 51 #include <trace/events/syscalls.h> 52 52 53 - static void init_fp_ctx(struct task_struct *target) 54 - { 55 - /* If FP has been used then the target already has context */ 56 - if (tsk_used_math(target)) 57 - return; 58 - 59 - /* Begin with data registers set to all 1s... */ 60 - memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr)); 61 - 62 - /* FCSR has been preset by `mips_set_personality_nan'. */ 63 - 64 - /* 65 - * Record that the target has "used" math, such that the context 66 - * just initialised, and any modifications made by the caller, 67 - * aren't discarded. 68 - */ 69 - set_stopped_child_used_math(target); 70 - } 71 - 72 53 /* 73 54 * Called by kernel/ptrace.c when detaching.. 74 55 *
-58
arch/mips/kernel/r2300_fpu.S
··· 52 52 jr ra 53 53 END(_restore_fp) 54 54 55 - /* 56 - * Load the FPU with signalling NANS. This bit pattern we're using has 57 - * the property that no matter whether considered as single or as double 58 - * precision represents signaling NANS. 59 - * 60 - * The value to initialize fcr31 to comes in $a0. 61 - */ 62 - 63 - .set push 64 - SET_HARDFLOAT 65 - 66 - LEAF(_init_fpu) 67 - mfc0 t0, CP0_STATUS 68 - li t1, ST0_CU1 69 - or t0, t1 70 - mtc0 t0, CP0_STATUS 71 - 72 - ctc1 a0, fcr31 73 - 74 - li t0, -1 75 - 76 - mtc1 t0, $f0 77 - mtc1 t0, $f1 78 - mtc1 t0, $f2 79 - mtc1 t0, $f3 80 - mtc1 t0, $f4 81 - mtc1 t0, $f5 82 - mtc1 t0, $f6 83 - mtc1 t0, $f7 84 - mtc1 t0, $f8 85 - mtc1 t0, $f9 86 - mtc1 t0, $f10 87 - mtc1 t0, $f11 88 - mtc1 t0, $f12 89 - mtc1 t0, $f13 90 - mtc1 t0, $f14 91 - mtc1 t0, $f15 92 - mtc1 t0, $f16 93 - mtc1 t0, $f17 94 - mtc1 t0, $f18 95 - mtc1 t0, $f19 96 - mtc1 t0, $f20 97 - mtc1 t0, $f21 98 - mtc1 t0, $f22 99 - mtc1 t0, $f23 100 - mtc1 t0, $f24 101 - mtc1 t0, $f25 102 - mtc1 t0, $f26 103 - mtc1 t0, $f27 104 - mtc1 t0, $f28 105 - mtc1 t0, $f29 106 - mtc1 t0, $f30 107 - mtc1 t0, $f31 108 - jr ra 109 - END(_init_fpu) 110 - 111 - .set pop 112 - 113 55 .set noreorder 114 56 115 57 /**
-144
arch/mips/kernel/r4k_fpu.S
··· 86 86 87 87 #endif 88 88 89 - /* 90 - * Load the FPU with signalling NANS. This bit pattern we're using has 91 - * the property that no matter whether considered as single or as double 92 - * precision represents signaling NANS. 93 - * 94 - * The value to initialize fcr31 to comes in $a0. 95 - */ 96 - 97 - .set push 98 - SET_HARDFLOAT 99 - 100 - LEAF(_init_fpu) 101 - mfc0 t0, CP0_STATUS 102 - li t1, ST0_CU1 103 - or t0, t1 104 - mtc0 t0, CP0_STATUS 105 - enable_fpu_hazard 106 - 107 - ctc1 a0, fcr31 108 - 109 - li t1, -1 # SNaN 110 - 111 - #ifdef CONFIG_64BIT 112 - sll t0, t0, 5 113 - bgez t0, 1f # 16 / 32 register mode? 114 - 115 - dmtc1 t1, $f1 116 - dmtc1 t1, $f3 117 - dmtc1 t1, $f5 118 - dmtc1 t1, $f7 119 - dmtc1 t1, $f9 120 - dmtc1 t1, $f11 121 - dmtc1 t1, $f13 122 - dmtc1 t1, $f15 123 - dmtc1 t1, $f17 124 - dmtc1 t1, $f19 125 - dmtc1 t1, $f21 126 - dmtc1 t1, $f23 127 - dmtc1 t1, $f25 128 - dmtc1 t1, $f27 129 - dmtc1 t1, $f29 130 - dmtc1 t1, $f31 131 - 1: 132 - #endif 133 - 134 - #ifdef CONFIG_CPU_MIPS32 135 - mtc1 t1, $f0 136 - mtc1 t1, $f1 137 - mtc1 t1, $f2 138 - mtc1 t1, $f3 139 - mtc1 t1, $f4 140 - mtc1 t1, $f5 141 - mtc1 t1, $f6 142 - mtc1 t1, $f7 143 - mtc1 t1, $f8 144 - mtc1 t1, $f9 145 - mtc1 t1, $f10 146 - mtc1 t1, $f11 147 - mtc1 t1, $f12 148 - mtc1 t1, $f13 149 - mtc1 t1, $f14 150 - mtc1 t1, $f15 151 - mtc1 t1, $f16 152 - mtc1 t1, $f17 153 - mtc1 t1, $f18 154 - mtc1 t1, $f19 155 - mtc1 t1, $f20 156 - mtc1 t1, $f21 157 - mtc1 t1, $f22 158 - mtc1 t1, $f23 159 - mtc1 t1, $f24 160 - mtc1 t1, $f25 161 - mtc1 t1, $f26 162 - mtc1 t1, $f27 163 - mtc1 t1, $f28 164 - mtc1 t1, $f29 165 - mtc1 t1, $f30 166 - mtc1 t1, $f31 167 - 168 - #if defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_CPU_MIPS32_R6) 169 - .set push 170 - .set MIPS_ISA_LEVEL_RAW 171 - .set fp=64 172 - sll t0, t0, 5 # is Status.FR set? 173 - bgez t0, 1f # no: skip setting upper 32b 174 - 175 - mthc1 t1, $f0 176 - mthc1 t1, $f1 177 - mthc1 t1, $f2 178 - mthc1 t1, $f3 179 - mthc1 t1, $f4 180 - mthc1 t1, $f5 181 - mthc1 t1, $f6 182 - mthc1 t1, $f7 183 - mthc1 t1, $f8 184 - mthc1 t1, $f9 185 - mthc1 t1, $f10 186 - mthc1 t1, $f11 187 - mthc1 t1, $f12 188 - mthc1 t1, $f13 189 - mthc1 t1, $f14 190 - mthc1 t1, $f15 191 - mthc1 t1, $f16 192 - mthc1 t1, $f17 193 - mthc1 t1, $f18 194 - mthc1 t1, $f19 195 - mthc1 t1, $f20 196 - mthc1 t1, $f21 197 - mthc1 t1, $f22 198 - mthc1 t1, $f23 199 - mthc1 t1, $f24 200 - mthc1 t1, $f25 201 - mthc1 t1, $f26 202 - mthc1 t1, $f27 203 - mthc1 t1, $f28 204 - mthc1 t1, $f29 205 - mthc1 t1, $f30 206 - mthc1 t1, $f31 207 - 1: .set pop 208 - #endif /* CONFIG_CPU_MIPS32_R2 || CONFIG_CPU_MIPS32_R6 */ 209 - #else 210 - .set MIPS_ISA_ARCH_LEVEL_RAW 211 - dmtc1 t1, $f0 212 - dmtc1 t1, $f2 213 - dmtc1 t1, $f4 214 - dmtc1 t1, $f6 215 - dmtc1 t1, $f8 216 - dmtc1 t1, $f10 217 - dmtc1 t1, $f12 218 - dmtc1 t1, $f14 219 - dmtc1 t1, $f16 220 - dmtc1 t1, $f18 221 - dmtc1 t1, $f20 222 - dmtc1 t1, $f22 223 - dmtc1 t1, $f24 224 - dmtc1 t1, $f26 225 - dmtc1 t1, $f28 226 - dmtc1 t1, $f30 227 - #endif 228 - jr ra 229 - END(_init_fpu) 230 - 231 - .set pop /* SET_HARDFLOAT */ 232 - 233 89 .set noreorder 234 90 235 91 /**
+6 -6
arch/mips/kernel/traps.c
··· 1218 1218 static int enable_restore_fp_context(int msa) 1219 1219 { 1220 1220 int err, was_fpu_owner, prior_msa; 1221 + bool first_fp; 1221 1222 1222 - if (!used_math()) { 1223 - /* First time FP context user. */ 1223 + /* Initialize context if it hasn't been used already */ 1224 + first_fp = init_fp_ctx(current); 1225 + 1226 + if (first_fp) { 1224 1227 preempt_disable(); 1225 - err = init_fpu(); 1228 + err = own_fpu_inatomic(1); 1226 1229 if (msa && !err) { 1227 1230 enable_msa(); 1228 - init_msa_upper(); 1229 1231 set_thread_flag(TIF_USEDMSA); 1230 1232 set_thread_flag(TIF_MSA_CTX_LIVE); 1231 1233 } 1232 1234 preempt_enable(); 1233 - if (!err) 1234 - set_used_math(); 1235 1235 return err; 1236 1236 } 1237 1237
+2 -5
arch/mips/loongson64/loongson-3/cop2-ex.c
··· 43 43 /* If FPU is owned, we needn't init or restore fp */ 44 44 if (!fpu_owned) { 45 45 set_thread_flag(TIF_USEDFPU); 46 - if (!used_math()) { 47 - _init_fpu(current->thread.fpu.fcr31); 48 - set_used_math(); 49 - } else 50 - _restore_fp(current); 46 + init_fp_ctx(current); 47 + _restore_fp(current); 51 48 } 52 49 preempt_enable(); 53 50