blob: 412b96cc5305746c20d554b1a6d2c7e55ab09686 [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>
Rafael J. Wysocki19234c02011-04-20 00:36:11 +020011#include <linux/syscore_ops.h>
Paul Gortmaker63c97442011-07-10 13:22:07 -040012#include <linux/export.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070013
Stefano Stabellini016b6f52010-05-14 12:45:07 +010014#include <xen/xen.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070015#include <xen/xenbus.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010016#include <xen/grant_table.h>
17#include <xen/events.h>
18#include <xen/hvc-console.h>
19#include <xen/xen-ops.h>
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070020
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010021#include <asm/xen/hypercall.h>
22#include <asm/xen/page.h>
Stefano Stabellini016b6f52010-05-14 12:45:07 +010023#include <asm/xen/hypervisor.h>
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010024
25enum shutdown_state {
26 SHUTDOWN_INVALID = -1,
27 SHUTDOWN_POWEROFF = 0,
28 SHUTDOWN_SUSPEND = 2,
29 /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
30 report a crash, not be instructed to crash!
31 HALT is the same as POWEROFF, as far as we're concerned. The tools use
32 the distinction when we return the reason code to them. */
33 SHUTDOWN_HALT = 4,
34};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -070035
36/* Ignore multiple shutdown requests. */
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010037static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
38
Ian Campbellceb18022011-02-17 11:04:20 +000039struct suspend_info {
40 int cancelled;
Ian Campbell36b401e2011-02-17 11:04:20 +000041 unsigned long arg; /* extra hypercall argument */
Ian Campbell55fb4ac2011-02-17 11:04:20 +000042 void (*pre)(void);
43 void (*post)(int cancelled);
Ian Campbellceb18022011-02-17 11:04:20 +000044};
45
Ian Campbell07af3812011-02-17 11:04:20 +000046static void xen_hvm_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000047{
Ian Campbell07af3812011-02-17 11:04:20 +000048 xen_arch_hvm_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000049 gnttab_resume();
50}
51
52static void xen_pre_suspend(void)
53{
54 xen_mm_pin_all();
55 gnttab_suspend();
Ian Campbell07af3812011-02-17 11:04:20 +000056 xen_arch_pre_suspend();
Ian Campbell82043bb2011-02-17 11:04:20 +000057}
58
Ian Campbell07af3812011-02-17 11:04:20 +000059static void xen_post_suspend(int cancelled)
Ian Campbell82043bb2011-02-17 11:04:20 +000060{
Ian Campbell07af3812011-02-17 11:04:20 +000061 xen_arch_post_suspend(cancelled);
Ian Campbell82043bb2011-02-17 11:04:20 +000062 gnttab_resume();
63 xen_mm_unpin_all();
64}
65
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +020066#ifdef CONFIG_HIBERNATE_CALLBACKS
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010067static int xen_suspend(void *data)
68{
Ian Campbellceb18022011-02-17 11:04:20 +000069 struct suspend_info *si = data;
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010070 int err;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010071
72 BUG_ON(!irqs_disabled());
73
Rafael J. Wysocki2e711c02011-04-26 19:15:07 +020074 err = syscore_suspend();
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010075 if (err) {
Rafael J. Wysocki19234c02011-04-20 00:36:11 +020076 printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n",
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010077 err);
Rafael J. Wysocki770824b2009-02-22 18:38:50 +010078 return err;
79 }
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +010080
Ian Campbell55fb4ac2011-02-17 11:04:20 +000081 if (si->pre)
82 si->pre();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010083
84 /*
85 * This hypercall returns 1 if suspend was cancelled
86 * or the domain was merely checkpointed, and 0 if it
87 * is resuming in a new domain.
88 */
Ian Campbell36b401e2011-02-17 11:04:20 +000089 si->cancelled = HYPERVISOR_suspend(si->arg);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010090
Ian Campbell55fb4ac2011-02-17 11:04:20 +000091 if (si->post)
92 si->post(si->cancelled);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010093
Ian Campbellceb18022011-02-17 11:04:20 +000094 if (!si->cancelled) {
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010095 xen_irq_resume();
96 xen_console_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -070097 xen_timer_resume();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +010098 }
99
Rafael J. Wysocki19234c02011-04-20 00:36:11 +0200100 syscore_resume();
Ian Campbell1e6fcf82009-03-25 17:46:42 +0000101
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100102 return 0;
103}
104
105static void do_suspend(void)
106{
107 int err;
Ian Campbellceb18022011-02-17 11:04:20 +0000108 struct suspend_info si;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100109
110 shutting_down = SHUTDOWN_SUSPEND;
111
112#ifdef CONFIG_PREEMPT
113 /* If the kernel is preemptible, we need to freeze all the processes
114 to prevent them from being in the middle of a pagetable update
115 during suspend. */
116 err = freeze_processes();
117 if (err) {
118 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200119 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100120 }
121#endif
122
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800123 err = dpm_suspend_start(PMSG_FREEZE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100124 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200125 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000126 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100127 }
128
Ian Campbellc5cae662009-12-17 13:57:09 +0000129 printk(KERN_DEBUG "suspending xenstore...\n");
130 xs_suspend();
131
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100132 err = dpm_suspend_end(PMSG_FREEZE);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100133 if (err) {
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100134 printk(KERN_ERR "dpm_suspend_end failed: %d\n", err);
Konrad Rzeszutek Wilk186bab12012-04-17 14:35:49 -0400135 si.cancelled = 0;
Ian Campbell65f63382009-12-01 11:47:14 +0000136 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100137 }
138
Ian Campbellceb18022011-02-17 11:04:20 +0000139 si.cancelled = 1;
140
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000141 if (xen_hvm_domain()) {
Ian Campbell36b401e2011-02-17 11:04:20 +0000142 si.arg = 0UL;
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000143 si.pre = NULL;
144 si.post = &xen_hvm_post_suspend;
145 } else {
Ian Campbell36b401e2011-02-17 11:04:20 +0000146 si.arg = virt_to_mfn(xen_start_info);
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000147 si.pre = &xen_pre_suspend;
148 si.post = &xen_post_suspend;
149 }
Ian Campbell36b401e2011-02-17 11:04:20 +0000150
Ian Campbellb056b6a2011-02-17 11:04:20 +0000151 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800152
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100153 dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800154
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100155 if (err) {
156 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000157 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100158 }
159
Ian Campbellc5cae662009-12-17 13:57:09 +0000160out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000161 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700162 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800163 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700164 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800165 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100166
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800167 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100168
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100169 /* Make sure timer events get retriggered on all CPUs */
170 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000171
172out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100173#ifdef CONFIG_PREEMPT
174 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000175out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200176#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100177 shutting_down = SHUTDOWN_INVALID;
178}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200179#endif /* CONFIG_HIBERNATE_CALLBACKS */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700180
Ian Campbell55271722011-02-17 11:04:20 +0000181struct shutdown_handler {
182 const char *command;
183 void (*cb)(void);
184};
185
186static void do_poweroff(void)
187{
188 shutting_down = SHUTDOWN_POWEROFF;
189 orderly_poweroff(false);
190}
191
192static void do_reboot(void)
193{
194 shutting_down = SHUTDOWN_POWEROFF; /* ? */
195 ctrl_alt_del();
196}
197
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700198static void shutdown_handler(struct xenbus_watch *watch,
199 const char **vec, unsigned int len)
200{
201 char *str;
202 struct xenbus_transaction xbt;
203 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000204 static struct shutdown_handler handlers[] = {
205 { "poweroff", do_poweroff },
206 { "halt", do_poweroff },
207 { "reboot", do_reboot },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200208#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000209 { "suspend", do_suspend },
210#endif
211 {NULL, NULL},
212 };
213 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700214
215 if (shutting_down != SHUTDOWN_INVALID)
216 return;
217
218 again:
219 err = xenbus_transaction_start(&xbt);
220 if (err)
221 return;
222
223 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
224 /* Ignore read errors and empty reads. */
225 if (XENBUS_IS_ERR_READ(str)) {
226 xenbus_transaction_end(xbt, 1);
227 return;
228 }
229
Ian Campbell55271722011-02-17 11:04:20 +0000230 for (handler = &handlers[0]; handler->command; handler++) {
231 if (strcmp(str, handler->command) == 0)
232 break;
233 }
234
235 /* Only acknowledge commands which we are prepared to handle. */
236 if (handler->cb)
237 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700238
239 err = xenbus_transaction_end(xbt, 0);
240 if (err == -EAGAIN) {
241 kfree(str);
242 goto again;
243 }
244
Ian Campbell55271722011-02-17 11:04:20 +0000245 if (handler->cb) {
246 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100247 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700248 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
249 shutting_down = SHUTDOWN_INVALID;
250 }
251
252 kfree(str);
253}
254
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700255#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700256static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
257 unsigned int len)
258{
259 char sysrq_key = '\0';
260 struct xenbus_transaction xbt;
261 int err;
262
263 again:
264 err = xenbus_transaction_start(&xbt);
265 if (err)
266 return;
267 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
268 printk(KERN_ERR "Unable to read sysrq code in "
269 "control/sysrq\n");
270 xenbus_transaction_end(xbt, 1);
271 return;
272 }
273
274 if (sysrq_key != '\0')
275 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
276
277 err = xenbus_transaction_end(xbt, 0);
278 if (err == -EAGAIN)
279 goto again;
280
281 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700282 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700283}
284
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700285static struct xenbus_watch sysrq_watch = {
286 .node = "control/sysrq",
287 .callback = sysrq_handler
288};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700289#endif
290
291static struct xenbus_watch shutdown_watch = {
292 .node = "control/shutdown",
293 .callback = shutdown_handler
294};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700295
296static int setup_shutdown_watcher(void)
297{
298 int err;
299
300 err = register_xenbus_watch(&shutdown_watch);
301 if (err) {
302 printk(KERN_ERR "Failed to set shutdown watcher\n");
303 return err;
304 }
305
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700306#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700307 err = register_xenbus_watch(&sysrq_watch);
308 if (err) {
309 printk(KERN_ERR "Failed to set sysrq watcher\n");
310 return err;
311 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700312#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700313
314 return 0;
315}
316
317static int shutdown_event(struct notifier_block *notifier,
318 unsigned long event,
319 void *data)
320{
321 setup_shutdown_watcher();
322 return NOTIFY_DONE;
323}
324
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100325int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700326{
327 static struct notifier_block xenstore_notifier = {
328 .notifier_call = shutdown_event
329 };
Stefano Stabellini702d4eb2010-12-02 17:54:50 +0000330
331 if (!xen_domain())
332 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700333 register_xenstore_notifier(&xenstore_notifier);
334
335 return 0;
336}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100337EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700338
Stefano Stabellini702d4eb2010-12-02 17:54:50 +0000339subsys_initcall(xen_setup_shutdown_event);