diff options
Diffstat (limited to 'drivers/edac/edac_device.c')
-rw-r--r-- | drivers/edac/edac_device.c | 37 |
1 files changed, 28 insertions, 9 deletions
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index 6020da68cbef..173f4ba0f7c8 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c | |||
@@ -48,6 +48,7 @@ static void edac_device_dump_device(struct edac_device_ctl_info *edac_dev) | |||
48 | } | 48 | } |
49 | #endif /* CONFIG_EDAC_DEBUG */ | 49 | #endif /* CONFIG_EDAC_DEBUG */ |
50 | 50 | ||
51 | |||
51 | /* | 52 | /* |
52 | * edac_device_alloc_ctl_info() | 53 | * edac_device_alloc_ctl_info() |
53 | * Allocate a new edac device control info structure | 54 | * Allocate a new edac device control info structure |
@@ -78,6 +79,7 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( | |||
78 | unsigned count; | 79 | unsigned count; |
79 | unsigned instance, block, attr; | 80 | unsigned instance, block, attr; |
80 | void *pvt; | 81 | void *pvt; |
82 | int err; | ||
81 | 83 | ||
82 | debugf1("%s() instances=%d blocks=%d\n", | 84 | debugf1("%s() instances=%d blocks=%d\n", |
83 | __func__, nr_instances, nr_blocks); | 85 | __func__, nr_instances, nr_blocks); |
@@ -208,6 +210,22 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( | |||
208 | /* Mark this instance as merely ALLOCATED */ | 210 | /* Mark this instance as merely ALLOCATED */ |
209 | dev_ctl->op_state = OP_ALLOC; | 211 | dev_ctl->op_state = OP_ALLOC; |
210 | 212 | ||
213 | /* | ||
214 | * Initialize the 'root' kobj for the edac_device controller | ||
215 | */ | ||
216 | err = edac_device_register_sysfs_main_kobj(dev_ctl); | ||
217 | if (err) { | ||
218 | kfree(dev_ctl); | ||
219 | return NULL; | ||
220 | } | ||
221 | |||
222 | /* at this point, the root kobj is valid, and in order to | ||
223 | * 'free' the object, then the function: | ||
224 | * edac_device_unregister_sysfs_main_kobj() must be called | ||
225 | * which will perform kobj unregistration and the actual free | ||
226 | * will occur during the kobject callback operation | ||
227 | */ | ||
228 | |||
211 | return dev_ctl; | 229 | return dev_ctl; |
212 | } | 230 | } |
213 | EXPORT_SYMBOL_GPL(edac_device_alloc_ctl_info); | 231 | EXPORT_SYMBOL_GPL(edac_device_alloc_ctl_info); |
@@ -219,7 +237,7 @@ EXPORT_SYMBOL_GPL(edac_device_alloc_ctl_info); | |||
219 | */ | 237 | */ |
220 | void edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info) | 238 | void edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info) |
221 | { | 239 | { |
222 | kfree(ctl_info); | 240 | edac_device_unregister_sysfs_main_kobj(ctl_info); |
223 | } | 241 | } |
224 | EXPORT_SYMBOL_GPL(edac_device_free_ctl_info); | 242 | EXPORT_SYMBOL_GPL(edac_device_free_ctl_info); |
225 | 243 | ||
@@ -315,22 +333,23 @@ static void complete_edac_device_list_del(struct rcu_head *head) | |||
315 | 333 | ||
316 | edac_dev = container_of(head, struct edac_device_ctl_info, rcu); | 334 | edac_dev = container_of(head, struct edac_device_ctl_info, rcu); |
317 | INIT_LIST_HEAD(&edac_dev->link); | 335 | INIT_LIST_HEAD(&edac_dev->link); |
318 | complete(&edac_dev->complete); | 336 | complete(&edac_dev->removal_complete); |
319 | } | 337 | } |
320 | 338 | ||
321 | /* | 339 | /* |
322 | * del_edac_device_from_global_list | 340 | * del_edac_device_from_global_list |
323 | * | 341 | * |
324 | * remove the RCU, setup for a callback call, then wait for the | 342 | * remove the RCU, setup for a callback call, |
325 | * callback to occur | 343 | * then wait for the callback to occur |
326 | */ | 344 | */ |
327 | static void del_edac_device_from_global_list(struct edac_device_ctl_info | 345 | static void del_edac_device_from_global_list(struct edac_device_ctl_info |
328 | *edac_device) | 346 | *edac_device) |
329 | { | 347 | { |
330 | list_del_rcu(&edac_device->link); | 348 | list_del_rcu(&edac_device->link); |
331 | init_completion(&edac_device->complete); | 349 | |
350 | init_completion(&edac_device->removal_complete); | ||
332 | call_rcu(&edac_device->rcu, complete_edac_device_list_del); | 351 | call_rcu(&edac_device->rcu, complete_edac_device_list_del); |
333 | wait_for_completion(&edac_device->complete); | 352 | wait_for_completion(&edac_device->removal_complete); |
334 | } | 353 | } |
335 | 354 | ||
336 | /** | 355 | /** |
@@ -542,14 +561,14 @@ struct edac_device_ctl_info *edac_device_del_device(struct device *dev) | |||
542 | /* clear workq processing on this instance */ | 561 | /* clear workq processing on this instance */ |
543 | edac_device_workq_teardown(edac_dev); | 562 | edac_device_workq_teardown(edac_dev); |
544 | 563 | ||
545 | /* Tear down the sysfs entries for this instance */ | ||
546 | edac_device_remove_sysfs(edac_dev); | ||
547 | |||
548 | /* deregister from global list */ | 564 | /* deregister from global list */ |
549 | del_edac_device_from_global_list(edac_dev); | 565 | del_edac_device_from_global_list(edac_dev); |
550 | 566 | ||
551 | mutex_unlock(&device_ctls_mutex); | 567 | mutex_unlock(&device_ctls_mutex); |
552 | 568 | ||
569 | /* Tear down the sysfs entries for this instance */ | ||
570 | edac_device_remove_sysfs(edac_dev); | ||
571 | |||
553 | edac_printk(KERN_INFO, EDAC_MC, | 572 | edac_printk(KERN_INFO, EDAC_MC, |
554 | "Removed device %d for %s %s: DEV %s\n", | 573 | "Removed device %d for %s %s: DEV %s\n", |
555 | edac_dev->dev_idx, | 574 | edac_dev->dev_idx, |