blob: 9b91617b9582d320b67ce6c1984663d48659e03d [file] [log] [blame]
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -07001/*
2 * Handle extern requests for shutdown, reboot and sysrq
3 */
4#include <linux/kernel.h>
5#include <linux/err.h>
6#include <linux/reboot.h>
7#include <linux/sysrq.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +01008#include <linux/stop_machine.h>
9#include <linux/freezer.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070010
11#include <xen/xenbus.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010012#include <xen/grant_table.h>
13#include <xen/events.h>
14#include <xen/hvc-console.h>
15#include <xen/xen-ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070016
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010017#include <asm/xen/hypercall.h>
18#include <asm/xen/page.h>
19
20enum shutdown_state {
21 SHUTDOWN_INVALID = -1,
22 SHUTDOWN_POWEROFF = 0,
23 SHUTDOWN_SUSPEND = 2,
24 /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
25 report a crash, not be instructed to crash!
26 HALT is the same as POWEROFF, as far as we're concerned. The tools use
27 the distinction when we return the reason code to them. */
28 SHUTDOWN_HALT = 4,
29};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070030
31/* Ignore multiple shutdown requests. */
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010032static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
33
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010034#ifdef CONFIG_PM_SLEEP
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010035static int xen_suspend(void *data)
36{
37 int *cancelled = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010038 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010039
40 BUG_ON(!irqs_disabled());
41
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010042 err = device_power_down(PMSG_SUSPEND);
43 if (err) {
44 printk(KERN_ERR "xen_suspend: device_power_down failed: %d\n",
45 err);
46 return err;
47 }
48
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010049 xen_mm_pin_all();
50 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010051 xen_pre_suspend();
52
53 /*
54 * This hypercall returns 1 if suspend was cancelled
55 * or the domain was merely checkpointed, and 0 if it
56 * is resuming in a new domain.
57 */
58 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
59
60 xen_post_suspend(*cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010061 gnttab_resume();
62 xen_mm_unpin_all();
63
Stephen Rothwell55ca0892008-07-17 13:09:24 +100064 device_power_up(PMSG_RESUME);
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010065
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010066 if (!*cancelled) {
67 xen_irq_resume();
68 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070069 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010070 }
71
72 return 0;
73}
74
75static void do_suspend(void)
76{
77 int err;
78 int cancelled = 1;
79
80 shutting_down = SHUTDOWN_SUSPEND;
81
82#ifdef CONFIG_PREEMPT
83 /* If the kernel is preemptible, we need to freeze all the processes
84 to prevent them from being in the middle of a pagetable update
85 during suspend. */
86 err = freeze_processes();
87 if (err) {
88 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
89 return;
90 }
91#endif
92
93 err = device_suspend(PMSG_SUSPEND);
94 if (err) {
95 printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
96 goto out;
97 }
98
99 printk("suspending xenbus...\n");
100 /* XXX use normal device tree? */
101 xenbus_suspend();
102
Rusty Russell37a7c0f2008-08-26 00:19:26 -0500103 err = stop_machine(xen_suspend, &cancelled, &cpumask_of_cpu(0));
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100104 if (err) {
105 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
106 goto out;
107 }
108
Isaku Yamahataad55db92008-07-08 15:06:32 -0700109 if (!cancelled) {
110 xen_arch_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100111 xenbus_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700112 } else
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100113 xenbus_suspend_cancel();
114
Stephen Rothwell55ca0892008-07-17 13:09:24 +1000115 device_resume(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100116
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100117 /* Make sure timer events get retriggered on all CPUs */
118 clock_was_set();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100119out:
120#ifdef CONFIG_PREEMPT
121 thaw_processes();
122#endif
123 shutting_down = SHUTDOWN_INVALID;
124}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100125#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700126
127static void shutdown_handler(struct xenbus_watch *watch,
128 const char **vec, unsigned int len)
129{
130 char *str;
131 struct xenbus_transaction xbt;
132 int err;
133
134 if (shutting_down != SHUTDOWN_INVALID)
135 return;
136
137 again:
138 err = xenbus_transaction_start(&xbt);
139 if (err)
140 return;
141
142 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
143 /* Ignore read errors and empty reads. */
144 if (XENBUS_IS_ERR_READ(str)) {
145 xenbus_transaction_end(xbt, 1);
146 return;
147 }
148
149 xenbus_write(xbt, "control", "shutdown", "");
150
151 err = xenbus_transaction_end(xbt, 0);
152 if (err == -EAGAIN) {
153 kfree(str);
154 goto again;
155 }
156
157 if (strcmp(str, "poweroff") == 0 ||
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100158 strcmp(str, "halt") == 0) {
159 shutting_down = SHUTDOWN_POWEROFF;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700160 orderly_poweroff(false);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100161 } else if (strcmp(str, "reboot") == 0) {
162 shutting_down = SHUTDOWN_POWEROFF; /* ? */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700163 ctrl_alt_del();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100164#ifdef CONFIG_PM_SLEEP
165 } else if (strcmp(str, "suspend") == 0) {
166 do_suspend();
167#endif
168 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700169 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
170 shutting_down = SHUTDOWN_INVALID;
171 }
172
173 kfree(str);
174}
175
176static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
177 unsigned int len)
178{
179 char sysrq_key = '\0';
180 struct xenbus_transaction xbt;
181 int err;
182
183 again:
184 err = xenbus_transaction_start(&xbt);
185 if (err)
186 return;
187 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
188 printk(KERN_ERR "Unable to read sysrq code in "
189 "control/sysrq\n");
190 xenbus_transaction_end(xbt, 1);
191 return;
192 }
193
194 if (sysrq_key != '\0')
195 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
196
197 err = xenbus_transaction_end(xbt, 0);
198 if (err == -EAGAIN)
199 goto again;
200
201 if (sysrq_key != '\0')
202 handle_sysrq(sysrq_key, NULL);
203}
204
205static struct xenbus_watch shutdown_watch = {
206 .node = "control/shutdown",
207 .callback = shutdown_handler
208};
209
210static struct xenbus_watch sysrq_watch = {
211 .node = "control/sysrq",
212 .callback = sysrq_handler
213};
214
215static int setup_shutdown_watcher(void)
216{
217 int err;
218
219 err = register_xenbus_watch(&shutdown_watch);
220 if (err) {
221 printk(KERN_ERR "Failed to set shutdown watcher\n");
222 return err;
223 }
224
225 err = register_xenbus_watch(&sysrq_watch);
226 if (err) {
227 printk(KERN_ERR "Failed to set sysrq watcher\n");
228 return err;
229 }
230
231 return 0;
232}
233
234static int shutdown_event(struct notifier_block *notifier,
235 unsigned long event,
236 void *data)
237{
238 setup_shutdown_watcher();
239 return NOTIFY_DONE;
240}
241
242static int __init setup_shutdown_event(void)
243{
244 static struct notifier_block xenstore_notifier = {
245 .notifier_call = shutdown_event
246 };
247 register_xenstore_notifier(&xenstore_notifier);
248
249 return 0;
250}
251
252subsys_initcall(setup_shutdown_event);