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