Blame view
drivers/edac/edac_pci.c
11.5 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 32 |
/* * 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); static struct list_head edac_pci_list = LIST_HEAD_INIT(edac_pci_list); |
91b99041c drivers/edac: upd... |
33 |
/* |
d4c1465b7 drivers/edac: fix... |
34 35 36 37 38 |
* 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... |
39 |
*/ |
079708b91 drivers/edac: cor... |
40 |
struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, |
052dfb45c drivers/edac: cle... |
41 |
const char *edac_pci_name) |
91b99041c drivers/edac: upd... |
42 43 44 45 |
{ struct edac_pci_ctl_info *pci; void *pvt; unsigned int size; |
d4c1465b7 drivers/edac: fix... |
46 47 |
debugf1("%s() ", __func__); |
91b99041c drivers/edac: upd... |
48 49 50 |
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... |
51 52 53 |
/* Alloc the needed control struct memory */ pci = kzalloc(size, GFP_KERNEL); if (pci == NULL) |
91b99041c drivers/edac: upd... |
54 |
return NULL; |
d4c1465b7 drivers/edac: fix... |
55 |
/* Now much private space */ |
91b99041c drivers/edac: upd... |
56 57 58 |
pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL; pci->pvt_info = pvt; |
91b99041c drivers/edac: upd... |
59 |
pci->op_state = OP_ALLOC; |
079708b91 drivers/edac: cor... |
60 |
snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name); |
91b99041c drivers/edac: upd... |
61 62 63 64 65 66 67 |
return pci; } EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info); /* * edac_pci_free_ctl_info() |
d4c1465b7 drivers/edac: fix... |
68 69 70 71 72 73 74 |
* * Last action on the pci control structure. * * call the remove sysfs informaton, which will unregister * 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... |
75 76 77 |
*/ void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci) { |
d4c1465b7 drivers/edac: fix... |
78 79 |
debugf1("%s() ", __func__); |
079708b91 drivers/edac: cor... |
80 |
|
d4c1465b7 drivers/edac: fix... |
81 82 |
edac_pci_remove_sysfs(pci); } |
91b99041c drivers/edac: upd... |
83 84 85 86 87 |
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... |
88 89 |
* * return NULL if not found, or return control struct pointer |
91b99041c drivers/edac: upd... |
90 |
*/ |
079708b91 drivers/edac: cor... |
91 |
static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev) |
91b99041c drivers/edac: upd... |
92 93 94 |
{ struct edac_pci_ctl_info *pci; struct list_head *item; |
d4c1465b7 drivers/edac: fix... |
95 96 |
debugf1("%s() ", __func__); |
91b99041c drivers/edac: upd... |
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
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... |
120 121 |
debugf1("%s() ", __func__); |
91b99041c drivers/edac: upd... |
122 123 124 |
insert_before = &edac_pci_list; /* Determine if already on the list */ |
d4c1465b7 drivers/edac: fix... |
125 126 |
rover = find_edac_pci_by_dev(pci->dev); if (unlikely(rover != NULL)) |
91b99041c drivers/edac: upd... |
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
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... |
144 |
fail0: |
91b99041c drivers/edac: upd... |
145 |
edac_printk(KERN_WARNING, EDAC_PCI, |
052dfb45c drivers/edac: cle... |
146 147 148 149 |
"%s (%s) %s %s already assigned %d ", rover->dev->bus_id, dev_name(rover), rover->mod_name, rover->ctl_name, rover->pci_idx); |
91b99041c drivers/edac: upd... |
150 |
return 1; |
052dfb45c drivers/edac: cle... |
151 |
fail1: |
91b99041c drivers/edac: upd... |
152 |
edac_printk(KERN_WARNING, EDAC_PCI, |
052dfb45c drivers/edac: cle... |
153 154 155 156 157 |
"but in low-level driver: attempt to assign " "\tduplicate pci_idx %d in %s() ", rover->pci_idx, __func__); |
91b99041c drivers/edac: upd... |
158 159 160 161 162 |
return 1; } /* * complete_edac_pci_list_del |
d4c1465b7 drivers/edac: fix... |
163 164 |
* * RCU completion callback to indicate item is deleted |
91b99041c drivers/edac: upd... |
165 166 167 168 169 170 171 172 173 174 175 176 |
*/ 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); complete(&pci->complete); } /* * del_edac_pci_from_global_list |
d4c1465b7 drivers/edac: fix... |
177 178 |
* * remove the PCI control struct from the global list |
91b99041c drivers/edac: upd... |
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
*/ static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) { list_del_rcu(&pci->link); init_completion(&pci->complete); call_rcu(&pci->rcu, complete_edac_pci_list_del); wait_for_completion(&pci->complete); } /* * 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... |
197 |
struct edac_pci_ctl_info *edac_pci_find(int idx) |
91b99041c drivers/edac: upd... |
198 199 200 201 202 203 204 205 206 207 208 |
{ 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... |
209 |
/* not on list, so terminate early */ |
91b99041c drivers/edac: upd... |
210 211 212 213 214 215 216 217 218 219 |
break; } } return NULL; } EXPORT_SYMBOL_GPL(edac_pci_find); /* * edac_pci_workq_function() |
d4c1465b7 drivers/edac: fix... |
220 221 222 |
* * periodic function that performs the operation * scheduled by a workq request, for a given PCI control struct |
91b99041c drivers/edac: upd... |
223 |
*/ |
91b99041c drivers/edac: upd... |
224 225 226 227 |
static void edac_pci_workq_function(struct work_struct *work_req) { struct delayed_work *d_work = (struct delayed_work *)work_req; struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work); |
d4c1465b7 drivers/edac: fix... |
228 229 |
int msec; unsigned long delay; |
91b99041c drivers/edac: upd... |
230 |
|
d4c1465b7 drivers/edac: fix... |
231 232 |
debugf3("%s() checking ", __func__); |
91b99041c drivers/edac: upd... |
233 |
|
d4c1465b7 drivers/edac: fix... |
234 |
mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
235 |
|
d4c1465b7 drivers/edac: fix... |
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
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) delay = round_jiffies(msecs_to_jiffies(msec)); 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... |
252 |
|
d4c1465b7 drivers/edac: fix... |
253 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
254 255 256 257 258 259 |
} /* * 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... |
260 261 262 |
* * locking model: * called when 'edac_pci_ctls_mutex' is locked |
91b99041c drivers/edac: upd... |
263 264 |
*/ static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci, |
079708b91 drivers/edac: cor... |
265 |
unsigned int msec) |
91b99041c drivers/edac: upd... |
266 267 268 |
{ debugf0("%s() ", __func__); |
91b99041c drivers/edac: upd... |
269 |
INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function); |
4de78c687 drivers/edac: mod... |
270 |
queue_delayed_work(edac_workqueue, &pci->work, |
052dfb45c drivers/edac: cle... |
271 |
msecs_to_jiffies(edac_pci_get_poll_msec())); |
91b99041c drivers/edac: upd... |
272 273 274 275 276 277 278 279 280 |
} /* * 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... |
281 282 |
debugf0("%s() ", __func__); |
91b99041c drivers/edac: upd... |
283 284 285 286 287 288 289 |
status = cancel_delayed_work(&pci->work); if (status == 0) flush_workqueue(edac_workqueue); } /* * edac_pci_reset_delay_period |
d4c1465b7 drivers/edac: fix... |
290 291 292 293 |
* * 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... |
294 295 |
*/ void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci, |
079708b91 drivers/edac: cor... |
296 |
unsigned long value) |
91b99041c drivers/edac: upd... |
297 |
{ |
d4c1465b7 drivers/edac: fix... |
298 299 |
debugf0("%s() ", __func__); |
91b99041c drivers/edac: upd... |
300 301 |
edac_pci_workq_teardown(pci); |
d4c1465b7 drivers/edac: fix... |
302 303 |
/* need to lock for the setup */ mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
304 |
edac_pci_workq_setup(pci, value); |
d4c1465b7 drivers/edac: fix... |
305 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
} EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period); /* * 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... |
327 |
pci->start_time = jiffies; |
91b99041c drivers/edac: upd... |
328 |
|
d4c1465b7 drivers/edac: fix... |
329 |
mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
330 331 332 |
if (add_edac_pci_to_global_list(pci)) goto fail0; |
91b99041c drivers/edac: upd... |
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
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... |
349 350 351 352 353 |
"Giving out device to module '%s' controller '%s':" " DEV '%s' (%s) ", pci->mod_name, pci->ctl_name, |
494d0d55b drivers/edac: mod... |
354 |
dev_name(pci), edac_op_state_to_string(pci->op_state)); |
91b99041c drivers/edac: upd... |
355 |
|
d4c1465b7 drivers/edac: fix... |
356 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
357 |
return 0; |
d4c1465b7 drivers/edac: fix... |
358 |
/* error unwind stack */ |
052dfb45c drivers/edac: cle... |
359 |
fail1: |
91b99041c drivers/edac: upd... |
360 |
del_edac_pci_from_global_list(pci); |
052dfb45c drivers/edac: cle... |
361 |
fail0: |
d4c1465b7 drivers/edac: fix... |
362 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
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... |
380 |
struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev) |
91b99041c drivers/edac: upd... |
381 382 383 384 385 |
{ struct edac_pci_ctl_info *pci; debugf0("%s() ", __func__); |
d4c1465b7 drivers/edac: fix... |
386 |
mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
387 |
|
d4c1465b7 drivers/edac: fix... |
388 389 390 391 392 393 |
/* 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... |
394 395 396 397 |
return NULL; } pci->op_state = OP_OFFLINE; |
91b99041c drivers/edac: upd... |
398 |
del_edac_pci_from_global_list(pci); |
d4c1465b7 drivers/edac: fix... |
399 400 401 402 |
mutex_unlock(&edac_pci_ctls_mutex); /* stop the workq timer */ edac_pci_workq_teardown(pci); |
91b99041c drivers/edac: upd... |
403 404 |
edac_printk(KERN_INFO, EDAC_PCI, |
052dfb45c drivers/edac: cle... |
405 406 407 |
"Removed device %d for %s %s: DEV %s ", pci->pci_idx, pci->mod_name, pci->ctl_name, dev_name(pci)); |
91b99041c drivers/edac: upd... |
408 409 410 411 |
return pci; } EXPORT_SYMBOL_GPL(edac_pci_del_device); |
d4c1465b7 drivers/edac: fix... |
412 413 414 415 416 |
/* * edac_pci_generic_check * * a Generic parity check API */ |
91b99041c drivers/edac: upd... |
417 418 |
void edac_pci_generic_check(struct edac_pci_ctl_info *pci) { |
d4c1465b7 drivers/edac: fix... |
419 420 |
debugf4("%s() ", __func__); |
91b99041c drivers/edac: upd... |
421 422 |
edac_pci_do_parity_check(); } |
d4c1465b7 drivers/edac: fix... |
423 |
/* free running instance index counter */ |
f044091ca drivers/edac: rem... |
424 |
static int edac_pci_idx; |
91b99041c drivers/edac: upd... |
425 426 427 428 429 |
#define EDAC_PCI_GENCTL_NAME "EDAC PCI controller" struct edac_pci_gen_data { int edac_idx; }; |
d4c1465b7 drivers/edac: fix... |
430 431 432 433 434 435 436 437 438 439 440 |
/* * 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... |
441 |
struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, |
052dfb45c drivers/edac: cle... |
442 |
const char *mod_name) |
91b99041c drivers/edac: upd... |
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
{ 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... |
472 473 474 475 476 |
/* * edac_pci_release_generic_ctl * * The release function of a generic EDAC PCI polling device */ |
91b99041c drivers/edac: upd... |
477 478 |
void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci) { |
d4c1465b7 drivers/edac: fix... |
479 480 |
debugf0("%s() pci mod=%s ", __func__, pci->mod_name); |
91b99041c drivers/edac: upd... |
481 482 483 484 |
edac_pci_del_device(pci->dev); edac_pci_free_ctl_info(pci); } EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl); |