aboutsummaryrefslogtreecommitdiffstats
path: root/unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c
diff options
context:
space:
mode:
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.c73
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;
}