xref: /linux/mm/hwpoison-inject.c (revision 7ea434a4eb49db83d17cc076f2267704c52938ae)
125985edcSLucas De Marchi /* Inject a hwpoison memory failure on a arbitrary 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>
843131e14SNaoya Horiguchi #include <linux/hugetlb.h>
97c116f2bSWu Fengguang #include "internal.h"
10cae681fcSAndi Kleen 
11847ce401SWu Fengguang static struct dentry *hwpoison_dir;
12cae681fcSAndi Kleen 
13cae681fcSAndi Kleen static int hwpoison_inject(void *data, u64 val)
14cae681fcSAndi Kleen {
1531d3d348SWu Fengguang 	unsigned long pfn = val;
1631d3d348SWu Fengguang 	struct page *p;
1743131e14SNaoya Horiguchi 	struct page *hpage;
1831d3d348SWu Fengguang 	int err;
1931d3d348SWu Fengguang 
20cae681fcSAndi Kleen 	if (!capable(CAP_SYS_ADMIN))
21cae681fcSAndi Kleen 		return -EPERM;
2231d3d348SWu Fengguang 
2331d3d348SWu Fengguang 	if (!pfn_valid(pfn))
2431d3d348SWu Fengguang 		return -ENXIO;
2531d3d348SWu Fengguang 
2631d3d348SWu Fengguang 	p = pfn_to_page(pfn);
2743131e14SNaoya Horiguchi 	hpage = compound_head(p);
2831d3d348SWu Fengguang 	/*
2931d3d348SWu Fengguang 	 * This implies unable to support free buddy pages.
3031d3d348SWu Fengguang 	 */
3143131e14SNaoya Horiguchi 	if (!get_page_unless_zero(hpage))
3231d3d348SWu Fengguang 		return 0;
3331d3d348SWu Fengguang 
34fb31ba30SWanpeng Li 	if (!hwpoison_filter_enable)
35fb31ba30SWanpeng Li 		goto inject;
36fb31ba30SWanpeng Li 
3743131e14SNaoya Horiguchi 	if (!PageLRU(p) && !PageHuge(p))
38facb6011SAndi Kleen 		shake_page(p, 0);
3931d3d348SWu Fengguang 	/*
4031d3d348SWu Fengguang 	 * This implies unable to support non-LRU pages.
4131d3d348SWu Fengguang 	 */
4243131e14SNaoya Horiguchi 	if (!PageLRU(p) && !PageHuge(p))
43*7ea434a4SNaoya Horiguchi 		goto put_out;
4431d3d348SWu Fengguang 
4531d3d348SWu Fengguang 	/*
4631d3d348SWu Fengguang 	 * do a racy check with elevated page count, to make sure PG_hwpoison
4731d3d348SWu Fengguang 	 * will only be set for the targeted owner (or on a free page).
4831d3d348SWu Fengguang 	 * We temporarily take page lock for try_get_mem_cgroup_from_page().
49cd42f4a3STony Luck 	 * memory_failure() will redo the check reliably inside page lock.
5031d3d348SWu Fengguang 	 */
5143131e14SNaoya Horiguchi 	lock_page(hpage);
5243131e14SNaoya Horiguchi 	err = hwpoison_filter(hpage);
5343131e14SNaoya Horiguchi 	unlock_page(hpage);
5431d3d348SWu Fengguang 	if (err)
55*7ea434a4SNaoya Horiguchi 		goto put_out;
5631d3d348SWu Fengguang 
570d57eb8dSAndi Kleen inject:
584883e997SWanpeng Li 	pr_info("Injecting memory failure at pfn %#lx\n", pfn);
59cd42f4a3STony Luck 	return memory_failure(pfn, 18, MF_COUNT_INCREASED);
60*7ea434a4SNaoya Horiguchi put_out:
61*7ea434a4SNaoya Horiguchi 	put_page(hpage);
62*7ea434a4SNaoya Horiguchi 	return 0;
63cae681fcSAndi Kleen }
64cae681fcSAndi Kleen 
65847ce401SWu Fengguang static int hwpoison_unpoison(void *data, u64 val)
66847ce401SWu Fengguang {
67847ce401SWu Fengguang 	if (!capable(CAP_SYS_ADMIN))
68847ce401SWu Fengguang 		return -EPERM;
69847ce401SWu Fengguang 
70847ce401SWu Fengguang 	return unpoison_memory(val);
71847ce401SWu Fengguang }
72847ce401SWu Fengguang 
73cae681fcSAndi Kleen DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
74847ce401SWu Fengguang DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
75cae681fcSAndi Kleen 
76cae681fcSAndi Kleen static void pfn_inject_exit(void)
77cae681fcSAndi Kleen {
78cae681fcSAndi Kleen 	debugfs_remove_recursive(hwpoison_dir);
79cae681fcSAndi Kleen }
80cae681fcSAndi Kleen 
81cae681fcSAndi Kleen static int pfn_inject_init(void)
82cae681fcSAndi Kleen {
83847ce401SWu Fengguang 	struct dentry *dentry;
84847ce401SWu Fengguang 
85cae681fcSAndi Kleen 	hwpoison_dir = debugfs_create_dir("hwpoison", NULL);
86cae681fcSAndi Kleen 	if (hwpoison_dir == NULL)
87cae681fcSAndi Kleen 		return -ENOMEM;
88847ce401SWu Fengguang 
89847ce401SWu Fengguang 	/*
90847ce401SWu Fengguang 	 * Note that the below poison/unpoison interfaces do not involve
91847ce401SWu Fengguang 	 * hardware status change, hence do not require hardware support.
92847ce401SWu Fengguang 	 * They are mainly for testing hwpoison in software level.
93847ce401SWu Fengguang 	 */
942d1e8b3fSWanpeng Li 	dentry = debugfs_create_file("corrupt-pfn", 0200, hwpoison_dir,
95cae681fcSAndi Kleen 					  NULL, &hwpoison_fops);
96847ce401SWu Fengguang 	if (!dentry)
97847ce401SWu Fengguang 		goto fail;
98847ce401SWu Fengguang 
992d1e8b3fSWanpeng Li 	dentry = debugfs_create_file("unpoison-pfn", 0200, hwpoison_dir,
100847ce401SWu Fengguang 				     NULL, &unpoison_fops);
101847ce401SWu Fengguang 	if (!dentry)
102847ce401SWu Fengguang 		goto fail;
103847ce401SWu Fengguang 
1041bfe5febSHaicheng Li 	dentry = debugfs_create_u32("corrupt-filter-enable", 0600,
1051bfe5febSHaicheng Li 				    hwpoison_dir, &hwpoison_filter_enable);
1061bfe5febSHaicheng Li 	if (!dentry)
1071bfe5febSHaicheng Li 		goto fail;
1081bfe5febSHaicheng Li 
1097c116f2bSWu Fengguang 	dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600,
1107c116f2bSWu Fengguang 				    hwpoison_dir, &hwpoison_filter_dev_major);
1117c116f2bSWu Fengguang 	if (!dentry)
1127c116f2bSWu Fengguang 		goto fail;
1137c116f2bSWu Fengguang 
1147c116f2bSWu Fengguang 	dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600,
1157c116f2bSWu Fengguang 				    hwpoison_dir, &hwpoison_filter_dev_minor);
1167c116f2bSWu Fengguang 	if (!dentry)
1177c116f2bSWu Fengguang 		goto fail;
1187c116f2bSWu Fengguang 
119478c5ffcSWu Fengguang 	dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600,
120478c5ffcSWu Fengguang 				    hwpoison_dir, &hwpoison_filter_flags_mask);
121478c5ffcSWu Fengguang 	if (!dentry)
122478c5ffcSWu Fengguang 		goto fail;
123478c5ffcSWu Fengguang 
124478c5ffcSWu Fengguang 	dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600,
125478c5ffcSWu Fengguang 				    hwpoison_dir, &hwpoison_filter_flags_value);
126478c5ffcSWu Fengguang 	if (!dentry)
127478c5ffcSWu Fengguang 		goto fail;
128478c5ffcSWu Fengguang 
129c255a458SAndrew Morton #ifdef CONFIG_MEMCG_SWAP
1304fd466ebSAndi Kleen 	dentry = debugfs_create_u64("corrupt-filter-memcg", 0600,
1314fd466ebSAndi Kleen 				    hwpoison_dir, &hwpoison_filter_memcg);
1324fd466ebSAndi Kleen 	if (!dentry)
1334fd466ebSAndi Kleen 		goto fail;
1344fd466ebSAndi Kleen #endif
1354fd466ebSAndi Kleen 
136847ce401SWu Fengguang 	return 0;
137847ce401SWu Fengguang fail:
138cae681fcSAndi Kleen 	pfn_inject_exit();
139cae681fcSAndi Kleen 	return -ENOMEM;
140cae681fcSAndi Kleen }
141cae681fcSAndi Kleen 
142cae681fcSAndi Kleen module_init(pfn_inject_init);
143cae681fcSAndi Kleen module_exit(pfn_inject_exit);
144cae681fcSAndi Kleen MODULE_LICENSE("GPL");
145