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 /* 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); 51*d1fe111fSluofei err = memory_failure(pfn, 0); 52*d1fe111fSluofei return (err == -EOPNOTSUPP) ? 0 : err; 53cae681fcSAndi Kleen } 54cae681fcSAndi Kleen 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 66cae681fcSAndi Kleen static void pfn_inject_exit(void) 67cae681fcSAndi Kleen { 68cae681fcSAndi Kleen debugfs_remove_recursive(hwpoison_dir); 69cae681fcSAndi Kleen } 70cae681fcSAndi Kleen 71cae681fcSAndi Kleen static int pfn_inject_init(void) 72cae681fcSAndi Kleen { 73cae681fcSAndi Kleen hwpoison_dir = debugfs_create_dir("hwpoison", NULL); 74847ce401SWu Fengguang 75847ce401SWu Fengguang /* 76847ce401SWu Fengguang * Note that the below poison/unpoison interfaces do not involve 77847ce401SWu Fengguang * hardware status change, hence do not require hardware support. 78847ce401SWu Fengguang * They are mainly for testing hwpoison in software level. 79847ce401SWu Fengguang */ 802fcc6e20SGreg Kroah-Hartman debugfs_create_file("corrupt-pfn", 0200, hwpoison_dir, NULL, 812fcc6e20SGreg Kroah-Hartman &hwpoison_fops); 82847ce401SWu Fengguang 832fcc6e20SGreg Kroah-Hartman debugfs_create_file("unpoison-pfn", 0200, hwpoison_dir, NULL, 842fcc6e20SGreg Kroah-Hartman &unpoison_fops); 85847ce401SWu Fengguang 862fcc6e20SGreg Kroah-Hartman debugfs_create_u32("corrupt-filter-enable", 0600, hwpoison_dir, 872fcc6e20SGreg Kroah-Hartman &hwpoison_filter_enable); 881bfe5febSHaicheng Li 892fcc6e20SGreg Kroah-Hartman debugfs_create_u32("corrupt-filter-dev-major", 0600, hwpoison_dir, 902fcc6e20SGreg Kroah-Hartman &hwpoison_filter_dev_major); 917c116f2bSWu Fengguang 922fcc6e20SGreg Kroah-Hartman debugfs_create_u32("corrupt-filter-dev-minor", 0600, hwpoison_dir, 932fcc6e20SGreg Kroah-Hartman &hwpoison_filter_dev_minor); 947c116f2bSWu Fengguang 952fcc6e20SGreg Kroah-Hartman debugfs_create_u64("corrupt-filter-flags-mask", 0600, hwpoison_dir, 962fcc6e20SGreg Kroah-Hartman &hwpoison_filter_flags_mask); 97478c5ffcSWu Fengguang 982fcc6e20SGreg Kroah-Hartman debugfs_create_u64("corrupt-filter-flags-value", 0600, hwpoison_dir, 992fcc6e20SGreg Kroah-Hartman &hwpoison_filter_flags_value); 100478c5ffcSWu Fengguang 10194a59fb3SVladimir Davydov #ifdef CONFIG_MEMCG 1022fcc6e20SGreg Kroah-Hartman debugfs_create_u64("corrupt-filter-memcg", 0600, hwpoison_dir, 1032fcc6e20SGreg Kroah-Hartman &hwpoison_filter_memcg); 1044fd466ebSAndi Kleen #endif 1054fd466ebSAndi Kleen 106847ce401SWu Fengguang return 0; 107cae681fcSAndi Kleen } 108cae681fcSAndi Kleen 109cae681fcSAndi Kleen module_init(pfn_inject_init); 110cae681fcSAndi Kleen module_exit(pfn_inject_exit); 111cae681fcSAndi Kleen MODULE_LICENSE("GPL"); 112