File Index
Files and directories
Sign in
FILE

hwpoison-inject.c

File preview with syntax highlighting.

linux-6.19.14/mm/hwpoison-inject.c C
  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
 14static u32 hwpoison_filter_enable;
 15static u32 hwpoison_filter_dev_major = ~0U;
 16static u32 hwpoison_filter_dev_minor = ~0U;
 17static u64 hwpoison_filter_flags_mask;
 18static u64 hwpoison_filter_flags_value;
 19
 20static 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
 45static 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
 68static u64 hwpoison_filter_memcg;
 69static 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
 80static int hwpoison_filter_task(struct page *p) { return 0; }
 81#endif
 82
 83static 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
100static struct dentry *hwpoison_dir;
101
102static 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
138inject:
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
144static 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
152DEFINE_DEBUGFS_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
153DEFINE_DEBUGFS_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
154
155static void __exit pfn_inject_exit(void)
156{
157	hwpoison_filter_enable = 0;
158	hwpoison_filter_unregister();
159	debugfs_remove_recursive(hwpoison_dir);
160}
161
162static 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
202module_init(pfn_inject_init);
203module_exit(pfn_inject_exit);
204MODULE_DESCRIPTION("HWPoison pages injector");
205MODULE_LICENSE("GPL");
~/ Read-only file preview.