]>
Commit | Line | Data |
---|---|---|
e27e3dac DT |
1 | /* |
2 | * file for managing the edac_device class of devices for EDAC | |
3 | * | |
4 | * (C) 2007 SoftwareBitMaker(http://www.softwarebitmaker.com) | |
5 | * This file may be distributed under the terms of the | |
6 | * GNU General Public License. | |
7 | * | |
8 | * Written Doug Thompson <norsk5@xmission.com> | |
9 | * | |
10 | */ | |
11 | ||
e27e3dac DT |
12 | #include <linux/ctype.h> |
13 | ||
14 | #include "edac_core.h" | |
15 | #include "edac_module.h" | |
16 | ||
e27e3dac DT |
17 | #define EDAC_DEVICE_SYMLINK "device" |
18 | ||
19 | #define to_edacdev(k) container_of(k, struct edac_device_ctl_info, kobj) | |
20 | #define to_edacdev_attr(a) container_of(a, struct edacdev_attribute, attr) | |
21 | ||
e27e3dac DT |
22 | /************************** edac_device sysfs code and data **************/ |
23 | ||
24 | /* | |
25 | * Set of edac_device_ctl_info attribute store/show functions | |
26 | */ | |
27 | ||
28 | /* 'log_ue' */ | |
079708b9 | 29 | static ssize_t edac_device_ctl_log_ue_show(struct edac_device_ctl_info |
052dfb45 | 30 | *ctl_info, char *data) |
e27e3dac | 31 | { |
079708b9 | 32 | return sprintf(data, "%u\n", ctl_info->log_ue); |
e27e3dac DT |
33 | } |
34 | ||
079708b9 | 35 | static ssize_t edac_device_ctl_log_ue_store(struct edac_device_ctl_info |
052dfb45 DT |
36 | *ctl_info, const char *data, |
37 | size_t count) | |
e27e3dac DT |
38 | { |
39 | /* if parameter is zero, turn off flag, if non-zero turn on flag */ | |
079708b9 | 40 | ctl_info->log_ue = (simple_strtoul(data, NULL, 0) != 0); |
e27e3dac | 41 | |
079708b9 | 42 | return count; |
e27e3dac DT |
43 | } |
44 | ||
45 | /* 'log_ce' */ | |
079708b9 | 46 | static ssize_t edac_device_ctl_log_ce_show(struct edac_device_ctl_info |
052dfb45 | 47 | *ctl_info, char *data) |
e27e3dac | 48 | { |
079708b9 | 49 | return sprintf(data, "%u\n", ctl_info->log_ce); |
e27e3dac DT |
50 | } |
51 | ||
079708b9 | 52 | static ssize_t edac_device_ctl_log_ce_store(struct edac_device_ctl_info |
052dfb45 DT |
53 | *ctl_info, const char *data, |
54 | size_t count) | |
e27e3dac DT |
55 | { |
56 | /* if parameter is zero, turn off flag, if non-zero turn on flag */ | |
079708b9 | 57 | ctl_info->log_ce = (simple_strtoul(data, NULL, 0) != 0); |
e27e3dac | 58 | |
079708b9 | 59 | return count; |
e27e3dac DT |
60 | } |
61 | ||
e27e3dac | 62 | /* 'panic_on_ue' */ |
079708b9 DT |
63 | static ssize_t edac_device_ctl_panic_on_ue_show(struct edac_device_ctl_info |
64 | *ctl_info, char *data) | |
e27e3dac | 65 | { |
079708b9 | 66 | return sprintf(data, "%u\n", ctl_info->panic_on_ue); |
e27e3dac DT |
67 | } |
68 | ||
079708b9 DT |
69 | static ssize_t edac_device_ctl_panic_on_ue_store(struct edac_device_ctl_info |
70 | *ctl_info, const char *data, | |
71 | size_t count) | |
e27e3dac DT |
72 | { |
73 | /* if parameter is zero, turn off flag, if non-zero turn on flag */ | |
079708b9 | 74 | ctl_info->panic_on_ue = (simple_strtoul(data, NULL, 0) != 0); |
e27e3dac DT |
75 | |
76 | return count; | |
77 | } | |
78 | ||
79 | /* 'poll_msec' show and store functions*/ | |
079708b9 | 80 | static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info |
052dfb45 | 81 | *ctl_info, char *data) |
e27e3dac | 82 | { |
079708b9 | 83 | return sprintf(data, "%u\n", ctl_info->poll_msec); |
e27e3dac DT |
84 | } |
85 | ||
079708b9 | 86 | static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info |
052dfb45 DT |
87 | *ctl_info, const char *data, |
88 | size_t count) | |
e27e3dac DT |
89 | { |
90 | unsigned long value; | |
91 | ||
92 | /* get the value and enforce that it is non-zero, must be at least | |
93 | * one millisecond for the delay period, between scans | |
94 | * Then cancel last outstanding delay for the work request | |
95 | * and set a new one. | |
96 | */ | |
079708b9 DT |
97 | value = simple_strtoul(data, NULL, 0); |
98 | edac_device_reset_delay_period(ctl_info, value); | |
e27e3dac | 99 | |
079708b9 | 100 | return count; |
e27e3dac DT |
101 | } |
102 | ||
e27e3dac DT |
103 | /* edac_device_ctl_info specific attribute structure */ |
104 | struct ctl_info_attribute { | |
079708b9 DT |
105 | struct attribute attr; |
106 | ssize_t(*show) (struct edac_device_ctl_info *, char *); | |
107 | ssize_t(*store) (struct edac_device_ctl_info *, const char *, size_t); | |
e27e3dac DT |
108 | }; |
109 | ||
110 | #define to_ctl_info(k) container_of(k, struct edac_device_ctl_info, kobj) | |
111 | #define to_ctl_info_attr(a) container_of(a,struct ctl_info_attribute,attr) | |
112 | ||
113 | /* Function to 'show' fields from the edac_dev 'ctl_info' structure */ | |
114 | static ssize_t edac_dev_ctl_info_show(struct kobject *kobj, | |
052dfb45 | 115 | struct attribute *attr, char *buffer) |
e27e3dac | 116 | { |
079708b9 DT |
117 | struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj); |
118 | struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr); | |
e27e3dac | 119 | |
079708b9 DT |
120 | if (ctl_info_attr->show) |
121 | return ctl_info_attr->show(edac_dev, buffer); | |
122 | return -EIO; | |
e27e3dac DT |
123 | } |
124 | ||
125 | /* Function to 'store' fields into the edac_dev 'ctl_info' structure */ | |
126 | static ssize_t edac_dev_ctl_info_store(struct kobject *kobj, | |
052dfb45 DT |
127 | struct attribute *attr, |
128 | const char *buffer, size_t count) | |
e27e3dac | 129 | { |
079708b9 DT |
130 | struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj); |
131 | struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr); | |
e27e3dac | 132 | |
079708b9 DT |
133 | if (ctl_info_attr->store) |
134 | return ctl_info_attr->store(edac_dev, buffer, count); | |
135 | return -EIO; | |
e27e3dac DT |
136 | } |
137 | ||
138 | /* edac_dev file operations for an 'ctl_info' */ | |
139 | static struct sysfs_ops device_ctl_info_ops = { | |
079708b9 DT |
140 | .show = edac_dev_ctl_info_show, |
141 | .store = edac_dev_ctl_info_store | |
e27e3dac DT |
142 | }; |
143 | ||
144 | #define CTL_INFO_ATTR(_name,_mode,_show,_store) \ | |
145 | static struct ctl_info_attribute attr_ctl_info_##_name = { \ | |
052dfb45 DT |
146 | .attr = {.name = __stringify(_name), .mode = _mode }, \ |
147 | .show = _show, \ | |
148 | .store = _store, \ | |
e27e3dac DT |
149 | }; |
150 | ||
e27e3dac | 151 | /* Declare the various ctl_info attributes here and their respective ops */ |
079708b9 | 152 | CTL_INFO_ATTR(log_ue, S_IRUGO | S_IWUSR, |
052dfb45 | 153 | edac_device_ctl_log_ue_show, edac_device_ctl_log_ue_store); |
079708b9 | 154 | CTL_INFO_ATTR(log_ce, S_IRUGO | S_IWUSR, |
052dfb45 | 155 | edac_device_ctl_log_ce_show, edac_device_ctl_log_ce_store); |
079708b9 | 156 | CTL_INFO_ATTR(panic_on_ue, S_IRUGO | S_IWUSR, |
052dfb45 DT |
157 | edac_device_ctl_panic_on_ue_show, |
158 | edac_device_ctl_panic_on_ue_store); | |
079708b9 | 159 | CTL_INFO_ATTR(poll_msec, S_IRUGO | S_IWUSR, |
052dfb45 | 160 | edac_device_ctl_poll_msec_show, edac_device_ctl_poll_msec_store); |
e27e3dac DT |
161 | |
162 | /* Base Attributes of the EDAC_DEVICE ECC object */ | |
163 | static struct ctl_info_attribute *device_ctrl_attr[] = { | |
164 | &attr_ctl_info_panic_on_ue, | |
165 | &attr_ctl_info_log_ue, | |
166 | &attr_ctl_info_log_ce, | |
167 | &attr_ctl_info_poll_msec, | |
168 | NULL, | |
169 | }; | |
170 | ||
171 | /* Main DEVICE kobject release() function */ | |
172 | static void edac_device_ctrl_master_release(struct kobject *kobj) | |
173 | { | |
174 | struct edac_device_ctl_info *edac_dev; | |
175 | ||
176 | edac_dev = to_edacdev(kobj); | |
177 | ||
178 | debugf1("%s()\n", __func__); | |
179 | complete(&edac_dev->kobj_complete); | |
180 | } | |
181 | ||
182 | static struct kobj_type ktype_device_ctrl = { | |
183 | .release = edac_device_ctrl_master_release, | |
184 | .sysfs_ops = &device_ctl_info_ops, | |
079708b9 | 185 | .default_attrs = (struct attribute **)device_ctrl_attr, |
e27e3dac DT |
186 | }; |
187 | ||
e27e3dac DT |
188 | /**************** edac_device main kobj ctor/dtor code *********************/ |
189 | ||
190 | /* | |
191 | * edac_device_register_main_kobj | |
192 | * | |
193 | * perform the high level setup for the new edac_device instance | |
194 | * | |
195 | * Return: 0 SUCCESS | |
196 | * !0 FAILURE | |
197 | */ | |
079708b9 | 198 | static int edac_device_register_main_kobj(struct edac_device_ctl_info *edac_dev) |
e27e3dac DT |
199 | { |
200 | int err = 0; | |
201 | struct sysdev_class *edac_class; | |
202 | ||
203 | debugf1("%s()\n", __func__); | |
204 | ||
079708b9 DT |
205 | /* get the /sys/devices/system/edac reference */ |
206 | edac_class = edac_get_edac_class(); | |
207 | if (edac_class == NULL) { | |
208 | debugf1("%s() no edac_class error=%d\n", __func__, err); | |
209 | return err; | |
210 | } | |
e27e3dac DT |
211 | |
212 | /* Point to the 'edac_class' this instance 'reports' to */ | |
213 | edac_dev->edac_class = edac_class; | |
214 | ||
215 | /* Init the devices's kobject */ | |
079708b9 | 216 | memset(&edac_dev->kobj, 0, sizeof(struct kobject)); |
e27e3dac DT |
217 | edac_dev->kobj.ktype = &ktype_device_ctrl; |
218 | ||
219 | /* set this new device under the edac_class kobject */ | |
220 | edac_dev->kobj.parent = &edac_class->kset.kobj; | |
221 | ||
222 | /* generate sysfs "..../edac/<name>" */ | |
079708b9 DT |
223 | debugf1("%s() set name of kobject to: %s\n", __func__, edac_dev->name); |
224 | err = kobject_set_name(&edac_dev->kobj, "%s", edac_dev->name); | |
e27e3dac DT |
225 | if (err) |
226 | return err; | |
227 | err = kobject_register(&edac_dev->kobj); | |
228 | if (err) { | |
229 | debugf1("%s()Failed to register '.../edac/%s'\n", | |
079708b9 | 230 | __func__, edac_dev->name); |
e27e3dac DT |
231 | return err; |
232 | } | |
233 | ||
234 | debugf1("%s() Registered '.../edac/%s' kobject\n", | |
235 | __func__, edac_dev->name); | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | /* | |
241 | * edac_device_unregister_main_kobj: | |
242 | * the '..../edac/<name>' kobject | |
243 | */ | |
079708b9 | 244 | static void edac_device_unregister_main_kobj(struct edac_device_ctl_info |
052dfb45 | 245 | *edac_dev) |
e27e3dac DT |
246 | { |
247 | debugf0("%s()\n", __func__); | |
248 | debugf1("%s() name of kobject is: %s\n", | |
249 | __func__, kobject_name(&edac_dev->kobj)); | |
250 | ||
251 | init_completion(&edac_dev->kobj_complete); | |
252 | ||
253 | /* | |
254 | * Unregister the edac device's kobject and | |
255 | * wait for reference count to reach 0. | |
256 | */ | |
257 | kobject_unregister(&edac_dev->kobj); | |
258 | wait_for_completion(&edac_dev->kobj_complete); | |
259 | } | |
260 | ||
e27e3dac DT |
261 | /*************** edac_dev -> instance information ***********/ |
262 | ||
263 | /* | |
264 | * Set of low-level instance attribute show functions | |
265 | */ | |
079708b9 | 266 | static ssize_t instance_ue_count_show(struct edac_device_instance *instance, |
052dfb45 | 267 | char *data) |
e27e3dac | 268 | { |
079708b9 | 269 | return sprintf(data, "%u\n", instance->counters.ue_count); |
e27e3dac DT |
270 | } |
271 | ||
079708b9 | 272 | static ssize_t instance_ce_count_show(struct edac_device_instance *instance, |
052dfb45 | 273 | char *data) |
e27e3dac | 274 | { |
079708b9 | 275 | return sprintf(data, "%u\n", instance->counters.ce_count); |
e27e3dac DT |
276 | } |
277 | ||
e27e3dac DT |
278 | #define to_instance(k) container_of(k, struct edac_device_instance, kobj) |
279 | #define to_instance_attr(a) container_of(a,struct instance_attribute,attr) | |
280 | ||
281 | /* DEVICE instance kobject release() function */ | |
282 | static void edac_device_ctrl_instance_release(struct kobject *kobj) | |
283 | { | |
284 | struct edac_device_instance *instance; | |
285 | ||
286 | debugf1("%s()\n", __func__); | |
287 | ||
288 | instance = to_instance(kobj); | |
289 | complete(&instance->kobj_complete); | |
290 | } | |
291 | ||
e27e3dac DT |
292 | /* instance specific attribute structure */ |
293 | struct instance_attribute { | |
079708b9 | 294 | struct attribute attr; |
52490c8d DT |
295 | ssize_t(*show) (struct edac_device_instance *, char *); |
296 | ssize_t(*store) (struct edac_device_instance *, const char *, size_t); | |
e27e3dac DT |
297 | }; |
298 | ||
e27e3dac DT |
299 | /* Function to 'show' fields from the edac_dev 'instance' structure */ |
300 | static ssize_t edac_dev_instance_show(struct kobject *kobj, | |
052dfb45 | 301 | struct attribute *attr, char *buffer) |
e27e3dac | 302 | { |
079708b9 DT |
303 | struct edac_device_instance *instance = to_instance(kobj); |
304 | struct instance_attribute *instance_attr = to_instance_attr(attr); | |
e27e3dac | 305 | |
079708b9 DT |
306 | if (instance_attr->show) |
307 | return instance_attr->show(instance, buffer); | |
308 | return -EIO; | |
e27e3dac DT |
309 | } |
310 | ||
e27e3dac DT |
311 | /* Function to 'store' fields into the edac_dev 'instance' structure */ |
312 | static ssize_t edac_dev_instance_store(struct kobject *kobj, | |
052dfb45 DT |
313 | struct attribute *attr, |
314 | const char *buffer, size_t count) | |
e27e3dac | 315 | { |
079708b9 DT |
316 | struct edac_device_instance *instance = to_instance(kobj); |
317 | struct instance_attribute *instance_attr = to_instance_attr(attr); | |
e27e3dac | 318 | |
079708b9 DT |
319 | if (instance_attr->store) |
320 | return instance_attr->store(instance, buffer, count); | |
321 | return -EIO; | |
e27e3dac DT |
322 | } |
323 | ||
e27e3dac DT |
324 | /* edac_dev file operations for an 'instance' */ |
325 | static struct sysfs_ops device_instance_ops = { | |
079708b9 DT |
326 | .show = edac_dev_instance_show, |
327 | .store = edac_dev_instance_store | |
e27e3dac DT |
328 | }; |
329 | ||
330 | #define INSTANCE_ATTR(_name,_mode,_show,_store) \ | |
331 | static struct instance_attribute attr_instance_##_name = { \ | |
052dfb45 DT |
332 | .attr = {.name = __stringify(_name), .mode = _mode }, \ |
333 | .show = _show, \ | |
334 | .store = _store, \ | |
e27e3dac DT |
335 | }; |
336 | ||
337 | /* | |
338 | * Define attributes visible for the edac_device instance object | |
339 | * Each contains a pointer to a show and an optional set | |
340 | * function pointer that does the low level output/input | |
341 | */ | |
079708b9 DT |
342 | INSTANCE_ATTR(ce_count, S_IRUGO, instance_ce_count_show, NULL); |
343 | INSTANCE_ATTR(ue_count, S_IRUGO, instance_ue_count_show, NULL); | |
e27e3dac DT |
344 | |
345 | /* list of edac_dev 'instance' attributes */ | |
346 | static struct instance_attribute *device_instance_attr[] = { | |
347 | &attr_instance_ce_count, | |
348 | &attr_instance_ue_count, | |
349 | NULL, | |
350 | }; | |
351 | ||
352 | /* The 'ktype' for each edac_dev 'instance' */ | |
353 | static struct kobj_type ktype_instance_ctrl = { | |
354 | .release = edac_device_ctrl_instance_release, | |
355 | .sysfs_ops = &device_instance_ops, | |
079708b9 | 356 | .default_attrs = (struct attribute **)device_instance_attr, |
e27e3dac DT |
357 | }; |
358 | ||
e27e3dac DT |
359 | /*************** edac_dev -> instance -> block information *********/ |
360 | ||
361 | /* | |
362 | * Set of low-level block attribute show functions | |
363 | */ | |
079708b9 | 364 | static ssize_t block_ue_count_show(struct edac_device_block *block, char *data) |
e27e3dac | 365 | { |
079708b9 | 366 | return sprintf(data, "%u\n", block->counters.ue_count); |
e27e3dac DT |
367 | } |
368 | ||
079708b9 | 369 | static ssize_t block_ce_count_show(struct edac_device_block *block, char *data) |
e27e3dac | 370 | { |
079708b9 | 371 | return sprintf(data, "%u\n", block->counters.ce_count); |
e27e3dac DT |
372 | } |
373 | ||
e27e3dac DT |
374 | #define to_block(k) container_of(k, struct edac_device_block, kobj) |
375 | #define to_block_attr(a) container_of(a,struct block_attribute,attr) | |
376 | ||
377 | /* DEVICE block kobject release() function */ | |
378 | static void edac_device_ctrl_block_release(struct kobject *kobj) | |
379 | { | |
380 | struct edac_device_block *block; | |
381 | ||
382 | debugf1("%s()\n", __func__); | |
383 | ||
384 | block = to_block(kobj); | |
385 | complete(&block->kobj_complete); | |
386 | } | |
387 | ||
388 | /* block specific attribute structure */ | |
389 | struct block_attribute { | |
079708b9 DT |
390 | struct attribute attr; |
391 | ssize_t(*show) (struct edac_device_block *, char *); | |
392 | ssize_t(*store) (struct edac_device_block *, const char *, size_t); | |
e27e3dac DT |
393 | }; |
394 | ||
395 | /* Function to 'show' fields from the edac_dev 'block' structure */ | |
396 | static ssize_t edac_dev_block_show(struct kobject *kobj, | |
052dfb45 | 397 | struct attribute *attr, char *buffer) |
e27e3dac | 398 | { |
079708b9 DT |
399 | struct edac_device_block *block = to_block(kobj); |
400 | struct block_attribute *block_attr = to_block_attr(attr); | |
e27e3dac | 401 | |
079708b9 DT |
402 | if (block_attr->show) |
403 | return block_attr->show(block, buffer); | |
404 | return -EIO; | |
e27e3dac DT |
405 | } |
406 | ||
e27e3dac DT |
407 | /* Function to 'store' fields into the edac_dev 'block' structure */ |
408 | static ssize_t edac_dev_block_store(struct kobject *kobj, | |
052dfb45 DT |
409 | struct attribute *attr, |
410 | const char *buffer, size_t count) | |
e27e3dac | 411 | { |
079708b9 DT |
412 | struct edac_device_block *block = to_block(kobj); |
413 | struct block_attribute *block_attr = to_block_attr(attr); | |
e27e3dac | 414 | |
079708b9 DT |
415 | if (block_attr->store) |
416 | return block_attr->store(block, buffer, count); | |
417 | return -EIO; | |
e27e3dac DT |
418 | } |
419 | ||
e27e3dac DT |
420 | /* edac_dev file operations for a 'block' */ |
421 | static struct sysfs_ops device_block_ops = { | |
079708b9 DT |
422 | .show = edac_dev_block_show, |
423 | .store = edac_dev_block_store | |
e27e3dac DT |
424 | }; |
425 | ||
e27e3dac DT |
426 | #define BLOCK_ATTR(_name,_mode,_show,_store) \ |
427 | static struct block_attribute attr_block_##_name = { \ | |
052dfb45 DT |
428 | .attr = {.name = __stringify(_name), .mode = _mode }, \ |
429 | .show = _show, \ | |
430 | .store = _store, \ | |
e27e3dac DT |
431 | }; |
432 | ||
079708b9 DT |
433 | BLOCK_ATTR(ce_count, S_IRUGO, block_ce_count_show, NULL); |
434 | BLOCK_ATTR(ue_count, S_IRUGO, block_ue_count_show, NULL); | |
e27e3dac DT |
435 | |
436 | /* list of edac_dev 'block' attributes */ | |
437 | static struct block_attribute *device_block_attr[] = { | |
438 | &attr_block_ce_count, | |
439 | &attr_block_ue_count, | |
440 | NULL, | |
441 | }; | |
442 | ||
443 | /* The 'ktype' for each edac_dev 'block' */ | |
444 | static struct kobj_type ktype_block_ctrl = { | |
445 | .release = edac_device_ctrl_block_release, | |
446 | .sysfs_ops = &device_block_ops, | |
079708b9 | 447 | .default_attrs = (struct attribute **)device_block_attr, |
e27e3dac DT |
448 | }; |
449 | ||
e27e3dac DT |
450 | /************** block ctor/dtor code ************/ |
451 | ||
452 | /* | |
453 | * edac_device_create_block | |
454 | */ | |
079708b9 | 455 | static int edac_device_create_block(struct edac_device_ctl_info *edac_dev, |
052dfb45 DT |
456 | struct edac_device_instance *instance, |
457 | int idx) | |
e27e3dac DT |
458 | { |
459 | int err; | |
460 | struct edac_device_block *block; | |
461 | ||
462 | block = &instance->blocks[idx]; | |
463 | ||
464 | debugf1("%s() Instance '%s' block[%d] '%s'\n", | |
079708b9 | 465 | __func__, instance->name, idx, block->name); |
e27e3dac DT |
466 | |
467 | /* init this block's kobject */ | |
079708b9 | 468 | memset(&block->kobj, 0, sizeof(struct kobject)); |
e27e3dac DT |
469 | block->kobj.parent = &instance->kobj; |
470 | block->kobj.ktype = &ktype_block_ctrl; | |
471 | ||
079708b9 | 472 | err = kobject_set_name(&block->kobj, "%s", block->name); |
e27e3dac DT |
473 | if (err) |
474 | return err; | |
475 | ||
476 | err = kobject_register(&block->kobj); | |
477 | if (err) { | |
478 | debugf1("%s()Failed to register instance '%s'\n", | |
079708b9 | 479 | __func__, block->name); |
e27e3dac DT |
480 | return err; |
481 | } | |
482 | ||
483 | return 0; | |
484 | } | |
485 | ||
486 | /* | |
487 | * edac_device_delete_block(edac_dev,j); | |
488 | */ | |
079708b9 | 489 | static void edac_device_delete_block(struct edac_device_ctl_info *edac_dev, |
052dfb45 DT |
490 | struct edac_device_instance *instance, |
491 | int idx) | |
e27e3dac DT |
492 | { |
493 | struct edac_device_block *block; | |
494 | ||
495 | block = &instance->blocks[idx]; | |
496 | ||
497 | /* unregister this block's kobject */ | |
498 | init_completion(&block->kobj_complete); | |
499 | kobject_unregister(&block->kobj); | |
500 | wait_for_completion(&block->kobj_complete); | |
501 | } | |
502 | ||
503 | /************** instance ctor/dtor code ************/ | |
504 | ||
505 | /* | |
506 | * edac_device_create_instance | |
507 | * create just one instance of an edac_device 'instance' | |
508 | */ | |
079708b9 | 509 | static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev, |
052dfb45 | 510 | int idx) |
e27e3dac DT |
511 | { |
512 | int i, j; | |
513 | int err; | |
514 | struct edac_device_instance *instance; | |
515 | ||
516 | instance = &edac_dev->instances[idx]; | |
517 | ||
518 | /* Init the instance's kobject */ | |
079708b9 | 519 | memset(&instance->kobj, 0, sizeof(struct kobject)); |
e27e3dac DT |
520 | |
521 | /* set this new device under the edac_device main kobject */ | |
522 | instance->kobj.parent = &edac_dev->kobj; | |
523 | instance->kobj.ktype = &ktype_instance_ctrl; | |
524 | ||
079708b9 | 525 | err = kobject_set_name(&instance->kobj, "%s", instance->name); |
e27e3dac DT |
526 | if (err) |
527 | return err; | |
528 | ||
529 | err = kobject_register(&instance->kobj); | |
530 | if (err != 0) { | |
531 | debugf2("%s() Failed to register instance '%s'\n", | |
079708b9 | 532 | __func__, instance->name); |
e27e3dac DT |
533 | return err; |
534 | } | |
535 | ||
536 | debugf1("%s() now register '%d' blocks for instance %d\n", | |
079708b9 | 537 | __func__, instance->nr_blocks, idx); |
e27e3dac DT |
538 | |
539 | /* register all blocks of this instance */ | |
079708b9 DT |
540 | for (i = 0; i < instance->nr_blocks; i++) { |
541 | err = edac_device_create_block(edac_dev, instance, i); | |
e27e3dac | 542 | if (err) { |
52490c8d | 543 | for (j = 0; j < i; j++) |
079708b9 | 544 | edac_device_delete_block(edac_dev, instance, j); |
e27e3dac DT |
545 | return err; |
546 | } | |
547 | } | |
548 | ||
549 | debugf1("%s() Registered instance %d '%s' kobject\n", | |
550 | __func__, idx, instance->name); | |
551 | ||
552 | return 0; | |
553 | } | |
554 | ||
555 | /* | |
556 | * edac_device_remove_instance | |
557 | * remove an edac_device instance | |
558 | */ | |
079708b9 DT |
559 | static void edac_device_delete_instance(struct edac_device_ctl_info *edac_dev, |
560 | int idx) | |
e27e3dac DT |
561 | { |
562 | int i; | |
563 | struct edac_device_instance *instance; | |
564 | ||
565 | instance = &edac_dev->instances[idx]; | |
566 | ||
567 | /* unregister all blocks in this instance */ | |
52490c8d | 568 | for (i = 0; i < instance->nr_blocks; i++) |
079708b9 | 569 | edac_device_delete_block(edac_dev, instance, i); |
e27e3dac DT |
570 | |
571 | /* unregister this instance's kobject */ | |
572 | init_completion(&instance->kobj_complete); | |
573 | kobject_unregister(&instance->kobj); | |
574 | wait_for_completion(&instance->kobj_complete); | |
575 | } | |
576 | ||
577 | /* | |
578 | * edac_device_create_instances | |
579 | * create the first level of 'instances' for this device | |
580 | * (ie 'cache' might have 'cache0', 'cache1', 'cache2', etc | |
581 | */ | |
582 | static int edac_device_create_instances(struct edac_device_ctl_info *edac_dev) | |
583 | { | |
584 | int i, j; | |
585 | int err; | |
586 | ||
587 | debugf0("%s()\n", __func__); | |
588 | ||
589 | /* iterate over creation of the instances */ | |
079708b9 DT |
590 | for (i = 0; i < edac_dev->nr_instances; i++) { |
591 | err = edac_device_create_instance(edac_dev, i); | |
e27e3dac DT |
592 | if (err) { |
593 | /* unwind previous instances on error */ | |
52490c8d | 594 | for (j = 0; j < i; j++) |
079708b9 | 595 | edac_device_delete_instance(edac_dev, j); |
e27e3dac DT |
596 | return err; |
597 | } | |
598 | } | |
599 | ||
600 | return 0; | |
601 | } | |
602 | ||
603 | /* | |
604 | * edac_device_delete_instances(edac_dev); | |
605 | * unregister all the kobjects of the instances | |
606 | */ | |
607 | static void edac_device_delete_instances(struct edac_device_ctl_info *edac_dev) | |
608 | { | |
609 | int i; | |
610 | ||
611 | /* iterate over creation of the instances */ | |
52490c8d | 612 | for (i = 0; i < edac_dev->nr_instances; i++) |
079708b9 | 613 | edac_device_delete_instance(edac_dev, i); |
e27e3dac DT |
614 | } |
615 | ||
616 | /******************* edac_dev sysfs ctor/dtor code *************/ | |
617 | ||
42a8e397 DT |
618 | /* |
619 | * edac_device_add_sysfs_attributes | |
620 | * add some attributes to this instance's main kobject | |
621 | */ | |
622 | static int edac_device_add_sysfs_attributes( | |
623 | struct edac_device_ctl_info *edac_dev) | |
624 | { | |
625 | int err; | |
052dfb45 DT |
626 | struct edac_dev_sysfs_attribute *sysfs_attrib; |
627 | ||
628 | /* point to the start of the array and iterate over it | |
629 | * adding each attribute listed to this mci instance's kobject | |
630 | */ | |
631 | sysfs_attrib = edac_dev->sysfs_attributes; | |
632 | ||
633 | while (sysfs_attrib->attr.name != NULL) { | |
634 | err = sysfs_create_file(&edac_dev->kobj, | |
635 | (struct attribute*) sysfs_attrib); | |
52490c8d | 636 | if (err) |
052dfb45 | 637 | return err; |
052dfb45 DT |
638 | |
639 | sysfs_attrib++; | |
640 | } | |
42a8e397 DT |
641 | |
642 | return 0; | |
643 | } | |
644 | ||
e27e3dac DT |
645 | /* |
646 | * edac_device_create_sysfs() Constructor | |
647 | * | |
648 | * Create a new edac_device kobject instance, | |
649 | * | |
650 | * Return: | |
651 | * 0 Success | |
652 | * !0 Failure | |
653 | */ | |
654 | int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev) | |
655 | { | |
656 | int err; | |
079708b9 | 657 | struct kobject *edac_kobj = &edac_dev->kobj; |
e27e3dac DT |
658 | |
659 | /* register this instance's main kobj with the edac class kobj */ | |
660 | err = edac_device_register_main_kobj(edac_dev); | |
661 | if (err) | |
662 | return err; | |
663 | ||
664 | debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx); | |
665 | ||
42a8e397 DT |
666 | /* If the low level driver requests some sysfs entries |
667 | * then go create them here | |
668 | */ | |
669 | if (edac_dev->sysfs_attributes != NULL) { | |
670 | err = edac_device_add_sysfs_attributes(edac_dev); | |
671 | if (err) { | |
672 | debugf0("%s() failed to add sysfs attribs\n", | |
673 | __func__); | |
674 | goto err_unreg_object; | |
675 | } | |
676 | } | |
677 | ||
e27e3dac DT |
678 | /* create a symlink from the edac device |
679 | * to the platform 'device' being used for this | |
680 | */ | |
681 | err = sysfs_create_link(edac_kobj, | |
079708b9 | 682 | &edac_dev->dev->kobj, EDAC_DEVICE_SYMLINK); |
e27e3dac DT |
683 | if (err) { |
684 | debugf0("%s() sysfs_create_link() returned err= %d\n", | |
685 | __func__, err); | |
42a8e397 | 686 | goto err_unreg_object; |
e27e3dac DT |
687 | } |
688 | ||
689 | debugf0("%s() calling create-instances, idx=%d\n", | |
690 | __func__, edac_dev->dev_idx); | |
691 | ||
692 | /* Create the first level instance directories */ | |
693 | err = edac_device_create_instances(edac_dev); | |
52490c8d | 694 | if (err) |
42a8e397 | 695 | goto err_remove_link; |
e27e3dac DT |
696 | |
697 | return 0; | |
698 | ||
699 | /* Error unwind stack */ | |
42a8e397 DT |
700 | err_remove_link: |
701 | /* remove the sym link */ | |
702 | sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK); | |
e27e3dac | 703 | |
42a8e397 | 704 | err_unreg_object: |
e27e3dac DT |
705 | edac_device_unregister_main_kobj(edac_dev); |
706 | ||
707 | return err; | |
708 | } | |
709 | ||
710 | /* | |
711 | * edac_device_remove_sysfs() destructor | |
712 | * | |
713 | * remove a edac_device instance | |
714 | */ | |
715 | void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev) | |
716 | { | |
717 | debugf0("%s()\n", __func__); | |
718 | ||
719 | edac_device_delete_instances(edac_dev); | |
720 | ||
721 | /* remove the sym link */ | |
722 | sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK); | |
723 | ||
724 | /* unregister the instance's main kobj */ | |
725 | edac_device_unregister_main_kobj(edac_dev); | |
726 | } |