x86, hibernate: fix breakage on x86_32 with CONFIG_NUMA set

Impact: fix crash during hibernation on 32-bit NUMA

The NUMA code on x86_32 creates special memory mapping that allows
each node's pgdat to be located in this node's memory. For this
purpose it allocates a memory area at the end of each node's memory
and maps this area so that it is accessible with virtual addresses
belonging to low memory. As a result, if there is high memory,
these NUMA-allocated areas are physically located in high memory,
although they are mapped to low memory addresses.

Our hibernation code does not take that into account and for this
reason hibernation fails on all x86_32 systems with CONFIG_NUMA=y and
with high memory present. Fix this by adding a special mapping for
the NUMA-allocated memory areas to the temporary page tables created
during the last phase of resume.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by Rafael J. Wysocki and committed by Ingo Molnar 97a70e54 3edac25f

+43
+4
arch/x86/include/asm/mmzone_32.h
··· 34 34 35 35 extern int early_pfn_to_nid(unsigned long pfn); 36 36 37 + extern void resume_map_numa_kva(pgd_t *pgd); 38 + 37 39 #else /* !CONFIG_NUMA */ 38 40 39 41 #define get_memcfg_numa get_memcfg_numa_flat 42 + 43 + static inline void resume_map_numa_kva(pgd_t *pgd) {} 40 44 41 45 #endif /* CONFIG_NUMA */ 42 46
+35
arch/x86/mm/numa_32.c
··· 222 222 } 223 223 } 224 224 225 + #ifdef CONFIG_HIBERNATION 226 + /** 227 + * resume_map_numa_kva - add KVA mapping to the temporary page tables created 228 + * during resume from hibernation 229 + * @pgd_base - temporary resume page directory 230 + */ 231 + void resume_map_numa_kva(pgd_t *pgd_base) 232 + { 233 + int node; 234 + 235 + for_each_online_node(node) { 236 + unsigned long start_va, start_pfn, size, pfn; 237 + 238 + start_va = (unsigned long)node_remap_start_vaddr[node]; 239 + start_pfn = node_remap_start_pfn[node]; 240 + size = node_remap_size[node]; 241 + 242 + printk(KERN_DEBUG "%s: node %d\n", __FUNCTION__, node); 243 + 244 + for (pfn = 0; pfn < size; pfn += PTRS_PER_PTE) { 245 + unsigned long vaddr = start_va + (pfn << PAGE_SHIFT); 246 + pgd_t *pgd = pgd_base + pgd_index(vaddr); 247 + pud_t *pud = pud_offset(pgd, vaddr); 248 + pmd_t *pmd = pmd_offset(pud, vaddr); 249 + 250 + set_pmd(pmd, pfn_pmd(start_pfn + pfn, 251 + PAGE_KERNEL_LARGE_EXEC)); 252 + 253 + printk(KERN_DEBUG "%s: %08lx -> pfn %08lx\n", 254 + __FUNCTION__, vaddr, start_pfn + pfn); 255 + } 256 + } 257 + } 258 + #endif 259 + 225 260 static unsigned long calculate_numa_remap_pages(void) 226 261 { 227 262 int nid;
+4
arch/x86/power/hibernate_32.c
··· 12 12 #include <asm/system.h> 13 13 #include <asm/page.h> 14 14 #include <asm/pgtable.h> 15 + #include <asm/mmzone.h> 15 16 16 17 /* Defined in hibernate_asm_32.S */ 17 18 extern int restore_image(void); ··· 128 127 } 129 128 } 130 129 } 130 + 131 + resume_map_numa_kva(pgd_base); 132 + 131 133 return 0; 132 134 } 133 135