]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/xen/manage.c
xen: don't call dpm_resume_noirq() with interrupts disabled.
[karo-tx-linux.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                 return;
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;
97         }
98
99         printk(KERN_DEBUG "suspending xenstore...\n");
100         xs_suspend();
101
102         err = dpm_suspend_noirq(PMSG_SUSPEND);
103         if (err) {
104                 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
105                 goto resume_devices;
106         }
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                 goto out;
115         }
116
117         if (!cancelled) {
118                 xen_arch_resume();
119                 xs_resume();
120         } else
121                 xs_suspend_cancel();
122
123 resume_devices:
124         dpm_resume_end(PMSG_RESUME);
125
126         /* Make sure timer events get retriggered on all CPUs */
127         clock_was_set();
128 out:
129 #ifdef CONFIG_PREEMPT
130         thaw_processes();
131 #endif
132         shutting_down = SHUTDOWN_INVALID;
133 }
134 #endif  /* CONFIG_PM_SLEEP */
135
136 static void shutdown_handler(struct xenbus_watch *watch,
137                              const char **vec, unsigned int len)
138 {
139         char *str;
140         struct xenbus_transaction xbt;
141         int err;
142
143         if (shutting_down != SHUTDOWN_INVALID)
144                 return;
145
146  again:
147         err = xenbus_transaction_start(&xbt);
148         if (err)
149                 return;
150
151         str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
152         /* Ignore read errors and empty reads. */
153         if (XENBUS_IS_ERR_READ(str)) {
154                 xenbus_transaction_end(xbt, 1);
155                 return;
156         }
157
158         xenbus_write(xbt, "control", "shutdown", "");
159
160         err = xenbus_transaction_end(xbt, 0);
161         if (err == -EAGAIN) {
162                 kfree(str);
163                 goto again;
164         }
165
166         if (strcmp(str, "poweroff") == 0 ||
167             strcmp(str, "halt") == 0) {
168                 shutting_down = SHUTDOWN_POWEROFF;
169                 orderly_poweroff(false);
170         } else if (strcmp(str, "reboot") == 0) {
171                 shutting_down = SHUTDOWN_POWEROFF; /* ? */
172                 ctrl_alt_del();
173 #ifdef CONFIG_PM_SLEEP
174         } else if (strcmp(str, "suspend") == 0) {
175                 do_suspend();
176 #endif
177         } else {
178                 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
179                 shutting_down = SHUTDOWN_INVALID;
180         }
181
182         kfree(str);
183 }
184
185 static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
186                           unsigned int len)
187 {
188         char sysrq_key = '\0';
189         struct xenbus_transaction xbt;
190         int err;
191
192  again:
193         err = xenbus_transaction_start(&xbt);
194         if (err)
195                 return;
196         if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
197                 printk(KERN_ERR "Unable to read sysrq code in "
198                        "control/sysrq\n");
199                 xenbus_transaction_end(xbt, 1);
200                 return;
201         }
202
203         if (sysrq_key != '\0')
204                 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
205
206         err = xenbus_transaction_end(xbt, 0);
207         if (err == -EAGAIN)
208                 goto again;
209
210         if (sysrq_key != '\0')
211                 handle_sysrq(sysrq_key, NULL);
212 }
213
214 static struct xenbus_watch shutdown_watch = {
215         .node = "control/shutdown",
216         .callback = shutdown_handler
217 };
218
219 static struct xenbus_watch sysrq_watch = {
220         .node = "control/sysrq",
221         .callback = sysrq_handler
222 };
223
224 static int setup_shutdown_watcher(void)
225 {
226         int err;
227
228         err = register_xenbus_watch(&shutdown_watch);
229         if (err) {
230                 printk(KERN_ERR "Failed to set shutdown watcher\n");
231                 return err;
232         }
233
234         err = register_xenbus_watch(&sysrq_watch);
235         if (err) {
236                 printk(KERN_ERR "Failed to set sysrq watcher\n");
237                 return err;
238         }
239
240         return 0;
241 }
242
243 static int shutdown_event(struct notifier_block *notifier,
244                           unsigned long event,
245                           void *data)
246 {
247         setup_shutdown_watcher();
248         return NOTIFY_DONE;
249 }
250
251 static int __init setup_shutdown_event(void)
252 {
253         static struct notifier_block xenstore_notifier = {
254                 .notifier_call = shutdown_event
255         };
256         register_xenstore_notifier(&xenstore_notifier);
257
258         return 0;
259 }
260
261 subsys_initcall(setup_shutdown_event);