aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/manage.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen/manage.c')
-rw-r--r--drivers/xen/manage.c159
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. */
35static enum shutdown_state shutting_down = SHUTDOWN_INVALID; 36static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
36 37
37#ifdef CONFIG_PM_SLEEP 38struct suspend_info {
38static 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); 45static 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) { 51static 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; 58static 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
58static int xen_suspend(void *data) 66static 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)
98static void do_suspend(void) 104static 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
143out_resume: 158out_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
179struct shutdown_handler {
180 const char *command;
181 void (*cb)(void);
182};
183
184static void do_poweroff(void)
185{
186 shutting_down = SHUTDOWN_POWEROFF;
187 orderly_poweroff(false);
188}
189
190static void do_reboot(void)
191{
192 shutting_down = SHUTDOWN_POWEROFF; /* ? */
193 ctrl_alt_del();
194}
163 195
164static void shutdown_handler(struct xenbus_watch *watch, 196static 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
283static 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
295int xen_setup_shutdown_event(void) 323int 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}
304EXPORT_SYMBOL_GPL(xen_setup_shutdown_event); 335EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
305 336
306subsys_initcall(__setup_shutdown_event); 337subsys_initcall(xen_setup_shutdown_event);