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. */
struct suspend {
int fast_suspend;
- void (*resume_notifier)(void);
+ void (*resume_notifier)(int);
};
static int take_machine_down(void *_suspend)
*/
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) {
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;
if (!suspend_cancelled) {
xencons_resume();
xenbus_resume();
- setup_suspend_evtchn();
} else {
xenbus_suspend_cancel();
}
/* 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)
{
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)
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);
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;
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))
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);
}
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 setup_suspend_evtchn(void)
{
- static int irq = -1;
+ 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);