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

[ARM] 4111/1: Allow VFP to work with thread migration on SMP

The current lazy saving of the VFP registers is no longer possible
with thread migration on SMP. This patch implements a per-CPU
vfp-state pointer and the saving of the VFP registers at every context
switch. The registers restoring is still performed in a lazy way.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Catalin Marinas and committed by
Russell King
c6428464 412489af

+58 -6
+1
arch/arm/vfp/entry.S
··· 25 25 do_vfp: 26 26 enable_irq 27 27 ldr r4, .LCvfp 28 + ldr r11, [r10, #TI_CPU] @ CPU number 28 29 add r10, r10, #TI_VFPSTATE @ r10 = workspace 29 30 ldr pc, [r4] @ call VFP entry point 30 31
+4
arch/arm/vfp/vfp.h
··· 370 370 u32 (* const fn)(int dd, int dn, int dm, u32 fpscr); 371 371 u32 flags; 372 372 }; 373 + 374 + #ifdef CONFIG_SMP 375 + extern void vfp_save_state(void *location, u32 fpexc); 376 + #endif
+24 -2
arch/arm/vfp/vfphw.S
··· 65 65 @ r2 = faulted PC+4 66 66 @ r9 = successful return 67 67 @ r10 = vfp_state union 68 + @ r11 = CPU number 68 69 @ lr = failure return 69 70 70 71 .globl vfp_support_entry ··· 80 79 DBGSTR1 "enable %x", r10 81 80 ldr r3, last_VFP_context_address 82 81 orr r1, r1, #FPEXC_ENABLE @ user FPEXC has the enable bit set 83 - ldr r4, [r3] @ last_VFP_context pointer 82 + ldr r4, [r3, r11, lsl #2] @ last_VFP_context pointer 84 83 bic r5, r1, #FPEXC_EXCEPTION @ make sure exceptions are disabled 85 84 cmp r4, r10 86 85 beq check_for_exception @ we are returning to the same ··· 92 91 @ exceptions, so we can get at the 93 92 @ rest of it 94 93 94 + #ifndef CONFIG_SMP 95 95 @ Save out the current registers to the old thread state 96 + @ No need for SMP since this is not done lazily 96 97 97 98 DBGSTR1 "save old state %p", r4 98 99 cmp r4, #0 ··· 108 105 stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 109 106 @ and point r4 at the word at the 110 107 @ start of the register dump 108 + #endif 111 109 112 110 no_old_VFP_process: 113 111 DBGSTR1 "load state %p", r10 114 - str r10, [r3] @ update the last_VFP_context pointer 112 + str r10, [r3, r11, lsl #2] @ update the last_VFP_context pointer 115 113 @ Load the saved state back into the VFP 116 114 VFPFLDMIA r10 @ reload the working registers while 117 115 @ FPEXC is in a safe state ··· 165 161 @ code will raise an exception if 166 162 @ required. If not, the user code will 167 163 @ retry the faulted instruction 164 + 165 + #ifdef CONFIG_SMP 166 + .globl vfp_save_state 167 + .type vfp_save_state, %function 168 + vfp_save_state: 169 + @ Save the current VFP state 170 + @ r0 - save location 171 + @ r1 - FPEXC 172 + DBGSTR1 "save VFP state %p", r0 173 + VFPFMRX r2, FPSCR @ current status 174 + VFPFMRX r3, FPINST @ FPINST (always there, rev0 onwards) 175 + tst r1, #FPEXC_FPV2 @ is there an FPINST2 to read? 176 + VFPFMRX r12, FPINST2, NE @ FPINST2 if needed - avoids reading 177 + @ nonexistant reg on rev0 178 + VFPFSTMIA r0 @ save the working registers 179 + stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2 180 + mov pc, lr 181 + #endif 168 182 169 183 last_VFP_context_address: 170 184 .word last_VFP_context
+26 -4
arch/arm/vfp/vfpmodule.c
··· 28 28 void vfp_support_entry(void); 29 29 30 30 void (*vfp_vector)(void) = vfp_testing_entry; 31 - union vfp_state *last_VFP_context; 31 + union vfp_state *last_VFP_context[NR_CPUS]; 32 32 33 33 /* 34 34 * Dual-use variable. ··· 41 41 { 42 42 struct thread_info *thread = v; 43 43 union vfp_state *vfp; 44 + __u32 cpu = thread->cpu; 44 45 45 46 if (likely(cmd == THREAD_NOTIFY_SWITCH)) { 47 + u32 fpexc = fmrx(FPEXC); 48 + 49 + #ifdef CONFIG_SMP 50 + /* 51 + * On SMP, if VFP is enabled, save the old state in 52 + * case the thread migrates to a different CPU. The 53 + * restoring is done lazily. 54 + */ 55 + if ((fpexc & FPEXC_ENABLE) && last_VFP_context[cpu]) { 56 + vfp_save_state(last_VFP_context[cpu], fpexc); 57 + last_VFP_context[cpu]->hard.cpu = cpu; 58 + } 59 + /* 60 + * Thread migration, just force the reloading of the 61 + * state on the new CPU in case the VFP registers 62 + * contain stale data. 63 + */ 64 + if (thread->vfpstate.hard.cpu != cpu) 65 + last_VFP_context[cpu] = NULL; 66 + #endif 67 + 46 68 /* 47 69 * Always disable VFP so we can lazily save/restore the 48 70 * old state. 49 71 */ 50 - fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE); 72 + fmxr(FPEXC, fpexc & ~FPEXC_ENABLE); 51 73 return NOTIFY_DONE; 52 74 } 53 75 ··· 90 68 } 91 69 92 70 /* flush and release case: Per-thread VFP cleanup. */ 93 - if (last_VFP_context == vfp) 94 - last_VFP_context = NULL; 71 + if (last_VFP_context[cpu] == vfp) 72 + last_VFP_context[cpu] = NULL; 95 73 96 74 return NOTIFY_DONE; 97 75 }
+3
include/asm-arm/fpstate.h
··· 35 35 */ 36 36 __u32 fpinst; 37 37 __u32 fpinst2; 38 + #ifdef CONFIG_SMP 39 + __u32 cpu; 40 + #endif 38 41 }; 39 42 40 43 union vfp_state {