Revert "ARM: vfp: fix a hole in VFP thread migration"
Colin Cross [Sat, 17 Sep 2011 01:41:13 +0000 (18:41 -0700)]
This reverts commit c0822d4f0bcccf227b751cfe1c047f3bdae0f1ce.

arch/arm/kernel/asm-offsets.c
arch/arm/vfp/vfphw.S
arch/arm/vfp/vfpmodule.c

index 16baba2..927522c 100644 (file)
@@ -59,9 +59,6 @@ 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
index 2d30c7f..404538a 100644 (file)
@@ -82,22 +82,19 @@ 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                  @ if the vfp_current_hw_state is NULL
-       beq     vfp_reload_hw           @ then the hw state needs reloading
+       cmp     r4, #0
+       beq     no_old_VFP_process
        VFPFSTMIA r4, r5                @ save the working registers
        VFPFMRX r5, FPSCR               @ current status
 #ifndef CONFIG_CPU_FEROCEON
@@ -110,33 +107,11 @@ ENTRY(vfp_support_entry)
 1:
 #endif
        stmia   r4, {r1, r5, r6, r8}    @ save FPEXC, FPSCR, FPINST, FPINST2
-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
+                                       @ and point r4 at the word at the
+                                       @ start of the register dump
 #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
index daa69a2..871f03c 100644 (file)
@@ -35,51 +35,18 @@ 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];
 
 /*
- * Is 'thread's most up to date state stored in this CPUs hardware?
- * Must be called from non-preemptible context.
- */
-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.
+ * Dual-use variable.
+ * Used in startup: set to non-zero if VFP checks fail
+ * After startup, holds VFP architecture
  */
-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
-}
+unsigned int VFP_arch;
 
 /*
  * Per-thread VFP initialization.
@@ -93,9 +60,6 @@ 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
@@ -126,9 +90,6 @@ 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
 }
 
 /*
@@ -174,8 +135,17 @@ 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
 
                /*
@@ -483,15 +453,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 (vfp_state_in_hw(cpu, thread)) {
+       /*
+        * 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);
 
                /*
@@ -505,13 +475,36 @@ 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();
 
-       vfp_force_reload(cpu, thread);
+       /*
+        * 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);
 
+               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();
 }
 
@@ -530,7 +523,8 @@ static int vfp_hotplug(struct notifier_block *b, unsigned long action,
        void *hcpu)
 {
        if (action == CPU_DYING || action == CPU_DYING_FROZEN) {
-               vfp_force_reload((long)hcpu, current_thread_info());
+               unsigned int cpu = (long)hcpu;
+               vfp_current_hw_state[cpu] = NULL;
        } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
                vfp_enable(NULL);
        return NOTIFY_OK;