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