blob: 9385b4524547860e54a55a14d3727561be8d124e [file] [log] [blame]
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -07001/*
2 * Handle extern requests for shutdown, reboot and sysrq
3 */
Joe Perches283c0972013-06-28 03:21:41 -07004
5#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
6
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -07007#include <linux/kernel.h>
8#include <linux/err.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09009#include <linux/slab.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070010#include <linux/reboot.h>
11#include <linux/sysrq.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010012#include <linux/stop_machine.h>
13#include <linux/freezer.h>
Rafael J. Wysocki19234c02011-04-20 00:36:11 +020014#include <linux/syscore_ops.h>
Paul Gortmaker63c97442011-07-10 13:22:07 -040015#include <linux/export.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070016
Stefano Stabellini016b6f52010-05-14 12:45:07 +010017#include <xen/xen.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070018#include <xen/xenbus.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010019#include <xen/grant_table.h>
20#include <xen/events.h>
21#include <xen/hvc-console.h>
22#include <xen/xen-ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070023
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010024#include <asm/xen/hypercall.h>
25#include <asm/xen/page.h>
Stefano Stabellini016b6f52010-05-14 12:45:07 +010026#include <asm/xen/hypervisor.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010027
28enum shutdown_state {
29 SHUTDOWN_INVALID = -1,
30 SHUTDOWN_POWEROFF = 0,
31 SHUTDOWN_SUSPEND = 2,
32 /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
33 report a crash, not be instructed to crash!
34 HALT is the same as POWEROFF, as far as we're concerned. The tools use
35 the distinction when we return the reason code to them. */
36 SHUTDOWN_HALT = 4,
37};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070038
39/* Ignore multiple shutdown requests. */
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010040static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
41
Ian Campbellceb18022011-02-17 11:04:20 +000042struct suspend_info {
43 int cancelled;
Ian Campbell36b401e2011-02-17 11:04:20 +000044 unsigned long arg; /* extra hypercall argument */
Ian Campbell55fb4ac2011-02-17 11:04:20 +000045 void (*pre)(void);
46 void (*post)(int cancelled);
Ian Campbellceb18022011-02-17 11:04:20 +000047};
48
Stefano Stabellini65e053a2013-06-27 14:28:38 +010049#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell07af3812011-02-17 11:04:20 +000050static void xen_hvm_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000051{
Ian Campbell07af3812011-02-17 11:04:20 +000052 xen_arch_hvm_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000053 gnttab_resume();
54}
55
56static void xen_pre_suspend(void)
57{
58 xen_mm_pin_all();
59 gnttab_suspend();
Ian Campbell07af3812011-02-17 11:04:20 +000060 xen_arch_pre_suspend();
Ian Campbell82043bb2011-02-17 11:04:20 +000061}
62
Ian Campbell07af3812011-02-17 11:04:20 +000063static void xen_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000064{
Ian Campbell07af3812011-02-17 11:04:20 +000065 xen_arch_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000066 gnttab_resume();
67 xen_mm_unpin_all();
68}
69
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010070static int xen_suspend(void *data)
71{
Ian Campbellceb18022011-02-17 11:04:20 +000072 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010073 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010074
75 BUG_ON(!irqs_disabled());
76
Rafael J. Wysocki2e711c02011-04-26 19:15:07 +020077 err = syscore_suspend();
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010078 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -070079 pr_err("%s: system core suspend failed: %d\n", __func__, err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010080 return err;
81 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010082
Ian Campbell55fb4ac2011-02-17 11:04:20 +000083 if (si->pre)
84 si->pre();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010085
86 /*
87 * This hypercall returns 1 if suspend was cancelled
88 * or the domain was merely checkpointed, and 0 if it
89 * is resuming in a new domain.
90 */
Ian Campbell36b401e2011-02-17 11:04:20 +000091 si->cancelled = HYPERVISOR_suspend(si->arg);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010092
Ian Campbell55fb4ac2011-02-17 11:04:20 +000093 if (si->post)
94 si->post(si->cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010095
Ian Campbellceb18022011-02-17 11:04:20 +000096 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010097 xen_irq_resume();
98 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070099 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100100 }
101
Rafael J. Wysocki19234c02011-04-20 00:36:11 +0200102 syscore_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000103
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100104 return 0;
105}
106
107static void do_suspend(void)
108{
109 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000110 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100111
112 shutting_down = SHUTDOWN_SUSPEND;
113
114#ifdef CONFIG_PREEMPT
115 /* If the kernel is preemptible, we need to freeze all the processes
116 to prevent them from being in the middle of a pagetable update
117 during suspend. */
118 err = freeze_processes();
119 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700120 pr_err("%s: freeze failed %d\n", __func__, err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200121 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100122 }
123#endif
124
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800125 err = dpm_suspend_start(PMSG_FREEZE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100126 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700127 pr_err("%s: dpm_suspend_start %d\n", __func__, err);
Ian Campbell65f63382009-12-01 11:47:14 +0000128 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100129 }
130
Ian Campbellc5cae662009-12-17 13:57:09 +0000131 printk(KERN_DEBUG "suspending xenstore...\n");
132 xs_suspend();
133
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100134 err = dpm_suspend_end(PMSG_FREEZE);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100135 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700136 pr_err("dpm_suspend_end failed: %d\n", err);
Konrad Rzeszutek Wilk186bab12012-04-17 14:35:49 -0400137 si.cancelled = 0;
Ian Campbell65f63382009-12-01 11:47:14 +0000138 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100139 }
140
Ian Campbellceb18022011-02-17 11:04:20 +0000141 si.cancelled = 1;
142
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000143 if (xen_hvm_domain()) {
Ian Campbell36b401e2011-02-17 11:04:20 +0000144 si.arg = 0UL;
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000145 si.pre = NULL;
146 si.post = &xen_hvm_post_suspend;
147 } else {
Ian Campbell36b401e2011-02-17 11:04:20 +0000148 si.arg = virt_to_mfn(xen_start_info);
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000149 si.pre = &xen_pre_suspend;
150 si.post = &xen_post_suspend;
151 }
Ian Campbell36b401e2011-02-17 11:04:20 +0000152
Ian Campbellb056b6a2011-02-17 11:04:20 +0000153 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800154
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100155 dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800156
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100157 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700158 pr_err("failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000159 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100160 }
161
Ian Campbellc5cae662009-12-17 13:57:09 +0000162out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000163 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700164 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800165 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700166 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800167 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100168
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800169 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100170
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100171 /* Make sure timer events get retriggered on all CPUs */
172 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000173
174out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100175#ifdef CONFIG_PREEMPT
176 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000177out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200178#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100179 shutting_down = SHUTDOWN_INVALID;
180}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200181#endif /* CONFIG_HIBERNATE_CALLBACKS */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700182
Ian Campbell55271722011-02-17 11:04:20 +0000183struct shutdown_handler {
184 const char *command;
185 void (*cb)(void);
186};
187
188static void do_poweroff(void)
189{
190 shutting_down = SHUTDOWN_POWEROFF;
191 orderly_poweroff(false);
192}
193
194static void do_reboot(void)
195{
196 shutting_down = SHUTDOWN_POWEROFF; /* ? */
197 ctrl_alt_del();
198}
199
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700200static void shutdown_handler(struct xenbus_watch *watch,
201 const char **vec, unsigned int len)
202{
203 char *str;
204 struct xenbus_transaction xbt;
205 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000206 static struct shutdown_handler handlers[] = {
207 { "poweroff", do_poweroff },
208 { "halt", do_poweroff },
209 { "reboot", do_reboot },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200210#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000211 { "suspend", do_suspend },
212#endif
213 {NULL, NULL},
214 };
215 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700216
217 if (shutting_down != SHUTDOWN_INVALID)
218 return;
219
220 again:
221 err = xenbus_transaction_start(&xbt);
222 if (err)
223 return;
224
225 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
226 /* Ignore read errors and empty reads. */
227 if (XENBUS_IS_ERR_READ(str)) {
228 xenbus_transaction_end(xbt, 1);
229 return;
230 }
231
Ian Campbell55271722011-02-17 11:04:20 +0000232 for (handler = &handlers[0]; handler->command; handler++) {
233 if (strcmp(str, handler->command) == 0)
234 break;
235 }
236
237 /* Only acknowledge commands which we are prepared to handle. */
238 if (handler->cb)
239 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700240
241 err = xenbus_transaction_end(xbt, 0);
242 if (err == -EAGAIN) {
243 kfree(str);
244 goto again;
245 }
246
Ian Campbell55271722011-02-17 11:04:20 +0000247 if (handler->cb) {
248 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100249 } else {
Joe Perches283c0972013-06-28 03:21:41 -0700250 pr_info("Ignoring shutdown request: %s\n", str);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700251 shutting_down = SHUTDOWN_INVALID;
252 }
253
254 kfree(str);
255}
256
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700257#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700258static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
259 unsigned int len)
260{
261 char sysrq_key = '\0';
262 struct xenbus_transaction xbt;
263 int err;
264
265 again:
266 err = xenbus_transaction_start(&xbt);
267 if (err)
268 return;
269 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
Joe Perches283c0972013-06-28 03:21:41 -0700270 pr_err("Unable to read sysrq code in control/sysrq\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700271 xenbus_transaction_end(xbt, 1);
272 return;
273 }
274
275 if (sysrq_key != '\0')
276 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
277
278 err = xenbus_transaction_end(xbt, 0);
279 if (err == -EAGAIN)
280 goto again;
281
282 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700283 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700284}
285
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700286static struct xenbus_watch sysrq_watch = {
287 .node = "control/sysrq",
288 .callback = sysrq_handler
289};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700290#endif
291
292static struct xenbus_watch shutdown_watch = {
293 .node = "control/shutdown",
294 .callback = shutdown_handler
295};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700296
297static int setup_shutdown_watcher(void)
298{
299 int err;
300
301 err = register_xenbus_watch(&shutdown_watch);
302 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700303 pr_err("Failed to set shutdown watcher\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700304 return err;
305 }
306
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700307#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700308 err = register_xenbus_watch(&sysrq_watch);
309 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700310 pr_err("Failed to set sysrq watcher\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700311 return err;
312 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700313#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700314
315 return 0;
316}
317
318static int shutdown_event(struct notifier_block *notifier,
319 unsigned long event,
320 void *data)
321{
322 setup_shutdown_watcher();
323 return NOTIFY_DONE;
324}
325
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100326int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700327{
328 static struct notifier_block xenstore_notifier = {
329 .notifier_call = shutdown_event
330 };
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000331
332 if (!xen_domain())
333 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700334 register_xenstore_notifier(&xenstore_notifier);
335
336 return 0;
337}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100338EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700339
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000340subsys_initcall(xen_setup_shutdown_event);