ia64/linux-2.6.18-xen.hg

view drivers/xen/core/reboot.c @ 456:49ffe9ef67d4

xen: Fix PV resume race against another back-to-back suspend request.
Previously the next suspend request was being dropped on the floor.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Feb 29 10:29:13 2008 +0000 (2008-02-29)
parents 1e2284d885fb
children cdadea74443c
line source
1 #define __KERNEL_SYSCALLS__
2 #include <linux/version.h>
3 #include <linux/kernel.h>
4 #include <linux/unistd.h>
5 #include <linux/module.h>
6 #include <linux/reboot.h>
7 #include <linux/sysrq.h>
8 #include <asm/hypervisor.h>
9 #include <xen/xenbus.h>
10 #include <linux/kmod.h>
11 #include <linux/slab.h>
12 #include <linux/workqueue.h>
14 #ifdef HAVE_XEN_PLATFORM_COMPAT_H
15 #include <xen/platform-compat.h>
16 #endif
18 MODULE_LICENSE("Dual BSD/GPL");
20 #define SHUTDOWN_INVALID -1
21 #define SHUTDOWN_POWEROFF 0
22 #define SHUTDOWN_SUSPEND 2
23 #define SHUTDOWN_RESUMING 3
24 #define SHUTDOWN_HALT 4
26 /* Ignore multiple shutdown requests. */
27 static int shutting_down = SHUTDOWN_INVALID;
29 /* Can we leave APs online when we suspend? */
30 static int fast_suspend;
32 static void __shutdown_handler(void *unused);
33 static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
35 int __xen_suspend(int fast_suspend, void (*resume_notifier)(void));
37 static int shutdown_process(void *__unused)
38 {
39 static char *envp[] = { "HOME=/", "TERM=linux",
40 "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
41 static char *poweroff_argv[] = { "/sbin/poweroff", NULL };
43 extern asmlinkage long sys_reboot(int magic1, int magic2,
44 unsigned int cmd, void *arg);
46 if ((shutting_down == SHUTDOWN_POWEROFF) ||
47 (shutting_down == SHUTDOWN_HALT)) {
48 if (call_usermodehelper("/sbin/poweroff", poweroff_argv,
49 envp, 0) < 0) {
50 #ifdef CONFIG_XEN
51 sys_reboot(LINUX_REBOOT_MAGIC1,
52 LINUX_REBOOT_MAGIC2,
53 LINUX_REBOOT_CMD_POWER_OFF,
54 NULL);
55 #endif /* CONFIG_XEN */
56 }
57 }
59 shutting_down = SHUTDOWN_INVALID; /* could try again */
61 return 0;
62 }
64 static void xen_resume_notifier(void)
65 {
66 int old_state = xchg(&shutting_down, SHUTDOWN_RESUMING);
67 BUG_ON(old_state != SHUTDOWN_SUSPEND);
68 }
70 static int xen_suspend(void *__unused)
71 {
72 int err, old_state;
74 daemonize("suspend");
75 err = set_cpus_allowed(current, cpumask_of_cpu(0));
76 if (err) {
77 printk(KERN_ERR "Xen suspend can't run on CPU0 (%d)\n", err);
78 goto fail;
79 }
81 do {
82 err = __xen_suspend(fast_suspend, xen_resume_notifier);
83 if (err) {
84 printk(KERN_ERR "Xen suspend failed (%d)\n", err);
85 goto fail;
86 }
87 old_state = cmpxchg(
88 &shutting_down, SHUTDOWN_RESUMING, SHUTDOWN_INVALID);
89 } while (old_state == SHUTDOWN_SUSPEND);
91 switch (old_state) {
92 case SHUTDOWN_INVALID:
93 case SHUTDOWN_SUSPEND:
94 BUG();
95 case SHUTDOWN_RESUMING:
96 break;
97 default:
98 schedule_work(&shutdown_work);
99 break;
100 }
102 return 0;
104 fail:
105 old_state = xchg(&shutting_down, SHUTDOWN_INVALID);
106 BUG_ON(old_state != SHUTDOWN_SUSPEND);
107 return 0;
108 }
110 static void __shutdown_handler(void *unused)
111 {
112 int err;
114 err = kernel_thread((shutting_down == SHUTDOWN_SUSPEND) ?
115 xen_suspend : shutdown_process,
116 NULL, CLONE_FS | CLONE_FILES);
118 if (err < 0) {
119 printk(KERN_WARNING "Error creating shutdown process (%d): "
120 "retrying...\n", -err);
121 schedule_delayed_work(&shutdown_work, HZ/2);
122 }
123 }
125 static void shutdown_handler(struct xenbus_watch *watch,
126 const char **vec, unsigned int len)
127 {
128 extern void ctrl_alt_del(void);
129 char *str;
130 struct xenbus_transaction xbt;
131 int err, old_state, new_state = SHUTDOWN_INVALID;
133 if ((shutting_down != SHUTDOWN_INVALID) &&
134 (shutting_down != SHUTDOWN_RESUMING))
135 return;
137 again:
138 err = xenbus_transaction_start(&xbt);
139 if (err)
140 return;
142 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
143 /* Ignore read errors and empty reads. */
144 if (XENBUS_IS_ERR_READ(str)) {
145 xenbus_transaction_end(xbt, 1);
146 return;
147 }
149 xenbus_write(xbt, "control", "shutdown", "");
151 err = xenbus_transaction_end(xbt, 0);
152 if (err == -EAGAIN) {
153 kfree(str);
154 goto again;
155 }
157 if (strcmp(str, "poweroff") == 0)
158 new_state = SHUTDOWN_POWEROFF;
159 else if (strcmp(str, "reboot") == 0)
160 ctrl_alt_del();
161 else if (strcmp(str, "suspend") == 0)
162 new_state = SHUTDOWN_SUSPEND;
163 else if (strcmp(str, "halt") == 0)
164 new_state = SHUTDOWN_HALT;
165 else
166 printk("Ignoring shutdown request: %s\n", str);
168 if (new_state != SHUTDOWN_INVALID) {
169 old_state = xchg(&shutting_down, new_state);
170 if (old_state == SHUTDOWN_INVALID)
171 schedule_work(&shutdown_work);
172 else
173 BUG_ON(old_state != SHUTDOWN_RESUMING);
174 }
176 kfree(str);
177 }
179 static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
180 unsigned int len)
181 {
182 char sysrq_key = '\0';
183 struct xenbus_transaction xbt;
184 int err;
186 again:
187 err = xenbus_transaction_start(&xbt);
188 if (err)
189 return;
190 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
191 printk(KERN_ERR "Unable to read sysrq code in "
192 "control/sysrq\n");
193 xenbus_transaction_end(xbt, 1);
194 return;
195 }
197 if (sysrq_key != '\0')
198 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
200 err = xenbus_transaction_end(xbt, 0);
201 if (err == -EAGAIN)
202 goto again;
204 #ifdef CONFIG_MAGIC_SYSRQ
205 if (sysrq_key != '\0')
206 handle_sysrq(sysrq_key, NULL, NULL);
207 #endif
208 }
210 static struct xenbus_watch shutdown_watch = {
211 .node = "control/shutdown",
212 .callback = shutdown_handler
213 };
215 static struct xenbus_watch sysrq_watch = {
216 .node = "control/sysrq",
217 .callback = sysrq_handler
218 };
220 static int setup_shutdown_watcher(void)
221 {
222 int err;
224 xenbus_scanf(XBT_NIL, "control",
225 "platform-feature-multiprocessor-suspend",
226 "%d", &fast_suspend);
228 err = register_xenbus_watch(&shutdown_watch);
229 if (err) {
230 printk(KERN_ERR "Failed to set shutdown watcher\n");
231 return err;
232 }
234 err = register_xenbus_watch(&sysrq_watch);
235 if (err) {
236 printk(KERN_ERR "Failed to set sysrq watcher\n");
237 return err;
238 }
240 return 0;
241 }
243 #ifdef CONFIG_XEN
245 static int shutdown_event(struct notifier_block *notifier,
246 unsigned long event,
247 void *data)
248 {
249 setup_shutdown_watcher();
250 return NOTIFY_DONE;
251 }
253 static int __init setup_shutdown_event(void)
254 {
255 static struct notifier_block xenstore_notifier = {
256 .notifier_call = shutdown_event
257 };
258 register_xenstore_notifier(&xenstore_notifier);
260 return 0;
261 }
263 subsys_initcall(setup_shutdown_event);
265 #else /* !defined(CONFIG_XEN) */
267 int xen_reboot_init(void)
268 {
269 return setup_shutdown_watcher();
270 }
272 #endif /* !defined(CONFIG_XEN) */