1 | #include <linux/config.h> |
---|
2 | #include <linux/stop_machine.h> |
---|
3 | #include <xen/evtchn.h> |
---|
4 | #include <xen/gnttab.h> |
---|
5 | #include <xen/xenbus.h> |
---|
6 | #include "platform-pci.h" |
---|
7 | #include <asm/hypervisor.h> |
---|
8 | |
---|
9 | struct ap_suspend_info { |
---|
10 | int do_spin; |
---|
11 | atomic_t nr_spinning; |
---|
12 | }; |
---|
13 | |
---|
14 | /* |
---|
15 | * Spinning prevents, for example, APs touching grant table entries while |
---|
16 | * the shared grant table is not mapped into the address space imemdiately |
---|
17 | * after resume. |
---|
18 | */ |
---|
19 | static void ap_suspend(void *_info) |
---|
20 | { |
---|
21 | struct ap_suspend_info *info = _info; |
---|
22 | |
---|
23 | BUG_ON(!irqs_disabled()); |
---|
24 | |
---|
25 | atomic_inc(&info->nr_spinning); |
---|
26 | mb(); |
---|
27 | |
---|
28 | while (info->do_spin) { |
---|
29 | cpu_relax(); |
---|
30 | HYPERVISOR_yield(); |
---|
31 | } |
---|
32 | |
---|
33 | mb(); |
---|
34 | atomic_dec(&info->nr_spinning); |
---|
35 | } |
---|
36 | |
---|
37 | static int bp_suspend(void) |
---|
38 | { |
---|
39 | int suspend_cancelled; |
---|
40 | |
---|
41 | BUG_ON(!irqs_disabled()); |
---|
42 | |
---|
43 | suspend_cancelled = HYPERVISOR_shutdown(SHUTDOWN_suspend); |
---|
44 | |
---|
45 | if (!suspend_cancelled) { |
---|
46 | platform_pci_resume(); |
---|
47 | gnttab_resume(); |
---|
48 | irq_resume(); |
---|
49 | } |
---|
50 | |
---|
51 | return suspend_cancelled; |
---|
52 | } |
---|
53 | |
---|
54 | int __xen_suspend(int fast_suspend) |
---|
55 | { |
---|
56 | int err, suspend_cancelled, nr_cpus; |
---|
57 | struct ap_suspend_info info; |
---|
58 | |
---|
59 | xenbus_suspend(); |
---|
60 | |
---|
61 | preempt_disable(); |
---|
62 | |
---|
63 | /* Prevent any races with evtchn_interrupt() handler. */ |
---|
64 | disable_irq(xen_platform_pdev->irq); |
---|
65 | |
---|
66 | info.do_spin = 1; |
---|
67 | atomic_set(&info.nr_spinning, 0); |
---|
68 | smp_mb(); |
---|
69 | |
---|
70 | nr_cpus = num_online_cpus() - 1; |
---|
71 | |
---|
72 | err = smp_call_function(ap_suspend, &info, 0, 0); |
---|
73 | if (err < 0) { |
---|
74 | preempt_enable(); |
---|
75 | xenbus_suspend_cancel(); |
---|
76 | return err; |
---|
77 | } |
---|
78 | |
---|
79 | while (atomic_read(&info.nr_spinning) != nr_cpus) |
---|
80 | cpu_relax(); |
---|
81 | |
---|
82 | local_irq_disable(); |
---|
83 | suspend_cancelled = bp_suspend(); |
---|
84 | local_irq_enable(); |
---|
85 | |
---|
86 | smp_mb(); |
---|
87 | info.do_spin = 0; |
---|
88 | while (atomic_read(&info.nr_spinning) != 0) |
---|
89 | cpu_relax(); |
---|
90 | |
---|
91 | enable_irq(xen_platform_pdev->irq); |
---|
92 | |
---|
93 | preempt_enable(); |
---|
94 | |
---|
95 | if (!suspend_cancelled) |
---|
96 | xenbus_resume(); |
---|
97 | else |
---|
98 | xenbus_suspend_cancel(); |
---|
99 | |
---|
100 | return 0; |
---|
101 | } |
---|