diff options
author | Taku Izumi <izumi.taku@jp.fujitsu.com> | 2016-03-15 17:55:22 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-15 19:55:16 -0400 |
commit | 342332e6a925e9ed015e5465062c38d2b86ec8f9 (patch) | |
tree | a9aabc6010913a03258f07583d25c8f51e338bba /mm/page_alloc.c | |
parent | d91749c1dda71a7030c054a5ab8dc5419bc6730b (diff) |
mm/page_alloc.c: introduce kernelcore=mirror option
This patch extends existing "kernelcore" option and introduces
kernelcore=mirror option. By specifying "mirror" instead of specifying
the amount of memory, non-mirrored (non-reliable) region will be
arranged into ZONE_MOVABLE.
[akpm@linux-foundation.org: fix build with CONFIG_HAVE_MEMBLOCK_NODE_MAP=n]
Signed-off-by: Taku Izumi <izumi.taku@jp.fujitsu.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Xishi Qiu <qiuxishi@huawei.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Matt Fleming <matt@codeblueprint.co.uk>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Steve Capper <steve.capper@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/page_alloc.c')
-rw-r--r-- | mm/page_alloc.c | 114 |
1 files changed, 108 insertions, 6 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 0d20a19151a4..b8160b9d5e72 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -247,6 +247,7 @@ static unsigned long __meminitdata arch_zone_highest_possible_pfn[MAX_NR_ZONES]; | |||
247 | static unsigned long __initdata required_kernelcore; | 247 | static unsigned long __initdata required_kernelcore; |
248 | static unsigned long __initdata required_movablecore; | 248 | static unsigned long __initdata required_movablecore; |
249 | static unsigned long __meminitdata zone_movable_pfn[MAX_NUMNODES]; | 249 | static unsigned long __meminitdata zone_movable_pfn[MAX_NUMNODES]; |
250 | static bool mirrored_kernelcore; | ||
250 | 251 | ||
251 | /* movable_zone is the "real" zone pages in ZONE_MOVABLE are taken from */ | 252 | /* movable_zone is the "real" zone pages in ZONE_MOVABLE are taken from */ |
252 | int movable_zone; | 253 | int movable_zone; |
@@ -4491,6 +4492,9 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone, | |||
4491 | pg_data_t *pgdat = NODE_DATA(nid); | 4492 | pg_data_t *pgdat = NODE_DATA(nid); |
4492 | unsigned long pfn; | 4493 | unsigned long pfn; |
4493 | unsigned long nr_initialised = 0; | 4494 | unsigned long nr_initialised = 0; |
4495 | #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP | ||
4496 | struct memblock_region *r = NULL, *tmp; | ||
4497 | #endif | ||
4494 | 4498 | ||
4495 | if (highest_memmap_pfn < end_pfn - 1) | 4499 | if (highest_memmap_pfn < end_pfn - 1) |
4496 | highest_memmap_pfn = end_pfn - 1; | 4500 | highest_memmap_pfn = end_pfn - 1; |
@@ -4516,6 +4520,40 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone, | |||
4516 | if (!update_defer_init(pgdat, pfn, end_pfn, | 4520 | if (!update_defer_init(pgdat, pfn, end_pfn, |
4517 | &nr_initialised)) | 4521 | &nr_initialised)) |
4518 | break; | 4522 | break; |
4523 | |||
4524 | #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP | ||
4525 | /* | ||
4526 | * if not mirrored_kernelcore and ZONE_MOVABLE exists, | ||
4527 | * range from zone_movable_pfn[nid] to end of each node | ||
4528 | * should be ZONE_MOVABLE not ZONE_NORMAL. skip it. | ||
4529 | */ | ||
4530 | if (!mirrored_kernelcore && zone_movable_pfn[nid]) | ||
4531 | if (zone == ZONE_NORMAL && | ||
4532 | pfn >= zone_movable_pfn[nid]) | ||
4533 | continue; | ||
4534 | |||
4535 | /* | ||
4536 | * check given memblock attribute by firmware which | ||
4537 | * can affect kernel memory layout. | ||
4538 | * if zone==ZONE_MOVABLE but memory is mirrored, | ||
4539 | * it's an overlapped memmap init. skip it. | ||
4540 | */ | ||
4541 | if (mirrored_kernelcore && zone == ZONE_MOVABLE) { | ||
4542 | if (!r || | ||
4543 | pfn >= memblock_region_memory_end_pfn(r)) { | ||
4544 | for_each_memblock(memory, tmp) | ||
4545 | if (pfn < memblock_region_memory_end_pfn(tmp)) | ||
4546 | break; | ||
4547 | r = tmp; | ||
4548 | } | ||
4549 | if (pfn >= memblock_region_memory_base_pfn(r) && | ||
4550 | memblock_is_mirror(r)) { | ||
4551 | /* already initialized as NORMAL */ | ||
4552 | pfn = memblock_region_memory_end_pfn(r); | ||
4553 | continue; | ||
4554 | } | ||
4555 | } | ||
4556 | #endif | ||
4519 | } | 4557 | } |
4520 | 4558 | ||
4521 | /* | 4559 | /* |
@@ -4934,11 +4972,6 @@ static void __meminit adjust_zone_range_for_zone_movable(int nid, | |||
4934 | *zone_end_pfn = min(node_end_pfn, | 4972 | *zone_end_pfn = min(node_end_pfn, |
4935 | arch_zone_highest_possible_pfn[movable_zone]); | 4973 | arch_zone_highest_possible_pfn[movable_zone]); |
4936 | 4974 | ||
4937 | /* Adjust for ZONE_MOVABLE starting within this range */ | ||
4938 | } else if (*zone_start_pfn < zone_movable_pfn[nid] && | ||
4939 | *zone_end_pfn > zone_movable_pfn[nid]) { | ||
4940 | *zone_end_pfn = zone_movable_pfn[nid]; | ||
4941 | |||
4942 | /* Check if this whole range is within ZONE_MOVABLE */ | 4975 | /* Check if this whole range is within ZONE_MOVABLE */ |
4943 | } else if (*zone_start_pfn >= zone_movable_pfn[nid]) | 4976 | } else if (*zone_start_pfn >= zone_movable_pfn[nid]) |
4944 | *zone_start_pfn = *zone_end_pfn; | 4977 | *zone_start_pfn = *zone_end_pfn; |
@@ -5023,6 +5056,7 @@ static unsigned long __meminit zone_absent_pages_in_node(int nid, | |||
5023 | unsigned long zone_low = arch_zone_lowest_possible_pfn[zone_type]; | 5056 | unsigned long zone_low = arch_zone_lowest_possible_pfn[zone_type]; |
5024 | unsigned long zone_high = arch_zone_highest_possible_pfn[zone_type]; | 5057 | unsigned long zone_high = arch_zone_highest_possible_pfn[zone_type]; |
5025 | unsigned long zone_start_pfn, zone_end_pfn; | 5058 | unsigned long zone_start_pfn, zone_end_pfn; |
5059 | unsigned long nr_absent; | ||
5026 | 5060 | ||
5027 | /* When hotadd a new node from cpu_up(), the node should be empty */ | 5061 | /* When hotadd a new node from cpu_up(), the node should be empty */ |
5028 | if (!node_start_pfn && !node_end_pfn) | 5062 | if (!node_start_pfn && !node_end_pfn) |
@@ -5034,7 +5068,39 @@ static unsigned long __meminit zone_absent_pages_in_node(int nid, | |||
5034 | adjust_zone_range_for_zone_movable(nid, zone_type, | 5068 | adjust_zone_range_for_zone_movable(nid, zone_type, |
5035 | node_start_pfn, node_end_pfn, | 5069 | node_start_pfn, node_end_pfn, |
5036 | &zone_start_pfn, &zone_end_pfn); | 5070 | &zone_start_pfn, &zone_end_pfn); |
5037 | return __absent_pages_in_range(nid, zone_start_pfn, zone_end_pfn); | 5071 | nr_absent = __absent_pages_in_range(nid, zone_start_pfn, zone_end_pfn); |
5072 | |||
5073 | /* | ||
5074 | * ZONE_MOVABLE handling. | ||
5075 | * Treat pages to be ZONE_MOVABLE in ZONE_NORMAL as absent pages | ||
5076 | * and vice versa. | ||
5077 | */ | ||
5078 | if (zone_movable_pfn[nid]) { | ||
5079 | if (mirrored_kernelcore) { | ||
5080 | unsigned long start_pfn, end_pfn; | ||
5081 | struct memblock_region *r; | ||
5082 | |||
5083 | for_each_memblock(memory, r) { | ||
5084 | start_pfn = clamp(memblock_region_memory_base_pfn(r), | ||
5085 | zone_start_pfn, zone_end_pfn); | ||
5086 | end_pfn = clamp(memblock_region_memory_end_pfn(r), | ||
5087 | zone_start_pfn, zone_end_pfn); | ||
5088 | |||
5089 | if (zone_type == ZONE_MOVABLE && | ||
5090 | memblock_is_mirror(r)) | ||
5091 | nr_absent += end_pfn - start_pfn; | ||
5092 | |||
5093 | if (zone_type == ZONE_NORMAL && | ||
5094 | !memblock_is_mirror(r)) | ||
5095 | nr_absent += end_pfn - start_pfn; | ||
5096 | } | ||
5097 | } else { | ||
5098 | if (zone_type == ZONE_NORMAL) | ||
5099 | nr_absent += node_end_pfn - zone_movable_pfn[nid]; | ||
5100 | } | ||
5101 | } | ||
5102 | |||
5103 | return nr_absent; | ||
5038 | } | 5104 | } |
5039 | 5105 | ||
5040 | #else /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ | 5106 | #else /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ |
@@ -5547,6 +5613,36 @@ static void __init find_zone_movable_pfns_for_nodes(void) | |||
5547 | } | 5613 | } |
5548 | 5614 | ||
5549 | /* | 5615 | /* |
5616 | * If kernelcore=mirror is specified, ignore movablecore option | ||
5617 | */ | ||
5618 | if (mirrored_kernelcore) { | ||
5619 | bool mem_below_4gb_not_mirrored = false; | ||
5620 | |||
5621 | for_each_memblock(memory, r) { | ||
5622 | if (memblock_is_mirror(r)) | ||
5623 | continue; | ||
5624 | |||
5625 | nid = r->nid; | ||
5626 | |||
5627 | usable_startpfn = memblock_region_memory_base_pfn(r); | ||
5628 | |||
5629 | if (usable_startpfn < 0x100000) { | ||
5630 | mem_below_4gb_not_mirrored = true; | ||
5631 | continue; | ||
5632 | } | ||
5633 | |||
5634 | zone_movable_pfn[nid] = zone_movable_pfn[nid] ? | ||
5635 | min(usable_startpfn, zone_movable_pfn[nid]) : | ||
5636 | usable_startpfn; | ||
5637 | } | ||
5638 | |||
5639 | if (mem_below_4gb_not_mirrored) | ||
5640 | pr_warn("This configuration results in unmirrored kernel memory."); | ||
5641 | |||
5642 | goto out2; | ||
5643 | } | ||
5644 | |||
5645 | /* | ||
5550 | * If movablecore=nn[KMG] was specified, calculate what size of | 5646 | * If movablecore=nn[KMG] was specified, calculate what size of |
5551 | * kernelcore that corresponds so that memory usable for | 5647 | * kernelcore that corresponds so that memory usable for |
5552 | * any allocation type is evenly spread. If both kernelcore | 5648 | * any allocation type is evenly spread. If both kernelcore |
@@ -5806,6 +5902,12 @@ static int __init cmdline_parse_core(char *p, unsigned long *core) | |||
5806 | */ | 5902 | */ |
5807 | static int __init cmdline_parse_kernelcore(char *p) | 5903 | static int __init cmdline_parse_kernelcore(char *p) |
5808 | { | 5904 | { |
5905 | /* parse kernelcore=mirror */ | ||
5906 | if (parse_option_str(p, "mirror")) { | ||
5907 | mirrored_kernelcore = true; | ||
5908 | return 0; | ||
5909 | } | ||
5910 | |||
5809 | return cmdline_parse_core(p, &required_kernelcore); | 5911 | return cmdline_parse_core(p, &required_kernelcore); |
5810 | } | 5912 | } |
5811 | 5913 | ||