blob: db8c4c4ac88086bf7c2e9d1dfa946da399e91b27 [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{
40 struct sched_shutdown r = { .reason = SHUTDOWN_suspend };
41 int *cancelled = data;
42
43 BUG_ON(!irqs_disabled());
44
45 *cancelled = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r);
46
47 xen_hvm_post_suspend(*cancelled);
48 gnttab_resume();
49
50 if (!*cancelled) {
51 xen_irq_resume();
Stefano Stabellini6411fe692010-12-01 14:51:44 +000052 xen_console_resume();
Stefano Stabellini016b6f52010-05-14 12:45:07 +010053 xen_timer_resume();
54 }
55
56 return 0;
57}
58
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010059static int xen_suspend(void *data)
60{
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010061 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010062 int *cancelled = data;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010063
64 BUG_ON(!irqs_disabled());
65
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010066 err = sysdev_suspend(PMSG_SUSPEND);
67 if (err) {
68 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
69 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010070 return err;
71 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010072
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010073 xen_mm_pin_all();
74 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010075 xen_pre_suspend();
76
77 /*
78 * This hypercall returns 1 if suspend was cancelled
79 * or the domain was merely checkpointed, and 0 if it
80 * is resuming in a new domain.
81 */
82 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
83
84 xen_post_suspend(*cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010085 gnttab_resume();
86 xen_mm_unpin_all();
87
88 if (!*cancelled) {
89 xen_irq_resume();
90 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070091 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010092 }
93
Ian Campbell1e6fcf82009-03-25 17:46:42 +000094 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +000095
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010096 return 0;
97}
98
99static void do_suspend(void)
100{
101 int err;
102 int cancelled = 1;
103
104 shutting_down = SHUTDOWN_SUSPEND;
105
106#ifdef CONFIG_PREEMPT
107 /* If the kernel is preemptible, we need to freeze all the processes
108 to prevent them from being in the middle of a pagetable update
109 during suspend. */
110 err = freeze_processes();
111 if (err) {
112 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200113 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100114 }
115#endif
116
Alan Sternd1616302009-05-24 22:05:42 +0200117 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100118 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200119 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000120 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100121 }
122
Ian Campbellc5cae662009-12-17 13:57:09 +0000123 printk(KERN_DEBUG "suspending xenstore...\n");
124 xs_suspend();
125
Alan Sternd1616302009-05-24 22:05:42 +0200126 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100127 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200128 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000129 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100130 }
131
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100132 if (xen_hvm_domain())
133 err = stop_machine(xen_hvm_suspend, &cancelled, cpumask_of(0));
134 else
135 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800136
137 dpm_resume_noirq(PMSG_RESUME);
138
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100139 if (err) {
140 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000141 cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100142 }
143
Ian Campbellc5cae662009-12-17 13:57:09 +0000144out_resume:
Isaku Yamahataad55db92008-07-08 15:06:32 -0700145 if (!cancelled) {
146 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800147 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700148 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800149 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100150
Alan Sternd1616302009-05-24 22:05:42 +0200151 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100152
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100153 /* Make sure timer events get retriggered on all CPUs */
154 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000155
156out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100157#ifdef CONFIG_PREEMPT
158 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000159out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200160#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100161 shutting_down = SHUTDOWN_INVALID;
162}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100163#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700164
165static void shutdown_handler(struct xenbus_watch *watch,
166 const char **vec, unsigned int len)
167{
168 char *str;
169 struct xenbus_transaction xbt;
170 int err;
171
172 if (shutting_down != SHUTDOWN_INVALID)
173 return;
174
175 again:
176 err = xenbus_transaction_start(&xbt);
177 if (err)
178 return;
179
180 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
181 /* Ignore read errors and empty reads. */
182 if (XENBUS_IS_ERR_READ(str)) {
183 xenbus_transaction_end(xbt, 1);
184 return;
185 }
186
187 xenbus_write(xbt, "control", "shutdown", "");
188
189 err = xenbus_transaction_end(xbt, 0);
190 if (err == -EAGAIN) {
191 kfree(str);
192 goto again;
193 }
194
195 if (strcmp(str, "poweroff") == 0 ||
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100196 strcmp(str, "halt") == 0) {
197 shutting_down = SHUTDOWN_POWEROFF;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700198 orderly_poweroff(false);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100199 } else if (strcmp(str, "reboot") == 0) {
200 shutting_down = SHUTDOWN_POWEROFF; /* ? */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700201 ctrl_alt_del();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100202#ifdef CONFIG_PM_SLEEP
203 } else if (strcmp(str, "suspend") == 0) {
204 do_suspend();
205#endif
206 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700207 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
208 shutting_down = SHUTDOWN_INVALID;
209 }
210
211 kfree(str);
212}
213
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700214#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700215static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
216 unsigned int len)
217{
218 char sysrq_key = '\0';
219 struct xenbus_transaction xbt;
220 int err;
221
222 again:
223 err = xenbus_transaction_start(&xbt);
224 if (err)
225 return;
226 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
227 printk(KERN_ERR "Unable to read sysrq code in "
228 "control/sysrq\n");
229 xenbus_transaction_end(xbt, 1);
230 return;
231 }
232
233 if (sysrq_key != '\0')
234 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
235
236 err = xenbus_transaction_end(xbt, 0);
237 if (err == -EAGAIN)
238 goto again;
239
240 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700241 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700242}
243
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700244static struct xenbus_watch sysrq_watch = {
245 .node = "control/sysrq",
246 .callback = sysrq_handler
247};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700248#endif
249
250static struct xenbus_watch shutdown_watch = {
251 .node = "control/shutdown",
252 .callback = shutdown_handler
253};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700254
255static int setup_shutdown_watcher(void)
256{
257 int err;
258
259 err = register_xenbus_watch(&shutdown_watch);
260 if (err) {
261 printk(KERN_ERR "Failed to set shutdown watcher\n");
262 return err;
263 }
264
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700265#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700266 err = register_xenbus_watch(&sysrq_watch);
267 if (err) {
268 printk(KERN_ERR "Failed to set sysrq watcher\n");
269 return err;
270 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700271#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700272
273 return 0;
274}
275
276static int shutdown_event(struct notifier_block *notifier,
277 unsigned long event,
278 void *data)
279{
280 setup_shutdown_watcher();
281 return NOTIFY_DONE;
282}
283
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100284static int __init __setup_shutdown_event(void)
285{
286 /* Delay initialization in the PV on HVM case */
287 if (xen_hvm_domain())
288 return 0;
289
290 if (!xen_pv_domain())
291 return -ENODEV;
292
293 return xen_setup_shutdown_event();
294}
295
296int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700297{
298 static struct notifier_block xenstore_notifier = {
299 .notifier_call = shutdown_event
300 };
301 register_xenstore_notifier(&xenstore_notifier);
302
303 return 0;
304}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100305EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700306
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100307subsys_initcall(__setup_shutdown_event);