blob: 5f1e1f3cd18619ed2899dcc5e967ca274f690189 [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;
44};
45
Stanislaw Gruszkacd979882014-02-26 11:30:30 +010046static RAW_NOTIFIER_HEAD(xen_resume_notifier);
47
48void xen_resume_notifier_register(struct notifier_block *nb)
49{
50 raw_notifier_chain_register(&xen_resume_notifier, nb);
51}
52EXPORT_SYMBOL_GPL(xen_resume_notifier_register);
53
54void xen_resume_notifier_unregister(struct notifier_block *nb)
55{
56 raw_notifier_chain_unregister(&xen_resume_notifier, nb);
57}
58EXPORT_SYMBOL_GPL(xen_resume_notifier_unregister);
59
Stefano Stabellini65e053a2013-06-27 14:28:38 +010060#ifdef CONFIG_HIBERNATE_CALLBACKS
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010061static int xen_suspend(void *data)
62{
Ian Campbellceb18022011-02-17 11:04:20 +000063 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010064 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010065
66 BUG_ON(!irqs_disabled());
67
Rafael J. Wysocki2e711c02011-04-26 19:15:07 +020068 err = syscore_suspend();
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010069 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -070070 pr_err("%s: system core suspend failed: %d\n", __func__, err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010071 return err;
72 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010073
David Vrabelaa8532c2014-05-08 11:09:23 +010074 gnttab_suspend();
75 xen_arch_pre_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010076
77 /*
78 * This hypercall returns 1 if suspend was cancelled
79 * or the domain was merely checkpointed, and 0 if it
80 * is resuming in a new domain.
81 */
David Vrabelaa8532c2014-05-08 11:09:23 +010082 si->cancelled = HYPERVISOR_suspend(xen_pv_domain()
83 ? virt_to_mfn(xen_start_info)
84 : 0);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010085
David Vrabelaa8532c2014-05-08 11:09:23 +010086 xen_arch_post_suspend(si->cancelled);
87 gnttab_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010088
Ian Campbellceb18022011-02-17 11:04:20 +000089 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010090 xen_irq_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070091 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010092 }
93
Rafael J. Wysocki19234c02011-04-20 00:36:11 +020094 syscore_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +000095
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010096 return 0;
97}
98
99static void do_suspend(void)
100{
101 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000102 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100103
104 shutting_down = SHUTDOWN_SUSPEND;
105
106#ifdef CONFIG_PREEMPT
107 /* If the kernel is preemptible, we need to freeze all the processes
108 to prevent them from being in the middle of a pagetable update
109 during suspend. */
110 err = freeze_processes();
111 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700112 pr_err("%s: freeze failed %d\n", __func__, err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200113 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100114 }
115#endif
116
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800117 err = dpm_suspend_start(PMSG_FREEZE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100118 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700119 pr_err("%s: dpm_suspend_start %d\n", __func__, err);
Ian Campbell65f63382009-12-01 11:47:14 +0000120 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100121 }
122
Ian Campbellc5cae662009-12-17 13:57:09 +0000123 printk(KERN_DEBUG "suspending xenstore...\n");
124 xs_suspend();
125
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100126 err = dpm_suspend_end(PMSG_FREEZE);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100127 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700128 pr_err("dpm_suspend_end failed: %d\n", err);
Konrad Rzeszutek Wilk186bab12012-04-17 14:35:49 -0400129 si.cancelled = 0;
Ian Campbell65f63382009-12-01 11:47:14 +0000130 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100131 }
132
Ian Campbellceb18022011-02-17 11:04:20 +0000133 si.cancelled = 1;
134
Ian Campbellb056b6a2011-02-17 11:04:20 +0000135 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800136
David Vrabel1b647822014-07-02 17:25:23 +0100137 /* Resume console as early as possible. */
138 if (!si.cancelled)
139 xen_console_resume();
140
Stanislaw Gruszkacd979882014-02-26 11:30:30 +0100141 raw_notifier_call_chain(&xen_resume_notifier, 0, NULL);
142
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100143 dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800144
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100145 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700146 pr_err("failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000147 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100148 }
149
Ian Campbellc5cae662009-12-17 13:57:09 +0000150out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000151 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700152 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800153 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700154 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800155 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100156
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800157 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100158
Ian Campbell65f63382009-12-01 11:47:14 +0000159out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100160#ifdef CONFIG_PREEMPT
161 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000162out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200163#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100164 shutting_down = SHUTDOWN_INVALID;
165}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200166#endif /* CONFIG_HIBERNATE_CALLBACKS */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700167
Ian Campbell55271722011-02-17 11:04:20 +0000168struct shutdown_handler {
169 const char *command;
170 void (*cb)(void);
171};
172
Konrad Rzeszutek Wilkeb47f712014-04-04 14:53:41 -0400173static int poweroff_nb(struct notifier_block *cb, unsigned long code, void *unused)
174{
175 switch (code) {
176 case SYS_DOWN:
177 case SYS_HALT:
178 case SYS_POWER_OFF:
179 shutting_down = SHUTDOWN_POWEROFF;
180 default:
181 break;
182 }
183 return NOTIFY_DONE;
184}
Ian Campbell55271722011-02-17 11:04:20 +0000185static void do_poweroff(void)
186{
Konrad Rzeszutek Wilkeb47f712014-04-04 14:53:41 -0400187 switch (system_state) {
188 case SYSTEM_BOOTING:
189 orderly_poweroff(true);
190 break;
191 case SYSTEM_RUNNING:
192 orderly_poweroff(false);
193 break;
194 default:
195 /* Don't do it when we are halting/rebooting. */
196 pr_info("Ignoring Xen toolstack shutdown.\n");
197 break;
198 }
Ian Campbell55271722011-02-17 11:04:20 +0000199}
200
201static void do_reboot(void)
202{
203 shutting_down = SHUTDOWN_POWEROFF; /* ? */
204 ctrl_alt_del();
205}
206
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700207static void shutdown_handler(struct xenbus_watch *watch,
208 const char **vec, unsigned int len)
209{
210 char *str;
211 struct xenbus_transaction xbt;
212 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000213 static struct shutdown_handler handlers[] = {
214 { "poweroff", do_poweroff },
215 { "halt", do_poweroff },
216 { "reboot", do_reboot },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200217#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000218 { "suspend", do_suspend },
219#endif
220 {NULL, NULL},
221 };
222 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700223
224 if (shutting_down != SHUTDOWN_INVALID)
225 return;
226
227 again:
228 err = xenbus_transaction_start(&xbt);
229 if (err)
230 return;
231
232 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
233 /* Ignore read errors and empty reads. */
234 if (XENBUS_IS_ERR_READ(str)) {
235 xenbus_transaction_end(xbt, 1);
236 return;
237 }
238
Ian Campbell55271722011-02-17 11:04:20 +0000239 for (handler = &handlers[0]; handler->command; handler++) {
240 if (strcmp(str, handler->command) == 0)
241 break;
242 }
243
244 /* Only acknowledge commands which we are prepared to handle. */
245 if (handler->cb)
246 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700247
248 err = xenbus_transaction_end(xbt, 0);
249 if (err == -EAGAIN) {
250 kfree(str);
251 goto again;
252 }
253
Ian Campbell55271722011-02-17 11:04:20 +0000254 if (handler->cb) {
255 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100256 } else {
Joe Perches283c0972013-06-28 03:21:41 -0700257 pr_info("Ignoring shutdown request: %s\n", str);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700258 shutting_down = SHUTDOWN_INVALID;
259 }
260
261 kfree(str);
262}
263
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700264#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700265static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
266 unsigned int len)
267{
268 char sysrq_key = '\0';
269 struct xenbus_transaction xbt;
270 int err;
271
272 again:
273 err = xenbus_transaction_start(&xbt);
274 if (err)
275 return;
276 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
Joe Perches283c0972013-06-28 03:21:41 -0700277 pr_err("Unable to read sysrq code in control/sysrq\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700278 xenbus_transaction_end(xbt, 1);
279 return;
280 }
281
282 if (sysrq_key != '\0')
283 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
284
285 err = xenbus_transaction_end(xbt, 0);
286 if (err == -EAGAIN)
287 goto again;
288
289 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700290 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700291}
292
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700293static struct xenbus_watch sysrq_watch = {
294 .node = "control/sysrq",
295 .callback = sysrq_handler
296};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700297#endif
298
299static struct xenbus_watch shutdown_watch = {
300 .node = "control/shutdown",
301 .callback = shutdown_handler
302};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700303
Konrad Rzeszutek Wilkeb47f712014-04-04 14:53:41 -0400304static struct notifier_block xen_reboot_nb = {
305 .notifier_call = poweroff_nb,
306};
307
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700308static int setup_shutdown_watcher(void)
309{
310 int err;
311
312 err = register_xenbus_watch(&shutdown_watch);
313 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700314 pr_err("Failed to set shutdown watcher\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700315 return err;
316 }
317
Konrad Rzeszutek Wilkeb47f712014-04-04 14:53:41 -0400318
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700319#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700320 err = register_xenbus_watch(&sysrq_watch);
321 if (err) {
Joe Perches283c0972013-06-28 03:21:41 -0700322 pr_err("Failed to set sysrq watcher\n");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700323 return err;
324 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700325#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700326
327 return 0;
328}
329
330static int shutdown_event(struct notifier_block *notifier,
331 unsigned long event,
332 void *data)
333{
334 setup_shutdown_watcher();
335 return NOTIFY_DONE;
336}
337
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100338int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700339{
340 static struct notifier_block xenstore_notifier = {
341 .notifier_call = shutdown_event
342 };
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000343
344 if (!xen_domain())
345 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700346 register_xenstore_notifier(&xenstore_notifier);
Konrad Rzeszutek Wilkeb47f712014-04-04 14:53:41 -0400347 register_reboot_notifier(&xen_reboot_nb);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700348
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);