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 34*fb31ba30SWanpeng Li if (!hwpoison_filter_enable) 35*fb31ba30SWanpeng Li goto inject; 36*fb31ba30SWanpeng 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)) 4331d3d348SWu Fengguang return 0; 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) 5531d3d348SWu Fengguang return 0; 5631d3d348SWu Fengguang 570d57eb8dSAndi Kleen inject: 5831d3d348SWu Fengguang printk(KERN_INFO "Injecting memory failure at pfn %lx\n", pfn); 59cd42f4a3STony Luck return memory_failure(pfn, 18, MF_COUNT_INCREASED); 60cae681fcSAndi Kleen } 61cae681fcSAndi Kleen 62847ce401SWu Fengguang static int hwpoison_unpoison(void *data, u64 val) 63847ce401SWu Fengguang { 64847ce401SWu Fengguang if (!capable(CAP_SYS_ADMIN)) 65847ce401SWu Fengguang return -EPERM; 66847ce401SWu Fengguang 67847ce401SWu Fengguang return unpoison_memory(val); 68847ce401SWu Fengguang } 69847ce401SWu Fengguang 70cae681fcSAndi Kleen DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n"); 71847ce401SWu Fengguang DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n"); 72cae681fcSAndi Kleen 73cae681fcSAndi Kleen static void pfn_inject_exit(void) 74cae681fcSAndi Kleen { 75cae681fcSAndi Kleen if (hwpoison_dir) 76cae681fcSAndi Kleen debugfs_remove_recursive(hwpoison_dir); 77cae681fcSAndi Kleen } 78cae681fcSAndi Kleen 79cae681fcSAndi Kleen static int pfn_inject_init(void) 80cae681fcSAndi Kleen { 81847ce401SWu Fengguang struct dentry *dentry; 82847ce401SWu Fengguang 83cae681fcSAndi Kleen hwpoison_dir = debugfs_create_dir("hwpoison", NULL); 84cae681fcSAndi Kleen if (hwpoison_dir == NULL) 85cae681fcSAndi Kleen return -ENOMEM; 86847ce401SWu Fengguang 87847ce401SWu Fengguang /* 88847ce401SWu Fengguang * Note that the below poison/unpoison interfaces do not involve 89847ce401SWu Fengguang * hardware status change, hence do not require hardware support. 90847ce401SWu Fengguang * They are mainly for testing hwpoison in software level. 91847ce401SWu Fengguang */ 922d1e8b3fSWanpeng Li dentry = debugfs_create_file("corrupt-pfn", 0200, hwpoison_dir, 93cae681fcSAndi Kleen NULL, &hwpoison_fops); 94847ce401SWu Fengguang if (!dentry) 95847ce401SWu Fengguang goto fail; 96847ce401SWu Fengguang 972d1e8b3fSWanpeng Li dentry = debugfs_create_file("unpoison-pfn", 0200, hwpoison_dir, 98847ce401SWu Fengguang NULL, &unpoison_fops); 99847ce401SWu Fengguang if (!dentry) 100847ce401SWu Fengguang goto fail; 101847ce401SWu Fengguang 1021bfe5febSHaicheng Li dentry = debugfs_create_u32("corrupt-filter-enable", 0600, 1031bfe5febSHaicheng Li hwpoison_dir, &hwpoison_filter_enable); 1041bfe5febSHaicheng Li if (!dentry) 1051bfe5febSHaicheng Li goto fail; 1061bfe5febSHaicheng Li 1077c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600, 1087c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_major); 1097c116f2bSWu Fengguang if (!dentry) 1107c116f2bSWu Fengguang goto fail; 1117c116f2bSWu Fengguang 1127c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600, 1137c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_minor); 1147c116f2bSWu Fengguang if (!dentry) 1157c116f2bSWu Fengguang goto fail; 1167c116f2bSWu Fengguang 117478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600, 118478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_mask); 119478c5ffcSWu Fengguang if (!dentry) 120478c5ffcSWu Fengguang goto fail; 121478c5ffcSWu Fengguang 122478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600, 123478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_value); 124478c5ffcSWu Fengguang if (!dentry) 125478c5ffcSWu Fengguang goto fail; 126478c5ffcSWu Fengguang 127c255a458SAndrew Morton #ifdef CONFIG_MEMCG_SWAP 1284fd466ebSAndi Kleen dentry = debugfs_create_u64("corrupt-filter-memcg", 0600, 1294fd466ebSAndi Kleen hwpoison_dir, &hwpoison_filter_memcg); 1304fd466ebSAndi Kleen if (!dentry) 1314fd466ebSAndi Kleen goto fail; 1324fd466ebSAndi Kleen #endif 1334fd466ebSAndi Kleen 134847ce401SWu Fengguang return 0; 135847ce401SWu Fengguang fail: 136cae681fcSAndi Kleen pfn_inject_exit(); 137cae681fcSAndi Kleen return -ENOMEM; 138cae681fcSAndi Kleen } 139cae681fcSAndi Kleen 140cae681fcSAndi Kleen module_init(pfn_inject_init); 141cae681fcSAndi Kleen module_exit(pfn_inject_exit); 142cae681fcSAndi Kleen MODULE_LICENSE("GPL"); 143