xref: /openbmc/linux/drivers/xen/manage.c (revision 67f43c9c)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ec9b2065SIsaku Yamahata /*
3ec9b2065SIsaku Yamahata  * Handle extern requests for shutdown, reboot and sysrq
4ec9b2065SIsaku Yamahata  */
5283c0972SJoe Perches 
6283c0972SJoe Perches #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
7283c0972SJoe Perches 
8ec9b2065SIsaku Yamahata #include <linux/kernel.h>
9ec9b2065SIsaku Yamahata #include <linux/err.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
11ec9b2065SIsaku Yamahata #include <linux/reboot.h>
12ec9b2065SIsaku Yamahata #include <linux/sysrq.h>
130e91398fSJeremy Fitzhardinge #include <linux/stop_machine.h>
140e91398fSJeremy Fitzhardinge #include <linux/freezer.h>
1519234c08SRafael J. Wysocki #include <linux/syscore_ops.h>
1663c9744bSPaul Gortmaker #include <linux/export.h>
17ec9b2065SIsaku Yamahata 
18016b6f5fSStefano Stabellini #include <xen/xen.h>
19ec9b2065SIsaku Yamahata #include <xen/xenbus.h>
200e91398fSJeremy Fitzhardinge #include <xen/grant_table.h>
210e91398fSJeremy Fitzhardinge #include <xen/events.h>
220e91398fSJeremy Fitzhardinge #include <xen/hvc-console.h>
23a9fd60e2SJulien Grall #include <xen/page.h>
240e91398fSJeremy Fitzhardinge #include <xen/xen-ops.h>
25ec9b2065SIsaku Yamahata 
260e91398fSJeremy Fitzhardinge #include <asm/xen/hypercall.h>
27016b6f5fSStefano Stabellini #include <asm/xen/hypervisor.h>
280e91398fSJeremy Fitzhardinge 
290e91398fSJeremy Fitzhardinge enum shutdown_state {
300e91398fSJeremy Fitzhardinge 	SHUTDOWN_INVALID = -1,
310e91398fSJeremy Fitzhardinge 	SHUTDOWN_POWEROFF = 0,
320e91398fSJeremy Fitzhardinge 	SHUTDOWN_SUSPEND = 2,
33ec9b2065SIsaku Yamahata 	/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
340e91398fSJeremy Fitzhardinge 	   report a crash, not be instructed to crash!
350e91398fSJeremy Fitzhardinge 	   HALT is the same as POWEROFF, as far as we're concerned.  The tools use
360e91398fSJeremy Fitzhardinge 	   the distinction when we return the reason code to them.  */
370e91398fSJeremy Fitzhardinge 	 SHUTDOWN_HALT = 4,
380e91398fSJeremy Fitzhardinge };
39ec9b2065SIsaku Yamahata 
40ec9b2065SIsaku Yamahata /* Ignore multiple shutdown requests. */
410e91398fSJeremy Fitzhardinge static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
420e91398fSJeremy Fitzhardinge 
43ceb18029SIan Campbell struct suspend_info {
44ceb18029SIan Campbell 	int cancelled;
45ceb18029SIan Campbell };
46ceb18029SIan Campbell 
47cd979883SStanislaw Gruszka static RAW_NOTIFIER_HEAD(xen_resume_notifier);
48cd979883SStanislaw Gruszka 
xen_resume_notifier_register(struct notifier_block * nb)49cd979883SStanislaw Gruszka void xen_resume_notifier_register(struct notifier_block *nb)
50cd979883SStanislaw Gruszka {
51cd979883SStanislaw Gruszka 	raw_notifier_chain_register(&xen_resume_notifier, nb);
52cd979883SStanislaw Gruszka }
53cd979883SStanislaw Gruszka EXPORT_SYMBOL_GPL(xen_resume_notifier_register);
54cd979883SStanislaw Gruszka 
xen_resume_notifier_unregister(struct notifier_block * nb)55cd979883SStanislaw Gruszka void xen_resume_notifier_unregister(struct notifier_block *nb)
56cd979883SStanislaw Gruszka {
57cd979883SStanislaw Gruszka 	raw_notifier_chain_unregister(&xen_resume_notifier, nb);
58cd979883SStanislaw Gruszka }
59cd979883SStanislaw Gruszka EXPORT_SYMBOL_GPL(xen_resume_notifier_unregister);
60cd979883SStanislaw Gruszka 
6165e053a7SStefano Stabellini #ifdef CONFIG_HIBERNATE_CALLBACKS
xen_suspend(void * data)620e91398fSJeremy Fitzhardinge static int xen_suspend(void *data)
630e91398fSJeremy Fitzhardinge {
64ceb18029SIan Campbell 	struct suspend_info *si = data;
65359cdd3fSJeremy Fitzhardinge 	int err;
660e91398fSJeremy Fitzhardinge 
670e91398fSJeremy Fitzhardinge 	BUG_ON(!irqs_disabled());
680e91398fSJeremy Fitzhardinge 
6919234c08SRafael J. Wysocki 	err = syscore_suspend();
70770824bdSRafael J. Wysocki 	if (err) {
71283c0972SJoe Perches 		pr_err("%s: system core suspend failed: %d\n", __func__, err);
72770824bdSRafael J. Wysocki 		return err;
73770824bdSRafael J. Wysocki 	}
74359cdd3fSJeremy Fitzhardinge 
75aa8532c3SDavid Vrabel 	gnttab_suspend();
765e25f5dbSDongli Zhang 	xen_manage_runstate_time(-1);
77aa8532c3SDavid Vrabel 	xen_arch_pre_suspend();
780e91398fSJeremy Fitzhardinge 
79aa8532c3SDavid Vrabel 	si->cancelled = HYPERVISOR_suspend(xen_pv_domain()
800df4f266SJulien Grall                                            ? virt_to_gfn(xen_start_info)
81aa8532c3SDavid Vrabel                                            : 0);
820e91398fSJeremy Fitzhardinge 
83aa8532c3SDavid Vrabel 	xen_arch_post_suspend(si->cancelled);
845e25f5dbSDongli Zhang 	xen_manage_runstate_time(si->cancelled ? 1 : 0);
85aa8532c3SDavid Vrabel 	gnttab_resume();
860e91398fSJeremy Fitzhardinge 
87ceb18029SIan Campbell 	if (!si->cancelled) {
880e91398fSJeremy Fitzhardinge 		xen_irq_resume();
89ad55db9fSIsaku Yamahata 		xen_timer_resume();
900e91398fSJeremy Fitzhardinge 	}
910e91398fSJeremy Fitzhardinge 
9219234c08SRafael J. Wysocki 	syscore_resume();
931e6fcf84SIan Campbell 
940e91398fSJeremy Fitzhardinge 	return 0;
950e91398fSJeremy Fitzhardinge }
960e91398fSJeremy Fitzhardinge 
do_suspend(void)970e91398fSJeremy Fitzhardinge static void do_suspend(void)
980e91398fSJeremy Fitzhardinge {
990e91398fSJeremy Fitzhardinge 	int err;
100ceb18029SIan Campbell 	struct suspend_info si;
1010e91398fSJeremy Fitzhardinge 
1020e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_SUSPEND;
1030e91398fSJeremy Fitzhardinge 
1040e91398fSJeremy Fitzhardinge 	err = freeze_processes();
1050e91398fSJeremy Fitzhardinge 	if (err) {
10672978b2fSRoss Lagerwall 		pr_err("%s: freeze processes failed %d\n", __func__, err);
1073fc1f1e2STejun Heo 		goto out;
1080e91398fSJeremy Fitzhardinge 	}
1090e91398fSJeremy Fitzhardinge 
11072978b2fSRoss Lagerwall 	err = freeze_kernel_threads();
11172978b2fSRoss Lagerwall 	if (err) {
11272978b2fSRoss Lagerwall 		pr_err("%s: freeze kernel threads failed %d\n", __func__, err);
11372978b2fSRoss Lagerwall 		goto out_thaw;
11472978b2fSRoss Lagerwall 	}
11572978b2fSRoss Lagerwall 
116b3e96c0cSShriram Rajagopalan 	err = dpm_suspend_start(PMSG_FREEZE);
1170e91398fSJeremy Fitzhardinge 	if (err) {
118283c0972SJoe Perches 		pr_err("%s: dpm_suspend_start %d\n", __func__, err);
11965f63384SIan Campbell 		goto out_thaw;
1200e91398fSJeremy Fitzhardinge 	}
1210e91398fSJeremy Fitzhardinge 
122c5cae661SIan Campbell 	printk(KERN_DEBUG "suspending xenstore...\n");
123c5cae661SIan Campbell 	xs_suspend();
124c5cae661SIan Campbell 
125cf579dfbSRafael J. Wysocki 	err = dpm_suspend_end(PMSG_FREEZE);
1262ed8d2b3SRafael J. Wysocki 	if (err) {
127283c0972SJoe Perches 		pr_err("dpm_suspend_end failed: %d\n", err);
128186bab1cSKonrad Rzeszutek Wilk 		si.cancelled = 0;
12965f63384SIan Campbell 		goto out_resume;
1302ed8d2b3SRafael J. Wysocki 	}
1312ed8d2b3SRafael J. Wysocki 
1322b953a5eSBoris Ostrovsky 	xen_arch_suspend();
1332b953a5eSBoris Ostrovsky 
134ceb18029SIan Campbell 	si.cancelled = 1;
135ceb18029SIan Campbell 
136ceb18029SIan Campbell 	err = stop_machine(xen_suspend, &si, cpumask_of(0));
137922cc38aSJeremy Fitzhardinge 
1381b647823SDavid Vrabel 	/* Resume console as early as possible. */
1391b647823SDavid Vrabel 	if (!si.cancelled)
1401b647823SDavid Vrabel 		xen_console_resume();
1411b647823SDavid Vrabel 
142cd979883SStanislaw Gruszka 	raw_notifier_call_chain(&xen_resume_notifier, 0, NULL);
143cd979883SStanislaw Gruszka 
144ff32baa1SJakub Kądziołka 	xen_arch_resume();
145ff32baa1SJakub Kądziołka 
146cf579dfbSRafael J. Wysocki 	dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
147922cc38aSJeremy Fitzhardinge 
1480e91398fSJeremy Fitzhardinge 	if (err) {
149283c0972SJoe Perches 		pr_err("failed to start xen_suspend: %d\n", err);
150ceb18029SIan Campbell 		si.cancelled = 1;
1510e91398fSJeremy Fitzhardinge 	}
1520e91398fSJeremy Fitzhardinge 
1532b953a5eSBoris Ostrovsky out_resume:
1542b953a5eSBoris Ostrovsky 	if (!si.cancelled)
155de5b31bdSIan Campbell 		xs_resume();
1562b953a5eSBoris Ostrovsky 	else
157de5b31bdSIan Campbell 		xs_suspend_cancel();
1580e91398fSJeremy Fitzhardinge 
159b3e96c0cSShriram Rajagopalan 	dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
1600e91398fSJeremy Fitzhardinge 
16165f63384SIan Campbell out_thaw:
1620e91398fSJeremy Fitzhardinge 	thaw_processes();
16365f63384SIan Campbell out:
1640e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_INVALID;
1650e91398fSJeremy Fitzhardinge }
1661f112ceeSRafael J. Wysocki #endif	/* CONFIG_HIBERNATE_CALLBACKS */
167ec9b2065SIsaku Yamahata 
16855271723SIan Campbell struct shutdown_handler {
16944b3c7afSJuergen Gross #define SHUTDOWN_CMD_SIZE 11
17044b3c7afSJuergen Gross 	const char command[SHUTDOWN_CMD_SIZE];
17144b3c7afSJuergen Gross 	bool flag;
17255271723SIan Campbell 	void (*cb)(void);
17355271723SIan Campbell };
17455271723SIan Campbell 
poweroff_nb(struct notifier_block * cb,unsigned long code,void * unused)175eb47f712SKonrad Rzeszutek Wilk static int poweroff_nb(struct notifier_block *cb, unsigned long code, void *unused)
176eb47f712SKonrad Rzeszutek Wilk {
177eb47f712SKonrad Rzeszutek Wilk 	switch (code) {
178eb47f712SKonrad Rzeszutek Wilk 	case SYS_DOWN:
179eb47f712SKonrad Rzeszutek Wilk 	case SYS_HALT:
180eb47f712SKonrad Rzeszutek Wilk 	case SYS_POWER_OFF:
181eb47f712SKonrad Rzeszutek Wilk 		shutting_down = SHUTDOWN_POWEROFF;
1825e65f524SGustavo A. R. Silva 		break;
183eb47f712SKonrad Rzeszutek Wilk 	default:
184eb47f712SKonrad Rzeszutek Wilk 		break;
185eb47f712SKonrad Rzeszutek Wilk 	}
186eb47f712SKonrad Rzeszutek Wilk 	return NOTIFY_DONE;
187eb47f712SKonrad Rzeszutek Wilk }
do_poweroff(void)18855271723SIan Campbell static void do_poweroff(void)
18955271723SIan Campbell {
190eb47f712SKonrad Rzeszutek Wilk 	switch (system_state) {
191eb47f712SKonrad Rzeszutek Wilk 	case SYSTEM_BOOTING:
19269a78ff2SThomas Gleixner 	case SYSTEM_SCHEDULING:
193eb47f712SKonrad Rzeszutek Wilk 		orderly_poweroff(true);
194eb47f712SKonrad Rzeszutek Wilk 		break;
195eb47f712SKonrad Rzeszutek Wilk 	case SYSTEM_RUNNING:
19655271723SIan Campbell 		orderly_poweroff(false);
197eb47f712SKonrad Rzeszutek Wilk 		break;
198eb47f712SKonrad Rzeszutek Wilk 	default:
199eb47f712SKonrad Rzeszutek Wilk 		/* Don't do it when we are halting/rebooting. */
200eb47f712SKonrad Rzeszutek Wilk 		pr_info("Ignoring Xen toolstack shutdown.\n");
201eb47f712SKonrad Rzeszutek Wilk 		break;
202eb47f712SKonrad Rzeszutek Wilk 	}
20355271723SIan Campbell }
20455271723SIan Campbell 
do_reboot(void)20555271723SIan Campbell static void do_reboot(void)
20655271723SIan Campbell {
20755271723SIan Campbell 	shutting_down = SHUTDOWN_POWEROFF; /* ? */
208*67f43c9cSRoss Lagerwall 	orderly_reboot();
20955271723SIan Campbell }
21055271723SIan Campbell 
21144b3c7afSJuergen Gross static struct shutdown_handler shutdown_handlers[] = {
21244b3c7afSJuergen Gross 	{ "poweroff",	true,	do_poweroff },
21344b3c7afSJuergen Gross 	{ "halt",	false,	do_poweroff },
21444b3c7afSJuergen Gross 	{ "reboot",	true,	do_reboot   },
21544b3c7afSJuergen Gross #ifdef CONFIG_HIBERNATE_CALLBACKS
21644b3c7afSJuergen Gross 	{ "suspend",	true,	do_suspend  },
21744b3c7afSJuergen Gross #endif
21844b3c7afSJuergen Gross };
21944b3c7afSJuergen Gross 
shutdown_handler(struct xenbus_watch * watch,const char * path,const char * token)220ec9b2065SIsaku Yamahata static void shutdown_handler(struct xenbus_watch *watch,
2215584ea25SJuergen Gross 			     const char *path, const char *token)
222ec9b2065SIsaku Yamahata {
223ec9b2065SIsaku Yamahata 	char *str;
224ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
225ec9b2065SIsaku Yamahata 	int err;
22644b3c7afSJuergen Gross 	int idx;
227ec9b2065SIsaku Yamahata 
228ec9b2065SIsaku Yamahata 	if (shutting_down != SHUTDOWN_INVALID)
229ec9b2065SIsaku Yamahata 		return;
230ec9b2065SIsaku Yamahata 
231ec9b2065SIsaku Yamahata  again:
232ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
233ec9b2065SIsaku Yamahata 	if (err)
234ec9b2065SIsaku Yamahata 		return;
235ec9b2065SIsaku Yamahata 
236ec9b2065SIsaku Yamahata 	str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
237ec9b2065SIsaku Yamahata 	/* Ignore read errors and empty reads. */
238ec9b2065SIsaku Yamahata 	if (XENBUS_IS_ERR_READ(str)) {
239ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
240ec9b2065SIsaku Yamahata 		return;
241ec9b2065SIsaku Yamahata 	}
242ec9b2065SIsaku Yamahata 
24344b3c7afSJuergen Gross 	for (idx = 0; idx < ARRAY_SIZE(shutdown_handlers); idx++) {
24444b3c7afSJuergen Gross 		if (strcmp(str, shutdown_handlers[idx].command) == 0)
24555271723SIan Campbell 			break;
24655271723SIan Campbell 	}
24755271723SIan Campbell 
24855271723SIan Campbell 	/* Only acknowledge commands which we are prepared to handle. */
24944b3c7afSJuergen Gross 	if (idx < ARRAY_SIZE(shutdown_handlers))
250ec9b2065SIsaku Yamahata 		xenbus_write(xbt, "control", "shutdown", "");
251ec9b2065SIsaku Yamahata 
252ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
253ec9b2065SIsaku Yamahata 	if (err == -EAGAIN) {
254ec9b2065SIsaku Yamahata 		kfree(str);
255ec9b2065SIsaku Yamahata 		goto again;
256ec9b2065SIsaku Yamahata 	}
257ec9b2065SIsaku Yamahata 
25844b3c7afSJuergen Gross 	if (idx < ARRAY_SIZE(shutdown_handlers)) {
25944b3c7afSJuergen Gross 		shutdown_handlers[idx].cb();
2600e91398fSJeremy Fitzhardinge 	} else {
261283c0972SJoe Perches 		pr_info("Ignoring shutdown request: %s\n", str);
262ec9b2065SIsaku Yamahata 		shutting_down = SHUTDOWN_INVALID;
263ec9b2065SIsaku Yamahata 	}
264ec9b2065SIsaku Yamahata 
265ec9b2065SIsaku Yamahata 	kfree(str);
266ec9b2065SIsaku Yamahata }
267ec9b2065SIsaku Yamahata 
268f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ
sysrq_handler(struct xenbus_watch * watch,const char * path,const char * token)2695584ea25SJuergen Gross static void sysrq_handler(struct xenbus_watch *watch, const char *path,
2705584ea25SJuergen Gross 			  const char *token)
271ec9b2065SIsaku Yamahata {
272ec9b2065SIsaku Yamahata 	char sysrq_key = '\0';
273ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
274ec9b2065SIsaku Yamahata 	int err;
275ec9b2065SIsaku Yamahata 
276ec9b2065SIsaku Yamahata  again:
277ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
278ec9b2065SIsaku Yamahata 	if (err)
279ec9b2065SIsaku Yamahata 		return;
2804e93b648SJuergen Gross 	err = xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key);
2814e93b648SJuergen Gross 	if (err < 0) {
2824e93b648SJuergen Gross 		/*
2834e93b648SJuergen Gross 		 * The Xenstore watch fires directly after registering it and
2844e93b648SJuergen Gross 		 * after a suspend/resume cycle. So ENOENT is no error but
28587dffe86SVitaly Kuznetsov 		 * might happen in those cases. ERANGE is observed when we get
28687dffe86SVitaly Kuznetsov 		 * an empty value (''), this happens when we acknowledge the
28787dffe86SVitaly Kuznetsov 		 * request by writing '\0' below.
2884e93b648SJuergen Gross 		 */
28987dffe86SVitaly Kuznetsov 		if (err != -ENOENT && err != -ERANGE)
2904e93b648SJuergen Gross 			pr_err("Error %d reading sysrq code in control/sysrq\n",
2914e93b648SJuergen Gross 			       err);
292ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
293ec9b2065SIsaku Yamahata 		return;
294ec9b2065SIsaku Yamahata 	}
295ec9b2065SIsaku Yamahata 
29684c029a7SZhouyang Jia 	if (sysrq_key != '\0') {
29784c029a7SZhouyang Jia 		err = xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
29884c029a7SZhouyang Jia 		if (err) {
29984c029a7SZhouyang Jia 			pr_err("%s: Error %d writing sysrq in control/sysrq\n",
30084c029a7SZhouyang Jia 			       __func__, err);
30184c029a7SZhouyang Jia 			xenbus_transaction_end(xbt, 1);
30284c029a7SZhouyang Jia 			return;
30384c029a7SZhouyang Jia 		}
30484c029a7SZhouyang Jia 	}
305ec9b2065SIsaku Yamahata 
306ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
307ec9b2065SIsaku Yamahata 	if (err == -EAGAIN)
308ec9b2065SIsaku Yamahata 		goto again;
309ec9b2065SIsaku Yamahata 
310ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
311f335397dSDmitry Torokhov 		handle_sysrq(sysrq_key);
312ec9b2065SIsaku Yamahata }
313ec9b2065SIsaku Yamahata 
314ec9b2065SIsaku Yamahata static struct xenbus_watch sysrq_watch = {
315ec9b2065SIsaku Yamahata 	.node = "control/sysrq",
316ec9b2065SIsaku Yamahata 	.callback = sysrq_handler
317ec9b2065SIsaku Yamahata };
318f3bc3189SRandy Dunlap #endif
319f3bc3189SRandy Dunlap 
320f3bc3189SRandy Dunlap static struct xenbus_watch shutdown_watch = {
321f3bc3189SRandy Dunlap 	.node = "control/shutdown",
322f3bc3189SRandy Dunlap 	.callback = shutdown_handler
323f3bc3189SRandy Dunlap };
324ec9b2065SIsaku Yamahata 
325eb47f712SKonrad Rzeszutek Wilk static struct notifier_block xen_reboot_nb = {
326eb47f712SKonrad Rzeszutek Wilk 	.notifier_call = poweroff_nb,
327eb47f712SKonrad Rzeszutek Wilk };
328eb47f712SKonrad Rzeszutek Wilk 
setup_shutdown_watcher(void)329ec9b2065SIsaku Yamahata static int setup_shutdown_watcher(void)
330ec9b2065SIsaku Yamahata {
331ec9b2065SIsaku Yamahata 	int err;
33244b3c7afSJuergen Gross 	int idx;
33344b3c7afSJuergen Gross #define FEATURE_PATH_SIZE (SHUTDOWN_CMD_SIZE + sizeof("feature-"))
33444b3c7afSJuergen Gross 	char node[FEATURE_PATH_SIZE];
335ec9b2065SIsaku Yamahata 
336ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&shutdown_watch);
337ec9b2065SIsaku Yamahata 	if (err) {
338283c0972SJoe Perches 		pr_err("Failed to set shutdown watcher\n");
339ec9b2065SIsaku Yamahata 		return err;
340ec9b2065SIsaku Yamahata 	}
341ec9b2065SIsaku Yamahata 
342eb47f712SKonrad Rzeszutek Wilk 
343f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ
344ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&sysrq_watch);
345ec9b2065SIsaku Yamahata 	if (err) {
346283c0972SJoe Perches 		pr_err("Failed to set sysrq watcher\n");
347ec9b2065SIsaku Yamahata 		return err;
348ec9b2065SIsaku Yamahata 	}
349f3bc3189SRandy Dunlap #endif
350ec9b2065SIsaku Yamahata 
35144b3c7afSJuergen Gross 	for (idx = 0; idx < ARRAY_SIZE(shutdown_handlers); idx++) {
35244b3c7afSJuergen Gross 		if (!shutdown_handlers[idx].flag)
35344b3c7afSJuergen Gross 			continue;
35444b3c7afSJuergen Gross 		snprintf(node, FEATURE_PATH_SIZE, "feature-%s",
35544b3c7afSJuergen Gross 			 shutdown_handlers[idx].command);
35684c029a7SZhouyang Jia 		err = xenbus_printf(XBT_NIL, "control", node, "%u", 1);
35784c029a7SZhouyang Jia 		if (err) {
35884c029a7SZhouyang Jia 			pr_err("%s: Error %d writing %s\n", __func__,
35984c029a7SZhouyang Jia 				err, node);
36084c029a7SZhouyang Jia 			return err;
36184c029a7SZhouyang Jia 		}
36244b3c7afSJuergen Gross 	}
36344b3c7afSJuergen Gross 
364ec9b2065SIsaku Yamahata 	return 0;
365ec9b2065SIsaku Yamahata }
366ec9b2065SIsaku Yamahata 
shutdown_event(struct notifier_block * notifier,unsigned long event,void * data)367ec9b2065SIsaku Yamahata static int shutdown_event(struct notifier_block *notifier,
368ec9b2065SIsaku Yamahata 			  unsigned long event,
369ec9b2065SIsaku Yamahata 			  void *data)
370ec9b2065SIsaku Yamahata {
371ec9b2065SIsaku Yamahata 	setup_shutdown_watcher();
372ec9b2065SIsaku Yamahata 	return NOTIFY_DONE;
373ec9b2065SIsaku Yamahata }
374ec9b2065SIsaku Yamahata 
xen_setup_shutdown_event(void)375016b6f5fSStefano Stabellini int xen_setup_shutdown_event(void)
376ec9b2065SIsaku Yamahata {
377ec9b2065SIsaku Yamahata 	static struct notifier_block xenstore_notifier = {
378ec9b2065SIsaku Yamahata 		.notifier_call = shutdown_event
379ec9b2065SIsaku Yamahata 	};
380702d4eb9SStefano Stabellini 
381702d4eb9SStefano Stabellini 	if (!xen_domain())
382702d4eb9SStefano Stabellini 		return -ENODEV;
383ec9b2065SIsaku Yamahata 	register_xenstore_notifier(&xenstore_notifier);
384eb47f712SKonrad Rzeszutek Wilk 	register_reboot_notifier(&xen_reboot_nb);
385ec9b2065SIsaku Yamahata 
386ec9b2065SIsaku Yamahata 	return 0;
387ec9b2065SIsaku Yamahata }
388183d03ccSStefano Stabellini EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
389ec9b2065SIsaku Yamahata 
390702d4eb9SStefano Stabellini subsys_initcall(xen_setup_shutdown_event);
391