blob: 624e8dc2453239ec84f1e1d3564da09c38b16581 [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
Ian Campbell65f63382009-12-01 11:47:14 +0000171out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100172#ifdef CONFIG_PREEMPT
173 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000174out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200175#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100176 shutting_down = SHUTDOWN_INVALID;
177}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200178#endif /* CONFIG_HIBERNATE_CALLBACKS */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700179
Ian Campbell55271722011-02-17 11:04:20 +0000180struct shutdown_handler {
181 const char *command;
182 void (*cb)(void);
183};
184
185static void do_poweroff(void)
186{
187 shutting_down = SHUTDOWN_POWEROFF;
188 orderly_poweroff(false);
189}
190
191static void do_reboot(void)
192{
193 shutting_down = SHUTDOWN_POWEROFF; /* ? */
194 ctrl_alt_del();
195}
196
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700197static void shutdown_handler(struct xenbus_watch *watch,
198 const char **vec, unsigned int len)
199{
200 char *str;
201 struct xenbus_transaction xbt;
202 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000203 static struct shutdown_handler handlers[] = {
204 { "poweroff", do_poweroff },
205 { "halt", do_poweroff },
206 { "reboot", do_reboot },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200207#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000208 { "suspend", do_suspend },
209#endif
210 {NULL, NULL},
211 };
212 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700213
214 if (shutting_down != SHUTDOWN_INVALID)
215 return;
216
217 again:
218 err = xenbus_transaction_start(&xbt);
219 if (err)
220 return;
221
222 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
223 /* Ignore read errors and empty reads. */
224 if (XENBUS_IS_ERR_READ(str)) {
225 xenbus_transaction_end(xbt, 1);
226 return;
227 }
228
Ian Campbell55271722011-02-17 11:04:20 +0000229 for (handler = &handlers[0]; handler->command; handler++) {
230 if (strcmp(str, handler->command) == 0)
231 break;
232 }
233
234 /* Only acknowledge commands which we are prepared to handle. */
235 if (handler->cb)
236 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700237
238 err = xenbus_transaction_end(xbt, 0);
239 if (err == -EAGAIN) {
240 kfree(str);
241 goto again;
242 }
243
Ian Campbell55271722011-02-17 11:04:20 +0000244 if (handler->cb) {
245 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100246 } else {
Joe Perches283c0972013-06-28 03:21:41 -0700247 pr_info("Ignoring shutdown request: %s\n", str);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700248 shutting_down = SHUTDOWN_INVALID;
249 }
250
251 kfree(str);
252}
253
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700254#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700255static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
256 unsigned int len)
257{
258 char sysrq_key = '\0';
259 struct xenbus_transaction xbt;
260 int err;
261
262 again:
263 err = xenbus_transaction_start(&xbt);
264 if (err)
265 return;
266 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
Joe Perches283c0972013-06-28 03:21:41 -0700267 pr_err("Unable to read sysrq code in control/sysrq\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700268 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) {
Joe Perches283c0972013-06-28 03:21:41 -0700300 pr_err("Failed to set shutdown watcher\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700301 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) {
Joe Perches283c0972013-06-28 03:21:41 -0700307 pr_err("Failed to set sysrq watcher\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700308 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);