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@intel.com> 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-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel