xen: implement save/restore
[deliverable/linux.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>
6#include <linux/reboot.h>
7#include <linux/sysrq.h>
0e91398f
JF
8#include <linux/stop_machine.h>
9#include <linux/freezer.h>
3e2b8fbe
JF
10
11#include <xen/xenbus.h>
0e91398f
JF
12#include <xen/grant_table.h>
13#include <xen/events.h>
14#include <xen/hvc-console.h>
15#include <xen/xen-ops.h>
3e2b8fbe 16
0e91398f
JF
17#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};
3e2b8fbe
JF
30
31/* Ignore multiple shutdown requests. */
0e91398f
JF
32static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
33
34static int xen_suspend(void *data)
35{
36 int *cancelled = data;
37
38 BUG_ON(!irqs_disabled());
39
40 load_cr3(swapper_pg_dir);
41
42 xen_mm_pin_all();
43 gnttab_suspend();
44 xen_time_suspend();
45 xen_pre_suspend();
46
47 /*
48 * This hypercall returns 1 if suspend was cancelled
49 * or the domain was merely checkpointed, and 0 if it
50 * is resuming in a new domain.
51 */
52 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
53
54 xen_post_suspend(*cancelled);
55 xen_time_resume();
56 gnttab_resume();
57 xen_mm_unpin_all();
58
59 if (!*cancelled) {
60 xen_irq_resume();
61 xen_console_resume();
62 }
63
64 return 0;
65}
66
67static void do_suspend(void)
68{
69 int err;
70 int cancelled = 1;
71
72 shutting_down = SHUTDOWN_SUSPEND;
73
74#ifdef CONFIG_PREEMPT
75 /* If the kernel is preemptible, we need to freeze all the processes
76 to prevent them from being in the middle of a pagetable update
77 during suspend. */
78 err = freeze_processes();
79 if (err) {
80 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
81 return;
82 }
83#endif
84
85 err = device_suspend(PMSG_SUSPEND);
86 if (err) {
87 printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
88 goto out;
89 }
90
91 printk("suspending xenbus...\n");
92 /* XXX use normal device tree? */
93 xenbus_suspend();
94
95 err = stop_machine_run(xen_suspend, &cancelled, 0);
96 if (err) {
97 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
98 goto out;
99 }
100
101 if (!cancelled)
102 xenbus_resume();
103 else
104 xenbus_suspend_cancel();
105
106 device_resume();
107
108
109out:
110#ifdef CONFIG_PREEMPT
111 thaw_processes();
112#endif
113 shutting_down = SHUTDOWN_INVALID;
114}
3e2b8fbe
JF
115
116static void shutdown_handler(struct xenbus_watch *watch,
117 const char **vec, unsigned int len)
118{
119 char *str;
120 struct xenbus_transaction xbt;
121 int err;
122
123 if (shutting_down != SHUTDOWN_INVALID)
124 return;
125
126 again:
127 err = xenbus_transaction_start(&xbt);
128 if (err)
129 return;
130
131 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
132 /* Ignore read errors and empty reads. */
133 if (XENBUS_IS_ERR_READ(str)) {
134 xenbus_transaction_end(xbt, 1);
135 return;
136 }
137
138 xenbus_write(xbt, "control", "shutdown", "");
139
140 err = xenbus_transaction_end(xbt, 0);
141 if (err == -EAGAIN) {
142 kfree(str);
143 goto again;
144 }
145
146 if (strcmp(str, "poweroff") == 0 ||
0e91398f
JF
147 strcmp(str, "halt") == 0) {
148 shutting_down = SHUTDOWN_POWEROFF;
3e2b8fbe 149 orderly_poweroff(false);
0e91398f
JF
150 } else if (strcmp(str, "reboot") == 0) {
151 shutting_down = SHUTDOWN_POWEROFF; /* ? */
3e2b8fbe 152 ctrl_alt_del();
0e91398f
JF
153#ifdef CONFIG_PM_SLEEP
154 } else if (strcmp(str, "suspend") == 0) {
155 do_suspend();
156#endif
157 } else {
3e2b8fbe
JF
158 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
159 shutting_down = SHUTDOWN_INVALID;
160 }
161
162 kfree(str);
163}
164
165static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
166 unsigned int len)
167{
168 char sysrq_key = '\0';
169 struct xenbus_transaction xbt;
170 int err;
171
172 again:
173 err = xenbus_transaction_start(&xbt);
174 if (err)
175 return;
176 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
177 printk(KERN_ERR "Unable to read sysrq code in "
178 "control/sysrq\n");
179 xenbus_transaction_end(xbt, 1);
180 return;
181 }
182
183 if (sysrq_key != '\0')
184 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
185
186 err = xenbus_transaction_end(xbt, 0);
187 if (err == -EAGAIN)
188 goto again;
189
190 if (sysrq_key != '\0')
191 handle_sysrq(sysrq_key, NULL);
192}
193
194static struct xenbus_watch shutdown_watch = {
195 .node = "control/shutdown",
196 .callback = shutdown_handler
197};
198
199static struct xenbus_watch sysrq_watch = {
200 .node = "control/sysrq",
201 .callback = sysrq_handler
202};
203
204static int setup_shutdown_watcher(void)
205{
206 int err;
207
208 err = register_xenbus_watch(&shutdown_watch);
209 if (err) {
210 printk(KERN_ERR "Failed to set shutdown watcher\n");
211 return err;
212 }
213
214 err = register_xenbus_watch(&sysrq_watch);
215 if (err) {
216 printk(KERN_ERR "Failed to set sysrq watcher\n");
217 return err;
218 }
219
220 return 0;
221}
222
223static int shutdown_event(struct notifier_block *notifier,
224 unsigned long event,
225 void *data)
226{
227 setup_shutdown_watcher();
228 return NOTIFY_DONE;
229}
230
231static int __init setup_shutdown_event(void)
232{
233 static struct notifier_block xenstore_notifier = {
234 .notifier_call = shutdown_event
235 };
236 register_xenstore_notifier(&xenstore_notifier);
237
238 return 0;
239}
240
241subsys_initcall(setup_shutdown_event);
This page took 0.186624 seconds and 5 git commands to generate.