Skip to content

Commit 4e8346d

Browse files
mzaslonktorvalds
authored andcommitted
memory_hotplug: fix kernel_panic on offline page processing
Within show_valid_zones() the function test_pages_in_a_zone() should be called for online memory blocks only. Otherwise it might lead to the VM_BUG_ON due to uninitialized struct pages (when CONFIG_DEBUG_VM_PGFLAGS kernel option is set): page dumped because: VM_BUG_ON_PAGE(PagePoisoned(p)) ------------[ cut here ]------------ Call Trace: ([<000000000038f91e>] test_pages_in_a_zone+0xe6/0x168) [<0000000000923472>] show_valid_zones+0x5a/0x1a8 [<0000000000900284>] dev_attr_show+0x3c/0x78 [<000000000046f6f0>] sysfs_kf_seq_show+0xd0/0x150 [<00000000003ef662>] seq_read+0x212/0x4b8 [<00000000003bf202>] __vfs_read+0x3a/0x178 [<00000000003bf3ca>] vfs_read+0x8a/0x148 [<00000000003bfa3a>] ksys_read+0x62/0xb8 [<0000000000bc2220>] system_call+0xdc/0x2d8 That VM_BUG_ON was triggered by the page poisoning introduced in mm/sparse.c with the git commit d0dc12e ("mm/memory_hotplug: optimize memory hotplug"). With the same commit the new 'nid' field has been added to the struct memory_block in order to store and later on derive the node id for offline pages (instead of accessing struct page which might be uninitialized). But one reference to nid in show_valid_zones() function has been overlooked. Fixed with current commit. Also, nr_pages will not be used any more after test_pages_in_a_zone() call, do not update it. Link: http://lkml.kernel.org/r/[email protected] Fixes: d0dc12e ("mm/memory_hotplug: optimize memory hotplug") Signed-off-by: Mikhail Zaslonko <[email protected]> Acked-by: Michal Hocko <[email protected]> Reviewed-by: Pavel Tatashin <[email protected]> Cc: <[email protected]> [4.17+] Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 328b5f4 commit 4e8346d

File tree

1 file changed

+9
-11
lines changed

1 file changed

+9
-11
lines changed

drivers/base/memory.c

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -416,26 +416,24 @@ static ssize_t show_valid_zones(struct device *dev,
416416
struct zone *default_zone;
417417
int nid;
418418

419-
/*
420-
* The block contains more than one zone can not be offlined.
421-
* This can happen e.g. for ZONE_DMA and ZONE_DMA32
422-
*/
423-
if (!test_pages_in_a_zone(start_pfn, start_pfn + nr_pages, &valid_start_pfn, &valid_end_pfn))
424-
return sprintf(buf, "none\n");
425-
426-
start_pfn = valid_start_pfn;
427-
nr_pages = valid_end_pfn - start_pfn;
428-
429419
/*
430420
* Check the existing zone. Make sure that we do that only on the
431421
* online nodes otherwise the page_zone is not reliable
432422
*/
433423
if (mem->state == MEM_ONLINE) {
424+
/*
425+
* The block contains more than one zone can not be offlined.
426+
* This can happen e.g. for ZONE_DMA and ZONE_DMA32
427+
*/
428+
if (!test_pages_in_a_zone(start_pfn, start_pfn + nr_pages,
429+
&valid_start_pfn, &valid_end_pfn))
430+
return sprintf(buf, "none\n");
431+
start_pfn = valid_start_pfn;
434432
strcat(buf, page_zone(pfn_to_page(start_pfn))->name);
435433
goto out;
436434
}
437435

438-
nid = pfn_to_nid(start_pfn);
436+
nid = mem->nid;
439437
default_zone = zone_for_pfn_range(MMOP_ONLINE_KEEP, nid, start_pfn, nr_pages);
440438
strcat(buf, default_zone->name);
441439

0 commit comments

Comments
 (0)