xref: /linux/mm/hwpoison-inject.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
225985edcSLucas De Marchi /* Inject a hwpoison memory failure on a arbitrary pfn */
3cae681fcSAndi Kleen #include <linux/module.h>
4cae681fcSAndi Kleen #include <linux/debugfs.h>
5cae681fcSAndi Kleen #include <linux/kernel.h>
6cae681fcSAndi Kleen #include <linux/mm.h>
731d3d348SWu Fengguang #include <linux/swap.h>
831d3d348SWu Fengguang #include <linux/pagemap.h>
943131e14SNaoya Horiguchi #include <linux/hugetlb.h>
107c116f2bSWu Fengguang #include "internal.h"
11cae681fcSAndi Kleen 
12847ce401SWu Fengguang static struct dentry *hwpoison_dir;
13cae681fcSAndi Kleen 
hwpoison_inject(void * data,u64 val)14cae681fcSAndi Kleen static int hwpoison_inject(void *data, u64 val)
15cae681fcSAndi Kleen {
1631d3d348SWu Fengguang 	unsigned long pfn = val;
1731d3d348SWu Fengguang 	struct page *p;
1843131e14SNaoya Horiguchi 	struct page *hpage;
1931d3d348SWu Fengguang 	int err;
2031d3d348SWu Fengguang 
21cae681fcSAndi Kleen 	if (!capable(CAP_SYS_ADMIN))
22cae681fcSAndi Kleen 		return -EPERM;
2331d3d348SWu Fengguang 
2431d3d348SWu Fengguang 	if (!pfn_valid(pfn))
2531d3d348SWu Fengguang 		return -ENXIO;
2631d3d348SWu Fengguang 
2731d3d348SWu Fengguang 	p = pfn_to_page(pfn);
2843131e14SNaoya Horiguchi 	hpage = compound_head(p);
2931d3d348SWu Fengguang 
30fb31ba30SWanpeng Li 	if (!hwpoison_filter_enable)
31fb31ba30SWanpeng Li 		goto inject;
32fb31ba30SWanpeng Li 
33d0505e9fSYang Shi 	shake_page(hpage);
3431d3d348SWu Fengguang 	/*
35a581865eSMiaohe Lin 	 * This implies unable to support non-LRU pages except free page.
3631d3d348SWu Fengguang 	 */
37a581865eSMiaohe Lin 	if (!PageLRU(hpage) && !PageHuge(p) && !is_free_buddy_page(p))
38fd476720SNaoya Horiguchi 		return 0;
3931d3d348SWu Fengguang 
4031d3d348SWu Fengguang 	/*
41fd476720SNaoya Horiguchi 	 * do a racy check to make sure PG_hwpoison will only be set for
42fd476720SNaoya Horiguchi 	 * the targeted owner (or on a free page).
43cd42f4a3STony Luck 	 * memory_failure() will redo the check reliably inside page lock.
4431d3d348SWu Fengguang 	 */
4543131e14SNaoya Horiguchi 	err = hwpoison_filter(hpage);
4631d3d348SWu Fengguang 	if (err)
47fd476720SNaoya Horiguchi 		return 0;
4831d3d348SWu Fengguang 
490d57eb8dSAndi Kleen inject:
504883e997SWanpeng Li 	pr_info("Injecting memory failure at pfn %#lx\n", pfn);
5167f22ba7Szhenwei pi 	err = memory_failure(pfn, MF_SW_SIMULATED);
52d1fe111fSluofei 	return (err == -EOPNOTSUPP) ? 0 : err;
53cae681fcSAndi Kleen }
54cae681fcSAndi Kleen 
hwpoison_unpoison(void * data,u64 val)55847ce401SWu Fengguang static int hwpoison_unpoison(void *data, u64 val)
56847ce401SWu Fengguang {
57847ce401SWu Fengguang 	if (!capable(CAP_SYS_ADMIN))
58847ce401SWu Fengguang 		return -EPERM;
59847ce401SWu Fengguang 
60847ce401SWu Fengguang 	return unpoison_memory(val);
61847ce401SWu Fengguang }
62847ce401SWu Fengguang 
6335e3d566Szhong jiang DEFINE_DEBUGFS_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
6435e3d566Szhong jiang DEFINE_DEBUGFS_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
65cae681fcSAndi Kleen 
pfn_inject_exit(void)66*4e07acddSXiu Jianfeng static void __exit pfn_inject_exit(void)
67cae681fcSAndi Kleen {
68f0696cb4Szhenwei pi 	hwpoison_filter_enable = 0;
69cae681fcSAndi Kleen 	debugfs_remove_recursive(hwpoison_dir);
70cae681fcSAndi Kleen }
71cae681fcSAndi Kleen 
pfn_inject_init(void)72*4e07acddSXiu Jianfeng static int __init pfn_inject_init(void)
73cae681fcSAndi Kleen {
74cae681fcSAndi Kleen 	hwpoison_dir = debugfs_create_dir("hwpoison", NULL);
75847ce401SWu Fengguang 
76847ce401SWu Fengguang 	/*
77847ce401SWu Fengguang 	 * Note that the below poison/unpoison interfaces do not involve
78847ce401SWu Fengguang 	 * hardware status change, hence do not require hardware support.
79847ce401SWu Fengguang 	 * They are mainly for testing hwpoison in software level.
80847ce401SWu Fengguang 	 */
812fcc6e20SGreg Kroah-Hartman 	debugfs_create_file("corrupt-pfn", 0200, hwpoison_dir, NULL,
822fcc6e20SGreg Kroah-Hartman 			    &hwpoison_fops);
83847ce401SWu Fengguang 
842fcc6e20SGreg Kroah-Hartman 	debugfs_create_file("unpoison-pfn", 0200, hwpoison_dir, NULL,
852fcc6e20SGreg Kroah-Hartman 			    &unpoison_fops);
86847ce401SWu Fengguang 
872fcc6e20SGreg Kroah-Hartman 	debugfs_create_u32("corrupt-filter-enable", 0600, hwpoison_dir,
882fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_enable);
891bfe5febSHaicheng Li 
902fcc6e20SGreg Kroah-Hartman 	debugfs_create_u32("corrupt-filter-dev-major", 0600, hwpoison_dir,
912fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_dev_major);
927c116f2bSWu Fengguang 
932fcc6e20SGreg Kroah-Hartman 	debugfs_create_u32("corrupt-filter-dev-minor", 0600, hwpoison_dir,
942fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_dev_minor);
957c116f2bSWu Fengguang 
962fcc6e20SGreg Kroah-Hartman 	debugfs_create_u64("corrupt-filter-flags-mask", 0600, hwpoison_dir,
972fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_flags_mask);
98478c5ffcSWu Fengguang 
992fcc6e20SGreg Kroah-Hartman 	debugfs_create_u64("corrupt-filter-flags-value", 0600, hwpoison_dir,
1002fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_flags_value);
101478c5ffcSWu Fengguang 
10294a59fb3SVladimir Davydov #ifdef CONFIG_MEMCG
1032fcc6e20SGreg Kroah-Hartman 	debugfs_create_u64("corrupt-filter-memcg", 0600, hwpoison_dir,
1042fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_memcg);
1054fd466ebSAndi Kleen #endif
1064fd466ebSAndi Kleen 
107847ce401SWu Fengguang 	return 0;
108cae681fcSAndi Kleen }
109cae681fcSAndi Kleen 
110cae681fcSAndi Kleen module_init(pfn_inject_init);
111cae681fcSAndi Kleen module_exit(pfn_inject_exit);
112cae681fcSAndi Kleen MODULE_LICENSE("GPL");
113