|
@@ -223,6 +223,19 @@ static char * const zone_names[MAX_NR_ZONES] = {
|
|
#endif
|
|
#endif
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+char * const migratetype_names[MIGRATE_TYPES] = {
|
|
|
|
+ "Unmovable",
|
|
|
|
+ "Movable",
|
|
|
|
+ "Reclaimable",
|
|
|
|
+ "HighAtomic",
|
|
|
|
+#ifdef CONFIG_CMA
|
|
|
|
+ "CMA",
|
|
|
|
+#endif
|
|
|
|
+#ifdef CONFIG_MEMORY_ISOLATION
|
|
|
|
+ "Isolate",
|
|
|
|
+#endif
|
|
|
|
+};
|
|
|
|
+
|
|
compound_page_dtor * const compound_page_dtors[] = {
|
|
compound_page_dtor * const compound_page_dtors[] = {
|
|
NULL,
|
|
NULL,
|
|
free_compound_page,
|
|
free_compound_page,
|
|
@@ -247,6 +260,7 @@ static unsigned long __meminitdata arch_zone_highest_possible_pfn[MAX_NR_ZONES];
|
|
static unsigned long __initdata required_kernelcore;
|
|
static unsigned long __initdata required_kernelcore;
|
|
static unsigned long __initdata required_movablecore;
|
|
static unsigned long __initdata required_movablecore;
|
|
static unsigned long __meminitdata zone_movable_pfn[MAX_NUMNODES];
|
|
static unsigned long __meminitdata zone_movable_pfn[MAX_NUMNODES];
|
|
|
|
+static bool mirrored_kernelcore;
|
|
|
|
|
|
/* movable_zone is the "real" zone pages in ZONE_MOVABLE are taken from */
|
|
/* movable_zone is the "real" zone pages in ZONE_MOVABLE are taken from */
|
|
int movable_zone;
|
|
int movable_zone;
|
|
@@ -416,7 +430,7 @@ static void bad_page(struct page *page, const char *reason,
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
if (nr_unshown) {
|
|
if (nr_unshown) {
|
|
- printk(KERN_ALERT
|
|
|
|
|
|
+ pr_alert(
|
|
"BUG: Bad page state: %lu messages suppressed\n",
|
|
"BUG: Bad page state: %lu messages suppressed\n",
|
|
nr_unshown);
|
|
nr_unshown);
|
|
nr_unshown = 0;
|
|
nr_unshown = 0;
|
|
@@ -426,9 +440,14 @@ static void bad_page(struct page *page, const char *reason,
|
|
if (nr_shown++ == 0)
|
|
if (nr_shown++ == 0)
|
|
resume = jiffies + 60 * HZ;
|
|
resume = jiffies + 60 * HZ;
|
|
|
|
|
|
- printk(KERN_ALERT "BUG: Bad page state in process %s pfn:%05lx\n",
|
|
|
|
|
|
+ pr_alert("BUG: Bad page state in process %s pfn:%05lx\n",
|
|
current->comm, page_to_pfn(page));
|
|
current->comm, page_to_pfn(page));
|
|
- dump_page_badflags(page, reason, bad_flags);
|
|
|
|
|
|
+ __dump_page(page, reason);
|
|
|
|
+ bad_flags &= page->flags;
|
|
|
|
+ if (bad_flags)
|
|
|
|
+ pr_alert("bad because of flags: %#lx(%pGp)\n",
|
|
|
|
+ bad_flags, &bad_flags);
|
|
|
|
+ dump_page_owner(page);
|
|
|
|
|
|
print_modules();
|
|
print_modules();
|
|
dump_stack();
|
|
dump_stack();
|
|
@@ -477,7 +496,8 @@ void prep_compound_page(struct page *page, unsigned int order)
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_PAGEALLOC
|
|
#ifdef CONFIG_DEBUG_PAGEALLOC
|
|
unsigned int _debug_guardpage_minorder;
|
|
unsigned int _debug_guardpage_minorder;
|
|
-bool _debug_pagealloc_enabled __read_mostly;
|
|
|
|
|
|
+bool _debug_pagealloc_enabled __read_mostly
|
|
|
|
+ = IS_ENABLED(CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT);
|
|
bool _debug_guardpage_enabled __read_mostly;
|
|
bool _debug_guardpage_enabled __read_mostly;
|
|
|
|
|
|
static int __init early_debug_pagealloc(char *buf)
|
|
static int __init early_debug_pagealloc(char *buf)
|
|
@@ -488,6 +508,9 @@ static int __init early_debug_pagealloc(char *buf)
|
|
if (strcmp(buf, "on") == 0)
|
|
if (strcmp(buf, "on") == 0)
|
|
_debug_pagealloc_enabled = true;
|
|
_debug_pagealloc_enabled = true;
|
|
|
|
|
|
|
|
+ if (strcmp(buf, "off") == 0)
|
|
|
|
+ _debug_pagealloc_enabled = false;
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
early_param("debug_pagealloc", early_debug_pagealloc);
|
|
early_param("debug_pagealloc", early_debug_pagealloc);
|
|
@@ -1002,6 +1025,7 @@ static bool free_pages_prepare(struct page *page, unsigned int order)
|
|
PAGE_SIZE << order);
|
|
PAGE_SIZE << order);
|
|
}
|
|
}
|
|
arch_free_page(page, order);
|
|
arch_free_page(page, order);
|
|
|
|
+ kernel_poison_pages(page, 1 << order, 0);
|
|
kernel_map_pages(page, 1 << order, 0);
|
|
kernel_map_pages(page, 1 << order, 0);
|
|
|
|
|
|
return true;
|
|
return true;
|
|
@@ -1104,6 +1128,75 @@ void __init __free_pages_bootmem(struct page *page, unsigned long pfn,
|
|
return __free_pages_boot_core(page, pfn, order);
|
|
return __free_pages_boot_core(page, pfn, order);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Check that the whole (or subset of) a pageblock given by the interval of
|
|
|
|
+ * [start_pfn, end_pfn) is valid and within the same zone, before scanning it
|
|
|
|
+ * with the migration of free compaction scanner. The scanners then need to
|
|
|
|
+ * use only pfn_valid_within() check for arches that allow holes within
|
|
|
|
+ * pageblocks.
|
|
|
|
+ *
|
|
|
|
+ * Return struct page pointer of start_pfn, or NULL if checks were not passed.
|
|
|
|
+ *
|
|
|
|
+ * It's possible on some configurations to have a setup like node0 node1 node0
|
|
|
|
+ * i.e. it's possible that all pages within a zones range of pages do not
|
|
|
|
+ * belong to a single zone. We assume that a border between node0 and node1
|
|
|
|
+ * can occur within a single pageblock, but not a node0 node1 node0
|
|
|
|
+ * interleaving within a single pageblock. It is therefore sufficient to check
|
|
|
|
+ * the first and last page of a pageblock and avoid checking each individual
|
|
|
|
+ * page in a pageblock.
|
|
|
|
+ */
|
|
|
|
+struct page *__pageblock_pfn_to_page(unsigned long start_pfn,
|
|
|
|
+ unsigned long end_pfn, struct zone *zone)
|
|
|
|
+{
|
|
|
|
+ struct page *start_page;
|
|
|
|
+ struct page *end_page;
|
|
|
|
+
|
|
|
|
+ /* end_pfn is one past the range we are checking */
|
|
|
|
+ end_pfn--;
|
|
|
|
+
|
|
|
|
+ if (!pfn_valid(start_pfn) || !pfn_valid(end_pfn))
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ start_page = pfn_to_page(start_pfn);
|
|
|
|
+
|
|
|
|
+ if (page_zone(start_page) != zone)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ end_page = pfn_to_page(end_pfn);
|
|
|
|
+
|
|
|
|
+ /* This gives a shorter code than deriving page_zone(end_page) */
|
|
|
|
+ if (page_zone_id(start_page) != page_zone_id(end_page))
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ return start_page;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void set_zone_contiguous(struct zone *zone)
|
|
|
|
+{
|
|
|
|
+ unsigned long block_start_pfn = zone->zone_start_pfn;
|
|
|
|
+ unsigned long block_end_pfn;
|
|
|
|
+
|
|
|
|
+ block_end_pfn = ALIGN(block_start_pfn + 1, pageblock_nr_pages);
|
|
|
|
+ for (; block_start_pfn < zone_end_pfn(zone);
|
|
|
|
+ block_start_pfn = block_end_pfn,
|
|
|
|
+ block_end_pfn += pageblock_nr_pages) {
|
|
|
|
+
|
|
|
|
+ block_end_pfn = min(block_end_pfn, zone_end_pfn(zone));
|
|
|
|
+
|
|
|
|
+ if (!__pageblock_pfn_to_page(block_start_pfn,
|
|
|
|
+ block_end_pfn, zone))
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* We confirm that there is no hole */
|
|
|
|
+ zone->contiguous = true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void clear_zone_contiguous(struct zone *zone)
|
|
|
|
+{
|
|
|
|
+ zone->contiguous = false;
|
|
|
|
+}
|
|
|
|
+
|
|
#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
|
|
#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
|
|
static void __init deferred_free_range(struct page *page,
|
|
static void __init deferred_free_range(struct page *page,
|
|
unsigned long pfn, int nr_pages)
|
|
unsigned long pfn, int nr_pages)
|
|
@@ -1254,9 +1347,13 @@ free_range:
|
|
pgdat_init_report_one_done();
|
|
pgdat_init_report_one_done();
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+#endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */
|
|
|
|
|
|
void __init page_alloc_init_late(void)
|
|
void __init page_alloc_init_late(void)
|
|
{
|
|
{
|
|
|
|
+ struct zone *zone;
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
|
|
int nid;
|
|
int nid;
|
|
|
|
|
|
/* There will be num_node_state(N_MEMORY) threads */
|
|
/* There will be num_node_state(N_MEMORY) threads */
|
|
@@ -1270,8 +1367,11 @@ void __init page_alloc_init_late(void)
|
|
|
|
|
|
/* Reinit limits that are based on free pages after the kernel is up */
|
|
/* Reinit limits that are based on free pages after the kernel is up */
|
|
files_maxfiles_init();
|
|
files_maxfiles_init();
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ for_each_populated_zone(zone)
|
|
|
|
+ set_zone_contiguous(zone);
|
|
}
|
|
}
|
|
-#endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */
|
|
|
|
|
|
|
|
#ifdef CONFIG_CMA
|
|
#ifdef CONFIG_CMA
|
|
/* Free whole pageblock and set its migration type to MIGRATE_CMA. */
|
|
/* Free whole pageblock and set its migration type to MIGRATE_CMA. */
|
|
@@ -1381,15 +1481,24 @@ static inline int check_new_page(struct page *page)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline bool free_pages_prezeroed(bool poisoned)
|
|
|
|
+{
|
|
|
|
+ return IS_ENABLED(CONFIG_PAGE_POISONING_ZERO) &&
|
|
|
|
+ page_poisoning_enabled() && poisoned;
|
|
|
|
+}
|
|
|
|
+
|
|
static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
|
|
static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
|
|
int alloc_flags)
|
|
int alloc_flags)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
|
|
+ bool poisoned = true;
|
|
|
|
|
|
for (i = 0; i < (1 << order); i++) {
|
|
for (i = 0; i < (1 << order); i++) {
|
|
struct page *p = page + i;
|
|
struct page *p = page + i;
|
|
if (unlikely(check_new_page(p)))
|
|
if (unlikely(check_new_page(p)))
|
|
return 1;
|
|
return 1;
|
|
|
|
+ if (poisoned)
|
|
|
|
+ poisoned &= page_is_poisoned(p);
|
|
}
|
|
}
|
|
|
|
|
|
set_page_private(page, 0);
|
|
set_page_private(page, 0);
|
|
@@ -1397,9 +1506,10 @@ static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
|
|
|
|
|
|
arch_alloc_page(page, order);
|
|
arch_alloc_page(page, order);
|
|
kernel_map_pages(page, 1 << order, 1);
|
|
kernel_map_pages(page, 1 << order, 1);
|
|
|
|
+ kernel_poison_pages(page, 1 << order, 1);
|
|
kasan_alloc_pages(page, order);
|
|
kasan_alloc_pages(page, order);
|
|
|
|
|
|
- if (gfp_flags & __GFP_ZERO)
|
|
|
|
|
|
+ if (!free_pages_prezeroed(poisoned) && (gfp_flags & __GFP_ZERO))
|
|
for (i = 0; i < (1 << order); i++)
|
|
for (i = 0; i < (1 << order); i++)
|
|
clear_highpage(page + i);
|
|
clear_highpage(page + i);
|
|
|
|
|
|
@@ -2690,9 +2800,8 @@ void warn_alloc_failed(gfp_t gfp_mask, unsigned int order, const char *fmt, ...)
|
|
va_end(args);
|
|
va_end(args);
|
|
}
|
|
}
|
|
|
|
|
|
- pr_warn("%s: page allocation failure: order:%u, mode:0x%x\n",
|
|
|
|
- current->comm, order, gfp_mask);
|
|
|
|
-
|
|
|
|
|
|
+ pr_warn("%s: page allocation failure: order:%u, mode:%#x(%pGg)\n",
|
|
|
|
+ current->comm, order, gfp_mask, &gfp_mask);
|
|
dump_stack();
|
|
dump_stack();
|
|
if (!should_suppress_show_mem())
|
|
if (!should_suppress_show_mem())
|
|
show_mem(filter);
|
|
show_mem(filter);
|
|
@@ -4491,6 +4600,9 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
|
|
pg_data_t *pgdat = NODE_DATA(nid);
|
|
pg_data_t *pgdat = NODE_DATA(nid);
|
|
unsigned long pfn;
|
|
unsigned long pfn;
|
|
unsigned long nr_initialised = 0;
|
|
unsigned long nr_initialised = 0;
|
|
|
|
+#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
|
|
|
|
+ struct memblock_region *r = NULL, *tmp;
|
|
|
|
+#endif
|
|
|
|
|
|
if (highest_memmap_pfn < end_pfn - 1)
|
|
if (highest_memmap_pfn < end_pfn - 1)
|
|
highest_memmap_pfn = end_pfn - 1;
|
|
highest_memmap_pfn = end_pfn - 1;
|
|
@@ -4504,20 +4616,51 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
|
|
|
|
|
|
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
|
|
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
|
|
/*
|
|
/*
|
|
- * There can be holes in boot-time mem_map[]s
|
|
|
|
- * handed to this function. They do not
|
|
|
|
- * exist on hotplugged memory.
|
|
|
|
|
|
+ * There can be holes in boot-time mem_map[]s handed to this
|
|
|
|
+ * function. They do not exist on hotplugged memory.
|
|
*/
|
|
*/
|
|
- if (context == MEMMAP_EARLY) {
|
|
|
|
- if (!early_pfn_valid(pfn))
|
|
|
|
|
|
+ if (context != MEMMAP_EARLY)
|
|
|
|
+ goto not_early;
|
|
|
|
+
|
|
|
|
+ if (!early_pfn_valid(pfn))
|
|
|
|
+ continue;
|
|
|
|
+ if (!early_pfn_in_nid(pfn, nid))
|
|
|
|
+ continue;
|
|
|
|
+ if (!update_defer_init(pgdat, pfn, end_pfn, &nr_initialised))
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
|
|
|
|
+ /*
|
|
|
|
+ * If not mirrored_kernelcore and ZONE_MOVABLE exists, range
|
|
|
|
+ * from zone_movable_pfn[nid] to end of each node should be
|
|
|
|
+ * ZONE_MOVABLE not ZONE_NORMAL. skip it.
|
|
|
|
+ */
|
|
|
|
+ if (!mirrored_kernelcore && zone_movable_pfn[nid])
|
|
|
|
+ if (zone == ZONE_NORMAL && pfn >= zone_movable_pfn[nid])
|
|
continue;
|
|
continue;
|
|
- if (!early_pfn_in_nid(pfn, nid))
|
|
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Check given memblock attribute by firmware which can affect
|
|
|
|
+ * kernel memory layout. If zone==ZONE_MOVABLE but memory is
|
|
|
|
+ * mirrored, it's an overlapped memmap init. skip it.
|
|
|
|
+ */
|
|
|
|
+ if (mirrored_kernelcore && zone == ZONE_MOVABLE) {
|
|
|
|
+ if (!r || pfn >= memblock_region_memory_end_pfn(r)) {
|
|
|
|
+ for_each_memblock(memory, tmp)
|
|
|
|
+ if (pfn < memblock_region_memory_end_pfn(tmp))
|
|
|
|
+ break;
|
|
|
|
+ r = tmp;
|
|
|
|
+ }
|
|
|
|
+ if (pfn >= memblock_region_memory_base_pfn(r) &&
|
|
|
|
+ memblock_is_mirror(r)) {
|
|
|
|
+ /* already initialized as NORMAL */
|
|
|
|
+ pfn = memblock_region_memory_end_pfn(r);
|
|
continue;
|
|
continue;
|
|
- if (!update_defer_init(pgdat, pfn, end_pfn,
|
|
|
|
- &nr_initialised))
|
|
|
|
- break;
|
|
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
+not_early:
|
|
/*
|
|
/*
|
|
* Mark the block movable so that blocks are reserved for
|
|
* Mark the block movable so that blocks are reserved for
|
|
* movable at startup. This will force kernel allocations
|
|
* movable at startup. This will force kernel allocations
|
|
@@ -4934,11 +5077,6 @@ static void __meminit adjust_zone_range_for_zone_movable(int nid,
|
|
*zone_end_pfn = min(node_end_pfn,
|
|
*zone_end_pfn = min(node_end_pfn,
|
|
arch_zone_highest_possible_pfn[movable_zone]);
|
|
arch_zone_highest_possible_pfn[movable_zone]);
|
|
|
|
|
|
- /* Adjust for ZONE_MOVABLE starting within this range */
|
|
|
|
- } else if (*zone_start_pfn < zone_movable_pfn[nid] &&
|
|
|
|
- *zone_end_pfn > zone_movable_pfn[nid]) {
|
|
|
|
- *zone_end_pfn = zone_movable_pfn[nid];
|
|
|
|
-
|
|
|
|
/* Check if this whole range is within ZONE_MOVABLE */
|
|
/* Check if this whole range is within ZONE_MOVABLE */
|
|
} else if (*zone_start_pfn >= zone_movable_pfn[nid])
|
|
} else if (*zone_start_pfn >= zone_movable_pfn[nid])
|
|
*zone_start_pfn = *zone_end_pfn;
|
|
*zone_start_pfn = *zone_end_pfn;
|
|
@@ -4953,31 +5091,31 @@ static unsigned long __meminit zone_spanned_pages_in_node(int nid,
|
|
unsigned long zone_type,
|
|
unsigned long zone_type,
|
|
unsigned long node_start_pfn,
|
|
unsigned long node_start_pfn,
|
|
unsigned long node_end_pfn,
|
|
unsigned long node_end_pfn,
|
|
|
|
+ unsigned long *zone_start_pfn,
|
|
|
|
+ unsigned long *zone_end_pfn,
|
|
unsigned long *ignored)
|
|
unsigned long *ignored)
|
|
{
|
|
{
|
|
- unsigned long zone_start_pfn, zone_end_pfn;
|
|
|
|
-
|
|
|
|
/* When hotadd a new node from cpu_up(), the node should be empty */
|
|
/* When hotadd a new node from cpu_up(), the node should be empty */
|
|
if (!node_start_pfn && !node_end_pfn)
|
|
if (!node_start_pfn && !node_end_pfn)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
/* Get the start and end of the zone */
|
|
/* Get the start and end of the zone */
|
|
- zone_start_pfn = arch_zone_lowest_possible_pfn[zone_type];
|
|
|
|
- zone_end_pfn = arch_zone_highest_possible_pfn[zone_type];
|
|
|
|
|
|
+ *zone_start_pfn = arch_zone_lowest_possible_pfn[zone_type];
|
|
|
|
+ *zone_end_pfn = arch_zone_highest_possible_pfn[zone_type];
|
|
adjust_zone_range_for_zone_movable(nid, zone_type,
|
|
adjust_zone_range_for_zone_movable(nid, zone_type,
|
|
node_start_pfn, node_end_pfn,
|
|
node_start_pfn, node_end_pfn,
|
|
- &zone_start_pfn, &zone_end_pfn);
|
|
|
|
|
|
+ zone_start_pfn, zone_end_pfn);
|
|
|
|
|
|
/* Check that this node has pages within the zone's required range */
|
|
/* Check that this node has pages within the zone's required range */
|
|
- if (zone_end_pfn < node_start_pfn || zone_start_pfn > node_end_pfn)
|
|
|
|
|
|
+ if (*zone_end_pfn < node_start_pfn || *zone_start_pfn > node_end_pfn)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
/* Move the zone boundaries inside the node if necessary */
|
|
/* Move the zone boundaries inside the node if necessary */
|
|
- zone_end_pfn = min(zone_end_pfn, node_end_pfn);
|
|
|
|
- zone_start_pfn = max(zone_start_pfn, node_start_pfn);
|
|
|
|
|
|
+ *zone_end_pfn = min(*zone_end_pfn, node_end_pfn);
|
|
|
|
+ *zone_start_pfn = max(*zone_start_pfn, node_start_pfn);
|
|
|
|
|
|
/* Return the spanned pages */
|
|
/* Return the spanned pages */
|
|
- return zone_end_pfn - zone_start_pfn;
|
|
|
|
|
|
+ return *zone_end_pfn - *zone_start_pfn;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -5023,6 +5161,7 @@ static unsigned long __meminit zone_absent_pages_in_node(int nid,
|
|
unsigned long zone_low = arch_zone_lowest_possible_pfn[zone_type];
|
|
unsigned long zone_low = arch_zone_lowest_possible_pfn[zone_type];
|
|
unsigned long zone_high = arch_zone_highest_possible_pfn[zone_type];
|
|
unsigned long zone_high = arch_zone_highest_possible_pfn[zone_type];
|
|
unsigned long zone_start_pfn, zone_end_pfn;
|
|
unsigned long zone_start_pfn, zone_end_pfn;
|
|
|
|
+ unsigned long nr_absent;
|
|
|
|
|
|
/* When hotadd a new node from cpu_up(), the node should be empty */
|
|
/* When hotadd a new node from cpu_up(), the node should be empty */
|
|
if (!node_start_pfn && !node_end_pfn)
|
|
if (!node_start_pfn && !node_end_pfn)
|
|
@@ -5034,7 +5173,39 @@ static unsigned long __meminit zone_absent_pages_in_node(int nid,
|
|
adjust_zone_range_for_zone_movable(nid, zone_type,
|
|
adjust_zone_range_for_zone_movable(nid, zone_type,
|
|
node_start_pfn, node_end_pfn,
|
|
node_start_pfn, node_end_pfn,
|
|
&zone_start_pfn, &zone_end_pfn);
|
|
&zone_start_pfn, &zone_end_pfn);
|
|
- return __absent_pages_in_range(nid, zone_start_pfn, zone_end_pfn);
|
|
|
|
|
|
+ nr_absent = __absent_pages_in_range(nid, zone_start_pfn, zone_end_pfn);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * ZONE_MOVABLE handling.
|
|
|
|
+ * Treat pages to be ZONE_MOVABLE in ZONE_NORMAL as absent pages
|
|
|
|
+ * and vice versa.
|
|
|
|
+ */
|
|
|
|
+ if (zone_movable_pfn[nid]) {
|
|
|
|
+ if (mirrored_kernelcore) {
|
|
|
|
+ unsigned long start_pfn, end_pfn;
|
|
|
|
+ struct memblock_region *r;
|
|
|
|
+
|
|
|
|
+ for_each_memblock(memory, r) {
|
|
|
|
+ start_pfn = clamp(memblock_region_memory_base_pfn(r),
|
|
|
|
+ zone_start_pfn, zone_end_pfn);
|
|
|
|
+ end_pfn = clamp(memblock_region_memory_end_pfn(r),
|
|
|
|
+ zone_start_pfn, zone_end_pfn);
|
|
|
|
+
|
|
|
|
+ if (zone_type == ZONE_MOVABLE &&
|
|
|
|
+ memblock_is_mirror(r))
|
|
|
|
+ nr_absent += end_pfn - start_pfn;
|
|
|
|
+
|
|
|
|
+ if (zone_type == ZONE_NORMAL &&
|
|
|
|
+ !memblock_is_mirror(r))
|
|
|
|
+ nr_absent += end_pfn - start_pfn;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if (zone_type == ZONE_NORMAL)
|
|
|
|
+ nr_absent += node_end_pfn - zone_movable_pfn[nid];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nr_absent;
|
|
}
|
|
}
|
|
|
|
|
|
#else /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
|
|
#else /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
|
|
@@ -5042,8 +5213,18 @@ static inline unsigned long __meminit zone_spanned_pages_in_node(int nid,
|
|
unsigned long zone_type,
|
|
unsigned long zone_type,
|
|
unsigned long node_start_pfn,
|
|
unsigned long node_start_pfn,
|
|
unsigned long node_end_pfn,
|
|
unsigned long node_end_pfn,
|
|
|
|
+ unsigned long *zone_start_pfn,
|
|
|
|
+ unsigned long *zone_end_pfn,
|
|
unsigned long *zones_size)
|
|
unsigned long *zones_size)
|
|
{
|
|
{
|
|
|
|
+ unsigned int zone;
|
|
|
|
+
|
|
|
|
+ *zone_start_pfn = node_start_pfn;
|
|
|
|
+ for (zone = 0; zone < zone_type; zone++)
|
|
|
|
+ *zone_start_pfn += zones_size[zone];
|
|
|
|
+
|
|
|
|
+ *zone_end_pfn = *zone_start_pfn + zones_size[zone_type];
|
|
|
|
+
|
|
return zones_size[zone_type];
|
|
return zones_size[zone_type];
|
|
}
|
|
}
|
|
|
|
|
|
@@ -5072,15 +5253,22 @@ static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,
|
|
|
|
|
|
for (i = 0; i < MAX_NR_ZONES; i++) {
|
|
for (i = 0; i < MAX_NR_ZONES; i++) {
|
|
struct zone *zone = pgdat->node_zones + i;
|
|
struct zone *zone = pgdat->node_zones + i;
|
|
|
|
+ unsigned long zone_start_pfn, zone_end_pfn;
|
|
unsigned long size, real_size;
|
|
unsigned long size, real_size;
|
|
|
|
|
|
size = zone_spanned_pages_in_node(pgdat->node_id, i,
|
|
size = zone_spanned_pages_in_node(pgdat->node_id, i,
|
|
node_start_pfn,
|
|
node_start_pfn,
|
|
node_end_pfn,
|
|
node_end_pfn,
|
|
|
|
+ &zone_start_pfn,
|
|
|
|
+ &zone_end_pfn,
|
|
zones_size);
|
|
zones_size);
|
|
real_size = size - zone_absent_pages_in_node(pgdat->node_id, i,
|
|
real_size = size - zone_absent_pages_in_node(pgdat->node_id, i,
|
|
node_start_pfn, node_end_pfn,
|
|
node_start_pfn, node_end_pfn,
|
|
zholes_size);
|
|
zholes_size);
|
|
|
|
+ if (size)
|
|
|
|
+ zone->zone_start_pfn = zone_start_pfn;
|
|
|
|
+ else
|
|
|
|
+ zone->zone_start_pfn = 0;
|
|
zone->spanned_pages = size;
|
|
zone->spanned_pages = size;
|
|
zone->present_pages = real_size;
|
|
zone->present_pages = real_size;
|
|
|
|
|
|
@@ -5201,7 +5389,6 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat)
|
|
{
|
|
{
|
|
enum zone_type j;
|
|
enum zone_type j;
|
|
int nid = pgdat->node_id;
|
|
int nid = pgdat->node_id;
|
|
- unsigned long zone_start_pfn = pgdat->node_start_pfn;
|
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
pgdat_resize_init(pgdat);
|
|
pgdat_resize_init(pgdat);
|
|
@@ -5222,6 +5409,7 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat)
|
|
for (j = 0; j < MAX_NR_ZONES; j++) {
|
|
for (j = 0; j < MAX_NR_ZONES; j++) {
|
|
struct zone *zone = pgdat->node_zones + j;
|
|
struct zone *zone = pgdat->node_zones + j;
|
|
unsigned long size, realsize, freesize, memmap_pages;
|
|
unsigned long size, realsize, freesize, memmap_pages;
|
|
|
|
+ unsigned long zone_start_pfn = zone->zone_start_pfn;
|
|
|
|
|
|
size = zone->spanned_pages;
|
|
size = zone->spanned_pages;
|
|
realsize = freesize = zone->present_pages;
|
|
realsize = freesize = zone->present_pages;
|
|
@@ -5290,7 +5478,6 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat)
|
|
ret = init_currently_empty_zone(zone, zone_start_pfn, size);
|
|
ret = init_currently_empty_zone(zone, zone_start_pfn, size);
|
|
BUG_ON(ret);
|
|
BUG_ON(ret);
|
|
memmap_init(size, nid, j, zone_start_pfn);
|
|
memmap_init(size, nid, j, zone_start_pfn);
|
|
- zone_start_pfn += size;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -5358,6 +5545,8 @@ void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
|
|
pr_info("Initmem setup node %d [mem %#018Lx-%#018Lx]\n", nid,
|
|
pr_info("Initmem setup node %d [mem %#018Lx-%#018Lx]\n", nid,
|
|
(u64)start_pfn << PAGE_SHIFT,
|
|
(u64)start_pfn << PAGE_SHIFT,
|
|
end_pfn ? ((u64)end_pfn << PAGE_SHIFT) - 1 : 0);
|
|
end_pfn ? ((u64)end_pfn << PAGE_SHIFT) - 1 : 0);
|
|
|
|
+#else
|
|
|
|
+ start_pfn = node_start_pfn;
|
|
#endif
|
|
#endif
|
|
calculate_node_totalpages(pgdat, start_pfn, end_pfn,
|
|
calculate_node_totalpages(pgdat, start_pfn, end_pfn,
|
|
zones_size, zholes_size);
|
|
zones_size, zholes_size);
|
|
@@ -5528,6 +5717,36 @@ static void __init find_zone_movable_pfns_for_nodes(void)
|
|
goto out2;
|
|
goto out2;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If kernelcore=mirror is specified, ignore movablecore option
|
|
|
|
+ */
|
|
|
|
+ if (mirrored_kernelcore) {
|
|
|
|
+ bool mem_below_4gb_not_mirrored = false;
|
|
|
|
+
|
|
|
|
+ for_each_memblock(memory, r) {
|
|
|
|
+ if (memblock_is_mirror(r))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ nid = r->nid;
|
|
|
|
+
|
|
|
|
+ usable_startpfn = memblock_region_memory_base_pfn(r);
|
|
|
|
+
|
|
|
|
+ if (usable_startpfn < 0x100000) {
|
|
|
|
+ mem_below_4gb_not_mirrored = true;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ zone_movable_pfn[nid] = zone_movable_pfn[nid] ?
|
|
|
|
+ min(usable_startpfn, zone_movable_pfn[nid]) :
|
|
|
|
+ usable_startpfn;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (mem_below_4gb_not_mirrored)
|
|
|
|
+ pr_warn("This configuration results in unmirrored kernel memory.");
|
|
|
|
+
|
|
|
|
+ goto out2;
|
|
|
|
+ }
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* If movablecore=nn[KMG] was specified, calculate what size of
|
|
* If movablecore=nn[KMG] was specified, calculate what size of
|
|
* kernelcore that corresponds so that memory usable for
|
|
* kernelcore that corresponds so that memory usable for
|
|
@@ -5788,6 +6007,12 @@ static int __init cmdline_parse_core(char *p, unsigned long *core)
|
|
*/
|
|
*/
|
|
static int __init cmdline_parse_kernelcore(char *p)
|
|
static int __init cmdline_parse_kernelcore(char *p)
|
|
{
|
|
{
|
|
|
|
+ /* parse kernelcore=mirror */
|
|
|
|
+ if (parse_option_str(p, "mirror")) {
|
|
|
|
+ mirrored_kernelcore = true;
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
return cmdline_parse_core(p, &required_kernelcore);
|
|
return cmdline_parse_core(p, &required_kernelcore);
|
|
}
|
|
}
|
|
|
|
|