blob: a2eee574784e801aec6463a8317267c24f928201 [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>
Rafael J. Wysocki19234c02011-04-20 00:36:11 +020011#include <linux/syscore_ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070012
Stefano Stabellini016b6f52010-05-14 12:45:07 +010013#include <xen/xen.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070014#include <xen/xenbus.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010015#include <xen/grant_table.h>
16#include <xen/events.h>
17#include <xen/hvc-console.h>
18#include <xen/xen-ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070019
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010020#include <asm/xen/hypercall.h>
21#include <asm/xen/page.h>
Stefano Stabellini016b6f52010-05-14 12:45:07 +010022#include <asm/xen/hypervisor.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010023
24enum shutdown_state {
25 SHUTDOWN_INVALID = -1,
26 SHUTDOWN_POWEROFF = 0,
27 SHUTDOWN_SUSPEND = 2,
28 /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
29 report a crash, not be instructed to crash!
30 HALT is the same as POWEROFF, as far as we're concerned. The tools use
31 the distinction when we return the reason code to them. */
32 SHUTDOWN_HALT = 4,
33};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070034
35/* Ignore multiple shutdown requests. */
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010036static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
37
Ian Campbellceb18022011-02-17 11:04:20 +000038struct suspend_info {
39 int cancelled;
Ian Campbell36b401e2011-02-17 11:04:20 +000040 unsigned long arg; /* extra hypercall argument */
Ian Campbell55fb4ac2011-02-17 11:04:20 +000041 void (*pre)(void);
42 void (*post)(int cancelled);
Ian Campbellceb18022011-02-17 11:04:20 +000043};
44
Ian Campbell07af3812011-02-17 11:04:20 +000045static void xen_hvm_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000046{
Ian Campbell07af3812011-02-17 11:04:20 +000047 xen_arch_hvm_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000048 gnttab_resume();
49}
50
51static void xen_pre_suspend(void)
52{
53 xen_mm_pin_all();
54 gnttab_suspend();
Ian Campbell07af3812011-02-17 11:04:20 +000055 xen_arch_pre_suspend();
Ian Campbell82043bb2011-02-17 11:04:20 +000056}
57
Ian Campbell07af3812011-02-17 11:04:20 +000058static void xen_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000059{
Ian Campbell07af3812011-02-17 11:04:20 +000060 xen_arch_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000061 gnttab_resume();
62 xen_mm_unpin_all();
63}
64
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +020065#ifdef CONFIG_HIBERNATE_CALLBACKS
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010066static int xen_suspend(void *data)
67{
Ian Campbellceb18022011-02-17 11:04:20 +000068 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010069 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010070
71 BUG_ON(!irqs_disabled());
72
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -080073 err = sysdev_suspend(PMSG_FREEZE);
Rafael J. Wysocki19234c02011-04-20 00:36:11 +020074 if (!err) {
75 err = syscore_suspend();
76 if (err)
77 sysdev_resume();
78 }
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010079 if (err) {
Rafael J. Wysocki19234c02011-04-20 00:36:11 +020080 printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n",
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010081 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010082 return err;
83 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010084
Ian Campbell55fb4ac2011-02-17 11:04:20 +000085 if (si->pre)
86 si->pre();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010087
88 /*
89 * This hypercall returns 1 if suspend was cancelled
90 * or the domain was merely checkpointed, and 0 if it
91 * is resuming in a new domain.
92 */
Ian Campbell36b401e2011-02-17 11:04:20 +000093 si->cancelled = HYPERVISOR_suspend(si->arg);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010094
Ian Campbell55fb4ac2011-02-17 11:04:20 +000095 if (si->post)
96 si->post(si->cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010097
Ian Campbellceb18022011-02-17 11:04:20 +000098 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010099 xen_irq_resume();
100 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700101 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100102 }
103
Rafael J. Wysocki19234c02011-04-20 00:36:11 +0200104 syscore_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000105 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000106
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100107 return 0;
108}
109
110static void do_suspend(void)
111{
112 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000113 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100114
115 shutting_down = SHUTDOWN_SUSPEND;
116
117#ifdef CONFIG_PREEMPT
118 /* If the kernel is preemptible, we need to freeze all the processes
119 to prevent them from being in the middle of a pagetable update
120 during suspend. */
121 err = freeze_processes();
122 if (err) {
123 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200124 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100125 }
126#endif
127
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800128 err = dpm_suspend_start(PMSG_FREEZE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100129 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200130 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000131 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100132 }
133
Ian Campbellc5cae662009-12-17 13:57:09 +0000134 printk(KERN_DEBUG "suspending xenstore...\n");
135 xs_suspend();
136
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800137 err = dpm_suspend_noirq(PMSG_FREEZE);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100138 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200139 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000140 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100141 }
142
Ian Campbellceb18022011-02-17 11:04:20 +0000143 si.cancelled = 1;
144
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000145 if (xen_hvm_domain()) {
Ian Campbell36b401e2011-02-17 11:04:20 +0000146 si.arg = 0UL;
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000147 si.pre = NULL;
148 si.post = &xen_hvm_post_suspend;
149 } else {
Ian Campbell36b401e2011-02-17 11:04:20 +0000150 si.arg = virt_to_mfn(xen_start_info);
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000151 si.pre = &xen_pre_suspend;
152 si.post = &xen_post_suspend;
153 }
Ian Campbell36b401e2011-02-17 11:04:20 +0000154
Ian Campbellb056b6a2011-02-17 11:04:20 +0000155 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800156
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800157 dpm_resume_noirq(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800158
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
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800171 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
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}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200183#endif /* CONFIG_HIBERNATE_CALLBACKS */
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 },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200212#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000213 { "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);