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 | if (!hwpoison_filter_enable) |
31 | goto inject; |
32 | |
33 | shake_page(p: hpage); |
34 | /* |
35 | * This implies unable to support non-LRU pages except free page. |
36 | */ |
37 | if (!PageLRU(page: hpage) && !PageHuge(page: p) && !is_free_buddy_page(page: p)) |
38 | return 0; |
39 | |
40 | /* |
41 | * do a racy check to make sure PG_hwpoison will only be set for |
42 | * the targeted owner (or on a free page). |
43 | * memory_failure() will redo the check reliably inside page lock. |
44 | */ |
45 | err = hwpoison_filter(p: hpage); |
46 | if (err) |
47 | return 0; |
48 | |
49 | inject: |
50 | pr_info("Injecting memory failure at pfn %#lx\n" , pfn); |
51 | err = memory_failure(pfn, flags: MF_SW_SIMULATED); |
52 | return (err == -EOPNOTSUPP) ? 0 : err; |
53 | } |
54 | |
55 | static int hwpoison_unpoison(void *data, u64 val) |
56 | { |
57 | if (!capable(CAP_SYS_ADMIN)) |
58 | return -EPERM; |
59 | |
60 | return unpoison_memory(pfn: val); |
61 | } |
62 | |
63 | DEFINE_DEBUGFS_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n" ); |
64 | DEFINE_DEBUGFS_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n" ); |
65 | |
66 | static void __exit pfn_inject_exit(void) |
67 | { |
68 | hwpoison_filter_enable = 0; |
69 | debugfs_remove_recursive(dentry: hwpoison_dir); |
70 | } |
71 | |
72 | static int __init pfn_inject_init(void) |
73 | { |
74 | hwpoison_dir = debugfs_create_dir(name: "hwpoison" , NULL); |
75 | |
76 | /* |
77 | * Note that the below poison/unpoison interfaces do not involve |
78 | * hardware status change, hence do not require hardware support. |
79 | * They are mainly for testing hwpoison in software level. |
80 | */ |
81 | debugfs_create_file(name: "corrupt-pfn" , mode: 0200, parent: hwpoison_dir, NULL, |
82 | fops: &hwpoison_fops); |
83 | |
84 | debugfs_create_file(name: "unpoison-pfn" , mode: 0200, parent: hwpoison_dir, NULL, |
85 | fops: &unpoison_fops); |
86 | |
87 | debugfs_create_u32(name: "corrupt-filter-enable" , mode: 0600, parent: hwpoison_dir, |
88 | value: &hwpoison_filter_enable); |
89 | |
90 | debugfs_create_u32(name: "corrupt-filter-dev-major" , mode: 0600, parent: hwpoison_dir, |
91 | value: &hwpoison_filter_dev_major); |
92 | |
93 | debugfs_create_u32(name: "corrupt-filter-dev-minor" , mode: 0600, parent: hwpoison_dir, |
94 | value: &hwpoison_filter_dev_minor); |
95 | |
96 | debugfs_create_u64(name: "corrupt-filter-flags-mask" , mode: 0600, parent: hwpoison_dir, |
97 | value: &hwpoison_filter_flags_mask); |
98 | |
99 | debugfs_create_u64(name: "corrupt-filter-flags-value" , mode: 0600, parent: hwpoison_dir, |
100 | value: &hwpoison_filter_flags_value); |
101 | |
102 | #ifdef CONFIG_MEMCG |
103 | debugfs_create_u64(name: "corrupt-filter-memcg" , mode: 0600, parent: hwpoison_dir, |
104 | value: &hwpoison_filter_memcg); |
105 | #endif |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | module_init(pfn_inject_init); |
111 | module_exit(pfn_inject_exit); |
112 | MODULE_LICENSE("GPL" ); |
113 | |