diff options
| author | Doug Thompson <dougthompson@xmission.com> | 2007-07-26 13:41:15 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-26 14:35:18 -0400 |
| commit | d4c1465b7de9686c4c5aa533b15c09ab014aab3a (patch) | |
| tree | 695434e881a3b395db782fe82e95eee2631b8a2e /drivers/edac/edac_pci.c | |
| parent | bce19683c17485b584b62b984d6dcf5332181588 (diff) | |
drivers/edac: fix edac_pci sysfs
This patch fixes sysfs exit code for the EDAC PCI device in a similiar manner
and the previous fixes for EDAC_MC and EDAC_DEVICE.
It removes the old (and incorrect) completion model and uses reference counts
on per instance kobjects and on the edac core module.
This pattern was applied to the edac_mc and edac_device code, but the EDAC PCI
code was missed. In addition, this fixes a system hang after a low level
driver was unloaded. (A cleanup function was called twice, which really
screwed things up)
Cc: Greg KH <greg@kroah.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Doug Thompson <dougthompson@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/edac/edac_pci.c')
| -rw-r--r-- | drivers/edac/edac_pci.c | 162 |
1 files changed, 111 insertions, 51 deletions
diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c index d9cd5e048cee..5dee9f50414b 100644 --- a/drivers/edac/edac_pci.c +++ b/drivers/edac/edac_pci.c | |||
| @@ -31,20 +31,12 @@ | |||
| 31 | static DEFINE_MUTEX(edac_pci_ctls_mutex); | 31 | static DEFINE_MUTEX(edac_pci_ctls_mutex); |
| 32 | static struct list_head edac_pci_list = LIST_HEAD_INIT(edac_pci_list); | 32 | static struct list_head edac_pci_list = LIST_HEAD_INIT(edac_pci_list); |
| 33 | 33 | ||
| 34 | static inline void edac_lock_pci_list(void) | ||
| 35 | { | ||
| 36 | mutex_lock(&edac_pci_ctls_mutex); | ||
| 37 | } | ||
| 38 | |||
| 39 | static inline void edac_unlock_pci_list(void) | ||
| 40 | { | ||
| 41 | mutex_unlock(&edac_pci_ctls_mutex); | ||
| 42 | } | ||
| 43 | |||
| 44 | /* | 34 | /* |
| 45 | * The alloc() and free() functions for the 'edac_pci' control info | 35 | * edac_pci_alloc_ctl_info |
| 46 | * structure. The chip driver will allocate one of these for each | 36 | * |
| 47 | * edac_pci it is going to control/register with the EDAC CORE. | 37 | * The alloc() function for the 'edac_pci' control info |
| 38 | * structure. The chip driver will allocate one of these for each | ||
| 39 | * edac_pci it is going to control/register with the EDAC CORE. | ||
| 48 | */ | 40 | */ |
| 49 | struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, | 41 | struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, |
| 50 | const char *edac_pci_name) | 42 | const char *edac_pci_name) |
| @@ -53,47 +45,59 @@ struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, | |||
| 53 | void *pvt; | 45 | void *pvt; |
| 54 | unsigned int size; | 46 | unsigned int size; |
| 55 | 47 | ||
| 48 | debugf1("%s()\n", __func__); | ||
| 49 | |||
| 56 | pci = (struct edac_pci_ctl_info *)0; | 50 | pci = (struct edac_pci_ctl_info *)0; |
| 57 | pvt = edac_align_ptr(&pci[1], sz_pvt); | 51 | pvt = edac_align_ptr(&pci[1], sz_pvt); |
| 58 | size = ((unsigned long)pvt) + sz_pvt; | 52 | size = ((unsigned long)pvt) + sz_pvt; |
| 59 | 53 | ||
| 60 | if ((pci = kzalloc(size, GFP_KERNEL)) == NULL) | 54 | /* Alloc the needed control struct memory */ |
| 55 | pci = kzalloc(size, GFP_KERNEL); | ||
| 56 | if (pci == NULL) | ||
| 61 | return NULL; | 57 | return NULL; |
| 62 | 58 | ||
| 59 | /* Now much private space */ | ||
| 63 | pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL; | 60 | pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL; |
| 64 | 61 | ||
| 65 | pci->pvt_info = pvt; | 62 | pci->pvt_info = pvt; |
| 66 | |||
| 67 | pci->op_state = OP_ALLOC; | 63 | pci->op_state = OP_ALLOC; |
| 68 | 64 | ||
| 69 | snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name); | 65 | snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name); |
| 70 | 66 | ||
| 71 | return pci; | 67 | return pci; |
| 72 | } | 68 | } |
| 73 | |||
| 74 | EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info); | 69 | EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info); |
| 75 | 70 | ||
| 76 | /* | 71 | /* |
| 77 | * edac_pci_free_ctl_info() | 72 | * edac_pci_free_ctl_info() |
| 78 | * frees the memory allocated by edac_pci_alloc_ctl_info() function | 73 | * |
| 74 | * Last action on the pci control structure. | ||
| 75 | * | ||
| 76 | * call the remove sysfs informaton, which will unregister | ||
| 77 | * this control struct's kobj. When that kobj's ref count | ||
| 78 | * goes to zero, its release function will be call and then | ||
| 79 | * kfree() the memory. | ||
| 79 | */ | 80 | */ |
| 80 | void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci) | 81 | void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci) |
| 81 | { | 82 | { |
| 82 | kfree(pci); | 83 | debugf1("%s()\n", __func__); |
| 83 | } | ||
| 84 | 84 | ||
| 85 | edac_pci_remove_sysfs(pci); | ||
| 86 | } | ||
| 85 | EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info); | 87 | EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info); |
| 86 | 88 | ||
| 87 | /* | 89 | /* |
| 88 | * find_edac_pci_by_dev() | 90 | * find_edac_pci_by_dev() |
| 89 | * scans the edac_pci list for a specific 'struct device *' | 91 | * scans the edac_pci list for a specific 'struct device *' |
| 92 | * | ||
| 93 | * return NULL if not found, or return control struct pointer | ||
| 90 | */ | 94 | */ |
| 91 | static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev) | 95 | static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev) |
| 92 | { | 96 | { |
| 93 | struct edac_pci_ctl_info *pci; | 97 | struct edac_pci_ctl_info *pci; |
| 94 | struct list_head *item; | 98 | struct list_head *item; |
| 95 | 99 | ||
| 96 | debugf3("%s()\n", __func__); | 100 | debugf1("%s()\n", __func__); |
| 97 | 101 | ||
| 98 | list_for_each(item, &edac_pci_list) { | 102 | list_for_each(item, &edac_pci_list) { |
| 99 | pci = list_entry(item, struct edac_pci_ctl_info, link); | 103 | pci = list_entry(item, struct edac_pci_ctl_info, link); |
| @@ -118,10 +122,13 @@ static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci) | |||
| 118 | struct list_head *item, *insert_before; | 122 | struct list_head *item, *insert_before; |
| 119 | struct edac_pci_ctl_info *rover; | 123 | struct edac_pci_ctl_info *rover; |
| 120 | 124 | ||
| 125 | debugf1("%s()\n", __func__); | ||
| 126 | |||
| 121 | insert_before = &edac_pci_list; | 127 | insert_before = &edac_pci_list; |
| 122 | 128 | ||
| 123 | /* Determine if already on the list */ | 129 | /* Determine if already on the list */ |
| 124 | if (unlikely((rover = find_edac_pci_by_dev(pci->dev)) != NULL)) | 130 | rover = find_edac_pci_by_dev(pci->dev); |
| 131 | if (unlikely(rover != NULL)) | ||
| 125 | goto fail0; | 132 | goto fail0; |
| 126 | 133 | ||
| 127 | /* Insert in ascending order by 'pci_idx', so find position */ | 134 | /* Insert in ascending order by 'pci_idx', so find position */ |
| @@ -157,6 +164,8 @@ fail1: | |||
| 157 | 164 | ||
| 158 | /* | 165 | /* |
| 159 | * complete_edac_pci_list_del | 166 | * complete_edac_pci_list_del |
| 167 | * | ||
| 168 | * RCU completion callback to indicate item is deleted | ||
| 160 | */ | 169 | */ |
| 161 | static void complete_edac_pci_list_del(struct rcu_head *head) | 170 | static void complete_edac_pci_list_del(struct rcu_head *head) |
| 162 | { | 171 | { |
| @@ -169,6 +178,8 @@ static void complete_edac_pci_list_del(struct rcu_head *head) | |||
| 169 | 178 | ||
| 170 | /* | 179 | /* |
| 171 | * del_edac_pci_from_global_list | 180 | * del_edac_pci_from_global_list |
| 181 | * | ||
| 182 | * remove the PCI control struct from the global list | ||
| 172 | */ | 183 | */ |
| 173 | static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) | 184 | static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) |
| 174 | { | 185 | { |
| @@ -207,35 +218,52 @@ struct edac_pci_ctl_info *edac_pci_find(int idx) | |||
| 207 | 218 | ||
| 208 | return NULL; | 219 | return NULL; |
| 209 | } | 220 | } |
| 210 | |||
| 211 | EXPORT_SYMBOL_GPL(edac_pci_find); | 221 | EXPORT_SYMBOL_GPL(edac_pci_find); |
| 212 | 222 | ||
| 213 | /* | 223 | /* |
| 214 | * edac_pci_workq_function() | 224 | * edac_pci_workq_function() |
| 215 | * performs the operation scheduled by a workq request | 225 | * |
| 226 | * periodic function that performs the operation | ||
| 227 | * scheduled by a workq request, for a given PCI control struct | ||
| 216 | */ | 228 | */ |
| 217 | static void edac_pci_workq_function(struct work_struct *work_req) | 229 | static void edac_pci_workq_function(struct work_struct *work_req) |
| 218 | { | 230 | { |
| 219 | struct delayed_work *d_work = (struct delayed_work *)work_req; | 231 | struct delayed_work *d_work = (struct delayed_work *)work_req; |
| 220 | struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work); | 232 | struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work); |
| 233 | int msec; | ||
| 234 | unsigned long delay; | ||
| 221 | 235 | ||
| 222 | edac_lock_pci_list(); | 236 | debugf3("%s() checking\n", __func__); |
| 223 | 237 | ||
| 224 | if ((pci->op_state == OP_RUNNING_POLL) && | 238 | mutex_lock(&edac_pci_ctls_mutex); |
| 225 | (pci->edac_check != NULL) && (edac_pci_get_check_errors())) | ||
| 226 | pci->edac_check(pci); | ||
| 227 | 239 | ||
| 228 | edac_unlock_pci_list(); | 240 | if (pci->op_state == OP_RUNNING_POLL) { |
| 241 | /* we might be in POLL mode, but there may NOT be a poll func | ||
| 242 | */ | ||
| 243 | if ((pci->edac_check != NULL) && edac_pci_get_check_errors()) | ||
| 244 | pci->edac_check(pci); | ||
| 245 | |||
| 246 | /* if we are on a one second period, then use round */ | ||
| 247 | msec = edac_pci_get_poll_msec(); | ||
| 248 | if (msec == 1000) | ||
| 249 | delay = round_jiffies(msecs_to_jiffies(msec)); | ||
| 250 | else | ||
| 251 | delay = msecs_to_jiffies(msec); | ||
| 252 | |||
| 253 | /* Reschedule only if we are in POLL mode */ | ||
| 254 | queue_delayed_work(edac_workqueue, &pci->work, delay); | ||
| 255 | } | ||
| 229 | 256 | ||
| 230 | /* Reschedule */ | 257 | mutex_unlock(&edac_pci_ctls_mutex); |
| 231 | queue_delayed_work(edac_workqueue, &pci->work, | ||
| 232 | msecs_to_jiffies(edac_pci_get_poll_msec())); | ||
| 233 | } | 258 | } |
| 234 | 259 | ||
| 235 | /* | 260 | /* |
| 236 | * edac_pci_workq_setup() | 261 | * edac_pci_workq_setup() |
| 237 | * initialize a workq item for this edac_pci instance | 262 | * initialize a workq item for this edac_pci instance |
| 238 | * passing in the new delay period in msec | 263 | * passing in the new delay period in msec |
| 264 | * | ||
| 265 | * locking model: | ||
| 266 | * called when 'edac_pci_ctls_mutex' is locked | ||
| 239 | */ | 267 | */ |
| 240 | static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci, | 268 | static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci, |
| 241 | unsigned int msec) | 269 | unsigned int msec) |
| @@ -255,6 +283,8 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci) | |||
| 255 | { | 283 | { |
| 256 | int status; | 284 | int status; |
| 257 | 285 | ||
| 286 | debugf0("%s()\n", __func__); | ||
| 287 | |||
| 258 | status = cancel_delayed_work(&pci->work); | 288 | status = cancel_delayed_work(&pci->work); |
| 259 | if (status == 0) | 289 | if (status == 0) |
| 260 | flush_workqueue(edac_workqueue); | 290 | flush_workqueue(edac_workqueue); |
| @@ -262,19 +292,25 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci) | |||
| 262 | 292 | ||
| 263 | /* | 293 | /* |
| 264 | * edac_pci_reset_delay_period | 294 | * edac_pci_reset_delay_period |
| 295 | * | ||
| 296 | * called with a new period value for the workq period | ||
| 297 | * a) stop current workq timer | ||
| 298 | * b) restart workq timer with new value | ||
| 265 | */ | 299 | */ |
| 266 | void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci, | 300 | void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci, |
| 267 | unsigned long value) | 301 | unsigned long value) |
| 268 | { | 302 | { |
| 269 | edac_lock_pci_list(); | 303 | debugf0("%s()\n", __func__); |
| 270 | 304 | ||
| 271 | edac_pci_workq_teardown(pci); | 305 | edac_pci_workq_teardown(pci); |
| 272 | 306 | ||
| 307 | /* need to lock for the setup */ | ||
| 308 | mutex_lock(&edac_pci_ctls_mutex); | ||
| 309 | |||
| 273 | edac_pci_workq_setup(pci, value); | 310 | edac_pci_workq_setup(pci, value); |
| 274 | 311 | ||
| 275 | edac_unlock_pci_list(); | 312 | mutex_unlock(&edac_pci_ctls_mutex); |
| 276 | } | 313 | } |
| 277 | |||
| 278 | EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period); | 314 | EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period); |
| 279 | 315 | ||
| 280 | /* | 316 | /* |
| @@ -294,14 +330,13 @@ int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx) | |||
| 294 | debugf0("%s()\n", __func__); | 330 | debugf0("%s()\n", __func__); |
| 295 | 331 | ||
| 296 | pci->pci_idx = edac_idx; | 332 | pci->pci_idx = edac_idx; |
| 333 | pci->start_time = jiffies; | ||
| 297 | 334 | ||
| 298 | edac_lock_pci_list(); | 335 | mutex_lock(&edac_pci_ctls_mutex); |
| 299 | 336 | ||
| 300 | if (add_edac_pci_to_global_list(pci)) | 337 | if (add_edac_pci_to_global_list(pci)) |
| 301 | goto fail0; | 338 | goto fail0; |
| 302 | 339 | ||
| 303 | pci->start_time = jiffies; | ||
| 304 | |||
| 305 | if (edac_pci_create_sysfs(pci)) { | 340 | if (edac_pci_create_sysfs(pci)) { |
| 306 | edac_pci_printk(pci, KERN_WARNING, | 341 | edac_pci_printk(pci, KERN_WARNING, |
| 307 | "failed to create sysfs pci\n"); | 342 | "failed to create sysfs pci\n"); |
| @@ -323,16 +358,16 @@ int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx) | |||
| 323 | pci->ctl_name, | 358 | pci->ctl_name, |
| 324 | dev_name(pci), edac_op_state_to_string(pci->op_state)); | 359 | dev_name(pci), edac_op_state_to_string(pci->op_state)); |
| 325 | 360 | ||
| 326 | edac_unlock_pci_list(); | 361 | mutex_unlock(&edac_pci_ctls_mutex); |
| 327 | return 0; | 362 | return 0; |
| 328 | 363 | ||
| 364 | /* error unwind stack */ | ||
| 329 | fail1: | 365 | fail1: |
| 330 | del_edac_pci_from_global_list(pci); | 366 | del_edac_pci_from_global_list(pci); |
| 331 | fail0: | 367 | fail0: |
| 332 | edac_unlock_pci_list(); | 368 | mutex_unlock(&edac_pci_ctls_mutex); |
| 333 | return 1; | 369 | return 1; |
| 334 | } | 370 | } |
| 335 | |||
| 336 | EXPORT_SYMBOL_GPL(edac_pci_add_device); | 371 | EXPORT_SYMBOL_GPL(edac_pci_add_device); |
| 337 | 372 | ||
| 338 | /* | 373 | /* |
| @@ -354,22 +389,25 @@ struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev) | |||
| 354 | 389 | ||
| 355 | debugf0("%s()\n", __func__); | 390 | debugf0("%s()\n", __func__); |
| 356 | 391 | ||
| 357 | edac_lock_pci_list(); | 392 | mutex_lock(&edac_pci_ctls_mutex); |
| 358 | 393 | ||
| 359 | if ((pci = find_edac_pci_by_dev(dev)) == NULL) { | 394 | /* ensure the control struct is on the global list |
| 360 | edac_unlock_pci_list(); | 395 | * if not, then leave |
| 396 | */ | ||
| 397 | pci = find_edac_pci_by_dev(dev); | ||
| 398 | if (pci == NULL) { | ||
| 399 | mutex_unlock(&edac_pci_ctls_mutex); | ||
| 361 | return NULL; | 400 | return NULL; |
| 362 | } | 401 | } |
| 363 | 402 | ||
| 364 | pci->op_state = OP_OFFLINE; | 403 | pci->op_state = OP_OFFLINE; |
| 365 | 404 | ||
| 366 | edac_pci_workq_teardown(pci); | ||
| 367 | |||
| 368 | edac_pci_remove_sysfs(pci); | ||
| 369 | |||
| 370 | del_edac_pci_from_global_list(pci); | 405 | del_edac_pci_from_global_list(pci); |
| 371 | 406 | ||
| 372 | edac_unlock_pci_list(); | 407 | mutex_unlock(&edac_pci_ctls_mutex); |
| 408 | |||
| 409 | /* stop the workq timer */ | ||
| 410 | edac_pci_workq_teardown(pci); | ||
| 373 | 411 | ||
| 374 | edac_printk(KERN_INFO, EDAC_PCI, | 412 | edac_printk(KERN_INFO, EDAC_PCI, |
| 375 | "Removed device %d for %s %s: DEV %s\n", | 413 | "Removed device %d for %s %s: DEV %s\n", |
| @@ -377,14 +415,20 @@ struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev) | |||
| 377 | 415 | ||
| 378 | return pci; | 416 | return pci; |
| 379 | } | 417 | } |
| 380 | |||
| 381 | EXPORT_SYMBOL_GPL(edac_pci_del_device); | 418 | EXPORT_SYMBOL_GPL(edac_pci_del_device); |
| 382 | 419 | ||
| 420 | /* | ||
| 421 | * edac_pci_generic_check | ||
| 422 | * | ||
| 423 | * a Generic parity check API | ||
| 424 | */ | ||
| 383 | void edac_pci_generic_check(struct edac_pci_ctl_info *pci) | 425 | void edac_pci_generic_check(struct edac_pci_ctl_info *pci) |
| 384 | { | 426 | { |
| 427 | debugf4("%s()\n", __func__); | ||
| 385 | edac_pci_do_parity_check(); | 428 | edac_pci_do_parity_check(); |
| 386 | } | 429 | } |
| 387 | 430 | ||
| 431 | /* free running instance index counter */ | ||
| 388 | static int edac_pci_idx; | 432 | static int edac_pci_idx; |
| 389 | #define EDAC_PCI_GENCTL_NAME "EDAC PCI controller" | 433 | #define EDAC_PCI_GENCTL_NAME "EDAC PCI controller" |
| 390 | 434 | ||
| @@ -392,6 +436,17 @@ struct edac_pci_gen_data { | |||
| 392 | int edac_idx; | 436 | int edac_idx; |
| 393 | }; | 437 | }; |
| 394 | 438 | ||
| 439 | /* | ||
| 440 | * edac_pci_create_generic_ctl | ||
| 441 | * | ||
| 442 | * A generic constructor for a PCI parity polling device | ||
| 443 | * Some systems have more than one domain of PCI busses. | ||
| 444 | * For systems with one domain, then this API will | ||
| 445 | * provide for a generic poller. | ||
| 446 | * | ||
| 447 | * This routine calls the edac_pci_alloc_ctl_info() for | ||
| 448 | * the generic device, with default values | ||
| 449 | */ | ||
| 395 | struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, | 450 | struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, |
| 396 | const char *mod_name) | 451 | const char *mod_name) |
| 397 | { | 452 | { |
| @@ -421,13 +476,18 @@ struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, | |||
| 421 | 476 | ||
| 422 | return pci; | 477 | return pci; |
| 423 | } | 478 | } |
| 424 | |||
| 425 | EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl); | 479 | EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl); |
| 426 | 480 | ||
| 481 | /* | ||
| 482 | * edac_pci_release_generic_ctl | ||
| 483 | * | ||
| 484 | * The release function of a generic EDAC PCI polling device | ||
| 485 | */ | ||
| 427 | void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci) | 486 | void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci) |
| 428 | { | 487 | { |
| 488 | debugf0("%s() pci mod=%s\n", __func__, pci->mod_name); | ||
| 489 | |||
| 429 | edac_pci_del_device(pci->dev); | 490 | edac_pci_del_device(pci->dev); |
| 430 | edac_pci_free_ctl_info(pci); | 491 | edac_pci_free_ctl_info(pci); |
| 431 | } | 492 | } |
| 432 | |||
| 433 | EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl); | 493 | EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl); |
