blob: 972bf783a1825d2b811ac70de4ef1f81d6fd2410 [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
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010037#ifdef CONFIG_PM_SLEEP
Stefano Stabellini016b6f52010-05-14 12:45:07 +010038static int xen_hvm_suspend(void *data)
39{
Ian Campbell8dd38382011-02-17 10:31:20 +000040 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010041 struct sched_shutdown r = { .reason = SHUTDOWN_suspend };
42 int *cancelled = data;
43
44 BUG_ON(!irqs_disabled());
45
Ian Campbell8dd38382011-02-17 10:31:20 +000046 err = sysdev_suspend(PMSG_SUSPEND);
47 if (err) {
48 printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
49 err);
50 return err;
51 }
52
Stefano Stabellini016b6f52010-05-14 12:45:07 +010053 *cancelled = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r);
54
55 xen_hvm_post_suspend(*cancelled);
56 gnttab_resume();
57
58 if (!*cancelled) {
59 xen_irq_resume();
Stefano Stabellini6411fe692010-12-01 14:51:44 +000060 xen_console_resume();
Stefano Stabellini016b6f52010-05-14 12:45:07 +010061 xen_timer_resume();
62 }
63
Ian Campbell8dd38382011-02-17 10:31:20 +000064 sysdev_resume();
65
Stefano Stabellini016b6f52010-05-14 12:45:07 +010066 return 0;
67}
68
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010069static int xen_suspend(void *data)
70{
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010071 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010072 int *cancelled = data;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010073
74 BUG_ON(!irqs_disabled());
75
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010076 err = sysdev_suspend(PMSG_SUSPEND);
77 if (err) {
78 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
79 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010080 return err;
81 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010082
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010083 xen_mm_pin_all();
84 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010085 xen_pre_suspend();
86
87 /*
88 * This hypercall returns 1 if suspend was cancelled
89 * or the domain was merely checkpointed, and 0 if it
90 * is resuming in a new domain.
91 */
92 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
93
94 xen_post_suspend(*cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010095 gnttab_resume();
96 xen_mm_unpin_all();
97
98 if (!*cancelled) {
99 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
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000104 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000105
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100106 return 0;
107}
108
109static void do_suspend(void)
110{
111 int err;
112 int cancelled = 1;
113
114 shutting_down = SHUTDOWN_SUSPEND;
115
116#ifdef CONFIG_PREEMPT
117 /* If the kernel is preemptible, we need to freeze all the processes
118 to prevent them from being in the middle of a pagetable update
119 during suspend. */
120 err = freeze_processes();
121 if (err) {
122 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200123 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100124 }
125#endif
126
Alan Sternd1616302009-05-24 22:05:42 +0200127 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100128 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200129 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000130 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100131 }
132
Ian Campbellc5cae662009-12-17 13:57:09 +0000133 printk(KERN_DEBUG "suspending xenstore...\n");
134 xs_suspend();
135
Alan Sternd1616302009-05-24 22:05:42 +0200136 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100137 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200138 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000139 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100140 }
141
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100142 if (xen_hvm_domain())
143 err = stop_machine(xen_hvm_suspend, &cancelled, cpumask_of(0));
144 else
145 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800146
147 dpm_resume_noirq(PMSG_RESUME);
148
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100149 if (err) {
150 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000151 cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100152 }
153
Ian Campbellc5cae662009-12-17 13:57:09 +0000154out_resume:
Isaku Yamahataad55db92008-07-08 15:06:32 -0700155 if (!cancelled) {
156 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800157 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700158 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800159 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100160
Alan Sternd1616302009-05-24 22:05:42 +0200161 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100162
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100163 /* Make sure timer events get retriggered on all CPUs */
164 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000165
166out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100167#ifdef CONFIG_PREEMPT
168 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000169out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200170#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100171 shutting_down = SHUTDOWN_INVALID;
172}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100173#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700174
Ian Campbell55271722011-02-17 11:04:20 +0000175struct shutdown_handler {
176 const char *command;
177 void (*cb)(void);
178};
179
180static void do_poweroff(void)
181{
182 shutting_down = SHUTDOWN_POWEROFF;
183 orderly_poweroff(false);
184}
185
186static void do_reboot(void)
187{
188 shutting_down = SHUTDOWN_POWEROFF; /* ? */
189 ctrl_alt_del();
190}
191
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700192static void shutdown_handler(struct xenbus_watch *watch,
193 const char **vec, unsigned int len)
194{
195 char *str;
196 struct xenbus_transaction xbt;
197 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000198 static struct shutdown_handler handlers[] = {
199 { "poweroff", do_poweroff },
200 { "halt", do_poweroff },
201 { "reboot", do_reboot },
202#ifdef CONFIG_PM_SLEEP
203 { "suspend", do_suspend },
204#endif
205 {NULL, NULL},
206 };
207 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700208
209 if (shutting_down != SHUTDOWN_INVALID)
210 return;
211
212 again:
213 err = xenbus_transaction_start(&xbt);
214 if (err)
215 return;
216
217 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
218 /* Ignore read errors and empty reads. */
219 if (XENBUS_IS_ERR_READ(str)) {
220 xenbus_transaction_end(xbt, 1);
221 return;
222 }
223
Ian Campbell55271722011-02-17 11:04:20 +0000224 for (handler = &handlers[0]; handler->command; handler++) {
225 if (strcmp(str, handler->command) == 0)
226 break;
227 }
228
229 /* Only acknowledge commands which we are prepared to handle. */
230 if (handler->cb)
231 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700232
233 err = xenbus_transaction_end(xbt, 0);
234 if (err == -EAGAIN) {
235 kfree(str);
236 goto again;
237 }
238
Ian Campbell55271722011-02-17 11:04:20 +0000239 if (handler->cb) {
240 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100241 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700242 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
243 shutting_down = SHUTDOWN_INVALID;
244 }
245
246 kfree(str);
247}
248
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700249#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700250static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
251 unsigned int len)
252{
253 char sysrq_key = '\0';
254 struct xenbus_transaction xbt;
255 int err;
256
257 again:
258 err = xenbus_transaction_start(&xbt);
259 if (err)
260 return;
261 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
262 printk(KERN_ERR "Unable to read sysrq code in "
263 "control/sysrq\n");
264 xenbus_transaction_end(xbt, 1);
265 return;
266 }
267
268 if (sysrq_key != '\0')
269 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
270
271 err = xenbus_transaction_end(xbt, 0);
272 if (err == -EAGAIN)
273 goto again;
274
275 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700276 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700277}
278
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700279static struct xenbus_watch sysrq_watch = {
280 .node = "control/sysrq",
281 .callback = sysrq_handler
282};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700283#endif
284
285static struct xenbus_watch shutdown_watch = {
286 .node = "control/shutdown",
287 .callback = shutdown_handler
288};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700289
290static int setup_shutdown_watcher(void)
291{
292 int err;
293
294 err = register_xenbus_watch(&shutdown_watch);
295 if (err) {
296 printk(KERN_ERR "Failed to set shutdown watcher\n");
297 return err;
298 }
299
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700300#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700301 err = register_xenbus_watch(&sysrq_watch);
302 if (err) {
303 printk(KERN_ERR "Failed to set sysrq watcher\n");
304 return err;
305 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700306#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700307
308 return 0;
309}
310
311static int shutdown_event(struct notifier_block *notifier,
312 unsigned long event,
313 void *data)
314{
315 setup_shutdown_watcher();
316 return NOTIFY_DONE;
317}
318
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100319int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700320{
321 static struct notifier_block xenstore_notifier = {
322 .notifier_call = shutdown_event
323 };
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000324
325 if (!xen_domain())
326 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700327 register_xenstore_notifier(&xenstore_notifier);
328
329 return 0;
330}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100331EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700332
Stefano Stabellini702d4eb92010-12-02 17:54:50 +0000333subsys_initcall(xen_setup_shutdown_event);