xen: improve error handling in do_suspend.
[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/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                 return err;
47         }
48
49         xen_mm_pin_all();
50         gnttab_suspend();
51         xen_pre_suspend();
52
53         /*
54          * This hypercall returns 1 if suspend was cancelled
55          * or the domain was merely checkpointed, and 0 if it
56          * is resuming in a new domain.
57          */
58         *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
59
60         xen_post_suspend(*cancelled);
61         gnttab_resume();
62         xen_mm_unpin_all();
63
64         if (!*cancelled) {
65                 xen_irq_resume();
66                 xen_console_resume();
67                 xen_timer_resume();
68         }
69
70         sysdev_resume();
71
72         return 0;
73 }
74
75 static void do_suspend(void)
76 {
77         int err;
78         int cancelled = 1;
79
80         shutting_down = SHUTDOWN_SUSPEND;
81
82 #ifdef CONFIG_PREEMPT
83         /* If the kernel is preemptible, we need to freeze all the processes
84            to prevent them from being in the middle of a pagetable update
85            during suspend. */
86         err = freeze_processes();
87         if (err) {
88                 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
89                 goto out;
90         }
91 #endif
92
93         err = dpm_suspend_start(PMSG_SUSPEND);
94         if (err) {
95                 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
96                 goto out_thaw;
97         }
98
99         err = dpm_suspend_noirq(PMSG_SUSPEND);
100         if (err) {
101                 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
102                 goto out_resume;
103         }
104
105         printk(KERN_DEBUG "suspending xenstore...\n");
106         xs_suspend();
107
108         err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
109
110         dpm_resume_noirq(PMSG_RESUME);
111
112         if (err) {
113                 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
114                 cancelled = 1;
115         }
116
117         if (!cancelled) {
118                 xen_arch_resume();
119                 xs_resume();
120         } else
121                 xs_suspend_cancel();
122
123 out_resume:
124         dpm_resume_end(PMSG_RESUME);
125
126         /* Make sure timer events get retriggered on all CPUs */
127         clock_was_set();
128
129 out_thaw:
130 #ifdef CONFIG_PREEMPT
131         thaw_processes();
132 #endif
133 out:
134         shutting_down = SHUTDOWN_INVALID;
135 }
136 #endif  /* CONFIG_PM_SLEEP */
137
138 static void shutdown_handler(struct xenbus_watch *watch,
139                              const char **vec, unsigned int len)
140 {
141         char *str;
142         struct xenbus_transaction xbt;
143         int err;
144
145         if (shutting_down != SHUTDOWN_INVALID)
146                 return;
147
148  again:
149         err = xenbus_transaction_start(&xbt);
150         if (err)
151                 return;
152
153         str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
154         /* Ignore read errors and empty reads. */
155         if (XENBUS_IS_ERR_READ(str)) {
156                 xenbus_transaction_end(xbt, 1);
157                 return;
158         }
159
160         xenbus_write(xbt, "control", "shutdown", "");
161
162         err = xenbus_transaction_end(xbt, 0);
163         if (err == -EAGAIN) {
164                 kfree(str);
165                 goto again;
166         }
167
168         if (strcmp(str, "poweroff") == 0 ||
169             strcmp(str, "halt") == 0) {
170                 shutting_down = SHUTDOWN_POWEROFF;
171                 orderly_poweroff(false);
172         } else if (strcmp(str, "reboot") == 0) {
173                 shutting_down = SHUTDOWN_POWEROFF; /* ? */
174                 ctrl_alt_del();
175 #ifdef CONFIG_PM_SLEEP
176         } else if (strcmp(str, "suspend") == 0) {
177                 do_suspend();
178 #endif
179         } else {
180                 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
181                 shutting_down = SHUTDOWN_INVALID;
182         }
183
184         kfree(str);
185 }
186
187 static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
188                           unsigned int len)
189 {
190         char sysrq_key = '\0';
191         struct xenbus_transaction xbt;
192         int err;
193
194  again:
195         err = xenbus_transaction_start(&xbt);
196         if (err)
197                 return;
198         if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
199                 printk(KERN_ERR "Unable to read sysrq code in "
200                        "control/sysrq\n");
201                 xenbus_transaction_end(xbt, 1);
202                 return;
203         }
204
205         if (sysrq_key != '\0')
206                 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
207
208         err = xenbus_transaction_end(xbt, 0);
209         if (err == -EAGAIN)
210                 goto again;
211
212         if (sysrq_key != '\0')
213                 handle_sysrq(sysrq_key, NULL);
214 }
215
216 static struct xenbus_watch shutdown_watch = {
217         .node = "control/shutdown",
218         .callback = shutdown_handler
219 };
220
221 static struct xenbus_watch sysrq_watch = {
222         .node = "control/sysrq",
223         .callback = sysrq_handler
224 };
225
226 static int setup_shutdown_watcher(void)
227 {
228         int err;
229
230         err = register_xenbus_watch(&shutdown_watch);
231         if (err) {
232                 printk(KERN_ERR "Failed to set shutdown watcher\n");
233                 return err;
234         }
235
236         err = register_xenbus_watch(&sysrq_watch);
237         if (err) {
238                 printk(KERN_ERR "Failed to set sysrq watcher\n");
239                 return err;
240         }
241
242         return 0;
243 }
244
245 static int shutdown_event(struct notifier_block *notifier,
246                           unsigned long event,
247                           void *data)
248 {
249         setup_shutdown_watcher();
250         return NOTIFY_DONE;
251 }
252
253 static int __init setup_shutdown_event(void)
254 {
255         static struct notifier_block xenstore_notifier = {
256                 .notifier_call = shutdown_event
257         };
258         register_xenstore_notifier(&xenstore_notifier);
259
260         return 0;
261 }
262
263 subsys_initcall(setup_shutdown_event);