blob: ef9c7db52077c6d58c0d5f036be0a03bd771dffa [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
Stefano Stabellini016b6f52010-05-14 12:45:07 +010012#include <xen/xen.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070013#include <xen/xenbus.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010014#include <xen/grant_table.h>
15#include <xen/events.h>
16#include <xen/hvc-console.h>
17#include <xen/xen-ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070018
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010019#include <asm/xen/hypercall.h>
20#include <asm/xen/page.h>
Stefano Stabellini016b6f52010-05-14 12:45:07 +010021#include <asm/xen/hypervisor.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010022
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};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070033
34/* Ignore multiple shutdown requests. */
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010035static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
36
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +010037#ifdef CONFIG_PM_SLEEP
Stefano Stabellini016b6f52010-05-14 12:45:07 +010038static int xen_hvm_suspend(void *data)
39{
40 struct sched_shutdown r = { .reason = SHUTDOWN_suspend };
41 int *cancelled = data;
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
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010058static int xen_suspend(void *data)
59{
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010060 int err;
Stefano Stabellini016b6f52010-05-14 12:45:07 +010061 int *cancelled = data;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010062
63 BUG_ON(!irqs_disabled());
64
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010065 err = sysdev_suspend(PMSG_SUSPEND);
66 if (err) {
67 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
68 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010069 return err;
70 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010071
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010072 xen_mm_pin_all();
73 gnttab_suspend();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010074 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);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010084 gnttab_resume();
85 xen_mm_unpin_all();
86
87 if (!*cancelled) {
88 xen_irq_resume();
89 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070090 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010091 }
92
Ian Campbell1e6fcf82009-03-25 17:46:42 +000093 sysdev_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +000094
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010095 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);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200112 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100113 }
114#endif
115
Alan Sternd1616302009-05-24 22:05:42 +0200116 err = dpm_suspend_start(PMSG_SUSPEND);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100117 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200118 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000119 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100120 }
121
Ian Campbellc5cae662009-12-17 13:57:09 +0000122 printk(KERN_DEBUG "suspending xenstore...\n");
123 xs_suspend();
124
Alan Sternd1616302009-05-24 22:05:42 +0200125 err = dpm_suspend_noirq(PMSG_SUSPEND);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100126 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200127 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000128 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100129 }
130
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100131 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));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800135
136 dpm_resume_noirq(PMSG_RESUME);
137
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100138 if (err) {
139 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000140 cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100141 }
142
Ian Campbellc5cae662009-12-17 13:57:09 +0000143out_resume:
Isaku Yamahataad55db92008-07-08 15:06:32 -0700144 if (!cancelled) {
145 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800146 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700147 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800148 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100149
Alan Sternd1616302009-05-24 22:05:42 +0200150 dpm_resume_end(PMSG_RESUME);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100151
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100152 /* Make sure timer events get retriggered on all CPUs */
153 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000154
155out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100156#ifdef CONFIG_PREEMPT
157 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000158out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200159#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100160 shutting_down = SHUTDOWN_INVALID;
161}
Jeremy Fitzhardingec7827722008-05-29 09:02:19 +0100162#endif /* CONFIG_PM_SLEEP */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700163
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 ||
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100195 strcmp(str, "halt") == 0) {
196 shutting_down = SHUTDOWN_POWEROFF;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700197 orderly_poweroff(false);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100198 } else if (strcmp(str, "reboot") == 0) {
199 shutting_down = SHUTDOWN_POWEROFF; /* ? */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700200 ctrl_alt_del();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100201#ifdef CONFIG_PM_SLEEP
202 } else if (strcmp(str, "suspend") == 0) {
203 do_suspend();
204#endif
205 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700206 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
207 shutting_down = SHUTDOWN_INVALID;
208 }
209
210 kfree(str);
211}
212
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700213#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700214static 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')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700240 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700241}
242
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700243static struct xenbus_watch sysrq_watch = {
244 .node = "control/sysrq",
245 .callback = sysrq_handler
246};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700247#endif
248
249static struct xenbus_watch shutdown_watch = {
250 .node = "control/shutdown",
251 .callback = shutdown_handler
252};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700253
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
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700264#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700265 err = register_xenbus_watch(&sysrq_watch);
266 if (err) {
267 printk(KERN_ERR "Failed to set sysrq watcher\n");
268 return err;
269 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700270#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700271
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
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100283static 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)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700296{
297 static struct notifier_block xenstore_notifier = {
298 .notifier_call = shutdown_event
299 };
300 register_xenstore_notifier(&xenstore_notifier);
301
302 return 0;
303}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100304EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700305
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100306subsys_initcall(__setup_shutdown_event);