diff options
Diffstat (limited to 'drivers/xen/manage.c')
| -rw-r--r-- | drivers/xen/manage.c | 201 | 
1 files changed, 124 insertions, 77 deletions
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index ef9c7db5207..5f1e1f3cd18 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -1,6 +1,9 @@  /*   * Handle extern requests for shutdown, reboot and sysrq   */ + +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt +  #include <linux/kernel.h>  #include <linux/err.h>  #include <linux/slab.h> @@ -8,6 +11,8 @@  #include <linux/sysrq.h>  #include <linux/stop_machine.h>  #include <linux/freezer.h> +#include <linux/syscore_ops.h> +#include <linux/export.h>  #include <xen/xen.h>  #include <xen/xenbus.h> @@ -34,63 +39,59 @@ enum shutdown_state {  /* Ignore multiple shutdown requests. */  static enum shutdown_state shutting_down = SHUTDOWN_INVALID; -#ifdef CONFIG_PM_SLEEP -static int xen_hvm_suspend(void *data) -{ -	struct sched_shutdown r = { .reason = SHUTDOWN_suspend }; -	int *cancelled = data; - -	BUG_ON(!irqs_disabled()); - -	*cancelled = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r); +struct suspend_info { +	int cancelled; +}; -	xen_hvm_post_suspend(*cancelled); -	gnttab_resume(); +static RAW_NOTIFIER_HEAD(xen_resume_notifier); -	if (!*cancelled) { -		xen_irq_resume(); -		xen_timer_resume(); -	} +void xen_resume_notifier_register(struct notifier_block *nb) +{ +	raw_notifier_chain_register(&xen_resume_notifier, nb); +} +EXPORT_SYMBOL_GPL(xen_resume_notifier_register); -	return 0; +void xen_resume_notifier_unregister(struct notifier_block *nb) +{ +	raw_notifier_chain_unregister(&xen_resume_notifier, nb);  } +EXPORT_SYMBOL_GPL(xen_resume_notifier_unregister); +#ifdef CONFIG_HIBERNATE_CALLBACKS  static int xen_suspend(void *data)  { +	struct suspend_info *si = data;  	int err; -	int *cancelled = data;  	BUG_ON(!irqs_disabled()); -	err = sysdev_suspend(PMSG_SUSPEND); +	err = syscore_suspend();  	if (err) { -		printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n", -			err); +		pr_err("%s: system core suspend failed: %d\n", __func__, err);  		return err;  	} -	xen_mm_pin_all();  	gnttab_suspend(); -	xen_pre_suspend(); +	xen_arch_pre_suspend();  	/*  	 * This hypercall returns 1 if suspend was cancelled  	 * or the domain was merely checkpointed, and 0 if it  	 * is resuming in a new domain.  	 */ -	*cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info)); +	si->cancelled = HYPERVISOR_suspend(xen_pv_domain() +                                           ? virt_to_mfn(xen_start_info) +                                           : 0); -	xen_post_suspend(*cancelled); +	xen_arch_post_suspend(si->cancelled);  	gnttab_resume(); -	xen_mm_unpin_all(); -	if (!*cancelled) { +	if (!si->cancelled) {  		xen_irq_resume(); -		xen_console_resume();  		xen_timer_resume();  	} -	sysdev_resume(); +	syscore_resume();  	return 0;  } @@ -98,7 +99,7 @@ static int xen_suspend(void *data)  static void do_suspend(void)  {  	int err; -	int cancelled = 1; +	struct suspend_info si;  	shutting_down = SHUTDOWN_SUSPEND; @@ -108,49 +109,52 @@ static void do_suspend(void)  	   during suspend. */  	err = freeze_processes();  	if (err) { -		printk(KERN_ERR "xen suspend: freeze failed %d\n", err); +		pr_err("%s: freeze failed %d\n", __func__, err);  		goto out;  	}  #endif -	err = dpm_suspend_start(PMSG_SUSPEND); +	err = dpm_suspend_start(PMSG_FREEZE);  	if (err) { -		printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err); +		pr_err("%s: dpm_suspend_start %d\n", __func__, err);  		goto out_thaw;  	}  	printk(KERN_DEBUG "suspending xenstore...\n");  	xs_suspend(); -	err = dpm_suspend_noirq(PMSG_SUSPEND); +	err = dpm_suspend_end(PMSG_FREEZE);  	if (err) { -		printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err); +		pr_err("dpm_suspend_end failed: %d\n", err); +		si.cancelled = 0;  		goto out_resume;  	} -	if (xen_hvm_domain()) -		err = stop_machine(xen_hvm_suspend, &cancelled, cpumask_of(0)); -	else -		err = stop_machine(xen_suspend, &cancelled, cpumask_of(0)); +	si.cancelled = 1; -	dpm_resume_noirq(PMSG_RESUME); +	err = stop_machine(xen_suspend, &si, cpumask_of(0)); + +	/* Resume console as early as possible. */ +	if (!si.cancelled) +		xen_console_resume(); + +	raw_notifier_call_chain(&xen_resume_notifier, 0, NULL); + +	dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);  	if (err) { -		printk(KERN_ERR "failed to start xen_suspend: %d\n", err); -		cancelled = 1; +		pr_err("failed to start xen_suspend: %d\n", err); +		si.cancelled = 1;  	}  out_resume: -	if (!cancelled) { +	if (!si.cancelled) {  		xen_arch_resume();  		xs_resume();  	} else  		xs_suspend_cancel(); -	dpm_resume_end(PMSG_RESUME); - -	/* Make sure timer events get retriggered on all CPUs */ -	clock_was_set(); +	dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);  out_thaw:  #ifdef CONFIG_PREEMPT @@ -159,7 +163,46 @@ out:  #endif  	shutting_down = SHUTDOWN_INVALID;  } -#endif	/* CONFIG_PM_SLEEP */ +#endif	/* CONFIG_HIBERNATE_CALLBACKS */ + +struct shutdown_handler { +	const char *command; +	void (*cb)(void); +}; + +static int poweroff_nb(struct notifier_block *cb, unsigned long code, void *unused) +{ +	switch (code) { +	case SYS_DOWN: +	case SYS_HALT: +	case SYS_POWER_OFF: +		shutting_down = SHUTDOWN_POWEROFF; +	default: +		break; +	} +	return NOTIFY_DONE; +} +static void do_poweroff(void) +{ +	switch (system_state) { +	case SYSTEM_BOOTING: +		orderly_poweroff(true); +		break; +	case SYSTEM_RUNNING: +		orderly_poweroff(false); +		break; +	default: +		/* Don't do it when we are halting/rebooting. */ +		pr_info("Ignoring Xen toolstack shutdown.\n"); +		break; +	} +} + +static void do_reboot(void) +{ +	shutting_down = SHUTDOWN_POWEROFF; /* ? */ +	ctrl_alt_del(); +}  static void shutdown_handler(struct xenbus_watch *watch,  			     const char **vec, unsigned int len) @@ -167,6 +210,16 @@ static void shutdown_handler(struct xenbus_watch *watch,  	char *str;  	struct xenbus_transaction xbt;  	int err; +	static struct shutdown_handler handlers[] = { +		{ "poweroff",	do_poweroff }, +		{ "halt",	do_poweroff }, +		{ "reboot",	do_reboot   }, +#ifdef CONFIG_HIBERNATE_CALLBACKS +		{ "suspend",	do_suspend  }, +#endif +		{NULL, NULL}, +	}; +	static struct shutdown_handler *handler;  	if (shutting_down != SHUTDOWN_INVALID)  		return; @@ -183,7 +236,14 @@ static void shutdown_handler(struct xenbus_watch *watch,  		return;  	} -	xenbus_write(xbt, "control", "shutdown", ""); +	for (handler = &handlers[0]; handler->command; handler++) { +		if (strcmp(str, handler->command) == 0) +			break; +	} + +	/* Only acknowledge commands which we are prepared to handle. */ +	if (handler->cb) +		xenbus_write(xbt, "control", "shutdown", "");  	err = xenbus_transaction_end(xbt, 0);  	if (err == -EAGAIN) { @@ -191,19 +251,10 @@ static void shutdown_handler(struct xenbus_watch *watch,  		goto again;  	} -	if (strcmp(str, "poweroff") == 0 || -	    strcmp(str, "halt") == 0) { -		shutting_down = SHUTDOWN_POWEROFF; -		orderly_poweroff(false); -	} else if (strcmp(str, "reboot") == 0) { -		shutting_down = SHUTDOWN_POWEROFF; /* ? */ -		ctrl_alt_del(); -#ifdef CONFIG_PM_SLEEP -	} else if (strcmp(str, "suspend") == 0) { -		do_suspend(); -#endif +	if (handler->cb) { +		handler->cb();  	} else { -		printk(KERN_INFO "Ignoring shutdown request: %s\n", str); +		pr_info("Ignoring shutdown request: %s\n", str);  		shutting_down = SHUTDOWN_INVALID;  	} @@ -223,8 +274,7 @@ static void sysrq_handler(struct xenbus_watch *watch, const char **vec,  	if (err)  		return;  	if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { -		printk(KERN_ERR "Unable to read sysrq code in " -		       "control/sysrq\n"); +		pr_err("Unable to read sysrq code in control/sysrq\n");  		xenbus_transaction_end(xbt, 1);  		return;  	} @@ -251,20 +301,25 @@ static struct xenbus_watch shutdown_watch = {  	.callback = shutdown_handler  }; +static struct notifier_block xen_reboot_nb = { +	.notifier_call = poweroff_nb, +}; +  static int setup_shutdown_watcher(void)  {  	int err;  	err = register_xenbus_watch(&shutdown_watch);  	if (err) { -		printk(KERN_ERR "Failed to set shutdown watcher\n"); +		pr_err("Failed to set shutdown watcher\n");  		return err;  	} +  #ifdef CONFIG_MAGIC_SYSRQ  	err = register_xenbus_watch(&sysrq_watch);  	if (err) { -		printk(KERN_ERR "Failed to set sysrq watcher\n"); +		pr_err("Failed to set sysrq watcher\n");  		return err;  	}  #endif @@ -280,27 +335,19 @@ static int shutdown_event(struct notifier_block *notifier,  	return NOTIFY_DONE;  } -static int __init __setup_shutdown_event(void) -{ -	/* Delay initialization in the PV on HVM case */ -	if (xen_hvm_domain()) -		return 0; - -	if (!xen_pv_domain()) -		return -ENODEV; - -	return xen_setup_shutdown_event(); -} -  int xen_setup_shutdown_event(void)  {  	static struct notifier_block xenstore_notifier = {  		.notifier_call = shutdown_event  	}; + +	if (!xen_domain()) +		return -ENODEV;  	register_xenstore_notifier(&xenstore_notifier); +	register_reboot_notifier(&xen_reboot_nb);  	return 0;  }  EXPORT_SYMBOL_GPL(xen_setup_shutdown_event); -subsys_initcall(__setup_shutdown_event); +subsys_initcall(xen_setup_shutdown_event);  | 
