xref: /OK3568_Linux_fs/kernel/mm/hwpoison-inject.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /* Inject a hwpoison memory failure on a arbitrary pfn */
3*4882a593Smuzhiyun #include <linux/module.h>
4*4882a593Smuzhiyun #include <linux/debugfs.h>
5*4882a593Smuzhiyun #include <linux/kernel.h>
6*4882a593Smuzhiyun #include <linux/mm.h>
7*4882a593Smuzhiyun #include <linux/swap.h>
8*4882a593Smuzhiyun #include <linux/pagemap.h>
9*4882a593Smuzhiyun #include <linux/hugetlb.h>
10*4882a593Smuzhiyun #include "internal.h"
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun static struct dentry *hwpoison_dir;
13*4882a593Smuzhiyun 
hwpoison_inject(void * data,u64 val)14*4882a593Smuzhiyun static int hwpoison_inject(void *data, u64 val)
15*4882a593Smuzhiyun {
16*4882a593Smuzhiyun 	unsigned long pfn = val;
17*4882a593Smuzhiyun 	struct page *p;
18*4882a593Smuzhiyun 	struct page *hpage;
19*4882a593Smuzhiyun 	int err;
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun 	if (!capable(CAP_SYS_ADMIN))
22*4882a593Smuzhiyun 		return -EPERM;
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun 	if (!pfn_valid(pfn))
25*4882a593Smuzhiyun 		return -ENXIO;
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun 	p = pfn_to_page(pfn);
28*4882a593Smuzhiyun 	hpage = compound_head(p);
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	if (!hwpoison_filter_enable)
31*4882a593Smuzhiyun 		goto inject;
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 	shake_page(hpage, 0);
34*4882a593Smuzhiyun 	/*
35*4882a593Smuzhiyun 	 * This implies unable to support non-LRU pages.
36*4882a593Smuzhiyun 	 */
37*4882a593Smuzhiyun 	if (!PageLRU(hpage) && !PageHuge(p))
38*4882a593Smuzhiyun 		return 0;
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	/*
41*4882a593Smuzhiyun 	 * do a racy check to make sure PG_hwpoison will only be set for
42*4882a593Smuzhiyun 	 * the targeted owner (or on a free page).
43*4882a593Smuzhiyun 	 * memory_failure() will redo the check reliably inside page lock.
44*4882a593Smuzhiyun 	 */
45*4882a593Smuzhiyun 	err = hwpoison_filter(hpage);
46*4882a593Smuzhiyun 	if (err)
47*4882a593Smuzhiyun 		return 0;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun inject:
50*4882a593Smuzhiyun 	pr_info("Injecting memory failure at pfn %#lx\n", pfn);
51*4882a593Smuzhiyun 	return memory_failure(pfn, 0);
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun 
hwpoison_unpoison(void * data,u64 val)54*4882a593Smuzhiyun static int hwpoison_unpoison(void *data, u64 val)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun 	if (!capable(CAP_SYS_ADMIN))
57*4882a593Smuzhiyun 		return -EPERM;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	return unpoison_memory(val);
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun DEFINE_DEBUGFS_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
63*4882a593Smuzhiyun DEFINE_DEBUGFS_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
64*4882a593Smuzhiyun 
pfn_inject_exit(void)65*4882a593Smuzhiyun static void pfn_inject_exit(void)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun 	debugfs_remove_recursive(hwpoison_dir);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun 
pfn_inject_init(void)70*4882a593Smuzhiyun static int pfn_inject_init(void)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun 	hwpoison_dir = debugfs_create_dir("hwpoison", NULL);
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	/*
75*4882a593Smuzhiyun 	 * Note that the below poison/unpoison interfaces do not involve
76*4882a593Smuzhiyun 	 * hardware status change, hence do not require hardware support.
77*4882a593Smuzhiyun 	 * They are mainly for testing hwpoison in software level.
78*4882a593Smuzhiyun 	 */
79*4882a593Smuzhiyun 	debugfs_create_file("corrupt-pfn", 0200, hwpoison_dir, NULL,
80*4882a593Smuzhiyun 			    &hwpoison_fops);
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	debugfs_create_file("unpoison-pfn", 0200, hwpoison_dir, NULL,
83*4882a593Smuzhiyun 			    &unpoison_fops);
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	debugfs_create_u32("corrupt-filter-enable", 0600, hwpoison_dir,
86*4882a593Smuzhiyun 			   &hwpoison_filter_enable);
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	debugfs_create_u32("corrupt-filter-dev-major", 0600, hwpoison_dir,
89*4882a593Smuzhiyun 			   &hwpoison_filter_dev_major);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	debugfs_create_u32("corrupt-filter-dev-minor", 0600, hwpoison_dir,
92*4882a593Smuzhiyun 			   &hwpoison_filter_dev_minor);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	debugfs_create_u64("corrupt-filter-flags-mask", 0600, hwpoison_dir,
95*4882a593Smuzhiyun 			   &hwpoison_filter_flags_mask);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	debugfs_create_u64("corrupt-filter-flags-value", 0600, hwpoison_dir,
98*4882a593Smuzhiyun 			   &hwpoison_filter_flags_value);
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun #ifdef CONFIG_MEMCG
101*4882a593Smuzhiyun 	debugfs_create_u64("corrupt-filter-memcg", 0600, hwpoison_dir,
102*4882a593Smuzhiyun 			   &hwpoison_filter_memcg);
103*4882a593Smuzhiyun #endif
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	return 0;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun module_init(pfn_inject_init);
109*4882a593Smuzhiyun module_exit(pfn_inject_exit);
110*4882a593Smuzhiyun MODULE_LICENSE("GPL");
111