MIPS: Alchemy: DB1200: Remove custom wait implementation
[linux-2.6/linux-mips.git] / drivers / xen / manage.c
blob2ac4440e7b087c1a9ac9c0b087d09a12be3f3a2e
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>
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>
18 #include <asm/xen/hypercall.h>
19 #include <asm/xen/page.h>
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,
32 /* Ignore multiple shutdown requests. */
33 static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
35 #ifdef CONFIG_PM_SLEEP
36 static int xen_suspend(void *data)
38 int *cancelled = data;
39 int err;
41 BUG_ON(!irqs_disabled());
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;
50 xen_mm_pin_all();
51 gnttab_suspend();
52 xen_pre_suspend();
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.
59 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
61 xen_post_suspend(*cancelled);
62 gnttab_resume();
63 xen_mm_unpin_all();
65 if (!*cancelled) {
66 xen_irq_resume();
67 xen_console_resume();
68 xen_timer_resume();
71 sysdev_resume();
73 return 0;
76 static void do_suspend(void)
78 int err;
79 int cancelled = 1;
81 shutting_down = SHUTDOWN_SUSPEND;
83 err = stop_machine_create();
84 if (err) {
85 printk(KERN_ERR "xen suspend: failed to setup stop_machine %d\n", err);
86 goto out;
89 #ifdef CONFIG_PREEMPT
90 /* If the kernel is preemptible, we need to freeze all the processes
91 to prevent them from being in the middle of a pagetable update
92 during suspend. */
93 err = freeze_processes();
94 if (err) {
95 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
96 goto out_destroy_sm;
98 #endif
100 err = dpm_suspend_start(PMSG_SUSPEND);
101 if (err) {
102 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
103 goto out_thaw;
106 printk(KERN_DEBUG "suspending xenstore...\n");
107 xs_suspend();
109 err = dpm_suspend_noirq(PMSG_SUSPEND);
110 if (err) {
111 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
112 goto out_resume;
115 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
117 dpm_resume_noirq(PMSG_RESUME);
119 if (err) {
120 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
121 cancelled = 1;
124 out_resume:
125 if (!cancelled) {
126 xen_arch_resume();
127 xs_resume();
128 } else
129 xs_suspend_cancel();
131 dpm_resume_end(PMSG_RESUME);
133 /* Make sure timer events get retriggered on all CPUs */
134 clock_was_set();
136 out_thaw:
137 #ifdef CONFIG_PREEMPT
138 thaw_processes();
140 out_destroy_sm:
141 #endif
142 stop_machine_destroy();
144 out:
145 shutting_down = SHUTDOWN_INVALID;
147 #endif /* CONFIG_PM_SLEEP */
149 static void shutdown_handler(struct xenbus_watch *watch,
150 const char **vec, unsigned int len)
152 char *str;
153 struct xenbus_transaction xbt;
154 int err;
156 if (shutting_down != SHUTDOWN_INVALID)
157 return;
159 again:
160 err = xenbus_transaction_start(&xbt);
161 if (err)
162 return;
164 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
165 /* Ignore read errors and empty reads. */
166 if (XENBUS_IS_ERR_READ(str)) {
167 xenbus_transaction_end(xbt, 1);
168 return;
171 xenbus_write(xbt, "control", "shutdown", "");
173 err = xenbus_transaction_end(xbt, 0);
174 if (err == -EAGAIN) {
175 kfree(str);
176 goto again;
179 if (strcmp(str, "poweroff") == 0 ||
180 strcmp(str, "halt") == 0) {
181 shutting_down = SHUTDOWN_POWEROFF;
182 orderly_poweroff(false);
183 } else if (strcmp(str, "reboot") == 0) {
184 shutting_down = SHUTDOWN_POWEROFF; /* ? */
185 ctrl_alt_del();
186 #ifdef CONFIG_PM_SLEEP
187 } else if (strcmp(str, "suspend") == 0) {
188 do_suspend();
189 #endif
190 } else {
191 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
192 shutting_down = SHUTDOWN_INVALID;
195 kfree(str);
198 static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
199 unsigned int len)
201 char sysrq_key = '\0';
202 struct xenbus_transaction xbt;
203 int err;
205 again:
206 err = xenbus_transaction_start(&xbt);
207 if (err)
208 return;
209 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
210 printk(KERN_ERR "Unable to read sysrq code in "
211 "control/sysrq\n");
212 xenbus_transaction_end(xbt, 1);
213 return;
216 if (sysrq_key != '\0')
217 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
219 err = xenbus_transaction_end(xbt, 0);
220 if (err == -EAGAIN)
221 goto again;
223 if (sysrq_key != '\0')
224 handle_sysrq(sysrq_key, NULL);
227 static struct xenbus_watch shutdown_watch = {
228 .node = "control/shutdown",
229 .callback = shutdown_handler
232 static struct xenbus_watch sysrq_watch = {
233 .node = "control/sysrq",
234 .callback = sysrq_handler
237 static int setup_shutdown_watcher(void)
239 int err;
241 err = register_xenbus_watch(&shutdown_watch);
242 if (err) {
243 printk(KERN_ERR "Failed to set shutdown watcher\n");
244 return err;
247 err = register_xenbus_watch(&sysrq_watch);
248 if (err) {
249 printk(KERN_ERR "Failed to set sysrq watcher\n");
250 return err;
253 return 0;
256 static int shutdown_event(struct notifier_block *notifier,
257 unsigned long event,
258 void *data)
260 setup_shutdown_watcher();
261 return NOTIFY_DONE;
264 static int __init setup_shutdown_event(void)
266 static struct notifier_block xenstore_notifier = {
267 .notifier_call = shutdown_event
269 register_xenstore_notifier(&xenstore_notifier);
271 return 0;
274 subsys_initcall(setup_shutdown_event);