diff options
Diffstat (limited to 'drivers/xen/manage.c')
-rw-r--r-- | drivers/xen/manage.c | 159 |
1 files changed, 95 insertions, 64 deletions
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index ef9c7db52077..0b5366b5be20 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <linux/sysrq.h> | 8 | #include <linux/sysrq.h> |
9 | #include <linux/stop_machine.h> | 9 | #include <linux/stop_machine.h> |
10 | #include <linux/freezer.h> | 10 | #include <linux/freezer.h> |
11 | #include <linux/syscore_ops.h> | ||
11 | 12 | ||
12 | #include <xen/xen.h> | 13 | #include <xen/xen.h> |
13 | #include <xen/xenbus.h> | 14 | #include <xen/xenbus.h> |
@@ -34,63 +35,68 @@ enum shutdown_state { | |||
34 | /* Ignore multiple shutdown requests. */ | 35 | /* Ignore multiple shutdown requests. */ |
35 | static enum shutdown_state shutting_down = SHUTDOWN_INVALID; | 36 | static enum shutdown_state shutting_down = SHUTDOWN_INVALID; |
36 | 37 | ||
37 | #ifdef CONFIG_PM_SLEEP | 38 | struct suspend_info { |
38 | static int xen_hvm_suspend(void *data) | 39 | int cancelled; |
39 | { | 40 | unsigned long arg; /* extra hypercall argument */ |
40 | struct sched_shutdown r = { .reason = SHUTDOWN_suspend }; | 41 | void (*pre)(void); |
41 | int *cancelled = data; | 42 | void (*post)(int cancelled); |
42 | 43 | }; | |
43 | BUG_ON(!irqs_disabled()); | ||
44 | |||
45 | *cancelled = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r); | ||
46 | 44 | ||
47 | xen_hvm_post_suspend(*cancelled); | 45 | static void xen_hvm_post_suspend(int cancelled) |
46 | { | ||
47 | xen_arch_hvm_post_suspend(cancelled); | ||
48 | gnttab_resume(); | 48 | gnttab_resume(); |
49 | } | ||
49 | 50 | ||
50 | if (!*cancelled) { | 51 | static void xen_pre_suspend(void) |
51 | xen_irq_resume(); | 52 | { |
52 | xen_timer_resume(); | 53 | xen_mm_pin_all(); |
53 | } | 54 | gnttab_suspend(); |
55 | xen_arch_pre_suspend(); | ||
56 | } | ||
54 | 57 | ||
55 | return 0; | 58 | static void xen_post_suspend(int cancelled) |
59 | { | ||
60 | xen_arch_post_suspend(cancelled); | ||
61 | gnttab_resume(); | ||
62 | xen_mm_unpin_all(); | ||
56 | } | 63 | } |
57 | 64 | ||
65 | #ifdef CONFIG_HIBERNATE_CALLBACKS | ||
58 | static int xen_suspend(void *data) | 66 | static int xen_suspend(void *data) |
59 | { | 67 | { |
68 | struct suspend_info *si = data; | ||
60 | int err; | 69 | int err; |
61 | int *cancelled = data; | ||
62 | 70 | ||
63 | BUG_ON(!irqs_disabled()); | 71 | BUG_ON(!irqs_disabled()); |
64 | 72 | ||
65 | err = sysdev_suspend(PMSG_SUSPEND); | 73 | err = syscore_suspend(); |
66 | if (err) { | 74 | if (err) { |
67 | printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n", | 75 | printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n", |
68 | err); | 76 | err); |
69 | return err; | 77 | return err; |
70 | } | 78 | } |
71 | 79 | ||
72 | xen_mm_pin_all(); | 80 | if (si->pre) |
73 | gnttab_suspend(); | 81 | si->pre(); |
74 | xen_pre_suspend(); | ||
75 | 82 | ||
76 | /* | 83 | /* |
77 | * This hypercall returns 1 if suspend was cancelled | 84 | * This hypercall returns 1 if suspend was cancelled |
78 | * or the domain was merely checkpointed, and 0 if it | 85 | * or the domain was merely checkpointed, and 0 if it |
79 | * is resuming in a new domain. | 86 | * is resuming in a new domain. |
80 | */ | 87 | */ |
81 | *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info)); | 88 | si->cancelled = HYPERVISOR_suspend(si->arg); |
82 | 89 | ||
83 | xen_post_suspend(*cancelled); | 90 | if (si->post) |
84 | gnttab_resume(); | 91 | si->post(si->cancelled); |
85 | xen_mm_unpin_all(); | ||
86 | 92 | ||
87 | if (!*cancelled) { | 93 | if (!si->cancelled) { |
88 | xen_irq_resume(); | 94 | xen_irq_resume(); |
89 | xen_console_resume(); | 95 | xen_console_resume(); |
90 | xen_timer_resume(); | 96 | xen_timer_resume(); |
91 | } | 97 | } |
92 | 98 | ||
93 | sysdev_resume(); | 99 | syscore_resume(); |
94 | 100 | ||
95 | return 0; | 101 | return 0; |
96 | } | 102 | } |
@@ -98,7 +104,7 @@ static int xen_suspend(void *data) | |||
98 | static void do_suspend(void) | 104 | static void do_suspend(void) |
99 | { | 105 | { |
100 | int err; | 106 | int err; |
101 | int cancelled = 1; | 107 | struct suspend_info si; |
102 | 108 | ||
103 | shutting_down = SHUTDOWN_SUSPEND; | 109 | shutting_down = SHUTDOWN_SUSPEND; |
104 | 110 | ||
@@ -113,7 +119,7 @@ static void do_suspend(void) | |||
113 | } | 119 | } |
114 | #endif | 120 | #endif |
115 | 121 | ||
116 | err = dpm_suspend_start(PMSG_SUSPEND); | 122 | err = dpm_suspend_start(PMSG_FREEZE); |
117 | if (err) { | 123 | if (err) { |
118 | printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err); | 124 | printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err); |
119 | goto out_thaw; | 125 | goto out_thaw; |
@@ -122,32 +128,41 @@ static void do_suspend(void) | |||
122 | printk(KERN_DEBUG "suspending xenstore...\n"); | 128 | printk(KERN_DEBUG "suspending xenstore...\n"); |
123 | xs_suspend(); | 129 | xs_suspend(); |
124 | 130 | ||
125 | err = dpm_suspend_noirq(PMSG_SUSPEND); | 131 | err = dpm_suspend_noirq(PMSG_FREEZE); |
126 | if (err) { | 132 | if (err) { |
127 | printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err); | 133 | printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err); |
128 | goto out_resume; | 134 | goto out_resume; |
129 | } | 135 | } |
130 | 136 | ||
131 | if (xen_hvm_domain()) | 137 | si.cancelled = 1; |
132 | err = stop_machine(xen_hvm_suspend, &cancelled, cpumask_of(0)); | ||
133 | else | ||
134 | err = stop_machine(xen_suspend, &cancelled, cpumask_of(0)); | ||
135 | 138 | ||
136 | dpm_resume_noirq(PMSG_RESUME); | 139 | if (xen_hvm_domain()) { |
140 | si.arg = 0UL; | ||
141 | si.pre = NULL; | ||
142 | si.post = &xen_hvm_post_suspend; | ||
143 | } else { | ||
144 | si.arg = virt_to_mfn(xen_start_info); | ||
145 | si.pre = &xen_pre_suspend; | ||
146 | si.post = &xen_post_suspend; | ||
147 | } | ||
148 | |||
149 | err = stop_machine(xen_suspend, &si, cpumask_of(0)); | ||
150 | |||
151 | dpm_resume_noirq(si.cancelled ? PMSG_THAW : PMSG_RESTORE); | ||
137 | 152 | ||
138 | if (err) { | 153 | if (err) { |
139 | printk(KERN_ERR "failed to start xen_suspend: %d\n", err); | 154 | printk(KERN_ERR "failed to start xen_suspend: %d\n", err); |
140 | cancelled = 1; | 155 | si.cancelled = 1; |
141 | } | 156 | } |
142 | 157 | ||
143 | out_resume: | 158 | out_resume: |
144 | if (!cancelled) { | 159 | if (!si.cancelled) { |
145 | xen_arch_resume(); | 160 | xen_arch_resume(); |
146 | xs_resume(); | 161 | xs_resume(); |
147 | } else | 162 | } else |
148 | xs_suspend_cancel(); | 163 | xs_suspend_cancel(); |
149 | 164 | ||
150 | dpm_resume_end(PMSG_RESUME); | 165 | dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE); |
151 | 166 | ||
152 | /* Make sure timer events get retriggered on all CPUs */ | 167 | /* Make sure timer events get retriggered on all CPUs */ |
153 | clock_was_set(); | 168 | clock_was_set(); |
@@ -159,7 +174,24 @@ out: | |||
159 | #endif | 174 | #endif |
160 | shutting_down = SHUTDOWN_INVALID; | 175 | shutting_down = SHUTDOWN_INVALID; |
161 | } | 176 | } |
162 | #endif /* CONFIG_PM_SLEEP */ | 177 | #endif /* CONFIG_HIBERNATE_CALLBACKS */ |
178 | |||
179 | struct shutdown_handler { | ||
180 | const char *command; | ||
181 | void (*cb)(void); | ||
182 | }; | ||
183 | |||
184 | static void do_poweroff(void) | ||
185 | { | ||
186 | shutting_down = SHUTDOWN_POWEROFF; | ||
187 | orderly_poweroff(false); | ||
188 | } | ||
189 | |||
190 | static void do_reboot(void) | ||
191 | { | ||
192 | shutting_down = SHUTDOWN_POWEROFF; /* ? */ | ||
193 | ctrl_alt_del(); | ||
194 | } | ||
163 | 195 | ||
164 | static void shutdown_handler(struct xenbus_watch *watch, | 196 | static void shutdown_handler(struct xenbus_watch *watch, |
165 | const char **vec, unsigned int len) | 197 | const char **vec, unsigned int len) |
@@ -167,6 +199,16 @@ static void shutdown_handler(struct xenbus_watch *watch, | |||
167 | char *str; | 199 | char *str; |
168 | struct xenbus_transaction xbt; | 200 | struct xenbus_transaction xbt; |
169 | int err; | 201 | int err; |
202 | static struct shutdown_handler handlers[] = { | ||
203 | { "poweroff", do_poweroff }, | ||
204 | { "halt", do_poweroff }, | ||
205 | { "reboot", do_reboot }, | ||
206 | #ifdef CONFIG_HIBERNATE_CALLBACKS | ||
207 | { "suspend", do_suspend }, | ||
208 | #endif | ||
209 | {NULL, NULL}, | ||
210 | }; | ||
211 | static struct shutdown_handler *handler; | ||
170 | 212 | ||
171 | if (shutting_down != SHUTDOWN_INVALID) | 213 | if (shutting_down != SHUTDOWN_INVALID) |
172 | return; | 214 | return; |
@@ -183,7 +225,14 @@ static void shutdown_handler(struct xenbus_watch *watch, | |||
183 | return; | 225 | return; |
184 | } | 226 | } |
185 | 227 | ||
186 | xenbus_write(xbt, "control", "shutdown", ""); | 228 | for (handler = &handlers[0]; handler->command; handler++) { |
229 | if (strcmp(str, handler->command) == 0) | ||
230 | break; | ||
231 | } | ||
232 | |||
233 | /* Only acknowledge commands which we are prepared to handle. */ | ||
234 | if (handler->cb) | ||
235 | xenbus_write(xbt, "control", "shutdown", ""); | ||
187 | 236 | ||
188 | err = xenbus_transaction_end(xbt, 0); | 237 | err = xenbus_transaction_end(xbt, 0); |
189 | if (err == -EAGAIN) { | 238 | if (err == -EAGAIN) { |
@@ -191,17 +240,8 @@ static void shutdown_handler(struct xenbus_watch *watch, | |||
191 | goto again; | 240 | goto again; |
192 | } | 241 | } |
193 | 242 | ||
194 | if (strcmp(str, "poweroff") == 0 || | 243 | if (handler->cb) { |
195 | strcmp(str, "halt") == 0) { | 244 | handler->cb(); |
196 | shutting_down = SHUTDOWN_POWEROFF; | ||
197 | orderly_poweroff(false); | ||
198 | } else if (strcmp(str, "reboot") == 0) { | ||
199 | shutting_down = SHUTDOWN_POWEROFF; /* ? */ | ||
200 | ctrl_alt_del(); | ||
201 | #ifdef CONFIG_PM_SLEEP | ||
202 | } else if (strcmp(str, "suspend") == 0) { | ||
203 | do_suspend(); | ||
204 | #endif | ||
205 | } else { | 245 | } else { |
206 | printk(KERN_INFO "Ignoring shutdown request: %s\n", str); | 246 | printk(KERN_INFO "Ignoring shutdown request: %s\n", str); |
207 | shutting_down = SHUTDOWN_INVALID; | 247 | shutting_down = SHUTDOWN_INVALID; |
@@ -280,27 +320,18 @@ static int shutdown_event(struct notifier_block *notifier, | |||
280 | return NOTIFY_DONE; | 320 | return NOTIFY_DONE; |
281 | } | 321 | } |
282 | 322 | ||
283 | static 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 | |||
295 | int xen_setup_shutdown_event(void) | 323 | int xen_setup_shutdown_event(void) |
296 | { | 324 | { |
297 | static struct notifier_block xenstore_notifier = { | 325 | static struct notifier_block xenstore_notifier = { |
298 | .notifier_call = shutdown_event | 326 | .notifier_call = shutdown_event |
299 | }; | 327 | }; |
328 | |||
329 | if (!xen_domain()) | ||
330 | return -ENODEV; | ||
300 | register_xenstore_notifier(&xenstore_notifier); | 331 | register_xenstore_notifier(&xenstore_notifier); |
301 | 332 | ||
302 | return 0; | 333 | return 0; |
303 | } | 334 | } |
304 | EXPORT_SYMBOL_GPL(xen_setup_shutdown_event); | 335 | EXPORT_SYMBOL_GPL(xen_setup_shutdown_event); |
305 | 336 | ||
306 | subsys_initcall(__setup_shutdown_event); | 337 | subsys_initcall(xen_setup_shutdown_event); |