blob: 5c0184fb9d84347d3d656422a6e6787823a249b8 [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;
39};
40
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010041#ifdef CONFIG_PM_SLEEP
Stefano Stabellini016b6f52010-05-14 12:45:07 +010042static int xen_hvm_suspend(void *data)
43{
Ian Campbellceb18022011-02-17 11:04:20 +000044 struct suspend_info *si = data;
Ian Campbell8dd38382011-02-17 10:31:20 +000045 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010046
47 BUG_ON(!irqs_disabled());
48
Ian Campbell8dd38382011-02-17 10:31:20 +000049 err = sysdev_suspend(PMSG_SUSPEND);
50 if (err) {
51 printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
52 err);
53 return err;
54 }
55
Ian Campbellbd1c0ad2011-02-17 11:04:20 +000056 /*
57 * This hypercall returns 1 if suspend was cancelled
58 * or the domain was merely checkpointed, and 0 if it
59 * is resuming in a new domain.
60 */
Ian Campbellceb18022011-02-17 11:04:20 +000061 si->cancelled = HYPERVISOR_suspend(0UL);
Stefano Stabellini016b6f52010-05-14 12:45:07 +010062
Ian Campbellceb18022011-02-17 11:04:20 +000063 xen_hvm_post_suspend(si->cancelled);
Stefano Stabellini016b6f52010-05-14 12:45:07 +010064 gnttab_resume();
65
Ian Campbellceb18022011-02-17 11:04:20 +000066 if (!si->cancelled) {
Stefano Stabellini016b6f52010-05-14 12:45:07 +010067 xen_irq_resume();
Stefano Stabellini6411fe692010-12-01 14:51:44 +000068 xen_console_resume();
Stefano Stabellini016b6f52010-05-14 12:45:07 +010069 xen_timer_resume();
70 }
71
Ian Campbell8dd38382011-02-17 10:31:20 +000072 sysdev_resume();
73
Stefano Stabellini016b6f52010-05-14 12:45:07 +010074 return 0;
75}
76
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010077static int xen_suspend(void *data)
78{
Ian Campbellceb18022011-02-17 11:04:20 +000079 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010080 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010081
82 BUG_ON(!irqs_disabled());
83
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010084 err = sysdev_suspend(PMSG_SUSPEND);
85 if (err) {
86 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
87 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010088 return err;
89 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010090
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010091 xen_mm_pin_all();
92 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010093 xen_pre_suspend();
94
95 /*
96 * This hypercall returns 1 if suspend was cancelled
97 * or the domain was merely checkpointed, and 0 if it
98 * is resuming in a new domain.
99 */
Ian Campbellceb18022011-02-17 11:04:20 +0000100 si->cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100101
Ian Campbellceb18022011-02-17 11:04:20 +0000102 xen_post_suspend(si->cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100103 gnttab_resume();
104 xen_mm_unpin_all();
105
Ian Campbellceb18022011-02-17 11:04:20 +0000106 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100107 xen_irq_resume();
108 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700109 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100110 }
111
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000112 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000113
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100114 return 0;
115}
116
117static void do_suspend(void)
118{
119 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000120 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100121
122 shutting_down = SHUTDOWN_SUSPEND;
123
124#ifdef CONFIG_PREEMPT
125 /* If the kernel is preemptible, we need to freeze all the processes
126 to prevent them from being in the middle of a pagetable update
127 during suspend. */
128 err = freeze_processes();
129 if (err) {
130 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200131 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100132 }
133#endif
134
Alan Sternd1616302009-05-24 22:05:42 +0200135 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100136 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200137 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000138 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100139 }
140
Ian Campbellc5cae662009-12-17 13:57:09 +0000141 printk(KERN_DEBUG "suspending xenstore...\n");
142 xs_suspend();
143
Alan Sternd1616302009-05-24 22:05:42 +0200144 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100145 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200146 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000147 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100148 }
149
Ian Campbellceb18022011-02-17 11:04:20 +0000150 si.cancelled = 1;
151
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100152 if (xen_hvm_domain())
Ian Campbellceb18022011-02-17 11:04:20 +0000153 err = stop_machine(xen_hvm_suspend, &si, cpumask_of(0));
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100154 else
Ian Campbellceb18022011-02-17 11:04:20 +0000155 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800156
157 dpm_resume_noirq(PMSG_RESUME);
158
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100159 if (err) {
160 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000161 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100162 }
163
Ian Campbellc5cae662009-12-17 13:57:09 +0000164out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000165 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700166 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800167 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700168 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800169 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100170
Alan Sternd1616302009-05-24 22:05:42 +0200171 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100172
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100173 /* Make sure timer events get retriggered on all CPUs */
174 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000175
176out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100177#ifdef CONFIG_PREEMPT
178 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000179out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200180#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100181 shutting_down = SHUTDOWN_INVALID;
182}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100183#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700184
Ian Campbell55271722011-02-17 11:04:20 +0000185struct shutdown_handler {
186 const char *command;
187 void (*cb)(void);
188};
189
190static void do_poweroff(void)
191{
192 shutting_down = SHUTDOWN_POWEROFF;
193 orderly_poweroff(false);
194}
195
196static void do_reboot(void)
197{
198 shutting_down = SHUTDOWN_POWEROFF; /* ? */
199 ctrl_alt_del();
200}
201
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700202static void shutdown_handler(struct xenbus_watch *watch,
203 const char **vec, unsigned int len)
204{
205 char *str;
206 struct xenbus_transaction xbt;
207 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000208 static struct shutdown_handler handlers[] = {
209 { "poweroff", do_poweroff },
210 { "halt", do_poweroff },
211 { "reboot", do_reboot },
212#ifdef CONFIG_PM_SLEEP
213 { "suspend", do_suspend },
214#endif
215 {NULL, NULL},
216 };
217 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700218
219 if (shutting_down != SHUTDOWN_INVALID)
220 return;
221
222 again:
223 err = xenbus_transaction_start(&xbt);
224 if (err)
225 return;
226
227 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
228 /* Ignore read errors and empty reads. */
229 if (XENBUS_IS_ERR_READ(str)) {
230 xenbus_transaction_end(xbt, 1);
231 return;
232 }
233
Ian Campbell55271722011-02-17 11:04:20 +0000234 for (handler = &handlers[0]; handler->command; handler++) {
235 if (strcmp(str, handler->command) == 0)
236 break;
237 }
238
239 /* Only acknowledge commands which we are prepared to handle. */
240 if (handler->cb)
241 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700242
243 err = xenbus_transaction_end(xbt, 0);
244 if (err == -EAGAIN) {
245 kfree(str);
246 goto again;
247 }
248
Ian Campbell55271722011-02-17 11:04:20 +0000249 if (handler->cb) {
250 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100251 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700252 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
253 shutting_down = SHUTDOWN_INVALID;
254 }
255
256 kfree(str);
257}
258
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700259#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700260static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
261 unsigned int len)
262{
263 char sysrq_key = '\0';
264 struct xenbus_transaction xbt;
265 int err;
266
267 again:
268 err = xenbus_transaction_start(&xbt);
269 if (err)
270 return;
271 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
272 printk(KERN_ERR "Unable to read sysrq code in "
273 "control/sysrq\n");
274 xenbus_transaction_end(xbt, 1);
275 return;
276 }
277
278 if (sysrq_key != '\0')
279 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
280
281 err = xenbus_transaction_end(xbt, 0);
282 if (err == -EAGAIN)
283 goto again;
284
285 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700286 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700287}
288
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700289static struct xenbus_watch sysrq_watch = {
290 .node = "control/sysrq",
291 .callback = sysrq_handler
292};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700293#endif
294
295static struct xenbus_watch shutdown_watch = {
296 .node = "control/shutdown",
297 .callback = shutdown_handler
298};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700299
300static int setup_shutdown_watcher(void)
301{
302 int err;
303
304 err = register_xenbus_watch(&shutdown_watch);
305 if (err) {
306 printk(KERN_ERR "Failed to set shutdown watcher\n");
307 return err;
308 }
309
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700310#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700311 err = register_xenbus_watch(&sysrq_watch);
312 if (err) {
313 printk(KERN_ERR "Failed to set sysrq watcher\n");
314 return err;
315 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700316#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700317
318 return 0;
319}
320
321static int shutdown_event(struct notifier_block *notifier,
322 unsigned long event,
323 void *data)
324{
325 setup_shutdown_watcher();
326 return NOTIFY_DONE;
327}
328
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100329int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700330{
331 static struct notifier_block xenstore_notifier = {
332 .notifier_call = shutdown_event
333 };
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000334
335 if (!xen_domain())
336 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700337 register_xenstore_notifier(&xenstore_notifier);
338
339 return 0;
340}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100341EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700342
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000343subsys_initcall(xen_setup_shutdown_event);