[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [linux-2.6.18-xen] xen suspend: Fix suspend-via-evtchn reentrancy.



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1217514834 -3600
# Node ID 2866e6af503ea0b33e1c1fb2340ab8ed81925e97
# Parent  44e3ace9a1f154941f7ccfb97cb75a8ab01ac3d1
xen suspend: Fix suspend-via-evtchn reentrancy.
Signed-off-by: Keir Fraser <keir.fraser@xxxxxxxxxx>
---
 drivers/xen/core/machine_reboot.c |    9 +----
 drivers/xen/core/reboot.c         |   63 ++++++++++++++++++++++++++------------
 2 files changed, 47 insertions(+), 25 deletions(-)

diff -r 44e3ace9a1f1 -r 2866e6af503e drivers/xen/core/machine_reboot.c
--- a/drivers/xen/core/machine_reboot.c Thu Jul 31 09:46:58 2008 +0100
+++ b/drivers/xen/core/machine_reboot.c Thu Jul 31 15:33:54 2008 +0100
@@ -26,8 +26,6 @@ void (*pm_power_off)(void);
 void (*pm_power_off)(void);
 EXPORT_SYMBOL(pm_power_off);
 
-int setup_suspend_evtchn(void);
-
 void machine_emergency_restart(void)
 {
        /* We really want to get pending console data out before we die. */
@@ -133,7 +131,7 @@ static void post_suspend(int suspend_can
 
 struct suspend {
        int fast_suspend;
-       void (*resume_notifier)(void);
+       void (*resume_notifier)(int);
 };
 
 static int take_machine_down(void *_suspend)
@@ -175,7 +173,7 @@ static int take_machine_down(void *_susp
         */
        suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
 
-       suspend->resume_notifier();
+       suspend->resume_notifier(suspend_cancelled);
        post_suspend(suspend_cancelled);
        gnttab_resume();
        if (!suspend_cancelled) {
@@ -204,7 +202,7 @@ static int take_machine_down(void *_susp
        return suspend_cancelled;
 }
 
-int __xen_suspend(int fast_suspend, void (*resume_notifier)(void))
+int __xen_suspend(int fast_suspend, void (*resume_notifier)(int))
 {
        int err, suspend_cancelled;
        struct suspend suspend;
@@ -243,7 +241,6 @@ int __xen_suspend(int fast_suspend, void
        if (!suspend_cancelled) {
                xencons_resume();
                xenbus_resume();
-               setup_suspend_evtchn();
        } else {
                xenbus_suspend_cancel();
        }
diff -r 44e3ace9a1f1 -r 2866e6af503e drivers/xen/core/reboot.c
--- a/drivers/xen/core/reboot.c Thu Jul 31 09:46:58 2008 +0100
+++ b/drivers/xen/core/reboot.c Thu Jul 31 15:33:54 2008 +0100
@@ -27,13 +27,18 @@ MODULE_LICENSE("Dual BSD/GPL");
 /* Ignore multiple shutdown requests. */
 static int shutting_down = SHUTDOWN_INVALID;
 
+/* Was last suspend request cancelled? */
+static int suspend_cancelled;
+
 /* 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);
 
-int __xen_suspend(int fast_suspend, void (*resume_notifier)(void));
+static int setup_suspend_evtchn(void);
+
+int __xen_suspend(int fast_suspend, void (*resume_notifier)(int));
 
 static int shutdown_process(void *__unused)
 {
@@ -62,10 +67,11 @@ static int shutdown_process(void *__unus
        return 0;
 }
 
-static void xen_resume_notifier(void)
+static void xen_resume_notifier(int _suspend_cancelled)
 {
        int old_state = xchg(&shutting_down, SHUTDOWN_RESUMING);
        BUG_ON(old_state != SHUTDOWN_SUSPEND);
+       suspend_cancelled = _suspend_cancelled;
 }
 
 static int xen_suspend(void *__unused)
@@ -85,6 +91,8 @@ static int xen_suspend(void *__unused)
                        printk(KERN_ERR "Xen suspend failed (%d)\n", err);
                        goto fail;
                }
+               if (!suspend_cancelled)
+                       setup_suspend_evtchn();
                old_state = cmpxchg(
                        &shutting_down, SHUTDOWN_RESUMING, SHUTDOWN_INVALID);
        } while (old_state == SHUTDOWN_SUSPEND);
@@ -108,6 +116,31 @@ static int xen_suspend(void *__unused)
        return 0;
 }
 
+static void switch_shutdown_state(int new_state)
+{
+       int prev_state, old_state = SHUTDOWN_INVALID;
+
+       /* We only drive shutdown_state into an active state. */
+       if (new_state == SHUTDOWN_INVALID)
+               return;
+
+       do {
+               /* We drop this transition if already in an active state. */
+               if ((old_state != SHUTDOWN_INVALID) &&
+                   (old_state != SHUTDOWN_RESUMING))
+                       return;
+               /* Attempt to transition. */
+               prev_state = old_state;
+               old_state = cmpxchg(&shutting_down, old_state, new_state);
+       } while (old_state != prev_state);
+
+       /* Either we kick off the work, or we leave it to xen_suspend(). */
+       if (old_state == SHUTDOWN_INVALID)
+               schedule_work(&shutdown_work);
+       else
+               BUG_ON(old_state != SHUTDOWN_RESUMING);
+}
+
 static void __shutdown_handler(void *unused)
 {
        int err;
@@ -129,7 +162,7 @@ static void shutdown_handler(struct xenb
        extern void ctrl_alt_del(void);
        char *str;
        struct xenbus_transaction xbt;
-       int err, old_state, new_state = SHUTDOWN_INVALID;
+       int err, new_state = SHUTDOWN_INVALID;
 
        if ((shutting_down != SHUTDOWN_INVALID) &&
            (shutting_down != SHUTDOWN_RESUMING))
@@ -166,13 +199,7 @@ static void shutdown_handler(struct xenb
        else
                printk("Ignoring shutdown request: %s\n", str);
 
-       if (new_state != SHUTDOWN_INVALID) {
-               old_state = xchg(&shutting_down, new_state);
-               if (old_state == SHUTDOWN_INVALID)
-                       schedule_work(&shutdown_work);
-               else
-                       BUG_ON(old_state != SHUTDOWN_RESUMING);
-       }
+       switch_shutdown_state(new_state);
 
        kfree(str);
 }
@@ -220,26 +247,24 @@ static struct xenbus_watch sysrq_watch =
 
 static irqreturn_t suspend_int(int irq, void* dev_id, struct pt_regs *ptregs)
 {
-       shutting_down = SHUTDOWN_SUSPEND;
-       schedule_work(&shutdown_work);
-
+       switch_shutdown_state(SHUTDOWN_SUSPEND);
        return IRQ_HANDLED;
 }
 
-int setup_suspend_evtchn(void)
-{
-       static int irq = -1;
+static int setup_suspend_evtchn(void)
+{
+       static int irq;
        int port;
-       char portstr[5]; /* 1024 max */
+       char portstr[16];
 
        if (irq > 0)
                unbind_from_irqhandler(irq, NULL);
 
        irq = bind_listening_port_to_irqhandler(0, suspend_int, 0, "suspend",
                                                NULL);
-       if (irq <= 0) {
+       if (irq <= 0)
                return -1;
-       }
+
        port = irq_to_evtchn_port(irq);
        printk(KERN_INFO "suspend: event channel %d\n", port);
        sprintf(portstr, "%d", port);

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.