|
@@ -4489,6 +4489,26 @@ void __init set_pageblock_order(void)
|
|
|
|
|
|
#endif /* CONFIG_HUGETLB_PAGE_SIZE_VARIABLE */
|
|
|
|
|
|
+static unsigned long __paginginit calc_memmap_size(unsigned long spanned_pages,
|
|
|
+ unsigned long present_pages)
|
|
|
+{
|
|
|
+ unsigned long pages = spanned_pages;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Provide a more accurate estimation if there are holes within
|
|
|
+ * the zone and SPARSEMEM is in use. If there are holes within the
|
|
|
+ * zone, each populated memory region may cost us one or two extra
|
|
|
+ * memmap pages due to alignment because memmap pages for each
|
|
|
+ * populated regions may not naturally algined on page boundary.
|
|
|
+ * So the (present_pages >> 4) heuristic is a tradeoff for that.
|
|
|
+ */
|
|
|
+ if (spanned_pages > present_pages + (present_pages >> 4) &&
|
|
|
+ IS_ENABLED(CONFIG_SPARSEMEM))
|
|
|
+ pages = present_pages;
|
|
|
+
|
|
|
+ return PAGE_ALIGN(pages * sizeof(struct page)) >> PAGE_SHIFT;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Set up the zone data structures:
|
|
|
* - mark all pages reserved
|
|
@@ -4523,8 +4543,7 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
|
|
|
* is used by this zone for memmap. This affects the watermark
|
|
|
* and per-cpu initialisations
|
|
|
*/
|
|
|
- memmap_pages =
|
|
|
- PAGE_ALIGN(size * sizeof(struct page)) >> PAGE_SHIFT;
|
|
|
+ memmap_pages = calc_memmap_size(size, realsize);
|
|
|
if (freesize >= memmap_pages) {
|
|
|
freesize -= memmap_pages;
|
|
|
if (memmap_pages)
|
|
@@ -4545,6 +4564,9 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
|
|
|
|
|
|
if (!is_highmem_idx(j))
|
|
|
nr_kernel_pages += freesize;
|
|
|
+ /* Charge for highmem memmap if there are enough kernel pages */
|
|
|
+ else if (nr_kernel_pages > memmap_pages * 2)
|
|
|
+ nr_kernel_pages -= memmap_pages;
|
|
|
nr_all_pages += freesize;
|
|
|
|
|
|
zone->spanned_pages = size;
|