Commit f14851af0ebb32745c6c5a2e400aa0549f9d20df

Authored by qiuxishi
Committed by Linus Torvalds
1 parent 0ba8f2d593

memory hotplug: fix section info double registration bug

There may be a bug when registering section info.  For example, on my
Itanium platform, the pfn range of node0 includes the other nodes, so
other nodes' section info will be double registered, and memmap's page
count will equal to 3.

  node0: start_pfn=0x100,    spanned_pfn=0x20fb00, present_pfn=0x7f8a3, => 0x000100-0x20fc00
  node1: start_pfn=0x80000,  spanned_pfn=0x80000,  present_pfn=0x80000, => 0x080000-0x100000
  node2: start_pfn=0x100000, spanned_pfn=0x80000,  present_pfn=0x80000, => 0x100000-0x180000
  node3: start_pfn=0x180000, spanned_pfn=0x80000,  present_pfn=0x80000, => 0x180000-0x200000

  free_all_bootmem_node()
	register_page_bootmem_info_node()
		register_page_bootmem_info_section()

When hot remove memory, we can't free the memmap's page because
page_count() is 2 after put_page_bootmem().

  sparse_remove_one_section()
	free_section_usemap()
		free_map_bootmem()
			put_page_bootmem()

[akpm@linux-foundation.org: add code comment]
Signed-off-by: Xishi Qiu <qiuxishi@huawei.com>
Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Acked-by: Mel Gorman <mgorman@suse.de>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 1 changed file with 10 additions and 6 deletions Side-by-side Diff

... ... @@ -126,9 +126,6 @@
126 126 struct mem_section *ms;
127 127 struct page *page, *memmap;
128 128  
129   - if (!pfn_valid(start_pfn))
130   - return;
131   -
132 129 section_nr = pfn_to_section_nr(start_pfn);
133 130 ms = __nr_to_section(section_nr);
134 131  
... ... @@ -187,9 +184,16 @@
187 184 end_pfn = pfn + pgdat->node_spanned_pages;
188 185  
189 186 /* register_section info */
190   - for (; pfn < end_pfn; pfn += PAGES_PER_SECTION)
191   - register_page_bootmem_info_section(pfn);
192   -
  187 + for (; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
  188 + /*
  189 + * Some platforms can assign the same pfn to multiple nodes - on
  190 + * node0 as well as nodeN. To avoid registering a pfn against
  191 + * multiple nodes we check that this pfn does not already
  192 + * reside in some other node.
  193 + */
  194 + if (pfn_valid(pfn) && (pfn_to_nid(pfn) == node))
  195 + register_page_bootmem_info_section(pfn);
  196 + }
193 197 }
194 198 #endif /* !CONFIG_SPARSEMEM_VMEMMAP */
195 199