blob: fc6c94c0b436f53c8d49b1849096d2f7ad441fbe [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
Stanislaw Gruszkacd979882014-02-26 11:30:30 +010049static RAW_NOTIFIER_HEAD(xen_resume_notifier);
50
51void xen_resume_notifier_register(struct notifier_block *nb)
52{
53 raw_notifier_chain_register(&xen_resume_notifier, nb);
54}
55EXPORT_SYMBOL_GPL(xen_resume_notifier_register);
56
57void xen_resume_notifier_unregister(struct notifier_block *nb)
58{
59 raw_notifier_chain_unregister(&xen_resume_notifier, nb);
60}
61EXPORT_SYMBOL_GPL(xen_resume_notifier_unregister);
62
Stefano Stabellini65e053a2013-06-27 14:28:38 +010063#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell07af3812011-02-17 11:04:20 +000064static void xen_hvm_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000065{
Ian Campbell07af3812011-02-17 11:04:20 +000066 xen_arch_hvm_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000067 gnttab_resume();
68}
69
70static void xen_pre_suspend(void)
71{
72 xen_mm_pin_all();
73 gnttab_suspend();
Ian Campbell07af3812011-02-17 11:04:20 +000074 xen_arch_pre_suspend();
Ian Campbell82043bb2011-02-17 11:04:20 +000075}
76
Ian Campbell07af3812011-02-17 11:04:20 +000077static void xen_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000078{
Ian Campbell07af3812011-02-17 11:04:20 +000079 xen_arch_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000080 gnttab_resume();
81 xen_mm_unpin_all();
82}
83
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010084static int xen_suspend(void *data)
85{
Ian Campbellceb18022011-02-17 11:04:20 +000086 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010087 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010088
89 BUG_ON(!irqs_disabled());
90
Rafael J. Wysocki2e711c02011-04-26 19:15:07 +020091 err = syscore_suspend();
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010092 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -070093 pr_err("%s: system core suspend failed: %d\n", __func__, err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010094 return err;
95 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010096
Ian Campbell55fb4ac2011-02-17 11:04:20 +000097 if (si->pre)
98 si->pre();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010099
100 /*
101 * This hypercall returns 1 if suspend was cancelled
102 * or the domain was merely checkpointed, and 0 if it
103 * is resuming in a new domain.
104 */
Ian Campbell36b401e2011-02-17 11:04:20 +0000105 si->cancelled = HYPERVISOR_suspend(si->arg);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100106
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000107 if (si->post)
108 si->post(si->cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100109
Ian Campbellceb18022011-02-17 11:04:20 +0000110 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100111 xen_irq_resume();
112 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700113 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100114 }
115
Rafael J. Wysocki19234c02011-04-20 00:36:11 +0200116 syscore_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000117
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100118 return 0;
119}
120
121static void do_suspend(void)
122{
123 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000124 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100125
126 shutting_down = SHUTDOWN_SUSPEND;
127
128#ifdef CONFIG_PREEMPT
129 /* If the kernel is preemptible, we need to freeze all the processes
130 to prevent them from being in the middle of a pagetable update
131 during suspend. */
132 err = freeze_processes();
133 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700134 pr_err("%s: freeze failed %d\n", __func__, err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200135 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100136 }
137#endif
138
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800139 err = dpm_suspend_start(PMSG_FREEZE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100140 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700141 pr_err("%s: dpm_suspend_start %d\n", __func__, err);
Ian Campbell65f63382009-12-01 11:47:14 +0000142 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100143 }
144
Ian Campbellc5cae662009-12-17 13:57:09 +0000145 printk(KERN_DEBUG "suspending xenstore...\n");
146 xs_suspend();
147
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100148 err = dpm_suspend_end(PMSG_FREEZE);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100149 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700150 pr_err("dpm_suspend_end failed: %d\n", err);
Konrad Rzeszutek Wilk186bab12012-04-17 14:35:49 -0400151 si.cancelled = 0;
Ian Campbell65f63382009-12-01 11:47:14 +0000152 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100153 }
154
Ian Campbellceb18022011-02-17 11:04:20 +0000155 si.cancelled = 1;
156
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000157 if (xen_hvm_domain()) {
Ian Campbell36b401e2011-02-17 11:04:20 +0000158 si.arg = 0UL;
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000159 si.pre = NULL;
160 si.post = &xen_hvm_post_suspend;
161 } else {
Ian Campbell36b401e2011-02-17 11:04:20 +0000162 si.arg = virt_to_mfn(xen_start_info);
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000163 si.pre = &xen_pre_suspend;
164 si.post = &xen_post_suspend;
165 }
Ian Campbell36b401e2011-02-17 11:04:20 +0000166
Ian Campbellb056b6a2011-02-17 11:04:20 +0000167 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800168
Stanislaw Gruszkacd979882014-02-26 11:30:30 +0100169 raw_notifier_call_chain(&xen_resume_notifier, 0, NULL);
170
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100171 dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800172
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100173 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700174 pr_err("failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000175 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100176 }
177
Ian Campbellc5cae662009-12-17 13:57:09 +0000178out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000179 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700180 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800181 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700182 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800183 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100184
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800185 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100186
Ian Campbell65f63382009-12-01 11:47:14 +0000187out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100188#ifdef CONFIG_PREEMPT
189 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000190out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200191#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100192 shutting_down = SHUTDOWN_INVALID;
193}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200194#endif /* CONFIG_HIBERNATE_CALLBACKS */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700195
Ian Campbell55271722011-02-17 11:04:20 +0000196struct shutdown_handler {
197 const char *command;
198 void (*cb)(void);
199};
200
201static void do_poweroff(void)
202{
203 shutting_down = SHUTDOWN_POWEROFF;
204 orderly_poweroff(false);
205}
206
207static void do_reboot(void)
208{
209 shutting_down = SHUTDOWN_POWEROFF; /* ? */
210 ctrl_alt_del();
211}
212
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700213static void shutdown_handler(struct xenbus_watch *watch,
214 const char **vec, unsigned int len)
215{
216 char *str;
217 struct xenbus_transaction xbt;
218 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000219 static struct shutdown_handler handlers[] = {
220 { "poweroff", do_poweroff },
221 { "halt", do_poweroff },
222 { "reboot", do_reboot },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200223#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000224 { "suspend", do_suspend },
225#endif
226 {NULL, NULL},
227 };
228 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700229
230 if (shutting_down != SHUTDOWN_INVALID)
231 return;
232
233 again:
234 err = xenbus_transaction_start(&xbt);
235 if (err)
236 return;
237
238 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
239 /* Ignore read errors and empty reads. */
240 if (XENBUS_IS_ERR_READ(str)) {
241 xenbus_transaction_end(xbt, 1);
242 return;
243 }
244
Ian Campbell55271722011-02-17 11:04:20 +0000245 for (handler = &handlers[0]; handler->command; handler++) {
246 if (strcmp(str, handler->command) == 0)
247 break;
248 }
249
250 /* Only acknowledge commands which we are prepared to handle. */
251 if (handler->cb)
252 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700253
254 err = xenbus_transaction_end(xbt, 0);
255 if (err == -EAGAIN) {
256 kfree(str);
257 goto again;
258 }
259
Ian Campbell55271722011-02-17 11:04:20 +0000260 if (handler->cb) {
261 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100262 } else {
Joe Perches283c0972013-06-28 03:21:41 -0700263 pr_info("Ignoring shutdown request: %s\n", str);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700264 shutting_down = SHUTDOWN_INVALID;
265 }
266
267 kfree(str);
268}
269
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700270#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700271static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
272 unsigned int len)
273{
274 char sysrq_key = '\0';
275 struct xenbus_transaction xbt;
276 int err;
277
278 again:
279 err = xenbus_transaction_start(&xbt);
280 if (err)
281 return;
282 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
Joe Perches283c0972013-06-28 03:21:41 -0700283 pr_err("Unable to read sysrq code in control/sysrq\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700284 xenbus_transaction_end(xbt, 1);
285 return;
286 }
287
288 if (sysrq_key != '\0')
289 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
290
291 err = xenbus_transaction_end(xbt, 0);
292 if (err == -EAGAIN)
293 goto again;
294
295 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700296 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700297}
298
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700299static struct xenbus_watch sysrq_watch = {
300 .node = "control/sysrq",
301 .callback = sysrq_handler
302};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700303#endif
304
305static struct xenbus_watch shutdown_watch = {
306 .node = "control/shutdown",
307 .callback = shutdown_handler
308};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700309
310static int setup_shutdown_watcher(void)
311{
312 int err;
313
314 err = register_xenbus_watch(&shutdown_watch);
315 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700316 pr_err("Failed to set shutdown watcher\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700317 return err;
318 }
319
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700320#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700321 err = register_xenbus_watch(&sysrq_watch);
322 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700323 pr_err("Failed to set sysrq watcher\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700324 return err;
325 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700326#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700327
328 return 0;
329}
330
331static int shutdown_event(struct notifier_block *notifier,
332 unsigned long event,
333 void *data)
334{
335 setup_shutdown_watcher();
336 return NOTIFY_DONE;
337}
338
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100339int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700340{
341 static struct notifier_block xenstore_notifier = {
342 .notifier_call = shutdown_event
343 };
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000344
345 if (!xen_domain())
346 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700347 register_xenstore_notifier(&xenstore_notifier);
348
349 return 0;
350}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100351EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700352
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000353subsys_initcall(xen_setup_shutdown_event);