Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/mm: Clean up memory hotplug failure paths

This makes a number of cleanups to handling of mapping failures during
memory hotplug on Power:

For errors creating the linear mapping for the hot-added region:
* This is now reported with EFAULT which is more appropriate than the
previous EINVAL (the failure is unlikely to be related to the
function's parameters)
* An error in this path now prints a warning message, rather than just
silently failing to add the extra memory.
* Previously a failure here could result in the region being partially
mapped. We now clean up any partial mapping before failing.

For errors creating the vmemmap for the hot-added region:
* This is now reported with EFAULT instead of causing a BUG() - this
could happen for external reason (e.g. full hash table) so it's better
to handle this non-fatally
* An error message is also printed, so the failure won't be silent
* As above a failure could cause a partially mapped region, we now
clean this up. [mpe: move htab_remove_mapping() out of #ifdef
CONFIG_MEMORY_HOTPLUG to enable this]

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Paul Mackerras <paulus@samba.org>
Reviewed-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

David Gibson and committed by
Michael Ellerman
1dace6c6 27828f98

+44 -19
+10 -5
arch/powerpc/mm/hash_utils_64.c
··· 263 263 return ret < 0 ? ret : 0; 264 264 } 265 265 266 - #ifdef CONFIG_MEMORY_HOTPLUG 267 266 int htab_remove_mapping(unsigned long vstart, unsigned long vend, 268 267 int psize, int ssize) 269 268 { ··· 289 290 290 291 return ret; 291 292 } 292 - #endif /* CONFIG_MEMORY_HOTPLUG */ 293 293 294 294 static int __init htab_dt_scan_seg_sizes(unsigned long node, 295 295 const char *uname, int depth, ··· 638 640 #ifdef CONFIG_MEMORY_HOTPLUG 639 641 int create_section_mapping(unsigned long start, unsigned long end) 640 642 { 641 - return htab_bolt_mapping(start, end, __pa(start), 642 - pgprot_val(PAGE_KERNEL), mmu_linear_psize, 643 - mmu_kernel_ssize); 643 + int rc = htab_bolt_mapping(start, end, __pa(start), 644 + pgprot_val(PAGE_KERNEL), mmu_linear_psize, 645 + mmu_kernel_ssize); 646 + 647 + if (rc < 0) { 648 + int rc2 = htab_remove_mapping(start, end, mmu_linear_psize, 649 + mmu_kernel_ssize); 650 + BUG_ON(rc2 && (rc2 != -ENOENT)); 651 + } 652 + return rc; 644 653 } 645 654 646 655 int remove_section_mapping(unsigned long start, unsigned long end)
+26 -12
arch/powerpc/mm/init_64.c
··· 188 188 */ 189 189 190 190 #ifdef CONFIG_PPC_BOOK3E 191 - static void __meminit vmemmap_create_mapping(unsigned long start, 192 - unsigned long page_size, 193 - unsigned long phys) 191 + static int __meminit vmemmap_create_mapping(unsigned long start, 192 + unsigned long page_size, 193 + unsigned long phys) 194 194 { 195 195 /* Create a PTE encoding without page size */ 196 196 unsigned long i, flags = _PAGE_PRESENT | _PAGE_ACCESSED | ··· 208 208 */ 209 209 for (i = 0; i < page_size; i += PAGE_SIZE) 210 210 BUG_ON(map_kernel_page(start + i, phys, flags)); 211 + 212 + return 0; 211 213 } 212 214 213 215 #ifdef CONFIG_MEMORY_HOTPLUG ··· 219 217 } 220 218 #endif 221 219 #else /* CONFIG_PPC_BOOK3E */ 222 - static void __meminit vmemmap_create_mapping(unsigned long start, 223 - unsigned long page_size, 224 - unsigned long phys) 220 + static int __meminit vmemmap_create_mapping(unsigned long start, 221 + unsigned long page_size, 222 + unsigned long phys) 225 223 { 226 - int mapped = htab_bolt_mapping(start, start + page_size, phys, 227 - pgprot_val(PAGE_KERNEL), 228 - mmu_vmemmap_psize, 229 - mmu_kernel_ssize); 230 - BUG_ON(mapped < 0); 224 + int rc = htab_bolt_mapping(start, start + page_size, phys, 225 + pgprot_val(PAGE_KERNEL), 226 + mmu_vmemmap_psize, mmu_kernel_ssize); 227 + if (rc < 0) { 228 + int rc2 = htab_remove_mapping(start, start + page_size, 229 + mmu_vmemmap_psize, 230 + mmu_kernel_ssize); 231 + BUG_ON(rc2 && (rc2 != -ENOENT)); 232 + } 233 + return rc; 231 234 } 232 235 233 236 #ifdef CONFIG_MEMORY_HOTPLUG ··· 311 304 312 305 for (; start < end; start += page_size) { 313 306 void *p; 307 + int rc; 314 308 315 309 if (vmemmap_populated(start, page_size)) 316 310 continue; ··· 325 317 pr_debug(" * %016lx..%016lx allocated at %p\n", 326 318 start, start + page_size, p); 327 319 328 - vmemmap_create_mapping(start, page_size, __pa(p)); 320 + rc = vmemmap_create_mapping(start, page_size, __pa(p)); 321 + if (rc < 0) { 322 + pr_warning( 323 + "vmemmap_populate: Unable to create vmemmap mapping: %d\n", 324 + rc); 325 + return -EFAULT; 326 + } 329 327 } 330 328 331 329 return 0;
+8 -2
arch/powerpc/mm/mem.c
··· 119 119 struct zone *zone; 120 120 unsigned long start_pfn = start >> PAGE_SHIFT; 121 121 unsigned long nr_pages = size >> PAGE_SHIFT; 122 + int rc; 122 123 123 124 pgdata = NODE_DATA(nid); 124 125 125 126 start = (unsigned long)__va(start); 126 - if (create_section_mapping(start, start + size)) 127 - return -EINVAL; 127 + rc = create_section_mapping(start, start + size); 128 + if (rc) { 129 + pr_warning( 130 + "Unable to create mapping for hot added memory 0x%llx..0x%llx: %d\n", 131 + start, start + size, rc); 132 + return -EFAULT; 133 + } 128 134 129 135 /* this should work for most non-highmem platforms */ 130 136 zone = pgdata->node_zones +