aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2011-05-19 08:21:14 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2011-05-26 05:23:24 -0400
commit7b7bf499f79de3f6c85a340c8453a78789523f85 (patch)
tree1d0bf7ae8d5befe135fb7e7cfc455656a0ec7b34
parent4db70f73e56961b9bcdfd0c36c62847a18b7dbb5 (diff)
ARM: 6913/1: sparsemem: allow pfn_valid to be overridden when using SPARSEMEM
In commit eb33575c ("[ARM] Double check memmap is actually valid with a memmap has unexpected holes V2"), a new function, memmap_valid_within, was introduced to mmzone.h so that holes in the memmap which pass pfn_valid in SPARSEMEM configurations can be detected and avoided. The fix to this problem checks that the pfn <-> page linkages are correct by calculating the page for the pfn and then checking that page_to_pfn on that page returns the original pfn. Unfortunately, in SPARSEMEM configurations, this results in reading from the page flags to determine the correct section. Since the memmap here has been freed, junk is read from memory and the check is no longer robust. In the best case, reading from /proc/pagetypeinfo will give you the wrong answer. In the worst case, you get SEGVs, Kernel OOPses and hung CPUs. Furthermore, ioremap implementations that use pfn_valid to disallow the remapping of normal memory will break. This patch allows architectures to provide their own pfn_valid function instead of using the default implementation used by sparsemem. The architecture-specific version is aware of the memmap state and will return false when passed a pfn for a freed page within a valid section. Acked-by: Mel Gorman <mgorman@suse.de> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Tested-by: H Hartley Sweeten <hsweeten@visionengravers.com> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/Kconfig3
-rw-r--r--arch/arm/include/asm/page.h2
-rw-r--r--arch/arm/mm/init.c4
-rw-r--r--include/linux/mmzone.h2
4 files changed, 9 insertions, 2 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 7275009686e6..5be55d950ab5 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1516,6 +1516,9 @@ config ARCH_SPARSEMEM_DEFAULT
1516config ARCH_SELECT_MEMORY_MODEL 1516config ARCH_SELECT_MEMORY_MODEL
1517 def_bool ARCH_SPARSEMEM_ENABLE 1517 def_bool ARCH_SPARSEMEM_ENABLE
1518 1518
1519config HAVE_ARCH_PFN_VALID
1520 def_bool ARCH_HAS_HOLES_MEMORYMODEL || !SPARSEMEM
1521
1519config HIGHMEM 1522config HIGHMEM
1520 bool "High Memory Support" 1523 bool "High Memory Support"
1521 depends on MMU 1524 depends on MMU
diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h
index f51a69595f6e..ac75d0848889 100644
--- a/arch/arm/include/asm/page.h
+++ b/arch/arm/include/asm/page.h
@@ -197,7 +197,7 @@ typedef unsigned long pgprot_t;
197 197
198typedef struct page *pgtable_t; 198typedef struct page *pgtable_t;
199 199
200#ifndef CONFIG_SPARSEMEM 200#ifdef CONFIG_HAVE_ARCH_PFN_VALID
201extern int pfn_valid(unsigned long); 201extern int pfn_valid(unsigned long);
202#endif 202#endif
203 203
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 3f17ea146f0e..bbc3346e8bcd 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -273,13 +273,15 @@ static void __init arm_bootmem_free(unsigned long min, unsigned long max_low,
273 free_area_init_node(0, zone_size, min, zhole_size); 273 free_area_init_node(0, zone_size, min, zhole_size);
274} 274}
275 275
276#ifndef CONFIG_SPARSEMEM 276#ifdef CONFIG_HAVE_ARCH_PFN_VALID
277int pfn_valid(unsigned long pfn) 277int pfn_valid(unsigned long pfn)
278{ 278{
279 return memblock_is_memory(pfn << PAGE_SHIFT); 279 return memblock_is_memory(pfn << PAGE_SHIFT);
280} 280}
281EXPORT_SYMBOL(pfn_valid); 281EXPORT_SYMBOL(pfn_valid);
282#endif
282 283
284#ifndef CONFIG_SPARSEMEM
283static void arm_memory_present(void) 285static void arm_memory_present(void)
284{ 286{
285} 287}
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 217bcf6bca77..261f299c9441 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -1056,12 +1056,14 @@ static inline struct mem_section *__pfn_to_section(unsigned long pfn)
1056 return __nr_to_section(pfn_to_section_nr(pfn)); 1056 return __nr_to_section(pfn_to_section_nr(pfn));
1057} 1057}
1058 1058
1059#ifndef CONFIG_HAVE_ARCH_PFN_VALID
1059static inline int pfn_valid(unsigned long pfn) 1060static inline int pfn_valid(unsigned long pfn)
1060{ 1061{
1061 if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS) 1062 if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS)
1062 return 0; 1063 return 0;
1063 return valid_section(__nr_to_section(pfn_to_section_nr(pfn))); 1064 return valid_section(__nr_to_section(pfn_to_section_nr(pfn)));
1064} 1065}
1066#endif
1065 1067
1066static inline int pfn_present(unsigned long pfn) 1068static inline int pfn_present(unsigned long pfn)
1067{ 1069{