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; |
