ia64/linux-2.6.18-xen.hg

view drivers/xen/core/machine_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 17a1a39df565
children 1cf7ba68d855
line source
1 #include <linux/version.h>
2 #include <linux/kernel.h>
3 #include <linux/mm.h>
4 #include <linux/unistd.h>
5 #include <linux/module.h>
6 #include <linux/reboot.h>
7 #include <linux/sysrq.h>
8 #include <linux/stringify.h>
9 #include <linux/stop_machine.h>
10 #include <asm/irq.h>
11 #include <asm/mmu_context.h>
12 #include <xen/evtchn.h>
13 #include <asm/hypervisor.h>
14 #include <xen/xenbus.h>
15 #include <linux/cpu.h>
16 #include <xen/gnttab.h>
17 #include <xen/xencons.h>
18 #include <xen/cpu_hotplug.h>
19 #include <xen/interface/vcpu.h>
21 #if defined(__i386__) || defined(__x86_64__)
23 /*
24 * Power off function, if any
25 */
26 void (*pm_power_off)(void);
27 EXPORT_SYMBOL(pm_power_off);
29 void machine_emergency_restart(void)
30 {
31 /* We really want to get pending console data out before we die. */
32 xencons_force_flush();
33 HYPERVISOR_shutdown(SHUTDOWN_reboot);
34 }
36 void machine_restart(char * __unused)
37 {
38 machine_emergency_restart();
39 }
41 void machine_halt(void)
42 {
43 machine_power_off();
44 }
46 void machine_power_off(void)
47 {
48 /* We really want to get pending console data out before we die. */
49 xencons_force_flush();
50 if (pm_power_off)
51 pm_power_off();
52 HYPERVISOR_shutdown(SHUTDOWN_poweroff);
53 }
55 int reboot_thru_bios = 0; /* for dmi_scan.c */
56 EXPORT_SYMBOL(machine_restart);
57 EXPORT_SYMBOL(machine_halt);
58 EXPORT_SYMBOL(machine_power_off);
60 static void pre_suspend(void)
61 {
62 HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page;
63 WARN_ON(HYPERVISOR_update_va_mapping(fix_to_virt(FIX_SHARED_INFO),
64 __pte_ma(0), 0));
66 xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn);
67 xen_start_info->console.domU.mfn =
68 mfn_to_pfn(xen_start_info->console.domU.mfn);
69 }
71 static void post_suspend(int suspend_cancelled)
72 {
73 int i, j, k, fpp;
74 unsigned long shinfo_mfn;
75 extern unsigned long max_pfn;
76 extern unsigned long *pfn_to_mfn_frame_list_list;
77 extern unsigned long *pfn_to_mfn_frame_list[];
79 if (suspend_cancelled) {
80 xen_start_info->store_mfn =
81 pfn_to_mfn(xen_start_info->store_mfn);
82 xen_start_info->console.domU.mfn =
83 pfn_to_mfn(xen_start_info->console.domU.mfn);
84 } else {
85 #ifdef CONFIG_SMP
86 cpu_initialized_map = cpu_online_map;
87 #endif
88 }
90 shinfo_mfn = xen_start_info->shared_info >> PAGE_SHIFT;
91 if (HYPERVISOR_update_va_mapping(fix_to_virt(FIX_SHARED_INFO),
92 pfn_pte_ma(shinfo_mfn, PAGE_KERNEL),
93 0))
94 BUG();
95 HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO);
97 memset(empty_zero_page, 0, PAGE_SIZE);
99 fpp = PAGE_SIZE/sizeof(unsigned long);
100 for (i = 0, j = 0, k = -1; i < max_pfn; i += fpp, j++) {
101 if ((j % fpp) == 0) {
102 k++;
103 pfn_to_mfn_frame_list_list[k] =
104 virt_to_mfn(pfn_to_mfn_frame_list[k]);
105 j = 0;
106 }
107 pfn_to_mfn_frame_list[k][j] =
108 virt_to_mfn(&phys_to_machine_mapping[i]);
109 }
110 HYPERVISOR_shared_info->arch.max_pfn = max_pfn;
111 HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
112 virt_to_mfn(pfn_to_mfn_frame_list_list);
113 }
115 #else /* !(defined(__i386__) || defined(__x86_64__)) */
117 #ifndef HAVE_XEN_PRE_SUSPEND
118 #define xen_pre_suspend() ((void)0)
119 #endif
121 #ifndef HAVE_XEN_POST_SUSPEND
122 #define xen_post_suspend(x) ((void)0)
123 #endif
125 #define switch_idle_mm() ((void)0)
126 #define mm_pin_all() ((void)0)
127 #define pre_suspend() xen_pre_suspend()
128 #define post_suspend(x) xen_post_suspend(x)
130 #endif
132 struct suspend {
133 int fast_suspend;
134 void (*resume_notifier)(void);
135 };
137 static int take_machine_down(void *_suspend)
138 {
139 struct suspend *suspend = _suspend;
140 int suspend_cancelled, err;
141 extern void time_resume(void);
143 if (suspend->fast_suspend) {
144 BUG_ON(!irqs_disabled());
145 } else {
146 BUG_ON(irqs_disabled());
148 for (;;) {
149 err = smp_suspend();
150 if (err)
151 return err;
153 xenbus_suspend();
154 preempt_disable();
156 if (num_online_cpus() == 1)
157 break;
159 preempt_enable();
160 xenbus_suspend_cancel();
161 }
163 local_irq_disable();
164 }
166 mm_pin_all();
167 gnttab_suspend();
168 pre_suspend();
170 /*
171 * This hypercall returns 1 if suspend was cancelled or the domain was
172 * merely checkpointed, and 0 if it is resuming in a new domain.
173 */
174 suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
176 suspend->resume_notifier();
177 post_suspend(suspend_cancelled);
178 gnttab_resume();
179 if (!suspend_cancelled) {
180 irq_resume();
181 #ifdef __x86_64__
182 /*
183 * Older versions of Xen do not save/restore the user %cr3.
184 * We do it here just in case, but there's no need if we are
185 * in fast-suspend mode as that implies a new enough Xen.
186 */
187 if (!suspend->fast_suspend) {
188 struct mmuext_op op;
189 op.cmd = MMUEXT_NEW_USER_BASEPTR;
190 op.arg1.mfn = pfn_to_mfn(__pa(__user_pgd(
191 current->active_mm->pgd)) >> PAGE_SHIFT);
192 if (HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF))
193 BUG();
194 }
195 #endif
196 }
197 time_resume();
199 if (!suspend->fast_suspend)
200 local_irq_enable();
202 return suspend_cancelled;
203 }
205 int __xen_suspend(int fast_suspend, void (*resume_notifier)(void))
206 {
207 int err, suspend_cancelled;
208 struct suspend suspend = { fast_suspend, resume_notifier };
210 BUG_ON(smp_processor_id() != 0);
211 BUG_ON(in_interrupt());
213 #if defined(__i386__) || defined(__x86_64__)
214 if (xen_feature(XENFEAT_auto_translated_physmap)) {
215 printk(KERN_WARNING "Cannot suspend in "
216 "auto_translated_physmap mode.\n");
217 return -EOPNOTSUPP;
218 }
219 #endif
221 /* If we are definitely UP then 'slow mode' is actually faster. */
222 if (num_possible_cpus() == 1)
223 fast_suspend = 0;
225 if (fast_suspend) {
226 xenbus_suspend();
227 err = stop_machine_run(take_machine_down, &suspend, 0);
228 if (err < 0)
229 xenbus_suspend_cancel();
230 } else {
231 err = take_machine_down(&suspend);
232 }
234 if (err < 0)
235 return err;
237 suspend_cancelled = err;
238 if (!suspend_cancelled) {
239 xencons_resume();
240 xenbus_resume();
241 } else {
242 xenbus_suspend_cancel();
243 }
245 if (!fast_suspend)
246 smp_resume();
248 return 0;
249 }