diff options
-rw-r--r-- | Documentation/vm/hwpoison.txt | 3 | ||||
-rw-r--r-- | mm/hwpoison-inject.c | 41 | ||||
-rw-r--r-- | mm/internal.h | 2 |
3 files changed, 43 insertions, 3 deletions
diff --git a/Documentation/vm/hwpoison.txt b/Documentation/vm/hwpoison.txt index fdf580464324..4ef7bb30d15c 100644 --- a/Documentation/vm/hwpoison.txt +++ b/Documentation/vm/hwpoison.txt | |||
@@ -103,7 +103,8 @@ hwpoison-inject module through debugfs | |||
103 | 103 | ||
104 | corrupt-pfn | 104 | corrupt-pfn |
105 | 105 | ||
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. | ||
107 | 108 | ||
108 | unpoison-pfn | 109 | unpoison-pfn |
109 | 110 | ||
diff --git a/mm/hwpoison-inject.c b/mm/hwpoison-inject.c index ac692a9b766c..2b6b3200fa65 100644 --- a/mm/hwpoison-inject.c +++ b/mm/hwpoison-inject.c | |||
@@ -3,16 +3,53 @@ | |||
3 | #include <linux/debugfs.h> | 3 | #include <linux/debugfs.h> |
4 | #include <linux/kernel.h> | 4 | #include <linux/kernel.h> |
5 | #include <linux/mm.h> | 5 | #include <linux/mm.h> |
6 | #include <linux/swap.h> | ||
7 | #include <linux/pagemap.h> | ||
6 | #include "internal.h" | 8 | #include "internal.h" |
7 | 9 | ||
8 | static struct dentry *hwpoison_dir; | 10 | static struct dentry *hwpoison_dir; |
9 | 11 | ||
10 | static int hwpoison_inject(void *data, u64 val) | 12 | static int hwpoison_inject(void *data, u64 val) |
11 | { | 13 | { |
14 | unsigned long pfn = val; | ||
15 | struct page *p; | ||
16 | int err; | ||
17 | |||
12 | if (!capable(CAP_SYS_ADMIN)) | 18 | if (!capable(CAP_SYS_ADMIN)) |
13 | return -EPERM; | 19 | return -EPERM; |
14 | printk(KERN_INFO "Injecting memory failure at pfn %Lx\n", val); | 20 | |
15 | return __memory_failure(val, 18, 0); | 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); | ||
16 | } | 53 | } |
17 | 54 | ||
18 | static int hwpoison_unpoison(void *data, u64 val) | 55 | static int hwpoison_unpoison(void *data, u64 val) |
diff --git a/mm/internal.h b/mm/internal.h index 814da335f050..04bbce8b8ba6 100644 --- a/mm/internal.h +++ b/mm/internal.h | |||
@@ -251,5 +251,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, | |||
251 | #define ZONE_RECLAIM_SUCCESS 1 | 251 | #define ZONE_RECLAIM_SUCCESS 1 |
252 | #endif | 252 | #endif |
253 | 253 | ||
254 | extern int hwpoison_filter(struct page *p); | ||
255 | |||
254 | extern u32 hwpoison_filter_dev_major; | 256 | extern u32 hwpoison_filter_dev_major; |
255 | extern u32 hwpoison_filter_dev_minor; | 257 | extern u32 hwpoison_filter_dev_minor; |