[PATCH 1/2] mm/memory-failure: add panic_on_unrecoverable_memory_failure sysctl

Breno Leitao posted 2 patches 4 hours ago
[PATCH 1/2] mm/memory-failure: add panic_on_unrecoverable_memory_failure sysctl
Posted by Breno Leitao 4 hours ago
When memory_failure() encounters an in-use kernel page that cannot be
recovered (slab, page tables, kernel stacks, reserved, vmalloc, etc.),
it currently logs MF_IGNORED and continues. This leaves corrupted data
accessible to the kernel, risking silent data corruption or a delayed
crash when the poisoned cache line is next accessed.

For example, a multi-bit ECC error on a dentry cache slab page was
ignored by memory_failure(), and 67 seconds later d_lookup() accessed
the poisoned cache line, causing a synchronous external abort:

  [88690.479680] [Hardware Error]: error_type: 3, multi-bit ECC
  [88690.498473] Memory failure: 0x40272d: unhandlable page.
  [88690.498619] Memory failure: 0x40272d: recovery action for
                 get hwpoison page: Ignored
  ...
  [88757.847126] Internal error: synchronous external abort:
                 0000000096000410 [#1] SMP
  [88758.061075] pc : d_lookup+0x5c/0x220

Add a new sysctl vm.panic_on_unrecoverable_memory_failure (default 0)
that, when set to 1, panics immediately on unrecoverable memory
failures. This provides a clean crash dump at the time of the error
rather than a delayed crash with potential silent corruption in between.

The panic is placed in action_result() so that all call sites that log
MF_MSG_GET_HWPOISON with MF_IGNORED are covered, including the hugetlb
path in try_memory_failure_hugetlb().

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 mm/memory-failure.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index ee42d43613097..25bd043497195 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -74,6 +74,8 @@ static int sysctl_memory_failure_recovery __read_mostly = 1;
 
 static int sysctl_enable_soft_offline __read_mostly = 1;
 
+static int sysctl_panic_on_unrecoverable_mf __read_mostly;
+
 atomic_long_t num_poisoned_pages __read_mostly = ATOMIC_LONG_INIT(0);
 
 static bool hw_memory_failure __read_mostly = false;
@@ -155,6 +157,15 @@ static const struct ctl_table memory_failure_table[] = {
 		.proc_handler	= proc_dointvec_minmax,
 		.extra1		= SYSCTL_ZERO,
 		.extra2		= SYSCTL_ONE,
+	},
+	{
+		.procname	= "panic_on_unrecoverable_memory_failure",
+		.data		= &sysctl_panic_on_unrecoverable_mf,
+		.maxlen		= sizeof(sysctl_panic_on_unrecoverable_mf),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= SYSCTL_ZERO,
+		.extra2		= SYSCTL_ONE,
 	}
 };
 
@@ -1298,6 +1309,10 @@ static int action_result(unsigned long pfn, enum mf_action_page_type type,
 	pr_err("%#lx: recovery action for %s: %s\n",
 		pfn, action_page_types[type], action_name[result]);
 
+	if (sysctl_panic_on_unrecoverable_mf &&
+	    type == MF_MSG_GET_HWPOISON && result == MF_IGNORED)
+		panic("Memory failure: %#lx: unrecoverable page", pfn);
+
 	return (result == MF_RECOVERED || result == MF_DELAYED) ? 0 : -EBUSY;
 }
 

-- 
2.52.0