]>
Commit | Line | Data |
---|---|---|
457c8996 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7ac8bf9b BP |
2 | #include "edac_module.h" |
3 | ||
4 | static struct dentry *edac_debugfs; | |
5 | ||
6 | static ssize_t edac_fake_inject_write(struct file *file, | |
7 | const char __user *data, | |
8 | size_t count, loff_t *ppos) | |
9 | { | |
10 | struct device *dev = file->private_data; | |
11 | struct mem_ctl_info *mci = to_mci(dev); | |
12 | static enum hw_event_mc_err_type type; | |
13 | u16 errcount = mci->fake_inject_count; | |
14 | ||
15 | if (!errcount) | |
16 | errcount = 1; | |
17 | ||
18 | type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED | |
19 | : HW_EVENT_ERR_CORRECTED; | |
20 | ||
21 | printk(KERN_DEBUG | |
22 | "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", | |
23 | errcount, | |
24 | (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE", | |
25 | errcount > 1 ? "s" : "", | |
26 | mci->fake_inject_layer[0], | |
27 | mci->fake_inject_layer[1], | |
28 | mci->fake_inject_layer[2] | |
29 | ); | |
30 | edac_mc_handle_error(type, mci, errcount, 0, 0, 0, | |
31 | mci->fake_inject_layer[0], | |
32 | mci->fake_inject_layer[1], | |
33 | mci->fake_inject_layer[2], | |
34 | "FAKE ERROR", "for EDAC testing only"); | |
35 | ||
36 | return count; | |
37 | } | |
38 | ||
39 | static const struct file_operations debug_fake_inject_fops = { | |
40 | .open = simple_open, | |
41 | .write = edac_fake_inject_write, | |
42 | .llseek = generic_file_llseek, | |
43 | }; | |
44 | ||
912ebd99 | 45 | void __init edac_debugfs_init(void) |
7ac8bf9b BP |
46 | { |
47 | edac_debugfs = debugfs_create_dir("edac", NULL); | |
7ac8bf9b BP |
48 | } |
49 | ||
50 | void edac_debugfs_exit(void) | |
51 | { | |
9bf4f005 | 52 | debugfs_remove_recursive(edac_debugfs); |
7ac8bf9b BP |
53 | } |
54 | ||
912ebd99 | 55 | void edac_create_debugfs_nodes(struct mem_ctl_info *mci) |
7ac8bf9b | 56 | { |
912ebd99 | 57 | struct dentry *parent; |
7ac8bf9b BP |
58 | char name[80]; |
59 | int i; | |
60 | ||
912ebd99 | 61 | parent = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs); |
7ac8bf9b BP |
62 | |
63 | for (i = 0; i < mci->n_layers; i++) { | |
64 | sprintf(name, "fake_inject_%s", | |
65 | edac_layer_name[mci->layers[i].type]); | |
912ebd99 GKH |
66 | debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent, |
67 | &mci->fake_inject_layer[i]); | |
7ac8bf9b BP |
68 | } |
69 | ||
912ebd99 GKH |
70 | debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent, |
71 | &mci->fake_inject_ue); | |
7ac8bf9b | 72 | |
912ebd99 GKH |
73 | debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent, |
74 | &mci->fake_inject_count); | |
7ac8bf9b | 75 | |
912ebd99 GKH |
76 | debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev, |
77 | &debug_fake_inject_fops); | |
7ac8bf9b BP |
78 | |
79 | mci->debugfs = parent; | |
7ac8bf9b | 80 | } |
4397bcb4 BP |
81 | |
82 | /* Create a toplevel dir under EDAC's debugfs hierarchy */ | |
83 | struct dentry *edac_debugfs_create_dir(const char *dirname) | |
84 | { | |
85 | if (!edac_debugfs) | |
86 | return NULL; | |
87 | ||
88 | return debugfs_create_dir(dirname, edac_debugfs); | |
89 | } | |
90 | EXPORT_SYMBOL_GPL(edac_debugfs_create_dir); | |
91 | ||
92 | /* Create a toplevel dir under EDAC's debugfs hierarchy with parent @parent */ | |
93 | struct dentry * | |
94 | edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent) | |
95 | { | |
96 | return debugfs_create_dir(dirname, parent); | |
97 | } | |
98 | EXPORT_SYMBOL_GPL(edac_debugfs_create_dir_at); | |
99 | ||
100 | /* | |
101 | * Create a file under EDAC's hierarchy or a sub-hierarchy: | |
102 | * | |
103 | * @name: file name | |
104 | * @mode: file permissions | |
105 | * @parent: parent dentry. If NULL, it becomes the toplevel EDAC dir | |
106 | * @data: private data of caller | |
107 | * @fops: file operations of this file | |
108 | */ | |
109 | struct dentry * | |
110 | edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, | |
111 | void *data, const struct file_operations *fops) | |
112 | { | |
113 | if (!parent) | |
114 | parent = edac_debugfs; | |
115 | ||
116 | return debugfs_create_file(name, mode, parent, data, fops); | |
117 | } | |
118 | EXPORT_SYMBOL_GPL(edac_debugfs_create_file); | |
119 | ||
120 | /* Wrapper for debugfs_create_x8() */ | |
40d79624 GKH |
121 | void edac_debugfs_create_x8(const char *name, umode_t mode, |
122 | struct dentry *parent, u8 *value) | |
4397bcb4 BP |
123 | { |
124 | if (!parent) | |
125 | parent = edac_debugfs; | |
126 | ||
40d79624 | 127 | debugfs_create_x8(name, mode, parent, value); |
4397bcb4 BP |
128 | } |
129 | EXPORT_SYMBOL_GPL(edac_debugfs_create_x8); | |
130 | ||
131 | /* Wrapper for debugfs_create_x16() */ | |
40d79624 GKH |
132 | void edac_debugfs_create_x16(const char *name, umode_t mode, |
133 | struct dentry *parent, u16 *value) | |
4397bcb4 BP |
134 | { |
135 | if (!parent) | |
136 | parent = edac_debugfs; | |
137 | ||
40d79624 | 138 | debugfs_create_x16(name, mode, parent, value); |
4397bcb4 BP |
139 | } |
140 | EXPORT_SYMBOL_GPL(edac_debugfs_create_x16); | |
0ecace04 JL |
141 | |
142 | /* Wrapper for debugfs_create_x32() */ | |
143 | void edac_debugfs_create_x32(const char *name, umode_t mode, | |
144 | struct dentry *parent, u32 *value) | |
145 | { | |
146 | if (!parent) | |
147 | parent = edac_debugfs; | |
148 | ||
149 | debugfs_create_x32(name, mode, parent, value); | |
150 | } | |
151 | EXPORT_SYMBOL_GPL(edac_debugfs_create_x32); |