blob: 5269bb4d2496930862605f1f664d311f97815b1f [file] [log] [blame]
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -07001/*
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>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +01008#include <linux/stop_machine.h>
9#include <linux/freezer.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070010
11#include <xen/xenbus.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010012#include <xen/grant_table.h>
13#include <xen/events.h>
14#include <xen/hvc-console.h>
15#include <xen/xen-ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070016
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010017#include <asm/xen/hypercall.h>
18#include <asm/xen/page.h>
19
20enum 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};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070030
31/* Ignore multiple shutdown requests. */
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010032static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
33
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010034#ifdef CONFIG_PM_SLEEP
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010035static int xen_suspend(void *data)
36{
37 int *cancelled = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010038 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010039
40 BUG_ON(!irqs_disabled());
41
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010042 err = device_power_down(PMSG_SUSPEND);
43 if (err) {
44 printk(KERN_ERR "xen_suspend: device_power_down failed: %d\n",
45 err);
46 return err;
47 }
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010048 err = sysdev_suspend(PMSG_SUSPEND);
49 if (err) {
50 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
51 err);
52 device_power_up(PMSG_RESUME);
53 return err;
54 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010055
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010056 xen_mm_pin_all();
57 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010058 xen_pre_suspend();
59
60 /*
61 * This hypercall returns 1 if suspend was cancelled
62 * or the domain was merely checkpointed, and 0 if it
63 * is resuming in a new domain.
64 */
65 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
66
67 xen_post_suspend(*cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010068 gnttab_resume();
69 xen_mm_unpin_all();
70
71 if (!*cancelled) {
72 xen_irq_resume();
73 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070074 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010075 }
76
Ian Campbell1e6fcf82009-03-25 17:46:42 +000077 sysdev_resume();
78 device_power_up(PMSG_RESUME);
79
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010080 return 0;
81}
82
83static void do_suspend(void)
84{
85 int err;
86 int cancelled = 1;
87
88 shutting_down = SHUTDOWN_SUSPEND;
89
90#ifdef CONFIG_PREEMPT
91 /* If the kernel is preemptible, we need to freeze all the processes
92 to prevent them from being in the middle of a pagetable update
93 during suspend. */
94 err = freeze_processes();
95 if (err) {
96 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
97 return;
98 }
99#endif
100
101 err = device_suspend(PMSG_SUSPEND);
102 if (err) {
103 printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
104 goto out;
105 }
106
Ian Campbellde5b31b2009-02-09 12:05:50 -0800107 printk(KERN_DEBUG "suspending xenstore...\n");
108 xs_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100109
Rusty Russellf7df8ed2009-01-10 21:58:09 -0800110 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100111 if (err) {
112 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
113 goto out;
114 }
115
Isaku Yamahataad55db92008-07-08 15:06:32 -0700116 if (!cancelled) {
117 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800118 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700119 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800120 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100121
Stephen Rothwell55ca0892008-07-17 13:09:24 +1000122 device_resume(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100123
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100124 /* Make sure timer events get retriggered on all CPUs */
125 clock_was_set();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100126out:
127#ifdef CONFIG_PREEMPT
128 thaw_processes();
129#endif
130 shutting_down = SHUTDOWN_INVALID;
131}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100132#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700133
134static void shutdown_handler(struct xenbus_watch *watch,
135 const char **vec, unsigned int len)
136{
137 char *str;
138 struct xenbus_transaction xbt;
139 int err;
140
141 if (shutting_down != SHUTDOWN_INVALID)
142 return;
143
144 again:
145 err = xenbus_transaction_start(&xbt);
146 if (err)
147 return;
148
149 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
150 /* Ignore read errors and empty reads. */
151 if (XENBUS_IS_ERR_READ(str)) {
152 xenbus_transaction_end(xbt, 1);
153 return;
154 }
155
156 xenbus_write(xbt, "control", "shutdown", "");
157
158 err = xenbus_transaction_end(xbt, 0);
159 if (err == -EAGAIN) {
160 kfree(str);
161 goto again;
162 }
163
164 if (strcmp(str, "poweroff") == 0 ||
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100165 strcmp(str, "halt") == 0) {
166 shutting_down = SHUTDOWN_POWEROFF;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700167 orderly_poweroff(false);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100168 } else if (strcmp(str, "reboot") == 0) {
169 shutting_down = SHUTDOWN_POWEROFF; /* ? */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700170 ctrl_alt_del();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100171#ifdef CONFIG_PM_SLEEP
172 } else if (strcmp(str, "suspend") == 0) {
173 do_suspend();
174#endif
175 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700176 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
177 shutting_down = SHUTDOWN_INVALID;
178 }
179
180 kfree(str);
181}
182
183static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
184 unsigned int len)
185{
186 char sysrq_key = '\0';
187 struct xenbus_transaction xbt;
188 int err;
189
190 again:
191 err = xenbus_transaction_start(&xbt);
192 if (err)
193 return;
194 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
195 printk(KERN_ERR "Unable to read sysrq code in "
196 "control/sysrq\n");
197 xenbus_transaction_end(xbt, 1);
198 return;
199 }
200
201 if (sysrq_key != '\0')
202 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
203
204 err = xenbus_transaction_end(xbt, 0);
205 if (err == -EAGAIN)
206 goto again;
207
208 if (sysrq_key != '\0')
209 handle_sysrq(sysrq_key, NULL);
210}
211
212static struct xenbus_watch shutdown_watch = {
213 .node = "control/shutdown",
214 .callback = shutdown_handler
215};
216
217static struct xenbus_watch sysrq_watch = {
218 .node = "control/sysrq",
219 .callback = sysrq_handler
220};
221
222static int setup_shutdown_watcher(void)
223{
224 int err;
225
226 err = register_xenbus_watch(&shutdown_watch);
227 if (err) {
228 printk(KERN_ERR "Failed to set shutdown watcher\n");
229 return err;
230 }
231
232 err = register_xenbus_watch(&sysrq_watch);
233 if (err) {
234 printk(KERN_ERR "Failed to set sysrq watcher\n");
235 return err;
236 }
237
238 return 0;
239}
240
241static int shutdown_event(struct notifier_block *notifier,
242 unsigned long event,
243 void *data)
244{
245 setup_shutdown_watcher();
246 return NOTIFY_DONE;
247}
248
249static int __init setup_shutdown_event(void)
250{
251 static struct notifier_block xenstore_notifier = {
252 .notifier_call = shutdown_event
253 };
254 register_xenstore_notifier(&xenstore_notifier);
255
256 return 0;
257}
258
259subsys_initcall(setup_shutdown_event);