xen: suspend: refactor cancellation flag into a structure
[linux-2.6-block.git] / drivers / xen / manage.c
CommitLineData
3e2b8fbe
JF
1/*
2 * Handle extern requests for shutdown, reboot and sysrq
3 */
4#include <linux/kernel.h>
5#include <linux/err.h>
5a0e3ad6 6#include <linux/slab.h>
3e2b8fbe
JF
7#include <linux/reboot.h>
8#include <linux/sysrq.h>
0e91398f
JF
9#include <linux/stop_machine.h>
10#include <linux/freezer.h>
3e2b8fbe 11
016b6f5f 12#include <xen/xen.h>
3e2b8fbe 13#include <xen/xenbus.h>
0e91398f
JF
14#include <xen/grant_table.h>
15#include <xen/events.h>
16#include <xen/hvc-console.h>
17#include <xen/xen-ops.h>
3e2b8fbe 18
0e91398f
JF
19#include <asm/xen/hypercall.h>
20#include <asm/xen/page.h>
016b6f5f 21#include <asm/xen/hypervisor.h>
0e91398f
JF
22
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};
3e2b8fbe
JF
33
34/* Ignore multiple shutdown requests. */
0e91398f
JF
35static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
36
ceb18029
IC
37struct suspend_info {
38 int cancelled;
39};
40
c7827728 41#ifdef CONFIG_PM_SLEEP
016b6f5f 42static int xen_hvm_suspend(void *data)
0e91398f 43{
ceb18029 44 struct suspend_info *si = data;
8dd38383 45 int err;
016b6f5f
SS
46
47 BUG_ON(!irqs_disabled());
48
8dd38383
IC
49 err = sysdev_suspend(PMSG_SUSPEND);
50 if (err) {
51 printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
52 err);
53 return err;
54 }
55
bd1c0ad2
IC
56 /*
57 * This hypercall returns 1 if suspend was cancelled
58 * or the domain was merely checkpointed, and 0 if it
59 * is resuming in a new domain.
60 */
ceb18029 61 si->cancelled = HYPERVISOR_suspend(0UL);
016b6f5f 62
ceb18029 63 xen_hvm_post_suspend(si->cancelled);
016b6f5f
SS
64 gnttab_resume();
65
ceb18029 66 if (!si->cancelled) {
016b6f5f 67 xen_irq_resume();
6411fe69 68 xen_console_resume();
016b6f5f
SS
69 xen_timer_resume();
70 }
71
8dd38383
IC
72 sysdev_resume();
73
016b6f5f
SS
74 return 0;
75}
76
77static int xen_suspend(void *data)
78{
ceb18029 79 struct suspend_info *si = data;
359cdd3f 80 int err;
0e91398f
JF
81
82 BUG_ON(!irqs_disabled());
83
770824bd
RW
84 err = sysdev_suspend(PMSG_SUSPEND);
85 if (err) {
86 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
87 err);
770824bd
RW
88 return err;
89 }
359cdd3f 90
0e91398f
JF
91 xen_mm_pin_all();
92 gnttab_suspend();
0e91398f
JF
93 xen_pre_suspend();
94
95 /*
96 * This hypercall returns 1 if suspend was cancelled
97 * or the domain was merely checkpointed, and 0 if it
98 * is resuming in a new domain.
99 */
ceb18029 100 si->cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
0e91398f 101
ceb18029 102 xen_post_suspend(si->cancelled);
0e91398f
JF
103 gnttab_resume();
104 xen_mm_unpin_all();
105
ceb18029 106 if (!si->cancelled) {
0e91398f
JF
107 xen_irq_resume();
108 xen_console_resume();
ad55db9f 109 xen_timer_resume();
0e91398f
JF
110 }
111
1e6fcf84 112 sysdev_resume();
1e6fcf84 113
0e91398f
JF
114 return 0;
115}
116
117static void do_suspend(void)
118{
119 int err;
ceb18029 120 struct suspend_info si;
0e91398f
JF
121
122 shutting_down = SHUTDOWN_SUSPEND;
123
124#ifdef CONFIG_PREEMPT
125 /* If the kernel is preemptible, we need to freeze all the processes
126 to prevent them from being in the middle of a pagetable update
127 during suspend. */
128 err = freeze_processes();
129 if (err) {
130 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
3fc1f1e2 131 goto out;
0e91398f
JF
132 }
133#endif
134
d1616302 135 err = dpm_suspend_start(PMSG_SUSPEND);
0e91398f 136 if (err) {
d1616302 137 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
65f63384 138 goto out_thaw;
0e91398f
JF
139 }
140
c5cae661
IC
141 printk(KERN_DEBUG "suspending xenstore...\n");
142 xs_suspend();
143
d1616302 144 err = dpm_suspend_noirq(PMSG_SUSPEND);
2ed8d2b3 145 if (err) {
d1616302 146 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
65f63384 147 goto out_resume;
2ed8d2b3
RW
148 }
149
ceb18029
IC
150 si.cancelled = 1;
151
016b6f5f 152 if (xen_hvm_domain())
ceb18029 153 err = stop_machine(xen_hvm_suspend, &si, cpumask_of(0));
016b6f5f 154 else
ceb18029 155 err = stop_machine(xen_suspend, &si, cpumask_of(0));
922cc38a
JF
156
157 dpm_resume_noirq(PMSG_RESUME);
158
0e91398f
JF
159 if (err) {
160 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
ceb18029 161 si.cancelled = 1;
0e91398f
JF
162 }
163
c5cae661 164out_resume:
ceb18029 165 if (!si.cancelled) {
ad55db9f 166 xen_arch_resume();
de5b31bd 167 xs_resume();
ad55db9f 168 } else
de5b31bd 169 xs_suspend_cancel();
0e91398f 170
d1616302 171 dpm_resume_end(PMSG_RESUME);
0e91398f 172
359cdd3f
JF
173 /* Make sure timer events get retriggered on all CPUs */
174 clock_was_set();
65f63384
IC
175
176out_thaw:
0e91398f
JF
177#ifdef CONFIG_PREEMPT
178 thaw_processes();
65f63384 179out:
3fc1f1e2 180#endif
0e91398f
JF
181 shutting_down = SHUTDOWN_INVALID;
182}
c7827728 183#endif /* CONFIG_PM_SLEEP */
3e2b8fbe 184
55271723
IC
185struct shutdown_handler {
186 const char *command;
187 void (*cb)(void);
188};
189
190static void do_poweroff(void)
191{
192 shutting_down = SHUTDOWN_POWEROFF;
193 orderly_poweroff(false);
194}
195
196static void do_reboot(void)
197{
198 shutting_down = SHUTDOWN_POWEROFF; /* ? */
199 ctrl_alt_del();
200}
201
3e2b8fbe
JF
202static void shutdown_handler(struct xenbus_watch *watch,
203 const char **vec, unsigned int len)
204{
205 char *str;
206 struct xenbus_transaction xbt;
207 int err;
55271723
IC
208 static struct shutdown_handler handlers[] = {
209 { "poweroff", do_poweroff },
210 { "halt", do_poweroff },
211 { "reboot", do_reboot },
212#ifdef CONFIG_PM_SLEEP
213 { "suspend", do_suspend },
214#endif
215 {NULL, NULL},
216 };
217 static struct shutdown_handler *handler;
3e2b8fbe
JF
218
219 if (shutting_down != SHUTDOWN_INVALID)
220 return;
221
222 again:
223 err = xenbus_transaction_start(&xbt);
224 if (err)
225 return;
226
227 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
228 /* Ignore read errors and empty reads. */
229 if (XENBUS_IS_ERR_READ(str)) {
230 xenbus_transaction_end(xbt, 1);
231 return;
232 }
233
55271723
IC
234 for (handler = &handlers[0]; handler->command; handler++) {
235 if (strcmp(str, handler->command) == 0)
236 break;
237 }
238
239 /* Only acknowledge commands which we are prepared to handle. */
240 if (handler->cb)
241 xenbus_write(xbt, "control", "shutdown", "");
3e2b8fbe
JF
242
243 err = xenbus_transaction_end(xbt, 0);
244 if (err == -EAGAIN) {
245 kfree(str);
246 goto again;
247 }
248
55271723
IC
249 if (handler->cb) {
250 handler->cb();
0e91398f 251 } else {
3e2b8fbe
JF
252 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
253 shutting_down = SHUTDOWN_INVALID;
254 }
255
256 kfree(str);
257}
258
f3bc3189 259#ifdef CONFIG_MAGIC_SYSRQ
3e2b8fbe
JF
260static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
261 unsigned int len)
262{
263 char sysrq_key = '\0';
264 struct xenbus_transaction xbt;
265 int err;
266
267 again:
268 err = xenbus_transaction_start(&xbt);
269 if (err)
270 return;
271 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
272 printk(KERN_ERR "Unable to read sysrq code in "
273 "control/sysrq\n");
274 xenbus_transaction_end(xbt, 1);
275 return;
276 }
277
278 if (sysrq_key != '\0')
279 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
280
281 err = xenbus_transaction_end(xbt, 0);
282 if (err == -EAGAIN)
283 goto again;
284
285 if (sysrq_key != '\0')
f335397d 286 handle_sysrq(sysrq_key);
3e2b8fbe
JF
287}
288
3e2b8fbe
JF
289static struct xenbus_watch sysrq_watch = {
290 .node = "control/sysrq",
291 .callback = sysrq_handler
292};
f3bc3189
RD
293#endif
294
295static struct xenbus_watch shutdown_watch = {
296 .node = "control/shutdown",
297 .callback = shutdown_handler
298};
3e2b8fbe
JF
299
300static int setup_shutdown_watcher(void)
301{
302 int err;
303
304 err = register_xenbus_watch(&shutdown_watch);
305 if (err) {
306 printk(KERN_ERR "Failed to set shutdown watcher\n");
307 return err;
308 }
309
f3bc3189 310#ifdef CONFIG_MAGIC_SYSRQ
3e2b8fbe
JF
311 err = register_xenbus_watch(&sysrq_watch);
312 if (err) {
313 printk(KERN_ERR "Failed to set sysrq watcher\n");
314 return err;
315 }
f3bc3189 316#endif
3e2b8fbe
JF
317
318 return 0;
319}
320
321static int shutdown_event(struct notifier_block *notifier,
322 unsigned long event,
323 void *data)
324{
325 setup_shutdown_watcher();
326 return NOTIFY_DONE;
327}
328
016b6f5f 329int xen_setup_shutdown_event(void)
3e2b8fbe
JF
330{
331 static struct notifier_block xenstore_notifier = {
332 .notifier_call = shutdown_event
333 };
702d4eb9
SS
334
335 if (!xen_domain())
336 return -ENODEV;
3e2b8fbe
JF
337 register_xenstore_notifier(&xenstore_notifier);
338
339 return 0;
340}
183d03cc 341EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
3e2b8fbe 342
702d4eb9 343subsys_initcall(xen_setup_shutdown_event);