Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/mm/hwpoison-inject.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/* Inject a hwpoison memory failure on a arbitrary pfn */
3
#include <linux/module.h>
4
#include <linux/debugfs.h>
5
#include <linux/kernel.h>
6
#include <linux/mm.h>
7
#include <linux/swap.h>
8
#include <linux/pagemap.h>
9
#include <linux/hugetlb.h>
10
#include <linux/page-flags.h>
11
#include <linux/memcontrol.h>
12
#include "internal.h"
13
14
static u32 hwpoison_filter_enable;
15
static u32 hwpoison_filter_dev_major = ~0U;
16
static u32 hwpoison_filter_dev_minor = ~0U;
17
static u64 hwpoison_filter_flags_mask;
18
static u64 hwpoison_filter_flags_value;
19
20
static int hwpoison_filter_dev(struct page *p)
21
{
22
struct folio *folio = page_folio(p);
23
struct address_space *mapping;
24
dev_t dev;
25
26
if (hwpoison_filter_dev_major == ~0U &&
27
hwpoison_filter_dev_minor == ~0U)
28
return 0;
29
30
mapping = folio_mapping(folio);
31
if (mapping == NULL || mapping->host == NULL)
32
return -EINVAL;
33
34
dev = mapping->host->i_sb->s_dev;
35
if (hwpoison_filter_dev_major != ~0U &&
36
hwpoison_filter_dev_major != MAJOR(dev))
37
return -EINVAL;
38
if (hwpoison_filter_dev_minor != ~0U &&
39
hwpoison_filter_dev_minor != MINOR(dev))
40
return -EINVAL;
41
42
return 0;
43
}
44
45
static int hwpoison_filter_flags(struct page *p)
46
{
47
if (!hwpoison_filter_flags_mask)
48
return 0;
49
50
if ((stable_page_flags(p) & hwpoison_filter_flags_mask) ==
51
hwpoison_filter_flags_value)
52
return 0;
53
else
54
return -EINVAL;
55
}
56
57
/*
58
* This allows stress tests to limit test scope to a collection of tasks
59
* by putting them under some memcg. This prevents killing unrelated/important
60
* processes such as /sbin/init. Note that the target task may share clean
61
* pages with init (eg. libc text), which is harmless. If the target task
62
* share _dirty_ pages with another task B, the test scheme must make sure B
63
* is also included in the memcg. At last, due to race conditions this filter
64
* can only guarantee that the page either belongs to the memcg tasks, or is
65
* a freed page.
66
*/
67
#ifdef CONFIG_MEMCG
68
static u64 hwpoison_filter_memcg;
69
static int hwpoison_filter_task(struct page *p)
70
{
71
if (!hwpoison_filter_memcg)
72
return 0;
73
74
if (page_cgroup_ino(p) != hwpoison_filter_memcg)
75
return -EINVAL;
76
77
return 0;
78
}
79
#else
80
static int hwpoison_filter_task(struct page *p) { return 0; }
81
#endif
82
83
static int hwpoison_filter(struct page *p)
84
{
85
if (!hwpoison_filter_enable)
86
return 0;
87
88
if (hwpoison_filter_dev(p))
89
return -EINVAL;
90
91
if (hwpoison_filter_flags(p))
92
return -EINVAL;
93
94
if (hwpoison_filter_task(p))
95
return -EINVAL;
96
97
return 0;
98
}
99
100
static struct dentry *hwpoison_dir;
101
102
static int hwpoison_inject(void *data, u64 val)
103
{
104
unsigned long pfn = val;
105
struct page *p;
106
struct folio *folio;
107
int err;
108
109
if (!capable(CAP_SYS_ADMIN))
110
return -EPERM;
111
112
if (!pfn_valid(pfn))
113
return -ENXIO;
114
115
p = pfn_to_page(pfn);
116
folio = page_folio(p);
117
118
if (!hwpoison_filter_enable)
119
goto inject;
120
121
shake_folio(folio);
122
/*
123
* This implies unable to support non-LRU pages except free page.
124
*/
125
if (!folio_test_lru(folio) && !folio_test_hugetlb(folio) &&
126
!is_free_buddy_page(p))
127
return 0;
128
129
/*
130
* do a racy check to make sure PG_hwpoison will only be set for
131
* the targeted owner (or on a free page).
132
* memory_failure() will redo the check reliably inside page lock.
133
*/
134
err = hwpoison_filter(&folio->page);
135
if (err)
136
return 0;
137
138
inject:
139
pr_info("Injecting memory failure at pfn %#lx\n", pfn);
140
err = memory_failure(pfn, MF_SW_SIMULATED);
141
return (err == -EOPNOTSUPP) ? 0 : err;
142
}
143
144
static int hwpoison_unpoison(void *data, u64 val)
145
{
146
if (!capable(CAP_SYS_ADMIN))
147
return -EPERM;
148
149
return unpoison_memory(val);
150
}
151
152
DEFINE_DEBUGFS_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
153
DEFINE_DEBUGFS_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
154
155
static void __exit pfn_inject_exit(void)
156
{
157
hwpoison_filter_enable = 0;
158
hwpoison_filter_unregister();
159
debugfs_remove_recursive(hwpoison_dir);
160
}
161
162
static int __init pfn_inject_init(void)
163
{
164
hwpoison_dir = debugfs_create_dir("hwpoison", NULL);
165
166
/*
167
* Note that the below poison/unpoison interfaces do not involve
168
* hardware status change, hence do not require hardware support.
169
* They are mainly for testing hwpoison in software level.
170
*/
171
debugfs_create_file("corrupt-pfn", 0200, hwpoison_dir, NULL,
172
&hwpoison_fops);
173
174
debugfs_create_file("unpoison-pfn", 0200, hwpoison_dir, NULL,
175
&unpoison_fops);
176
177
debugfs_create_u32("corrupt-filter-enable", 0600, hwpoison_dir,
178
&hwpoison_filter_enable);
179
180
debugfs_create_u32("corrupt-filter-dev-major", 0600, hwpoison_dir,
181
&hwpoison_filter_dev_major);
182
183
debugfs_create_u32("corrupt-filter-dev-minor", 0600, hwpoison_dir,
184
&hwpoison_filter_dev_minor);
185
186
debugfs_create_u64("corrupt-filter-flags-mask", 0600, hwpoison_dir,
187
&hwpoison_filter_flags_mask);
188
189
debugfs_create_u64("corrupt-filter-flags-value", 0600, hwpoison_dir,
190
&hwpoison_filter_flags_value);
191
192
#ifdef CONFIG_MEMCG
193
debugfs_create_u64("corrupt-filter-memcg", 0600, hwpoison_dir,
194
&hwpoison_filter_memcg);
195
#endif
196
197
hwpoison_filter_register(hwpoison_filter);
198
199
return 0;
200
}
201
202
module_init(pfn_inject_init);
203
module_exit(pfn_inject_exit);
204
MODULE_DESCRIPTION("HWPoison pages injector");
205
MODULE_LICENSE("GPL");
206
207