blob: ce4fa0831860175dedd4b53d3cd22e168474f7b9 [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
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800132 err = dpm_suspend_noirq(PMSG_FREEZE);
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100133 if (err) {
Alan Sternd1616302009-05-24 22:05:42 +0200134 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
Ian Campbell65f63382009-12-01 11:47:14 +0000135 goto out_resume;
Rafael J. Wysocki2ed8d2b2009-03-16 22:34:06 +0100136 }
137
Ian Campbellceb18022011-02-17 11:04:20 +0000138 si.cancelled = 1;
139
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000140 if (xen_hvm_domain()) {
Ian Campbell36b401e2011-02-17 11:04:20 +0000141 si.arg = 0UL;
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000142 si.pre = NULL;
143 si.post = &xen_hvm_post_suspend;
144 } else {
Ian Campbell36b401e2011-02-17 11:04:20 +0000145 si.arg = virt_to_mfn(xen_start_info);
Ian Campbell55fb4ac2011-02-17 11:04:20 +0000146 si.pre = &xen_pre_suspend;
147 si.post = &xen_post_suspend;
148 }
Ian Campbell36b401e2011-02-17 11:04:20 +0000149
Ian Campbellb056b6a2011-02-17 11:04:20 +0000150 err = stop_machine(xen_suspend, &si, cpumask_of(0));
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800151
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800152 dpm_resume_noirq(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge922cc38a2009-11-24 09:58:49 -0800153
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100154 if (err) {
155 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
Ian Campbellceb18022011-02-17 11:04:20 +0000156 si.cancelled = 1;
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100157 }
158
Ian Campbellc5cae662009-12-17 13:57:09 +0000159out_resume:
Ian Campbellceb18022011-02-17 11:04:20 +0000160 if (!si.cancelled) {
Isaku Yamahataad55db92008-07-08 15:06:32 -0700161 xen_arch_resume();
Ian Campbellde5b31b2009-02-09 12:05:50 -0800162 xs_resume();
Isaku Yamahataad55db92008-07-08 15:06:32 -0700163 } else
Ian Campbellde5b31b2009-02-09 12:05:50 -0800164 xs_suspend_cancel();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100165
Shriram Rajagopalanb3e96c02011-02-22 14:59:06 -0800166 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100167
Jeremy Fitzhardinge359cdd32008-05-26 23:31:28 +0100168 /* Make sure timer events get retriggered on all CPUs */
169 clock_was_set();
Ian Campbell65f63382009-12-01 11:47:14 +0000170
171out_thaw:
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100172#ifdef CONFIG_PREEMPT
173 thaw_processes();
Ian Campbell65f63382009-12-01 11:47:14 +0000174out:
Tejun Heo3fc1f1e2010-05-06 18:49:20 +0200175#endif
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100176 shutting_down = SHUTDOWN_INVALID;
177}
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200178#endif /* CONFIG_HIBERNATE_CALLBACKS */
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700179
Ian Campbell55271722011-02-17 11:04:20 +0000180struct shutdown_handler {
181 const char *command;
182 void (*cb)(void);
183};
184
185static void do_poweroff(void)
186{
187 shutting_down = SHUTDOWN_POWEROFF;
188 orderly_poweroff(false);
189}
190
191static void do_reboot(void)
192{
193 shutting_down = SHUTDOWN_POWEROFF; /* ? */
194 ctrl_alt_del();
195}
196
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700197static void shutdown_handler(struct xenbus_watch *watch,
198 const char **vec, unsigned int len)
199{
200 char *str;
201 struct xenbus_transaction xbt;
202 int err;
Ian Campbell55271722011-02-17 11:04:20 +0000203 static struct shutdown_handler handlers[] = {
204 { "poweroff", do_poweroff },
205 { "halt", do_poweroff },
206 { "reboot", do_reboot },
Rafael J. Wysocki1f112ce2011-04-11 22:54:42 +0200207#ifdef CONFIG_HIBERNATE_CALLBACKS
Ian Campbell55271722011-02-17 11:04:20 +0000208 { "suspend", do_suspend },
209#endif
210 {NULL, NULL},
211 };
212 static struct shutdown_handler *handler;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700213
214 if (shutting_down != SHUTDOWN_INVALID)
215 return;
216
217 again:
218 err = xenbus_transaction_start(&xbt);
219 if (err)
220 return;
221
222 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
223 /* Ignore read errors and empty reads. */
224 if (XENBUS_IS_ERR_READ(str)) {
225 xenbus_transaction_end(xbt, 1);
226 return;
227 }
228
Ian Campbell55271722011-02-17 11:04:20 +0000229 for (handler = &handlers[0]; handler->command; handler++) {
230 if (strcmp(str, handler->command) == 0)
231 break;
232 }
233
234 /* Only acknowledge commands which we are prepared to handle. */
235 if (handler->cb)
236 xenbus_write(xbt, "control", "shutdown", "");
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700237
238 err = xenbus_transaction_end(xbt, 0);
239 if (err == -EAGAIN) {
240 kfree(str);
241 goto again;
242 }
243
Ian Campbell55271722011-02-17 11:04:20 +0000244 if (handler->cb) {
245 handler->cb();
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100246 } else {
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700247 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
248 shutting_down = SHUTDOWN_INVALID;
249 }
250
251 kfree(str);
252}
253
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700254#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700255static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
256 unsigned int len)
257{
258 char sysrq_key = '\0';
259 struct xenbus_transaction xbt;
260 int err;
261
262 again:
263 err = xenbus_transaction_start(&xbt);
264 if (err)
265 return;
266 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
267 printk(KERN_ERR "Unable to read sysrq code in "
268 "control/sysrq\n");
269 xenbus_transaction_end(xbt, 1);
270 return;
271 }
272
273 if (sysrq_key != '\0')
274 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
275
276 err = xenbus_transaction_end(xbt, 0);
277 if (err == -EAGAIN)
278 goto again;
279
280 if (sysrq_key != '\0')
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700281 handle_sysrq(sysrq_key);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700282}
283
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700284static struct xenbus_watch sysrq_watch = {
285 .node = "control/sysrq",
286 .callback = sysrq_handler
287};
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700288#endif
289
290static struct xenbus_watch shutdown_watch = {
291 .node = "control/shutdown",
292 .callback = shutdown_handler
293};
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700294
295static int setup_shutdown_watcher(void)
296{
297 int err;
298
299 err = register_xenbus_watch(&shutdown_watch);
300 if (err) {
301 printk(KERN_ERR "Failed to set shutdown watcher\n");
302 return err;
303 }
304
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700305#ifdef CONFIG_MAGIC_SYSRQ
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700306 err = register_xenbus_watch(&sysrq_watch);
307 if (err) {
308 printk(KERN_ERR "Failed to set sysrq watcher\n");
309 return err;
310 }
Randy Dunlapf3bc3182010-05-24 14:33:41 -0700311#endif
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700312
313 return 0;
314}
315
316static int shutdown_event(struct notifier_block *notifier,
317 unsigned long event,
318 void *data)
319{
320 setup_shutdown_watcher();
321 return NOTIFY_DONE;
322}
323
Stefano Stabellini016b6f52010-05-14 12:45:07 +0100324int xen_setup_shutdown_event(void)
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700325{
326 static struct notifier_block xenstore_notifier = {
327 .notifier_call = shutdown_event
328 };
Stefano Stabellini702d4eb2010-12-02 17:54:50 +0000329
330 if (!xen_domain())
331 return -ENODEV;
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700332 register_xenstore_notifier(&xenstore_notifier);
333
334 return 0;
335}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100336EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
Jeremy Fitzhardinge3e2b8fb2007-07-17 18:37:07 -0700337
Stefano Stabellini702d4eb2010-12-02 17:54:50 +0000338subsys_initcall(xen_setup_shutdown_event);