x86/S3: add cache flush on secondary CPUs before going to sleep
authorBen Guthro <ben@guthro.net>
Tue, 25 Sep 2012 06:38:14 +0000 (08:38 +0200)
committerBen Guthro <ben@guthro.net>
Tue, 25 Sep 2012 06:38:14 +0000 (08:38 +0200)
Secondary CPUs, between doing their final memory writes (particularly
updating cpu_initialized) and getting a subsequent INIT, may not write
back all modified data. The INIT itself then causes those modifications
to be lost, so in the cpu_initialized case the CPU would find itself
already initialized, (intentionally) entering an infinite loop instead
of actually coming online.

Signed-off-by: Ben Guthro <ben@guthro.net>
Make acpi_dead_idle() call default_dead_idle() rather than duplicating
the logic there.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
Committed-by: Jan Beulich <jbeulich@suse.com>
xen/arch/x86/acpi/cpu_idle.c
xen/arch/x86/domain.c
xen/include/asm-x86/cpuidle.h

index 45eb6032425b83fab31f0165cc7022a15abc35b4..fd4c42bfec9d9d49774befc8888cd2563dd7b6a2 100644 (file)
@@ -659,8 +659,7 @@ void acpi_dead_idle(void)
     }
 
 default_halt:
-    for ( ; ; )
-        halt();
+    default_dead_idle();
 }
 
 int cpuidle_init_cpu(unsigned int cpu)
index 9f9bfd3af795fbec174ddae0633fe1e1461ad143..ec0d06f79fd8d699f88cce942505ab7f42c617b8 100644 (file)
@@ -45,6 +45,7 @@
 #include <asm/desc.h>
 #include <asm/i387.h>
 #include <asm/xstate.h>
+#include <asm/cpuidle.h>
 #include <asm/mpspec.h>
 #include <asm/ldt.h>
 #include <asm/fixmap.h>
@@ -64,7 +65,6 @@ DEFINE_PER_CPU(struct vcpu *, curr_vcpu);
 DEFINE_PER_CPU(unsigned long, cr4);
 
 static void default_idle(void);
-static void default_dead_idle(void);
 void (*pm_idle) (void) __read_mostly = default_idle;
 void (*dead_idle) (void) __read_mostly = default_dead_idle;
 
@@ -82,8 +82,14 @@ static void default_idle(void)
         local_irq_enable();
 }
 
-static void default_dead_idle(void)
+void default_dead_idle(void)
 {
+    /*
+     * When going into S3, without flushing caches modified data may be
+     * held by the CPUs spinning here indefinitely, and get discarded by
+     * a subsequent INIT.
+     */
+    wbinvd();
     for ( ; ; )
         halt();
 }
index ac1dddf0696b484d6de55be893e4fda427aa17c8..4d70677d09a91693cdd978c9153e1c6f9005ad1b 100644 (file)
@@ -18,6 +18,7 @@ extern uint64_t (*cpuidle_get_tick)(void);
 
 int mwait_idle_init(struct notifier_block *);
 int cpuidle_init_cpu(unsigned int cpu);
+void default_dead_idle(void);
 void acpi_dead_idle(void);
 void trace_exit_reason(u32 *irq_traced);
 void update_idle_stats(struct acpi_processor_power *,