HWPOISON: add page flags filter
Wu Fengguang [Wed, 16 Dec 2009 11:19:59 +0000 (12:19 +0100)]
When specified, only poison pages if ((page_flags & mask) == value).

-       corrupt-filter-flags-mask
-       corrupt-filter-flags-value

This allows stress testing of many kinds of pages.

Strictly speaking, the buddy pages requires taking zone lock, to avoid
setting PG_hwpoison on a "was buddy but now allocated to someone" page.
However we can just do nothing because we set PG_locked in the beginning,
this prevents the page allocator from allocating it to someone. (It will
BUG() on the unexpected PG_locked, which is fine for hwpoison testing.)

[AK: Add select PROC_PAGE_MONITOR to satisfy dependency]

CC: Nick Piggin <npiggin@suse.de>
Signed-off-by: Wu Fengguang <fengguang.wu@intel.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>

Documentation/vm/hwpoison.txt
mm/Kconfig
mm/hwpoison-inject.c
mm/internal.h
mm/memory-failure.c

index 4ef7bb3..f454d3c 100644 (file)
@@ -123,6 +123,16 @@ Only handle memory failures to pages associated with the file system defined
 by block device major/minor.  -1U is the wildcard value.
 This should be only used for testing with artificial injection.
 
+
+corrupt-filter-flags-mask
+corrupt-filter-flags-value
+
+When specified, only poison pages if ((page_flags & mask) == value).
+This allows stress testing of many kinds of pages. The page_flags
+are the same as in /proc/kpageflags. The flag bits are defined in
+include/linux/kernel-page-flags.h and documented in
+Documentation/vm/pagemap.txt
+
 Architecture specific MCE injector
 
 x86 has mce-inject, mce-test
index 2310984..8cea7fd 100644 (file)
@@ -253,6 +253,7 @@ config MEMORY_FAILURE
 config HWPOISON_INJECT
        tristate "Poison pages injector"
        depends on MEMORY_FAILURE && DEBUG_KERNEL
+       select PROC_PAGE_MONITOR
 
 config NOMMU_INITIAL_TRIM_EXCESS
        int "Turn on mmap() excess space trimming before booting"
index 2b6b320..c4dfd89 100644 (file)
@@ -102,6 +102,16 @@ static int pfn_inject_init(void)
        if (!dentry)
                goto fail;
 
+       dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600,
+                                   hwpoison_dir, &hwpoison_filter_flags_mask);
+       if (!dentry)
+               goto fail;
+
+       dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600,
+                                   hwpoison_dir, &hwpoison_filter_flags_value);
+       if (!dentry)
+               goto fail;
+
        return 0;
 fail:
        pfn_inject_exit();
index 04bbce8..b2027c7 100644 (file)
@@ -255,3 +255,5 @@ extern int hwpoison_filter(struct page *p);
 
 extern u32 hwpoison_filter_dev_major;
 extern u32 hwpoison_filter_dev_minor;
+extern u64 hwpoison_filter_flags_mask;
+extern u64 hwpoison_filter_flags_value;
index 82ac734..22d2b20 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/page-flags.h>
+#include <linux/kernel-page-flags.h>
 #include <linux/sched.h>
 #include <linux/ksm.h>
 #include <linux/rmap.h>
@@ -50,8 +51,12 @@ atomic_long_t mce_bad_pages __read_mostly = ATOMIC_LONG_INIT(0);
 
 u32 hwpoison_filter_dev_major = ~0U;
 u32 hwpoison_filter_dev_minor = ~0U;
+u64 hwpoison_filter_flags_mask;
+u64 hwpoison_filter_flags_value;
 EXPORT_SYMBOL_GPL(hwpoison_filter_dev_major);
 EXPORT_SYMBOL_GPL(hwpoison_filter_dev_minor);
+EXPORT_SYMBOL_GPL(hwpoison_filter_flags_mask);
+EXPORT_SYMBOL_GPL(hwpoison_filter_flags_value);
 
 static int hwpoison_filter_dev(struct page *p)
 {
@@ -83,11 +88,26 @@ static int hwpoison_filter_dev(struct page *p)
        return 0;
 }
 
+static int hwpoison_filter_flags(struct page *p)
+{
+       if (!hwpoison_filter_flags_mask)
+               return 0;
+
+       if ((stable_page_flags(p) & hwpoison_filter_flags_mask) ==
+                                   hwpoison_filter_flags_value)
+               return 0;
+       else
+               return -EINVAL;
+}
+
 int hwpoison_filter(struct page *p)
 {
        if (hwpoison_filter_dev(p))
                return -EINVAL;
 
+       if (hwpoison_filter_flags(p))
+               return -EINVAL;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(hwpoison_filter);