Blame view
drivers/edac/edac_pci.c
11.7 KB
91b99041c drivers/edac: upd... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/* * EDAC PCI component * * Author: Dave Jiang <djiang@mvista.com> * * 2007 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. * */ #include <linux/module.h> #include <linux/types.h> #include <linux/smp.h> #include <linux/init.h> #include <linux/sysctl.h> #include <linux/highmem.h> #include <linux/timer.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/list.h> #include <linux/sysdev.h> #include <linux/ctype.h> #include <linux/workqueue.h> #include <asm/uaccess.h> #include <asm/page.h> #include "edac_core.h" #include "edac_module.h" static DEFINE_MUTEX(edac_pci_ctls_mutex); |
ff6ac2a61 edac: use the sho... |
32 |
static LIST_HEAD(edac_pci_list); |
8641a3845 edac: Add edac_pc... |
33 |
static atomic_t pci_indexes = ATOMIC_INIT(0); |
91b99041c drivers/edac: upd... |
34 |
|
91b99041c drivers/edac: upd... |
35 |
/* |
d4c1465b7 drivers/edac: fix... |
36 37 38 39 40 |
* edac_pci_alloc_ctl_info * * The alloc() function for the 'edac_pci' control info * structure. The chip driver will allocate one of these for each * edac_pci it is going to control/register with the EDAC CORE. |
91b99041c drivers/edac: upd... |
41 |
*/ |
079708b91 drivers/edac: cor... |
42 |
struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, |
052dfb45c drivers/edac: cle... |
43 |
const char *edac_pci_name) |
91b99041c drivers/edac: upd... |
44 45 46 47 |
{ struct edac_pci_ctl_info *pci; void *pvt; unsigned int size; |
d4c1465b7 drivers/edac: fix... |
48 49 |
debugf1("%s() ", __func__); |
91b99041c drivers/edac: upd... |
50 51 52 |
pci = (struct edac_pci_ctl_info *)0; pvt = edac_align_ptr(&pci[1], sz_pvt); size = ((unsigned long)pvt) + sz_pvt; |
d4c1465b7 drivers/edac: fix... |
53 54 55 |
/* Alloc the needed control struct memory */ pci = kzalloc(size, GFP_KERNEL); if (pci == NULL) |
91b99041c drivers/edac: upd... |
56 |
return NULL; |
d4c1465b7 drivers/edac: fix... |
57 |
/* Now much private space */ |
91b99041c drivers/edac: upd... |
58 59 60 |
pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL; pci->pvt_info = pvt; |
91b99041c drivers/edac: upd... |
61 |
pci->op_state = OP_ALLOC; |
079708b91 drivers/edac: cor... |
62 |
snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name); |
91b99041c drivers/edac: upd... |
63 64 65 66 67 68 69 |
return pci; } EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info); /* * edac_pci_free_ctl_info() |
d4c1465b7 drivers/edac: fix... |
70 71 72 |
* * Last action on the pci control structure. * |
6f042b50e drivers/edac/: Sp... |
73 |
* call the remove sysfs information, which will unregister |
d4c1465b7 drivers/edac: fix... |
74 75 76 |
* this control struct's kobj. When that kobj's ref count * goes to zero, its release function will be call and then * kfree() the memory. |
91b99041c drivers/edac: upd... |
77 78 79 |
*/ void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci) { |
d4c1465b7 drivers/edac: fix... |
80 81 |
debugf1("%s() ", __func__); |
079708b91 drivers/edac: cor... |
82 |
|
d4c1465b7 drivers/edac: fix... |
83 84 |
edac_pci_remove_sysfs(pci); } |
91b99041c drivers/edac: upd... |
85 86 87 88 89 |
EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info); /* * find_edac_pci_by_dev() * scans the edac_pci list for a specific 'struct device *' |
d4c1465b7 drivers/edac: fix... |
90 91 |
* * return NULL if not found, or return control struct pointer |
91b99041c drivers/edac: upd... |
92 |
*/ |
079708b91 drivers/edac: cor... |
93 |
static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev) |
91b99041c drivers/edac: upd... |
94 95 96 |
{ struct edac_pci_ctl_info *pci; struct list_head *item; |
d4c1465b7 drivers/edac: fix... |
97 98 |
debugf1("%s() ", __func__); |
91b99041c drivers/edac: upd... |
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
list_for_each(item, &edac_pci_list) { pci = list_entry(item, struct edac_pci_ctl_info, link); if (pci->dev == dev) return pci; } return NULL; } /* * add_edac_pci_to_global_list * Before calling this function, caller must assign a unique value to * edac_dev->pci_idx. * Return: * 0 on success * 1 on failure */ static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci) { struct list_head *item, *insert_before; struct edac_pci_ctl_info *rover; |
d4c1465b7 drivers/edac: fix... |
122 123 |
debugf1("%s() ", __func__); |
91b99041c drivers/edac: upd... |
124 125 126 |
insert_before = &edac_pci_list; /* Determine if already on the list */ |
d4c1465b7 drivers/edac: fix... |
127 128 |
rover = find_edac_pci_by_dev(pci->dev); if (unlikely(rover != NULL)) |
91b99041c drivers/edac: upd... |
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
goto fail0; /* Insert in ascending order by 'pci_idx', so find position */ list_for_each(item, &edac_pci_list) { rover = list_entry(item, struct edac_pci_ctl_info, link); if (rover->pci_idx >= pci->pci_idx) { if (unlikely(rover->pci_idx == pci->pci_idx)) goto fail1; insert_before = item; break; } } list_add_tail_rcu(&pci->link, insert_before); return 0; |
052dfb45c drivers/edac: cle... |
146 |
fail0: |
91b99041c drivers/edac: upd... |
147 |
edac_printk(KERN_WARNING, EDAC_PCI, |
052dfb45c drivers/edac: cle... |
148 149 |
"%s (%s) %s %s already assigned %d ", |
281efb17d edac: struct devi... |
150 |
dev_name(rover->dev), edac_dev_name(rover), |
052dfb45c drivers/edac: cle... |
151 |
rover->mod_name, rover->ctl_name, rover->pci_idx); |
91b99041c drivers/edac: upd... |
152 |
return 1; |
052dfb45c drivers/edac: cle... |
153 |
fail1: |
91b99041c drivers/edac: upd... |
154 |
edac_printk(KERN_WARNING, EDAC_PCI, |
052dfb45c drivers/edac: cle... |
155 156 157 158 159 |
"but in low-level driver: attempt to assign " "\tduplicate pci_idx %d in %s() ", rover->pci_idx, __func__); |
91b99041c drivers/edac: upd... |
160 161 162 163 164 |
return 1; } /* * complete_edac_pci_list_del |
d4c1465b7 drivers/edac: fix... |
165 166 |
* * RCU completion callback to indicate item is deleted |
91b99041c drivers/edac: upd... |
167 168 169 170 171 172 173 |
*/ static void complete_edac_pci_list_del(struct rcu_head *head) { struct edac_pci_ctl_info *pci; pci = container_of(head, struct edac_pci_ctl_info, rcu); INIT_LIST_HEAD(&pci->link); |
91b99041c drivers/edac: upd... |
174 175 176 177 |
} /* * del_edac_pci_from_global_list |
d4c1465b7 drivers/edac: fix... |
178 179 |
* * remove the PCI control struct from the global list |
91b99041c drivers/edac: upd... |
180 181 182 183 |
*/ static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) { list_del_rcu(&pci->link); |
91b99041c drivers/edac: upd... |
184 |
call_rcu(&pci->rcu, complete_edac_pci_list_del); |
458e5ff13 edac: core: remov... |
185 |
rcu_barrier(); |
91b99041c drivers/edac: upd... |
186 |
} |
1a45027d1 edac: remove unne... |
187 188 |
#if 0 /* Older code, but might use in the future */ |
91b99041c drivers/edac: upd... |
189 190 191 192 193 194 195 196 197 |
/* * edac_pci_find() * Search for an edac_pci_ctl_info structure whose index is 'idx' * * If found, return a pointer to the structure * Else return NULL. * * Caller must hold pci_ctls_mutex. */ |
079708b91 drivers/edac: cor... |
198 |
struct edac_pci_ctl_info *edac_pci_find(int idx) |
91b99041c drivers/edac: upd... |
199 200 201 202 203 204 205 206 207 208 209 |
{ struct list_head *item; struct edac_pci_ctl_info *pci; /* Iterage over list, looking for exact match of ID */ list_for_each(item, &edac_pci_list) { pci = list_entry(item, struct edac_pci_ctl_info, link); if (pci->pci_idx >= idx) { if (pci->pci_idx == idx) return pci; |
079708b91 drivers/edac: cor... |
210 |
/* not on list, so terminate early */ |
91b99041c drivers/edac: upd... |
211 212 213 214 215 216 217 |
break; } } return NULL; } EXPORT_SYMBOL_GPL(edac_pci_find); |
1a45027d1 edac: remove unne... |
218 |
#endif |
91b99041c drivers/edac: upd... |
219 220 221 |
/* * edac_pci_workq_function() |
d4c1465b7 drivers/edac: fix... |
222 223 224 |
* * periodic function that performs the operation * scheduled by a workq request, for a given PCI control struct |
91b99041c drivers/edac: upd... |
225 |
*/ |
91b99041c drivers/edac: upd... |
226 227 |
static void edac_pci_workq_function(struct work_struct *work_req) { |
fbeb43847 edac: use to_dela... |
228 |
struct delayed_work *d_work = to_delayed_work(work_req); |
91b99041c drivers/edac: upd... |
229 |
struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work); |
d4c1465b7 drivers/edac: fix... |
230 231 |
int msec; unsigned long delay; |
91b99041c drivers/edac: upd... |
232 |
|
d4c1465b7 drivers/edac: fix... |
233 234 |
debugf3("%s() checking ", __func__); |
91b99041c drivers/edac: upd... |
235 |
|
d4c1465b7 drivers/edac: fix... |
236 |
mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
237 |
|
d4c1465b7 drivers/edac: fix... |
238 239 240 241 242 243 244 245 246 |
if (pci->op_state == OP_RUNNING_POLL) { /* we might be in POLL mode, but there may NOT be a poll func */ if ((pci->edac_check != NULL) && edac_pci_get_check_errors()) pci->edac_check(pci); /* if we are on a one second period, then use round */ msec = edac_pci_get_poll_msec(); if (msec == 1000) |
c2ae24cfd drivers-edac: use... |
247 |
delay = round_jiffies_relative(msecs_to_jiffies(msec)); |
d4c1465b7 drivers/edac: fix... |
248 249 250 251 252 253 |
else delay = msecs_to_jiffies(msec); /* Reschedule only if we are in POLL mode */ queue_delayed_work(edac_workqueue, &pci->work, delay); } |
91b99041c drivers/edac: upd... |
254 |
|
d4c1465b7 drivers/edac: fix... |
255 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
256 257 258 259 260 261 |
} /* * edac_pci_workq_setup() * initialize a workq item for this edac_pci instance * passing in the new delay period in msec |
d4c1465b7 drivers/edac: fix... |
262 263 264 |
* * locking model: * called when 'edac_pci_ctls_mutex' is locked |
91b99041c drivers/edac: upd... |
265 266 |
*/ static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci, |
079708b91 drivers/edac: cor... |
267 |
unsigned int msec) |
91b99041c drivers/edac: upd... |
268 269 270 |
{ debugf0("%s() ", __func__); |
91b99041c drivers/edac: upd... |
271 |
INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function); |
4de78c687 drivers/edac: mod... |
272 |
queue_delayed_work(edac_workqueue, &pci->work, |
052dfb45c drivers/edac: cle... |
273 |
msecs_to_jiffies(edac_pci_get_poll_msec())); |
91b99041c drivers/edac: upd... |
274 275 276 277 278 279 280 281 282 |
} /* * edac_pci_workq_teardown() * stop the workq processing on this edac_pci instance */ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci) { int status; |
d4c1465b7 drivers/edac: fix... |
283 284 |
debugf0("%s() ", __func__); |
91b99041c drivers/edac: upd... |
285 286 287 288 289 290 291 |
status = cancel_delayed_work(&pci->work); if (status == 0) flush_workqueue(edac_workqueue); } /* * edac_pci_reset_delay_period |
d4c1465b7 drivers/edac: fix... |
292 293 294 295 |
* * called with a new period value for the workq period * a) stop current workq timer * b) restart workq timer with new value |
91b99041c drivers/edac: upd... |
296 297 |
*/ void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci, |
079708b91 drivers/edac: cor... |
298 |
unsigned long value) |
91b99041c drivers/edac: upd... |
299 |
{ |
d4c1465b7 drivers/edac: fix... |
300 301 |
debugf0("%s() ", __func__); |
91b99041c drivers/edac: upd... |
302 303 |
edac_pci_workq_teardown(pci); |
d4c1465b7 drivers/edac: fix... |
304 305 |
/* need to lock for the setup */ mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
306 |
edac_pci_workq_setup(pci, value); |
d4c1465b7 drivers/edac: fix... |
307 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
308 309 310 311 |
} EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period); /* |
8641a3845 edac: Add edac_pc... |
312 313 314 315 316 317 318 319 320 321 322 323 324 |
* edac_pci_alloc_index: Allocate a unique PCI index number * * Return: * allocated index number * */ int edac_pci_alloc_index(void) { return atomic_inc_return(&pci_indexes) - 1; } EXPORT_SYMBOL_GPL(edac_pci_alloc_index); /* |
91b99041c drivers/edac: upd... |
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
* edac_pci_add_device: Insert the 'edac_dev' structure into the * edac_pci global list and create sysfs entries associated with * edac_pci structure. * @pci: pointer to the edac_device structure to be added to the list * @edac_idx: A unique numeric identifier to be assigned to the * 'edac_pci' structure. * * Return: * 0 Success * !0 Failure */ int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx) { debugf0("%s() ", __func__); pci->pci_idx = edac_idx; |
d4c1465b7 drivers/edac: fix... |
342 |
pci->start_time = jiffies; |
91b99041c drivers/edac: upd... |
343 |
|
d4c1465b7 drivers/edac: fix... |
344 |
mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
345 346 347 |
if (add_edac_pci_to_global_list(pci)) goto fail0; |
91b99041c drivers/edac: upd... |
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
if (edac_pci_create_sysfs(pci)) { edac_pci_printk(pci, KERN_WARNING, "failed to create sysfs pci "); goto fail1; } if (pci->edac_check != NULL) { pci->op_state = OP_RUNNING_POLL; edac_pci_workq_setup(pci, 1000); } else { pci->op_state = OP_RUNNING_INTERRUPT; } edac_pci_printk(pci, KERN_INFO, |
079708b91 drivers/edac: cor... |
364 365 366 367 368 |
"Giving out device to module '%s' controller '%s':" " DEV '%s' (%s) ", pci->mod_name, pci->ctl_name, |
17aa7e034 dev_name introduc... |
369 |
edac_dev_name(pci), edac_op_state_to_string(pci->op_state)); |
91b99041c drivers/edac: upd... |
370 |
|
d4c1465b7 drivers/edac: fix... |
371 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
372 |
return 0; |
d4c1465b7 drivers/edac: fix... |
373 |
/* error unwind stack */ |
052dfb45c drivers/edac: cle... |
374 |
fail1: |
91b99041c drivers/edac: upd... |
375 |
del_edac_pci_from_global_list(pci); |
052dfb45c drivers/edac: cle... |
376 |
fail0: |
d4c1465b7 drivers/edac: fix... |
377 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
return 1; } EXPORT_SYMBOL_GPL(edac_pci_add_device); /* * edac_pci_del_device() * Remove sysfs entries for specified edac_pci structure and * then remove edac_pci structure from global list * * @dev: * Pointer to 'struct device' representing edac_pci structure * to remove * * Return: * Pointer to removed edac_pci structure, * or NULL if device not found */ |
079708b91 drivers/edac: cor... |
395 |
struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev) |
91b99041c drivers/edac: upd... |
396 397 398 399 400 |
{ struct edac_pci_ctl_info *pci; debugf0("%s() ", __func__); |
d4c1465b7 drivers/edac: fix... |
401 |
mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
402 |
|
d4c1465b7 drivers/edac: fix... |
403 404 405 406 407 408 |
/* ensure the control struct is on the global list * if not, then leave */ pci = find_edac_pci_by_dev(dev); if (pci == NULL) { mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
409 410 411 412 |
return NULL; } pci->op_state = OP_OFFLINE; |
91b99041c drivers/edac: upd... |
413 |
del_edac_pci_from_global_list(pci); |
d4c1465b7 drivers/edac: fix... |
414 415 416 417 |
mutex_unlock(&edac_pci_ctls_mutex); /* stop the workq timer */ edac_pci_workq_teardown(pci); |
91b99041c drivers/edac: upd... |
418 419 |
edac_printk(KERN_INFO, EDAC_PCI, |
052dfb45c drivers/edac: cle... |
420 421 |
"Removed device %d for %s %s: DEV %s ", |
17aa7e034 dev_name introduc... |
422 |
pci->pci_idx, pci->mod_name, pci->ctl_name, edac_dev_name(pci)); |
91b99041c drivers/edac: upd... |
423 424 425 426 |
return pci; } EXPORT_SYMBOL_GPL(edac_pci_del_device); |
d4c1465b7 drivers/edac: fix... |
427 428 429 430 431 |
/* * edac_pci_generic_check * * a Generic parity check API */ |
1a45027d1 edac: remove unne... |
432 |
static void edac_pci_generic_check(struct edac_pci_ctl_info *pci) |
91b99041c drivers/edac: upd... |
433 |
{ |
d4c1465b7 drivers/edac: fix... |
434 435 |
debugf4("%s() ", __func__); |
91b99041c drivers/edac: upd... |
436 437 |
edac_pci_do_parity_check(); } |
d4c1465b7 drivers/edac: fix... |
438 |
/* free running instance index counter */ |
f044091ca drivers/edac: rem... |
439 |
static int edac_pci_idx; |
91b99041c drivers/edac: upd... |
440 441 442 443 444 |
#define EDAC_PCI_GENCTL_NAME "EDAC PCI controller" struct edac_pci_gen_data { int edac_idx; }; |
d4c1465b7 drivers/edac: fix... |
445 446 447 448 449 450 451 452 453 454 455 |
/* * edac_pci_create_generic_ctl * * A generic constructor for a PCI parity polling device * Some systems have more than one domain of PCI busses. * For systems with one domain, then this API will * provide for a generic poller. * * This routine calls the edac_pci_alloc_ctl_info() for * the generic device, with default values */ |
079708b91 drivers/edac: cor... |
456 |
struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, |
052dfb45c drivers/edac: cle... |
457 |
const char *mod_name) |
91b99041c drivers/edac: upd... |
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
{ struct edac_pci_ctl_info *pci; struct edac_pci_gen_data *pdata; pci = edac_pci_alloc_ctl_info(sizeof(*pdata), EDAC_PCI_GENCTL_NAME); if (!pci) return NULL; pdata = pci->pvt_info; pci->dev = dev; dev_set_drvdata(pci->dev, pci); pci->dev_name = pci_name(to_pci_dev(dev)); pci->mod_name = mod_name; pci->ctl_name = EDAC_PCI_GENCTL_NAME; pci->edac_check = edac_pci_generic_check; pdata->edac_idx = edac_pci_idx++; if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { debugf3("%s(): failed edac_pci_add_device() ", __func__); edac_pci_free_ctl_info(pci); return NULL; } return pci; } EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl); |
d4c1465b7 drivers/edac: fix... |
487 488 489 490 491 |
/* * edac_pci_release_generic_ctl * * The release function of a generic EDAC PCI polling device */ |
91b99041c drivers/edac: upd... |
492 493 |
void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci) { |
d4c1465b7 drivers/edac: fix... |
494 495 |
debugf0("%s() pci mod=%s ", __func__, pci->mod_name); |
91b99041c drivers/edac: upd... |
496 497 498 499 |
edac_pci_del_device(pci->dev); edac_pci_free_ctl_info(pci); } EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl); |