]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/xen/manage.c
xen: suspend: pass extra hypercall argument via suspend_info struct
[mv-sheeva.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 struct suspend_info {
38         int cancelled;
39         unsigned long arg; /* extra hypercall argument */
40 };
41
42 #ifdef CONFIG_PM_SLEEP
43 static int xen_hvm_suspend(void *data)
44 {
45         struct suspend_info *si = data;
46         int err;
47
48         BUG_ON(!irqs_disabled());
49
50         err = sysdev_suspend(PMSG_SUSPEND);
51         if (err) {
52                 printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
53                        err);
54                 return err;
55         }
56
57         /*
58          * This hypercall returns 1 if suspend was cancelled
59          * or the domain was merely checkpointed, and 0 if it
60          * is resuming in a new domain.
61          */
62         si->cancelled = HYPERVISOR_suspend(si->arg);
63
64         xen_hvm_post_suspend(si->cancelled);
65         gnttab_resume();
66
67         if (!si->cancelled) {
68                 xen_irq_resume();
69                 xen_console_resume();
70                 xen_timer_resume();
71         }
72
73         sysdev_resume();
74
75         return 0;
76 }
77
78 static int xen_suspend(void *data)
79 {
80         struct suspend_info *si = data;
81         int err;
82
83         BUG_ON(!irqs_disabled());
84
85         err = sysdev_suspend(PMSG_SUSPEND);
86         if (err) {
87                 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
88                         err);
89                 return err;
90         }
91
92         xen_mm_pin_all();
93         gnttab_suspend();
94         xen_pre_suspend();
95
96         /*
97          * This hypercall returns 1 if suspend was cancelled
98          * or the domain was merely checkpointed, and 0 if it
99          * is resuming in a new domain.
100          */
101         si->cancelled = HYPERVISOR_suspend(si->arg);
102
103         xen_post_suspend(si->cancelled);
104         gnttab_resume();
105         xen_mm_unpin_all();
106
107         if (!si->cancelled) {
108                 xen_irq_resume();
109                 xen_console_resume();
110                 xen_timer_resume();
111         }
112
113         sysdev_resume();
114
115         return 0;
116 }
117
118 static void do_suspend(void)
119 {
120         int err;
121         struct suspend_info si;
122
123         shutting_down = SHUTDOWN_SUSPEND;
124
125 #ifdef CONFIG_PREEMPT
126         /* If the kernel is preemptible, we need to freeze all the processes
127            to prevent them from being in the middle of a pagetable update
128            during suspend. */
129         err = freeze_processes();
130         if (err) {
131                 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
132                 goto out;
133         }
134 #endif
135
136         err = dpm_suspend_start(PMSG_SUSPEND);
137         if (err) {
138                 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
139                 goto out_thaw;
140         }
141
142         printk(KERN_DEBUG "suspending xenstore...\n");
143         xs_suspend();
144
145         err = dpm_suspend_noirq(PMSG_SUSPEND);
146         if (err) {
147                 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
148                 goto out_resume;
149         }
150
151         si.cancelled = 1;
152
153         if (xen_hvm_domain())
154                 si.arg = 0UL;
155         else
156                 si.arg = virt_to_mfn(xen_start_info);
157
158         if (xen_hvm_domain())
159                 err = stop_machine(xen_hvm_suspend, &si, cpumask_of(0));
160         else
161                 err = stop_machine(xen_suspend, &si, cpumask_of(0));
162
163         dpm_resume_noirq(PMSG_RESUME);
164
165         if (err) {
166                 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
167                 si.cancelled = 1;
168         }
169
170 out_resume:
171         if (!si.cancelled) {
172                 xen_arch_resume();
173                 xs_resume();
174         } else
175                 xs_suspend_cancel();
176
177         dpm_resume_end(PMSG_RESUME);
178
179         /* Make sure timer events get retriggered on all CPUs */
180         clock_was_set();
181
182 out_thaw:
183 #ifdef CONFIG_PREEMPT
184         thaw_processes();
185 out:
186 #endif
187         shutting_down = SHUTDOWN_INVALID;
188 }
189 #endif  /* CONFIG_PM_SLEEP */
190
191 struct shutdown_handler {
192         const char *command;
193         void (*cb)(void);
194 };
195
196 static void do_poweroff(void)
197 {
198         shutting_down = SHUTDOWN_POWEROFF;
199         orderly_poweroff(false);
200 }
201
202 static void do_reboot(void)
203 {
204         shutting_down = SHUTDOWN_POWEROFF; /* ? */
205         ctrl_alt_del();
206 }
207
208 static void shutdown_handler(struct xenbus_watch *watch,
209                              const char **vec, unsigned int len)
210 {
211         char *str;
212         struct xenbus_transaction xbt;
213         int err;
214         static struct shutdown_handler handlers[] = {
215                 { "poweroff",   do_poweroff },
216                 { "halt",       do_poweroff },
217                 { "reboot",     do_reboot   },
218 #ifdef CONFIG_PM_SLEEP
219                 { "suspend",    do_suspend  },
220 #endif
221                 {NULL, NULL},
222         };
223         static struct shutdown_handler *handler;
224
225         if (shutting_down != SHUTDOWN_INVALID)
226                 return;
227
228  again:
229         err = xenbus_transaction_start(&xbt);
230         if (err)
231                 return;
232
233         str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
234         /* Ignore read errors and empty reads. */
235         if (XENBUS_IS_ERR_READ(str)) {
236                 xenbus_transaction_end(xbt, 1);
237                 return;
238         }
239
240         for (handler = &handlers[0]; handler->command; handler++) {
241                 if (strcmp(str, handler->command) == 0)
242                         break;
243         }
244
245         /* Only acknowledge commands which we are prepared to handle. */
246         if (handler->cb)
247                 xenbus_write(xbt, "control", "shutdown", "");
248
249         err = xenbus_transaction_end(xbt, 0);
250         if (err == -EAGAIN) {
251                 kfree(str);
252                 goto again;
253         }
254
255         if (handler->cb) {
256                 handler->cb();
257         } else {
258                 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
259                 shutting_down = SHUTDOWN_INVALID;
260         }
261
262         kfree(str);
263 }
264
265 #ifdef CONFIG_MAGIC_SYSRQ
266 static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
267                           unsigned int len)
268 {
269         char sysrq_key = '\0';
270         struct xenbus_transaction xbt;
271         int err;
272
273  again:
274         err = xenbus_transaction_start(&xbt);
275         if (err)
276                 return;
277         if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
278                 printk(KERN_ERR "Unable to read sysrq code in "
279                        "control/sysrq\n");
280                 xenbus_transaction_end(xbt, 1);
281                 return;
282         }
283
284         if (sysrq_key != '\0')
285                 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
286
287         err = xenbus_transaction_end(xbt, 0);
288         if (err == -EAGAIN)
289                 goto again;
290
291         if (sysrq_key != '\0')
292                 handle_sysrq(sysrq_key);
293 }
294
295 static struct xenbus_watch sysrq_watch = {
296         .node = "control/sysrq",
297         .callback = sysrq_handler
298 };
299 #endif
300
301 static struct xenbus_watch shutdown_watch = {
302         .node = "control/shutdown",
303         .callback = shutdown_handler
304 };
305
306 static int setup_shutdown_watcher(void)
307 {
308         int err;
309
310         err = register_xenbus_watch(&shutdown_watch);
311         if (err) {
312                 printk(KERN_ERR "Failed to set shutdown watcher\n");
313                 return err;
314         }
315
316 #ifdef CONFIG_MAGIC_SYSRQ
317         err = register_xenbus_watch(&sysrq_watch);
318         if (err) {
319                 printk(KERN_ERR "Failed to set sysrq watcher\n");
320                 return err;
321         }
322 #endif
323
324         return 0;
325 }
326
327 static int shutdown_event(struct notifier_block *notifier,
328                           unsigned long event,
329                           void *data)
330 {
331         setup_shutdown_watcher();
332         return NOTIFY_DONE;
333 }
334
335 int xen_setup_shutdown_event(void)
336 {
337         static struct notifier_block xenstore_notifier = {
338                 .notifier_call = shutdown_event
339         };
340
341         if (!xen_domain())
342                 return -ENODEV;
343         register_xenstore_notifier(&xenstore_notifier);
344
345         return 0;
346 }
347 EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
348
349 subsys_initcall(xen_setup_shutdown_event);