When the pages are being given to the allocator, check if they are
poisoned, and mark them as such.

Similar to unaccepted memory, hook it in __free_pages_core(), and thus
the frames never enter the allocator, rather than being taken back out
of it.

hwpoison_boot_page() leaves a frame in the state a frame poisoned by this
kernel would be in, so everything that already understands PG_hwpoison
covers it, the kexec segment placement check included.

Suggested-by: Kiryl Shutsemau <[email protected]>
Signed-off-by: Breno Leitao <[email protected]>
---
 include/linux/mm.h  |  5 +++++
 mm/memory-failure.c | 15 +++++++++++++++
 mm/page_alloc.c     | 25 +++++++++++++++++++++++++
 3 files changed, 45 insertions(+)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index b68824fcfbef1..9d9f2e8fdc136 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -5225,6 +5225,7 @@ extern const struct attribute_group 
memory_failure_attr_group;
 extern void memory_failure_queue(unsigned long pfn, int flags);
 void num_poisoned_pages_inc(unsigned long pfn);
 void num_poisoned_pages_sub(unsigned long pfn, long i);
+void __meminit hwpoison_boot_page(struct page *page);
 phys_addr_t range_first_hwpoison(phys_addr_t start, unsigned long size);
 phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size);
 #else
@@ -5232,6 +5233,10 @@ static inline void memory_failure_queue(unsigned long 
pfn, int flags)
 {
 }
 
+static inline void hwpoison_boot_page(struct page *page)
+{
+}
+
 static inline void num_poisoned_pages_inc(unsigned long pfn)
 {
 }
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index d9b8be696aac3..f6afdb2a89a94 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -137,6 +137,21 @@ phys_addr_t range_last_hwpoison(phys_addr_t start, 
unsigned long size)
        return range_hwpoison(start, size, false);
 }
 
+static void update_per_node_mf_stats(unsigned long pfn, enum mf_result result);
+
+/* Not num_poisoned_pages_inc(): its per block half divides by zero this 
early. */
+void __meminit hwpoison_boot_page(struct page *page)
+{
+       if (PageHWPoison(page))
+               return;
+
+       SetPageHWPoison(page);
+       set_page_count(page, 1);
+       /* The page has been completely isolated == MF_RECOVERED */
+       update_per_node_mf_stats(page_to_pfn(page), MF_RECOVERED);
+       atomic_long_inc(&num_poisoned_pages);
+}
+
 /**
  * MF_ATTR_RO - Create sysfs entry for each memory failure statistics.
  * @_name: name of the file in the per NUMA sysfs directory.
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 404896b53003e..9e2ce833fd409 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1579,6 +1579,19 @@ static void __free_pages_ok(struct page *page, unsigned 
int order,
                free_one_page(zone, page, pfn, order, fpi_flags);
 }
 
+/* Flag the frames an earlier kernel recorded as bad. */
+static void __meminit poison_block(struct page *page, unsigned int order)
+{
+       unsigned long i, nr_pages = 1UL << order;
+
+       for (i = 0; i < nr_pages; i++) {
+               struct page *p = page + i;
+
+               if (range_contains_poisoned_memory(page_to_phys(p), PAGE_SIZE))
+                       hwpoison_boot_page(p);
+       }
+}
+
 void __meminit __free_pages_core(struct page *page, unsigned int order,
                enum meminit_context context)
 {
@@ -1613,6 +1626,18 @@ void __meminit __free_pages_core(struct page *page, 
unsigned int order,
                atomic_long_add(nr_pages, &page_zone(page)->managed_pages);
        }
 
+       /* First: a block parked by __free_unaccepted() never returns here. */
+       if (range_contains_poisoned_memory(page_to_phys(page),
+                                          PAGE_SIZE << order)) {
+               poison_block(page, order);
+               /*
+                * TODO: free the frames in the block that are not poisoned.
+                * They stay out of the allocator and still count in
+                * managed_pages, so a unit costs up to a block.
+                */
+               return;
+       }
+
        if (page_contains_unaccepted(page, order)) {
                if (order == MAX_PAGE_ORDER && __free_unaccepted(page))
                        return;

-- 
2.53.0-Meta


Reply via email to