hwpoison-inject.c revision 43131e141abdb44c487cf79af3ef1fe5164dcef9
1/* Inject a hwpoison memory failure on a arbitary pfn */
2#include <linux/module.h>
3#include <linux/debugfs.h>
4#include <linux/kernel.h>
5#include <linux/mm.h>
6#include <linux/swap.h>
7#include <linux/pagemap.h>
8#include <linux/hugetlb.h>
9#include "internal.h"
10
11static struct dentry *hwpoison_dir;
12
13static int hwpoison_inject(void *data, u64 val)
14{
15	unsigned long pfn = val;
16	struct page *p;
17	struct page *hpage;
18	int err;
19
20	if (!capable(CAP_SYS_ADMIN))
21		return -EPERM;
22
23	if (!hwpoison_filter_enable)
24		goto inject;
25	if (!pfn_valid(pfn))
26		return -ENXIO;
27
28	p = pfn_to_page(pfn);
29	hpage = compound_head(p);
30	/*
31	 * This implies unable to support free buddy pages.
32	 */
33	if (!get_page_unless_zero(hpage))
34		return 0;
35
36	if (!PageLRU(p) && !PageHuge(p))
37		shake_page(p, 0);
38	/*
39	 * This implies unable to support non-LRU pages.
40	 */
41	if (!PageLRU(p) && !PageHuge(p))
42		return 0;
43
44	/*
45	 * do a racy check with elevated page count, to make sure PG_hwpoison
46	 * will only be set for the targeted owner (or on a free page).
47	 * We temporarily take page lock for try_get_mem_cgroup_from_page().
48	 * __memory_failure() will redo the check reliably inside page lock.
49	 */
50	lock_page(hpage);
51	err = hwpoison_filter(hpage);
52	unlock_page(hpage);
53	if (err)
54		return 0;
55
56inject:
57	printk(KERN_INFO "Injecting memory failure at pfn %lx\n", pfn);
58	return __memory_failure(pfn, 18, MF_COUNT_INCREASED);
59}
60
61static int hwpoison_unpoison(void *data, u64 val)
62{
63	if (!capable(CAP_SYS_ADMIN))
64		return -EPERM;
65
66	return unpoison_memory(val);
67}
68
69DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
70DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
71
72static void pfn_inject_exit(void)
73{
74	if (hwpoison_dir)
75		debugfs_remove_recursive(hwpoison_dir);
76}
77
78static int pfn_inject_init(void)
79{
80	struct dentry *dentry;
81
82	hwpoison_dir = debugfs_create_dir("hwpoison", NULL);
83	if (hwpoison_dir == NULL)
84		return -ENOMEM;
85
86	/*
87	 * Note that the below poison/unpoison interfaces do not involve
88	 * hardware status change, hence do not require hardware support.
89	 * They are mainly for testing hwpoison in software level.
90	 */
91	dentry = debugfs_create_file("corrupt-pfn", 0600, hwpoison_dir,
92					  NULL, &hwpoison_fops);
93	if (!dentry)
94		goto fail;
95
96	dentry = debugfs_create_file("unpoison-pfn", 0600, hwpoison_dir,
97				     NULL, &unpoison_fops);
98	if (!dentry)
99		goto fail;
100
101	dentry = debugfs_create_u32("corrupt-filter-enable", 0600,
102				    hwpoison_dir, &hwpoison_filter_enable);
103	if (!dentry)
104		goto fail;
105
106	dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600,
107				    hwpoison_dir, &hwpoison_filter_dev_major);
108	if (!dentry)
109		goto fail;
110
111	dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600,
112				    hwpoison_dir, &hwpoison_filter_dev_minor);
113	if (!dentry)
114		goto fail;
115
116	dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600,
117				    hwpoison_dir, &hwpoison_filter_flags_mask);
118	if (!dentry)
119		goto fail;
120
121	dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600,
122				    hwpoison_dir, &hwpoison_filter_flags_value);
123	if (!dentry)
124		goto fail;
125
126#ifdef	CONFIG_CGROUP_MEM_RES_CTLR_SWAP
127	dentry = debugfs_create_u64("corrupt-filter-memcg", 0600,
128				    hwpoison_dir, &hwpoison_filter_memcg);
129	if (!dentry)
130		goto fail;
131#endif
132
133	return 0;
134fail:
135	pfn_inject_exit();
136	return -ENOMEM;
137}
138
139module_init(pfn_inject_init);
140module_exit(pfn_inject_exit);
141MODULE_LICENSE("GPL");
142