aboutsummaryrefslogtreecommitdiffstats
path: root/linux-2.6-xen-sparse
diff options
context:
space:
mode:
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>2007-03-02 12:11:52 +0000
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>2007-03-02 12:11:52 +0000
commit188ac9feda87c12b11af6f4c05ce04e653ca7b53 (patch)
treedeff6eddd3b4cfb745f3093adcebb31494a866be /linux-2.6-xen-sparse
parenta7c824cbf152d82bf3842e207089b1d3377051cf (diff)
downloadxen-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.c77
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c100
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/core/reboot.c18
-rw-r--r--linux-2.6-xen-sparse/include/xen/cpu_hotplug.h7
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);