125985edcSLucas De Marchi /* Inject a hwpoison memory failure on a arbitrary 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> 843131e14SNaoya Horiguchi #include <linux/hugetlb.h> 97c116f2bSWu Fengguang #include "internal.h" 10cae681fcSAndi Kleen 11847ce401SWu Fengguang static struct dentry *hwpoison_dir; 12cae681fcSAndi Kleen 13cae681fcSAndi Kleen static int hwpoison_inject(void *data, u64 val) 14cae681fcSAndi Kleen { 1531d3d348SWu Fengguang unsigned long pfn = val; 1631d3d348SWu Fengguang struct page *p; 1743131e14SNaoya Horiguchi struct page *hpage; 1831d3d348SWu Fengguang int err; 1931d3d348SWu Fengguang 20cae681fcSAndi Kleen if (!capable(CAP_SYS_ADMIN)) 21cae681fcSAndi Kleen return -EPERM; 2231d3d348SWu Fengguang 230d57eb8dSAndi Kleen if (!hwpoison_filter_enable) 240d57eb8dSAndi Kleen goto inject; 2531d3d348SWu Fengguang if (!pfn_valid(pfn)) 2631d3d348SWu Fengguang return -ENXIO; 2731d3d348SWu Fengguang 2831d3d348SWu Fengguang p = pfn_to_page(pfn); 2943131e14SNaoya Horiguchi hpage = compound_head(p); 3031d3d348SWu Fengguang /* 3131d3d348SWu Fengguang * This implies unable to support free buddy pages. 3231d3d348SWu Fengguang */ 3343131e14SNaoya Horiguchi if (!get_page_unless_zero(hpage)) 3431d3d348SWu Fengguang return 0; 3531d3d348SWu Fengguang 3643131e14SNaoya Horiguchi if (!PageLRU(p) && !PageHuge(p)) 37facb6011SAndi Kleen shake_page(p, 0); 3831d3d348SWu Fengguang /* 3931d3d348SWu Fengguang * This implies unable to support non-LRU pages. 4031d3d348SWu Fengguang */ 4143131e14SNaoya Horiguchi if (!PageLRU(p) && !PageHuge(p)) 4231d3d348SWu Fengguang return 0; 4331d3d348SWu Fengguang 4431d3d348SWu Fengguang /* 4531d3d348SWu Fengguang * do a racy check with elevated page count, to make sure PG_hwpoison 4631d3d348SWu Fengguang * will only be set for the targeted owner (or on a free page). 4731d3d348SWu Fengguang * We temporarily take page lock for try_get_mem_cgroup_from_page(). 48cd42f4a3STony Luck * memory_failure() will redo the check reliably inside page lock. 4931d3d348SWu Fengguang */ 5043131e14SNaoya Horiguchi lock_page(hpage); 5143131e14SNaoya Horiguchi err = hwpoison_filter(hpage); 5243131e14SNaoya Horiguchi unlock_page(hpage); 5331d3d348SWu Fengguang if (err) 5431d3d348SWu Fengguang return 0; 5531d3d348SWu Fengguang 560d57eb8dSAndi Kleen inject: 5731d3d348SWu Fengguang printk(KERN_INFO "Injecting memory failure at pfn %lx\n", pfn); 58cd42f4a3STony Luck return memory_failure(pfn, 18, MF_COUNT_INCREASED); 59cae681fcSAndi Kleen } 60cae681fcSAndi Kleen 61847ce401SWu Fengguang static int hwpoison_unpoison(void *data, u64 val) 62847ce401SWu Fengguang { 63847ce401SWu Fengguang if (!capable(CAP_SYS_ADMIN)) 64847ce401SWu Fengguang return -EPERM; 65847ce401SWu Fengguang 66847ce401SWu Fengguang return unpoison_memory(val); 67847ce401SWu Fengguang } 68847ce401SWu Fengguang 69cae681fcSAndi Kleen DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n"); 70847ce401SWu Fengguang DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n"); 71cae681fcSAndi Kleen 72cae681fcSAndi Kleen static void pfn_inject_exit(void) 73cae681fcSAndi Kleen { 74cae681fcSAndi Kleen if (hwpoison_dir) 75cae681fcSAndi Kleen debugfs_remove_recursive(hwpoison_dir); 76cae681fcSAndi Kleen } 77cae681fcSAndi Kleen 78cae681fcSAndi Kleen static int pfn_inject_init(void) 79cae681fcSAndi Kleen { 80847ce401SWu Fengguang struct dentry *dentry; 81847ce401SWu Fengguang 82cae681fcSAndi Kleen hwpoison_dir = debugfs_create_dir("hwpoison", NULL); 83cae681fcSAndi Kleen if (hwpoison_dir == NULL) 84cae681fcSAndi Kleen return -ENOMEM; 85847ce401SWu Fengguang 86847ce401SWu Fengguang /* 87847ce401SWu Fengguang * Note that the below poison/unpoison interfaces do not involve 88847ce401SWu Fengguang * hardware status change, hence do not require hardware support. 89847ce401SWu Fengguang * They are mainly for testing hwpoison in software level. 90847ce401SWu Fengguang */ 91847ce401SWu Fengguang dentry = debugfs_create_file("corrupt-pfn", 0600, hwpoison_dir, 92cae681fcSAndi Kleen NULL, &hwpoison_fops); 93847ce401SWu Fengguang if (!dentry) 94847ce401SWu Fengguang goto fail; 95847ce401SWu Fengguang 96847ce401SWu Fengguang dentry = debugfs_create_file("unpoison-pfn", 0600, hwpoison_dir, 97847ce401SWu Fengguang NULL, &unpoison_fops); 98847ce401SWu Fengguang if (!dentry) 99847ce401SWu Fengguang goto fail; 100847ce401SWu Fengguang 1011bfe5febSHaicheng Li dentry = debugfs_create_u32("corrupt-filter-enable", 0600, 1021bfe5febSHaicheng Li hwpoison_dir, &hwpoison_filter_enable); 1031bfe5febSHaicheng Li if (!dentry) 1041bfe5febSHaicheng Li goto fail; 1051bfe5febSHaicheng Li 1067c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600, 1077c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_major); 1087c116f2bSWu Fengguang if (!dentry) 1097c116f2bSWu Fengguang goto fail; 1107c116f2bSWu Fengguang 1117c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600, 1127c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_minor); 1137c116f2bSWu Fengguang if (!dentry) 1147c116f2bSWu Fengguang goto fail; 1157c116f2bSWu Fengguang 116478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600, 117478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_mask); 118478c5ffcSWu Fengguang if (!dentry) 119478c5ffcSWu Fengguang goto fail; 120478c5ffcSWu Fengguang 121478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600, 122478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_value); 123478c5ffcSWu Fengguang if (!dentry) 124478c5ffcSWu Fengguang goto fail; 125478c5ffcSWu Fengguang 126*c255a458SAndrew Morton #ifdef CONFIG_MEMCG_SWAP 1274fd466ebSAndi Kleen dentry = debugfs_create_u64("corrupt-filter-memcg", 0600, 1284fd466ebSAndi Kleen hwpoison_dir, &hwpoison_filter_memcg); 1294fd466ebSAndi Kleen if (!dentry) 1304fd466ebSAndi Kleen goto fail; 1314fd466ebSAndi Kleen #endif 1324fd466ebSAndi Kleen 133847ce401SWu Fengguang return 0; 134847ce401SWu Fengguang fail: 135cae681fcSAndi Kleen pfn_inject_exit(); 136cae681fcSAndi Kleen return -ENOMEM; 137cae681fcSAndi Kleen } 138cae681fcSAndi Kleen 139cae681fcSAndi Kleen module_init(pfn_inject_init); 140cae681fcSAndi Kleen module_exit(pfn_inject_exit); 141cae681fcSAndi Kleen MODULE_LICENSE("GPL"); 142