mm/memory_hotplug.c: check for missing sections in test_pages_in_a_zone()
test_pages_in_a_zone() does not account for the possibility of missing sections in the given pfn range. pfn_valid_within always returns 1 when CONFIG_HOLES_IN_ZONE is not set, allowing invalid pfns from missing sections to pass the test, leading to a kernel oops. Wrap an additional pfn loop with PAGES_PER_SECTION granularity to check for missing sections before proceeding into the zone-check code. This also prevents a crash from offlining memory devices with missing sections. Despite this, it may be a good idea to keep the related patch '[PATCH 3/3] drivers: memory: prohibit offlining of memory blocks with missing sections' because missing sections in a memory block may lead to other problems not covered by the scope of this fix. Signed-off-by: Andrew Banman <abanman@sgi.com> Acked-by: Alex Thorlton <athorlton@sgi.com> Cc: Russ Anderson <rja@sgi.com> Cc: Alex Thorlton <athorlton@sgi.com> Cc: Yinghai Lu <yinghai@kernel.org> Cc: Greg KH <greg@kroah.com> Cc: Seth Jennings <sjennings@variantweb.net> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
b5a8bc338e
commit
5f0f2887f4
|
@ -1375,23 +1375,30 @@ int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
|
||||||
*/
|
*/
|
||||||
int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
|
int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
|
||||||
{
|
{
|
||||||
unsigned long pfn;
|
unsigned long pfn, sec_end_pfn;
|
||||||
struct zone *zone = NULL;
|
struct zone *zone = NULL;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
int i;
|
int i;
|
||||||
for (pfn = start_pfn;
|
for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn);
|
||||||
pfn < end_pfn;
|
pfn < end_pfn;
|
||||||
pfn += MAX_ORDER_NR_PAGES) {
|
pfn = sec_end_pfn + 1, sec_end_pfn += PAGES_PER_SECTION) {
|
||||||
i = 0;
|
/* Make sure the memory section is present first */
|
||||||
/* This is just a CONFIG_HOLES_IN_ZONE check.*/
|
if (!present_section_nr(pfn_to_section_nr(pfn)))
|
||||||
while ((i < MAX_ORDER_NR_PAGES) && !pfn_valid_within(pfn + i))
|
|
||||||
i++;
|
|
||||||
if (i == MAX_ORDER_NR_PAGES)
|
|
||||||
continue;
|
continue;
|
||||||
page = pfn_to_page(pfn + i);
|
for (; pfn < sec_end_pfn && pfn < end_pfn;
|
||||||
if (zone && page_zone(page) != zone)
|
pfn += MAX_ORDER_NR_PAGES) {
|
||||||
return 0;
|
i = 0;
|
||||||
zone = page_zone(page);
|
/* This is just a CONFIG_HOLES_IN_ZONE check.*/
|
||||||
|
while ((i < MAX_ORDER_NR_PAGES) &&
|
||||||
|
!pfn_valid_within(pfn + i))
|
||||||
|
i++;
|
||||||
|
if (i == MAX_ORDER_NR_PAGES)
|
||||||
|
continue;
|
||||||
|
page = pfn_to_page(pfn + i);
|
||||||
|
if (zone && page_zone(page) != zone)
|
||||||
|
return 0;
|
||||||
|
zone = page_zone(page);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue