Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 258261
b: refs/heads/master
c: f8f2a85
h: refs/heads/master
i:
  258259: 4fe6e24
v: v3
  • Loading branch information
Russell King committed Jul 9, 2011
1 parent 13d4f14 commit f4b8623
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 56 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 08409c33d6fdb43fa19d7dbdafd4f280b7835592
refs/heads/master: f8f2a8522a88aacd62a310ce49e8dac530d1b403
3 changes: 3 additions & 0 deletions trunk/arch/arm/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ int main(void)
DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value));
DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate));
DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate));
#ifdef CONFIG_SMP
DEFINE(VFP_CPU, offsetof(union vfp_state, hard.cpu));
#endif
#ifdef CONFIG_ARM_THUMBEE
DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state));
#endif
Expand Down
43 changes: 34 additions & 9 deletions trunk/arch/arm/vfp/vfphw.S
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,22 @@ ENTRY(vfp_support_entry)
ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer
bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled
cmp r4, r10 @ this thread owns the hw context?
#ifndef CONFIG_SMP
@ For UP, checking that this thread owns the hw context is
@ sufficient to determine that the hardware state is valid.
beq vfp_hw_state_valid

@ On UP, we lazily save the VFP context. As a different
@ thread wants ownership of the VFP hardware, save the old
@ state if there was a previous (valid) owner.

VFPFMXR FPEXC, r5 @ enable VFP, disable any pending
@ exceptions, so we can get at the
@ rest of it

#ifndef CONFIG_SMP
@ 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
cmp r4, #0
beq no_old_VFP_process
cmp r4, #0 @ if the vfp_current_hw_state is NULL
beq vfp_reload_hw @ then the hw state needs reloading
VFPFSTMIA r4, r5 @ save the working registers
VFPFMRX r5, FPSCR @ current status
#ifndef CONFIG_CPU_FEROCEON
Expand All @@ -107,11 +110,33 @@ ENTRY(vfp_support_entry)
1:
#endif
stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2
@ and point r4 at the word at the
@ start of the register dump
vfp_reload_hw:

#else
@ For SMP, if this thread does not own the hw context, then we
@ need to reload it. No need to save the old state as on SMP,
@ we always save the state when we switch away from a thread.
bne vfp_reload_hw

@ This thread has ownership of the current hardware context.
@ However, it may have been migrated to another CPU, in which
@ case the saved state is newer than the hardware context.
@ Check this by looking at the CPU number which the state was
@ last loaded onto.
ldr ip, [r10, #VFP_CPU]
teq ip, r11
beq vfp_hw_state_valid

vfp_reload_hw:
@ We're loading this threads state into the VFP hardware. Update
@ the CPU number which contains the most up to date VFP context.
str r11, [r10, #VFP_CPU]

VFPFMXR FPEXC, r5 @ enable VFP, disable any pending
@ exceptions, so we can get at the
@ rest of it
#endif

no_old_VFP_process:
DBGSTR1 "load state %p", r10
str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer
@ Load the saved state back into the VFP
Expand Down
98 changes: 52 additions & 46 deletions trunk/arch/arm/vfp/vfpmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,52 @@ void vfp_null_entry(void);

void (*vfp_vector)(void) = vfp_null_entry;

/*
* Dual-use variable.
* Used in startup: set to non-zero if VFP checks fail
* After startup, holds VFP architecture
*/
unsigned int VFP_arch;

/*
* The pointer to the vfpstate structure of the thread which currently
* owns the context held in the VFP hardware, or NULL if the hardware
* context is invalid.
*
* For UP, this is sufficient to tell which thread owns the VFP context.
* However, for SMP, we also need to check the CPU number stored in the
* saved state too to catch migrations.
*/
union vfp_state *vfp_current_hw_state[NR_CPUS];

/*
* Dual-use variable.
* Used in startup: set to non-zero if VFP checks fail
* After startup, holds VFP architecture
* Is 'thread's most up to date state stored in this CPUs hardware?
* Must be called from non-preemptible context.
*/
unsigned int VFP_arch;
static bool vfp_state_in_hw(unsigned int cpu, struct thread_info *thread)
{
#ifdef CONFIG_SMP
if (thread->vfpstate.hard.cpu != cpu)
return false;
#endif
return vfp_current_hw_state[cpu] == &thread->vfpstate;
}

/*
* Force a reload of the VFP context from the thread structure. We do
* this by ensuring that access to the VFP hardware is disabled, and
* clear last_VFP_context. Must be called from non-preemptible context.
*/
static void vfp_force_reload(unsigned int cpu, struct thread_info *thread)
{
if (vfp_state_in_hw(cpu, thread)) {
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
vfp_current_hw_state[cpu] = NULL;
}
#ifdef CONFIG_SMP
thread->vfpstate.hard.cpu = NR_CPUS;
#endif
}

/*
* Per-thread VFP initialization.
Expand All @@ -60,6 +93,9 @@ static void vfp_thread_flush(struct thread_info *thread)

vfp->hard.fpexc = FPEXC_EN;
vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
#ifdef CONFIG_SMP
vfp->hard.cpu = NR_CPUS;
#endif

/*
* Disable VFP to ensure we initialize it first. We must ensure
Expand Down Expand Up @@ -90,6 +126,9 @@ static void vfp_thread_copy(struct thread_info *thread)

vfp_sync_hwstate(parent);
thread->vfpstate = parent->vfpstate;
#ifdef CONFIG_SMP
thread->vfpstate.hard.cpu = NR_CPUS;
#endif
}

/*
Expand Down Expand Up @@ -135,17 +174,8 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
* case the thread migrates to a different CPU. The
* restoring is done lazily.
*/
if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
vfp_save_state(vfp_current_hw_state[cpu], fpexc);
vfp_current_hw_state[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)
vfp_current_hw_state[cpu] = NULL;
#endif

/*
Expand Down Expand Up @@ -449,15 +479,15 @@ static void vfp_pm_init(void)
static inline void vfp_pm_init(void) { }
#endif /* CONFIG_PM */

/*
* Ensure that the VFP state stored in 'thread->vfpstate' is up to date
* with the hardware state.
*/
void vfp_sync_hwstate(struct thread_info *thread)
{
unsigned int cpu = get_cpu();

/*
* If the thread we're interested in is the current owner of the
* hardware VFP state, then we need to save its state.
*/
if (vfp_current_hw_state[cpu] == &thread->vfpstate) {
if (vfp_state_in_hw(cpu, thread)) {
u32 fpexc = fmrx(FPEXC);

/*
Expand All @@ -471,36 +501,13 @@ void vfp_sync_hwstate(struct thread_info *thread)
put_cpu();
}

/* Ensure that the thread reloads the hardware VFP state on the next use. */
void vfp_flush_hwstate(struct thread_info *thread)
{
unsigned int cpu = get_cpu();

/*
* If the thread we're interested in is the current owner of the
* hardware VFP state, then we need to save its state.
*/
if (vfp_current_hw_state[cpu] == &thread->vfpstate) {
u32 fpexc = fmrx(FPEXC);
vfp_force_reload(cpu, thread);

fmxr(FPEXC, fpexc & ~FPEXC_EN);

/*
* Set the context to NULL to force a reload the next time
* the thread uses the VFP.
*/
vfp_current_hw_state[cpu] = NULL;
}

#ifdef CONFIG_SMP
/*
* For SMP we still have to take care of the case where the thread
* migrates to another CPU and then back to the original CPU on which
* the last VFP user is still the same thread. Mark the thread VFP
* state as belonging to a non-existent CPU so that the saved one will
* be reloaded in the above case.
*/
thread->vfpstate.hard.cpu = NR_CPUS;
#endif
put_cpu();
}

Expand All @@ -519,8 +526,7 @@ static int vfp_hotplug(struct notifier_block *b, unsigned long action,
void *hcpu)
{
if (action == CPU_DYING || action == CPU_DYING_FROZEN) {
unsigned int cpu = (long)hcpu;
vfp_current_hw_state[cpu] = NULL;
vfp_force_reload((long)hcpu, current_thread_info());
} else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
vfp_enable(NULL);
return NOTIFY_OK;
Expand Down

0 comments on commit f4b8623

Please sign in to comment.