xref: /linux/mm/hwpoison-inject.c (revision a581865ecd0a5a0b8464d6f1e668ae6681c1572f)
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 
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 	/*
35*a581865eSMiaohe Lin 	 * This implies unable to support non-LRU pages except free page.
3631d3d348SWu Fengguang 	 */
37*a581865eSMiaohe 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);
51fd476720SNaoya Horiguchi 	return memory_failure(pfn, 0);
52cae681fcSAndi Kleen }
53cae681fcSAndi Kleen 
54847ce401SWu Fengguang static int hwpoison_unpoison(void *data, u64 val)
55847ce401SWu Fengguang {
56847ce401SWu Fengguang 	if (!capable(CAP_SYS_ADMIN))
57847ce401SWu Fengguang 		return -EPERM;
58847ce401SWu Fengguang 
59847ce401SWu Fengguang 	return unpoison_memory(val);
60847ce401SWu Fengguang }
61847ce401SWu Fengguang 
6235e3d566Szhong jiang DEFINE_DEBUGFS_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
6335e3d566Szhong jiang DEFINE_DEBUGFS_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
64cae681fcSAndi Kleen 
65cae681fcSAndi Kleen static void pfn_inject_exit(void)
66cae681fcSAndi Kleen {
67cae681fcSAndi Kleen 	debugfs_remove_recursive(hwpoison_dir);
68cae681fcSAndi Kleen }
69cae681fcSAndi Kleen 
70cae681fcSAndi Kleen static int pfn_inject_init(void)
71cae681fcSAndi Kleen {
72cae681fcSAndi Kleen 	hwpoison_dir = debugfs_create_dir("hwpoison", NULL);
73847ce401SWu Fengguang 
74847ce401SWu Fengguang 	/*
75847ce401SWu Fengguang 	 * Note that the below poison/unpoison interfaces do not involve
76847ce401SWu Fengguang 	 * hardware status change, hence do not require hardware support.
77847ce401SWu Fengguang 	 * They are mainly for testing hwpoison in software level.
78847ce401SWu Fengguang 	 */
792fcc6e20SGreg Kroah-Hartman 	debugfs_create_file("corrupt-pfn", 0200, hwpoison_dir, NULL,
802fcc6e20SGreg Kroah-Hartman 			    &hwpoison_fops);
81847ce401SWu Fengguang 
822fcc6e20SGreg Kroah-Hartman 	debugfs_create_file("unpoison-pfn", 0200, hwpoison_dir, NULL,
832fcc6e20SGreg Kroah-Hartman 			    &unpoison_fops);
84847ce401SWu Fengguang 
852fcc6e20SGreg Kroah-Hartman 	debugfs_create_u32("corrupt-filter-enable", 0600, hwpoison_dir,
862fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_enable);
871bfe5febSHaicheng Li 
882fcc6e20SGreg Kroah-Hartman 	debugfs_create_u32("corrupt-filter-dev-major", 0600, hwpoison_dir,
892fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_dev_major);
907c116f2bSWu Fengguang 
912fcc6e20SGreg Kroah-Hartman 	debugfs_create_u32("corrupt-filter-dev-minor", 0600, hwpoison_dir,
922fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_dev_minor);
937c116f2bSWu Fengguang 
942fcc6e20SGreg Kroah-Hartman 	debugfs_create_u64("corrupt-filter-flags-mask", 0600, hwpoison_dir,
952fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_flags_mask);
96478c5ffcSWu Fengguang 
972fcc6e20SGreg Kroah-Hartman 	debugfs_create_u64("corrupt-filter-flags-value", 0600, hwpoison_dir,
982fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_flags_value);
99478c5ffcSWu Fengguang 
10094a59fb3SVladimir Davydov #ifdef CONFIG_MEMCG
1012fcc6e20SGreg Kroah-Hartman 	debugfs_create_u64("corrupt-filter-memcg", 0600, hwpoison_dir,
1022fcc6e20SGreg Kroah-Hartman 			   &hwpoison_filter_memcg);
1034fd466ebSAndi Kleen #endif
1044fd466ebSAndi Kleen 
105847ce401SWu Fengguang 	return 0;
106cae681fcSAndi Kleen }
107cae681fcSAndi Kleen 
108cae681fcSAndi Kleen module_init(pfn_inject_init);
109cae681fcSAndi Kleen module_exit(pfn_inject_exit);
110cae681fcSAndi Kleen MODULE_LICENSE("GPL");
111