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)) 3231d3d348SWu Fengguang shake_page(p); 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 957c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600, 967c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_major); 977c116f2bSWu Fengguang if (!dentry) 987c116f2bSWu Fengguang goto fail; 997c116f2bSWu Fengguang 1007c116f2bSWu Fengguang dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600, 1017c116f2bSWu Fengguang hwpoison_dir, &hwpoison_filter_dev_minor); 1027c116f2bSWu Fengguang if (!dentry) 1037c116f2bSWu Fengguang goto fail; 1047c116f2bSWu Fengguang 105*478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600, 106*478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_mask); 107*478c5ffcSWu Fengguang if (!dentry) 108*478c5ffcSWu Fengguang goto fail; 109*478c5ffcSWu Fengguang 110*478c5ffcSWu Fengguang dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600, 111*478c5ffcSWu Fengguang hwpoison_dir, &hwpoison_filter_flags_value); 112*478c5ffcSWu Fengguang if (!dentry) 113*478c5ffcSWu Fengguang goto fail; 114*478c5ffcSWu Fengguang 115847ce401SWu Fengguang return 0; 116847ce401SWu Fengguang fail: 117cae681fcSAndi Kleen pfn_inject_exit(); 118cae681fcSAndi Kleen return -ENOMEM; 119cae681fcSAndi Kleen } 120cae681fcSAndi Kleen 121cae681fcSAndi Kleen module_init(pfn_inject_init); 122cae681fcSAndi Kleen module_exit(pfn_inject_exit); 123cae681fcSAndi Kleen MODULE_LICENSE("GPL"); 124