Add SMP support to Xen host S3
Signed-off-by Kevin Tian <kevin.tian@intel.com>
diff -r 1539f5a2b3ba xen/arch/x86/acpi/power.c
--- a/xen/arch/x86/acpi/power.c Tue Jun 26 18:05:22 2007 -0400
+++ b/xen/arch/x86/acpi/power.c Tue Jun 26 19:44:36 2007 -0400
@@ -25,6 +25,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);
@@ -80,37 +81,77 @@ static void device_power_up(void)
console_resume();
}
-/* Main interface to do xen specific suspend/resume */
-int enter_state(u32 state)
-{
- struct domain *d, *pd = NULL;
- unsigned long flags;
- int error;
-
- if (state <= ACPI_STATE_S0 || state > ACPI_S_STATES_MAX)
- return -EINVAL;
-
- /* Sync lazy state on ths cpu */
- __sync_lazy_execstate();
- pmprintk(XENLOG_INFO, "Flush lazy state\n");
-
- if (!spin_trylock(&pm_lock))
- return -EBUSY;
-
+/* Record the last paused domain at freeze phase */
+static struct domain *pd;
+static int freeze_domains(void)
+{
+ struct domain *d;
+
+ pd = NULL;
for_each_domain(d)
if (d->domain_id != 0)
{
domain_pause(d);
if (is_hvm_domain(d) && !hvm_suspend_domain(d))
{
- error = -EINVAL;
- goto Unpause;
+ domain_unpause(d);
+ return 0;
}
pd = d;
}
-
+ return 1;
+}
+
+static void thaw_domains(void)
+{
+ struct domain *d;
+
+ if (pd)
+ {
+ for_each_domain(d)
+ {
+ if (d->domain_id != 0)
+ domain_unpause(d);
+
+ /* Unpause until recorded last paused domain */
+ if (d == pd)
+ break;
+ }
+ }
+}
+
+/* Main interface to do xen specific suspend/resume */
+int enter_state(u32 state)
+{
+ unsigned long flags;
+ cpumask_t mask = cpu_online_map;
+ int error;
+
+ if (state <= ACPI_STATE_S0 || state > ACPI_S_STATES_MAX)
+ return -EINVAL;
+
+ /* Sync lazy state on ths cpu */
+ __sync_lazy_execstate();
+ pmprintk(XENLOG_INFO, "Flush lazy state\n");
+
+ if (!spin_trylock(&pm_lock))
+ return -EBUSY;
+
pmprintk(XENLOG_INFO, "PM: Preparing system for %s sleep\n",
acpi_states[state]);
+
+ /* Sync all lazy states on other cpus, since APs will be
+ * re-intialized like fresh boot and stale context loses
+ */
+ cpu_clear(0, mask);
+ flush_tlb_mask(mask);
+ pmprintk(XENLOG_INFO, "Finish lazy state sync\n");
+
+ disable_nonboot_cpus();
+ if (num_online_cpus() != 1) {
+ error = -EBUSY;
+ goto Enable_cpu;
+ }
local_irq_save(flags);
@@ -141,20 +182,8 @@ int enter_state(u32 state)
Done:
local_irq_restore(flags);
- Unpause:
- if (pd)
- {
- for_each_domain(d)
- {
- /* Unpause until recorded last paused domain */
- if (d == pd)
- break;
-
- if (d->domain_id != 0)
- domain_unpause(d);
- }
- }
-
+ Enable_cpu:
+ enable_nonboot_cpus();
spin_unlock(&pm_lock);
return error;
}
@@ -181,6 +210,14 @@ int set_acpi_sleep_info(struct xenpf_set
acpi_sinfo.pm1a_evt, acpi_sinfo.pm1b_evt,
info->xen_waking_vec);
return 0;
+}
+
+static void acpi_power_off(void)
+{
+ pmprintk(XENLOG_INFO, "%s called\n", __FUNCTION__);
+ local_irq_disable();
+ /* Some SMP machines only can poweroff in boot CPU */
+ acpi_enter_sleep_state(ACPI_STATE_S5);
}
/*
@@ -224,7 +261,34 @@ 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);
+ /* acpi power off method */
+ if (acpi_sinfo.sleep_state == ACPI_STATE_S5) {
+ acpi_power_off();
+ /* Shouldn''t return */
+ while(1);
+ }
+
+ if (!freeze_domains())
+ {
+ pmprintk(XENLOG_ERR, "Failed to freeze domains\n");
+ return -EINVAL;
+ }
+
+ if (current->processor == 0) {
+ int ret;
+
+ pmprintk(XENLOG_INFO, "vcpu0 on cpu0, sleep direclty\n");
+ ret = enter_state(acpi_sinfo.sleep_state);
+ thaw_domains();
+ return ret;
+ }
+
+ pmprintk(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)
@@ -250,6 +314,51 @@ 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;
+
+ pmprintk(XENLOG_DEBUG, "In pm_softirq\n");
+ /* only cpu0 handles this irq for now */
+ if (cpu != 0)
+ return;
+
+ pmprintk(XENLOG_DEBUG, "handled by cpu0\n");
+ /* Wait vcpu0/dom0 to be paused */
+ while ( !atomic_read(&v->pause_count) )
+ cpu_relax();
+
+ /* Then wait for context of vcpu/dom0 to be sync-ed */
+ while ( test_bit(_VPF_need_sync, &v->pause_flags) )
+ cpu_relax();
+
+ pmprintk(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)
@@ -268,6 +377,8 @@ static int __init acpi_sleep_init(void)
sleep_states[i] = 0;
}
printk(")\n");
+
+ open_softirq(PM_SOFTIRQ, pm_softirq);
return 0;
}
__initcall(acpi_sleep_init);
diff -r 1539f5a2b3ba xen/include/asm-x86/smp.h
--- a/xen/include/asm-x86/smp.h Tue Jun 26 18:05:22 2007 -0400
+++ b/xen/include/asm-x86/smp.h Tue Jun 26 18:05:22 2007 -0400
@@ -69,6 +69,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 1539f5a2b3ba xen/include/xen/softirq.h
--- a/xen/include/xen/softirq.h Tue Jun 26 18:05:22 2007 -0400
+++ b/xen/include/xen/softirq.h Tue Jun 26 18:05:22 2007 -0400
@@ -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-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel