blob: 6ce6b91e7645954fb9ccb574adf65478928cc6d5 [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 Campbellceb18022011-02-17 11:04:20 +000040};
41
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010042#ifdef CONFIG_PM_SLEEP
Stefano Stabellini016b6f52010-05-14 12:45:07 +010043static int xen_hvm_suspend(void *data)
44{
Ian Campbellceb18022011-02-17 11:04:20 +000045 struct suspend_info *si = data;
Ian Campbell8dd38382011-02-17 10:31:20 +000046 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010047
48 BUG_ON(!irqs_disabled());
49
Ian Campbell8dd38382011-02-17 10:31:20 +000050 err = sysdev_suspend(PMSG_SUSPEND);
51 if (err) {
52 printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
53 err);
54 return err;
55 }
56
Ian Campbellbd1c0ad2011-02-17 11:04:20 +000057 /*
58 * This hypercall returns 1 if suspend was cancelled
59 * or the domain was merely checkpointed, and 0 if it
60 * is resuming in a new domain.
61 */
Ian Campbell36b401e2011-02-17 11:04:20 +000062 si->cancelled = HYPERVISOR_suspend(si->arg);
Stefano Stabellini016b6f52010-05-14 12:45:07 +010063
Ian Campbellceb18022011-02-17 11:04:20 +000064 xen_hvm_post_suspend(si->cancelled);
Stefano Stabellini016b6f52010-05-14 12:45:07 +010065 gnttab_resume();
66
Ian Campbellceb18022011-02-17 11:04:20 +000067 if (!si->cancelled) {
Stefano Stabellini016b6f52010-05-14 12:45:07 +010068 xen_irq_resume();
Stefano Stabellini6411fe692010-12-01 14:51:44 +000069 xen_console_resume();
Stefano Stabellini016b6f52010-05-14 12:45:07 +010070 xen_timer_resume();
71 }
72
Ian Campbell8dd38382011-02-17 10:31:20 +000073 sysdev_resume();
74
Stefano Stabellini016b6f52010-05-14 12:45:07 +010075 return 0;
76}
77
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010078static int xen_suspend(void *data)
79{
Ian Campbellceb18022011-02-17 11:04:20 +000080 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010081 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010082
83 BUG_ON(!irqs_disabled());
84
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010085 err = sysdev_suspend(PMSG_SUSPEND);
86 if (err) {
87 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
88 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010089 return err;
90 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010091
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010092 xen_mm_pin_all();
93 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010094 xen_pre_suspend();
95
96 /*
97 * This hypercall returns 1 if suspend was cancelled
98 * or the domain was merely checkpointed, and 0 if it
99 * is resuming in a new domain.
100 */
Ian Campbell36b401e2011-02-17 11:04:20 +0000101 si->cancelled = HYPERVISOR_suspend(si->arg);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100102
Ian Campbellceb18022011-02-17 11:04:20 +0000103 xen_post_suspend(si->cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100104 gnttab_resume();
105 xen_mm_unpin_all();
106
Ian Campbellceb18022011-02-17 11:04:20 +0000107 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100108 xen_irq_resume();
109 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700110 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100111 }
112
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000113 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000114
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100115 return 0;
116}
117
118static void do_suspend(void)
119{
120 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000121 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100122
123 shutting_down = SHUTDOWN_SUSPEND;
124
125#ifdef CONFIG_PREEMPT
126 /* If the kernel is preemptible, we need to freeze all the processes
127 to prevent them from being in the middle of a pagetable update
128 during suspend. */
129 err = freeze_processes();
130 if (err) {
131 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200132 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100133 }
134#endif
135
Alan Sternd1616302009-05-24 22:05:42 +0200136 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100137 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200138 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000139 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100140 }
141
Ian Campbellc5cae662009-12-17 13:57:09 +0000142 printk(KERN_DEBUG "suspending xenstore...\n");
143 xs_suspend();
144
Alan Sternd1616302009-05-24 22:05:42 +0200145 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100146 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200147 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000148 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100149 }
150
Ian Campbellceb18022011-02-17 11:04:20 +0000151 si.cancelled = 1;
152
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100153 if (xen_hvm_domain())
Ian Campbell36b401e2011-02-17 11:04:20 +0000154 si.arg = 0UL;
155 else
156 si.arg = virt_to_mfn(xen_start_info);
157
158 if (xen_hvm_domain())
Ian Campbellceb18022011-02-17 11:04:20 +0000159 err = stop_machine(xen_hvm_suspend, &si, cpumask_of(0));
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100160 else
Ian Campbellceb18022011-02-17 11:04:20 +0000161 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800162
163 dpm_resume_noirq(PMSG_RESUME);
164
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100165 if (err) {
166 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000167 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100168 }
169
Ian Campbellc5cae662009-12-17 13:57:09 +0000170out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000171 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700172 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800173 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700174 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800175 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100176
Alan Sternd1616302009-05-24 22:05:42 +0200177 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100178
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100179 /* Make sure timer events get retriggered on all CPUs */
180 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000181
182out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100183#ifdef CONFIG_PREEMPT
184 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000185out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200186#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100187 shutting_down = SHUTDOWN_INVALID;
188}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100189#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700190
Ian Campbell55271722011-02-17 11:04:20 +0000191struct shutdown_handler {
192 const char *command;
193 void (*cb)(void);
194};
195
196static void do_poweroff(void)
197{
198 shutting_down = SHUTDOWN_POWEROFF;
199 orderly_poweroff(false);
200}
201
202static void do_reboot(void)
203{
204 shutting_down = SHUTDOWN_POWEROFF; /* ? */
205 ctrl_alt_del();
206}
207
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700208static void shutdown_handler(struct xenbus_watch *watch,
209 const char **vec, unsigned int len)
210{
211 char *str;
212 struct xenbus_transaction xbt;
213 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000214 static struct shutdown_handler handlers[] = {
215 { "poweroff", do_poweroff },
216 { "halt", do_poweroff },
217 { "reboot", do_reboot },
218#ifdef CONFIG_PM_SLEEP
219 { "suspend", do_suspend },
220#endif
221 {NULL, NULL},
222 };
223 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700224
225 if (shutting_down != SHUTDOWN_INVALID)
226 return;
227
228 again:
229 err = xenbus_transaction_start(&xbt);
230 if (err)
231 return;
232
233 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
234 /* Ignore read errors and empty reads. */
235 if (XENBUS_IS_ERR_READ(str)) {
236 xenbus_transaction_end(xbt, 1);
237 return;
238 }
239
Ian Campbell55271722011-02-17 11:04:20 +0000240 for (handler = &handlers[0]; handler->command; handler++) {
241 if (strcmp(str, handler->command) == 0)
242 break;
243 }
244
245 /* Only acknowledge commands which we are prepared to handle. */
246 if (handler->cb)
247 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700248
249 err = xenbus_transaction_end(xbt, 0);
250 if (err == -EAGAIN) {
251 kfree(str);
252 goto again;
253 }
254
Ian Campbell55271722011-02-17 11:04:20 +0000255 if (handler->cb) {
256 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100257 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700258 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
259 shutting_down = SHUTDOWN_INVALID;
260 }
261
262 kfree(str);
263}
264
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700265#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700266static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
267 unsigned int len)
268{
269 char sysrq_key = '\0';
270 struct xenbus_transaction xbt;
271 int err;
272
273 again:
274 err = xenbus_transaction_start(&xbt);
275 if (err)
276 return;
277 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
278 printk(KERN_ERR "Unable to read sysrq code in "
279 "control/sysrq\n");
280 xenbus_transaction_end(xbt, 1);
281 return;
282 }
283
284 if (sysrq_key != '\0')
285 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
286
287 err = xenbus_transaction_end(xbt, 0);
288 if (err == -EAGAIN)
289 goto again;
290
291 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700292 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700293}
294
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700295static struct xenbus_watch sysrq_watch = {
296 .node = "control/sysrq",
297 .callback = sysrq_handler
298};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700299#endif
300
301static struct xenbus_watch shutdown_watch = {
302 .node = "control/shutdown",
303 .callback = shutdown_handler
304};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700305
306static int setup_shutdown_watcher(void)
307{
308 int err;
309
310 err = register_xenbus_watch(&shutdown_watch);
311 if (err) {
312 printk(KERN_ERR "Failed to set shutdown watcher\n");
313 return err;
314 }
315
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700316#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700317 err = register_xenbus_watch(&sysrq_watch);
318 if (err) {
319 printk(KERN_ERR "Failed to set sysrq watcher\n");
320 return err;
321 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700322#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700323
324 return 0;
325}
326
327static int shutdown_event(struct notifier_block *notifier,
328 unsigned long event,
329 void *data)
330{
331 setup_shutdown_watcher();
332 return NOTIFY_DONE;
333}
334
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100335int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700336{
337 static struct notifier_block xenstore_notifier = {
338 .notifier_call = shutdown_event
339 };
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000340
341 if (!xen_domain())
342 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700343 register_xenstore_notifier(&xenstore_notifier);
344
345 return 0;
346}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100347EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700348
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000349subsys_initcall(xen_setup_shutdown_event);