]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - drivers/xen/manage.c
07e857b0de13958e974181ff76f9da11e1af7c7e
[linux-2.6.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/xenbus.h>
13 #include <xen/grant_table.h>
14 #include <xen/events.h>
15 #include <xen/hvc-console.h>
16 #include <xen/xen-ops.h>
17
18 #include <asm/xen/hypercall.h>
19 #include <asm/xen/page.h>
20
21 enum shutdown_state {
22         SHUTDOWN_INVALID = -1,
23         SHUTDOWN_POWEROFF = 0,
24         SHUTDOWN_SUSPEND = 2,
25         /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
26            report a crash, not be instructed to crash!
27            HALT is the same as POWEROFF, as far as we're concerned.  The tools use
28            the distinction when we return the reason code to them.  */
29          SHUTDOWN_HALT = 4,
30 };
31
32 /* Ignore multiple shutdown requests. */
33 static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
34
35 #ifdef CONFIG_PM_SLEEP
36 static int xen_suspend(void *data)
37 {
38         int *cancelled = data;
39         int err;
40
41         BUG_ON(!irqs_disabled());
42
43         err = sysdev_suspend(PMSG_SUSPEND);
44         if (err) {
45                 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
46                         err);
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         if (!*cancelled) {
66                 xen_irq_resume();
67                 xen_console_resume();
68                 xen_timer_resume();
69         }
70
71         sysdev_resume();
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                 goto out;
91         }
92 #endif
93
94         err = dpm_suspend_start(PMSG_SUSPEND);
95         if (err) {
96                 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
97                 goto out_thaw;
98         }
99
100         printk(KERN_DEBUG "suspending xenstore...\n");
101         xs_suspend();
102
103         err = dpm_suspend_noirq(PMSG_SUSPEND);
104         if (err) {
105                 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
106                 goto out_resume;
107         }
108
109         err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
110
111         dpm_resume_noirq(PMSG_RESUME);
112
113         if (err) {
114                 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
115                 cancelled = 1;
116         }
117
118 out_resume:
119         if (!cancelled) {
120                 xen_arch_resume();
121                 xs_resume();
122         } else
123                 xs_suspend_cancel();
124
125         dpm_resume_end(PMSG_RESUME);
126
127         /* Make sure timer events get retriggered on all CPUs */
128         clock_was_set();
129
130 out_thaw:
131 #ifdef CONFIG_PREEMPT
132         thaw_processes();
133 out:
134 #endif
135         shutting_down = SHUTDOWN_INVALID;
136 }
137 #endif  /* CONFIG_PM_SLEEP */
138
139 static void shutdown_handler(struct xenbus_watch *watch,
140                              const char **vec, unsigned int len)
141 {
142         char *str;
143         struct xenbus_transaction xbt;
144         int err;
145
146         if (shutting_down != SHUTDOWN_INVALID)
147                 return;
148
149  again:
150         err = xenbus_transaction_start(&xbt);
151         if (err)
152                 return;
153
154         str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
155         /* Ignore read errors and empty reads. */
156         if (XENBUS_IS_ERR_READ(str)) {
157                 xenbus_transaction_end(xbt, 1);
158                 return;
159         }
160
161         xenbus_write(xbt, "control", "shutdown", "");
162
163         err = xenbus_transaction_end(xbt, 0);
164         if (err == -EAGAIN) {
165                 kfree(str);
166                 goto again;
167         }
168
169         if (strcmp(str, "poweroff") == 0 ||
170             strcmp(str, "halt") == 0) {
171                 shutting_down = SHUTDOWN_POWEROFF;
172                 orderly_poweroff(false);
173         } else if (strcmp(str, "reboot") == 0) {
174                 shutting_down = SHUTDOWN_POWEROFF; /* ? */
175                 ctrl_alt_del();
176 #ifdef CONFIG_PM_SLEEP
177         } else if (strcmp(str, "suspend") == 0) {
178                 do_suspend();
179 #endif
180         } else {
181                 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
182                 shutting_down = SHUTDOWN_INVALID;
183         }
184
185         kfree(str);
186 }
187
188 #ifdef CONFIG_MAGIC_SYSRQ
189 static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
190                           unsigned int len)
191 {
192         char sysrq_key = '\0';
193         struct xenbus_transaction xbt;
194         int err;
195
196  again:
197         err = xenbus_transaction_start(&xbt);
198         if (err)
199                 return;
200         if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
201                 printk(KERN_ERR "Unable to read sysrq code in "
202                        "control/sysrq\n");
203                 xenbus_transaction_end(xbt, 1);
204                 return;
205         }
206
207         if (sysrq_key != '\0')
208                 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
209
210         err = xenbus_transaction_end(xbt, 0);
211         if (err == -EAGAIN)
212                 goto again;
213
214         if (sysrq_key != '\0')
215                 handle_sysrq(sysrq_key, NULL);
216 }
217
218 static struct xenbus_watch sysrq_watch = {
219         .node = "control/sysrq",
220         .callback = sysrq_handler
221 };
222 #endif
223
224 static struct xenbus_watch shutdown_watch = {
225         .node = "control/shutdown",
226         .callback = shutdown_handler
227 };
228
229 static int setup_shutdown_watcher(void)
230 {
231         int err;
232
233         err = register_xenbus_watch(&shutdown_watch);
234         if (err) {
235                 printk(KERN_ERR "Failed to set shutdown watcher\n");
236                 return err;
237         }
238
239 #ifdef CONFIG_MAGIC_SYSRQ
240         err = register_xenbus_watch(&sysrq_watch);
241         if (err) {
242                 printk(KERN_ERR "Failed to set sysrq watcher\n");
243                 return err;
244         }
245 #endif
246
247         return 0;
248 }
249
250 static int shutdown_event(struct notifier_block *notifier,
251                           unsigned long event,
252                           void *data)
253 {
254         setup_shutdown_watcher();
255         return NOTIFY_DONE;
256 }
257
258 static int __init setup_shutdown_event(void)
259 {
260         static struct notifier_block xenstore_notifier = {
261                 .notifier_call = shutdown_event
262         };
263         register_xenstore_notifier(&xenstore_notifier);
264
265         return 0;
266 }
267
268 subsys_initcall(setup_shutdown_event);