Commit c6428464 authored by Catalin Marinas's avatar Catalin Marinas Committed by Russell King

[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: default avatarCatalin Marinas <catalin.marinas@arm.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 412489af
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
do_vfp: do_vfp:
enable_irq enable_irq
ldr r4, .LCvfp ldr r4, .LCvfp
ldr r11, [r10, #TI_CPU] @ CPU number
add r10, r10, #TI_VFPSTATE @ r10 = workspace add r10, r10, #TI_VFPSTATE @ r10 = workspace
ldr pc, [r4] @ call VFP entry point ldr pc, [r4] @ call VFP entry point
......
...@@ -370,3 +370,7 @@ struct op { ...@@ -370,3 +370,7 @@ struct op {
u32 (* const fn)(int dd, int dn, int dm, u32 fpscr); u32 (* const fn)(int dd, int dn, int dm, u32 fpscr);
u32 flags; u32 flags;
}; };
#ifdef CONFIG_SMP
extern void vfp_save_state(void *location, u32 fpexc);
#endif
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
@ r2 = faulted PC+4 @ r2 = faulted PC+4
@ r9 = successful return @ r9 = successful return
@ r10 = vfp_state union @ r10 = vfp_state union
@ r11 = CPU number
@ lr = failure return @ lr = failure return
.globl vfp_support_entry .globl vfp_support_entry
...@@ -79,7 +80,7 @@ vfp_support_entry: ...@@ -79,7 +80,7 @@ vfp_support_entry:
DBGSTR1 "enable %x", r10 DBGSTR1 "enable %x", r10
ldr r3, last_VFP_context_address ldr r3, last_VFP_context_address
orr r1, r1, #FPEXC_ENABLE @ user FPEXC has the enable bit set orr r1, r1, #FPEXC_ENABLE @ user FPEXC has the enable bit set
ldr r4, [r3] @ last_VFP_context pointer ldr r4, [r3, r11, lsl #2] @ last_VFP_context pointer
bic r5, r1, #FPEXC_EXCEPTION @ make sure exceptions are disabled bic r5, r1, #FPEXC_EXCEPTION @ make sure exceptions are disabled
cmp r4, r10 cmp r4, r10
beq check_for_exception @ we are returning to the same beq check_for_exception @ we are returning to the same
...@@ -91,7 +92,9 @@ vfp_support_entry: ...@@ -91,7 +92,9 @@ vfp_support_entry:
@ exceptions, so we can get at the @ exceptions, so we can get at the
@ rest of it @ rest of it
#ifndef CONFIG_SMP
@ Save out the current registers to the old thread state @ Save out the current registers to the old thread state
@ No need for SMP since this is not done lazily
DBGSTR1 "save old state %p", r4 DBGSTR1 "save old state %p", r4
cmp r4, #0 cmp r4, #0
...@@ -105,10 +108,11 @@ vfp_support_entry: ...@@ -105,10 +108,11 @@ vfp_support_entry:
stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2
@ and point r4 at the word at the @ and point r4 at the word at the
@ start of the register dump @ start of the register dump
#endif
no_old_VFP_process: no_old_VFP_process:
DBGSTR1 "load state %p", r10 DBGSTR1 "load state %p", r10
str r10, [r3] @ update the last_VFP_context pointer str r10, [r3, r11, lsl #2] @ update the last_VFP_context pointer
@ Load the saved state back into the VFP @ Load the saved state back into the VFP
VFPFLDMIA r10 @ reload the working registers while VFPFLDMIA r10 @ reload the working registers while
@ FPEXC is in a safe state @ FPEXC is in a safe state
...@@ -162,6 +166,24 @@ process_exception: ...@@ -162,6 +166,24 @@ process_exception:
@ required. If not, the user code will @ required. If not, the user code will
@ retry the faulted instruction @ retry the faulted instruction
#ifdef CONFIG_SMP
.globl vfp_save_state
.type vfp_save_state, %function
vfp_save_state:
@ Save the current VFP state
@ r0 - save location
@ r1 - FPEXC
DBGSTR1 "save VFP state %p", r0
VFPFMRX r2, FPSCR @ current status
VFPFMRX r3, FPINST @ FPINST (always there, rev0 onwards)
tst r1, #FPEXC_FPV2 @ is there an FPINST2 to read?
VFPFMRX r12, FPINST2, NE @ FPINST2 if needed - avoids reading
@ nonexistant reg on rev0
VFPFSTMIA r0 @ save the working registers
stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2
mov pc, lr
#endif
last_VFP_context_address: last_VFP_context_address:
.word last_VFP_context .word last_VFP_context
......
...@@ -28,7 +28,7 @@ void vfp_testing_entry(void); ...@@ -28,7 +28,7 @@ void vfp_testing_entry(void);
void vfp_support_entry(void); void vfp_support_entry(void);
void (*vfp_vector)(void) = vfp_testing_entry; void (*vfp_vector)(void) = vfp_testing_entry;
union vfp_state *last_VFP_context; union vfp_state *last_VFP_context[NR_CPUS];
/* /*
* Dual-use variable. * Dual-use variable.
...@@ -41,13 +41,35 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) ...@@ -41,13 +41,35 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
{ {
struct thread_info *thread = v; struct thread_info *thread = v;
union vfp_state *vfp; union vfp_state *vfp;
__u32 cpu = thread->cpu;
if (likely(cmd == THREAD_NOTIFY_SWITCH)) { if (likely(cmd == THREAD_NOTIFY_SWITCH)) {
u32 fpexc = fmrx(FPEXC);
#ifdef CONFIG_SMP
/*
* On SMP, if VFP is enabled, save the old state in
* case the thread migrates to a different CPU. The
* restoring is done lazily.
*/
if ((fpexc & FPEXC_ENABLE) && last_VFP_context[cpu]) {
vfp_save_state(last_VFP_context[cpu], fpexc);
last_VFP_context[cpu]->hard.cpu = cpu;
}
/*
* Thread migration, just force the reloading of the
* state on the new CPU in case the VFP registers
* contain stale data.
*/
if (thread->vfpstate.hard.cpu != cpu)
last_VFP_context[cpu] = NULL;
#endif
/* /*
* Always disable VFP so we can lazily save/restore the * Always disable VFP so we can lazily save/restore the
* old state. * old state.
*/ */
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE); fmxr(FPEXC, fpexc & ~FPEXC_ENABLE);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -68,8 +90,8 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) ...@@ -68,8 +90,8 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
} }
/* flush and release case: Per-thread VFP cleanup. */ /* flush and release case: Per-thread VFP cleanup. */
if (last_VFP_context == vfp) if (last_VFP_context[cpu] == vfp)
last_VFP_context = NULL; last_VFP_context[cpu] = NULL;
return NOTIFY_DONE; return NOTIFY_DONE;
} }
......
...@@ -35,6 +35,9 @@ struct vfp_hard_struct { ...@@ -35,6 +35,9 @@ struct vfp_hard_struct {
*/ */
__u32 fpinst; __u32 fpinst;
__u32 fpinst2; __u32 fpinst2;
#ifdef CONFIG_SMP
__u32 cpu;
#endif
}; };
union vfp_state { union vfp_state {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment