We can currently crash in shrink_zone_span() in case we access an
uninitialized memmap (via page_to_nid()). Root issue is that we cannot
always identify which memmap was actually initialized.

Let's improve the situation by looking only at online PFNs for
!ZONE_DEVICE memory. This is now very reliable - similar to
set_zone_contiguous(). (Side note: set_zone_contiguous() will never
succeed on ZONE_DEVICE memory right now as we have no online PFNs ...).

For ZONE_DEVICE memory, make sure we don't crash by special-casing
poisoned pages and always checking that the NID has a sane value. We
might still read garbage and get false positives, but it certainly
improves the situation.

Note: Especially subsections make it very hard to detect which parts of
a ZONE_DEVICE memmap were actually initialized - otherwise we could just
have reused SECTION_IS_ONLINE. This needs more thought.

Cc: Andrew Morton <[email protected]>
Cc: Oscar Salvador <[email protected]>
Cc: David Hildenbrand <[email protected]>
Cc: Michal Hocko <[email protected]>
Cc: Pavel Tatashin <[email protected]>
Cc: Dan Williams <[email protected]>
Cc: Wei Yang <[email protected]>
Reported-by: Aneesh Kumar K.V <[email protected]>
Signed-off-by: David Hildenbrand <[email protected]>
---
 mm/memory_hotplug.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 663853bf97ed..65b3fdf7f838 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -334,6 +334,17 @@ static unsigned long find_smallest_section_pfn(int nid, 
struct zone *zone,
                if (unlikely(!pfn_valid(start_pfn)))
                        continue;
 
+               /*
+                * TODO: There is no way we can identify whether the memmap
+                * of ZONE_DEVICE memory was initialized. We might get
+                * false positives when reading garbage.
+                */
+               if (zone_idx(zone) == ZONE_DEVICE) {
+                       if (PagePoisoned(pfn_to_page(start_pfn)))
+                               continue;
+               } else if (!pfn_to_online_page(start_pfn))
+                       continue;
+
                if (unlikely(pfn_to_nid(start_pfn) != nid))
                        continue;
 
@@ -359,6 +370,17 @@ static unsigned long find_biggest_section_pfn(int nid, 
struct zone *zone,
                if (unlikely(!pfn_valid(pfn)))
                        continue;
 
+               /*
+                * TODO: There is no way we can identify whether the memmap
+                * of ZONE_DEVICE memory was initialized. We might get
+                * false positives when reading garbage.
+                */
+               if (zone_idx(zone) == ZONE_DEVICE) {
+                       if (PagePoisoned(pfn_to_page(pfn)))
+                               continue;
+               } else if (!pfn_to_online_page(pfn))
+                       continue;
+
                if (unlikely(pfn_to_nid(pfn) != nid))
                        continue;
 
-- 
2.21.0

Reply via email to