[PATCH] mm, hwpoison: Call memory_failure() for source page of COW failure

From: Tony Luck
Date: Thu Oct 20 2022 - 12:57:28 EST


Cannot call memory_failure() directly from the fault handler because
mmap_lock (and others) are held.

It is important, but not urgent, to mark the source page as h/w poisoned
and unmap it from other tasks.

Use schedule_work() to queue a request to call memory_failure() for the
page with the error.

Signed-off-by: Tony Luck <tony.luck@xxxxxxxxx>
---
mm/memory.c | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/mm/memory.c b/mm/memory.c
index b6056eef2f72..4a1304cf1f4e 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2848,6 +2848,37 @@ static inline int pte_unmap_same(struct vm_fault *vmf)
return same;
}

+#ifdef CONFIG_MEMORY_FAILURE
+struct pfn_work {
+ struct work_struct work;
+ unsigned long pfn;
+};
+
+static void do_sched_memory_failure(struct work_struct *w)
+{
+ struct pfn_work *p = container_of(w, struct pfn_work, work);
+
+ memory_failure(p->pfn, 0);
+ kfree(p);
+}
+
+static void sched_memory_failure(unsigned long pfn)
+{
+ struct pfn_work *p;
+
+ p = kmalloc(sizeof *p, GFP_KERNEL);
+ if (!p)
+ return;
+ INIT_WORK(&p->work, do_sched_memory_failure);
+ p->pfn = pfn;
+ schedule_work(&p->work);
+}
+#else
+static void sched_memory_failure(unsigned long pfn)
+{
+}
+#endif
+
/*
* Return:
* 0: copied succeeded
@@ -2866,8 +2897,10 @@ static inline int __wp_page_copy_user(struct page *dst, struct page *src,
unsigned long addr = vmf->address;

if (likely(src)) {
- if (copy_mc_user_highpage(dst, src, addr, vma))
+ if (copy_mc_user_highpage(dst, src, addr, vma)) {
+ sched_memory_failure(page_to_pfn(src));
return -EHWPOISON;
+ }
return 0;
}

--
2.37.3