diff options
Diffstat (limited to 'drivers/edac/edac_module.c')
-rw-r--r-- | drivers/edac/edac_module.c | 147 |
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; | |||
13 | EXPORT_SYMBOL_GPL(edac_debug_level); | 13 | EXPORT_SYMBOL_GPL(edac_debug_level); |
14 | #endif | 14 | #endif |
15 | 15 | ||
16 | /* scope is to module level only */ | ||
17 | struct workqueue_struct *edac_workqueue; | ||
18 | |||
19 | /* private to this file */ | ||
16 | static struct task_struct *edac_thread; | 20 | static struct task_struct *edac_thread; |
17 | 21 | ||
22 | |||
23 | /* | ||
24 | * sysfs object: /sys/devices/system/edac | ||
25 | * need to export to other files in this modules | ||
26 | */ | ||
27 | static struct sysdev_class edac_class = { | ||
28 | set_kset_name("edac"), | ||
29 | }; | ||
30 | static int edac_class_valid = 0; | ||
31 | |||
32 | /* | ||
33 | * edac_get_edac_class() | ||
34 | * | ||
35 | * return pointer to the edac class of 'edac' | ||
36 | */ | ||
37 | struct sysdev_class *edac_get_edac_class(void) | ||
38 | { | ||
39 | struct sysdev_class *classptr=NULL; | ||
40 | |||
41 | if (edac_class_valid) | ||
42 | classptr = &edac_class; | ||
43 | |||
44 | return classptr; | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * edac_register_sysfs_edac_name() | ||
49 | * | ||
50 | * register the 'edac' into /sys/devices/system | ||
51 | * | ||
52 | * return: | ||
53 | * 0 success | ||
54 | * !0 error | ||
55 | */ | ||
56 | static int edac_register_sysfs_edac_name(void) | ||
57 | { | ||
58 | int err; | ||
59 | |||
60 | /* create the /sys/devices/system/edac directory */ | ||
61 | err = sysdev_class_register(&edac_class); | ||
62 | |||
63 | if (err) { | ||
64 | debugf1("%s() error=%d\n", __func__, err); | ||
65 | return err; | ||
66 | } | ||
67 | |||
68 | edac_class_valid = 1; | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * sysdev_class_unregister() | ||
74 | * | ||
75 | * unregister the 'edac' from /sys/devices/system | ||
76 | */ | ||
77 | static void edac_unregister_sysfs_edac_name(void) | ||
78 | { | ||
79 | /* only if currently registered, then unregister it */ | ||
80 | if (edac_class_valid) | ||
81 | sysdev_class_unregister(&edac_class); | ||
82 | |||
83 | edac_class_valid = 0; | ||
84 | } | ||
85 | |||
86 | |||
18 | /* | 87 | /* |
19 | * Check MC status every edac_get_poll_msec(). | 88 | * Check MC status every edac_get_poll_msec(). |
20 | * Check PCI status every edac_get_poll_msec() as well. | 89 | * Check PCI status every edac_get_poll_msec() as well. |
@@ -53,11 +122,40 @@ static int edac_kernel_thread(void *arg) | |||
53 | } | 122 | } |
54 | 123 | ||
55 | /* | 124 | /* |
125 | * edac_workqueue_setup | ||
126 | * initialize the edac work queue for polling operations | ||
127 | */ | ||
128 | static int edac_workqueue_setup(void) | ||
129 | { | ||
130 | edac_workqueue = create_singlethread_workqueue("edac-poller"); | ||
131 | if (edac_workqueue == NULL) | ||
132 | return -ENODEV; | ||
133 | else | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * edac_workqueue_teardown | ||
139 | * teardown the edac workqueue | ||
140 | */ | ||
141 | static void edac_workqueue_teardown(void) | ||
142 | { | ||
143 | if (edac_workqueue) { | ||
144 | flush_workqueue(edac_workqueue); | ||
145 | destroy_workqueue(edac_workqueue); | ||
146 | edac_workqueue = NULL; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | |||
151 | /* | ||
56 | * edac_init | 152 | * edac_init |
57 | * module initialization entry point | 153 | * module initialization entry point |
58 | */ | 154 | */ |
59 | static int __init edac_init(void) | 155 | static int __init edac_init(void) |
60 | { | 156 | { |
157 | int err = 0; | ||
158 | |||
61 | edac_printk(KERN_INFO, EDAC_MC, EDAC_MC_VERSION "\n"); | 159 | edac_printk(KERN_INFO, EDAC_MC, EDAC_MC_VERSION "\n"); |
62 | 160 | ||
63 | /* | 161 | /* |
@@ -69,32 +167,61 @@ static int __init edac_init(void) | |||
69 | */ | 167 | */ |
70 | edac_pci_clear_parity_errors(); | 168 | edac_pci_clear_parity_errors(); |
71 | 169 | ||
72 | /* Create the MC sysfs entries */ | 170 | /* |
171 | * perform the registration of the /sys/devices/system/edac object | ||
172 | */ | ||
173 | if (edac_register_sysfs_edac_name()) { | ||
174 | edac_printk(KERN_ERR, EDAC_MC, | ||
175 | "Error initializing 'edac' kobject\n"); | ||
176 | err = -ENODEV; | ||
177 | goto error; | ||
178 | } | ||
179 | |||
180 | /* Create the MC sysfs entries, must be first | ||
181 | */ | ||
73 | if (edac_sysfs_memctrl_setup()) { | 182 | if (edac_sysfs_memctrl_setup()) { |
74 | edac_printk(KERN_ERR, EDAC_MC, | 183 | edac_printk(KERN_ERR, EDAC_MC, |
75 | "Error initializing sysfs code\n"); | 184 | "Error initializing sysfs code\n"); |
76 | return -ENODEV; | 185 | err = -ENODEV; |
186 | goto error_sysfs; | ||
77 | } | 187 | } |
78 | 188 | ||
79 | /* Create the PCI parity sysfs entries */ | 189 | /* Create the PCI parity sysfs entries */ |
80 | if (edac_sysfs_pci_setup()) { | 190 | if (edac_sysfs_pci_setup()) { |
81 | edac_sysfs_memctrl_teardown(); | ||
82 | edac_printk(KERN_ERR, EDAC_MC, | 191 | edac_printk(KERN_ERR, EDAC_MC, |
83 | "PCI: Error initializing sysfs code\n"); | 192 | "PCI: Error initializing sysfs code\n"); |
84 | return -ENODEV; | 193 | err = -ENODEV; |
194 | goto error_mem; | ||
195 | } | ||
196 | |||
197 | /* Setup/Initialize the edac_device system */ | ||
198 | err = edac_workqueue_setup(); | ||
199 | if (err) { | ||
200 | edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); | ||
201 | goto error_pci; | ||
85 | } | 202 | } |
86 | 203 | ||
87 | /* create our kernel thread */ | 204 | /* create our kernel thread */ |
88 | edac_thread = kthread_run(edac_kernel_thread, NULL, "kedac"); | 205 | edac_thread = kthread_run(edac_kernel_thread, NULL, "kedac"); |
89 | 206 | ||
90 | if (IS_ERR(edac_thread)) { | 207 | if (IS_ERR(edac_thread)) { |
91 | /* remove the sysfs entries */ | 208 | err = PTR_ERR(edac_thread); |
92 | edac_sysfs_memctrl_teardown(); | 209 | goto error_work; |
93 | edac_sysfs_pci_teardown(); | ||
94 | return PTR_ERR(edac_thread); | ||
95 | } | 210 | } |
96 | 211 | ||
97 | return 0; | 212 | return 0; |
213 | |||
214 | /* Error teardown stack */ | ||
215 | error_work: | ||
216 | edac_workqueue_teardown(); | ||
217 | error_pci: | ||
218 | edac_sysfs_pci_teardown(); | ||
219 | error_mem: | ||
220 | edac_sysfs_memctrl_teardown(); | ||
221 | error_sysfs: | ||
222 | edac_unregister_sysfs_edac_name(); | ||
223 | error: | ||
224 | return err; | ||
98 | } | 225 | } |
99 | 226 | ||
100 | /* | 227 | /* |
@@ -106,9 +233,11 @@ static void __exit edac_exit(void) | |||
106 | debugf0("%s()\n", __func__); | 233 | debugf0("%s()\n", __func__); |
107 | kthread_stop(edac_thread); | 234 | kthread_stop(edac_thread); |
108 | 235 | ||
109 | /* tear down the sysfs device */ | 236 | /* tear down the various subsystems*/ |
237 | edac_workqueue_teardown(); | ||
110 | edac_sysfs_memctrl_teardown(); | 238 | edac_sysfs_memctrl_teardown(); |
111 | edac_sysfs_pci_teardown(); | 239 | edac_sysfs_pci_teardown(); |
240 | edac_unregister_sysfs_edac_name(); | ||
112 | } | 241 | } |
113 | 242 | ||
114 | /* | 243 | /* |