debuggers.hg

view linux-2.6-xen-sparse/arch/xen/kernel/reboot.c @ 6652:dd7aac3720b1

Suspend console after xenbus, resume console before xenbus.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Mon Sep 05 08:21:58 2005 +0000 (2005-09-05)
parents b6c98fe62e1a
children 4594827336ad
line source
1 #define __KERNEL_SYSCALLS__
2 #include <linux/version.h>
3 #include <linux/kernel.h>
4 #include <linux/mm.h>
5 #include <linux/unistd.h>
6 #include <linux/module.h>
7 #include <linux/reboot.h>
8 #include <linux/sysrq.h>
9 #include <linux/stringify.h>
10 #include <asm/irq.h>
11 #include <asm/mmu_context.h>
12 #include <asm-xen/evtchn.h>
13 #include <asm-xen/hypervisor.h>
14 #include <asm-xen/xen-public/dom0_ops.h>
15 #include <asm-xen/linux-public/suspend.h>
16 #include <asm-xen/queues.h>
17 #include <asm-xen/xenbus.h>
18 #include <asm-xen/ctrl_if.h>
19 #include <linux/cpu.h>
20 #include <linux/kthread.h>
22 #define SHUTDOWN_INVALID -1
23 #define SHUTDOWN_POWEROFF 0
24 #define SHUTDOWN_REBOOT 1
25 #define SHUTDOWN_SUSPEND 2
27 void machine_restart(char * __unused)
28 {
29 /* We really want to get pending console data out before we die. */
30 extern void xencons_force_flush(void);
31 xencons_force_flush();
32 HYPERVISOR_reboot();
33 }
35 void machine_halt(void)
36 {
37 machine_power_off();
38 }
40 void machine_power_off(void)
41 {
42 /* We really want to get pending console data out before we die. */
43 extern void xencons_force_flush(void);
44 xencons_force_flush();
45 HYPERVISOR_shutdown();
46 }
48 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
49 int reboot_thru_bios = 0; /* for dmi_scan.c */
50 EXPORT_SYMBOL(machine_restart);
51 EXPORT_SYMBOL(machine_halt);
52 EXPORT_SYMBOL(machine_power_off);
53 #endif
56 /******************************************************************************
57 * Stop/pickle callback handling.
58 */
60 /* Ignore multiple shutdown requests. */
61 static int shutting_down = SHUTDOWN_INVALID;
63 #ifndef CONFIG_HOTPLUG_CPU
64 #define cpu_down(x) (-EOPNOTSUPP)
65 #define cpu_up(x) (-EOPNOTSUPP)
66 #endif
69 static int __do_suspend(void *ignore)
70 {
71 int i, j;
72 suspend_record_t *suspend_record;
74 #ifdef CONFIG_XEN_USB_FRONTEND
75 extern void usbif_resume();
76 #else
77 #define usbif_resume() do{}while(0)
78 #endif
80 extern int gnttab_suspend(void);
81 extern int gnttab_resume(void);
83 extern void time_suspend(void);
84 extern void time_resume(void);
85 extern unsigned long max_pfn;
86 extern unsigned int *pfn_to_mfn_frame_list;
88 #ifdef CONFIG_SMP
89 extern void smp_suspend(void);
90 extern void smp_resume(void);
92 static vcpu_guest_context_t suspended_cpu_records[NR_CPUS];
93 cpumask_t prev_online_cpus, prev_present_cpus;
95 void save_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt);
96 int restore_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt);
97 #endif
99 extern void xencons_suspend(void);
100 extern void xencons_resume(void);
102 int err = 0;
104 BUG_ON(smp_processor_id() != 0);
105 BUG_ON(in_interrupt());
107 #if defined(CONFIG_SMP) && !defined(CONFIG_HOTPLUG_CPU)
108 if (num_online_cpus() > 1) {
109 printk(KERN_WARNING
110 "Can't suspend SMP guests without CONFIG_HOTPLUG_CPU\n");
111 return -EOPNOTSUPP;
112 }
113 #endif
115 suspend_record = (suspend_record_t *)__get_free_page(GFP_KERNEL);
116 if ( suspend_record == NULL )
117 goto out;
119 preempt_disable();
120 #ifdef CONFIG_SMP
121 /* Take all of the other cpus offline. We need to be careful not
122 to get preempted between the final test for num_online_cpus()
123 == 1 and disabling interrupts, since otherwise userspace could
124 bring another cpu online, and then we'd be stuffed. At the
125 same time, cpu_down can reschedule, so we need to enable
126 preemption while doing that. This kind of sucks, but should be
127 correct. */
128 /* (We don't need to worry about other cpus bringing stuff up,
129 since by the time num_online_cpus() == 1, there aren't any
130 other cpus) */
131 cpus_clear(prev_online_cpus);
132 while (num_online_cpus() > 1) {
133 preempt_enable();
134 for_each_online_cpu(i) {
135 if (i == 0)
136 continue;
137 err = cpu_down(i);
138 if (err != 0) {
139 printk(KERN_CRIT "Failed to take all CPUs down: %d.\n", err);
140 goto out_reenable_cpus;
141 }
142 cpu_set(i, prev_online_cpus);
143 }
144 preempt_disable();
145 }
146 #endif
148 suspend_record->nr_pfns = max_pfn; /* final number of pfns */
150 __cli();
152 preempt_enable();
154 #ifdef CONFIG_SMP
155 cpus_clear(prev_present_cpus);
156 for_each_present_cpu(i) {
157 if (i == 0)
158 continue;
159 save_vcpu_context(i, &suspended_cpu_records[i]);
160 cpu_set(i, prev_present_cpus);
161 }
162 #endif
164 #ifdef __i386__
165 mm_pin_all();
166 kmem_cache_shrink(pgd_cache);
167 #endif
169 time_suspend();
171 #ifdef CONFIG_SMP
172 smp_suspend();
173 #endif
175 xenbus_suspend();
177 xencons_suspend();
179 ctrl_if_suspend();
181 irq_suspend();
183 gnttab_suspend();
185 HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page;
186 clear_fixmap(FIX_SHARED_INFO);
188 memcpy(&suspend_record->resume_info, &xen_start_info,
189 sizeof(xen_start_info));
191 /* We'll stop somewhere inside this hypercall. When it returns,
192 we'll start resuming after the restore. */
193 HYPERVISOR_suspend(virt_to_mfn(suspend_record));
195 shutting_down = SHUTDOWN_INVALID;
197 memcpy(&xen_start_info, &suspend_record->resume_info,
198 sizeof(xen_start_info));
200 set_fixmap(FIX_SHARED_INFO, xen_start_info.shared_info);
202 HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO);
204 memset(empty_zero_page, 0, PAGE_SIZE);
206 for ( i=0, j=0; i < max_pfn; i+=(PAGE_SIZE/sizeof(unsigned long)), j++ )
207 {
208 pfn_to_mfn_frame_list[j] =
209 virt_to_mfn(&phys_to_machine_mapping[i]);
210 }
211 HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list =
212 virt_to_mfn(pfn_to_mfn_frame_list);
214 gnttab_resume();
216 irq_resume();
218 ctrl_if_resume();
220 xencons_resume();
222 xenbus_resume();
224 #ifdef CONFIG_SMP
225 smp_resume();
226 #endif
228 time_resume();
230 usbif_resume();
232 #ifdef CONFIG_SMP
233 for_each_cpu_mask(i, prev_present_cpus)
234 restore_vcpu_context(i, &suspended_cpu_records[i]);
235 #endif
237 __sti();
239 #ifdef CONFIG_SMP
240 out_reenable_cpus:
241 for_each_cpu_mask(i, prev_online_cpus) {
242 j = cpu_up(i);
243 if (j != 0) {
244 printk(KERN_CRIT "Failed to bring cpu %d back up (%d).\n",
245 i, j);
246 err = j;
247 }
248 }
249 #endif
251 out:
252 if ( suspend_record != NULL )
253 free_page((unsigned long)suspend_record);
254 return err;
255 }
257 static int shutdown_process(void *__unused)
258 {
259 static char *envp[] = { "HOME=/", "TERM=linux",
260 "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
261 static char *restart_argv[] = { "/sbin/reboot", NULL };
262 static char *poweroff_argv[] = { "/sbin/poweroff", NULL };
264 extern asmlinkage long sys_reboot(int magic1, int magic2,
265 unsigned int cmd, void *arg);
267 daemonize(
268 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
269 "shutdown"
270 #endif
271 );
273 switch ( shutting_down )
274 {
275 case SHUTDOWN_POWEROFF:
276 if ( execve("/sbin/poweroff", poweroff_argv, envp) < 0 )
277 {
278 sys_reboot(LINUX_REBOOT_MAGIC1,
279 LINUX_REBOOT_MAGIC2,
280 LINUX_REBOOT_CMD_POWER_OFF,
281 NULL);
282 }
283 break;
285 case SHUTDOWN_REBOOT:
286 if ( execve("/sbin/reboot", restart_argv, envp) < 0 )
287 {
288 sys_reboot(LINUX_REBOOT_MAGIC1,
289 LINUX_REBOOT_MAGIC2,
290 LINUX_REBOOT_CMD_RESTART,
291 NULL);
292 }
293 break;
294 }
296 shutting_down = SHUTDOWN_INVALID; /* could try again */
298 return 0;
299 }
301 static struct task_struct *kthread_create_on_cpu(int (*f)(void *arg),
302 void *arg,
303 const char *name,
304 int cpu)
305 {
306 struct task_struct *p;
307 p = kthread_create(f, arg, name);
308 kthread_bind(p, cpu);
309 wake_up_process(p);
310 return p;
311 }
313 static void __shutdown_handler(void *unused)
314 {
315 int err;
317 if ( shutting_down != SHUTDOWN_SUSPEND )
318 {
319 err = kernel_thread(shutdown_process, NULL, CLONE_FS | CLONE_FILES);
320 if ( err < 0 )
321 printk(KERN_ALERT "Error creating shutdown process!\n");
322 }
323 else
324 {
325 kthread_create_on_cpu(__do_suspend, NULL, "suspender", 0);
326 }
327 }
329 static void shutdown_handler(struct xenbus_watch *watch, const char *node)
330 {
331 static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
333 char *str;
335 str = (char *)xenbus_read("control", "shutdown", NULL);
336 /* Ignore read errors. */
337 if (IS_ERR(str))
338 return;
339 if (strlen(str) == 0) {
340 kfree(str);
341 return;
342 }
344 xenbus_write("control", "shutdown", "", O_CREAT);
346 if (strcmp(str, "poweroff") == 0)
347 shutting_down = SHUTDOWN_POWEROFF;
348 else if (strcmp(str, "reboot") == 0)
349 shutting_down = SHUTDOWN_REBOOT;
350 else if (strcmp(str, "suspend") == 0)
351 shutting_down = SHUTDOWN_SUSPEND;
352 else {
353 printk("Ignoring shutdown request: %s\n", str);
354 shutting_down = SHUTDOWN_INVALID;
355 }
357 kfree(str);
359 if (shutting_down != SHUTDOWN_INVALID)
360 schedule_work(&shutdown_work);
361 }
363 #ifdef CONFIG_MAGIC_SYSRQ
364 static void sysrq_handler(struct xenbus_watch *watch, const char *node)
365 {
366 char sysrq_key = '\0';
368 if (!xenbus_scanf("control", "sysrq", "%c", &sysrq_key)) {
369 printk(KERN_ERR "Unable to read sysrq code in control/sysrq\n");
370 return;
371 }
373 xenbus_printf("control", "sysrq", "%c", '\0');
375 if (sysrq_key != '\0') {
377 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
378 handle_sysrq(sysrq_key, NULL, NULL);
379 #else
380 handle_sysrq(sysrq_key, NULL, NULL, NULL);
381 #endif
382 }
383 }
384 #endif
386 static struct xenbus_watch shutdown_watch = {
387 .node = "control/shutdown",
388 .callback = shutdown_handler
389 };
391 #ifdef CONFIG_MAGIC_SYSRQ
392 static struct xenbus_watch sysrq_watch = {
393 .node ="control/sysrq",
394 .callback = sysrq_handler
395 };
396 #endif
398 static struct notifier_block xenstore_notifier;
400 /* Setup our watcher
401 NB: Assumes xenbus_lock is held!
402 */
403 static int setup_shutdown_watcher(struct notifier_block *notifier,
404 unsigned long event,
405 void *data)
406 {
407 int err1 = 0;
408 #ifdef CONFIG_MAGIC_SYSRQ
409 int err2 = 0;
410 #endif
412 BUG_ON(down_trylock(&xenbus_lock) == 0);
414 err1 = register_xenbus_watch(&shutdown_watch);
415 #ifdef CONFIG_MAGIC_SYSRQ
416 err2 = register_xenbus_watch(&sysrq_watch);
417 #endif
419 if (err1) {
420 printk(KERN_ERR "Failed to set shutdown watcher\n");
421 }
423 #ifdef CONFIG_MAGIC_SYSRQ
424 if (err2) {
425 printk(KERN_ERR "Failed to set sysrq watcher\n");
426 }
427 #endif
429 return NOTIFY_DONE;
430 }
432 static int __init setup_shutdown_event(void)
433 {
435 xenstore_notifier.notifier_call = setup_shutdown_watcher;
437 register_xenstore_notifier(&xenstore_notifier);
439 return 0;
440 }
442 subsys_initcall(setup_shutdown_event);