blob: 2ac4440e7b087c1a9ac9c0b087d09a12be3f3a2e [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09006#include <linux/slab.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -07007#include <linux/reboot.h>
8#include <linux/sysrq.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +01009#include <linux/stop_machine.h>
10#include <linux/freezer.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070011
12#include <xen/xenbus.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010013#include <xen/grant_table.h>
14#include <xen/events.h>
15#include <xen/hvc-console.h>
16#include <xen/xen-ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070017
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010018#include <asm/xen/hypercall.h>
19#include <asm/xen/page.h>
20
21enum 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,
30};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070031
32/* Ignore multiple shutdown requests. */
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010033static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
34
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010035#ifdef CONFIG_PM_SLEEP
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010036static int xen_suspend(void *data)
37{
38 int *cancelled = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010039 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010040
41 BUG_ON(!irqs_disabled());
42
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010043 err = sysdev_suspend(PMSG_SUSPEND);
44 if (err) {
45 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
46 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010047 return err;
48 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010049
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010050 xen_mm_pin_all();
51 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010052 xen_pre_suspend();
53
54 /*
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.
58 */
59 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
60
61 xen_post_suspend(*cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010062 gnttab_resume();
63 xen_mm_unpin_all();
64
65 if (!*cancelled) {
66 xen_irq_resume();
67 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070068 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010069 }
70
Ian Campbell1e6fcf82009-03-25 17:46:42 +000071 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +000072
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010073 return 0;
74}
75
76static void do_suspend(void)
77{
78 int err;
79 int cancelled = 1;
80
81 shutting_down = SHUTDOWN_SUSPEND;
82
Ian Campbellb4606f22009-12-01 11:47:15 +000083 err = stop_machine_create();
84 if (err) {
85 printk(KERN_ERR "xen suspend: failed to setup stop_machine %d\n", err);
86 goto out;
87 }
88
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010089#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);
Ian Campbellb4606f22009-12-01 11:47:15 +000096 goto out_destroy_sm;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010097 }
98#endif
99
Alan Sternd1616302009-05-24 22:05:42 +0200100 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100101 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200102 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000103 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100104 }
105
Ian Campbellc5cae662009-12-17 13:57:09 +0000106 printk(KERN_DEBUG "suspending xenstore...\n");
107 xs_suspend();
108
Alan Sternd1616302009-05-24 22:05:42 +0200109 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100110 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200111 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000112 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100113 }
114
Rusty Russellf7df8ed2009-01-10 21:58:09 -0800115 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800116
117 dpm_resume_noirq(PMSG_RESUME);
118
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100119 if (err) {
120 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000121 cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100122 }
123
Ian Campbellc5cae662009-12-17 13:57:09 +0000124out_resume:
Isaku Yamahataad55db92008-07-08 15:06:32 -0700125 if (!cancelled) {
126 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800127 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700128 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800129 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100130
Alan Sternd1616302009-05-24 22:05:42 +0200131 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100132
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100133 /* Make sure timer events get retriggered on all CPUs */
134 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000135
136out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100137#ifdef CONFIG_PREEMPT
138 thaw_processes();
Ian Campbellb4606f22009-12-01 11:47:15 +0000139
140out_destroy_sm:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100141#endif
Ian Campbellb4606f22009-12-01 11:47:15 +0000142 stop_machine_destroy();
143
Ian Campbell65f63382009-12-01 11:47:14 +0000144out:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100145 shutting_down = SHUTDOWN_INVALID;
146}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100147#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700148
149static void shutdown_handler(struct xenbus_watch *watch,
150 const char **vec, unsigned int len)
151{
152 char *str;
153 struct xenbus_transaction xbt;
154 int err;
155
156 if (shutting_down != SHUTDOWN_INVALID)
157 return;
158
159 again:
160 err = xenbus_transaction_start(&xbt);
161 if (err)
162 return;
163
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;
169 }
170
171 xenbus_write(xbt, "control", "shutdown", "");
172
173 err = xenbus_transaction_end(xbt, 0);
174 if (err == -EAGAIN) {
175 kfree(str);
176 goto again;
177 }
178
179 if (strcmp(str, "poweroff") == 0 ||
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100180 strcmp(str, "halt") == 0) {
181 shutting_down = SHUTDOWN_POWEROFF;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700182 orderly_poweroff(false);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100183 } else if (strcmp(str, "reboot") == 0) {
184 shutting_down = SHUTDOWN_POWEROFF; /* ? */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700185 ctrl_alt_del();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100186#ifdef CONFIG_PM_SLEEP
187 } else if (strcmp(str, "suspend") == 0) {
188 do_suspend();
189#endif
190 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700191 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
192 shutting_down = SHUTDOWN_INVALID;
193 }
194
195 kfree(str);
196}
197
198static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
199 unsigned int len)
200{
201 char sysrq_key = '\0';
202 struct xenbus_transaction xbt;
203 int err;
204
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;
214 }
215
216 if (sysrq_key != '\0')
217 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
218
219 err = xenbus_transaction_end(xbt, 0);
220 if (err == -EAGAIN)
221 goto again;
222
223 if (sysrq_key != '\0')
224 handle_sysrq(sysrq_key, NULL);
225}
226
227static struct xenbus_watch shutdown_watch = {
228 .node = "control/shutdown",
229 .callback = shutdown_handler
230};
231
232static struct xenbus_watch sysrq_watch = {
233 .node = "control/sysrq",
234 .callback = sysrq_handler
235};
236
237static int setup_shutdown_watcher(void)
238{
239 int err;
240
241 err = register_xenbus_watch(&shutdown_watch);
242 if (err) {
243 printk(KERN_ERR "Failed to set shutdown watcher\n");
244 return err;
245 }
246
247 err = register_xenbus_watch(&sysrq_watch);
248 if (err) {
249 printk(KERN_ERR "Failed to set sysrq watcher\n");
250 return err;
251 }
252
253 return 0;
254}
255
256static int shutdown_event(struct notifier_block *notifier,
257 unsigned long event,
258 void *data)
259{
260 setup_shutdown_watcher();
261 return NOTIFY_DONE;
262}
263
264static int __init setup_shutdown_event(void)
265{
266 static struct notifier_block xenstore_notifier = {
267 .notifier_call = shutdown_event
268 };
269 register_xenstore_notifier(&xenstore_notifier);
270
271 return 0;
272}
273
274subsys_initcall(setup_shutdown_event);