blob: 24177272bcb84aed3ec51cb82f91535f0c034fc0 [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
175static void shutdown_handler(struct xenbus_watch *watch,
176 const char **vec, unsigned int len)
177{
178 char *str;
179 struct xenbus_transaction xbt;
180 int err;
181
182 if (shutting_down != SHUTDOWN_INVALID)
183 return;
184
185 again:
186 err = xenbus_transaction_start(&xbt);
187 if (err)
188 return;
189
190 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
191 /* Ignore read errors and empty reads. */
192 if (XENBUS_IS_ERR_READ(str)) {
193 xenbus_transaction_end(xbt, 1);
194 return;
195 }
196
197 xenbus_write(xbt, "control", "shutdown", "");
198
199 err = xenbus_transaction_end(xbt, 0);
200 if (err == -EAGAIN) {
201 kfree(str);
202 goto again;
203 }
204
205 if (strcmp(str, "poweroff") == 0 ||
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100206 strcmp(str, "halt") == 0) {
207 shutting_down = SHUTDOWN_POWEROFF;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700208 orderly_poweroff(false);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100209 } else if (strcmp(str, "reboot") == 0) {
210 shutting_down = SHUTDOWN_POWEROFF; /* ? */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700211 ctrl_alt_del();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100212#ifdef CONFIG_PM_SLEEP
213 } else if (strcmp(str, "suspend") == 0) {
214 do_suspend();
215#endif
216 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700217 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
218 shutting_down = SHUTDOWN_INVALID;
219 }
220
221 kfree(str);
222}
223
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700224#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700225static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
226 unsigned int len)
227{
228 char sysrq_key = '\0';
229 struct xenbus_transaction xbt;
230 int err;
231
232 again:
233 err = xenbus_transaction_start(&xbt);
234 if (err)
235 return;
236 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
237 printk(KERN_ERR "Unable to read sysrq code in "
238 "control/sysrq\n");
239 xenbus_transaction_end(xbt, 1);
240 return;
241 }
242
243 if (sysrq_key != '\0')
244 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
245
246 err = xenbus_transaction_end(xbt, 0);
247 if (err == -EAGAIN)
248 goto again;
249
250 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700251 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700252}
253
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700254static struct xenbus_watch sysrq_watch = {
255 .node = "control/sysrq",
256 .callback = sysrq_handler
257};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700258#endif
259
260static struct xenbus_watch shutdown_watch = {
261 .node = "control/shutdown",
262 .callback = shutdown_handler
263};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700264
265static int setup_shutdown_watcher(void)
266{
267 int err;
268
269 err = register_xenbus_watch(&shutdown_watch);
270 if (err) {
271 printk(KERN_ERR "Failed to set shutdown watcher\n");
272 return err;
273 }
274
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700275#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700276 err = register_xenbus_watch(&sysrq_watch);
277 if (err) {
278 printk(KERN_ERR "Failed to set sysrq watcher\n");
279 return err;
280 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700281#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700282
283 return 0;
284}
285
286static int shutdown_event(struct notifier_block *notifier,
287 unsigned long event,
288 void *data)
289{
290 setup_shutdown_watcher();
291 return NOTIFY_DONE;
292}
293
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100294static int __init __setup_shutdown_event(void)
295{
296 /* Delay initialization in the PV on HVM case */
297 if (xen_hvm_domain())
298 return 0;
299
300 if (!xen_pv_domain())
301 return -ENODEV;
302
303 return xen_setup_shutdown_event();
304}
305
306int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700307{
308 static struct notifier_block xenstore_notifier = {
309 .notifier_call = shutdown_event
310 };
311 register_xenstore_notifier(&xenstore_notifier);
312
313 return 0;
314}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100315EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700316
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100317subsys_initcall(__setup_shutdown_event);