diff options
author | kfraser@localhost.localdomain <kfraser@localhost.localdomain> | 2007-03-02 12:11:52 +0000 |
---|---|---|
committer | kfraser@localhost.localdomain <kfraser@localhost.localdomain> | 2007-03-02 12:11:52 +0000 |
commit | 188ac9feda87c12b11af6f4c05ce04e653ca7b53 (patch) | |
tree | deff6eddd3b4cfb745f3093adcebb31494a866be /linux-2.6-xen-sparse | |
parent | a7c824cbf152d82bf3842e207089b1d3377051cf (diff) | |
download | xen-188ac9feda87c12b11af6f4c05ce04e653ca7b53.tar.gz xen-188ac9feda87c12b11af6f4c05ce04e653ca7b53.tar.bz2 xen-188ac9feda87c12b11af6f4c05ce04e653ca7b53.zip |
linux: Support new 'fast suspend' mode which does not require us to
hotplug all auxiliary CPUs.
Signed-off-by: Keir Fraser <keir@xensource.com>
Diffstat (limited to 'linux-2.6-xen-sparse')
-rw-r--r-- | linux-2.6-xen-sparse/drivers/xen/core/evtchn.c | 77 | ||||
-rw-r--r-- | linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c | 100 | ||||
-rw-r--r-- | linux-2.6-xen-sparse/drivers/xen/core/reboot.c | 18 | ||||
-rw-r--r-- | linux-2.6-xen-sparse/include/xen/cpu_hotplug.h | 7 |
4 files changed, 128 insertions, 74 deletions
diff --git a/linux-2.6-xen-sparse/drivers/xen/core/evtchn.c b/linux-2.6-xen-sparse/drivers/xen/core/evtchn.c index e0657a6c87..fda12a3f32 100644 --- a/linux-2.6-xen-sparse/drivers/xen/core/evtchn.c +++ b/linux-2.6-xen-sparse/drivers/xen/core/evtchn.c @@ -888,48 +888,20 @@ void unmask_evtchn(int port) } EXPORT_SYMBOL_GPL(unmask_evtchn); -void irq_resume(void) +static void restore_cpu_virqs(int cpu) { struct evtchn_bind_virq bind_virq; - struct evtchn_bind_ipi bind_ipi; - int cpu, pirq, virq, ipi, irq, evtchn; - - init_evtchn_cpu_bindings(); - - /* New event-channel space is not 'live' yet. */ - for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) - mask_evtchn(evtchn); + int virq, irq, evtchn; - /* Check that no PIRQs are still bound. */ - for (pirq = 0; pirq < NR_PIRQS; pirq++) - BUG_ON(irq_info[pirq_to_irq(pirq)] != IRQ_UNBOUND); - - /* Secondary CPUs must have no VIRQ or IPI bindings. */ - for_each_possible_cpu(cpu) { - if (cpu == 0) - continue; - for (virq = 0; virq < NR_VIRQS; virq++) - BUG_ON(per_cpu(virq_to_irq, cpu)[virq] != -1); - for (ipi = 0; ipi < NR_IPIS; ipi++) - BUG_ON(per_cpu(ipi_to_irq, cpu)[ipi] != -1); - } - - /* No IRQ <-> event-channel mappings. */ - for (irq = 0; irq < NR_IRQS; irq++) - irq_info[irq] &= ~0xFFFF; /* zap event-channel binding */ - for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) - evtchn_to_irq[evtchn] = -1; - - /* Primary CPU: rebind VIRQs automatically. */ for (virq = 0; virq < NR_VIRQS; virq++) { - if ((irq = per_cpu(virq_to_irq, 0)[virq]) == -1) + if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) continue; BUG_ON(irq_info[irq] != mk_irq_info(IRQT_VIRQ, virq, 0)); /* Get a new binding from Xen. */ bind_virq.virq = virq; - bind_virq.vcpu = 0; + bind_virq.vcpu = cpu; if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind_virq) != 0) BUG(); @@ -938,20 +910,26 @@ void irq_resume(void) /* Record the new mapping. */ evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); + bind_evtchn_to_cpu(evtchn, cpu); /* Ready for use. */ unmask_evtchn(evtchn); } +} + +static void restore_cpu_ipis(int cpu) +{ + struct evtchn_bind_ipi bind_ipi; + int ipi, irq, evtchn; - /* Primary CPU: rebind IPIs automatically. */ for (ipi = 0; ipi < NR_IPIS; ipi++) { - if ((irq = per_cpu(ipi_to_irq, 0)[ipi]) == -1) + if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1) continue; BUG_ON(irq_info[irq] != mk_irq_info(IRQT_IPI, ipi, 0)); /* Get a new binding from Xen. */ - bind_ipi.vcpu = 0; + bind_ipi.vcpu = cpu; if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi) != 0) BUG(); @@ -960,12 +938,41 @@ void irq_resume(void) /* Record the new mapping. */ evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); + bind_evtchn_to_cpu(evtchn, cpu); /* Ready for use. */ unmask_evtchn(evtchn); + } } +void irq_resume(void) +{ + int cpu, pirq, irq, evtchn; + + init_evtchn_cpu_bindings(); + + /* New event-channel space is not 'live' yet. */ + for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) + mask_evtchn(evtchn); + + /* Check that no PIRQs are still bound. */ + for (pirq = 0; pirq < NR_PIRQS; pirq++) + BUG_ON(irq_info[pirq_to_irq(pirq)] != IRQ_UNBOUND); + + /* No IRQ <-> event-channel mappings. */ + for (irq = 0; irq < NR_IRQS; irq++) + irq_info[irq] &= ~0xFFFF; /* zap event-channel binding */ + for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) + evtchn_to_irq[evtchn] = -1; + + for_each_possible_cpu(cpu) { + restore_cpu_virqs(cpu); + restore_cpu_ipis(cpu); + } + +} + void __init xen_init_IRQ(void) { int i; diff --git a/linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c b/linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c index 95337b8077..d8667b336a 100644 --- a/linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c +++ b/linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c @@ -1,4 +1,3 @@ -#define __KERNEL_SYSCALLS__ #include <linux/version.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -7,6 +6,7 @@ #include <linux/reboot.h> #include <linux/sysrq.h> #include <linux/stringify.h> +#include <linux/stop_machine.h> #include <asm/irq.h> #include <asm/mmu_context.h> #include <xen/evtchn.h> @@ -18,6 +18,7 @@ #include <xen/gnttab.h> #include <xen/xencons.h> #include <xen/cpu_hotplug.h> +#include <xen/interface/vcpu.h> #if defined(__i386__) || defined(__x86_64__) @@ -98,7 +99,6 @@ static void post_suspend(int suspend_cancelled) xen_start_info->console.domU.mfn = pfn_to_mfn(xen_start_info->console.domU.mfn); } else { - extern cpumask_t cpu_initialized_map; cpu_initialized_map = cpumask_of_cpu(0); } @@ -133,44 +133,35 @@ static void post_suspend(int suspend_cancelled) #endif -int __xen_suspend(void) +static int take_machine_down(void *p_fast_suspend) { - int err, suspend_cancelled; - + int fast_suspend = *(int *)p_fast_suspend; + int suspend_cancelled, err, cpu; extern void time_resume(void); - BUG_ON(smp_processor_id() != 0); - BUG_ON(in_interrupt()); - -#if defined(__i386__) || defined(__x86_64__) - if (xen_feature(XENFEAT_auto_translated_physmap)) { - printk(KERN_WARNING "Cannot suspend in " - "auto_translated_physmap mode.\n"); - return -EOPNOTSUPP; - } -#endif - - for (;;) { - err = smp_suspend(); - if (err) - return err; - - xenbus_suspend(); + if (fast_suspend) { preempt_disable(); + } else { + for (;;) { + err = smp_suspend(); + if (err) + return err; - if (num_online_cpus() == 1) - break; + xenbus_suspend(); + preempt_disable(); - preempt_enable(); - xenbus_suspend_cancel(); + if (num_online_cpus() == 1) + break; + + preempt_enable(); + xenbus_suspend_cancel(); + } } mm_pin_all(); local_irq_disable(); preempt_enable(); - gnttab_suspend(); - pre_suspend(); /* @@ -180,18 +171,56 @@ int __xen_suspend(void) suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info)); post_suspend(suspend_cancelled); - gnttab_resume(); - if (!suspend_cancelled) irq_resume(); - time_resume(); - switch_idle_mm(); - local_irq_enable(); + if (fast_suspend && !suspend_cancelled) { + /* + * In fast-suspend mode the APs may not be brought back online + * when we resume. In that case we do it here. + */ + for_each_online_cpu(cpu) { + if (cpu == 0) + continue; + cpu_set_initialized(cpu); + err = HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL); + BUG_ON(err); + } + } + + return suspend_cancelled; +} + +int __xen_suspend(int fast_suspend) +{ + int err, suspend_cancelled; + + BUG_ON(smp_processor_id() != 0); + BUG_ON(in_interrupt()); + +#if defined(__i386__) || defined(__x86_64__) + if (xen_feature(XENFEAT_auto_translated_physmap)) { + printk(KERN_WARNING "Cannot suspend in " + "auto_translated_physmap mode.\n"); + return -EOPNOTSUPP; + } +#endif + + if (fast_suspend) { + xenbus_suspend(); + err = stop_machine_run(take_machine_down, &fast_suspend, 0); + } else { + err = take_machine_down(&fast_suspend); + } + + if (err < 0) + return err; + + suspend_cancelled = err; if (!suspend_cancelled) { xencons_resume(); xenbus_resume(); @@ -199,7 +228,8 @@ int __xen_suspend(void) xenbus_suspend_cancel(); } - smp_resume(); + if (!fast_suspend) + smp_resume(); - return err; + return 0; } diff --git a/linux-2.6-xen-sparse/drivers/xen/core/reboot.c b/linux-2.6-xen-sparse/drivers/xen/core/reboot.c index aab244a738..0dcd2fa252 100644 --- a/linux-2.6-xen-sparse/drivers/xen/core/reboot.c +++ b/linux-2.6-xen-sparse/drivers/xen/core/reboot.c @@ -24,13 +24,16 @@ MODULE_LICENSE("Dual BSD/GPL"); /* Ignore multiple shutdown requests. */ static int shutting_down = SHUTDOWN_INVALID; +/* Can we leave APs online when we suspend? */ +static int fast_suspend; + static void __shutdown_handler(void *unused); static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL); #ifdef CONFIG_XEN -int __xen_suspend(void); +int __xen_suspend(int fast_suspend); #else -#define __xen_suspend() (void)0 +#define __xen_suspend(fast_suspend) 0 #endif static int shutdown_process(void *__unused) @@ -44,7 +47,8 @@ static int shutdown_process(void *__unused) if ((shutting_down == SHUTDOWN_POWEROFF) || (shutting_down == SHUTDOWN_HALT)) { - if (call_usermodehelper("/sbin/poweroff", poweroff_argv, envp, 0) < 0) { + if (call_usermodehelper("/sbin/poweroff", poweroff_argv, + envp, 0) < 0) { #ifdef CONFIG_XEN sys_reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, @@ -61,7 +65,9 @@ static int shutdown_process(void *__unused) static int xen_suspend(void *__unused) { - __xen_suspend(); + int err = __xen_suspend(fast_suspend); + if (err) + printk(KERN_ERR "Xen suspend failed (%d)\n", err); shutting_down = SHUTDOWN_INVALID; return 0; } @@ -193,6 +199,10 @@ static int setup_shutdown_watcher(struct notifier_block *notifier, { int err; + xenbus_scanf(XBT_NIL, "control", + "platform-feature-multiprocessor-suspend", + "%d", &fast_suspend); + err = register_xenbus_watch(&shutdown_watch); if (err) printk(KERN_ERR "Failed to set shutdown watcher\n"); diff --git a/linux-2.6-xen-sparse/include/xen/cpu_hotplug.h b/linux-2.6-xen-sparse/include/xen/cpu_hotplug.h index 234503f1ea..8c9eeb2a98 100644 --- a/linux-2.6-xen-sparse/include/xen/cpu_hotplug.h +++ b/linux-2.6-xen-sparse/include/xen/cpu_hotplug.h @@ -4,6 +4,13 @@ #include <linux/kernel.h> #include <linux/cpumask.h> +#if defined(CONFIG_X86) +extern cpumask_t cpu_initialized_map; +#define cpu_set_initialized(cpu) cpu_set(cpu, cpu_initialized_map) +#else +#define cpu_set_initialized(cpu) ((void)0) +#endif + #if defined(CONFIG_HOTPLUG_CPU) int cpu_up_check(unsigned int cpu); |