# 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
|