blob: 2f84f0d035bef968e70eb5541e1d249f7e1e1194 [file] [log] [blame]
Douglas Thompson7c9281d2007-07-19 01:49:33 -07001
2#include <linux/freezer.h>
3#include <linux/kthread.h>
Dave Jiangc0d12172007-07-19 01:49:46 -07004#include <linux/edac.h>
Douglas Thompson7c9281d2007-07-19 01:49:33 -07005
Douglas Thompson20bcb7a2007-07-19 01:49:47 -07006#include "edac_core.h"
Douglas Thompson7c9281d2007-07-19 01:49:33 -07007#include "edac_module.h"
8
Douglas Thompson20bcb7a2007-07-19 01:49:47 -07009#define EDAC_MC_VERSION "Ver: 2.0.4 " __DATE__
Douglas Thompson7c9281d2007-07-19 01:49:33 -070010
11#ifdef CONFIG_EDAC_DEBUG
12/* Values of 0 to 4 will generate output */
13int edac_debug_level = 1;
14EXPORT_SYMBOL_GPL(edac_debug_level);
15#endif
16
Douglas Thompsone27e3da2007-07-19 01:49:36 -070017/* scope is to module level only */
18struct workqueue_struct *edac_workqueue;
19
20/* private to this file */
Douglas Thompson7c9281d2007-07-19 01:49:33 -070021static struct task_struct *edac_thread;
22
Douglas Thompsone27e3da2007-07-19 01:49:36 -070023
24/*
25 * sysfs object: /sys/devices/system/edac
26 * need to export to other files in this modules
27 */
28static struct sysdev_class edac_class = {
29 set_kset_name("edac"),
30};
31static int edac_class_valid = 0;
32
33/*
34 * edac_get_edac_class()
35 *
36 * return pointer to the edac class of 'edac'
37 */
38struct sysdev_class *edac_get_edac_class(void)
39{
40 struct sysdev_class *classptr=NULL;
41
42 if (edac_class_valid)
43 classptr = &edac_class;
44
45 return classptr;
46}
47
48/*
49 * edac_register_sysfs_edac_name()
50 *
51 * register the 'edac' into /sys/devices/system
52 *
53 * return:
54 * 0 success
55 * !0 error
56 */
57static int edac_register_sysfs_edac_name(void)
58{
59 int err;
60
61 /* create the /sys/devices/system/edac directory */
62 err = sysdev_class_register(&edac_class);
63
64 if (err) {
65 debugf1("%s() error=%d\n", __func__, err);
66 return err;
67 }
68
69 edac_class_valid = 1;
70 return 0;
71}
72
73/*
74 * sysdev_class_unregister()
75 *
76 * unregister the 'edac' from /sys/devices/system
77 */
78static void edac_unregister_sysfs_edac_name(void)
79{
80 /* only if currently registered, then unregister it */
81 if (edac_class_valid)
82 sysdev_class_unregister(&edac_class);
83
84 edac_class_valid = 0;
85}
86
87
Douglas Thompson7c9281d2007-07-19 01:49:33 -070088/*
89 * Check MC status every edac_get_poll_msec().
90 * Check PCI status every edac_get_poll_msec() as well.
91 *
92 * This where the work gets done for edac.
93 *
94 * SMP safe, doesn't use NMI, and auto-rate-limits.
95 */
96static void do_edac_check(void)
97{
98 debugf3("%s()\n", __func__);
99
100 /* perform the poll activities */
101 edac_check_mc_devices();
102 edac_pci_do_parity_check();
103}
104
105/*
Dave Jiangc0d12172007-07-19 01:49:46 -0700106 * handler for EDAC to check if NMI type handler has asserted interrupt
107 */
108static int edac_assert_error_check_and_clear(void)
109{
110 int vreg;
111
112 if(edac_op_state == EDAC_OPSTATE_POLL)
113 return 1;
114
115 vreg = atomic_read(&edac_err_assert);
116 if(vreg) {
117 atomic_set(&edac_err_assert, 0);
118 return 1;
119 }
120
121 return 0;
122}
123
124/*
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700125 * Action thread for EDAC to perform the POLL operations
126 */
127static int edac_kernel_thread(void *arg)
128{
129 int msec;
130
131 while (!kthread_should_stop()) {
Dave Jiangc0d12172007-07-19 01:49:46 -0700132 if(edac_assert_error_check_and_clear())
133 do_edac_check();
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700134
135 /* goto sleep for the interval */
136 msec = (HZ * edac_get_poll_msec()) / 1000;
137 schedule_timeout_interruptible(msec);
138 try_to_freeze();
139 }
140
141 return 0;
142}
143
144/*
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700145 * edac_workqueue_setup
146 * initialize the edac work queue for polling operations
147 */
148static int edac_workqueue_setup(void)
149{
150 edac_workqueue = create_singlethread_workqueue("edac-poller");
151 if (edac_workqueue == NULL)
152 return -ENODEV;
153 else
154 return 0;
155}
156
157/*
158 * edac_workqueue_teardown
159 * teardown the edac workqueue
160 */
161static void edac_workqueue_teardown(void)
162{
163 if (edac_workqueue) {
164 flush_workqueue(edac_workqueue);
165 destroy_workqueue(edac_workqueue);
166 edac_workqueue = NULL;
167 }
168}
169
170
171/*
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700172 * edac_init
173 * module initialization entry point
174 */
175static int __init edac_init(void)
176{
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700177 int err = 0;
178
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700179 edac_printk(KERN_INFO, EDAC_MC, EDAC_MC_VERSION "\n");
180
181 /*
182 * Harvest and clear any boot/initialization PCI parity errors
183 *
184 * FIXME: This only clears errors logged by devices present at time of
185 * module initialization. We should also do an initial clear
186 * of each newly hotplugged device.
187 */
188 edac_pci_clear_parity_errors();
189
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700190 /*
191 * perform the registration of the /sys/devices/system/edac object
192 */
193 if (edac_register_sysfs_edac_name()) {
194 edac_printk(KERN_ERR, EDAC_MC,
195 "Error initializing 'edac' kobject\n");
196 err = -ENODEV;
197 goto error;
198 }
199
200 /* Create the MC sysfs entries, must be first
201 */
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700202 if (edac_sysfs_memctrl_setup()) {
203 edac_printk(KERN_ERR, EDAC_MC,
204 "Error initializing sysfs code\n");
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700205 err = -ENODEV;
206 goto error_sysfs;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700207 }
208
209 /* Create the PCI parity sysfs entries */
210 if (edac_sysfs_pci_setup()) {
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700211 edac_printk(KERN_ERR, EDAC_MC,
212 "PCI: Error initializing sysfs code\n");
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700213 err = -ENODEV;
214 goto error_mem;
215 }
216
217 /* Setup/Initialize the edac_device system */
218 err = edac_workqueue_setup();
219 if (err) {
220 edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n");
221 goto error_pci;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700222 }
223
224 /* create our kernel thread */
225 edac_thread = kthread_run(edac_kernel_thread, NULL, "kedac");
226
227 if (IS_ERR(edac_thread)) {
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700228 err = PTR_ERR(edac_thread);
229 goto error_work;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700230 }
231
232 return 0;
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700233
234 /* Error teardown stack */
235error_work:
236 edac_workqueue_teardown();
237error_pci:
238 edac_sysfs_pci_teardown();
239error_mem:
240 edac_sysfs_memctrl_teardown();
241error_sysfs:
242 edac_unregister_sysfs_edac_name();
243error:
244 return err;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700245}
246
247/*
248 * edac_exit()
249 * module exit/termination function
250 */
251static void __exit edac_exit(void)
252{
253 debugf0("%s()\n", __func__);
254 kthread_stop(edac_thread);
255
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700256 /* tear down the various subsystems*/
257 edac_workqueue_teardown();
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700258 edac_sysfs_memctrl_teardown();
259 edac_sysfs_pci_teardown();
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700260 edac_unregister_sysfs_edac_name();
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700261}
262
263/*
264 * Inform the kernel of our entry and exit points
265 */
266module_init(edac_init);
267module_exit(edac_exit);
268
269MODULE_LICENSE("GPL");
270MODULE_AUTHOR("Doug Thompson www.softwarebitmaker.com, et al");
271MODULE_DESCRIPTION("Core library routines for EDAC reporting");
272
273/* refer to *_sysfs.c files for parameters that are exported via sysfs */
274
275#ifdef CONFIG_EDAC_DEBUG
276module_param(edac_debug_level, int, 0644);
277MODULE_PARM_DESC(edac_debug_level, "Debug level");
278#endif
279