Blame view
drivers/edac/edac_pci.c
7.62 KB
91b99041c drivers/edac: upd... |
1 2 3 4 5 6 7 8 9 10 11 |
/* * 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. * */ |
0b892c717 edac: move EDAC P... |
12 |
#include <asm/page.h> |
7c0f6ba68 Replace <asm/uacc... |
13 |
#include <linux/uaccess.h> |
0b892c717 edac: move EDAC P... |
14 15 16 |
#include <linux/ctype.h> #include <linux/highmem.h> #include <linux/init.h> |
91b99041c drivers/edac: upd... |
17 |
#include <linux/module.h> |
0b892c717 edac: move EDAC P... |
18 |
#include <linux/slab.h> |
91b99041c drivers/edac: upd... |
19 |
#include <linux/smp.h> |
0b892c717 edac: move EDAC P... |
20 |
#include <linux/spinlock.h> |
91b99041c drivers/edac: upd... |
21 |
#include <linux/sysctl.h> |
91b99041c drivers/edac: upd... |
22 |
#include <linux/timer.h> |
91b99041c drivers/edac: upd... |
23 |
|
0b892c717 edac: move EDAC P... |
24 |
#include "edac_pci.h" |
91b99041c drivers/edac: upd... |
25 26 27 |
#include "edac_module.h" static DEFINE_MUTEX(edac_pci_ctls_mutex); |
ff6ac2a61 edac: use the sho... |
28 |
static LIST_HEAD(edac_pci_list); |
8641a3845 edac: Add edac_pc... |
29 |
static atomic_t pci_indexes = ATOMIC_INIT(0); |
91b99041c drivers/edac: upd... |
30 |
|
079708b91 drivers/edac: cor... |
31 |
struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, |
052dfb45c drivers/edac: cle... |
32 |
const char *edac_pci_name) |
91b99041c drivers/edac: upd... |
33 34 |
{ struct edac_pci_ctl_info *pci; |
93e4fe64e edac: rewrite eda... |
35 |
void *p = NULL, *pvt; |
91b99041c drivers/edac: upd... |
36 |
unsigned int size; |
956b9ba15 edac: Convert deb... |
37 38 |
edac_dbg(1, " "); |
d4c1465b7 drivers/edac: fix... |
39 |
|
93e4fe64e edac: rewrite eda... |
40 41 |
pci = edac_align_ptr(&p, sizeof(*pci), 1); pvt = edac_align_ptr(&p, 1, sz_pvt); |
91b99041c drivers/edac: upd... |
42 |
size = ((unsigned long)pvt) + sz_pvt; |
d4c1465b7 drivers/edac: fix... |
43 44 45 |
/* Alloc the needed control struct memory */ pci = kzalloc(size, GFP_KERNEL); if (pci == NULL) |
91b99041c drivers/edac: upd... |
46 |
return NULL; |
d4c1465b7 drivers/edac: fix... |
47 |
/* Now much private space */ |
91b99041c drivers/edac: upd... |
48 49 50 |
pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL; pci->pvt_info = pvt; |
91b99041c drivers/edac: upd... |
51 |
pci->op_state = OP_ALLOC; |
079708b91 drivers/edac: cor... |
52 |
snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name); |
91b99041c drivers/edac: upd... |
53 54 55 56 |
return pci; } EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info); |
91b99041c drivers/edac: upd... |
57 58 |
void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci) { |
956b9ba15 edac: Convert deb... |
59 60 |
edac_dbg(1, " "); |
079708b91 drivers/edac: cor... |
61 |
|
d4c1465b7 drivers/edac: fix... |
62 63 |
edac_pci_remove_sysfs(pci); } |
91b99041c drivers/edac: upd... |
64 65 66 67 68 |
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... |
69 70 |
* * return NULL if not found, or return control struct pointer |
91b99041c drivers/edac: upd... |
71 |
*/ |
079708b91 drivers/edac: cor... |
72 |
static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev) |
91b99041c drivers/edac: upd... |
73 74 75 |
{ struct edac_pci_ctl_info *pci; struct list_head *item; |
956b9ba15 edac: Convert deb... |
76 77 |
edac_dbg(1, " "); |
91b99041c drivers/edac: upd... |
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
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; |
956b9ba15 edac: Convert deb... |
101 102 |
edac_dbg(1, " "); |
d4c1465b7 drivers/edac: fix... |
103 |
|
91b99041c drivers/edac: upd... |
104 105 106 |
insert_before = &edac_pci_list; /* Determine if already on the list */ |
d4c1465b7 drivers/edac: fix... |
107 108 |
rover = find_edac_pci_by_dev(pci->dev); if (unlikely(rover != NULL)) |
91b99041c drivers/edac: upd... |
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
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... |
126 |
fail0: |
91b99041c drivers/edac: upd... |
127 |
edac_printk(KERN_WARNING, EDAC_PCI, |
052dfb45c drivers/edac: cle... |
128 129 |
"%s (%s) %s %s already assigned %d ", |
281efb17d edac: struct devi... |
130 |
dev_name(rover->dev), edac_dev_name(rover), |
052dfb45c drivers/edac: cle... |
131 |
rover->mod_name, rover->ctl_name, rover->pci_idx); |
91b99041c drivers/edac: upd... |
132 |
return 1; |
052dfb45c drivers/edac: cle... |
133 |
fail1: |
91b99041c drivers/edac: upd... |
134 |
edac_printk(KERN_WARNING, EDAC_PCI, |
052dfb45c drivers/edac: cle... |
135 136 137 138 139 |
"but in low-level driver: attempt to assign " "\tduplicate pci_idx %d in %s() ", rover->pci_idx, __func__); |
91b99041c drivers/edac: upd... |
140 141 142 143 |
return 1; } /* |
91b99041c drivers/edac: upd... |
144 |
* del_edac_pci_from_global_list |
d4c1465b7 drivers/edac: fix... |
145 146 |
* * remove the PCI control struct from the global list |
91b99041c drivers/edac: upd... |
147 148 149 150 |
*/ static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) { list_del_rcu(&pci->link); |
e2e770987 edac,rcu: use syn... |
151 152 153 154 155 156 |
/* these are for safe removal of devices from global list while * NMI handlers may be traversing list */ synchronize_rcu(); INIT_LIST_HEAD(&pci->link); |
91b99041c drivers/edac: upd... |
157 |
} |
91b99041c drivers/edac: upd... |
158 159 |
/* * edac_pci_workq_function() |
d4c1465b7 drivers/edac: fix... |
160 161 162 |
* * periodic function that performs the operation * scheduled by a workq request, for a given PCI control struct |
91b99041c drivers/edac: upd... |
163 |
*/ |
91b99041c drivers/edac: upd... |
164 165 |
static void edac_pci_workq_function(struct work_struct *work_req) { |
fbeb43847 edac: use to_dela... |
166 |
struct delayed_work *d_work = to_delayed_work(work_req); |
91b99041c drivers/edac: upd... |
167 |
struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work); |
d4c1465b7 drivers/edac: fix... |
168 169 |
int msec; unsigned long delay; |
91b99041c drivers/edac: upd... |
170 |
|
956b9ba15 edac: Convert deb... |
171 172 |
edac_dbg(3, "checking "); |
91b99041c drivers/edac: upd... |
173 |
|
d4c1465b7 drivers/edac: fix... |
174 |
mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
175 |
|
06e912d4d EDAC: Cleanup/syn... |
176 177 178 |
if (pci->op_state != OP_RUNNING_POLL) { mutex_unlock(&edac_pci_ctls_mutex); return; |
d4c1465b7 drivers/edac: fix... |
179 |
} |
91b99041c drivers/edac: upd... |
180 |
|
06e912d4d EDAC: Cleanup/syn... |
181 182 183 184 185 186 187 188 189 190 191 |
if (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_relative(msecs_to_jiffies(msec)); else delay = msecs_to_jiffies(msec); edac_queue_work(&pci->work, delay); |
d4c1465b7 drivers/edac: fix... |
192 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
193 |
} |
8641a3845 edac: Add edac_pc... |
194 195 196 197 198 |
int edac_pci_alloc_index(void) { return atomic_inc_return(&pci_indexes) - 1; } EXPORT_SYMBOL_GPL(edac_pci_alloc_index); |
91b99041c drivers/edac: upd... |
199 200 |
int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx) { |
956b9ba15 edac: Convert deb... |
201 202 |
edac_dbg(0, " "); |
91b99041c drivers/edac: upd... |
203 204 |
pci->pci_idx = edac_idx; |
d4c1465b7 drivers/edac: fix... |
205 |
pci->start_time = jiffies; |
91b99041c drivers/edac: upd... |
206 |
|
d4c1465b7 drivers/edac: fix... |
207 |
mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
208 209 210 |
if (add_edac_pci_to_global_list(pci)) goto fail0; |
91b99041c drivers/edac: upd... |
211 212 213 214 215 216 |
if (edac_pci_create_sysfs(pci)) { edac_pci_printk(pci, KERN_WARNING, "failed to create sysfs pci "); goto fail1; } |
096676061 EDAC: Balance wor... |
217 |
if (pci->edac_check) { |
91b99041c drivers/edac: upd... |
218 |
pci->op_state = OP_RUNNING_POLL; |
626a7a4db EDAC: Kill workqu... |
219 220 |
INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function); edac_queue_work(&pci->work, msecs_to_jiffies(edac_pci_get_poll_msec())); |
91b99041c drivers/edac: upd... |
221 222 223 224 225 |
} else { pci->op_state = OP_RUNNING_INTERRUPT; } edac_pci_printk(pci, KERN_INFO, |
7270a6085 edac: Unify repor... |
226 227 228 229 |
"Giving out device to module %s controller %s: DEV %s (%s) ", pci->mod_name, pci->ctl_name, pci->dev_name, edac_op_state_to_string(pci->op_state)); |
91b99041c drivers/edac: upd... |
230 |
|
d4c1465b7 drivers/edac: fix... |
231 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
232 |
return 0; |
d4c1465b7 drivers/edac: fix... |
233 |
/* error unwind stack */ |
052dfb45c drivers/edac: cle... |
234 |
fail1: |
91b99041c drivers/edac: upd... |
235 |
del_edac_pci_from_global_list(pci); |
052dfb45c drivers/edac: cle... |
236 |
fail0: |
d4c1465b7 drivers/edac: fix... |
237 |
mutex_unlock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
238 239 240 |
return 1; } EXPORT_SYMBOL_GPL(edac_pci_add_device); |
079708b91 drivers/edac: cor... |
241 |
struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev) |
91b99041c drivers/edac: upd... |
242 243 |
{ struct edac_pci_ctl_info *pci; |
956b9ba15 edac: Convert deb... |
244 245 |
edac_dbg(0, " "); |
91b99041c drivers/edac: upd... |
246 |
|
d4c1465b7 drivers/edac: fix... |
247 |
mutex_lock(&edac_pci_ctls_mutex); |
91b99041c drivers/edac: upd... |
248 |
|
d4c1465b7 drivers/edac: fix... |
249 250 251 252 253 254 |
/* 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... |
255 256 257 258 |
return NULL; } pci->op_state = OP_OFFLINE; |
91b99041c drivers/edac: upd... |
259 |
del_edac_pci_from_global_list(pci); |
d4c1465b7 drivers/edac: fix... |
260 |
mutex_unlock(&edac_pci_ctls_mutex); |
096676061 EDAC: Balance wor... |
261 |
if (pci->edac_check) |
626a7a4db EDAC: Kill workqu... |
262 |
edac_stop_work(&pci->work); |
91b99041c drivers/edac: upd... |
263 264 |
edac_printk(KERN_INFO, EDAC_PCI, |
052dfb45c drivers/edac: cle... |
265 266 |
"Removed device %d for %s %s: DEV %s ", |
17aa7e034 dev_name introduc... |
267 |
pci->pci_idx, pci->mod_name, pci->ctl_name, edac_dev_name(pci)); |
91b99041c drivers/edac: upd... |
268 269 270 271 |
return pci; } EXPORT_SYMBOL_GPL(edac_pci_del_device); |
d4c1465b7 drivers/edac: fix... |
272 273 274 275 276 |
/* * edac_pci_generic_check * * a Generic parity check API */ |
1a45027d1 edac: remove unne... |
277 |
static void edac_pci_generic_check(struct edac_pci_ctl_info *pci) |
91b99041c drivers/edac: upd... |
278 |
{ |
956b9ba15 edac: Convert deb... |
279 280 |
edac_dbg(4, " "); |
91b99041c drivers/edac: upd... |
281 282 |
edac_pci_do_parity_check(); } |
d4c1465b7 drivers/edac: fix... |
283 |
/* free running instance index counter */ |
f044091ca drivers/edac: rem... |
284 |
static int edac_pci_idx; |
91b99041c drivers/edac: upd... |
285 286 287 288 289 |
#define EDAC_PCI_GENCTL_NAME "EDAC PCI controller" struct edac_pci_gen_data { int edac_idx; }; |
079708b91 drivers/edac: cor... |
290 |
struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, |
052dfb45c drivers/edac: cle... |
291 |
const char *mod_name) |
91b99041c drivers/edac: upd... |
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
{ 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; |
876bb331e EDAC: Respect ope... |
307 308 |
if (edac_op_state == EDAC_OPSTATE_POLL) pci->edac_check = edac_pci_generic_check; |
91b99041c drivers/edac: upd... |
309 310 311 312 |
pdata->edac_idx = edac_pci_idx++; if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { |
956b9ba15 edac: Convert deb... |
313 314 |
edac_dbg(3, "failed edac_pci_add_device() "); |
91b99041c drivers/edac: upd... |
315 316 317 318 319 320 321 322 323 324 |
edac_pci_free_ctl_info(pci); return NULL; } return pci; } EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl); void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci) { |
956b9ba15 edac: Convert deb... |
325 326 |
edac_dbg(0, "pci mod=%s ", pci->mod_name); |
d4c1465b7 drivers/edac: fix... |
327 |
|
91b99041c drivers/edac: upd... |
328 329 330 331 |
edac_pci_del_device(pci->dev); edac_pci_free_ctl_info(pci); } EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl); |