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