Merge branch 'master' of git://git.infradead.org/users/cbou/linux-cns3xxx into devel...
[pandora-kernel.git] / 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
12 #include <xen/xen.h>
13 #include <xen/xenbus.h>
14 #include <xen/grant_table.h>
15 #include <xen/events.h>
16 #include <xen/hvc-console.h>
17 #include <xen/xen-ops.h>
18
19 #include <asm/xen/hypercall.h>
20 #include <asm/xen/page.h>
21 #include <asm/xen/hypervisor.h>
22
23 enum 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 };
33
34 /* Ignore multiple shutdown requests. */
35 static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
36
37 #ifdef CONFIG_PM_SLEEP
38 static 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();
52                 xen_console_resume();
53                 xen_timer_resume();
54         }
55
56         return 0;
57 }
58
59 static int xen_suspend(void *data)
60 {
61         int err;
62         int *cancelled = data;
63
64         BUG_ON(!irqs_disabled());
65
66         err = sysdev_suspend(PMSG_SUSPEND);
67         if (err) {
68                 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
69                         err);
70                 return err;
71         }
72
73         xen_mm_pin_all();
74         gnttab_suspend();
75         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);
85         gnttab_resume();
86         xen_mm_unpin_all();
87
88         if (!*cancelled) {
89                 xen_irq_resume();
90                 xen_console_resume();
91                 xen_timer_resume();
92         }
93
94         sysdev_resume();
95
96         return 0;
97 }
98
99 static 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);
113                 goto out;
114         }
115 #endif
116
117         err = dpm_suspend_start(PMSG_SUSPEND);
118         if (err) {
119                 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
120                 goto out_thaw;
121         }
122
123         printk(KERN_DEBUG "suspending xenstore...\n");
124         xs_suspend();
125
126         err = dpm_suspend_noirq(PMSG_SUSPEND);
127         if (err) {
128                 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
129                 goto out_resume;
130         }
131
132         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));
136
137         dpm_resume_noirq(PMSG_RESUME);
138
139         if (err) {
140                 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
141                 cancelled = 1;
142         }
143
144 out_resume:
145         if (!cancelled) {
146                 xen_arch_resume();
147                 xs_resume();
148         } else
149                 xs_suspend_cancel();
150
151         dpm_resume_end(PMSG_RESUME);
152
153         /* Make sure timer events get retriggered on all CPUs */
154         clock_was_set();
155
156 out_thaw:
157 #ifdef CONFIG_PREEMPT
158         thaw_processes();
159 out:
160 #endif
161         shutting_down = SHUTDOWN_INVALID;
162 }
163 #endif  /* CONFIG_PM_SLEEP */
164
165 static 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 ||
196             strcmp(str, "halt") == 0) {
197                 shutting_down = SHUTDOWN_POWEROFF;
198                 orderly_poweroff(false);
199         } else if (strcmp(str, "reboot") == 0) {
200                 shutting_down = SHUTDOWN_POWEROFF; /* ? */
201                 ctrl_alt_del();
202 #ifdef CONFIG_PM_SLEEP
203         } else if (strcmp(str, "suspend") == 0) {
204                 do_suspend();
205 #endif
206         } else {
207                 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
208                 shutting_down = SHUTDOWN_INVALID;
209         }
210
211         kfree(str);
212 }
213
214 #ifdef CONFIG_MAGIC_SYSRQ
215 static 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')
241                 handle_sysrq(sysrq_key);
242 }
243
244 static struct xenbus_watch sysrq_watch = {
245         .node = "control/sysrq",
246         .callback = sysrq_handler
247 };
248 #endif
249
250 static struct xenbus_watch shutdown_watch = {
251         .node = "control/shutdown",
252         .callback = shutdown_handler
253 };
254
255 static 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
265 #ifdef CONFIG_MAGIC_SYSRQ
266         err = register_xenbus_watch(&sysrq_watch);
267         if (err) {
268                 printk(KERN_ERR "Failed to set sysrq watcher\n");
269                 return err;
270         }
271 #endif
272
273         return 0;
274 }
275
276 static 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
284 static 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
296 int xen_setup_shutdown_event(void)
297 {
298         static struct notifier_block xenstore_notifier = {
299                 .notifier_call = shutdown_event
300         };
301         register_xenstore_notifier(&xenstore_notifier);
302
303         return 0;
304 }
305 EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
306
307 subsys_initcall(__setup_shutdown_event);