ia64/xen-unstable

view linux-2.6-xen-sparse/arch/xen/kernel/reboot.c @ 6674:a75b08af8d19

merge?
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 07 12:48:11 2005 +0000 (2005-09-07)
parents 8db9c5873b9b 7a36f58f64ee
children 549f4256ab3c 7d0fb56b4a91
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/queues.h>
16 #include <asm-xen/xenbus.h>
17 #include <asm-xen/ctrl_if.h>
18 #include <linux/cpu.h>
19 #include <linux/kthread.h>
21 #define SHUTDOWN_INVALID -1
22 #define SHUTDOWN_POWEROFF 0
23 #define SHUTDOWN_REBOOT 1
24 #define SHUTDOWN_SUSPEND 2
26 void machine_restart(char * __unused)
27 {
28 /* We really want to get pending console data out before we die. */
29 extern void xencons_force_flush(void);
30 xencons_force_flush();
31 HYPERVISOR_reboot();
32 }
34 void machine_halt(void)
35 {
36 machine_power_off();
37 }
39 void machine_power_off(void)
40 {
41 /* We really want to get pending console data out before we die. */
42 extern void xencons_force_flush(void);
43 xencons_force_flush();
44 HYPERVISOR_shutdown();
45 }
47 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
48 int reboot_thru_bios = 0; /* for dmi_scan.c */
49 EXPORT_SYMBOL(machine_restart);
50 EXPORT_SYMBOL(machine_halt);
51 EXPORT_SYMBOL(machine_power_off);
52 #endif
55 /******************************************************************************
56 * Stop/pickle callback handling.
57 */
59 /* Ignore multiple shutdown requests. */
60 static int shutting_down = SHUTDOWN_INVALID;
62 #ifndef CONFIG_HOTPLUG_CPU
63 #define cpu_down(x) (-EOPNOTSUPP)
64 #define cpu_up(x) (-EOPNOTSUPP)
65 #endif
68 static int __do_suspend(void *ignore)
69 {
70 int i, j, k, fpp;
72 #ifdef CONFIG_XEN_USB_FRONTEND
73 extern void usbif_resume();
74 #else
75 #define usbif_resume() do{}while(0)
76 #endif
78 extern int gnttab_suspend(void);
79 extern int gnttab_resume(void);
81 extern void time_suspend(void);
82 extern void time_resume(void);
83 extern unsigned long max_pfn;
84 extern unsigned long *pfn_to_mfn_frame_list_list, *pfn_to_mfn_frame_list[];
86 #ifdef CONFIG_SMP
87 extern void smp_suspend(void);
88 extern void smp_resume(void);
90 static vcpu_guest_context_t suspended_cpu_records[NR_CPUS];
91 cpumask_t prev_online_cpus, prev_present_cpus;
93 void save_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt);
94 int restore_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt);
95 #endif
97 extern void xencons_suspend(void);
98 extern void xencons_resume(void);
100 int err = 0;
102 BUG_ON(smp_processor_id() != 0);
103 BUG_ON(in_interrupt());
105 #if defined(CONFIG_SMP) && !defined(CONFIG_HOTPLUG_CPU)
106 if (num_online_cpus() > 1) {
107 printk(KERN_WARNING
108 "Can't suspend SMP guests without CONFIG_HOTPLUG_CPU\n");
109 return -EOPNOTSUPP;
110 }
111 #endif
113 preempt_disable();
114 #ifdef CONFIG_SMP
115 /* Take all of the other cpus offline. We need to be careful not
116 to get preempted between the final test for num_online_cpus()
117 == 1 and disabling interrupts, since otherwise userspace could
118 bring another cpu online, and then we'd be stuffed. At the
119 same time, cpu_down can reschedule, so we need to enable
120 preemption while doing that. This kind of sucks, but should be
121 correct. */
122 /* (We don't need to worry about other cpus bringing stuff up,
123 since by the time num_online_cpus() == 1, there aren't any
124 other cpus) */
125 cpus_clear(prev_online_cpus);
126 while (num_online_cpus() > 1) {
127 preempt_enable();
128 for_each_online_cpu(i) {
129 if (i == 0)
130 continue;
131 err = cpu_down(i);
132 if (err != 0) {
133 printk(KERN_CRIT "Failed to take all CPUs down: %d.\n", err);
134 goto out_reenable_cpus;
135 }
136 cpu_set(i, prev_online_cpus);
137 }
138 preempt_disable();
139 }
140 #endif
142 __cli();
144 preempt_enable();
146 #ifdef CONFIG_SMP
147 cpus_clear(prev_present_cpus);
148 for_each_present_cpu(i) {
149 if (i == 0)
150 continue;
151 save_vcpu_context(i, &suspended_cpu_records[i]);
152 cpu_set(i, prev_present_cpus);
153 }
154 #endif
156 #ifdef __i386__
157 mm_pin_all();
158 kmem_cache_shrink(pgd_cache);
159 #endif
161 time_suspend();
163 #ifdef CONFIG_SMP
164 smp_suspend();
165 #endif
167 xenbus_suspend();
169 xencons_suspend();
171 ctrl_if_suspend();
173 irq_suspend();
175 gnttab_suspend();
177 HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page;
178 clear_fixmap(FIX_SHARED_INFO);
180 xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn);
181 xen_start_info->console_mfn = mfn_to_pfn(xen_start_info->console_mfn);
183 /* We'll stop somewhere inside this hypercall. When it returns,
184 we'll start resuming after the restore. */
185 HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
187 shutting_down = SHUTDOWN_INVALID;
189 set_fixmap(FIX_SHARED_INFO, xen_start_info->shared_info);
191 HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO);
193 memset(empty_zero_page, 0, PAGE_SIZE);
195 HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
196 virt_to_mfn(pfn_to_mfn_frame_list_list);
198 fpp = PAGE_SIZE/sizeof(unsigned long);
199 for ( i=0, j=0, k=-1; i< max_pfn; i+=fpp, j++ )
200 {
201 if ( (j % fpp) == 0 )
202 {
203 k++;
204 pfn_to_mfn_frame_list_list[k] =
205 virt_to_mfn(pfn_to_mfn_frame_list[k]);
206 j=0;
207 }
208 pfn_to_mfn_frame_list[k][j] =
209 virt_to_mfn(&phys_to_machine_mapping[i]);
210 }
211 HYPERVISOR_shared_info->arch.max_pfn = max_pfn;
213 gnttab_resume();
215 irq_resume();
217 ctrl_if_resume();
219 xencons_resume();
221 xenbus_resume();
223 #ifdef CONFIG_SMP
224 smp_resume();
225 #endif
227 time_resume();
229 usbif_resume();
231 #ifdef CONFIG_SMP
232 for_each_cpu_mask(i, prev_present_cpus)
233 restore_vcpu_context(i, &suspended_cpu_records[i]);
234 #endif
236 __sti();
238 #ifdef CONFIG_SMP
239 out_reenable_cpus:
240 for_each_cpu_mask(i, prev_online_cpus) {
241 j = cpu_up(i);
242 if (j != 0) {
243 printk(KERN_CRIT "Failed to bring cpu %d back up (%d).\n",
244 i, j);
245 err = j;
246 }
247 }
248 #endif
250 return err;
251 }
253 static int shutdown_process(void *__unused)
254 {
255 static char *envp[] = { "HOME=/", "TERM=linux",
256 "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
257 static char *restart_argv[] = { "/sbin/reboot", NULL };
258 static char *poweroff_argv[] = { "/sbin/poweroff", NULL };
260 extern asmlinkage long sys_reboot(int magic1, int magic2,
261 unsigned int cmd, void *arg);
263 daemonize(
264 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
265 "shutdown"
266 #endif
267 );
269 switch ( shutting_down )
270 {
271 case SHUTDOWN_POWEROFF:
272 if ( execve("/sbin/poweroff", poweroff_argv, envp) < 0 )
273 {
274 sys_reboot(LINUX_REBOOT_MAGIC1,
275 LINUX_REBOOT_MAGIC2,
276 LINUX_REBOOT_CMD_POWER_OFF,
277 NULL);
278 }
279 break;
281 case SHUTDOWN_REBOOT:
282 if ( execve("/sbin/reboot", restart_argv, envp) < 0 )
283 {
284 sys_reboot(LINUX_REBOOT_MAGIC1,
285 LINUX_REBOOT_MAGIC2,
286 LINUX_REBOOT_CMD_RESTART,
287 NULL);
288 }
289 break;
290 }
292 shutting_down = SHUTDOWN_INVALID; /* could try again */
294 return 0;
295 }
297 static struct task_struct *kthread_create_on_cpu(int (*f)(void *arg),
298 void *arg,
299 const char *name,
300 int cpu)
301 {
302 struct task_struct *p;
303 p = kthread_create(f, arg, name);
304 kthread_bind(p, cpu);
305 wake_up_process(p);
306 return p;
307 }
309 static void __shutdown_handler(void *unused)
310 {
311 int err;
313 if ( shutting_down != SHUTDOWN_SUSPEND )
314 {
315 err = kernel_thread(shutdown_process, NULL, CLONE_FS | CLONE_FILES);
316 if ( err < 0 )
317 printk(KERN_ALERT "Error creating shutdown process!\n");
318 }
319 else
320 {
321 kthread_create_on_cpu(__do_suspend, NULL, "suspender", 0);
322 }
323 }
325 static void shutdown_handler(struct xenbus_watch *watch, const char *node)
326 {
327 static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
329 char *str;
331 str = (char *)xenbus_read("control", "shutdown", NULL);
332 /* Ignore read errors. */
333 if (IS_ERR(str))
334 return;
335 if (strlen(str) == 0) {
336 kfree(str);
337 return;
338 }
340 xenbus_write("control", "shutdown", "", O_CREAT);
342 if (strcmp(str, "poweroff") == 0)
343 shutting_down = SHUTDOWN_POWEROFF;
344 else if (strcmp(str, "reboot") == 0)
345 shutting_down = SHUTDOWN_REBOOT;
346 else if (strcmp(str, "suspend") == 0)
347 shutting_down = SHUTDOWN_SUSPEND;
348 else {
349 printk("Ignoring shutdown request: %s\n", str);
350 shutting_down = SHUTDOWN_INVALID;
351 }
353 kfree(str);
355 if (shutting_down != SHUTDOWN_INVALID)
356 schedule_work(&shutdown_work);
357 }
359 #ifdef CONFIG_MAGIC_SYSRQ
360 static void sysrq_handler(struct xenbus_watch *watch, const char *node)
361 {
362 char sysrq_key = '\0';
364 if (!xenbus_scanf("control", "sysrq", "%c", &sysrq_key)) {
365 printk(KERN_ERR "Unable to read sysrq code in control/sysrq\n");
366 return;
367 }
369 xenbus_printf("control", "sysrq", "%c", '\0');
371 if (sysrq_key != '\0') {
373 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
374 handle_sysrq(sysrq_key, NULL, NULL);
375 #else
376 handle_sysrq(sysrq_key, NULL, NULL, NULL);
377 #endif
378 }
379 }
380 #endif
382 static struct xenbus_watch shutdown_watch = {
383 .node = "control/shutdown",
384 .callback = shutdown_handler
385 };
387 #ifdef CONFIG_MAGIC_SYSRQ
388 static struct xenbus_watch sysrq_watch = {
389 .node ="control/sysrq",
390 .callback = sysrq_handler
391 };
392 #endif
394 static struct notifier_block xenstore_notifier;
396 /* Setup our watcher
397 NB: Assumes xenbus_lock is held!
398 */
399 static int setup_shutdown_watcher(struct notifier_block *notifier,
400 unsigned long event,
401 void *data)
402 {
403 int err1 = 0;
404 #ifdef CONFIG_MAGIC_SYSRQ
405 int err2 = 0;
406 #endif
408 BUG_ON(down_trylock(&xenbus_lock) == 0);
410 err1 = register_xenbus_watch(&shutdown_watch);
411 #ifdef CONFIG_MAGIC_SYSRQ
412 err2 = register_xenbus_watch(&sysrq_watch);
413 #endif
415 if (err1) {
416 printk(KERN_ERR "Failed to set shutdown watcher\n");
417 }
419 #ifdef CONFIG_MAGIC_SYSRQ
420 if (err2) {
421 printk(KERN_ERR "Failed to set sysrq watcher\n");
422 }
423 #endif
425 return NOTIFY_DONE;
426 }
428 static int __init setup_shutdown_event(void)
429 {
431 xenstore_notifier.notifier_call = setup_shutdown_watcher;
433 register_xenstore_notifier(&xenstore_notifier);
435 return 0;
436 }
438 subsys_initcall(setup_shutdown_event);