aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorWu Fengguang <fengguang.wu@intel.com>2009-12-16 06:19:58 -0500
committerAndi Kleen <ak@linux.intel.com>2009-12-16 06:19:58 -0500
commit847ce401df392b0704369fd3f75df614ac1414b4 (patch)
tree7c5021386dedea0d12f8a05b00c5267c4d28e426 /mm
parent8d22ba1b74aa9420b6032d856446564fb21f8090 (diff)
HWPOISON: Add unpoisoning support
The unpoisoning interface is useful for stress testing tools to reclaim poisoned pages (to prevent OOM) There is no hardware level unpoisioning, so this cannot be used for real memory errors, only for software injected errors. Note that it may leak pages silently - those who have been removed from LRU cache, but not isolated from page cache/swap cache at hwpoison time. Especially the stress test of dirty swap cache pages shall reboot system before exhausting memory. AK: Fix comments, add documentation, add printks, rename symbol Signed-off-by: Wu Fengguang <fengguang.wu@intel.com> Signed-off-by: Andi Kleen <ak@linux.intel.com>
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);