blob: 7b69a1aef877a81a547f51aa5000b53ea73623d9 [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
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010042 err = sysdev_suspend(PMSG_SUSPEND);
43 if (err) {
44 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
45 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010046 return err;
47 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010048
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010049 xen_mm_pin_all();
50 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010051 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);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010061 gnttab_resume();
62 xen_mm_unpin_all();
63
64 if (!*cancelled) {
65 xen_irq_resume();
66 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070067 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010068 }
69
Ian Campbell1e6fcf82009-03-25 17:46:42 +000070 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +000071
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010072 return 0;
73}
74
75static 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
Alan Sternd1616302009-05-24 22:05:42 +020093 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010094 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +020095 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010096 goto out;
97 }
98
Ian Campbellde5b31b2009-02-09 12:05:50 -080099 printk(KERN_DEBUG "suspending xenstore...\n");
100 xs_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100101
Alan Sternd1616302009-05-24 22:05:42 +0200102 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100103 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200104 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100105 goto resume_devices;
106 }
107
Rusty Russellf7df8ed2009-01-10 21:58:09 -0800108 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800109
110 dpm_resume_noirq(PMSG_RESUME);
111
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100112 if (err) {
113 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
114 goto out;
115 }
116
Isaku Yamahataad55db92008-07-08 15:06:32 -0700117 if (!cancelled) {
118 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800119 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700120 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800121 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100122
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100123resume_devices:
Alan Sternd1616302009-05-24 22:05:42 +0200124 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100125
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100126 /* Make sure timer events get retriggered on all CPUs */
127 clock_was_set();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100128out:
129#ifdef CONFIG_PREEMPT
130 thaw_processes();
131#endif
132 shutting_down = SHUTDOWN_INVALID;
133}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100134#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700135
136static 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 ||
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100167 strcmp(str, "halt") == 0) {
168 shutting_down = SHUTDOWN_POWEROFF;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700169 orderly_poweroff(false);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100170 } else if (strcmp(str, "reboot") == 0) {
171 shutting_down = SHUTDOWN_POWEROFF; /* ? */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700172 ctrl_alt_del();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100173#ifdef CONFIG_PM_SLEEP
174 } else if (strcmp(str, "suspend") == 0) {
175 do_suspend();
176#endif
177 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700178 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
179 shutting_down = SHUTDOWN_INVALID;
180 }
181
182 kfree(str);
183}
184
185static 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
214static struct xenbus_watch shutdown_watch = {
215 .node = "control/shutdown",
216 .callback = shutdown_handler
217};
218
219static struct xenbus_watch sysrq_watch = {
220 .node = "control/sysrq",
221 .callback = sysrq_handler
222};
223
224static 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
243static 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
251static 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
261subsys_initcall(setup_shutdown_event);