1cae681fcSAndi Kleen /* Inject a hwpoison memory failure on a arbitary pfn */ 2cae681fcSAndi Kleen #include <linux/module.h> 3cae681fcSAndi Kleen #include <linux/debugfs.h> 4cae681fcSAndi Kleen #include <linux/kernel.h> 5cae681fcSAndi Kleen #include <linux/mm.h> 631d3d348SWu Fengguang #include <linux/swap.h> 731d3d348SWu Fengguang #include <linux/pagemap.h> 87c116f2bSWu Fengguang #include "internal.h" 9cae681fcSAndi Kleen 10847ce401SWu Fengguang static struct dentry *hwpoison_dir; 11cae681fcSAndi Kleen 12cae681fcSAndi Kleen static int hwpoison_inject(void *data, u64 val) 13cae681fcSAndi Kleen { 1431d3d348SWu Fengguang unsigned long pfn = val; 1531d3d348SWu Fengguang struct page *p; 1631d3d348SWu Fengguang int err; 1731d3d348SWu Fengguang 18cae681fcSAndi Kleen if (!capable(CAP_SYS_ADMIN)) 19cae681fcSAndi Kleen return -EPERM; 2031d3d348SWu Fengguang 2131d3d348SWu Fengguang if (!pfn_valid(pfn)) 2231d3d348SWu Fengguang return -ENXIO; 2331d3d348SWu Fengguang 2431d3d348SWu Fengguang p = pfn_to_page(pfn); 2531d3d348SWu Fengguang /* 2631d3d348SWu Fengguang * This implies unable to support free buddy pages. 2731d3d348SWu Fengguang */ 2831d3d348SWu Fengguang if (!get_page_unless_zero(p)) 2931d3d348SWu Fengguang return 0; 3031d3d348SWu Fengguang 3131d3d348SWu Fengguang if (!PageLRU(p)) 32*facb6011SAndi Kleen shake_page(p, 0); 3331d3d348SWu Fengguang /* 3431d3d348SWu Fengguang * This implies unable to support non-LRU pages. 3531d3d348SWu Fengguang */ 3631d3d348SWu Fengguang if (!PageLRU(p)) 3731d3d348SWu Fengguang return 0; 3831d3d348SWu Fengguang 3931d3d348SWu Fengguang /* 4031d3d348SWu Fengguang * do a racy check with elevated page count, to make sure PG_hwpoison 4131d3d348SWu Fengguang * will only be set for the targeted owner (or on a free page). 4231d3d348SWu Fengguang * We temporarily take page lock for try_get_mem_cgroup_from_page(). 4331d3d348SWu Fengguang * __memory_failure() will redo the check reliably inside page lock. 4431d3d348SWu Fengguang */ 4531d3d348SWu Fengguang lock_page(p); 4631d3d348SWu Fengguang err = hwpoison_filter(p); 4731d3d348SWu Fengguang unlock_page(p); 4831d3d348SWu Fengguang if (err) 4931d3d348SWu Fengguang return 0; 5031d3d348SWu Fengguang 5131d3d348SWu Fengguang printk(KERN_INFO "Injecting memory failure at pfn %lx\n", pfn); 5231d3d348SWu Fengguang return __memory_failure(pfn, 18, MF_COUNT_INCREASED); 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 63cae681fcSAndi Kleen DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n"); 64847ce401SWu Fengguang DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n"); 65cae681fcSAndi Kleen 66cae681fcSAndi Kleen static void pfn_inject_exit(void) 67cae681fcSAndi Kleen { 68cae681fcSAndi Kleen if (hwpoison_dir) 69cae681fcSAndi Kleen debugfs_remove_recursive(hwpoison_dir); 70cae681fcSAndi Kleen } 71cae681fcSAndi Kleen 72cae681fcSAndi Kleen static int pfn_inject_init(void) 73cae681fcSAndi Kleen { 74847ce401SWu Fengguang struct dentry *dentry; 75847ce401SWu Fengguang 76cae681fcSAndi Kleen hwpoison_dir = debugfs_create_dir("hwpoison", NULL); 77cae681fcSAndi Kleen if (hwpoison_dir == NULL) 78cae681fcSAndi Kleen return -ENOMEM; 79847ce401SWu Fengguang 80847ce401SWu Fengguang /* 81847ce401SWu Fengguang * Note that the below poison/unpoison interfaces do not involve 82847ce401SWu Fengguang * hardware status change, hence do not require hardware support. 83847ce401SWu Fengguang * They are mainly for testing hwpoison in software level. 84847ce401SWu Fengguang */ 85847ce401SWu Fengguang dentry = debugfs_create_file("corrupt-pfn", 0600, hwpoison_dir, 86cae681fcSAndi Kleen NULL, &hwpoison_fops); 87847ce401SWu Fengguang if (!dentry) 88847ce401SWu Fengguang goto fail; 89847ce401SWu Fengguang 90847ce401SWu Fengguang dentry = debugfs_create_file("unpoison-pfn", 0600, hwpoison_dir, 91847ce401SWu Fengguang NULL, &unpoison_fops); 92847ce401SWu Fengguang if (!dentry) 93847ce401SWu Fengguang goto fail; 94847ce401SWu Fengguang 951bfe5febSHaicheng Li dentry = debugfs_create_u32("corrupt-filter-enable", 0600, 961bfe5febSHaicheng Li hwpoison_dir, &hwpoison_filter_enable); 971bfe5febSHaicheng Li if (!dentry) 981bfe5febSHaicheng Li goto fail; 991bfe5febSHaicheng Li 1007c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600, 1017c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_major); 1027c116f2bSWu Fengguang if (!dentry) 1037c116f2bSWu Fengguang goto fail; 1047c116f2bSWu Fengguang 1057c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600, 1067c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_minor); 1077c116f2bSWu Fengguang if (!dentry) 1087c116f2bSWu Fengguang goto fail; 1097c116f2bSWu Fengguang 110478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600, 111478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_mask); 112478c5ffcSWu Fengguang if (!dentry) 113478c5ffcSWu Fengguang goto fail; 114478c5ffcSWu Fengguang 115478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600, 116478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_value); 117478c5ffcSWu Fengguang if (!dentry) 118478c5ffcSWu Fengguang goto fail; 119478c5ffcSWu Fengguang 1204fd466ebSAndi Kleen #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP 1214fd466ebSAndi Kleen dentry = debugfs_create_u64("corrupt-filter-memcg", 0600, 1224fd466ebSAndi Kleen hwpoison_dir, &hwpoison_filter_memcg); 1234fd466ebSAndi Kleen if (!dentry) 1244fd466ebSAndi Kleen goto fail; 1254fd466ebSAndi Kleen #endif 1264fd466ebSAndi Kleen 127847ce401SWu Fengguang return 0; 128847ce401SWu Fengguang fail: 129cae681fcSAndi Kleen pfn_inject_exit(); 130cae681fcSAndi Kleen return -ENOMEM; 131cae681fcSAndi Kleen } 132cae681fcSAndi Kleen 133cae681fcSAndi Kleen module_init(pfn_inject_init); 134cae681fcSAndi Kleen module_exit(pfn_inject_exit); 135cae681fcSAndi Kleen MODULE_LICENSE("GPL"); 136