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