1/* Inject a hwpoison memory failure on a arbitary pfn */ 2#include <linux/module.h> 3#include <linux/debugfs.h> 4#include <linux/kernel.h> 5#include <linux/mm.h> 6#include <linux/swap.h> 7#include <linux/pagemap.h> 8#include <linux/hugetlb.h> 9#include "internal.h" 10 11static struct dentry *hwpoison_dir; 12 13static int hwpoison_inject(void *data, u64 val) 14{ 15 unsigned long pfn = val; 16 struct page *p; 17 struct page *hpage; 18 int err; 19 20 if (!capable(CAP_SYS_ADMIN)) 21 return -EPERM; 22 23 if (!hwpoison_filter_enable) 24 goto inject; 25 if (!pfn_valid(pfn)) 26 return -ENXIO; 27 28 p = pfn_to_page(pfn); 29 hpage = compound_head(p); 30 /* 31 * This implies unable to support free buddy pages. 32 */ 33 if (!get_page_unless_zero(hpage)) 34 return 0; 35 36 if (!PageLRU(p) && !PageHuge(p)) 37 shake_page(p, 0); 38 /* 39 * This implies unable to support non-LRU pages. 40 */ 41 if (!PageLRU(p) && !PageHuge(p)) 42 return 0; 43 44 /* 45 * do a racy check with elevated page count, to make sure PG_hwpoison 46 * will only be set for the targeted owner (or on a free page). 47 * We temporarily take page lock for try_get_mem_cgroup_from_page(). 48 * __memory_failure() will redo the check reliably inside page lock. 49 */ 50 lock_page(hpage); 51 err = hwpoison_filter(hpage); 52 unlock_page(hpage); 53 if (err) 54 return 0; 55 56inject: 57 printk(KERN_INFO "Injecting memory failure at pfn %lx\n", pfn); 58 return __memory_failure(pfn, 18, MF_COUNT_INCREASED); 59} 60 61static int hwpoison_unpoison(void *data, u64 val) 62{ 63 if (!capable(CAP_SYS_ADMIN)) 64 return -EPERM; 65 66 return unpoison_memory(val); 67} 68 69DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n"); 70DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n"); 71 72static void pfn_inject_exit(void) 73{ 74 if (hwpoison_dir) 75 debugfs_remove_recursive(hwpoison_dir); 76} 77 78static int pfn_inject_init(void) 79{ 80 struct dentry *dentry; 81 82 hwpoison_dir = debugfs_create_dir("hwpoison", NULL); 83 if (hwpoison_dir == NULL) 84 return -ENOMEM; 85 86 /* 87 * Note that the below poison/unpoison interfaces do not involve 88 * hardware status change, hence do not require hardware support. 89 * They are mainly for testing hwpoison in software level. 90 */ 91 dentry = debugfs_create_file("corrupt-pfn", 0600, hwpoison_dir, 92 NULL, &hwpoison_fops); 93 if (!dentry) 94 goto fail; 95 96 dentry = debugfs_create_file("unpoison-pfn", 0600, hwpoison_dir, 97 NULL, &unpoison_fops); 98 if (!dentry) 99 goto fail; 100 101 dentry = debugfs_create_u32("corrupt-filter-enable", 0600, 102 hwpoison_dir, &hwpoison_filter_enable); 103 if (!dentry) 104 goto fail; 105 106 dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600, 107 hwpoison_dir, &hwpoison_filter_dev_major); 108 if (!dentry) 109 goto fail; 110 111 dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600, 112 hwpoison_dir, &hwpoison_filter_dev_minor); 113 if (!dentry) 114 goto fail; 115 116 dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600, 117 hwpoison_dir, &hwpoison_filter_flags_mask); 118 if (!dentry) 119 goto fail; 120 121 dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600, 122 hwpoison_dir, &hwpoison_filter_flags_value); 123 if (!dentry) 124 goto fail; 125 126#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP 127 dentry = debugfs_create_u64("corrupt-filter-memcg", 0600, 128 hwpoison_dir, &hwpoison_filter_memcg); 129 if (!dentry) 130 goto fail; 131#endif 132 133 return 0; 134fail: 135 pfn_inject_exit(); 136 return -ENOMEM; 137} 138 139module_init(pfn_inject_init); 140module_exit(pfn_inject_exit); 141MODULE_LICENSE("GPL"); 142