blob: 0b5366b5be2017dc90aab1c6ed869908556fd954 [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
Rafael J. Wysocki2e711c02011-04-26 19:15:07 +020073 err = syscore_suspend();
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010074 if (err) {
Rafael J. Wysocki19234c02011-04-20 00:36:11 +020075 printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n",
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010076 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010077 return err;
78 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010079
Ian Campbell55fb4ac2011-02-17 11:04:20 +000080 if (si->pre)
81 si->pre();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010082
83 /*
84 * This hypercall returns 1 if suspend was cancelled
85 * or the domain was merely checkpointed, and 0 if it
86 * is resuming in a new domain.
87 */
Ian Campbell36b401e2011-02-17 11:04:20 +000088 si->cancelled = HYPERVISOR_suspend(si->arg);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010089
Ian Campbell55fb4ac2011-02-17 11:04:20 +000090 if (si->post)
91 si->post(si->cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010092
Ian Campbellceb18022011-02-17 11:04:20 +000093 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010094 xen_irq_resume();
95 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070096 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010097 }
98
Rafael J. Wysocki19234c02011-04-20 00:36:11 +020099 syscore_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000100
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100101 return 0;
102}
103
104static void do_suspend(void)
105{
106 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000107 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100108
109 shutting_down = SHUTDOWN_SUSPEND;
110
111#ifdef CONFIG_PREEMPT
112 /* If the kernel is preemptible, we need to freeze all the processes
113 to prevent them from being in the middle of a pagetable update
114 during suspend. */
115 err = freeze_processes();
116 if (err) {
117 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200118 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100119 }
120#endif
121
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800122 err = dpm_suspend_start(PMSG_FREEZE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100123 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200124 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000125 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100126 }
127
Ian Campbellc5cae662009-12-17 13:57:09 +0000128 printk(KERN_DEBUG "suspending xenstore...\n");
129 xs_suspend();
130
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800131 err = dpm_suspend_noirq(PMSG_FREEZE);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100132 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200133 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000134 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100135 }
136
Ian Campbellceb18022011-02-17 11:04:20 +0000137 si.cancelled = 1;
138
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000139 if (xen_hvm_domain()) {
Ian Campbell36b401e2011-02-17 11:04:20 +0000140 si.arg = 0UL;
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000141 si.pre = NULL;
142 si.post = &xen_hvm_post_suspend;
143 } else {
Ian Campbell36b401e2011-02-17 11:04:20 +0000144 si.arg = virt_to_mfn(xen_start_info);
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000145 si.pre = &xen_pre_suspend;
146 si.post = &xen_post_suspend;
147 }
Ian Campbell36b401e2011-02-17 11:04:20 +0000148
Ian Campbellb056b6a2011-02-17 11:04:20 +0000149 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800150
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800151 dpm_resume_noirq(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800152
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100153 if (err) {
154 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000155 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100156 }
157
Ian Campbellc5cae662009-12-17 13:57:09 +0000158out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000159 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700160 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800161 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700162 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800163 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100164
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800165 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100166
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100167 /* Make sure timer events get retriggered on all CPUs */
168 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000169
170out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100171#ifdef CONFIG_PREEMPT
172 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000173out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200174#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100175 shutting_down = SHUTDOWN_INVALID;
176}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200177#endif /* CONFIG_HIBERNATE_CALLBACKS */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700178
Ian Campbell55271722011-02-17 11:04:20 +0000179struct shutdown_handler {
180 const char *command;
181 void (*cb)(void);
182};
183
184static void do_poweroff(void)
185{
186 shutting_down = SHUTDOWN_POWEROFF;
187 orderly_poweroff(false);
188}
189
190static void do_reboot(void)
191{
192 shutting_down = SHUTDOWN_POWEROFF; /* ? */
193 ctrl_alt_del();
194}
195
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700196static void shutdown_handler(struct xenbus_watch *watch,
197 const char **vec, unsigned int len)
198{
199 char *str;
200 struct xenbus_transaction xbt;
201 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000202 static struct shutdown_handler handlers[] = {
203 { "poweroff", do_poweroff },
204 { "halt", do_poweroff },
205 { "reboot", do_reboot },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200206#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000207 { "suspend", do_suspend },
208#endif
209 {NULL, NULL},
210 };
211 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700212
213 if (shutting_down != SHUTDOWN_INVALID)
214 return;
215
216 again:
217 err = xenbus_transaction_start(&xbt);
218 if (err)
219 return;
220
221 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
222 /* Ignore read errors and empty reads. */
223 if (XENBUS_IS_ERR_READ(str)) {
224 xenbus_transaction_end(xbt, 1);
225 return;
226 }
227
Ian Campbell55271722011-02-17 11:04:20 +0000228 for (handler = &handlers[0]; handler->command; handler++) {
229 if (strcmp(str, handler->command) == 0)
230 break;
231 }
232
233 /* Only acknowledge commands which we are prepared to handle. */
234 if (handler->cb)
235 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700236
237 err = xenbus_transaction_end(xbt, 0);
238 if (err == -EAGAIN) {
239 kfree(str);
240 goto again;
241 }
242
Ian Campbell55271722011-02-17 11:04:20 +0000243 if (handler->cb) {
244 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100245 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700246 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
247 shutting_down = SHUTDOWN_INVALID;
248 }
249
250 kfree(str);
251}
252
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700253#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700254static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
255 unsigned int len)
256{
257 char sysrq_key = '\0';
258 struct xenbus_transaction xbt;
259 int err;
260
261 again:
262 err = xenbus_transaction_start(&xbt);
263 if (err)
264 return;
265 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
266 printk(KERN_ERR "Unable to read sysrq code in "
267 "control/sysrq\n");
268 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) {
300 printk(KERN_ERR "Failed to set shutdown watcher\n");
301 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) {
307 printk(KERN_ERR "Failed to set sysrq watcher\n");
308 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 Stabellini702d4eb2010-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 Stabellini702d4eb2010-12-02 17:54:50 +0000337subsys_initcall(xen_setup_shutdown_event);