blob: 5b7a0a9402e704b9b5616766632a498a49816088 [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
Ian Campbellceb18022011-02-17 11:04:20 +000037struct suspend_info {
38 int cancelled;
Ian Campbell36b401e2011-02-17 11:04:20 +000039 unsigned long arg; /* extra hypercall argument */
Ian Campbell55fb4ac2011-02-17 11:04:20 +000040 void (*pre)(void);
41 void (*post)(int cancelled);
Ian Campbellceb18022011-02-17 11:04:20 +000042};
43
Ian Campbell07af3812011-02-17 11:04:20 +000044static void xen_hvm_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000045{
Ian Campbell07af3812011-02-17 11:04:20 +000046 xen_arch_hvm_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000047 gnttab_resume();
48}
49
50static void xen_pre_suspend(void)
51{
52 xen_mm_pin_all();
53 gnttab_suspend();
Ian Campbell07af3812011-02-17 11:04:20 +000054 xen_arch_pre_suspend();
Ian Campbell82043bb2011-02-17 11:04:20 +000055}
56
Ian Campbell07af3812011-02-17 11:04:20 +000057static void xen_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000058{
Ian Campbell07af3812011-02-17 11:04:20 +000059 xen_arch_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000060 gnttab_resume();
61 xen_mm_unpin_all();
62}
63
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010064#ifdef CONFIG_PM_SLEEP
Stefano Stabellini016b6f52010-05-14 12:45:07 +010065static int xen_hvm_suspend(void *data)
66{
Ian Campbellceb18022011-02-17 11:04:20 +000067 struct suspend_info *si = data;
Ian Campbell8dd38382011-02-17 10:31:20 +000068 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010069
70 BUG_ON(!irqs_disabled());
71
Ian Campbell8dd38382011-02-17 10:31:20 +000072 err = sysdev_suspend(PMSG_SUSPEND);
73 if (err) {
74 printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
75 err);
76 return err;
77 }
78
Ian Campbell55fb4ac2011-02-17 11:04:20 +000079 if (si->pre)
80 si->pre();
81
Ian Campbellbd1c0ad2011-02-17 11:04:20 +000082 /*
83 * This hypercall returns 1 if suspend was cancelled
84 * or the domain was merely checkpointed, and 0 if it
85 * is resuming in a new domain.
86 */
Ian Campbell36b401e2011-02-17 11:04:20 +000087 si->cancelled = HYPERVISOR_suspend(si->arg);
Stefano Stabellini016b6f52010-05-14 12:45:07 +010088
Ian Campbell55fb4ac2011-02-17 11:04:20 +000089 if (si->post)
90 si->post(si->cancelled);
Stefano Stabellini016b6f52010-05-14 12:45:07 +010091
Ian Campbellceb18022011-02-17 11:04:20 +000092 if (!si->cancelled) {
Stefano Stabellini016b6f52010-05-14 12:45:07 +010093 xen_irq_resume();
Stefano Stabellini6411fe692010-12-01 14:51:44 +000094 xen_console_resume();
Stefano Stabellini016b6f52010-05-14 12:45:07 +010095 xen_timer_resume();
96 }
97
Ian Campbell8dd38382011-02-17 10:31:20 +000098 sysdev_resume();
99
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100100 return 0;
101}
102
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100103static int xen_suspend(void *data)
104{
Ian Campbellceb18022011-02-17 11:04:20 +0000105 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100106 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100107
108 BUG_ON(!irqs_disabled());
109
Rafael J. Wysocki770824b2009-02-22 18:38:50 +0100110 err = sysdev_suspend(PMSG_SUSPEND);
111 if (err) {
112 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
113 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +0100114 return err;
115 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100116
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000117 if (si->pre)
118 si->pre();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100119
120 /*
121 * This hypercall returns 1 if suspend was cancelled
122 * or the domain was merely checkpointed, and 0 if it
123 * is resuming in a new domain.
124 */
Ian Campbell36b401e2011-02-17 11:04:20 +0000125 si->cancelled = HYPERVISOR_suspend(si->arg);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100126
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000127 if (si->post)
128 si->post(si->cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100129
Ian Campbellceb18022011-02-17 11:04:20 +0000130 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100131 xen_irq_resume();
132 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700133 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100134 }
135
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000136 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000137
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100138 return 0;
139}
140
141static void do_suspend(void)
142{
143 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000144 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100145
146 shutting_down = SHUTDOWN_SUSPEND;
147
148#ifdef CONFIG_PREEMPT
149 /* If the kernel is preemptible, we need to freeze all the processes
150 to prevent them from being in the middle of a pagetable update
151 during suspend. */
152 err = freeze_processes();
153 if (err) {
154 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200155 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100156 }
157#endif
158
Alan Sternd1616302009-05-24 22:05:42 +0200159 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100160 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200161 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000162 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100163 }
164
Ian Campbellc5cae662009-12-17 13:57:09 +0000165 printk(KERN_DEBUG "suspending xenstore...\n");
166 xs_suspend();
167
Alan Sternd1616302009-05-24 22:05:42 +0200168 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100169 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200170 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000171 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100172 }
173
Ian Campbellceb18022011-02-17 11:04:20 +0000174 si.cancelled = 1;
175
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000176 if (xen_hvm_domain()) {
Ian Campbell36b401e2011-02-17 11:04:20 +0000177 si.arg = 0UL;
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000178 si.pre = NULL;
179 si.post = &xen_hvm_post_suspend;
180 } else {
Ian Campbell36b401e2011-02-17 11:04:20 +0000181 si.arg = virt_to_mfn(xen_start_info);
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000182 si.pre = &xen_pre_suspend;
183 si.post = &xen_post_suspend;
184 }
Ian Campbell36b401e2011-02-17 11:04:20 +0000185
186 if (xen_hvm_domain())
Ian Campbellceb18022011-02-17 11:04:20 +0000187 err = stop_machine(xen_hvm_suspend, &si, cpumask_of(0));
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100188 else
Ian Campbellceb18022011-02-17 11:04:20 +0000189 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800190
191 dpm_resume_noirq(PMSG_RESUME);
192
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100193 if (err) {
194 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000195 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100196 }
197
Ian Campbellc5cae662009-12-17 13:57:09 +0000198out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000199 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700200 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800201 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700202 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800203 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100204
Alan Sternd1616302009-05-24 22:05:42 +0200205 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100206
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100207 /* Make sure timer events get retriggered on all CPUs */
208 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000209
210out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100211#ifdef CONFIG_PREEMPT
212 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000213out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200214#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100215 shutting_down = SHUTDOWN_INVALID;
216}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100217#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700218
Ian Campbell55271722011-02-17 11:04:20 +0000219struct shutdown_handler {
220 const char *command;
221 void (*cb)(void);
222};
223
224static void do_poweroff(void)
225{
226 shutting_down = SHUTDOWN_POWEROFF;
227 orderly_poweroff(false);
228}
229
230static void do_reboot(void)
231{
232 shutting_down = SHUTDOWN_POWEROFF; /* ? */
233 ctrl_alt_del();
234}
235
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700236static void shutdown_handler(struct xenbus_watch *watch,
237 const char **vec, unsigned int len)
238{
239 char *str;
240 struct xenbus_transaction xbt;
241 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000242 static struct shutdown_handler handlers[] = {
243 { "poweroff", do_poweroff },
244 { "halt", do_poweroff },
245 { "reboot", do_reboot },
246#ifdef CONFIG_PM_SLEEP
247 { "suspend", do_suspend },
248#endif
249 {NULL, NULL},
250 };
251 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700252
253 if (shutting_down != SHUTDOWN_INVALID)
254 return;
255
256 again:
257 err = xenbus_transaction_start(&xbt);
258 if (err)
259 return;
260
261 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
262 /* Ignore read errors and empty reads. */
263 if (XENBUS_IS_ERR_READ(str)) {
264 xenbus_transaction_end(xbt, 1);
265 return;
266 }
267
Ian Campbell55271722011-02-17 11:04:20 +0000268 for (handler = &handlers[0]; handler->command; handler++) {
269 if (strcmp(str, handler->command) == 0)
270 break;
271 }
272
273 /* Only acknowledge commands which we are prepared to handle. */
274 if (handler->cb)
275 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700276
277 err = xenbus_transaction_end(xbt, 0);
278 if (err == -EAGAIN) {
279 kfree(str);
280 goto again;
281 }
282
Ian Campbell55271722011-02-17 11:04:20 +0000283 if (handler->cb) {
284 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100285 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700286 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
287 shutting_down = SHUTDOWN_INVALID;
288 }
289
290 kfree(str);
291}
292
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700293#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700294static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
295 unsigned int len)
296{
297 char sysrq_key = '\0';
298 struct xenbus_transaction xbt;
299 int err;
300
301 again:
302 err = xenbus_transaction_start(&xbt);
303 if (err)
304 return;
305 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
306 printk(KERN_ERR "Unable to read sysrq code in "
307 "control/sysrq\n");
308 xenbus_transaction_end(xbt, 1);
309 return;
310 }
311
312 if (sysrq_key != '\0')
313 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
314
315 err = xenbus_transaction_end(xbt, 0);
316 if (err == -EAGAIN)
317 goto again;
318
319 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700320 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700321}
322
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700323static struct xenbus_watch sysrq_watch = {
324 .node = "control/sysrq",
325 .callback = sysrq_handler
326};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700327#endif
328
329static struct xenbus_watch shutdown_watch = {
330 .node = "control/shutdown",
331 .callback = shutdown_handler
332};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700333
334static int setup_shutdown_watcher(void)
335{
336 int err;
337
338 err = register_xenbus_watch(&shutdown_watch);
339 if (err) {
340 printk(KERN_ERR "Failed to set shutdown watcher\n");
341 return err;
342 }
343
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700344#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700345 err = register_xenbus_watch(&sysrq_watch);
346 if (err) {
347 printk(KERN_ERR "Failed to set sysrq watcher\n");
348 return err;
349 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700350#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700351
352 return 0;
353}
354
355static int shutdown_event(struct notifier_block *notifier,
356 unsigned long event,
357 void *data)
358{
359 setup_shutdown_watcher();
360 return NOTIFY_DONE;
361}
362
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100363int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700364{
365 static struct notifier_block xenstore_notifier = {
366 .notifier_call = shutdown_event
367 };
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000368
369 if (!xen_domain())
370 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700371 register_xenstore_notifier(&xenstore_notifier);
372
373 return 0;
374}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100375EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700376
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000377subsys_initcall(xen_setup_shutdown_event);