Utilize cpu hotplug infrastructure to pull down all other
cpus except cpu0, before starting suspend sequence. One
trick point is, cpu0 is a bit special and we'd better do
suspend on it. However vcpu0/dom0 is the one to trigger
power event which however may not bind to cpu0. So a new
softirq is introduced to switch flow to idle vcpu on cpu0
if such case happens.
Signed-off-by Kevin Tian <kevin.tian@xxxxxxxxx>
diff -r 308c4ef593e9 xen/arch/x86/acpi/power.c
--- a/xen/arch/x86/acpi/power.c Wed Feb 14 11:13:42 2007 +0800
+++ b/xen/arch/x86/acpi/power.c Wed Feb 14 14:59:23 2007 +0800
@@ -23,6 +23,7 @@
#include <xen/sched.h>
#include <xen/domain.h>
#include <xen/console.h>
+#include <xen/softirq.h>
u8 sleep_states[ACPI_S_STATE_COUNT];
DEFINE_SPINLOCK(pm_lock);
@@ -75,26 +76,48 @@ static void device_power_up(void)
console_resume();
}
-/* Main interface to do xen specific suspend/resume */
-int enter_state(u32 state)
+static void freeze_domains(void)
{
struct domain *d;
- unsigned long flags;
- int error;
-
- if (state <= ACPI_STATE_S0 || state > ACPI_S_STATES_MAX)
- return -EINVAL;
-
- if (!spin_trylock(&pm_lock))
- return -EBUSY;
-
+
for_each_domain(d)
if (d->domain_id != 0) {
domain_pause(d);
arch_domain_suspend(d);
}
-
+}
+
+static void thaw_domains(void)
+{
+ struct domain *d;
+
+ for_each_domain(d)
+ if (d->domain_id != 0)
+ domain_unpause(d);
+}
+
+/* Main interface to do xen specific suspend/resume */
+int enter_state(u32 state)
+{
+ unsigned long flags;
+ int error;
+
+ if (smp_processor_id() != 0)
+ return -EPERM;
+
+ if (state <= ACPI_STATE_S0 || state > ACPI_S_STATES_MAX)
+ return -EINVAL;
+
+ if (!spin_trylock(&pm_lock))
+ return -EBUSY;
+
printk("PM: Preparing system for %s sleep\n", acpi_states[state]);
+
+ disable_nonboot_cpus();
+ if (num_online_cpus() != 1) {
+ error = -EBUSY;
+ goto Enable_cpu;
+ }
local_irq_save(flags);
@@ -128,12 +151,10 @@ int enter_state(u32 state)
device_power_up();
printk("PM: Finishing wakeup.\n");
- for_each_domain(d)
- if (d->domain_id!=0)
- domain_unpause(d);
-
Done:
local_irq_restore(flags);
+ Enable_cpu:
+ enable_nonboot_cpus();
spin_unlock(&pm_lock);
return error;
@@ -202,7 +223,22 @@ int acpi_enter_sleep(struct xenpf_enter_
acpi_video_flags = sleep->video_flags;
saved_videomode = sleep->video_mode;
- return enter_state(acpi_sinfo.sleep_state);
+ freeze_domains();
+ if (current->processor == 0) {
+ int ret;
+
+ printk(XENLOG_INFO "vcpu0 on cpu0, sleep direclty\n");
+ ret = enter_state(acpi_sinfo.sleep_state);
+ thaw_domains();
+ return ret;
+ }
+
+ printk(XENLOG_INFO "vcpu0 on cpu%d, pause self and notify cpu0\n",
+ current->processor);
+ cpu_raise_softirq(0, PM_SOFTIRQ);
+ vcpu_pause_self();
+ /* return value doens't matter here. */
+ return 0;
}
static int acpi_get_wake_status(void)
@@ -228,6 +264,49 @@ acpi_status asmlinkage acpi_enter_sleep_
/* Wait until we enter sleep state, and spin until we wake */
while (!acpi_get_wake_status());
return_ACPI_STATUS(AE_OK);
+}
+
+/*
+ * Power management related softirq, and cpu0 only.
+ *
+ * The reason for introducing this softirq is that cpu0 is a bit
+ * special as the last one to be pull down. However the sleep request
+ * is issued from vcpu0 of dom0 and this vcpu may not bind to cpu0.
+ *
+ * So if above case happens, the CPU receiving sleep request will
+ * raise a softirq to cpu0 and idle vcpu on cpu0 then execute this
+ * handler immediately.
+ *
+ * If vcpu0 is already running on cpu0, this softirq is not triggered
+ */
+static void pm_softirq(void)
+{
+ int cpu = smp_processor_id();
+ struct vcpu *v = dom0->vcpu[0];
+ struct cpu_user_regs *regs;
+
+ printk(XENLOG_DEBUG "In pm_softirq\n");
+ /* only cpu0 handles this irq for now */
+ if (cpu != 0)
+ return;
+
+ printk(XENLOG_DEBUG "handled by cpu0\n");
+ /* wait vcpu0/dom0 to pause itself */
+ while ( test_bit(_VCPUF_migrating, &v->vcpu_flags) )
+ cpu_relax();
+
+ while ( test_bit(_VCPUF_need_sync, &v->vcpu_flags) )
+ cpu_relax();
+
+ printk(XENLOG_INFO "vcpu0/dom0 has been paused\n");
+ /* now safe to suspend whole system from cpu 0 */
+ regs = &v->arch.guest_context.user_regs;
+ regs->eax = enter_state(acpi_sinfo.sleep_state);
+
+ /* Now unpause vcpu0/dom0 */
+ vcpu_unpause(v);
+
+ thaw_domains();
}
static int __init acpi_sleep_init(void)
@@ -247,6 +326,8 @@ static int __init acpi_sleep_init(void)
printk(")\n");
acpi_reserve_bootmem();
+
+ open_softirq(PM_SOFTIRQ, pm_softirq);
return 0;
}
__initcall(acpi_sleep_init);
diff -r 308c4ef593e9 xen/include/asm-x86/smp.h
--- a/xen/include/asm-x86/smp.h Wed Feb 14 11:13:42 2007 +0800
+++ b/xen/include/asm-x86/smp.h Wed Feb 14 11:13:42 2007 +0800
@@ -70,6 +70,8 @@ extern void enable_nonboot_cpus(void);
extern void enable_nonboot_cpus(void);
#else
static inline int cpu_is_offline(int cpu) {return 0;}
+static inline void disable_nonboot_cpus(void) {}
+static inline void enable_nonboot_cpus(void) {}
#endif
/*
diff -r 308c4ef593e9 xen/include/xen/softirq.h
--- a/xen/include/xen/softirq.h Wed Feb 14 11:13:42 2007 +0800
+++ b/xen/include/xen/softirq.h Wed Feb 14 11:13:42 2007 +0800
@@ -10,8 +10,9 @@
#define PAGE_SCRUB_SOFTIRQ 5
#define TRACE_SOFTIRQ 6
#define RCU_SOFTIRQ 7
+#define PM_SOFTIRQ 8
-#define NR_COMMON_SOFTIRQS 8
+#define NR_COMMON_SOFTIRQS 9
#include <asm/softirq.h>
xen_smp_pm_support.patch
Description: xen_smp_pm_support.patch
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|