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