summaryrefslogtreecommitdiff
path: root/drivers/edac/edac_module.c
diff options
context:
space:
mode:
authorDouglas Thompson <dougthompson@xmission.com>2007-07-19 12:49:36 +0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-19 21:04:53 +0400
commite27e3dac651771fe3250f6305dee277bce29fc5d (patch)
tree9c0ac81a0948d8e52a72865ff9fbae4a12031a32 /drivers/edac/edac_module.c
parent7c9281d76c1c0b130f79d5fc021084e9749959d4 (diff)
downloadlinux-e27e3dac651771fe3250f6305dee277bce29fc5d.tar.xz
drivers/edac: add edac_device class
This patch adds the new 'class' of object to be managed, named: 'edac_device'. As a peer of the 'edac_mc' class of object, it provides a non-memory centric view of an ERROR DETECTING device in hardware. It provides a sysfs interface and an abstraction for varioius EDAC type devices. Multiple 'instances' within the class are possible, with each 'instance' able to have multiple 'blocks', and each 'block' having 'attributes'. At the 'block' level there are the 'ce_count' and 'ue_count' fields which the device driver can update and/or call edac_device_handle_XX() functions. At each higher level are additional 'total' count fields, which are a summation of counts below that level. This 'edac_device' has been used to capture and present ECC errors which are found in a a L1 and L2 system on a per CORE/CPU basis. Signed-off-by: Douglas 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_module.c')
-rw-r--r--drivers/edac/edac_module.c147
1 files changed, 138 insertions, 9 deletions
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c
index 8db0471a9476..3cd3a236821c 100644
--- a/drivers/edac/edac_module.c
+++ b/drivers/edac/edac_module.c
@@ -13,8 +13,77 @@ int edac_debug_level = 1;
EXPORT_SYMBOL_GPL(edac_debug_level);
#endif
+/* scope is to module level only */
+struct workqueue_struct *edac_workqueue;
+
+/* private to this file */
static struct task_struct *edac_thread;
+
+/*
+ * sysfs object: /sys/devices/system/edac
+ * need to export to other files in this modules
+ */
+static struct sysdev_class edac_class = {
+ set_kset_name("edac"),
+};
+static int edac_class_valid = 0;
+
+/*
+ * edac_get_edac_class()
+ *
+ * return pointer to the edac class of 'edac'
+ */
+struct sysdev_class *edac_get_edac_class(void)
+{
+ struct sysdev_class *classptr=NULL;
+
+ if (edac_class_valid)
+ classptr = &edac_class;
+
+ return classptr;
+}
+
+/*
+ * edac_register_sysfs_edac_name()
+ *
+ * register the 'edac' into /sys/devices/system
+ *
+ * return:
+ * 0 success
+ * !0 error
+ */
+static int edac_register_sysfs_edac_name(void)
+{
+ int err;
+
+ /* create the /sys/devices/system/edac directory */
+ err = sysdev_class_register(&edac_class);
+
+ if (err) {
+ debugf1("%s() error=%d\n", __func__, err);
+ return err;
+ }
+
+ edac_class_valid = 1;
+ return 0;
+}
+
+/*
+ * sysdev_class_unregister()
+ *
+ * unregister the 'edac' from /sys/devices/system
+ */
+static void edac_unregister_sysfs_edac_name(void)
+{
+ /* only if currently registered, then unregister it */
+ if (edac_class_valid)
+ sysdev_class_unregister(&edac_class);
+
+ edac_class_valid = 0;
+}
+
+
/*
* Check MC status every edac_get_poll_msec().
* Check PCI status every edac_get_poll_msec() as well.
@@ -53,11 +122,40 @@ static int edac_kernel_thread(void *arg)
}
/*
+ * edac_workqueue_setup
+ * initialize the edac work queue for polling operations
+ */
+static int edac_workqueue_setup(void)
+{
+ edac_workqueue = create_singlethread_workqueue("edac-poller");
+ if (edac_workqueue == NULL)
+ return -ENODEV;
+ else
+ return 0;
+}
+
+/*
+ * edac_workqueue_teardown
+ * teardown the edac workqueue
+ */
+static void edac_workqueue_teardown(void)
+{
+ if (edac_workqueue) {
+ flush_workqueue(edac_workqueue);
+ destroy_workqueue(edac_workqueue);
+ edac_workqueue = NULL;
+ }
+}
+
+
+/*
* edac_init
* module initialization entry point
*/
static int __init edac_init(void)
{
+ int err = 0;
+
edac_printk(KERN_INFO, EDAC_MC, EDAC_MC_VERSION "\n");
/*
@@ -69,32 +167,61 @@ static int __init edac_init(void)
*/
edac_pci_clear_parity_errors();
- /* Create the MC sysfs entries */
+ /*
+ * perform the registration of the /sys/devices/system/edac object
+ */
+ if (edac_register_sysfs_edac_name()) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Error initializing 'edac' kobject\n");
+ err = -ENODEV;
+ goto error;
+ }
+
+ /* Create the MC sysfs entries, must be first
+ */
if (edac_sysfs_memctrl_setup()) {
edac_printk(KERN_ERR, EDAC_MC,
"Error initializing sysfs code\n");
- return -ENODEV;
+ err = -ENODEV;
+ goto error_sysfs;
}
/* Create the PCI parity sysfs entries */
if (edac_sysfs_pci_setup()) {
- edac_sysfs_memctrl_teardown();
edac_printk(KERN_ERR, EDAC_MC,
"PCI: Error initializing sysfs code\n");
- return -ENODEV;
+ err = -ENODEV;
+ goto error_mem;
+ }
+
+ /* Setup/Initialize the edac_device system */
+ err = edac_workqueue_setup();
+ if (err) {
+ edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n");
+ goto error_pci;
}
/* create our kernel thread */
edac_thread = kthread_run(edac_kernel_thread, NULL, "kedac");
if (IS_ERR(edac_thread)) {
- /* remove the sysfs entries */
- edac_sysfs_memctrl_teardown();
- edac_sysfs_pci_teardown();
- return PTR_ERR(edac_thread);
+ err = PTR_ERR(edac_thread);
+ goto error_work;
}
return 0;
+
+ /* Error teardown stack */
+error_work:
+ edac_workqueue_teardown();
+error_pci:
+ edac_sysfs_pci_teardown();
+error_mem:
+ edac_sysfs_memctrl_teardown();
+error_sysfs:
+ edac_unregister_sysfs_edac_name();
+error:
+ return err;
}
/*
@@ -106,9 +233,11 @@ static void __exit edac_exit(void)
debugf0("%s()\n", __func__);
kthread_stop(edac_thread);
- /* tear down the sysfs device */
+ /* tear down the various subsystems*/
+ edac_workqueue_teardown();
edac_sysfs_memctrl_teardown();
edac_sysfs_pci_teardown();
+ edac_unregister_sysfs_edac_name();
}
/*