]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/xen/manage.c
Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[net-next-2.6.git] / drivers / xen / manage.c
CommitLineData
3e2b8fbe
JF
1/*
2 * Handle extern requests for shutdown, reboot and sysrq
3 */
4#include <linux/kernel.h>
5#include <linux/err.h>
5a0e3ad6 6#include <linux/slab.h>
3e2b8fbe
JF
7#include <linux/reboot.h>
8#include <linux/sysrq.h>
0e91398f
JF
9#include <linux/stop_machine.h>
10#include <linux/freezer.h>
3e2b8fbe 11
016b6f5f 12#include <xen/xen.h>
3e2b8fbe 13#include <xen/xenbus.h>
0e91398f
JF
14#include <xen/grant_table.h>
15#include <xen/events.h>
16#include <xen/hvc-console.h>
17#include <xen/xen-ops.h>
3e2b8fbe 18
0e91398f
JF
19#include <asm/xen/hypercall.h>
20#include <asm/xen/page.h>
016b6f5f 21#include <asm/xen/hypervisor.h>
0e91398f
JF
22
23enum 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};
3e2b8fbe
JF
33
34/* Ignore multiple shutdown requests. */
0e91398f
JF
35static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
36
c7827728 37#ifdef CONFIG_PM_SLEEP
016b6f5f 38static int xen_hvm_suspend(void *data)
0e91398f 39{
016b6f5f 40 struct sched_shutdown r = { .reason = SHUTDOWN_suspend };
0e91398f 41 int *cancelled = data;
016b6f5f
SS
42
43 BUG_ON(!irqs_disabled());
44
45 *cancelled = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r);
46
47 xen_hvm_post_suspend(*cancelled);
48 gnttab_resume();
49
50 if (!*cancelled) {
51 xen_irq_resume();
52 xen_timer_resume();
53 }
54
55 return 0;
56}
57
58static int xen_suspend(void *data)
59{
359cdd3f 60 int err;
016b6f5f 61 int *cancelled = data;
0e91398f
JF
62
63 BUG_ON(!irqs_disabled());
64
770824bd
RW
65 err = sysdev_suspend(PMSG_SUSPEND);
66 if (err) {
67 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
68 err);
770824bd
RW
69 return err;
70 }
359cdd3f 71
0e91398f
JF
72 xen_mm_pin_all();
73 gnttab_suspend();
0e91398f
JF
74 xen_pre_suspend();
75
76 /*
77 * This hypercall returns 1 if suspend was cancelled
78 * or the domain was merely checkpointed, and 0 if it
79 * is resuming in a new domain.
80 */
81 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
82
83 xen_post_suspend(*cancelled);
0e91398f
JF
84 gnttab_resume();
85 xen_mm_unpin_all();
86
87 if (!*cancelled) {
88 xen_irq_resume();
89 xen_console_resume();
ad55db9f 90 xen_timer_resume();
0e91398f
JF
91 }
92
1e6fcf84 93 sysdev_resume();
1e6fcf84 94
0e91398f
JF
95 return 0;
96}
97
98static void do_suspend(void)
99{
100 int err;
101 int cancelled = 1;
102
103 shutting_down = SHUTDOWN_SUSPEND;
104
105#ifdef CONFIG_PREEMPT
106 /* If the kernel is preemptible, we need to freeze all the processes
107 to prevent them from being in the middle of a pagetable update
108 during suspend. */
109 err = freeze_processes();
110 if (err) {
111 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
3fc1f1e2 112 goto out;
0e91398f
JF
113 }
114#endif
115
d1616302 116 err = dpm_suspend_start(PMSG_SUSPEND);
0e91398f 117 if (err) {
d1616302 118 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
65f63384 119 goto out_thaw;
0e91398f
JF
120 }
121
c5cae661
IC
122 printk(KERN_DEBUG "suspending xenstore...\n");
123 xs_suspend();
124
d1616302 125 err = dpm_suspend_noirq(PMSG_SUSPEND);
2ed8d2b3 126 if (err) {
d1616302 127 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
65f63384 128 goto out_resume;
2ed8d2b3
RW
129 }
130
016b6f5f
SS
131 if (xen_hvm_domain())
132 err = stop_machine(xen_hvm_suspend, &cancelled, cpumask_of(0));
133 else
134 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
922cc38a
JF
135
136 dpm_resume_noirq(PMSG_RESUME);
137
0e91398f
JF
138 if (err) {
139 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
65f63384 140 cancelled = 1;
0e91398f
JF
141 }
142
c5cae661 143out_resume:
ad55db9f
IY
144 if (!cancelled) {
145 xen_arch_resume();
de5b31bd 146 xs_resume();
ad55db9f 147 } else
de5b31bd 148 xs_suspend_cancel();
0e91398f 149
d1616302 150 dpm_resume_end(PMSG_RESUME);
0e91398f 151
359cdd3f
JF
152 /* Make sure timer events get retriggered on all CPUs */
153 clock_was_set();
65f63384
IC
154
155out_thaw:
0e91398f
JF
156#ifdef CONFIG_PREEMPT
157 thaw_processes();
65f63384 158out:
3fc1f1e2 159#endif
0e91398f
JF
160 shutting_down = SHUTDOWN_INVALID;
161}
c7827728 162#endif /* CONFIG_PM_SLEEP */
3e2b8fbe
JF
163
164static void shutdown_handler(struct xenbus_watch *watch,
165 const char **vec, unsigned int len)
166{
167 char *str;
168 struct xenbus_transaction xbt;
169 int err;
170
171 if (shutting_down != SHUTDOWN_INVALID)
172 return;
173
174 again:
175 err = xenbus_transaction_start(&xbt);
176 if (err)
177 return;
178
179 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
180 /* Ignore read errors and empty reads. */
181 if (XENBUS_IS_ERR_READ(str)) {
182 xenbus_transaction_end(xbt, 1);
183 return;
184 }
185
186 xenbus_write(xbt, "control", "shutdown", "");
187
188 err = xenbus_transaction_end(xbt, 0);
189 if (err == -EAGAIN) {
190 kfree(str);
191 goto again;
192 }
193
194 if (strcmp(str, "poweroff") == 0 ||
0e91398f
JF
195 strcmp(str, "halt") == 0) {
196 shutting_down = SHUTDOWN_POWEROFF;
3e2b8fbe 197 orderly_poweroff(false);
0e91398f
JF
198 } else if (strcmp(str, "reboot") == 0) {
199 shutting_down = SHUTDOWN_POWEROFF; /* ? */
3e2b8fbe 200 ctrl_alt_del();
0e91398f
JF
201#ifdef CONFIG_PM_SLEEP
202 } else if (strcmp(str, "suspend") == 0) {
203 do_suspend();
204#endif
205 } else {
3e2b8fbe
JF
206 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
207 shutting_down = SHUTDOWN_INVALID;
208 }
209
210 kfree(str);
211}
212
f3bc3189 213#ifdef CONFIG_MAGIC_SYSRQ
3e2b8fbe
JF
214static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
215 unsigned int len)
216{
217 char sysrq_key = '\0';
218 struct xenbus_transaction xbt;
219 int err;
220
221 again:
222 err = xenbus_transaction_start(&xbt);
223 if (err)
224 return;
225 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
226 printk(KERN_ERR "Unable to read sysrq code in "
227 "control/sysrq\n");
228 xenbus_transaction_end(xbt, 1);
229 return;
230 }
231
232 if (sysrq_key != '\0')
233 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
234
235 err = xenbus_transaction_end(xbt, 0);
236 if (err == -EAGAIN)
237 goto again;
238
239 if (sysrq_key != '\0')
f335397d 240 handle_sysrq(sysrq_key);
3e2b8fbe
JF
241}
242
3e2b8fbe
JF
243static struct xenbus_watch sysrq_watch = {
244 .node = "control/sysrq",
245 .callback = sysrq_handler
246};
f3bc3189
RD
247#endif
248
249static struct xenbus_watch shutdown_watch = {
250 .node = "control/shutdown",
251 .callback = shutdown_handler
252};
3e2b8fbe
JF
253
254static int setup_shutdown_watcher(void)
255{
256 int err;
257
258 err = register_xenbus_watch(&shutdown_watch);
259 if (err) {
260 printk(KERN_ERR "Failed to set shutdown watcher\n");
261 return err;
262 }
263
f3bc3189 264#ifdef CONFIG_MAGIC_SYSRQ
3e2b8fbe
JF
265 err = register_xenbus_watch(&sysrq_watch);
266 if (err) {
267 printk(KERN_ERR "Failed to set sysrq watcher\n");
268 return err;
269 }
f3bc3189 270#endif
3e2b8fbe
JF
271
272 return 0;
273}
274
275static int shutdown_event(struct notifier_block *notifier,
276 unsigned long event,
277 void *data)
278{
279 setup_shutdown_watcher();
280 return NOTIFY_DONE;
281}
282
016b6f5f
SS
283static int __init __setup_shutdown_event(void)
284{
285 /* Delay initialization in the PV on HVM case */
286 if (xen_hvm_domain())
287 return 0;
288
289 if (!xen_pv_domain())
290 return -ENODEV;
291
292 return xen_setup_shutdown_event();
293}
294
295int xen_setup_shutdown_event(void)
3e2b8fbe
JF
296{
297 static struct notifier_block xenstore_notifier = {
298 .notifier_call = shutdown_event
299 };
300 register_xenstore_notifier(&xenstore_notifier);
301
302 return 0;
303}
183d03cc 304EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
3e2b8fbe 305
016b6f5f 306subsys_initcall(__setup_shutdown_event);