diff options
Diffstat (limited to 'unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c')
-rw-r--r-- | unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c | 73 |
1 files changed, 65 insertions, 8 deletions
diff --git a/unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c b/unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c index d14bdbf1d9..c6b1f91b7f 100644 --- a/unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c +++ b/unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c @@ -1,24 +1,81 @@ #include <linux/config.h> +#include <linux/stop_machine.h> +#include <xen/evtchn.h> +#include <xen/gnttab.h> #include <xen/xenbus.h> #include "platform-pci.h" #include <asm/hypervisor.h> -int __xen_suspend(int fast_suspend) +/* + * Spinning prevents, for example, APs touching grant table entries while + * the shared grant table is not mapped into the address space imemdiately + * after resume. + */ +static void ap_suspend(void *_ap_spin) +{ + int *ap_spin = _ap_spin; + + BUG_ON(!irqs_disabled()); + + while (*ap_spin) { + cpu_relax(); + HYPERVISOR_yield(); + } +} + +static int bp_suspend(void) { int suspend_cancelled; - xenbus_suspend(); - platform_pci_suspend(); + BUG_ON(!irqs_disabled()); suspend_cancelled = HYPERVISOR_shutdown(SHUTDOWN_suspend); - if (suspend_cancelled) { - platform_pci_suspend_cancel(); - xenbus_suspend_cancel(); - } else { + if (!suspend_cancelled) { platform_pci_resume(); - xenbus_resume(); + gnttab_resume(); + irq_resume(); + } + + return suspend_cancelled; +} + +int __xen_suspend(int fast_suspend) +{ + int err, suspend_cancelled, ap_spin; + + xenbus_suspend(); + + preempt_disable(); + + /* Prevent any races with evtchn_interrupt() handler. */ + disable_irq(xen_platform_pdev->irq); + + ap_spin = 1; + smp_mb(); + + err = smp_call_function(ap_suspend, &ap_spin, 0, 0); + if (err < 0) { + preempt_enable(); + xenbus_suspend_cancel(); + return err; } + local_irq_disable(); + suspend_cancelled = bp_suspend(); + local_irq_enable(); + + smp_mb(); + ap_spin = 0; + + enable_irq(xen_platform_pdev->irq); + + preempt_enable(); + + if (!suspend_cancelled) + xenbus_resume(); + else + xenbus_suspend_cancel(); + return 0; } |