blob: 4dd01865ad18dbb45810ed44fc9a08b73bf3e64b [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09006#include <linux/slab.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -07007#include <linux/reboot.h>
8#include <linux/sysrq.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +01009#include <linux/stop_machine.h>
10#include <linux/freezer.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070011
Stefano Stabellini016b6f52010-05-14 12:45:07 +010012#include <xen/xen.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070013#include <xen/xenbus.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010014#include <xen/grant_table.h>
15#include <xen/events.h>
16#include <xen/hvc-console.h>
17#include <xen/xen-ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070018
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010019#include <asm/xen/hypercall.h>
20#include <asm/xen/page.h>
Stefano Stabellini016b6f52010-05-14 12:45:07 +010021#include <asm/xen/hypervisor.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010022
23enum shutdown_state {
24 SHUTDOWN_INVALID = -1,
25 SHUTDOWN_POWEROFF = 0,
26 SHUTDOWN_SUSPEND = 2,
27 /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
28 report a crash, not be instructed to crash!
29 HALT is the same as POWEROFF, as far as we're concerned. The tools use
30 the distinction when we return the reason code to them. */
31 SHUTDOWN_HALT = 4,
32};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070033
34/* Ignore multiple shutdown requests. */
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010035static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
36
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010037#ifdef CONFIG_PM_SLEEP
Stefano Stabellini016b6f52010-05-14 12:45:07 +010038static int xen_hvm_suspend(void *data)
39{
Ian Campbell8dd38382011-02-17 10:31:20 +000040 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010041 int *cancelled = data;
42
43 BUG_ON(!irqs_disabled());
44
Ian Campbell8dd38382011-02-17 10:31:20 +000045 err = sysdev_suspend(PMSG_SUSPEND);
46 if (err) {
47 printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
48 err);
49 return err;
50 }
51
Ian Campbellbd1c0ad2011-02-17 11:04:20 +000052 /*
53 * This hypercall returns 1 if suspend was cancelled
54 * or the domain was merely checkpointed, and 0 if it
55 * is resuming in a new domain.
56 */
57 *cancelled = HYPERVISOR_suspend(0UL);
Stefano Stabellini016b6f52010-05-14 12:45:07 +010058
59 xen_hvm_post_suspend(*cancelled);
60 gnttab_resume();
61
62 if (!*cancelled) {
63 xen_irq_resume();
Stefano Stabellini6411fe692010-12-01 14:51:44 +000064 xen_console_resume();
Stefano Stabellini016b6f52010-05-14 12:45:07 +010065 xen_timer_resume();
66 }
67
Ian Campbell8dd38382011-02-17 10:31:20 +000068 sysdev_resume();
69
Stefano Stabellini016b6f52010-05-14 12:45:07 +010070 return 0;
71}
72
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010073static int xen_suspend(void *data)
74{
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010075 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010076 int *cancelled = data;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010077
78 BUG_ON(!irqs_disabled());
79
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010080 err = sysdev_suspend(PMSG_SUSPEND);
81 if (err) {
82 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
83 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010084 return err;
85 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010086
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010087 xen_mm_pin_all();
88 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010089 xen_pre_suspend();
90
91 /*
92 * This hypercall returns 1 if suspend was cancelled
93 * or the domain was merely checkpointed, and 0 if it
94 * is resuming in a new domain.
95 */
96 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
97
98 xen_post_suspend(*cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010099 gnttab_resume();
100 xen_mm_unpin_all();
101
102 if (!*cancelled) {
103 xen_irq_resume();
104 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700105 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100106 }
107
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000108 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000109
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100110 return 0;
111}
112
113static void do_suspend(void)
114{
115 int err;
116 int cancelled = 1;
117
118 shutting_down = SHUTDOWN_SUSPEND;
119
120#ifdef CONFIG_PREEMPT
121 /* If the kernel is preemptible, we need to freeze all the processes
122 to prevent them from being in the middle of a pagetable update
123 during suspend. */
124 err = freeze_processes();
125 if (err) {
126 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200127 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100128 }
129#endif
130
Alan Sternd1616302009-05-24 22:05:42 +0200131 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100132 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200133 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000134 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100135 }
136
Ian Campbellc5cae662009-12-17 13:57:09 +0000137 printk(KERN_DEBUG "suspending xenstore...\n");
138 xs_suspend();
139
Alan Sternd1616302009-05-24 22:05:42 +0200140 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100141 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200142 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000143 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100144 }
145
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100146 if (xen_hvm_domain())
147 err = stop_machine(xen_hvm_suspend, &cancelled, cpumask_of(0));
148 else
149 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800150
151 dpm_resume_noirq(PMSG_RESUME);
152
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100153 if (err) {
154 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000155 cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100156 }
157
Ian Campbellc5cae662009-12-17 13:57:09 +0000158out_resume:
Isaku Yamahataad55db92008-07-08 15:06:32 -0700159 if (!cancelled) {
160 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800161 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700162 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800163 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100164
Alan Sternd1616302009-05-24 22:05:42 +0200165 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100166
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100167 /* Make sure timer events get retriggered on all CPUs */
168 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000169
170out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100171#ifdef CONFIG_PREEMPT
172 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000173out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200174#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100175 shutting_down = SHUTDOWN_INVALID;
176}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100177#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700178
Ian Campbell55271722011-02-17 11:04:20 +0000179struct shutdown_handler {
180 const char *command;
181 void (*cb)(void);
182};
183
184static void do_poweroff(void)
185{
186 shutting_down = SHUTDOWN_POWEROFF;
187 orderly_poweroff(false);
188}
189
190static void do_reboot(void)
191{
192 shutting_down = SHUTDOWN_POWEROFF; /* ? */
193 ctrl_alt_del();
194}
195
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700196static void shutdown_handler(struct xenbus_watch *watch,
197 const char **vec, unsigned int len)
198{
199 char *str;
200 struct xenbus_transaction xbt;
201 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000202 static struct shutdown_handler handlers[] = {
203 { "poweroff", do_poweroff },
204 { "halt", do_poweroff },
205 { "reboot", do_reboot },
206#ifdef CONFIG_PM_SLEEP
207 { "suspend", do_suspend },
208#endif
209 {NULL, NULL},
210 };
211 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700212
213 if (shutting_down != SHUTDOWN_INVALID)
214 return;
215
216 again:
217 err = xenbus_transaction_start(&xbt);
218 if (err)
219 return;
220
221 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
222 /* Ignore read errors and empty reads. */
223 if (XENBUS_IS_ERR_READ(str)) {
224 xenbus_transaction_end(xbt, 1);
225 return;
226 }
227
Ian Campbell55271722011-02-17 11:04:20 +0000228 for (handler = &handlers[0]; handler->command; handler++) {
229 if (strcmp(str, handler->command) == 0)
230 break;
231 }
232
233 /* Only acknowledge commands which we are prepared to handle. */
234 if (handler->cb)
235 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700236
237 err = xenbus_transaction_end(xbt, 0);
238 if (err == -EAGAIN) {
239 kfree(str);
240 goto again;
241 }
242
Ian Campbell55271722011-02-17 11:04:20 +0000243 if (handler->cb) {
244 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100245 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700246 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
247 shutting_down = SHUTDOWN_INVALID;
248 }
249
250 kfree(str);
251}
252
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700253#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700254static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
255 unsigned int len)
256{
257 char sysrq_key = '\0';
258 struct xenbus_transaction xbt;
259 int err;
260
261 again:
262 err = xenbus_transaction_start(&xbt);
263 if (err)
264 return;
265 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
266 printk(KERN_ERR "Unable to read sysrq code in "
267 "control/sysrq\n");
268 xenbus_transaction_end(xbt, 1);
269 return;
270 }
271
272 if (sysrq_key != '\0')
273 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
274
275 err = xenbus_transaction_end(xbt, 0);
276 if (err == -EAGAIN)
277 goto again;
278
279 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700280 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700281}
282
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700283static struct xenbus_watch sysrq_watch = {
284 .node = "control/sysrq",
285 .callback = sysrq_handler
286};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700287#endif
288
289static struct xenbus_watch shutdown_watch = {
290 .node = "control/shutdown",
291 .callback = shutdown_handler
292};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700293
294static int setup_shutdown_watcher(void)
295{
296 int err;
297
298 err = register_xenbus_watch(&shutdown_watch);
299 if (err) {
300 printk(KERN_ERR "Failed to set shutdown watcher\n");
301 return err;
302 }
303
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700304#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700305 err = register_xenbus_watch(&sysrq_watch);
306 if (err) {
307 printk(KERN_ERR "Failed to set sysrq watcher\n");
308 return err;
309 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700310#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700311
312 return 0;
313}
314
315static int shutdown_event(struct notifier_block *notifier,
316 unsigned long event,
317 void *data)
318{
319 setup_shutdown_watcher();
320 return NOTIFY_DONE;
321}
322
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100323int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700324{
325 static struct notifier_block xenstore_notifier = {
326 .notifier_call = shutdown_event
327 };
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000328
329 if (!xen_domain())
330 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700331 register_xenstore_notifier(&xenstore_notifier);
332
333 return 0;
334}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100335EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700336
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000337subsys_initcall(xen_setup_shutdown_event);