ia64/xen-unstable

view linux-2.6-xen-sparse/arch/xen/kernel/reboot.c @ 6689:7d0fb56b4a91

merge?
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 07 19:01:31 2005 +0000 (2005-09-07)
parents 549f4256ab3c a75b08af8d19
children b2f4823b6ff0 b35215021b32 36c4d3bb29c8
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 <linux/cpu.h>
18 #include <linux/kthread.h>
20 #define SHUTDOWN_INVALID -1
21 #define SHUTDOWN_POWEROFF 0
22 #define SHUTDOWN_REBOOT 1
23 #define SHUTDOWN_SUSPEND 2
25 void machine_restart(char * __unused)
26 {
27 /* We really want to get pending console data out before we die. */
28 extern void xencons_force_flush(void);
29 xencons_force_flush();
30 HYPERVISOR_reboot();
31 }
33 void machine_halt(void)
34 {
35 machine_power_off();
36 }
38 void machine_power_off(void)
39 {
40 /* We really want to get pending console data out before we die. */
41 extern void xencons_force_flush(void);
42 xencons_force_flush();
43 HYPERVISOR_shutdown();
44 }
46 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
47 int reboot_thru_bios = 0; /* for dmi_scan.c */
48 EXPORT_SYMBOL(machine_restart);
49 EXPORT_SYMBOL(machine_halt);
50 EXPORT_SYMBOL(machine_power_off);
51 #endif
54 /******************************************************************************
55 * Stop/pickle callback handling.
56 */
58 /* Ignore multiple shutdown requests. */
59 static int shutting_down = SHUTDOWN_INVALID;
61 #ifndef CONFIG_HOTPLUG_CPU
62 #define cpu_down(x) (-EOPNOTSUPP)
63 #define cpu_up(x) (-EOPNOTSUPP)
64 #endif
67 static int __do_suspend(void *ignore)
68 {
69 int i, j, k, fpp;
71 #ifdef CONFIG_XEN_USB_FRONTEND
72 extern void usbif_resume();
73 #else
74 #define usbif_resume() do{}while(0)
75 #endif
77 extern int gnttab_suspend(void);
78 extern int gnttab_resume(void);
80 extern void time_suspend(void);
81 extern void time_resume(void);
82 extern unsigned long max_pfn;
83 extern unsigned long *pfn_to_mfn_frame_list_list, *pfn_to_mfn_frame_list[];
85 #ifdef CONFIG_SMP
86 extern void smp_suspend(void);
87 extern void smp_resume(void);
89 static vcpu_guest_context_t suspended_cpu_records[NR_CPUS];
90 cpumask_t prev_online_cpus, prev_present_cpus;
92 void save_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt);
93 int restore_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt);
94 #endif
96 extern void xencons_suspend(void);
97 extern void xencons_resume(void);
99 int err = 0;
101 BUG_ON(smp_processor_id() != 0);
102 BUG_ON(in_interrupt());
104 #if defined(CONFIG_SMP) && !defined(CONFIG_HOTPLUG_CPU)
105 if (num_online_cpus() > 1) {
106 printk(KERN_WARNING
107 "Can't suspend SMP guests without CONFIG_HOTPLUG_CPU\n");
108 return -EOPNOTSUPP;
109 }
110 #endif
112 preempt_disable();
113 #ifdef CONFIG_SMP
114 /* Take all of the other cpus offline. We need to be careful not
115 to get preempted between the final test for num_online_cpus()
116 == 1 and disabling interrupts, since otherwise userspace could
117 bring another cpu online, and then we'd be stuffed. At the
118 same time, cpu_down can reschedule, so we need to enable
119 preemption while doing that. This kind of sucks, but should be
120 correct. */
121 /* (We don't need to worry about other cpus bringing stuff up,
122 since by the time num_online_cpus() == 1, there aren't any
123 other cpus) */
124 cpus_clear(prev_online_cpus);
125 while (num_online_cpus() > 1) {
126 preempt_enable();
127 for_each_online_cpu(i) {
128 if (i == 0)
129 continue;
130 err = cpu_down(i);
131 if (err != 0) {
132 printk(KERN_CRIT "Failed to take all CPUs down: %d.\n", err);
133 goto out_reenable_cpus;
134 }
135 cpu_set(i, prev_online_cpus);
136 }
137 preempt_disable();
138 }
139 #endif
141 __cli();
143 preempt_enable();
145 #ifdef CONFIG_SMP
146 cpus_clear(prev_present_cpus);
147 for_each_present_cpu(i) {
148 if (i == 0)
149 continue;
150 save_vcpu_context(i, &suspended_cpu_records[i]);
151 cpu_set(i, prev_present_cpus);
152 }
153 #endif
155 #ifdef __i386__
156 mm_pin_all();
157 kmem_cache_shrink(pgd_cache);
158 #endif
160 time_suspend();
162 #ifdef CONFIG_SMP
163 smp_suspend();
164 #endif
166 xenbus_suspend();
168 xencons_suspend();
170 irq_suspend();
172 gnttab_suspend();
174 HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page;
175 clear_fixmap(FIX_SHARED_INFO);
177 xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn);
178 xen_start_info->console_mfn = mfn_to_pfn(xen_start_info->console_mfn);
180 /* We'll stop somewhere inside this hypercall. When it returns,
181 we'll start resuming after the restore. */
182 HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
184 shutting_down = SHUTDOWN_INVALID;
186 set_fixmap(FIX_SHARED_INFO, xen_start_info->shared_info);
188 HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO);
190 memset(empty_zero_page, 0, PAGE_SIZE);
192 HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
193 virt_to_mfn(pfn_to_mfn_frame_list_list);
195 fpp = PAGE_SIZE/sizeof(unsigned long);
196 for ( i=0, j=0, k=-1; i< max_pfn; i+=fpp, j++ )
197 {
198 if ( (j % fpp) == 0 )
199 {
200 k++;
201 pfn_to_mfn_frame_list_list[k] =
202 virt_to_mfn(pfn_to_mfn_frame_list[k]);
203 j=0;
204 }
205 pfn_to_mfn_frame_list[k][j] =
206 virt_to_mfn(&phys_to_machine_mapping[i]);
207 }
208 HYPERVISOR_shared_info->arch.max_pfn = max_pfn;
210 gnttab_resume();
212 irq_resume();
214 xencons_resume();
216 xenbus_resume();
218 #ifdef CONFIG_SMP
219 smp_resume();
220 #endif
222 time_resume();
224 usbif_resume();
226 #ifdef CONFIG_SMP
227 for_each_cpu_mask(i, prev_present_cpus)
228 restore_vcpu_context(i, &suspended_cpu_records[i]);
229 #endif
231 __sti();
233 #ifdef CONFIG_SMP
234 out_reenable_cpus:
235 for_each_cpu_mask(i, prev_online_cpus) {
236 j = cpu_up(i);
237 if (j != 0) {
238 printk(KERN_CRIT "Failed to bring cpu %d back up (%d).\n",
239 i, j);
240 err = j;
241 }
242 }
243 #endif
245 return err;
246 }
248 static int shutdown_process(void *__unused)
249 {
250 static char *envp[] = { "HOME=/", "TERM=linux",
251 "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
252 static char *restart_argv[] = { "/sbin/reboot", NULL };
253 static char *poweroff_argv[] = { "/sbin/poweroff", NULL };
255 extern asmlinkage long sys_reboot(int magic1, int magic2,
256 unsigned int cmd, void *arg);
258 daemonize(
259 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
260 "shutdown"
261 #endif
262 );
264 switch ( shutting_down )
265 {
266 case SHUTDOWN_POWEROFF:
267 if ( execve("/sbin/poweroff", poweroff_argv, envp) < 0 )
268 {
269 sys_reboot(LINUX_REBOOT_MAGIC1,
270 LINUX_REBOOT_MAGIC2,
271 LINUX_REBOOT_CMD_POWER_OFF,
272 NULL);
273 }
274 break;
276 case SHUTDOWN_REBOOT:
277 if ( execve("/sbin/reboot", restart_argv, envp) < 0 )
278 {
279 sys_reboot(LINUX_REBOOT_MAGIC1,
280 LINUX_REBOOT_MAGIC2,
281 LINUX_REBOOT_CMD_RESTART,
282 NULL);
283 }
284 break;
285 }
287 shutting_down = SHUTDOWN_INVALID; /* could try again */
289 return 0;
290 }
292 static struct task_struct *kthread_create_on_cpu(int (*f)(void *arg),
293 void *arg,
294 const char *name,
295 int cpu)
296 {
297 struct task_struct *p;
298 p = kthread_create(f, arg, name);
299 kthread_bind(p, cpu);
300 wake_up_process(p);
301 return p;
302 }
304 static void __shutdown_handler(void *unused)
305 {
306 int err;
308 if ( shutting_down != SHUTDOWN_SUSPEND )
309 {
310 err = kernel_thread(shutdown_process, NULL, CLONE_FS | CLONE_FILES);
311 if ( err < 0 )
312 printk(KERN_ALERT "Error creating shutdown process!\n");
313 }
314 else
315 {
316 kthread_create_on_cpu(__do_suspend, NULL, "suspender", 0);
317 }
318 }
320 static void shutdown_handler(struct xenbus_watch *watch, const char *node)
321 {
322 static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
324 char *str;
326 str = (char *)xenbus_read("control", "shutdown", NULL);
327 /* Ignore read errors. */
328 if (IS_ERR(str))
329 return;
330 if (strlen(str) == 0) {
331 kfree(str);
332 return;
333 }
335 xenbus_write("control", "shutdown", "", O_CREAT);
337 if (strcmp(str, "poweroff") == 0)
338 shutting_down = SHUTDOWN_POWEROFF;
339 else if (strcmp(str, "reboot") == 0)
340 shutting_down = SHUTDOWN_REBOOT;
341 else if (strcmp(str, "suspend") == 0)
342 shutting_down = SHUTDOWN_SUSPEND;
343 else {
344 printk("Ignoring shutdown request: %s\n", str);
345 shutting_down = SHUTDOWN_INVALID;
346 }
348 kfree(str);
350 if (shutting_down != SHUTDOWN_INVALID)
351 schedule_work(&shutdown_work);
352 }
354 #ifdef CONFIG_MAGIC_SYSRQ
355 static void sysrq_handler(struct xenbus_watch *watch, const char *node)
356 {
357 char sysrq_key = '\0';
359 if (!xenbus_scanf("control", "sysrq", "%c", &sysrq_key)) {
360 printk(KERN_ERR "Unable to read sysrq code in control/sysrq\n");
361 return;
362 }
364 xenbus_printf("control", "sysrq", "%c", '\0');
366 if (sysrq_key != '\0') {
368 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
369 handle_sysrq(sysrq_key, NULL, NULL);
370 #else
371 handle_sysrq(sysrq_key, NULL, NULL, NULL);
372 #endif
373 }
374 }
375 #endif
377 static struct xenbus_watch shutdown_watch = {
378 .node = "control/shutdown",
379 .callback = shutdown_handler
380 };
382 #ifdef CONFIG_MAGIC_SYSRQ
383 static struct xenbus_watch sysrq_watch = {
384 .node ="control/sysrq",
385 .callback = sysrq_handler
386 };
387 #endif
389 static struct notifier_block xenstore_notifier;
391 /* Setup our watcher
392 NB: Assumes xenbus_lock is held!
393 */
394 static int setup_shutdown_watcher(struct notifier_block *notifier,
395 unsigned long event,
396 void *data)
397 {
398 int err1 = 0;
399 #ifdef CONFIG_MAGIC_SYSRQ
400 int err2 = 0;
401 #endif
403 BUG_ON(down_trylock(&xenbus_lock) == 0);
405 err1 = register_xenbus_watch(&shutdown_watch);
406 #ifdef CONFIG_MAGIC_SYSRQ
407 err2 = register_xenbus_watch(&sysrq_watch);
408 #endif
410 if (err1) {
411 printk(KERN_ERR "Failed to set shutdown watcher\n");
412 }
414 #ifdef CONFIG_MAGIC_SYSRQ
415 if (err2) {
416 printk(KERN_ERR "Failed to set sysrq watcher\n");
417 }
418 #endif
420 return NOTIFY_DONE;
421 }
423 static int __init setup_shutdown_event(void)
424 {
426 xenstore_notifier.notifier_call = setup_shutdown_watcher;
428 register_xenstore_notifier(&xenstore_notifier);
430 return 0;
431 }
433 subsys_initcall(setup_shutdown_event);