diff options
author | sos22@douglas.cl.cam.ac.uk <sos22@douglas.cl.cam.ac.uk> | 2005-08-17 14:37:22 +0000 |
---|---|---|
committer | sos22@douglas.cl.cam.ac.uk <sos22@douglas.cl.cam.ac.uk> | 2005-08-17 14:37:22 +0000 |
commit | 0b86412f9dad3c5cd990e51b0501f8551353e111 (patch) | |
tree | 9ef5356231f509df995f1e7ac8f7c8e6f05d53f6 | |
parent | 806ac068522c4ef96a8bf302004d725ab9e20431 (diff) | |
download | xen-0b86412f9dad3c5cd990e51b0501f8551353e111.tar.gz xen-0b86412f9dad3c5cd990e51b0501f8551353e111.tar.bz2 xen-0b86412f9dad3c5cd990e51b0501f8551353e111.zip |
Make xm save/restore work for SMP guest domains.
Signed-off-by: Steven Smith, sos22@cam.ac.uk
-rw-r--r-- | linux-2.6-xen-sparse/arch/xen/kernel/reboot.c | 80 |
1 files changed, 78 insertions, 2 deletions
diff --git a/linux-2.6-xen-sparse/arch/xen/kernel/reboot.c b/linux-2.6-xen-sparse/arch/xen/kernel/reboot.c index 445daf01e6..eb4d712ec7 100644 --- a/linux-2.6-xen-sparse/arch/xen/kernel/reboot.c +++ b/linux-2.6-xen-sparse/arch/xen/kernel/reboot.c @@ -16,6 +16,8 @@ #include <asm-xen/queues.h> #include <asm-xen/xenbus.h> #include <asm-xen/ctrl_if.h> +#include <linux/cpu.h> +#include <linux/kthread.h> #define SHUTDOWN_INVALID -1 #define SHUTDOWN_POWEROFF 0 @@ -58,7 +60,12 @@ EXPORT_SYMBOL(machine_power_off); /* Ignore multiple shutdown requests. */ static int shutting_down = SHUTDOWN_INVALID; -static void __do_suspend(void) +#ifndef CONFIG_HOTPLUG_CPU +#define cpu_down(x) (-EOPNOTSUPP) +#define cpu_up(x) (-EOPNOTSUPP) +#endif + +static int __do_suspend(void *ignore) { int i, j; suspend_record_t *suspend_record; @@ -104,10 +111,49 @@ static void __do_suspend(void) extern unsigned long max_pfn; extern unsigned int *pfn_to_mfn_frame_list; + cpumask_t feasible_cpus; + int err = 0; + + BUG_ON(smp_processor_id() != 0); + BUG_ON(in_interrupt()); + +#if defined(CONFIG_SMP) && !defined(CONFIG_HOTPLUG_CPU) + if (num_online_cpus() > 1) { + printk(KERN_WARNING "Can't suspend SMP guests without CONFIG_HOTPLUG_CPU\n"); + return -EOPNOTSUPP; + } +#endif + suspend_record = (suspend_record_t *)__get_free_page(GFP_KERNEL); if ( suspend_record == NULL ) goto out; + /* Take all of the other cpus offline. We need to be careful not + to get preempted between the final test for num_online_cpus() + == 1 and disabling interrupts, since otherwise userspace could + bring another cpu online, and then we'd be stuffed. At the + same time, cpu_down can reschedule, so we need to enable + preemption while doing that. This kind of sucks, but should be + correct. */ + /* (We don't need to worry about other cpus bringing stuff up, + since by the time num_online_cpus() == 1, there aren't any + other cpus) */ + cpus_clear(feasible_cpus); + preempt_disable(); + while (num_online_cpus() > 1) { + preempt_enable(); + for_each_online_cpu(i) { + if (i == 0) + continue; + err = cpu_down(i); + if (err != 0) { + printk(KERN_CRIT "Failed to take all CPUs down: %d.\n", err); + goto out_reenable_cpus; + } + cpu_set(i, feasible_cpus); + } + } + suspend_record->nr_pfns = max_pfn; /* final number of pfns */ __cli(); @@ -141,8 +187,11 @@ static void __do_suspend(void) memcpy(&suspend_record->resume_info, &xen_start_info, sizeof(xen_start_info)); + /* We'll stop somewhere inside this hypercall. When it returns, + we'll start resuming after the restore. */ HYPERVISOR_suspend(virt_to_machine(suspend_record) >> PAGE_SHIFT); + shutting_down = SHUTDOWN_INVALID; memcpy(&xen_start_info, &suspend_record->resume_info, @@ -182,11 +231,26 @@ static void __do_suspend(void) usbif_resume(); + preempt_enable(); + __sti(); + out_reenable_cpus: + while (!cpus_empty(feasible_cpus)) { + i = first_cpu(feasible_cpus); + j = cpu_up(i); + if (j != 0) { + printk(KERN_CRIT "Failed to bring cpu %d back up (%d).\n", + i, j); + err = j; + } + cpu_clear(i, feasible_cpus); + } + out: if ( suspend_record != NULL ) free_page((unsigned long)suspend_record); + return err; } static int shutdown_process(void *__unused) @@ -233,6 +297,18 @@ static int shutdown_process(void *__unused) return 0; } +static struct task_struct *kthread_create_on_cpu(int (*f)(void *arg), + void *arg, + const char *name, + int cpu) +{ + struct task_struct *p; + p = kthread_create(f, arg, name); + kthread_bind(p, cpu); + wake_up_process(p); + return p; +} + static void __shutdown_handler(void *unused) { int err; @@ -245,7 +321,7 @@ static void __shutdown_handler(void *unused) } else { - __do_suspend(); + kthread_create_on_cpu(__do_suspend, NULL, "suspender", 0); } } |