[PATCH] x86/32/highmem: check if the zone is matched when free highmem pages on init

From: Joonsoo Kim
Date: Thu May 17 2018 - 21:52:05 EST


If CONFIG_CMA is enabled, it extends the span of the MOVABLE_ZONE
to manage the CMA memory later. And, in this case, the span of the
MOVABLE_ZONE could overlap the other zone's memory. We need to
avoid freeing this overlapped memory here since it would be the
memory of the other zone. Therefore, this patch adds a check
whether the page is indeed on the requested zone or not. Skipped
page will be freed when the memory of the matched zone is freed.

Reported-by: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx>
Signed-off-by: Joonsoo Kim <iamjoonsoo.kim@xxxxxxx>
---
arch/x86/include/asm/highmem.h | 4 ++--
arch/x86/mm/highmem_32.c | 5 ++++-
arch/x86/mm/init_32.c | 25 +++++++++++++++++++++----
3 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/arch/x86/include/asm/highmem.h b/arch/x86/include/asm/highmem.h
index a805993..e383f57 100644
--- a/arch/x86/include/asm/highmem.h
+++ b/arch/x86/include/asm/highmem.h
@@ -72,8 +72,8 @@ void *kmap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot);

#define flush_cache_kmaps() do { } while (0)

-extern void add_highpages_with_active_regions(int nid, unsigned long start_pfn,
- unsigned long end_pfn);
+extern void add_highpages_with_active_regions(int nid, struct zone *zone,
+ unsigned long start_pfn, unsigned long end_pfn);

#endif /* __KERNEL__ */

diff --git a/arch/x86/mm/highmem_32.c b/arch/x86/mm/highmem_32.c
index 6d18b70..bf9f5b8 100644
--- a/arch/x86/mm/highmem_32.c
+++ b/arch/x86/mm/highmem_32.c
@@ -120,6 +120,9 @@ void __init set_highmem_pages_init(void)
if (!is_highmem(zone))
continue;

+ if (!populated_zone(zone))
+ continue;
+
zone_start_pfn = zone->zone_start_pfn;
zone_end_pfn = zone_start_pfn + zone->spanned_pages;

@@ -127,7 +130,7 @@ void __init set_highmem_pages_init(void)
printk(KERN_INFO "Initializing %s for node %d (%08lx:%08lx)\n",
zone->name, nid, zone_start_pfn, zone_end_pfn);

- add_highpages_with_active_regions(nid, zone_start_pfn,
+ add_highpages_with_active_regions(nid, zone, zone_start_pfn,
zone_end_pfn);
}
}
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index 8008db2..f646072 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -431,7 +431,7 @@ static void __init permanent_kmaps_init(pgd_t *pgd_base)
pkmap_page_table = pte;
}

-void __init add_highpages_with_active_regions(int nid,
+void __init add_highpages_with_active_regions(int nid, struct zone *zone,
unsigned long start_pfn, unsigned long end_pfn)
{
phys_addr_t start, end;
@@ -442,9 +442,26 @@ void __init add_highpages_with_active_regions(int nid,
start_pfn, end_pfn);
unsigned long e_pfn = clamp_t(unsigned long, PFN_DOWN(end),
start_pfn, end_pfn);
- for ( ; pfn < e_pfn; pfn++)
- if (pfn_valid(pfn))
- free_highmem_page(pfn_to_page(pfn));
+ for ( ; pfn < e_pfn; pfn++) {
+ struct page *page;
+
+ if (!pfn_valid(pfn))
+ continue;
+
+ page = pfn_to_page(pfn);
+
+ /*
+ * If CONFIG_CMA is enabled, it extends the span of
+ * the MOVABLE_ZONE to manage the CMA memory
+ * in the future. And, in this case, the span of the
+ * MOVABLE_ZONE could overlap the other zone's memory.
+ * We need to avoid freeing this memory here.
+ */
+ if (IS_ENABLED(CONFIG_CMA) && page_zone(page) != zone)
+ continue;
+
+ free_highmem_page(pfn_to_page(pfn));
+ }
}
}
#else
--
2.7.4