ARM: tegra: pm: hold cpus unti all have booted when exiting lp2
Colin Cross [Wed, 24 Aug 2011 02:26:33 +0000 (19:26 -0700)]
When exiting lp2, each cpu boots through cpu_resume, which
modifies the last used page table to add a 1-1 mapping in
order to turn on the mmu.  The first cpu to boot triggers
booting the second cpu, and if allowed to continue immediately
may start executing a userspace task that is using the same
page tables as the second cpu is modifying during its boot
process.  Hold each cpu in a loop until all cpus have
finished booting to ensure page tables are back to their
original state.  Each cpu triggers a global tlb flush
after it restores the page table, so all cpus will see the
original values before they exit idle.

Change-Id: Iad91ae57e2abbbec3d6d491460c3e19411b519c0
Signed-off-by: Colin Cross <ccross@android.com>

Rebase-Id: R110a4b1f28846af97edd39bb382e1e09c3f92330

arch/arm/mach-tegra/pm.c

index ff766db..8ef08df 100644 (file)
@@ -414,18 +414,9 @@ void tegra_idle_lp2_last(void)
        restore_cpu_complex();
        cpu_cluster_pm_exit();
 
-       spin_lock(&tegra_lp2_lock);
-
-       cpumask_clear_cpu(cpu, &tegra_in_lp2);
-
-       for_each_online_cpu(i) {
-               if (i != cpu) {
+       for_each_online_cpu(i)
+               if (i != cpu)
                        tegra_wake_reset_cpu(i);
-                       cpumask_clear_cpu(i, &tegra_in_lp2);
-               }
-       }
-
-       spin_unlock(&tegra_lp2_lock);
 }
 
 void tegra_idle_lp2(void)
@@ -454,6 +445,19 @@ void tegra_idle_lp2(void)
 
        spin_lock(&tegra_lp2_lock);
        cpumask_clear_cpu(cpu, &tegra_in_lp2);
+
+       /*
+        * cpus coming out of idle muck with page tables that belong to the
+        * last process executed before idle.  Don't release any cpus back to
+        * the scheduler until all cpus have booted to avoid modifying the
+        * page table of a running process on another cpu.
+        */
+       while (!cpumask_empty(&tegra_in_lp2)) {
+               spin_unlock(&tegra_lp2_lock);
+               cpu_relax();
+               spin_lock(&tegra_lp2_lock);
+       }
+
        spin_unlock(&tegra_lp2_lock);
 }