diff options
Diffstat (limited to 'arch/x86_64/kernel')
-rw-r--r-- | arch/x86_64/kernel/setup64.c | 4 | ||||
-rw-r--r-- | arch/x86_64/kernel/smpboot.c | 6 | ||||
-rw-r--r-- | arch/x86_64/kernel/suspend.c | 127 | ||||
-rw-r--r-- | arch/x86_64/kernel/suspend_asm.S | 17 |
4 files changed, 143 insertions, 11 deletions
diff --git a/arch/x86_64/kernel/setup64.c b/arch/x86_64/kernel/setup64.c index bd33be24a386..79190891fbc5 100644 --- a/arch/x86_64/kernel/setup64.c +++ b/arch/x86_64/kernel/setup64.c | |||
@@ -87,6 +87,10 @@ void __init setup_per_cpu_areas(void) | |||
87 | int i; | 87 | int i; |
88 | unsigned long size; | 88 | unsigned long size; |
89 | 89 | ||
90 | #ifdef CONFIG_HOTPLUG_CPU | ||
91 | prefill_possible_map(); | ||
92 | #endif | ||
93 | |||
90 | /* Copy section for each CPU (we discard the original) */ | 94 | /* Copy section for each CPU (we discard the original) */ |
91 | size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES); | 95 | size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES); |
92 | #ifdef CONFIG_MODULES | 96 | #ifdef CONFIG_MODULES |
diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c index e12d7baeb33e..658a81b33f3b 100644 --- a/arch/x86_64/kernel/smpboot.c +++ b/arch/x86_64/kernel/smpboot.c | |||
@@ -892,7 +892,7 @@ static __init void disable_smp(void) | |||
892 | * those NR_CPUS, hence cpu_possible_map represents entire NR_CPUS range. | 892 | * those NR_CPUS, hence cpu_possible_map represents entire NR_CPUS range. |
893 | * - Ashok Raj | 893 | * - Ashok Raj |
894 | */ | 894 | */ |
895 | static void prefill_possible_map(void) | 895 | __init void prefill_possible_map(void) |
896 | { | 896 | { |
897 | int i; | 897 | int i; |
898 | for (i = 0; i < NR_CPUS; i++) | 898 | for (i = 0; i < NR_CPUS; i++) |
@@ -967,10 +967,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus) | |||
967 | current_cpu_data = boot_cpu_data; | 967 | current_cpu_data = boot_cpu_data; |
968 | current_thread_info()->cpu = 0; /* needed? */ | 968 | current_thread_info()->cpu = 0; /* needed? */ |
969 | 969 | ||
970 | #ifdef CONFIG_HOTPLUG_CPU | ||
971 | prefill_possible_map(); | ||
972 | #endif | ||
973 | |||
974 | if (smp_sanity_check(max_cpus) < 0) { | 970 | if (smp_sanity_check(max_cpus) < 0) { |
975 | printk(KERN_INFO "SMP disabled\n"); | 971 | printk(KERN_INFO "SMP disabled\n"); |
976 | disable_smp(); | 972 | disable_smp(); |
diff --git a/arch/x86_64/kernel/suspend.c b/arch/x86_64/kernel/suspend.c index ebb9abf3ce6d..f066c6ab3618 100644 --- a/arch/x86_64/kernel/suspend.c +++ b/arch/x86_64/kernel/suspend.c | |||
@@ -11,6 +11,8 @@ | |||
11 | #include <linux/smp.h> | 11 | #include <linux/smp.h> |
12 | #include <linux/suspend.h> | 12 | #include <linux/suspend.h> |
13 | #include <asm/proto.h> | 13 | #include <asm/proto.h> |
14 | #include <asm/page.h> | ||
15 | #include <asm/pgtable.h> | ||
14 | 16 | ||
15 | struct saved_context saved_context; | 17 | struct saved_context saved_context; |
16 | 18 | ||
@@ -140,4 +142,129 @@ void fix_processor_context(void) | |||
140 | 142 | ||
141 | } | 143 | } |
142 | 144 | ||
145 | #ifdef CONFIG_SOFTWARE_SUSPEND | ||
146 | /* Defined in arch/x86_64/kernel/suspend_asm.S */ | ||
147 | extern int restore_image(void); | ||
143 | 148 | ||
149 | pgd_t *temp_level4_pgt; | ||
150 | |||
151 | static void **pages; | ||
152 | |||
153 | static inline void *__add_page(void) | ||
154 | { | ||
155 | void **c; | ||
156 | |||
157 | c = (void **)get_usable_page(GFP_ATOMIC); | ||
158 | if (c) { | ||
159 | *c = pages; | ||
160 | pages = c; | ||
161 | } | ||
162 | return c; | ||
163 | } | ||
164 | |||
165 | static inline void *__next_page(void) | ||
166 | { | ||
167 | void **c; | ||
168 | |||
169 | c = pages; | ||
170 | if (c) { | ||
171 | pages = *c; | ||
172 | *c = NULL; | ||
173 | } | ||
174 | return c; | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * Try to allocate as many usable pages as needed and daisy chain them. | ||
179 | * If one allocation fails, free the pages allocated so far | ||
180 | */ | ||
181 | static int alloc_usable_pages(unsigned long n) | ||
182 | { | ||
183 | void *p; | ||
184 | |||
185 | pages = NULL; | ||
186 | do | ||
187 | if (!__add_page()) | ||
188 | break; | ||
189 | while (--n); | ||
190 | if (n) { | ||
191 | p = __next_page(); | ||
192 | while (p) { | ||
193 | free_page((unsigned long)p); | ||
194 | p = __next_page(); | ||
195 | } | ||
196 | return -ENOMEM; | ||
197 | } | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static void res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end) | ||
202 | { | ||
203 | long i, j; | ||
204 | |||
205 | i = pud_index(address); | ||
206 | pud = pud + i; | ||
207 | for (; i < PTRS_PER_PUD; pud++, i++) { | ||
208 | unsigned long paddr; | ||
209 | pmd_t *pmd; | ||
210 | |||
211 | paddr = address + i*PUD_SIZE; | ||
212 | if (paddr >= end) | ||
213 | break; | ||
214 | |||
215 | pmd = (pmd_t *)__next_page(); | ||
216 | set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE)); | ||
217 | for (j = 0; j < PTRS_PER_PMD; pmd++, j++, paddr += PMD_SIZE) { | ||
218 | unsigned long pe; | ||
219 | |||
220 | if (paddr >= end) | ||
221 | break; | ||
222 | pe = _PAGE_NX | _PAGE_PSE | _KERNPG_TABLE | paddr; | ||
223 | pe &= __supported_pte_mask; | ||
224 | set_pmd(pmd, __pmd(pe)); | ||
225 | } | ||
226 | } | ||
227 | } | ||
228 | |||
229 | static void set_up_temporary_mappings(void) | ||
230 | { | ||
231 | unsigned long start, end, next; | ||
232 | |||
233 | temp_level4_pgt = (pgd_t *)__next_page(); | ||
234 | |||
235 | /* It is safe to reuse the original kernel mapping */ | ||
236 | set_pgd(temp_level4_pgt + pgd_index(__START_KERNEL_map), | ||
237 | init_level4_pgt[pgd_index(__START_KERNEL_map)]); | ||
238 | |||
239 | /* Set up the direct mapping from scratch */ | ||
240 | start = (unsigned long)pfn_to_kaddr(0); | ||
241 | end = (unsigned long)pfn_to_kaddr(end_pfn); | ||
242 | |||
243 | for (; start < end; start = next) { | ||
244 | pud_t *pud = (pud_t *)__next_page(); | ||
245 | next = start + PGDIR_SIZE; | ||
246 | if (next > end) | ||
247 | next = end; | ||
248 | res_phys_pud_init(pud, __pa(start), __pa(next)); | ||
249 | set_pgd(temp_level4_pgt + pgd_index(start), | ||
250 | mk_kernel_pgd(__pa(pud))); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | int swsusp_arch_resume(void) | ||
255 | { | ||
256 | unsigned long n; | ||
257 | |||
258 | n = ((end_pfn << PAGE_SHIFT) + PUD_SIZE - 1) >> PUD_SHIFT; | ||
259 | n += (n + PTRS_PER_PUD - 1) / PTRS_PER_PUD + 1; | ||
260 | pr_debug("swsusp_arch_resume(): pages needed = %lu\n", n); | ||
261 | if (alloc_usable_pages(n)) { | ||
262 | free_eaten_memory(); | ||
263 | return -ENOMEM; | ||
264 | } | ||
265 | /* We have got enough memory and from now on we cannot recover */ | ||
266 | set_up_temporary_mappings(); | ||
267 | restore_image(); | ||
268 | return 0; | ||
269 | } | ||
270 | #endif /* CONFIG_SOFTWARE_SUSPEND */ | ||
diff --git a/arch/x86_64/kernel/suspend_asm.S b/arch/x86_64/kernel/suspend_asm.S index 4d659e97df10..320b6fb00cca 100644 --- a/arch/x86_64/kernel/suspend_asm.S +++ b/arch/x86_64/kernel/suspend_asm.S | |||
@@ -39,12 +39,13 @@ ENTRY(swsusp_arch_suspend) | |||
39 | call swsusp_save | 39 | call swsusp_save |
40 | ret | 40 | ret |
41 | 41 | ||
42 | ENTRY(swsusp_arch_resume) | 42 | ENTRY(restore_image) |
43 | /* set up cr3 */ | 43 | /* switch to temporary page tables */ |
44 | leaq init_level4_pgt(%rip),%rax | 44 | movq $__PAGE_OFFSET, %rdx |
45 | subq $__START_KERNEL_map,%rax | 45 | movq temp_level4_pgt(%rip), %rax |
46 | movq %rax,%cr3 | 46 | subq %rdx, %rax |
47 | 47 | movq %rax, %cr3 | |
48 | /* Flush TLB */ | ||
48 | movq mmu_cr4_features(%rip), %rax | 49 | movq mmu_cr4_features(%rip), %rax |
49 | movq %rax, %rdx | 50 | movq %rax, %rdx |
50 | andq $~(1<<7), %rdx # PGE | 51 | andq $~(1<<7), %rdx # PGE |
@@ -69,6 +70,10 @@ loop: | |||
69 | movq pbe_next(%rdx), %rdx | 70 | movq pbe_next(%rdx), %rdx |
70 | jmp loop | 71 | jmp loop |
71 | done: | 72 | done: |
73 | /* go back to the original page tables */ | ||
74 | leaq init_level4_pgt(%rip), %rax | ||
75 | subq $__START_KERNEL_map, %rax | ||
76 | movq %rax, %cr3 | ||
72 | /* Flush TLB, including "global" things (vmalloc) */ | 77 | /* Flush TLB, including "global" things (vmalloc) */ |
73 | movq mmu_cr4_features(%rip), %rax | 78 | movq mmu_cr4_features(%rip), %rax |
74 | movq %rax, %rdx | 79 | movq %rax, %rdx |