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