ia64/linux-2.6.18-xen.hg

changeset 631:2866e6af503e

xen suspend: Fix suspend-via-evtchn reentrancy.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jul 31 15:33:54 2008 +0100 (2008-07-31)
parents 44e3ace9a1f1
children 324a5c041301 87413eba04ab
files drivers/xen/core/machine_reboot.c drivers/xen/core/reboot.c
line diff
     1.1 --- a/drivers/xen/core/machine_reboot.c	Thu Jul 31 09:46:58 2008 +0100
     1.2 +++ b/drivers/xen/core/machine_reboot.c	Thu Jul 31 15:33:54 2008 +0100
     1.3 @@ -26,8 +26,6 @@
     1.4  void (*pm_power_off)(void);
     1.5  EXPORT_SYMBOL(pm_power_off);
     1.6  
     1.7 -int setup_suspend_evtchn(void);
     1.8 -
     1.9  void machine_emergency_restart(void)
    1.10  {
    1.11  	/* We really want to get pending console data out before we die. */
    1.12 @@ -133,7 +131,7 @@ static void post_suspend(int suspend_can
    1.13  
    1.14  struct suspend {
    1.15  	int fast_suspend;
    1.16 -	void (*resume_notifier)(void);
    1.17 +	void (*resume_notifier)(int);
    1.18  };
    1.19  
    1.20  static int take_machine_down(void *_suspend)
    1.21 @@ -175,7 +173,7 @@ static int take_machine_down(void *_susp
    1.22  	 */
    1.23  	suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
    1.24  
    1.25 -	suspend->resume_notifier();
    1.26 +	suspend->resume_notifier(suspend_cancelled);
    1.27  	post_suspend(suspend_cancelled);
    1.28  	gnttab_resume();
    1.29  	if (!suspend_cancelled) {
    1.30 @@ -204,7 +202,7 @@ static int take_machine_down(void *_susp
    1.31  	return suspend_cancelled;
    1.32  }
    1.33  
    1.34 -int __xen_suspend(int fast_suspend, void (*resume_notifier)(void))
    1.35 +int __xen_suspend(int fast_suspend, void (*resume_notifier)(int))
    1.36  {
    1.37  	int err, suspend_cancelled;
    1.38  	struct suspend suspend;
    1.39 @@ -243,7 +241,6 @@ int __xen_suspend(int fast_suspend, void
    1.40  	if (!suspend_cancelled) {
    1.41  		xencons_resume();
    1.42  		xenbus_resume();
    1.43 -		setup_suspend_evtchn();
    1.44  	} else {
    1.45  		xenbus_suspend_cancel();
    1.46  	}
     2.1 --- a/drivers/xen/core/reboot.c	Thu Jul 31 09:46:58 2008 +0100
     2.2 +++ b/drivers/xen/core/reboot.c	Thu Jul 31 15:33:54 2008 +0100
     2.3 @@ -27,13 +27,18 @@ MODULE_LICENSE("Dual BSD/GPL");
     2.4  /* Ignore multiple shutdown requests. */
     2.5  static int shutting_down = SHUTDOWN_INVALID;
     2.6  
     2.7 +/* Was last suspend request cancelled? */
     2.8 +static int suspend_cancelled;
     2.9 +
    2.10  /* Can we leave APs online when we suspend? */
    2.11  static int fast_suspend;
    2.12  
    2.13  static void __shutdown_handler(void *unused);
    2.14  static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
    2.15  
    2.16 -int __xen_suspend(int fast_suspend, void (*resume_notifier)(void));
    2.17 +static int setup_suspend_evtchn(void);
    2.18 +
    2.19 +int __xen_suspend(int fast_suspend, void (*resume_notifier)(int));
    2.20  
    2.21  static int shutdown_process(void *__unused)
    2.22  {
    2.23 @@ -62,10 +67,11 @@ static int shutdown_process(void *__unus
    2.24  	return 0;
    2.25  }
    2.26  
    2.27 -static void xen_resume_notifier(void)
    2.28 +static void xen_resume_notifier(int _suspend_cancelled)
    2.29  {
    2.30  	int old_state = xchg(&shutting_down, SHUTDOWN_RESUMING);
    2.31  	BUG_ON(old_state != SHUTDOWN_SUSPEND);
    2.32 +	suspend_cancelled = _suspend_cancelled;
    2.33  }
    2.34  
    2.35  static int xen_suspend(void *__unused)
    2.36 @@ -85,6 +91,8 @@ static int xen_suspend(void *__unused)
    2.37  			printk(KERN_ERR "Xen suspend failed (%d)\n", err);
    2.38  			goto fail;
    2.39  		}
    2.40 +		if (!suspend_cancelled)
    2.41 +			setup_suspend_evtchn();
    2.42  		old_state = cmpxchg(
    2.43  			&shutting_down, SHUTDOWN_RESUMING, SHUTDOWN_INVALID);
    2.44  	} while (old_state == SHUTDOWN_SUSPEND);
    2.45 @@ -108,6 +116,31 @@ static int xen_suspend(void *__unused)
    2.46  	return 0;
    2.47  }
    2.48  
    2.49 +static void switch_shutdown_state(int new_state)
    2.50 +{
    2.51 +	int prev_state, old_state = SHUTDOWN_INVALID;
    2.52 +
    2.53 +	/* We only drive shutdown_state into an active state. */
    2.54 +	if (new_state == SHUTDOWN_INVALID)
    2.55 +		return;
    2.56 +
    2.57 +	do {
    2.58 +		/* We drop this transition if already in an active state. */
    2.59 +		if ((old_state != SHUTDOWN_INVALID) &&
    2.60 +		    (old_state != SHUTDOWN_RESUMING))
    2.61 +			return;
    2.62 +		/* Attempt to transition. */
    2.63 +		prev_state = old_state;
    2.64 +		old_state = cmpxchg(&shutting_down, old_state, new_state);
    2.65 +	} while (old_state != prev_state);
    2.66 +
    2.67 +	/* Either we kick off the work, or we leave it to xen_suspend(). */
    2.68 +	if (old_state == SHUTDOWN_INVALID)
    2.69 +		schedule_work(&shutdown_work);
    2.70 +	else
    2.71 +		BUG_ON(old_state != SHUTDOWN_RESUMING);
    2.72 +}
    2.73 +
    2.74  static void __shutdown_handler(void *unused)
    2.75  {
    2.76  	int err;
    2.77 @@ -129,7 +162,7 @@ static void shutdown_handler(struct xenb
    2.78  	extern void ctrl_alt_del(void);
    2.79  	char *str;
    2.80  	struct xenbus_transaction xbt;
    2.81 -	int err, old_state, new_state = SHUTDOWN_INVALID;
    2.82 +	int err, new_state = SHUTDOWN_INVALID;
    2.83  
    2.84  	if ((shutting_down != SHUTDOWN_INVALID) &&
    2.85  	    (shutting_down != SHUTDOWN_RESUMING))
    2.86 @@ -166,13 +199,7 @@ static void shutdown_handler(struct xenb
    2.87  	else
    2.88  		printk("Ignoring shutdown request: %s\n", str);
    2.89  
    2.90 -	if (new_state != SHUTDOWN_INVALID) {
    2.91 -		old_state = xchg(&shutting_down, new_state);
    2.92 -		if (old_state == SHUTDOWN_INVALID)
    2.93 -			schedule_work(&shutdown_work);
    2.94 -		else
    2.95 -			BUG_ON(old_state != SHUTDOWN_RESUMING);
    2.96 -	}
    2.97 +	switch_shutdown_state(new_state);
    2.98  
    2.99  	kfree(str);
   2.100  }
   2.101 @@ -220,26 +247,24 @@ static struct xenbus_watch sysrq_watch =
   2.102  
   2.103  static irqreturn_t suspend_int(int irq, void* dev_id, struct pt_regs *ptregs)
   2.104  {
   2.105 -	shutting_down = SHUTDOWN_SUSPEND;
   2.106 -	schedule_work(&shutdown_work);
   2.107 -
   2.108 +	switch_shutdown_state(SHUTDOWN_SUSPEND);
   2.109  	return IRQ_HANDLED;
   2.110  }
   2.111  
   2.112 -int setup_suspend_evtchn(void)
   2.113 +static int setup_suspend_evtchn(void)
   2.114  {
   2.115 -	static int irq = -1;
   2.116 +	static int irq;
   2.117  	int port;
   2.118 -	char portstr[5]; /* 1024 max */
   2.119 +	char portstr[16];
   2.120  
   2.121  	if (irq > 0)
   2.122  		unbind_from_irqhandler(irq, NULL);
   2.123  
   2.124  	irq = bind_listening_port_to_irqhandler(0, suspend_int, 0, "suspend",
   2.125  						NULL);
   2.126 -	if (irq <= 0) {
   2.127 +	if (irq <= 0)
   2.128  		return -1;
   2.129 -	}
   2.130 +
   2.131  	port = irq_to_evtchn_port(irq);
   2.132  	printk(KERN_INFO "suspend: event channel %d\n", port);
   2.133  	sprintf(portstr, "%d", port);