blob: 8da1ed1cf3c85113f80ab6667dd47caa0093b5fb [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
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100112 err = freeze_processes();
113 if (err) {
114 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200115 goto out;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100116 }
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100117
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800118 err = dpm_suspend_start(PMSG_FREEZE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100119 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200120 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000121 goto out_thaw;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100122 }
123
Ian Campbellc5cae662009-12-17 13:57:09 +0000124 printk(KERN_DEBUG "suspending xenstore...\n");
125 xs_suspend();
126
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100127 err = dpm_suspend_end(PMSG_FREEZE);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100128 if (err) {
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100129 printk(KERN_ERR "dpm_suspend_end failed: %d\n", err);
Konrad Rzeszutek Wilk186bab12012-04-17 14:35:49 -0400130 si.cancelled = 0;
Ian Campbell65f63382009-12-01 11:47:14 +0000131 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100132 }
133
Ian Campbellceb18022011-02-17 11:04:20 +0000134 si.cancelled = 1;
135
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000136 if (xen_hvm_domain()) {
Ian Campbell36b401e2011-02-17 11:04:20 +0000137 si.arg = 0UL;
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000138 si.pre = NULL;
139 si.post = &xen_hvm_post_suspend;
140 } else {
Ian Campbell36b401e2011-02-17 11:04:20 +0000141 si.arg = virt_to_mfn(xen_start_info);
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000142 si.pre = &xen_pre_suspend;
143 si.post = &xen_post_suspend;
144 }
Ian Campbell36b401e2011-02-17 11:04:20 +0000145
Ian Campbellb056b6a2011-02-17 11:04:20 +0000146 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800147
Rafael J. Wysockicf579df2012-01-29 20:38:29 +0100148 dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800149
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100150 if (err) {
151 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000152 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100153 }
154
Ian Campbellc5cae662009-12-17 13:57:09 +0000155out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000156 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700157 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800158 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700159 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800160 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100161
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800162 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100163
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100164 /* Make sure timer events get retriggered on all CPUs */
165 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000166
167out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100168 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000169out:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100170 shutting_down = SHUTDOWN_INVALID;
171}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200172#endif /* CONFIG_HIBERNATE_CALLBACKS */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700173
Ian Campbell55271722011-02-17 11:04:20 +0000174struct shutdown_handler {
175 const char *command;
176 void (*cb)(void);
177};
178
179static void do_poweroff(void)
180{
181 shutting_down = SHUTDOWN_POWEROFF;
182 orderly_poweroff(false);
183}
184
185static void do_reboot(void)
186{
187 shutting_down = SHUTDOWN_POWEROFF; /* ? */
188 ctrl_alt_del();
189}
190
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700191static void shutdown_handler(struct xenbus_watch *watch,
192 const char **vec, unsigned int len)
193{
194 char *str;
195 struct xenbus_transaction xbt;
196 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000197 static struct shutdown_handler handlers[] = {
198 { "poweroff", do_poweroff },
199 { "halt", do_poweroff },
200 { "reboot", do_reboot },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200201#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000202 { "suspend", do_suspend },
203#endif
204 {NULL, NULL},
205 };
206 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700207
208 if (shutting_down != SHUTDOWN_INVALID)
209 return;
210
211 again:
212 err = xenbus_transaction_start(&xbt);
213 if (err)
214 return;
215
216 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
217 /* Ignore read errors and empty reads. */
218 if (XENBUS_IS_ERR_READ(str)) {
219 xenbus_transaction_end(xbt, 1);
220 return;
221 }
222
Ian Campbell55271722011-02-17 11:04:20 +0000223 for (handler = &handlers[0]; handler->command; handler++) {
224 if (strcmp(str, handler->command) == 0)
225 break;
226 }
227
228 /* Only acknowledge commands which we are prepared to handle. */
229 if (handler->cb)
230 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700231
232 err = xenbus_transaction_end(xbt, 0);
233 if (err == -EAGAIN) {
234 kfree(str);
235 goto again;
236 }
237
Ian Campbell55271722011-02-17 11:04:20 +0000238 if (handler->cb) {
239 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100240 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700241 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
242 shutting_down = SHUTDOWN_INVALID;
243 }
244
245 kfree(str);
246}
247
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700248#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700249static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
250 unsigned int len)
251{
252 char sysrq_key = '\0';
253 struct xenbus_transaction xbt;
254 int err;
255
256 again:
257 err = xenbus_transaction_start(&xbt);
258 if (err)
259 return;
260 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
261 printk(KERN_ERR "Unable to read sysrq code in "
262 "control/sysrq\n");
263 xenbus_transaction_end(xbt, 1);
264 return;
265 }
266
267 if (sysrq_key != '\0')
268 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
269
270 err = xenbus_transaction_end(xbt, 0);
271 if (err == -EAGAIN)
272 goto again;
273
274 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700275 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700276}
277
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700278static struct xenbus_watch sysrq_watch = {
279 .node = "control/sysrq",
280 .callback = sysrq_handler
281};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700282#endif
283
284static struct xenbus_watch shutdown_watch = {
285 .node = "control/shutdown",
286 .callback = shutdown_handler
287};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700288
289static int setup_shutdown_watcher(void)
290{
291 int err;
292
293 err = register_xenbus_watch(&shutdown_watch);
294 if (err) {
295 printk(KERN_ERR "Failed to set shutdown watcher\n");
296 return err;
297 }
298
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700299#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700300 err = register_xenbus_watch(&sysrq_watch);
301 if (err) {
302 printk(KERN_ERR "Failed to set sysrq watcher\n");
303 return err;
304 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700305#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700306
307 return 0;
308}
309
310static int shutdown_event(struct notifier_block *notifier,
311 unsigned long event,
312 void *data)
313{
314 setup_shutdown_watcher();
315 return NOTIFY_DONE;
316}
317
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100318int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700319{
320 static struct notifier_block xenstore_notifier = {
321 .notifier_call = shutdown_event
322 };
Stefano Stabellini702d4eb2010-12-02 17:54:50 +0000323
324 if (!xen_domain())
325 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700326 register_xenstore_notifier(&xenstore_notifier);
327
328 return 0;
329}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100330EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700331
Stefano Stabellini702d4eb2010-12-02 17:54:50 +0000332subsys_initcall(xen_setup_shutdown_event);