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 2331d3d348SWu Fengguang if (!pfn_valid(pfn)) 2431d3d348SWu Fengguang return -ENXIO; 2531d3d348SWu Fengguang 2631d3d348SWu Fengguang p = pfn_to_page(pfn); 2743131e14SNaoya Horiguchi hpage = compound_head(p); 2831d3d348SWu Fengguang /* 2931d3d348SWu Fengguang * This implies unable to support free buddy pages. 3031d3d348SWu Fengguang */ 3143131e14SNaoya Horiguchi if (!get_page_unless_zero(hpage)) 3231d3d348SWu Fengguang return 0; 3331d3d348SWu Fengguang 34fb31ba30SWanpeng Li if (!hwpoison_filter_enable) 35fb31ba30SWanpeng Li goto inject; 36fb31ba30SWanpeng Li 3743131e14SNaoya Horiguchi if (!PageLRU(p) && !PageHuge(p)) 38facb6011SAndi Kleen shake_page(p, 0); 3931d3d348SWu Fengguang /* 4031d3d348SWu Fengguang * This implies unable to support non-LRU pages. 4131d3d348SWu Fengguang */ 4243131e14SNaoya Horiguchi if (!PageLRU(p) && !PageHuge(p)) 43*7ea434a4SNaoya Horiguchi goto put_out; 4431d3d348SWu Fengguang 4531d3d348SWu Fengguang /* 4631d3d348SWu Fengguang * do a racy check with elevated page count, to make sure PG_hwpoison 4731d3d348SWu Fengguang * will only be set for the targeted owner (or on a free page). 4831d3d348SWu Fengguang * We temporarily take page lock for try_get_mem_cgroup_from_page(). 49cd42f4a3STony Luck * memory_failure() will redo the check reliably inside page lock. 5031d3d348SWu Fengguang */ 5143131e14SNaoya Horiguchi lock_page(hpage); 5243131e14SNaoya Horiguchi err = hwpoison_filter(hpage); 5343131e14SNaoya Horiguchi unlock_page(hpage); 5431d3d348SWu Fengguang if (err) 55*7ea434a4SNaoya Horiguchi goto put_out; 5631d3d348SWu Fengguang 570d57eb8dSAndi Kleen inject: 584883e997SWanpeng Li pr_info("Injecting memory failure at pfn %#lx\n", pfn); 59cd42f4a3STony Luck return memory_failure(pfn, 18, MF_COUNT_INCREASED); 60*7ea434a4SNaoya Horiguchi put_out: 61*7ea434a4SNaoya Horiguchi put_page(hpage); 62*7ea434a4SNaoya Horiguchi return 0; 63cae681fcSAndi Kleen } 64cae681fcSAndi Kleen 65847ce401SWu Fengguang static int hwpoison_unpoison(void *data, u64 val) 66847ce401SWu Fengguang { 67847ce401SWu Fengguang if (!capable(CAP_SYS_ADMIN)) 68847ce401SWu Fengguang return -EPERM; 69847ce401SWu Fengguang 70847ce401SWu Fengguang return unpoison_memory(val); 71847ce401SWu Fengguang } 72847ce401SWu Fengguang 73cae681fcSAndi Kleen DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n"); 74847ce401SWu Fengguang DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n"); 75cae681fcSAndi Kleen 76cae681fcSAndi Kleen static void pfn_inject_exit(void) 77cae681fcSAndi Kleen { 78cae681fcSAndi Kleen debugfs_remove_recursive(hwpoison_dir); 79cae681fcSAndi Kleen } 80cae681fcSAndi Kleen 81cae681fcSAndi Kleen static int pfn_inject_init(void) 82cae681fcSAndi Kleen { 83847ce401SWu Fengguang struct dentry *dentry; 84847ce401SWu Fengguang 85cae681fcSAndi Kleen hwpoison_dir = debugfs_create_dir("hwpoison", NULL); 86cae681fcSAndi Kleen if (hwpoison_dir == NULL) 87cae681fcSAndi Kleen return -ENOMEM; 88847ce401SWu Fengguang 89847ce401SWu Fengguang /* 90847ce401SWu Fengguang * Note that the below poison/unpoison interfaces do not involve 91847ce401SWu Fengguang * hardware status change, hence do not require hardware support. 92847ce401SWu Fengguang * They are mainly for testing hwpoison in software level. 93847ce401SWu Fengguang */ 942d1e8b3fSWanpeng Li dentry = debugfs_create_file("corrupt-pfn", 0200, hwpoison_dir, 95cae681fcSAndi Kleen NULL, &hwpoison_fops); 96847ce401SWu Fengguang if (!dentry) 97847ce401SWu Fengguang goto fail; 98847ce401SWu Fengguang 992d1e8b3fSWanpeng Li dentry = debugfs_create_file("unpoison-pfn", 0200, hwpoison_dir, 100847ce401SWu Fengguang NULL, &unpoison_fops); 101847ce401SWu Fengguang if (!dentry) 102847ce401SWu Fengguang goto fail; 103847ce401SWu Fengguang 1041bfe5febSHaicheng Li dentry = debugfs_create_u32("corrupt-filter-enable", 0600, 1051bfe5febSHaicheng Li hwpoison_dir, &hwpoison_filter_enable); 1061bfe5febSHaicheng Li if (!dentry) 1071bfe5febSHaicheng Li goto fail; 1081bfe5febSHaicheng Li 1097c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600, 1107c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_major); 1117c116f2bSWu Fengguang if (!dentry) 1127c116f2bSWu Fengguang goto fail; 1137c116f2bSWu Fengguang 1147c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600, 1157c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_minor); 1167c116f2bSWu Fengguang if (!dentry) 1177c116f2bSWu Fengguang goto fail; 1187c116f2bSWu Fengguang 119478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600, 120478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_mask); 121478c5ffcSWu Fengguang if (!dentry) 122478c5ffcSWu Fengguang goto fail; 123478c5ffcSWu Fengguang 124478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600, 125478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_value); 126478c5ffcSWu Fengguang if (!dentry) 127478c5ffcSWu Fengguang goto fail; 128478c5ffcSWu Fengguang 129c255a458SAndrew Morton #ifdef CONFIG_MEMCG_SWAP 1304fd466ebSAndi Kleen dentry = debugfs_create_u64("corrupt-filter-memcg", 0600, 1314fd466ebSAndi Kleen hwpoison_dir, &hwpoison_filter_memcg); 1324fd466ebSAndi Kleen if (!dentry) 1334fd466ebSAndi Kleen goto fail; 1344fd466ebSAndi Kleen #endif 1354fd466ebSAndi Kleen 136847ce401SWu Fengguang return 0; 137847ce401SWu Fengguang fail: 138cae681fcSAndi Kleen pfn_inject_exit(); 139cae681fcSAndi Kleen return -ENOMEM; 140cae681fcSAndi Kleen } 141cae681fcSAndi Kleen 142cae681fcSAndi Kleen module_init(pfn_inject_init); 143cae681fcSAndi Kleen module_exit(pfn_inject_exit); 144cae681fcSAndi Kleen MODULE_LICENSE("GPL"); 145