Skip to content

Commit 31d3d34

Browse files
Wu FengguangAndi Kleen
Wu Fengguang
authored and
Andi Kleen
committed
HWPOISON: limit hwpoison injector to known page types
__memory_failure()'s workflow is set PG_hwpoison //... unset PG_hwpoison if didn't pass hwpoison filter That could kill unrelated process if it happens to page fault on the page with the (temporary) PG_hwpoison. The race should be big enough to appear in stress tests. Fix it by grabbing the page and checking filter at inject time. This also avoids the very noisy "Injecting memory failure..." messages. - we don't touch madvise() based injection, because the filters are generally not necessary for it. - if we want to apply the filters to h/w aided injection, we'd better to rearrange the logic in __memory_failure() instead of this patch. AK: fix documentation, use drain all, cleanups CC: Haicheng Li <[email protected]> Signed-off-by: Wu Fengguang <[email protected]> Signed-off-by: Andi Kleen <[email protected]>
1 parent 7c116f2 commit 31d3d34

File tree

3 files changed

+43
-3
lines changed

3 files changed

+43
-3
lines changed

Documentation/vm/hwpoison.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ hwpoison-inject module through debugfs
103103

104104
corrupt-pfn
105105

106-
Inject hwpoison fault at PFN echoed into this file.
106+
Inject hwpoison fault at PFN echoed into this file. This does
107+
some early filtering to avoid corrupted unintended pages in test suites.
107108

108109
unpoison-pfn
109110

mm/hwpoison-inject.c

+39-2
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,53 @@
33
#include <linux/debugfs.h>
44
#include <linux/kernel.h>
55
#include <linux/mm.h>
6+
#include <linux/swap.h>
7+
#include <linux/pagemap.h>
68
#include "internal.h"
79

810
static struct dentry *hwpoison_dir;
911

1012
static int hwpoison_inject(void *data, u64 val)
1113
{
14+
unsigned long pfn = val;
15+
struct page *p;
16+
int err;
17+
1218
if (!capable(CAP_SYS_ADMIN))
1319
return -EPERM;
14-
printk(KERN_INFO "Injecting memory failure at pfn %Lx\n", val);
15-
return __memory_failure(val, 18, 0);
20+
21+
if (!pfn_valid(pfn))
22+
return -ENXIO;
23+
24+
p = pfn_to_page(pfn);
25+
/*
26+
* This implies unable to support free buddy pages.
27+
*/
28+
if (!get_page_unless_zero(p))
29+
return 0;
30+
31+
if (!PageLRU(p))
32+
shake_page(p);
33+
/*
34+
* This implies unable to support non-LRU pages.
35+
*/
36+
if (!PageLRU(p))
37+
return 0;
38+
39+
/*
40+
* do a racy check with elevated page count, to make sure PG_hwpoison
41+
* will only be set for the targeted owner (or on a free page).
42+
* We temporarily take page lock for try_get_mem_cgroup_from_page().
43+
* __memory_failure() will redo the check reliably inside page lock.
44+
*/
45+
lock_page(p);
46+
err = hwpoison_filter(p);
47+
unlock_page(p);
48+
if (err)
49+
return 0;
50+
51+
printk(KERN_INFO "Injecting memory failure at pfn %lx\n", pfn);
52+
return __memory_failure(pfn, 18, MF_COUNT_INCREASED);
1653
}
1754

1855
static int hwpoison_unpoison(void *data, u64 val)

mm/internal.h

+2
Original file line numberDiff line numberDiff line change
@@ -251,5 +251,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
251251
#define ZONE_RECLAIM_SUCCESS 1
252252
#endif
253253

254+
extern int hwpoison_filter(struct page *p);
255+
254256
extern u32 hwpoison_filter_dev_major;
255257
extern u32 hwpoison_filter_dev_minor;

0 commit comments

Comments
 (0)