blob: 1ac94125bf93c38bb98342828490e9633a12c75f [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 Campbell55fb4ac2011-02-17 11:04:20 +000040 void (*pre)(void);
41 void (*post)(int cancelled);
Ian Campbellceb18022011-02-17 11:04:20 +000042};
43
Ian Campbell07af3812011-02-17 11:04:20 +000044static void xen_hvm_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000045{
Ian Campbell07af3812011-02-17 11:04:20 +000046 xen_arch_hvm_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000047 gnttab_resume();
48}
49
50static void xen_pre_suspend(void)
51{
52 xen_mm_pin_all();
53 gnttab_suspend();
Ian Campbell07af3812011-02-17 11:04:20 +000054 xen_arch_pre_suspend();
Ian Campbell82043bb2011-02-17 11:04:20 +000055}
56
Ian Campbell07af3812011-02-17 11:04:20 +000057static void xen_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000058{
Ian Campbell07af3812011-02-17 11:04:20 +000059 xen_arch_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000060 gnttab_resume();
61 xen_mm_unpin_all();
62}
63
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +020064#ifdef CONFIG_HIBERNATE_CALLBACKS
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010065static int xen_suspend(void *data)
66{
Ian Campbellceb18022011-02-17 11:04:20 +000067 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010068 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010069
70 BUG_ON(!irqs_disabled());
71
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -080072 err = sysdev_suspend(PMSG_FREEZE);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010073 if (err) {
74 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
75 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010076 return err;
77 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010078
Ian Campbell55fb4ac2011-02-17 11:04:20 +000079 if (si->pre)
80 si->pre();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010081
82 /*
83 * This hypercall returns 1 if suspend was cancelled
84 * or the domain was merely checkpointed, and 0 if it
85 * is resuming in a new domain.
86 */
Ian Campbell36b401e2011-02-17 11:04:20 +000087 si->cancelled = HYPERVISOR_suspend(si->arg);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010088
Ian Campbell55fb4ac2011-02-17 11:04:20 +000089 if (si->post)
90 si->post(si->cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010091
Ian Campbellceb18022011-02-17 11:04:20 +000092 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010093 xen_irq_resume();
94 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070095 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010096 }
97
Ian Campbell1e6fcf82009-03-25 17:46:42 +000098 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +000099
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100100 return 0;
101}
102
103static void do_suspend(void)
104{
105 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000106 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100107
108 shutting_down = SHUTDOWN_SUSPEND;
109
110#ifdef CONFIG_PREEMPT
111 /* If the kernel is preemptible, we need to freeze all the processes
112 to prevent them from being in the middle of a pagetable update
113 during suspend. */
114 err = freeze_processes();
115 if (err) {
116 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200117 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100118 }
119#endif
120
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800121 err = dpm_suspend_start(PMSG_FREEZE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100122 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200123 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000124 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100125 }
126
Ian Campbellc5cae662009-12-17 13:57:09 +0000127 printk(KERN_DEBUG "suspending xenstore...\n");
128 xs_suspend();
129
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800130 err = dpm_suspend_noirq(PMSG_FREEZE);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100131 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200132 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000133 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100134 }
135
Ian Campbellceb18022011-02-17 11:04:20 +0000136 si.cancelled = 1;
137
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000138 if (xen_hvm_domain()) {
Ian Campbell36b401e2011-02-17 11:04:20 +0000139 si.arg = 0UL;
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000140 si.pre = NULL;
141 si.post = &xen_hvm_post_suspend;
142 } else {
Ian Campbell36b401e2011-02-17 11:04:20 +0000143 si.arg = virt_to_mfn(xen_start_info);
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000144 si.pre = &xen_pre_suspend;
145 si.post = &xen_post_suspend;
146 }
Ian Campbell36b401e2011-02-17 11:04:20 +0000147
Ian Campbellb056b6a2011-02-17 11:04:20 +0000148 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800149
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800150 dpm_resume_noirq(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800151
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100152 if (err) {
153 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000154 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100155 }
156
Ian Campbellc5cae662009-12-17 13:57:09 +0000157out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000158 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700159 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800160 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700161 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800162 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100163
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800164 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100165
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100166 /* Make sure timer events get retriggered on all CPUs */
167 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000168
169out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100170#ifdef CONFIG_PREEMPT
171 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000172out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200173#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100174 shutting_down = SHUTDOWN_INVALID;
175}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200176#endif /* CONFIG_HIBERNATE_CALLBACKS */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700177
Ian Campbell55271722011-02-17 11:04:20 +0000178struct shutdown_handler {
179 const char *command;
180 void (*cb)(void);
181};
182
183static void do_poweroff(void)
184{
185 shutting_down = SHUTDOWN_POWEROFF;
186 orderly_poweroff(false);
187}
188
189static void do_reboot(void)
190{
191 shutting_down = SHUTDOWN_POWEROFF; /* ? */
192 ctrl_alt_del();
193}
194
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700195static void shutdown_handler(struct xenbus_watch *watch,
196 const char **vec, unsigned int len)
197{
198 char *str;
199 struct xenbus_transaction xbt;
200 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000201 static struct shutdown_handler handlers[] = {
202 { "poweroff", do_poweroff },
203 { "halt", do_poweroff },
204 { "reboot", do_reboot },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200205#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000206 { "suspend", do_suspend },
207#endif
208 {NULL, NULL},
209 };
210 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700211
212 if (shutting_down != SHUTDOWN_INVALID)
213 return;
214
215 again:
216 err = xenbus_transaction_start(&xbt);
217 if (err)
218 return;
219
220 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
221 /* Ignore read errors and empty reads. */
222 if (XENBUS_IS_ERR_READ(str)) {
223 xenbus_transaction_end(xbt, 1);
224 return;
225 }
226
Ian Campbell55271722011-02-17 11:04:20 +0000227 for (handler = &handlers[0]; handler->command; handler++) {
228 if (strcmp(str, handler->command) == 0)
229 break;
230 }
231
232 /* Only acknowledge commands which we are prepared to handle. */
233 if (handler->cb)
234 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700235
236 err = xenbus_transaction_end(xbt, 0);
237 if (err == -EAGAIN) {
238 kfree(str);
239 goto again;
240 }
241
Ian Campbell55271722011-02-17 11:04:20 +0000242 if (handler->cb) {
243 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100244 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700245 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
246 shutting_down = SHUTDOWN_INVALID;
247 }
248
249 kfree(str);
250}
251
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700252#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700253static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
254 unsigned int len)
255{
256 char sysrq_key = '\0';
257 struct xenbus_transaction xbt;
258 int err;
259
260 again:
261 err = xenbus_transaction_start(&xbt);
262 if (err)
263 return;
264 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
265 printk(KERN_ERR "Unable to read sysrq code in "
266 "control/sysrq\n");
267 xenbus_transaction_end(xbt, 1);
268 return;
269 }
270
271 if (sysrq_key != '\0')
272 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
273
274 err = xenbus_transaction_end(xbt, 0);
275 if (err == -EAGAIN)
276 goto again;
277
278 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700279 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700280}
281
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700282static struct xenbus_watch sysrq_watch = {
283 .node = "control/sysrq",
284 .callback = sysrq_handler
285};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700286#endif
287
288static struct xenbus_watch shutdown_watch = {
289 .node = "control/shutdown",
290 .callback = shutdown_handler
291};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700292
293static int setup_shutdown_watcher(void)
294{
295 int err;
296
297 err = register_xenbus_watch(&shutdown_watch);
298 if (err) {
299 printk(KERN_ERR "Failed to set shutdown watcher\n");
300 return err;
301 }
302
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700303#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700304 err = register_xenbus_watch(&sysrq_watch);
305 if (err) {
306 printk(KERN_ERR "Failed to set sysrq watcher\n");
307 return err;
308 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700309#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700310
311 return 0;
312}
313
314static int shutdown_event(struct notifier_block *notifier,
315 unsigned long event,
316 void *data)
317{
318 setup_shutdown_watcher();
319 return NOTIFY_DONE;
320}
321
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100322int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700323{
324 static struct notifier_block xenstore_notifier = {
325 .notifier_call = shutdown_event
326 };
Stefano Stabellini702d4eb2010-12-02 17:54:50 +0000327
328 if (!xen_domain())
329 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700330 register_xenstore_notifier(&xenstore_notifier);
331
332 return 0;
333}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100334EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700335
Stefano Stabellini702d4eb2010-12-02 17:54:50 +0000336subsys_initcall(xen_setup_shutdown_event);