blob: 33312c09829e00f8432f24c8770d6c7d969596cf [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
Ian Campbell82043bb2011-02-17 11:04:20 +000042static void xen_hvm_post_suspend(void)
43{
44 gnttab_resume();
45}
46
47static void xen_pre_suspend(void)
48{
49 xen_mm_pin_all();
50 gnttab_suspend();
51}
52
53static void xen_post_suspend(void)
54{
55 gnttab_resume();
56 xen_mm_unpin_all();
57}
58
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010059#ifdef CONFIG_PM_SLEEP
Stefano Stabellini016b6f52010-05-14 12:45:07 +010060static int xen_hvm_suspend(void *data)
61{
Ian Campbellceb18022011-02-17 11:04:20 +000062 struct suspend_info *si = data;
Ian Campbell8dd38382011-02-17 10:31:20 +000063 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010064
65 BUG_ON(!irqs_disabled());
66
Ian Campbell8dd38382011-02-17 10:31:20 +000067 err = sysdev_suspend(PMSG_SUSPEND);
68 if (err) {
69 printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
70 err);
71 return err;
72 }
73
Ian Campbellbd1c0ad2011-02-17 11:04:20 +000074 /*
75 * This hypercall returns 1 if suspend was cancelled
76 * or the domain was merely checkpointed, and 0 if it
77 * is resuming in a new domain.
78 */
Ian Campbell36b401e2011-02-17 11:04:20 +000079 si->cancelled = HYPERVISOR_suspend(si->arg);
Stefano Stabellini016b6f52010-05-14 12:45:07 +010080
Ian Campbell03c81422011-02-17 11:04:20 +000081 xen_arch_hvm_post_suspend(si->cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000082 xen_hvm_post_suspend();
Stefano Stabellini016b6f52010-05-14 12:45:07 +010083
Ian Campbellceb18022011-02-17 11:04:20 +000084 if (!si->cancelled) {
Stefano Stabellini016b6f52010-05-14 12:45:07 +010085 xen_irq_resume();
Stefano Stabellini6411fe692010-12-01 14:51:44 +000086 xen_console_resume();
Stefano Stabellini016b6f52010-05-14 12:45:07 +010087 xen_timer_resume();
88 }
89
Ian Campbell8dd38382011-02-17 10:31:20 +000090 sysdev_resume();
91
Stefano Stabellini016b6f52010-05-14 12:45:07 +010092 return 0;
93}
94
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010095static int xen_suspend(void *data)
96{
Ian Campbellceb18022011-02-17 11:04:20 +000097 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010098 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010099
100 BUG_ON(!irqs_disabled());
101
Rafael J. Wysocki770824b2009-02-22 18:38:50 +0100102 err = sysdev_suspend(PMSG_SUSPEND);
103 if (err) {
104 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
105 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +0100106 return err;
107 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100108
Ian Campbell82043bb2011-02-17 11:04:20 +0000109 xen_pre_suspend();
Ian Campbell03c81422011-02-17 11:04:20 +0000110 xen_arch_pre_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100111
112 /*
113 * This hypercall returns 1 if suspend was cancelled
114 * or the domain was merely checkpointed, and 0 if it
115 * is resuming in a new domain.
116 */
Ian Campbell36b401e2011-02-17 11:04:20 +0000117 si->cancelled = HYPERVISOR_suspend(si->arg);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100118
Ian Campbell03c81422011-02-17 11:04:20 +0000119 xen_arch_post_suspend(si->cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +0000120 xen_post_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100121
Ian Campbellceb18022011-02-17 11:04:20 +0000122 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100123 xen_irq_resume();
124 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700125 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100126 }
127
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000128 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000129
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100130 return 0;
131}
132
133static void do_suspend(void)
134{
135 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000136 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100137
138 shutting_down = SHUTDOWN_SUSPEND;
139
140#ifdef CONFIG_PREEMPT
141 /* If the kernel is preemptible, we need to freeze all the processes
142 to prevent them from being in the middle of a pagetable update
143 during suspend. */
144 err = freeze_processes();
145 if (err) {
146 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200147 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100148 }
149#endif
150
Alan Sternd1616302009-05-24 22:05:42 +0200151 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100152 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200153 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000154 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100155 }
156
Ian Campbellc5cae662009-12-17 13:57:09 +0000157 printk(KERN_DEBUG "suspending xenstore...\n");
158 xs_suspend();
159
Alan Sternd1616302009-05-24 22:05:42 +0200160 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100161 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200162 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000163 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100164 }
165
Ian Campbellceb18022011-02-17 11:04:20 +0000166 si.cancelled = 1;
167
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100168 if (xen_hvm_domain())
Ian Campbell36b401e2011-02-17 11:04:20 +0000169 si.arg = 0UL;
170 else
171 si.arg = virt_to_mfn(xen_start_info);
172
173 if (xen_hvm_domain())
Ian Campbellceb18022011-02-17 11:04:20 +0000174 err = stop_machine(xen_hvm_suspend, &si, cpumask_of(0));
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100175 else
Ian Campbellceb18022011-02-17 11:04:20 +0000176 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800177
178 dpm_resume_noirq(PMSG_RESUME);
179
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100180 if (err) {
181 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000182 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100183 }
184
Ian Campbellc5cae662009-12-17 13:57:09 +0000185out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000186 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700187 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800188 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700189 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800190 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100191
Alan Sternd1616302009-05-24 22:05:42 +0200192 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100193
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100194 /* Make sure timer events get retriggered on all CPUs */
195 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000196
197out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100198#ifdef CONFIG_PREEMPT
199 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000200out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200201#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100202 shutting_down = SHUTDOWN_INVALID;
203}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100204#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700205
Ian Campbell55271722011-02-17 11:04:20 +0000206struct shutdown_handler {
207 const char *command;
208 void (*cb)(void);
209};
210
211static void do_poweroff(void)
212{
213 shutting_down = SHUTDOWN_POWEROFF;
214 orderly_poweroff(false);
215}
216
217static void do_reboot(void)
218{
219 shutting_down = SHUTDOWN_POWEROFF; /* ? */
220 ctrl_alt_del();
221}
222
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700223static void shutdown_handler(struct xenbus_watch *watch,
224 const char **vec, unsigned int len)
225{
226 char *str;
227 struct xenbus_transaction xbt;
228 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000229 static struct shutdown_handler handlers[] = {
230 { "poweroff", do_poweroff },
231 { "halt", do_poweroff },
232 { "reboot", do_reboot },
233#ifdef CONFIG_PM_SLEEP
234 { "suspend", do_suspend },
235#endif
236 {NULL, NULL},
237 };
238 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700239
240 if (shutting_down != SHUTDOWN_INVALID)
241 return;
242
243 again:
244 err = xenbus_transaction_start(&xbt);
245 if (err)
246 return;
247
248 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
249 /* Ignore read errors and empty reads. */
250 if (XENBUS_IS_ERR_READ(str)) {
251 xenbus_transaction_end(xbt, 1);
252 return;
253 }
254
Ian Campbell55271722011-02-17 11:04:20 +0000255 for (handler = &handlers[0]; handler->command; handler++) {
256 if (strcmp(str, handler->command) == 0)
257 break;
258 }
259
260 /* Only acknowledge commands which we are prepared to handle. */
261 if (handler->cb)
262 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700263
264 err = xenbus_transaction_end(xbt, 0);
265 if (err == -EAGAIN) {
266 kfree(str);
267 goto again;
268 }
269
Ian Campbell55271722011-02-17 11:04:20 +0000270 if (handler->cb) {
271 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100272 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700273 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
274 shutting_down = SHUTDOWN_INVALID;
275 }
276
277 kfree(str);
278}
279
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700280#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700281static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
282 unsigned int len)
283{
284 char sysrq_key = '\0';
285 struct xenbus_transaction xbt;
286 int err;
287
288 again:
289 err = xenbus_transaction_start(&xbt);
290 if (err)
291 return;
292 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
293 printk(KERN_ERR "Unable to read sysrq code in "
294 "control/sysrq\n");
295 xenbus_transaction_end(xbt, 1);
296 return;
297 }
298
299 if (sysrq_key != '\0')
300 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
301
302 err = xenbus_transaction_end(xbt, 0);
303 if (err == -EAGAIN)
304 goto again;
305
306 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700307 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700308}
309
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700310static struct xenbus_watch sysrq_watch = {
311 .node = "control/sysrq",
312 .callback = sysrq_handler
313};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700314#endif
315
316static struct xenbus_watch shutdown_watch = {
317 .node = "control/shutdown",
318 .callback = shutdown_handler
319};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700320
321static int setup_shutdown_watcher(void)
322{
323 int err;
324
325 err = register_xenbus_watch(&shutdown_watch);
326 if (err) {
327 printk(KERN_ERR "Failed to set shutdown watcher\n");
328 return err;
329 }
330
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700331#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700332 err = register_xenbus_watch(&sysrq_watch);
333 if (err) {
334 printk(KERN_ERR "Failed to set sysrq watcher\n");
335 return err;
336 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700337#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700338
339 return 0;
340}
341
342static int shutdown_event(struct notifier_block *notifier,
343 unsigned long event,
344 void *data)
345{
346 setup_shutdown_watcher();
347 return NOTIFY_DONE;
348}
349
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100350int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700351{
352 static struct notifier_block xenstore_notifier = {
353 .notifier_call = shutdown_event
354 };
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000355
356 if (!xen_domain())
357 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700358 register_xenstore_notifier(&xenstore_notifier);
359
360 return 0;
361}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100362EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700363
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000364subsys_initcall(xen_setup_shutdown_event);