blob: 675c3dd23962ff45fa6c058250bbb0c834085be1 [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>
6#include <linux/reboot.h>
7#include <linux/sysrq.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +01008#include <linux/stop_machine.h>
9#include <linux/freezer.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070010
11#include <xen/xenbus.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010012#include <xen/grant_table.h>
13#include <xen/events.h>
14#include <xen/hvc-console.h>
15#include <xen/xen-ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070016
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010017#include <asm/xen/hypercall.h>
18#include <asm/xen/page.h>
19
20enum shutdown_state {
21 SHUTDOWN_INVALID = -1,
22 SHUTDOWN_POWEROFF = 0,
23 SHUTDOWN_SUSPEND = 2,
24 /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
25 report a crash, not be instructed to crash!
26 HALT is the same as POWEROFF, as far as we're concerned. The tools use
27 the distinction when we return the reason code to them. */
28 SHUTDOWN_HALT = 4,
29};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070030
31/* Ignore multiple shutdown requests. */
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010032static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
33
34static int xen_suspend(void *data)
35{
36 int *cancelled = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010037 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010038
39 BUG_ON(!irqs_disabled());
40
41 load_cr3(swapper_pg_dir);
42
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010043 err = device_power_down(PMSG_SUSPEND);
44 if (err) {
45 printk(KERN_ERR "xen_suspend: device_power_down failed: %d\n",
46 err);
47 return err;
48 }
49
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010050 xen_mm_pin_all();
51 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010052 xen_pre_suspend();
53
54 /*
55 * This hypercall returns 1 if suspend was cancelled
56 * or the domain was merely checkpointed, and 0 if it
57 * is resuming in a new domain.
58 */
59 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
60
61 xen_post_suspend(*cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010062 gnttab_resume();
63 xen_mm_unpin_all();
64
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010065 device_power_up();
66
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010067 if (!*cancelled) {
68 xen_irq_resume();
69 xen_console_resume();
70 }
71
72 return 0;
73}
74
75static void do_suspend(void)
76{
77 int err;
78 int cancelled = 1;
79
80 shutting_down = SHUTDOWN_SUSPEND;
81
82#ifdef CONFIG_PREEMPT
83 /* If the kernel is preemptible, we need to freeze all the processes
84 to prevent them from being in the middle of a pagetable update
85 during suspend. */
86 err = freeze_processes();
87 if (err) {
88 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
89 return;
90 }
91#endif
92
93 err = device_suspend(PMSG_SUSPEND);
94 if (err) {
95 printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
96 goto out;
97 }
98
99 printk("suspending xenbus...\n");
100 /* XXX use normal device tree? */
101 xenbus_suspend();
102
103 err = stop_machine_run(xen_suspend, &cancelled, 0);
104 if (err) {
105 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
106 goto out;
107 }
108
109 if (!cancelled)
110 xenbus_resume();
111 else
112 xenbus_suspend_cancel();
113
114 device_resume();
115
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100116 /* Make sure timer events get retriggered on all CPUs */
117 clock_was_set();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100118out:
119#ifdef CONFIG_PREEMPT
120 thaw_processes();
121#endif
122 shutting_down = SHUTDOWN_INVALID;
123}
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700124
125static void shutdown_handler(struct xenbus_watch *watch,
126 const char **vec, unsigned int len)
127{
128 char *str;
129 struct xenbus_transaction xbt;
130 int err;
131
132 if (shutting_down != SHUTDOWN_INVALID)
133 return;
134
135 again:
136 err = xenbus_transaction_start(&xbt);
137 if (err)
138 return;
139
140 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
141 /* Ignore read errors and empty reads. */
142 if (XENBUS_IS_ERR_READ(str)) {
143 xenbus_transaction_end(xbt, 1);
144 return;
145 }
146
147 xenbus_write(xbt, "control", "shutdown", "");
148
149 err = xenbus_transaction_end(xbt, 0);
150 if (err == -EAGAIN) {
151 kfree(str);
152 goto again;
153 }
154
155 if (strcmp(str, "poweroff") == 0 ||
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100156 strcmp(str, "halt") == 0) {
157 shutting_down = SHUTDOWN_POWEROFF;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700158 orderly_poweroff(false);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100159 } else if (strcmp(str, "reboot") == 0) {
160 shutting_down = SHUTDOWN_POWEROFF; /* ? */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700161 ctrl_alt_del();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100162#ifdef CONFIG_PM_SLEEP
163 } else if (strcmp(str, "suspend") == 0) {
164 do_suspend();
165#endif
166 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700167 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
168 shutting_down = SHUTDOWN_INVALID;
169 }
170
171 kfree(str);
172}
173
174static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
175 unsigned int len)
176{
177 char sysrq_key = '\0';
178 struct xenbus_transaction xbt;
179 int err;
180
181 again:
182 err = xenbus_transaction_start(&xbt);
183 if (err)
184 return;
185 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
186 printk(KERN_ERR "Unable to read sysrq code in "
187 "control/sysrq\n");
188 xenbus_transaction_end(xbt, 1);
189 return;
190 }
191
192 if (sysrq_key != '\0')
193 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
194
195 err = xenbus_transaction_end(xbt, 0);
196 if (err == -EAGAIN)
197 goto again;
198
199 if (sysrq_key != '\0')
200 handle_sysrq(sysrq_key, NULL);
201}
202
203static struct xenbus_watch shutdown_watch = {
204 .node = "control/shutdown",
205 .callback = shutdown_handler
206};
207
208static struct xenbus_watch sysrq_watch = {
209 .node = "control/sysrq",
210 .callback = sysrq_handler
211};
212
213static int setup_shutdown_watcher(void)
214{
215 int err;
216
217 err = register_xenbus_watch(&shutdown_watch);
218 if (err) {
219 printk(KERN_ERR "Failed to set shutdown watcher\n");
220 return err;
221 }
222
223 err = register_xenbus_watch(&sysrq_watch);
224 if (err) {
225 printk(KERN_ERR "Failed to set sysrq watcher\n");
226 return err;
227 }
228
229 return 0;
230}
231
232static int shutdown_event(struct notifier_block *notifier,
233 unsigned long event,
234 void *data)
235{
236 setup_shutdown_watcher();
237 return NOTIFY_DONE;
238}
239
240static int __init setup_shutdown_event(void)
241{
242 static struct notifier_block xenstore_notifier = {
243 .notifier_call = shutdown_event
244 };
245 register_xenstore_notifier(&xenstore_notifier);
246
247 return 0;
248}
249
250subsys_initcall(setup_shutdown_event);