HWPOISON: add page flags filter

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>
This commit is contained in:
Wu Fengguang 2009-12-16 12:19:59 +01:00 committed by Andi Kleen
parent 1a9b5b7fe0
commit 478c5ffc0b
5 changed files with 43 additions and 0 deletions

View 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. by block device major/minor. -1U is the wildcard value.
This should be only used for testing with artificial injection. 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 Architecture specific MCE injector
x86 has mce-inject, mce-test x86 has mce-inject, mce-test

View file

@ -253,6 +253,7 @@ config MEMORY_FAILURE
config HWPOISON_INJECT config HWPOISON_INJECT
tristate "Poison pages injector" tristate "Poison pages injector"
depends on MEMORY_FAILURE && DEBUG_KERNEL depends on MEMORY_FAILURE && DEBUG_KERNEL
select PROC_PAGE_MONITOR
config NOMMU_INITIAL_TRIM_EXCESS config NOMMU_INITIAL_TRIM_EXCESS
int "Turn on mmap() excess space trimming before booting" int "Turn on mmap() excess space trimming before booting"

View file

@ -102,6 +102,16 @@ static int pfn_inject_init(void)
if (!dentry) if (!dentry)
goto fail; 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; return 0;
fail: fail:
pfn_inject_exit(); pfn_inject_exit();

View file

@ -255,3 +255,5 @@ extern int hwpoison_filter(struct page *p);
extern u32 hwpoison_filter_dev_major; extern u32 hwpoison_filter_dev_major;
extern u32 hwpoison_filter_dev_minor; extern u32 hwpoison_filter_dev_minor;
extern u64 hwpoison_filter_flags_mask;
extern u64 hwpoison_filter_flags_value;

View file

@ -34,6 +34,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/page-flags.h> #include <linux/page-flags.h>
#include <linux/kernel-page-flags.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/ksm.h> #include <linux/ksm.h>
#include <linux/rmap.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_major = ~0U;
u32 hwpoison_filter_dev_minor = ~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_major);
EXPORT_SYMBOL_GPL(hwpoison_filter_dev_minor); 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) static int hwpoison_filter_dev(struct page *p)
{ {
@ -83,11 +88,26 @@ static int hwpoison_filter_dev(struct page *p)
return 0; 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) int hwpoison_filter(struct page *p)
{ {
if (hwpoison_filter_dev(p)) if (hwpoison_filter_dev(p))
return -EINVAL; return -EINVAL;
if (hwpoison_filter_flags(p))
return -EINVAL;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(hwpoison_filter); EXPORT_SYMBOL_GPL(hwpoison_filter);