Skip to content

Commit 6a46079

Browse files
Andi KleenAndi Kleen
authored andcommitted
HWPOISON: The high level memory error handler in the VM v7
Add the high level memory handler that poisons pages that got corrupted by hardware (typically by a two bit flip in a DIMM or a cache) on the Linux level. The goal is to prevent everyone from accessing these pages in the future. This done at the VM level by marking a page hwpoisoned and doing the appropriate action based on the type of page it is. The code that does this is portable and lives in mm/memory-failure.c To quote the overview comment: High level machine check handler. Handles pages reported by the hardware as being corrupted usually due to a 2bit ECC memory or cache failure. This focuses on pages detected as corrupted in the background. When the current CPU tries to consume corruption the currently running process can just be killed directly instead. This implies that if the error cannot be handled for some reason it's safe to just ignore it because no corruption has been consumed yet. Instead when that happens another machine check will happen. Handles page cache pages in various states. The tricky part here is that we can access any page asynchronous to other VM users, because memory failures could happen anytime and anywhere, possibly violating some of their assumptions. This is why this code has to be extremely careful. Generally it tries to use normal locking rules, as in get the standard locks, even if that means the error handling takes potentially a long time. Some of the operations here are somewhat inefficient and have non linear algorithmic complexity, because the data structures have not been optimized for this case. This is in particular the case for the mapping from a vma to a process. Since this case is expected to be rare we hope we can get away with this. There are in principle two strategies to kill processes on poison: - just unmap the data and wait for an actual reference before killing - kill as soon as corruption is detected. Both have advantages and disadvantages and should be used in different situations. Right now both are implemented and can be switched with a new sysctl vm.memory_failure_early_kill The default is early kill. The patch does some rmap data structure walking on its own to collect processes to kill. This is unusual because normally all rmap data structure knowledge is in rmap.c only. I put it here for now to keep everything together and rmap knowledge has been seeping out anyways Includes contributions from Johannes Weiner, Chris Mason, Fengguang Wu, Nick Piggin (who did a lot of great work) and others. Cc: [email protected] Cc: [email protected] Signed-off-by: Andi Kleen <[email protected]> Acked-by: Rik van Riel <[email protected]> Reviewed-by: Hidehiro Kawai <[email protected]>
1 parent 4db96cf commit 6a46079

File tree

10 files changed

+934
-3
lines changed

10 files changed

+934
-3
lines changed

Documentation/sysctl/vm.txt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/vm:
3232
- legacy_va_layout
3333
- lowmem_reserve_ratio
3434
- max_map_count
35+
- memory_failure_early_kill
36+
- memory_failure_recovery
3537
- min_free_kbytes
3638
- min_slab_ratio
3739
- min_unmapped_ratio
@@ -53,7 +55,6 @@ Currently, these files are in /proc/sys/vm:
5355
- vfs_cache_pressure
5456
- zone_reclaim_mode
5557

56-
5758
==============================================================
5859

5960
block_dump
@@ -275,6 +276,44 @@ e.g., up to one or two maps per allocation.
275276

276277
The default value is 65536.
277278

279+
=============================================================
280+
281+
memory_failure_early_kill:
282+
283+
Control how to kill processes when uncorrected memory error (typically
284+
a 2bit error in a memory module) is detected in the background by hardware
285+
that cannot be handled by the kernel. In some cases (like the page
286+
still having a valid copy on disk) the kernel will handle the failure
287+
transparently without affecting any applications. But if there is
288+
no other uptodate copy of the data it will kill to prevent any data
289+
corruptions from propagating.
290+
291+
1: Kill all processes that have the corrupted and not reloadable page mapped
292+
as soon as the corruption is detected. Note this is not supported
293+
for a few types of pages, like kernel internally allocated data or
294+
the swap cache, but works for the majority of user pages.
295+
296+
0: Only unmap the corrupted page from all processes and only kill a process
297+
who tries to access it.
298+
299+
The kill is done using a catchable SIGBUS with BUS_MCEERR_AO, so processes can
300+
handle this if they want to.
301+
302+
This is only active on architectures/platforms with advanced machine
303+
check handling and depends on the hardware capabilities.
304+
305+
Applications can override this setting individually with the PR_MCE_KILL prctl
306+
307+
==============================================================
308+
309+
memory_failure_recovery
310+
311+
Enable memory failure recovery (when supported by the platform)
312+
313+
1: Attempt recovery.
314+
315+
0: Always panic on a memory failure.
316+
278317
==============================================================
279318

280319
min_free_kbytes:

fs/proc/meminfo.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,11 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
9595
"Committed_AS: %8lu kB\n"
9696
"VmallocTotal: %8lu kB\n"
9797
"VmallocUsed: %8lu kB\n"
98-
"VmallocChunk: %8lu kB\n",
98+
"VmallocChunk: %8lu kB\n"
99+
#ifdef CONFIG_MEMORY_FAILURE
100+
"HardwareCorrupted: %8lu kB\n"
101+
#endif
102+
,
99103
K(i.totalram),
100104
K(i.freeram),
101105
K(i.bufferram),
@@ -140,6 +144,9 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
140144
(unsigned long)VMALLOC_TOTAL >> 10,
141145
vmi.used >> 10,
142146
vmi.largest_chunk >> 10
147+
#ifdef CONFIG_MEMORY_FAILURE
148+
,atomic_long_read(&mce_bad_pages) << (PAGE_SHIFT - 10)
149+
#endif
143150
);
144151

145152
hugetlb_report_meminfo(m);

include/linux/mm.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,5 +1309,12 @@ void vmemmap_populate_print_last(void);
13091309
extern int account_locked_memory(struct mm_struct *mm, struct rlimit *rlim,
13101310
size_t size);
13111311
extern void refund_locked_memory(struct mm_struct *mm, size_t size);
1312+
1313+
extern void memory_failure(unsigned long pfn, int trapno);
1314+
extern int __memory_failure(unsigned long pfn, int trapno, int ref);
1315+
extern int sysctl_memory_failure_early_kill;
1316+
extern int sysctl_memory_failure_recovery;
1317+
extern atomic_long_t mce_bad_pages;
1318+
13121319
#endif /* __KERNEL__ */
13131320
#endif /* _LINUX_MM_H */

include/linux/rmap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ int try_to_munlock(struct page *);
129129
*/
130130
struct anon_vma *page_lock_anon_vma(struct page *page);
131131
void page_unlock_anon_vma(struct anon_vma *anon_vma);
132+
int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma);
132133

133134
#else /* !CONFIG_MMU */
134135

kernel/sysctl.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,31 @@ static struct ctl_table vm_table[] = {
13721372
.mode = 0644,
13731373
.proc_handler = &scan_unevictable_handler,
13741374
},
1375+
#ifdef CONFIG_MEMORY_FAILURE
1376+
{
1377+
.ctl_name = CTL_UNNUMBERED,
1378+
.procname = "memory_failure_early_kill",
1379+
.data = &sysctl_memory_failure_early_kill,
1380+
.maxlen = sizeof(sysctl_memory_failure_early_kill),
1381+
.mode = 0644,
1382+
.proc_handler = &proc_dointvec_minmax,
1383+
.strategy = &sysctl_intvec,
1384+
.extra1 = &zero,
1385+
.extra2 = &one,
1386+
},
1387+
{
1388+
.ctl_name = CTL_UNNUMBERED,
1389+
.procname = "memory_failure_recovery",
1390+
.data = &sysctl_memory_failure_recovery,
1391+
.maxlen = sizeof(sysctl_memory_failure_recovery),
1392+
.mode = 0644,
1393+
.proc_handler = &proc_dointvec_minmax,
1394+
.strategy = &sysctl_intvec,
1395+
.extra1 = &zero,
1396+
.extra2 = &one,
1397+
},
1398+
#endif
1399+
13751400
/*
13761401
* NOTE: do not add new entries to this table unless you have read
13771402
* Documentation/sysctl/ctl_unnumbered.txt

mm/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,16 @@ config DEFAULT_MMAP_MIN_ADDR
233233
/proc/sys/vm/mmap_min_addr tunable.
234234

235235

236+
config MEMORY_FAILURE
237+
depends on MMU
238+
depends on X86_MCE
239+
bool "Enable recovery from hardware memory errors"
240+
help
241+
Enables code to recover from some memory failures on systems
242+
with MCA recovery. This allows a system to continue running
243+
even when some of its memory has uncorrected errors. This requires
244+
special hardware support and typically ECC memory.
245+
236246
config NOMMU_INITIAL_TRIM_EXCESS
237247
int "Turn on mmap() excess space trimming before booting"
238248
depends on !MMU

mm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@ obj-$(CONFIG_SMP) += allocpercpu.o
4040
endif
4141
obj-$(CONFIG_QUICKLIST) += quicklist.o
4242
obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o
43+
obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o
4344
obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
4445
obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o

mm/filemap.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@
104104
*
105105
* ->task->proc_lock
106106
* ->dcache_lock (proc_pid_lookup)
107+
*
108+
* (code doesn't rely on that order, so you could switch it around)
109+
* ->tasklist_lock (memory_failure, collect_procs_ao)
110+
* ->i_mmap_lock
107111
*/
108112

109113
/*

0 commit comments

Comments
 (0)