1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * EDAC PCI component |
4 | * |
5 | * Author: Dave Jiang <djiang@mvista.com> |
6 | * |
7 | * 2007 (c) MontaVista Software, Inc. |
8 | */ |
9 | #include <asm/page.h> |
10 | #include <linux/uaccess.h> |
11 | #include <linux/ctype.h> |
12 | #include <linux/highmem.h> |
13 | #include <linux/init.h> |
14 | #include <linux/module.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/smp.h> |
17 | #include <linux/spinlock.h> |
18 | #include <linux/sysctl.h> |
19 | #include <linux/timer.h> |
20 | |
21 | #include "edac_pci.h" |
22 | #include "edac_module.h" |
23 | |
24 | static DEFINE_MUTEX(edac_pci_ctls_mutex); |
25 | static LIST_HEAD(edac_pci_list); |
26 | static atomic_t pci_indexes = ATOMIC_INIT(0); |
27 | |
28 | struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, |
29 | const char *edac_pci_name) |
30 | { |
31 | struct edac_pci_ctl_info *pci; |
32 | |
33 | edac_dbg(1, "\n" ); |
34 | |
35 | pci = kzalloc(size: sizeof(struct edac_pci_ctl_info), GFP_KERNEL); |
36 | if (!pci) |
37 | return NULL; |
38 | |
39 | if (sz_pvt) { |
40 | pci->pvt_info = kzalloc(size: sz_pvt, GFP_KERNEL); |
41 | if (!pci->pvt_info) |
42 | goto free; |
43 | } |
44 | |
45 | pci->op_state = OP_ALLOC; |
46 | |
47 | snprintf(buf: pci->name, strlen(edac_pci_name) + 1, fmt: "%s" , edac_pci_name); |
48 | |
49 | return pci; |
50 | |
51 | free: |
52 | kfree(objp: pci); |
53 | return NULL; |
54 | } |
55 | EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info); |
56 | |
57 | void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci) |
58 | { |
59 | edac_dbg(1, "\n" ); |
60 | |
61 | edac_pci_remove_sysfs(pci); |
62 | } |
63 | EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info); |
64 | |
65 | /* |
66 | * find_edac_pci_by_dev() |
67 | * scans the edac_pci list for a specific 'struct device *' |
68 | * |
69 | * return NULL if not found, or return control struct pointer |
70 | */ |
71 | static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev) |
72 | { |
73 | struct edac_pci_ctl_info *pci; |
74 | struct list_head *item; |
75 | |
76 | edac_dbg(1, "\n" ); |
77 | |
78 | list_for_each(item, &edac_pci_list) { |
79 | pci = list_entry(item, struct edac_pci_ctl_info, link); |
80 | |
81 | if (pci->dev == dev) |
82 | return pci; |
83 | } |
84 | |
85 | return NULL; |
86 | } |
87 | |
88 | /* |
89 | * add_edac_pci_to_global_list |
90 | * Before calling this function, caller must assign a unique value to |
91 | * edac_dev->pci_idx. |
92 | * Return: |
93 | * 0 on success |
94 | * 1 on failure |
95 | */ |
96 | static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci) |
97 | { |
98 | struct list_head *item, *insert_before; |
99 | struct edac_pci_ctl_info *rover; |
100 | |
101 | edac_dbg(1, "\n" ); |
102 | |
103 | insert_before = &edac_pci_list; |
104 | |
105 | /* Determine if already on the list */ |
106 | rover = find_edac_pci_by_dev(dev: pci->dev); |
107 | if (unlikely(rover != NULL)) |
108 | goto fail0; |
109 | |
110 | /* Insert in ascending order by 'pci_idx', so find position */ |
111 | list_for_each(item, &edac_pci_list) { |
112 | rover = list_entry(item, struct edac_pci_ctl_info, link); |
113 | |
114 | if (rover->pci_idx >= pci->pci_idx) { |
115 | if (unlikely(rover->pci_idx == pci->pci_idx)) |
116 | goto fail1; |
117 | |
118 | insert_before = item; |
119 | break; |
120 | } |
121 | } |
122 | |
123 | list_add_tail_rcu(new: &pci->link, head: insert_before); |
124 | return 0; |
125 | |
126 | fail0: |
127 | edac_printk(KERN_WARNING, EDAC_PCI, |
128 | "%s (%s) %s %s already assigned %d\n" , |
129 | dev_name(rover->dev), edac_dev_name(rover), |
130 | rover->mod_name, rover->ctl_name, rover->pci_idx); |
131 | return 1; |
132 | |
133 | fail1: |
134 | edac_printk(KERN_WARNING, EDAC_PCI, |
135 | "but in low-level driver: attempt to assign\n" |
136 | "\tduplicate pci_idx %d in %s()\n" , rover->pci_idx, |
137 | __func__); |
138 | return 1; |
139 | } |
140 | |
141 | /* |
142 | * del_edac_pci_from_global_list |
143 | * |
144 | * remove the PCI control struct from the global list |
145 | */ |
146 | static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) |
147 | { |
148 | list_del_rcu(entry: &pci->link); |
149 | |
150 | /* these are for safe removal of devices from global list while |
151 | * NMI handlers may be traversing list |
152 | */ |
153 | synchronize_rcu(); |
154 | INIT_LIST_HEAD(list: &pci->link); |
155 | } |
156 | |
157 | /* |
158 | * edac_pci_workq_function() |
159 | * |
160 | * periodic function that performs the operation |
161 | * scheduled by a workq request, for a given PCI control struct |
162 | */ |
163 | static void edac_pci_workq_function(struct work_struct *work_req) |
164 | { |
165 | struct delayed_work *d_work = to_delayed_work(work: work_req); |
166 | struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work); |
167 | int msec; |
168 | unsigned long delay; |
169 | |
170 | edac_dbg(3, "checking\n" ); |
171 | |
172 | mutex_lock(&edac_pci_ctls_mutex); |
173 | |
174 | if (pci->op_state != OP_RUNNING_POLL) { |
175 | mutex_unlock(lock: &edac_pci_ctls_mutex); |
176 | return; |
177 | } |
178 | |
179 | if (edac_pci_get_check_errors()) |
180 | pci->edac_check(pci); |
181 | |
182 | /* if we are on a one second period, then use round */ |
183 | msec = edac_pci_get_poll_msec(); |
184 | if (msec == 1000) |
185 | delay = round_jiffies_relative(j: msecs_to_jiffies(m: msec)); |
186 | else |
187 | delay = msecs_to_jiffies(m: msec); |
188 | |
189 | edac_queue_work(work: &pci->work, delay); |
190 | |
191 | mutex_unlock(lock: &edac_pci_ctls_mutex); |
192 | } |
193 | |
194 | int edac_pci_alloc_index(void) |
195 | { |
196 | return atomic_inc_return(v: &pci_indexes) - 1; |
197 | } |
198 | EXPORT_SYMBOL_GPL(edac_pci_alloc_index); |
199 | |
200 | int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx) |
201 | { |
202 | edac_dbg(0, "\n" ); |
203 | |
204 | pci->pci_idx = edac_idx; |
205 | pci->start_time = jiffies; |
206 | |
207 | mutex_lock(&edac_pci_ctls_mutex); |
208 | |
209 | if (add_edac_pci_to_global_list(pci)) |
210 | goto fail0; |
211 | |
212 | if (edac_pci_create_sysfs(pci)) { |
213 | edac_pci_printk(pci, KERN_WARNING, |
214 | "failed to create sysfs pci\n" ); |
215 | goto fail1; |
216 | } |
217 | |
218 | if (pci->edac_check) { |
219 | pci->op_state = OP_RUNNING_POLL; |
220 | |
221 | INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function); |
222 | edac_queue_work(work: &pci->work, delay: msecs_to_jiffies(m: edac_pci_get_poll_msec())); |
223 | |
224 | } else { |
225 | pci->op_state = OP_RUNNING_INTERRUPT; |
226 | } |
227 | |
228 | edac_pci_printk(pci, KERN_INFO, |
229 | "Giving out device to module %s controller %s: DEV %s (%s)\n" , |
230 | pci->mod_name, pci->ctl_name, pci->dev_name, |
231 | edac_op_state_to_string(pci->op_state)); |
232 | |
233 | mutex_unlock(lock: &edac_pci_ctls_mutex); |
234 | return 0; |
235 | |
236 | /* error unwind stack */ |
237 | fail1: |
238 | del_edac_pci_from_global_list(pci); |
239 | fail0: |
240 | mutex_unlock(lock: &edac_pci_ctls_mutex); |
241 | return 1; |
242 | } |
243 | EXPORT_SYMBOL_GPL(edac_pci_add_device); |
244 | |
245 | struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev) |
246 | { |
247 | struct edac_pci_ctl_info *pci; |
248 | |
249 | edac_dbg(0, "\n" ); |
250 | |
251 | mutex_lock(&edac_pci_ctls_mutex); |
252 | |
253 | /* ensure the control struct is on the global list |
254 | * if not, then leave |
255 | */ |
256 | pci = find_edac_pci_by_dev(dev); |
257 | if (pci == NULL) { |
258 | mutex_unlock(lock: &edac_pci_ctls_mutex); |
259 | return NULL; |
260 | } |
261 | |
262 | pci->op_state = OP_OFFLINE; |
263 | |
264 | del_edac_pci_from_global_list(pci); |
265 | |
266 | mutex_unlock(lock: &edac_pci_ctls_mutex); |
267 | |
268 | if (pci->edac_check) |
269 | edac_stop_work(work: &pci->work); |
270 | |
271 | edac_printk(KERN_INFO, EDAC_PCI, |
272 | "Removed device %d for %s %s: DEV %s\n" , |
273 | pci->pci_idx, pci->mod_name, pci->ctl_name, edac_dev_name(pci)); |
274 | |
275 | return pci; |
276 | } |
277 | EXPORT_SYMBOL_GPL(edac_pci_del_device); |
278 | |
279 | /* |
280 | * edac_pci_generic_check |
281 | * |
282 | * a Generic parity check API |
283 | */ |
284 | static void edac_pci_generic_check(struct edac_pci_ctl_info *pci) |
285 | { |
286 | edac_dbg(4, "\n" ); |
287 | edac_pci_do_parity_check(); |
288 | } |
289 | |
290 | /* free running instance index counter */ |
291 | static int edac_pci_idx; |
292 | #define EDAC_PCI_GENCTL_NAME "EDAC PCI controller" |
293 | |
294 | struct edac_pci_gen_data { |
295 | int edac_idx; |
296 | }; |
297 | |
298 | struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, |
299 | const char *mod_name) |
300 | { |
301 | struct edac_pci_ctl_info *pci; |
302 | struct edac_pci_gen_data *pdata; |
303 | |
304 | pci = edac_pci_alloc_ctl_info(sizeof(*pdata), EDAC_PCI_GENCTL_NAME); |
305 | if (!pci) |
306 | return NULL; |
307 | |
308 | pdata = pci->pvt_info; |
309 | pci->dev = dev; |
310 | dev_set_drvdata(dev: pci->dev, data: pci); |
311 | pci->dev_name = pci_name(to_pci_dev(dev)); |
312 | |
313 | pci->mod_name = mod_name; |
314 | pci->ctl_name = EDAC_PCI_GENCTL_NAME; |
315 | if (edac_op_state == EDAC_OPSTATE_POLL) |
316 | pci->edac_check = edac_pci_generic_check; |
317 | |
318 | pdata->edac_idx = edac_pci_idx++; |
319 | |
320 | if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { |
321 | edac_dbg(3, "failed edac_pci_add_device()\n" ); |
322 | edac_pci_free_ctl_info(pci); |
323 | return NULL; |
324 | } |
325 | |
326 | return pci; |
327 | } |
328 | EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl); |
329 | |
330 | void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci) |
331 | { |
332 | edac_dbg(0, "pci mod=%s\n" , pci->mod_name); |
333 | |
334 | edac_pci_del_device(pci->dev); |
335 | edac_pci_free_ctl_info(pci); |
336 | } |
337 | EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl); |
338 | |