xref: /linux/mm/hwpoison-inject.c (revision 478c5ffc0b50527bd2390f2daa46cc16276b8413)
1cae681fcSAndi Kleen /* Inject a hwpoison memory failure on a arbitary pfn */
2cae681fcSAndi Kleen #include <linux/module.h>
3cae681fcSAndi Kleen #include <linux/debugfs.h>
4cae681fcSAndi Kleen #include <linux/kernel.h>
5cae681fcSAndi Kleen #include <linux/mm.h>
631d3d348SWu Fengguang #include <linux/swap.h>
731d3d348SWu Fengguang #include <linux/pagemap.h>
87c116f2bSWu Fengguang #include "internal.h"
9cae681fcSAndi Kleen 
10847ce401SWu Fengguang static struct dentry *hwpoison_dir;
11cae681fcSAndi Kleen 
12cae681fcSAndi Kleen static int hwpoison_inject(void *data, u64 val)
13cae681fcSAndi Kleen {
1431d3d348SWu Fengguang 	unsigned long pfn = val;
1531d3d348SWu Fengguang 	struct page *p;
1631d3d348SWu Fengguang 	int err;
1731d3d348SWu Fengguang 
18cae681fcSAndi Kleen 	if (!capable(CAP_SYS_ADMIN))
19cae681fcSAndi Kleen 		return -EPERM;
2031d3d348SWu Fengguang 
2131d3d348SWu Fengguang 	if (!pfn_valid(pfn))
2231d3d348SWu Fengguang 		return -ENXIO;
2331d3d348SWu Fengguang 
2431d3d348SWu Fengguang 	p = pfn_to_page(pfn);
2531d3d348SWu Fengguang 	/*
2631d3d348SWu Fengguang 	 * This implies unable to support free buddy pages.
2731d3d348SWu Fengguang 	 */
2831d3d348SWu Fengguang 	if (!get_page_unless_zero(p))
2931d3d348SWu Fengguang 		return 0;
3031d3d348SWu Fengguang 
3131d3d348SWu Fengguang 	if (!PageLRU(p))
3231d3d348SWu Fengguang 		shake_page(p);
3331d3d348SWu Fengguang 	/*
3431d3d348SWu Fengguang 	 * This implies unable to support non-LRU pages.
3531d3d348SWu Fengguang 	 */
3631d3d348SWu Fengguang 	if (!PageLRU(p))
3731d3d348SWu Fengguang 		return 0;
3831d3d348SWu Fengguang 
3931d3d348SWu Fengguang 	/*
4031d3d348SWu Fengguang 	 * do a racy check with elevated page count, to make sure PG_hwpoison
4131d3d348SWu Fengguang 	 * will only be set for the targeted owner (or on a free page).
4231d3d348SWu Fengguang 	 * We temporarily take page lock for try_get_mem_cgroup_from_page().
4331d3d348SWu Fengguang 	 * __memory_failure() will redo the check reliably inside page lock.
4431d3d348SWu Fengguang 	 */
4531d3d348SWu Fengguang 	lock_page(p);
4631d3d348SWu Fengguang 	err = hwpoison_filter(p);
4731d3d348SWu Fengguang 	unlock_page(p);
4831d3d348SWu Fengguang 	if (err)
4931d3d348SWu Fengguang 		return 0;
5031d3d348SWu Fengguang 
5131d3d348SWu Fengguang 	printk(KERN_INFO "Injecting memory failure at pfn %lx\n", pfn);
5231d3d348SWu Fengguang 	return __memory_failure(pfn, 18, MF_COUNT_INCREASED);
53cae681fcSAndi Kleen }
54cae681fcSAndi Kleen 
55847ce401SWu Fengguang static int hwpoison_unpoison(void *data, u64 val)
56847ce401SWu Fengguang {
57847ce401SWu Fengguang 	if (!capable(CAP_SYS_ADMIN))
58847ce401SWu Fengguang 		return -EPERM;
59847ce401SWu Fengguang 
60847ce401SWu Fengguang 	return unpoison_memory(val);
61847ce401SWu Fengguang }
62847ce401SWu Fengguang 
63cae681fcSAndi Kleen DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
64847ce401SWu Fengguang DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
65cae681fcSAndi Kleen 
66cae681fcSAndi Kleen static void pfn_inject_exit(void)
67cae681fcSAndi Kleen {
68cae681fcSAndi Kleen 	if (hwpoison_dir)
69cae681fcSAndi Kleen 		debugfs_remove_recursive(hwpoison_dir);
70cae681fcSAndi Kleen }
71cae681fcSAndi Kleen 
72cae681fcSAndi Kleen static int pfn_inject_init(void)
73cae681fcSAndi Kleen {
74847ce401SWu Fengguang 	struct dentry *dentry;
75847ce401SWu Fengguang 
76cae681fcSAndi Kleen 	hwpoison_dir = debugfs_create_dir("hwpoison", NULL);
77cae681fcSAndi Kleen 	if (hwpoison_dir == NULL)
78cae681fcSAndi Kleen 		return -ENOMEM;
79847ce401SWu Fengguang 
80847ce401SWu Fengguang 	/*
81847ce401SWu Fengguang 	 * Note that the below poison/unpoison interfaces do not involve
82847ce401SWu Fengguang 	 * hardware status change, hence do not require hardware support.
83847ce401SWu Fengguang 	 * They are mainly for testing hwpoison in software level.
84847ce401SWu Fengguang 	 */
85847ce401SWu Fengguang 	dentry = debugfs_create_file("corrupt-pfn", 0600, hwpoison_dir,
86cae681fcSAndi Kleen 					  NULL, &hwpoison_fops);
87847ce401SWu Fengguang 	if (!dentry)
88847ce401SWu Fengguang 		goto fail;
89847ce401SWu Fengguang 
90847ce401SWu Fengguang 	dentry = debugfs_create_file("unpoison-pfn", 0600, hwpoison_dir,
91847ce401SWu Fengguang 				     NULL, &unpoison_fops);
92847ce401SWu Fengguang 	if (!dentry)
93847ce401SWu Fengguang 		goto fail;
94847ce401SWu Fengguang 
957c116f2bSWu Fengguang 	dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600,
967c116f2bSWu Fengguang 				    hwpoison_dir, &hwpoison_filter_dev_major);
977c116f2bSWu Fengguang 	if (!dentry)
987c116f2bSWu Fengguang 		goto fail;
997c116f2bSWu Fengguang 
1007c116f2bSWu Fengguang 	dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600,
1017c116f2bSWu Fengguang 				    hwpoison_dir, &hwpoison_filter_dev_minor);
1027c116f2bSWu Fengguang 	if (!dentry)
1037c116f2bSWu Fengguang 		goto fail;
1047c116f2bSWu Fengguang 
105*478c5ffcSWu Fengguang 	dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600,
106*478c5ffcSWu Fengguang 				    hwpoison_dir, &hwpoison_filter_flags_mask);
107*478c5ffcSWu Fengguang 	if (!dentry)
108*478c5ffcSWu Fengguang 		goto fail;
109*478c5ffcSWu Fengguang 
110*478c5ffcSWu Fengguang 	dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600,
111*478c5ffcSWu Fengguang 				    hwpoison_dir, &hwpoison_filter_flags_value);
112*478c5ffcSWu Fengguang 	if (!dentry)
113*478c5ffcSWu Fengguang 		goto fail;
114*478c5ffcSWu Fengguang 
115847ce401SWu Fengguang 	return 0;
116847ce401SWu Fengguang fail:
117cae681fcSAndi Kleen 	pfn_inject_exit();
118cae681fcSAndi Kleen 	return -ENOMEM;
119cae681fcSAndi Kleen }
120cae681fcSAndi Kleen 
121cae681fcSAndi Kleen module_init(pfn_inject_init);
122cae681fcSAndi Kleen module_exit(pfn_inject_exit);
123cae681fcSAndi Kleen MODULE_LICENSE("GPL");
124