aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
Diffstat (limited to 'mm')
-rw-r--r--mm/hwpoison-inject.c36
-rw-r--r--mm/memory-failure.c68
2 files changed, 98 insertions, 6 deletions
diff --git a/mm/hwpoison-inject.c b/mm/hwpoison-inject.c
index e1d85137f086..6e35e563bf50 100644
--- a/mm/hwpoison-inject.c
+++ b/mm/hwpoison-inject.c
@@ -4,7 +4,7 @@
4#include <linux/kernel.h> 4#include <linux/kernel.h>
5#include <linux/mm.h> 5#include <linux/mm.h>
6 6
7static struct dentry *hwpoison_dir, *corrupt_pfn; 7static struct dentry *hwpoison_dir;
8 8
9static int hwpoison_inject(void *data, u64 val) 9static int hwpoison_inject(void *data, u64 val)
10{ 10{
@@ -14,7 +14,16 @@ static int hwpoison_inject(void *data, u64 val)
14 return __memory_failure(val, 18, 0); 14 return __memory_failure(val, 18, 0);
15} 15}
16 16
17static int hwpoison_unpoison(void *data, u64 val)
18{
19 if (!capable(CAP_SYS_ADMIN))
20 return -EPERM;
21
22 return unpoison_memory(val);
23}
24
17DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n"); 25DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
26DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
18 27
19static void pfn_inject_exit(void) 28static void pfn_inject_exit(void)
20{ 29{
@@ -24,16 +33,31 @@ static void pfn_inject_exit(void)
24 33
25static int pfn_inject_init(void) 34static int pfn_inject_init(void)
26{ 35{
36 struct dentry *dentry;
37
27 hwpoison_dir = debugfs_create_dir("hwpoison", NULL); 38 hwpoison_dir = debugfs_create_dir("hwpoison", NULL);
28 if (hwpoison_dir == NULL) 39 if (hwpoison_dir == NULL)
29 return -ENOMEM; 40 return -ENOMEM;
30 corrupt_pfn = debugfs_create_file("corrupt-pfn", 0600, hwpoison_dir, 41
42 /*
43 * Note that the below poison/unpoison interfaces do not involve
44 * hardware status change, hence do not require hardware support.
45 * They are mainly for testing hwpoison in software level.
46 */
47 dentry = debugfs_create_file("corrupt-pfn", 0600, hwpoison_dir,
31 NULL, &hwpoison_fops); 48 NULL, &hwpoison_fops);
32 if (corrupt_pfn == NULL) { 49 if (!dentry)
33 pfn_inject_exit(); 50 goto fail;
34 return -ENOMEM; 51
35 } 52 dentry = debugfs_create_file("unpoison-pfn", 0600, hwpoison_dir,
53 NULL, &unpoison_fops);
54 if (!dentry)
55 goto fail;
56
36 return 0; 57 return 0;
58fail:
59 pfn_inject_exit();
60 return -ENOMEM;
37} 61}
38 62
39module_init(pfn_inject_init); 63module_init(pfn_inject_init);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 5055b940df5f..ed6e91c87a54 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -838,6 +838,16 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
838 * and in many cases impossible, so we just avoid it here. 838 * and in many cases impossible, so we just avoid it here.
839 */ 839 */
840 lock_page_nosync(p); 840 lock_page_nosync(p);
841
842 /*
843 * unpoison always clear PG_hwpoison inside page lock
844 */
845 if (!PageHWPoison(p)) {
846 action_result(pfn, "unpoisoned", IGNORED);
847 res = 0;
848 goto out;
849 }
850
841 wait_on_page_writeback(p); 851 wait_on_page_writeback(p);
842 852
843 /* 853 /*
@@ -893,3 +903,61 @@ void memory_failure(unsigned long pfn, int trapno)
893{ 903{
894 __memory_failure(pfn, trapno, 0); 904 __memory_failure(pfn, trapno, 0);
895} 905}
906
907/**
908 * unpoison_memory - Unpoison a previously poisoned page
909 * @pfn: Page number of the to be unpoisoned page
910 *
911 * Software-unpoison a page that has been poisoned by
912 * memory_failure() earlier.
913 *
914 * This is only done on the software-level, so it only works
915 * for linux injected failures, not real hardware failures
916 *
917 * Returns 0 for success, otherwise -errno.
918 */
919int unpoison_memory(unsigned long pfn)
920{
921 struct page *page;
922 struct page *p;
923 int freeit = 0;
924
925 if (!pfn_valid(pfn))
926 return -ENXIO;
927
928 p = pfn_to_page(pfn);
929 page = compound_head(p);
930
931 if (!PageHWPoison(p)) {
932 pr_debug("MCE: Page was already unpoisoned %#lx\n", pfn);
933 return 0;
934 }
935
936 if (!get_page_unless_zero(page)) {
937 if (TestClearPageHWPoison(p))
938 atomic_long_dec(&mce_bad_pages);
939 pr_debug("MCE: Software-unpoisoned free page %#lx\n", pfn);
940 return 0;
941 }
942
943 lock_page_nosync(page);
944 /*
945 * This test is racy because PG_hwpoison is set outside of page lock.
946 * That's acceptable because that won't trigger kernel panic. Instead,
947 * the PG_hwpoison page will be caught and isolated on the entrance to
948 * the free buddy page pool.
949 */
950 if (TestClearPageHWPoison(p)) {
951 pr_debug("MCE: Software-unpoisoned page %#lx\n", pfn);
952 atomic_long_dec(&mce_bad_pages);
953 freeit = 1;
954 }
955 unlock_page(page);
956
957 put_page(page);
958 if (freeit)
959 put_page(page);
960
961 return 0;
962}
963EXPORT_SYMBOL(unpoison_memory);