]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/xen/manage.c
xen: don't leak IRQs over suspend/resume.
[mirror_ubuntu-bionic-kernel.git] / drivers / xen / manage.c
CommitLineData
3e2b8fbe
JF
1/*
2 * Handle extern requests for shutdown, reboot and sysrq
3 */
4#include <linux/kernel.h>
5#include <linux/err.h>
6#include <linux/reboot.h>
7#include <linux/sysrq.h>
0e91398f
JF
8#include <linux/stop_machine.h>
9#include <linux/freezer.h>
3e2b8fbe
JF
10
11#include <xen/xenbus.h>
0e91398f
JF
12#include <xen/grant_table.h>
13#include <xen/events.h>
14#include <xen/hvc-console.h>
15#include <xen/xen-ops.h>
3e2b8fbe 16
0e91398f
JF
17#include <asm/xen/hypercall.h>
18#include <asm/xen/page.h>
19
20enum shutdown_state {
21 SHUTDOWN_INVALID = -1,
22 SHUTDOWN_POWEROFF = 0,
23 SHUTDOWN_SUSPEND = 2,
24 /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
25 report a crash, not be instructed to crash!
26 HALT is the same as POWEROFF, as far as we're concerned. The tools use
27 the distinction when we return the reason code to them. */
28 SHUTDOWN_HALT = 4,
29};
3e2b8fbe
JF
30
31/* Ignore multiple shutdown requests. */
0e91398f
JF
32static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
33
c7827728 34#ifdef CONFIG_PM_SLEEP
0e91398f
JF
35static int xen_suspend(void *data)
36{
37 int *cancelled = data;
359cdd3f 38 int err;
0e91398f
JF
39
40 BUG_ON(!irqs_disabled());
41
770824bd
RW
42 err = sysdev_suspend(PMSG_SUSPEND);
43 if (err) {
44 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
45 err);
770824bd
RW
46 return err;
47 }
359cdd3f 48
0e91398f
JF
49 xen_mm_pin_all();
50 gnttab_suspend();
0e91398f
JF
51 xen_pre_suspend();
52
53 /*
54 * This hypercall returns 1 if suspend was cancelled
55 * or the domain was merely checkpointed, and 0 if it
56 * is resuming in a new domain.
57 */
58 *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
59
60 xen_post_suspend(*cancelled);
0e91398f
JF
61 gnttab_resume();
62 xen_mm_unpin_all();
63
64 if (!*cancelled) {
65 xen_irq_resume();
66 xen_console_resume();
ad55db9f 67 xen_timer_resume();
0e91398f
JF
68 }
69
1e6fcf84 70 sysdev_resume();
1e6fcf84 71
0e91398f
JF
72 return 0;
73}
74
75static void do_suspend(void)
76{
77 int err;
78 int cancelled = 1;
79
80 shutting_down = SHUTDOWN_SUSPEND;
81
82#ifdef CONFIG_PREEMPT
83 /* If the kernel is preemptible, we need to freeze all the processes
84 to prevent them from being in the middle of a pagetable update
85 during suspend. */
86 err = freeze_processes();
87 if (err) {
88 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
89 return;
90 }
91#endif
92
d1616302 93 err = dpm_suspend_start(PMSG_SUSPEND);
0e91398f 94 if (err) {
d1616302 95 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
0e91398f
JF
96 goto out;
97 }
98
de5b31bd
IC
99 printk(KERN_DEBUG "suspending xenstore...\n");
100 xs_suspend();
0e91398f 101
d1616302 102 err = dpm_suspend_noirq(PMSG_SUSPEND);
2ed8d2b3 103 if (err) {
d1616302 104 printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
2ed8d2b3
RW
105 goto resume_devices;
106 }
107
f7df8ed1 108 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
922cc38a
JF
109
110 dpm_resume_noirq(PMSG_RESUME);
111
0e91398f
JF
112 if (err) {
113 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
114 goto out;
115 }
116
ad55db9f
IY
117 if (!cancelled) {
118 xen_arch_resume();
de5b31bd 119 xs_resume();
ad55db9f 120 } else
de5b31bd 121 xs_suspend_cancel();
0e91398f 122
2ed8d2b3 123resume_devices:
d1616302 124 dpm_resume_end(PMSG_RESUME);
0e91398f 125
359cdd3f
JF
126 /* Make sure timer events get retriggered on all CPUs */
127 clock_was_set();
0e91398f
JF
128out:
129#ifdef CONFIG_PREEMPT
130 thaw_processes();
131#endif
132 shutting_down = SHUTDOWN_INVALID;
133}
c7827728 134#endif /* CONFIG_PM_SLEEP */
3e2b8fbe
JF
135
136static void shutdown_handler(struct xenbus_watch *watch,
137 const char **vec, unsigned int len)
138{
139 char *str;
140 struct xenbus_transaction xbt;
141 int err;
142
143 if (shutting_down != SHUTDOWN_INVALID)
144 return;
145
146 again:
147 err = xenbus_transaction_start(&xbt);
148 if (err)
149 return;
150
151 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
152 /* Ignore read errors and empty reads. */
153 if (XENBUS_IS_ERR_READ(str)) {
154 xenbus_transaction_end(xbt, 1);
155 return;
156 }
157
158 xenbus_write(xbt, "control", "shutdown", "");
159
160 err = xenbus_transaction_end(xbt, 0);
161 if (err == -EAGAIN) {
162 kfree(str);
163 goto again;
164 }
165
166 if (strcmp(str, "poweroff") == 0 ||
0e91398f
JF
167 strcmp(str, "halt") == 0) {
168 shutting_down = SHUTDOWN_POWEROFF;
3e2b8fbe 169 orderly_poweroff(false);
0e91398f
JF
170 } else if (strcmp(str, "reboot") == 0) {
171 shutting_down = SHUTDOWN_POWEROFF; /* ? */
3e2b8fbe 172 ctrl_alt_del();
0e91398f
JF
173#ifdef CONFIG_PM_SLEEP
174 } else if (strcmp(str, "suspend") == 0) {
175 do_suspend();
176#endif
177 } else {
3e2b8fbe
JF
178 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
179 shutting_down = SHUTDOWN_INVALID;
180 }
181
182 kfree(str);
183}
184
185static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
186 unsigned int len)
187{
188 char sysrq_key = '\0';
189 struct xenbus_transaction xbt;
190 int err;
191
192 again:
193 err = xenbus_transaction_start(&xbt);
194 if (err)
195 return;
196 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
197 printk(KERN_ERR "Unable to read sysrq code in "
198 "control/sysrq\n");
199 xenbus_transaction_end(xbt, 1);
200 return;
201 }
202
203 if (sysrq_key != '\0')
204 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
205
206 err = xenbus_transaction_end(xbt, 0);
207 if (err == -EAGAIN)
208 goto again;
209
210 if (sysrq_key != '\0')
211 handle_sysrq(sysrq_key, NULL);
212}
213
214static struct xenbus_watch shutdown_watch = {
215 .node = "control/shutdown",
216 .callback = shutdown_handler
217};
218
219static struct xenbus_watch sysrq_watch = {
220 .node = "control/sysrq",
221 .callback = sysrq_handler
222};
223
224static int setup_shutdown_watcher(void)
225{
226 int err;
227
228 err = register_xenbus_watch(&shutdown_watch);
229 if (err) {
230 printk(KERN_ERR "Failed to set shutdown watcher\n");
231 return err;
232 }
233
234 err = register_xenbus_watch(&sysrq_watch);
235 if (err) {
236 printk(KERN_ERR "Failed to set sysrq watcher\n");
237 return err;
238 }
239
240 return 0;
241}
242
243static int shutdown_event(struct notifier_block *notifier,
244 unsigned long event,
245 void *data)
246{
247 setup_shutdown_watcher();
248 return NOTIFY_DONE;
249}
250
251static int __init setup_shutdown_event(void)
252{
253 static struct notifier_block xenstore_notifier = {
254 .notifier_call = shutdown_event
255 };
256 register_xenstore_notifier(&xenstore_notifier);
257
258 return 0;
259}
260
261subsys_initcall(setup_shutdown_event);