Blame view
drivers/edac/edac_device_sysfs.c
23.7 KB
e27e3dac6
|
1 2 3 |
/* * file for managing the edac_device class of devices for EDAC * |
1c3631ff1
|
4 5 |
* (C) 2007 SoftwareBitMaker (http://www.softwarebitmaker.com) * |
e27e3dac6
|
6 7 8 9 10 11 |
* This file may be distributed under the terms of the * GNU General Public License. * * Written Doug Thompson <norsk5@xmission.com> * */ |
e27e3dac6
|
12 |
#include <linux/ctype.h> |
1c3631ff1
|
13 |
#include <linux/module.h> |
5a0e3ad6a
|
14 |
#include <linux/slab.h> |
30e1f7a81
|
15 |
#include <linux/edac.h> |
e27e3dac6
|
16 17 18 |
#include "edac_core.h" #include "edac_module.h" |
e27e3dac6
|
19 20 21 22 |
#define EDAC_DEVICE_SYMLINK "device" #define to_edacdev(k) container_of(k, struct edac_device_ctl_info, kobj) #define to_edacdev_attr(a) container_of(a, struct edacdev_attribute, attr) |
e27e3dac6
|
23 24 25 26 27 28 |
/* * Set of edac_device_ctl_info attribute store/show functions */ /* 'log_ue' */ |
079708b91
|
29 |
static ssize_t edac_device_ctl_log_ue_show(struct edac_device_ctl_info |
052dfb45c
|
30 |
*ctl_info, char *data) |
e27e3dac6
|
31 |
{ |
079708b91
|
32 33 |
return sprintf(data, "%u ", ctl_info->log_ue); |
e27e3dac6
|
34 |
} |
079708b91
|
35 |
static ssize_t edac_device_ctl_log_ue_store(struct edac_device_ctl_info |
052dfb45c
|
36 37 |
*ctl_info, const char *data, size_t count) |
e27e3dac6
|
38 39 |
{ /* if parameter is zero, turn off flag, if non-zero turn on flag */ |
079708b91
|
40 |
ctl_info->log_ue = (simple_strtoul(data, NULL, 0) != 0); |
e27e3dac6
|
41 |
|
079708b91
|
42 |
return count; |
e27e3dac6
|
43 44 45 |
} /* 'log_ce' */ |
079708b91
|
46 |
static ssize_t edac_device_ctl_log_ce_show(struct edac_device_ctl_info |
052dfb45c
|
47 |
*ctl_info, char *data) |
e27e3dac6
|
48 |
{ |
079708b91
|
49 50 |
return sprintf(data, "%u ", ctl_info->log_ce); |
e27e3dac6
|
51 |
} |
079708b91
|
52 |
static ssize_t edac_device_ctl_log_ce_store(struct edac_device_ctl_info |
052dfb45c
|
53 54 |
*ctl_info, const char *data, size_t count) |
e27e3dac6
|
55 56 |
{ /* if parameter is zero, turn off flag, if non-zero turn on flag */ |
079708b91
|
57 |
ctl_info->log_ce = (simple_strtoul(data, NULL, 0) != 0); |
e27e3dac6
|
58 |
|
079708b91
|
59 |
return count; |
e27e3dac6
|
60 |
} |
e27e3dac6
|
61 |
/* 'panic_on_ue' */ |
079708b91
|
62 63 |
static ssize_t edac_device_ctl_panic_on_ue_show(struct edac_device_ctl_info *ctl_info, char *data) |
e27e3dac6
|
64 |
{ |
079708b91
|
65 66 |
return sprintf(data, "%u ", ctl_info->panic_on_ue); |
e27e3dac6
|
67 |
} |
079708b91
|
68 69 70 |
static ssize_t edac_device_ctl_panic_on_ue_store(struct edac_device_ctl_info *ctl_info, const char *data, size_t count) |
e27e3dac6
|
71 72 |
{ /* if parameter is zero, turn off flag, if non-zero turn on flag */ |
079708b91
|
73 |
ctl_info->panic_on_ue = (simple_strtoul(data, NULL, 0) != 0); |
e27e3dac6
|
74 75 76 77 78 |
return count; } /* 'poll_msec' show and store functions*/ |
079708b91
|
79 |
static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info |
052dfb45c
|
80 |
*ctl_info, char *data) |
e27e3dac6
|
81 |
{ |
079708b91
|
82 83 |
return sprintf(data, "%u ", ctl_info->poll_msec); |
e27e3dac6
|
84 |
} |
079708b91
|
85 |
static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info |
052dfb45c
|
86 87 |
*ctl_info, const char *data, size_t count) |
e27e3dac6
|
88 89 90 91 92 93 94 95 |
{ unsigned long value; /* get the value and enforce that it is non-zero, must be at least * one millisecond for the delay period, between scans * Then cancel last outstanding delay for the work request * and set a new one. */ |
079708b91
|
96 97 |
value = simple_strtoul(data, NULL, 0); edac_device_reset_delay_period(ctl_info, value); |
e27e3dac6
|
98 |
|
079708b91
|
99 |
return count; |
e27e3dac6
|
100 |
} |
e27e3dac6
|
101 102 |
/* edac_device_ctl_info specific attribute structure */ struct ctl_info_attribute { |
079708b91
|
103 |
struct attribute attr; |
1c3631ff1
|
104 105 |
ssize_t(*show) (struct edac_device_ctl_info *, char *); ssize_t(*store) (struct edac_device_ctl_info *, const char *, size_t); |
e27e3dac6
|
106 107 108 109 110 111 112 |
}; #define to_ctl_info(k) container_of(k, struct edac_device_ctl_info, kobj) #define to_ctl_info_attr(a) container_of(a,struct ctl_info_attribute,attr) /* Function to 'show' fields from the edac_dev 'ctl_info' structure */ static ssize_t edac_dev_ctl_info_show(struct kobject *kobj, |
052dfb45c
|
113 |
struct attribute *attr, char *buffer) |
e27e3dac6
|
114 |
{ |
079708b91
|
115 116 |
struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj); struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr); |
e27e3dac6
|
117 |
|
079708b91
|
118 119 120 |
if (ctl_info_attr->show) return ctl_info_attr->show(edac_dev, buffer); return -EIO; |
e27e3dac6
|
121 122 123 124 |
} /* Function to 'store' fields into the edac_dev 'ctl_info' structure */ static ssize_t edac_dev_ctl_info_store(struct kobject *kobj, |
052dfb45c
|
125 126 |
struct attribute *attr, const char *buffer, size_t count) |
e27e3dac6
|
127 |
{ |
079708b91
|
128 129 |
struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj); struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr); |
e27e3dac6
|
130 |
|
079708b91
|
131 132 133 |
if (ctl_info_attr->store) return ctl_info_attr->store(edac_dev, buffer, count); return -EIO; |
e27e3dac6
|
134 135 136 |
} /* edac_dev file operations for an 'ctl_info' */ |
52cf25d0a
|
137 |
static const struct sysfs_ops device_ctl_info_ops = { |
079708b91
|
138 139 |
.show = edac_dev_ctl_info_show, .store = edac_dev_ctl_info_store |
e27e3dac6
|
140 141 142 143 |
}; #define CTL_INFO_ATTR(_name,_mode,_show,_store) \ static struct ctl_info_attribute attr_ctl_info_##_name = { \ |
052dfb45c
|
144 145 146 |
.attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ |
e27e3dac6
|
147 |
}; |
e27e3dac6
|
148 |
/* Declare the various ctl_info attributes here and their respective ops */ |
079708b91
|
149 |
CTL_INFO_ATTR(log_ue, S_IRUGO | S_IWUSR, |
052dfb45c
|
150 |
edac_device_ctl_log_ue_show, edac_device_ctl_log_ue_store); |
079708b91
|
151 |
CTL_INFO_ATTR(log_ce, S_IRUGO | S_IWUSR, |
052dfb45c
|
152 |
edac_device_ctl_log_ce_show, edac_device_ctl_log_ce_store); |
079708b91
|
153 |
CTL_INFO_ATTR(panic_on_ue, S_IRUGO | S_IWUSR, |
052dfb45c
|
154 155 |
edac_device_ctl_panic_on_ue_show, edac_device_ctl_panic_on_ue_store); |
079708b91
|
156 |
CTL_INFO_ATTR(poll_msec, S_IRUGO | S_IWUSR, |
052dfb45c
|
157 |
edac_device_ctl_poll_msec_show, edac_device_ctl_poll_msec_store); |
e27e3dac6
|
158 159 160 161 162 163 164 165 166 |
/* Base Attributes of the EDAC_DEVICE ECC object */ static struct ctl_info_attribute *device_ctrl_attr[] = { &attr_ctl_info_panic_on_ue, &attr_ctl_info_log_ue, &attr_ctl_info_log_ce, &attr_ctl_info_poll_msec, NULL, }; |
1c3631ff1
|
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
/* * edac_device_ctrl_master_release * * called when the reference count for the 'main' kobj * for a edac_device control struct reaches zero * * Reference count model: * One 'main' kobject for each control structure allocated. * That main kobj is initially set to one AND * the reference count for the EDAC 'core' module is * bumped by one, thus added 'keep in memory' dependency. * * Each new internal kobj (in instances and blocks) then * bumps the 'main' kobject. * * When they are released their release functions decrement * the 'main' kobj. * * When the main kobj reaches zero (0) then THIS function * is called which then decrements the EDAC 'core' module. * When the module reference count reaches zero then the * module no longer has dependency on keeping the release * function code in memory and module can be unloaded. * * This will support several control objects as well, each * with its own 'main' kobj. */ |
e27e3dac6
|
194 195 |
static void edac_device_ctrl_master_release(struct kobject *kobj) { |
1c3631ff1
|
196 |
struct edac_device_ctl_info *edac_dev = to_edacdev(kobj); |
e27e3dac6
|
197 |
|
b2a4ac0c2
|
198 199 |
debugf4("%s() control index=%d ", __func__, edac_dev->dev_idx); |
e27e3dac6
|
200 |
|
1c3631ff1
|
201 202 203 204 205 206 207 |
/* decrement the EDAC CORE module ref count */ module_put(edac_dev->owner); /* free the control struct containing the 'main' kobj * passed in to this routine */ kfree(edac_dev); |
e27e3dac6
|
208 |
} |
1c3631ff1
|
209 |
/* ktype for the main (master) kobject */ |
e27e3dac6
|
210 211 212 |
static struct kobj_type ktype_device_ctrl = { .release = edac_device_ctrl_master_release, .sysfs_ops = &device_ctl_info_ops, |
079708b91
|
213 |
.default_attrs = (struct attribute **)device_ctrl_attr, |
e27e3dac6
|
214 |
}; |
e27e3dac6
|
215 |
/* |
1c3631ff1
|
216 |
* edac_device_register_sysfs_main_kobj |
e27e3dac6
|
217 218 219 220 221 222 |
* * perform the high level setup for the new edac_device instance * * Return: 0 SUCCESS * !0 FAILURE */ |
1c3631ff1
|
223 |
int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev) |
e27e3dac6
|
224 |
{ |
e27e3dac6
|
225 |
struct sysdev_class *edac_class; |
1c3631ff1
|
226 |
int err; |
e27e3dac6
|
227 228 229 |
debugf1("%s() ", __func__); |
079708b91
|
230 |
/* get the /sys/devices/system/edac reference */ |
30e1f7a81
|
231 |
edac_class = edac_get_sysfs_class(); |
079708b91
|
232 |
if (edac_class == NULL) { |
1c3631ff1
|
233 234 235 236 |
debugf1("%s() no edac_class error ", __func__); err = -ENODEV; goto err_out; |
079708b91
|
237 |
} |
e27e3dac6
|
238 239 240 241 242 |
/* Point to the 'edac_class' this instance 'reports' to */ edac_dev->edac_class = edac_class; /* Init the devices's kobject */ |
079708b91
|
243 |
memset(&edac_dev->kobj, 0, sizeof(struct kobject)); |
1c3631ff1
|
244 245 246 247 248 249 250 251 |
/* Record which module 'owns' this control structure * and bump the ref count of the module */ edac_dev->owner = THIS_MODULE; if (!try_module_get(edac_dev->owner)) { err = -ENODEV; |
30e1f7a81
|
252 |
goto err_mod_get; |
1c3631ff1
|
253 254 255 |
} /* register */ |
b2ed215a3
|
256 257 258 |
err = kobject_init_and_add(&edac_dev->kobj, &ktype_device_ctrl, &edac_class->kset.kobj, "%s", edac_dev->name); |
e27e3dac6
|
259 260 261 |
if (err) { debugf1("%s()Failed to register '.../edac/%s' ", |
079708b91
|
262 |
__func__, edac_dev->name); |
1c3631ff1
|
263 |
goto err_kobj_reg; |
e27e3dac6
|
264 |
} |
b2ed215a3
|
265 |
kobject_uevent(&edac_dev->kobj, KOBJ_ADD); |
e27e3dac6
|
266 |
|
1c3631ff1
|
267 268 269 |
/* At this point, to 'free' the control struct, * edac_device_unregister_sysfs_main_kobj() must be used */ |
b2a4ac0c2
|
270 271 |
debugf4("%s() Registered '.../edac/%s' kobject ", |
e27e3dac6
|
272 273 274 |
__func__, edac_dev->name); return 0; |
1c3631ff1
|
275 276 277 278 |
/* Error exit stack */ err_kobj_reg: module_put(edac_dev->owner); |
30e1f7a81
|
279 280 |
err_mod_get: edac_put_sysfs_class(); |
1c3631ff1
|
281 282 |
err_out: return err; |
e27e3dac6
|
283 284 285 |
} /* |
1c3631ff1
|
286 |
* edac_device_unregister_sysfs_main_kobj: |
e27e3dac6
|
287 288 |
* the '..../edac/<name>' kobject */ |
30e1f7a81
|
289 |
void edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev) |
e27e3dac6
|
290 291 292 |
{ debugf0("%s() ", __func__); |
b2a4ac0c2
|
293 294 |
debugf4("%s() name of kobject is: %s ", |
30e1f7a81
|
295 |
__func__, kobject_name(&dev->kobj)); |
e27e3dac6
|
296 |
|
e27e3dac6
|
297 298 |
/* * Unregister the edac device's kobject and |
1c3631ff1
|
299 300 301 302 |
* allow for reference count to reach 0 at which point * the callback will be called to: * a) module_put() this module * b) 'kfree' the memory |
e27e3dac6
|
303 |
*/ |
30e1f7a81
|
304 305 |
kobject_put(&dev->kobj); edac_put_sysfs_class(); |
e27e3dac6
|
306 |
} |
1c3631ff1
|
307 |
/* edac_dev -> instance information */ |
e27e3dac6
|
308 309 310 311 |
/* * Set of low-level instance attribute show functions */ |
079708b91
|
312 |
static ssize_t instance_ue_count_show(struct edac_device_instance *instance, |
052dfb45c
|
313 |
char *data) |
e27e3dac6
|
314 |
{ |
079708b91
|
315 316 |
return sprintf(data, "%u ", instance->counters.ue_count); |
e27e3dac6
|
317 |
} |
079708b91
|
318 |
static ssize_t instance_ce_count_show(struct edac_device_instance *instance, |
052dfb45c
|
319 |
char *data) |
e27e3dac6
|
320 |
{ |
079708b91
|
321 322 |
return sprintf(data, "%u ", instance->counters.ce_count); |
e27e3dac6
|
323 |
} |
e27e3dac6
|
324 325 326 327 328 329 330 331 332 333 |
#define to_instance(k) container_of(k, struct edac_device_instance, kobj) #define to_instance_attr(a) container_of(a,struct instance_attribute,attr) /* DEVICE instance kobject release() function */ static void edac_device_ctrl_instance_release(struct kobject *kobj) { struct edac_device_instance *instance; debugf1("%s() ", __func__); |
1c3631ff1
|
334 335 336 |
/* map from this kobj to the main control struct * and then dec the main kobj count */ |
e27e3dac6
|
337 |
instance = to_instance(kobj); |
1c3631ff1
|
338 |
kobject_put(&instance->ctl->kobj); |
e27e3dac6
|
339 |
} |
e27e3dac6
|
340 341 |
/* instance specific attribute structure */ struct instance_attribute { |
079708b91
|
342 |
struct attribute attr; |
52490c8d0
|
343 344 |
ssize_t(*show) (struct edac_device_instance *, char *); ssize_t(*store) (struct edac_device_instance *, const char *, size_t); |
e27e3dac6
|
345 |
}; |
e27e3dac6
|
346 347 |
/* Function to 'show' fields from the edac_dev 'instance' structure */ static ssize_t edac_dev_instance_show(struct kobject *kobj, |
052dfb45c
|
348 |
struct attribute *attr, char *buffer) |
e27e3dac6
|
349 |
{ |
079708b91
|
350 351 |
struct edac_device_instance *instance = to_instance(kobj); struct instance_attribute *instance_attr = to_instance_attr(attr); |
e27e3dac6
|
352 |
|
079708b91
|
353 354 355 |
if (instance_attr->show) return instance_attr->show(instance, buffer); return -EIO; |
e27e3dac6
|
356 |
} |
e27e3dac6
|
357 358 |
/* Function to 'store' fields into the edac_dev 'instance' structure */ static ssize_t edac_dev_instance_store(struct kobject *kobj, |
052dfb45c
|
359 360 |
struct attribute *attr, const char *buffer, size_t count) |
e27e3dac6
|
361 |
{ |
079708b91
|
362 363 |
struct edac_device_instance *instance = to_instance(kobj); struct instance_attribute *instance_attr = to_instance_attr(attr); |
e27e3dac6
|
364 |
|
079708b91
|
365 366 367 |
if (instance_attr->store) return instance_attr->store(instance, buffer, count); return -EIO; |
e27e3dac6
|
368 |
} |
e27e3dac6
|
369 |
/* edac_dev file operations for an 'instance' */ |
52cf25d0a
|
370 |
static const struct sysfs_ops device_instance_ops = { |
079708b91
|
371 372 |
.show = edac_dev_instance_show, .store = edac_dev_instance_store |
e27e3dac6
|
373 374 375 376 |
}; #define INSTANCE_ATTR(_name,_mode,_show,_store) \ static struct instance_attribute attr_instance_##_name = { \ |
052dfb45c
|
377 378 379 |
.attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ |
e27e3dac6
|
380 381 382 383 384 385 386 |
}; /* * Define attributes visible for the edac_device instance object * Each contains a pointer to a show and an optional set * function pointer that does the low level output/input */ |
079708b91
|
387 388 |
INSTANCE_ATTR(ce_count, S_IRUGO, instance_ce_count_show, NULL); INSTANCE_ATTR(ue_count, S_IRUGO, instance_ue_count_show, NULL); |
e27e3dac6
|
389 390 391 392 393 394 395 396 397 398 399 400 |
/* list of edac_dev 'instance' attributes */ static struct instance_attribute *device_instance_attr[] = { &attr_instance_ce_count, &attr_instance_ue_count, NULL, }; /* The 'ktype' for each edac_dev 'instance' */ static struct kobj_type ktype_instance_ctrl = { .release = edac_device_ctrl_instance_release, .sysfs_ops = &device_instance_ops, |
079708b91
|
401 |
.default_attrs = (struct attribute **)device_instance_attr, |
e27e3dac6
|
402 |
}; |
1c3631ff1
|
403 |
/* edac_dev -> instance -> block information */ |
e27e3dac6
|
404 |
|
b2a4ac0c2
|
405 406 407 |
#define to_block(k) container_of(k, struct edac_device_block, kobj) #define to_block_attr(a) \ container_of(a, struct edac_dev_sysfs_block_attribute, attr) |
e27e3dac6
|
408 409 410 |
/* * Set of low-level block attribute show functions */ |
b2a4ac0c2
|
411 412 |
static ssize_t block_ue_count_show(struct kobject *kobj, struct attribute *attr, char *data) |
e27e3dac6
|
413 |
{ |
b2a4ac0c2
|
414 |
struct edac_device_block *block = to_block(kobj); |
079708b91
|
415 416 |
return sprintf(data, "%u ", block->counters.ue_count); |
e27e3dac6
|
417 |
} |
b2a4ac0c2
|
418 419 |
static ssize_t block_ce_count_show(struct kobject *kobj, struct attribute *attr, char *data) |
e27e3dac6
|
420 |
{ |
b2a4ac0c2
|
421 |
struct edac_device_block *block = to_block(kobj); |
079708b91
|
422 423 |
return sprintf(data, "%u ", block->counters.ce_count); |
e27e3dac6
|
424 |
} |
e27e3dac6
|
425 426 427 428 429 430 431 |
/* DEVICE block kobject release() function */ static void edac_device_ctrl_block_release(struct kobject *kobj) { struct edac_device_block *block; debugf1("%s() ", __func__); |
1c3631ff1
|
432 |
/* get the container of the kobj */ |
e27e3dac6
|
433 |
block = to_block(kobj); |
1c3631ff1
|
434 435 436 437 438 |
/* map from 'block kobj' to 'block->instance->controller->main_kobj' * now 'release' the block kobject */ kobject_put(&block->instance->ctl->kobj); |
e27e3dac6
|
439 |
} |
e27e3dac6
|
440 441 442 |
/* Function to 'show' fields from the edac_dev 'block' structure */ static ssize_t edac_dev_block_show(struct kobject *kobj, |
052dfb45c
|
443 |
struct attribute *attr, char *buffer) |
e27e3dac6
|
444 |
{ |
b2a4ac0c2
|
445 446 |
struct edac_dev_sysfs_block_attribute *block_attr = to_block_attr(attr); |
e27e3dac6
|
447 |
|
079708b91
|
448 |
if (block_attr->show) |
b2a4ac0c2
|
449 |
return block_attr->show(kobj, attr, buffer); |
079708b91
|
450 |
return -EIO; |
e27e3dac6
|
451 |
} |
e27e3dac6
|
452 453 |
/* Function to 'store' fields into the edac_dev 'block' structure */ static ssize_t edac_dev_block_store(struct kobject *kobj, |
052dfb45c
|
454 455 |
struct attribute *attr, const char *buffer, size_t count) |
e27e3dac6
|
456 |
{ |
b2a4ac0c2
|
457 458 459 |
struct edac_dev_sysfs_block_attribute *block_attr; block_attr = to_block_attr(attr); |
e27e3dac6
|
460 |
|
079708b91
|
461 |
if (block_attr->store) |
b2a4ac0c2
|
462 |
return block_attr->store(kobj, attr, buffer, count); |
079708b91
|
463 |
return -EIO; |
e27e3dac6
|
464 |
} |
e27e3dac6
|
465 |
/* edac_dev file operations for a 'block' */ |
52cf25d0a
|
466 |
static const struct sysfs_ops device_block_ops = { |
079708b91
|
467 468 |
.show = edac_dev_block_show, .store = edac_dev_block_store |
e27e3dac6
|
469 |
}; |
e27e3dac6
|
470 |
#define BLOCK_ATTR(_name,_mode,_show,_store) \ |
b2a4ac0c2
|
471 |
static struct edac_dev_sysfs_block_attribute attr_block_##_name = { \ |
052dfb45c
|
472 473 474 |
.attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ |
e27e3dac6
|
475 |
}; |
079708b91
|
476 477 |
BLOCK_ATTR(ce_count, S_IRUGO, block_ce_count_show, NULL); BLOCK_ATTR(ue_count, S_IRUGO, block_ue_count_show, NULL); |
e27e3dac6
|
478 479 |
/* list of edac_dev 'block' attributes */ |
b2a4ac0c2
|
480 |
static struct edac_dev_sysfs_block_attribute *device_block_attr[] = { |
e27e3dac6
|
481 482 483 484 485 486 487 488 489 |
&attr_block_ce_count, &attr_block_ue_count, NULL, }; /* The 'ktype' for each edac_dev 'block' */ static struct kobj_type ktype_block_ctrl = { .release = edac_device_ctrl_block_release, .sysfs_ops = &device_block_ops, |
079708b91
|
490 |
.default_attrs = (struct attribute **)device_block_attr, |
e27e3dac6
|
491 |
}; |
1c3631ff1
|
492 |
/* block ctor/dtor code */ |
e27e3dac6
|
493 494 495 496 |
/* * edac_device_create_block */ |
079708b91
|
497 |
static int edac_device_create_block(struct edac_device_ctl_info *edac_dev, |
052dfb45c
|
498 |
struct edac_device_instance *instance, |
1c3631ff1
|
499 |
struct edac_device_block *block) |
e27e3dac6
|
500 |
{ |
fd309a9d8
|
501 |
int i; |
e27e3dac6
|
502 |
int err; |
fd309a9d8
|
503 |
struct edac_dev_sysfs_block_attribute *sysfs_attrib; |
1c3631ff1
|
504 |
struct kobject *main_kobj; |
e27e3dac6
|
505 |
|
b2a4ac0c2
|
506 507 508 509 510 511 |
debugf4("%s() Instance '%s' inst_p=%p block '%s' block_p=%p ", __func__, instance->name, instance, block->name, block); debugf4("%s() block kobj=%p block kobj->parent=%p ", __func__, &block->kobj, &block->kobj.parent); |
e27e3dac6
|
512 513 |
/* init this block's kobject */ |
079708b91
|
514 |
memset(&block->kobj, 0, sizeof(struct kobject)); |
e27e3dac6
|
515 |
|
1c3631ff1
|
516 517 518 519 520 521 522 523 524 525 |
/* bump the main kobject's reference count for this controller * and this instance is dependant on the main */ main_kobj = kobject_get(&edac_dev->kobj); if (!main_kobj) { err = -ENODEV; goto err_out; } /* Add this block's kobject */ |
b2ed215a3
|
526 527 528 |
err = kobject_init_and_add(&block->kobj, &ktype_block_ctrl, &instance->kobj, "%s", block->name); |
e27e3dac6
|
529 |
if (err) { |
1c3631ff1
|
530 531 |
debugf1("%s() Failed to register instance '%s' ", |
079708b91
|
532 |
__func__, block->name); |
1c3631ff1
|
533 534 535 |
kobject_put(main_kobj); err = -ENODEV; goto err_out; |
e27e3dac6
|
536 |
} |
fd309a9d8
|
537 538 539 540 |
/* If there are driver level block attributes, then added them * to the block kobject */ sysfs_attrib = block->block_attributes; |
b2a4ac0c2
|
541 542 543 544 545 546 547 548 549 550 551 |
if (sysfs_attrib && block->nr_attribs) { for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) { debugf4("%s() creating block attrib='%s' " "attrib->%p to kobj=%p ", __func__, sysfs_attrib->attr.name, sysfs_attrib, &block->kobj); /* Create each block_attribute file */ |
fd309a9d8
|
552 |
err = sysfs_create_file(&block->kobj, |
b2a4ac0c2
|
553 |
&sysfs_attrib->attr); |
fd309a9d8
|
554 555 |
if (err) goto err_on_attrib; |
fd309a9d8
|
556 557 |
} } |
b2ed215a3
|
558 |
kobject_uevent(&block->kobj, KOBJ_ADD); |
fd309a9d8
|
559 |
|
e27e3dac6
|
560 |
return 0; |
fd309a9d8
|
561 |
|
1c3631ff1
|
562 |
/* Error unwind stack */ |
fd309a9d8
|
563 |
err_on_attrib: |
c10997f65
|
564 |
kobject_put(&block->kobj); |
fd309a9d8
|
565 |
|
1c3631ff1
|
566 |
err_out: |
fd309a9d8
|
567 |
return err; |
e27e3dac6
|
568 569 570 |
} /* |
1c3631ff1
|
571 |
* edac_device_delete_block(edac_dev,block); |
e27e3dac6
|
572 |
*/ |
079708b91
|
573 |
static void edac_device_delete_block(struct edac_device_ctl_info *edac_dev, |
1c3631ff1
|
574 |
struct edac_device_block *block) |
e27e3dac6
|
575 |
{ |
1c3631ff1
|
576 577 |
struct edac_dev_sysfs_block_attribute *sysfs_attrib; int i; |
e27e3dac6
|
578 |
|
1c3631ff1
|
579 580 581 582 583 |
/* if this block has 'attributes' then we need to iterate over the list * and 'remove' the attributes on this block */ sysfs_attrib = block->block_attributes; if (sysfs_attrib && block->nr_attribs) { |
b2a4ac0c2
|
584 585 586 |
for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) { /* remove each block_attrib file */ |
1c3631ff1
|
587 588 589 590 |
sysfs_remove_file(&block->kobj, (struct attribute *) sysfs_attrib); } } |
e27e3dac6
|
591 |
|
1c3631ff1
|
592 593 594 |
/* unregister this block's kobject, SEE: * edac_device_ctrl_block_release() callback operation */ |
c10997f65
|
595 |
kobject_put(&block->kobj); |
e27e3dac6
|
596 |
} |
1c3631ff1
|
597 |
/* instance ctor/dtor code */ |
e27e3dac6
|
598 599 600 601 602 |
/* * edac_device_create_instance * create just one instance of an edac_device 'instance' */ |
079708b91
|
603 |
static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev, |
052dfb45c
|
604 |
int idx) |
e27e3dac6
|
605 606 607 608 |
{ int i, j; int err; struct edac_device_instance *instance; |
1c3631ff1
|
609 |
struct kobject *main_kobj; |
e27e3dac6
|
610 611 612 613 |
instance = &edac_dev->instances[idx]; /* Init the instance's kobject */ |
079708b91
|
614 |
memset(&instance->kobj, 0, sizeof(struct kobject)); |
e27e3dac6
|
615 |
|
1c3631ff1
|
616 |
instance->ctl = edac_dev; |
e27e3dac6
|
617 |
|
1c3631ff1
|
618 619 620 621 622 623 624 625 |
/* bump the main kobject's reference count for this controller * and this instance is dependant on the main */ main_kobj = kobject_get(&edac_dev->kobj); if (!main_kobj) { err = -ENODEV; goto err_out; } |
e27e3dac6
|
626 |
|
b2ed215a3
|
627 628 629 |
/* Formally register this instance's kobject under the edac_device */ err = kobject_init_and_add(&instance->kobj, &ktype_instance_ctrl, &edac_dev->kobj, "%s", instance->name); |
e27e3dac6
|
630 631 632 |
if (err != 0) { debugf2("%s() Failed to register instance '%s' ", |
079708b91
|
633 |
__func__, instance->name); |
1c3631ff1
|
634 635 |
kobject_put(main_kobj); goto err_out; |
e27e3dac6
|
636 |
} |
b2a4ac0c2
|
637 638 |
debugf4("%s() now register '%d' blocks for instance %d ", |
079708b91
|
639 |
__func__, instance->nr_blocks, idx); |
e27e3dac6
|
640 641 |
/* register all blocks of this instance */ |
079708b91
|
642 |
for (i = 0; i < instance->nr_blocks; i++) { |
1c3631ff1
|
643 644 |
err = edac_device_create_block(edac_dev, instance, &instance->blocks[i]); |
e27e3dac6
|
645 |
if (err) { |
1c3631ff1
|
646 |
/* If any fail, remove all previous ones */ |
52490c8d0
|
647 |
for (j = 0; j < i; j++) |
1c3631ff1
|
648 649 650 |
edac_device_delete_block(edac_dev, &instance->blocks[j]); goto err_release_instance_kobj; |
e27e3dac6
|
651 652 |
} } |
b2ed215a3
|
653 |
kobject_uevent(&instance->kobj, KOBJ_ADD); |
e27e3dac6
|
654 |
|
b2a4ac0c2
|
655 656 |
debugf4("%s() Registered instance %d '%s' kobject ", |
e27e3dac6
|
657 658 659 |
__func__, idx, instance->name); return 0; |
1c3631ff1
|
660 661 662 |
/* error unwind stack */ err_release_instance_kobj: |
c10997f65
|
663 |
kobject_put(&instance->kobj); |
1c3631ff1
|
664 665 666 |
err_out: return err; |
e27e3dac6
|
667 668 669 670 671 672 |
} /* * edac_device_remove_instance * remove an edac_device instance */ |
079708b91
|
673 674 |
static void edac_device_delete_instance(struct edac_device_ctl_info *edac_dev, int idx) |
e27e3dac6
|
675 |
{ |
e27e3dac6
|
676 |
struct edac_device_instance *instance; |
1c3631ff1
|
677 |
int i; |
e27e3dac6
|
678 679 680 681 |
instance = &edac_dev->instances[idx]; /* unregister all blocks in this instance */ |
52490c8d0
|
682 |
for (i = 0; i < instance->nr_blocks; i++) |
1c3631ff1
|
683 |
edac_device_delete_block(edac_dev, &instance->blocks[i]); |
e27e3dac6
|
684 |
|
1c3631ff1
|
685 686 687 |
/* unregister this instance's kobject, SEE: * edac_device_ctrl_instance_release() for callback operation */ |
c10997f65
|
688 |
kobject_put(&instance->kobj); |
e27e3dac6
|
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 |
} /* * edac_device_create_instances * create the first level of 'instances' for this device * (ie 'cache' might have 'cache0', 'cache1', 'cache2', etc */ static int edac_device_create_instances(struct edac_device_ctl_info *edac_dev) { int i, j; int err; debugf0("%s() ", __func__); /* iterate over creation of the instances */ |
079708b91
|
705 706 |
for (i = 0; i < edac_dev->nr_instances; i++) { err = edac_device_create_instance(edac_dev, i); |
e27e3dac6
|
707 708 |
if (err) { /* unwind previous instances on error */ |
52490c8d0
|
709 |
for (j = 0; j < i; j++) |
079708b91
|
710 |
edac_device_delete_instance(edac_dev, j); |
e27e3dac6
|
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 |
return err; } } return 0; } /* * edac_device_delete_instances(edac_dev); * unregister all the kobjects of the instances */ static void edac_device_delete_instances(struct edac_device_ctl_info *edac_dev) { int i; /* iterate over creation of the instances */ |
52490c8d0
|
727 |
for (i = 0; i < edac_dev->nr_instances; i++) |
079708b91
|
728 |
edac_device_delete_instance(edac_dev, i); |
e27e3dac6
|
729 |
} |
1c3631ff1
|
730 |
/* edac_dev sysfs ctor/dtor code */ |
e27e3dac6
|
731 732 |
/* |
1c3631ff1
|
733 |
* edac_device_add_main_sysfs_attributes |
42a8e397a
|
734 735 |
* add some attributes to this instance's main kobject */ |
1c3631ff1
|
736 |
static int edac_device_add_main_sysfs_attributes( |
42a8e397a
|
737 738 |
struct edac_device_ctl_info *edac_dev) { |
052dfb45c
|
739 |
struct edac_dev_sysfs_attribute *sysfs_attrib; |
1c3631ff1
|
740 |
int err = 0; |
052dfb45c
|
741 |
|
052dfb45c
|
742 |
sysfs_attrib = edac_dev->sysfs_attributes; |
1c3631ff1
|
743 744 745 746 747 748 |
if (sysfs_attrib) { /* iterate over the array and create an attribute for each * entry in the list */ while (sysfs_attrib->attr.name != NULL) { err = sysfs_create_file(&edac_dev->kobj, |
052dfb45c
|
749 |
(struct attribute*) sysfs_attrib); |
1c3631ff1
|
750 751 |
if (err) goto err_out; |
052dfb45c
|
752 |
|
1c3631ff1
|
753 754 |
sysfs_attrib++; } |
052dfb45c
|
755 |
} |
42a8e397a
|
756 |
|
1c3631ff1
|
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 |
err_out: return err; } /* * edac_device_remove_main_sysfs_attributes * remove any attributes to this instance's main kobject */ static void edac_device_remove_main_sysfs_attributes( struct edac_device_ctl_info *edac_dev) { struct edac_dev_sysfs_attribute *sysfs_attrib; /* if there are main attributes, defined, remove them. First, * point to the start of the array and iterate over it * removing each attribute listed from this device's instance's kobject */ sysfs_attrib = edac_dev->sysfs_attributes; if (sysfs_attrib) { while (sysfs_attrib->attr.name != NULL) { sysfs_remove_file(&edac_dev->kobj, (struct attribute *) sysfs_attrib); sysfs_attrib++; } } |
42a8e397a
|
782 783 784 |
} /* |
e27e3dac6
|
785 786 |
* edac_device_create_sysfs() Constructor * |
1c3631ff1
|
787 788 789 790 791 792 |
* accept a created edac_device control structure * and 'export' it to sysfs. The 'main' kobj should already have been * created. 'instance' and 'block' kobjects should be registered * along with any 'block' attributes from the low driver. In addition, * the main attributes (if any) are connected to the main kobject of * the control structure. |
e27e3dac6
|
793 794 795 796 797 798 799 800 |
* * Return: * 0 Success * !0 Failure */ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev) { int err; |
079708b91
|
801 |
struct kobject *edac_kobj = &edac_dev->kobj; |
e27e3dac6
|
802 |
|
e27e3dac6
|
803 804 |
debugf0("%s() idx=%d ", __func__, edac_dev->dev_idx); |
1c3631ff1
|
805 806 807 808 809 810 |
/* go create any main attributes callers wants */ err = edac_device_add_main_sysfs_attributes(edac_dev); if (err) { debugf0("%s() failed to add sysfs attribs ", __func__); goto err_out; |
42a8e397a
|
811 |
} |
e27e3dac6
|
812 813 814 815 |
/* create a symlink from the edac device * to the platform 'device' being used for this */ err = sysfs_create_link(edac_kobj, |
079708b91
|
816 |
&edac_dev->dev->kobj, EDAC_DEVICE_SYMLINK); |
e27e3dac6
|
817 818 819 820 |
if (err) { debugf0("%s() sysfs_create_link() returned err= %d ", __func__, err); |
1c3631ff1
|
821 |
goto err_remove_main_attribs; |
e27e3dac6
|
822 |
} |
1c3631ff1
|
823 824 825 826 |
/* Create the first level instance directories * In turn, the nested blocks beneath the instances will * be registered as well */ |
e27e3dac6
|
827 |
err = edac_device_create_instances(edac_dev); |
1c3631ff1
|
828 829 830 831 |
if (err) { debugf0("%s() edac_device_create_instances() " "returned err= %d ", __func__, err); |
42a8e397a
|
832 |
goto err_remove_link; |
1c3631ff1
|
833 |
} |
b2a4ac0c2
|
834 835 |
debugf4("%s() create-instances done, idx=%d ", |
1c3631ff1
|
836 |
__func__, edac_dev->dev_idx); |
e27e3dac6
|
837 838 839 840 |
return 0; /* Error unwind stack */ |
42a8e397a
|
841 842 843 |
err_remove_link: /* remove the sym link */ sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK); |
e27e3dac6
|
844 |
|
1c3631ff1
|
845 846 |
err_remove_main_attribs: edac_device_remove_main_sysfs_attributes(edac_dev); |
e27e3dac6
|
847 |
|
1c3631ff1
|
848 |
err_out: |
e27e3dac6
|
849 850 851 852 853 854 |
return err; } /* * edac_device_remove_sysfs() destructor * |
1c3631ff1
|
855 |
* given an edac_device struct, tear down the kobject resources |
e27e3dac6
|
856 857 858 859 860 |
*/ void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev) { debugf0("%s() ", __func__); |
1c3631ff1
|
861 862 |
/* remove any main attributes for this device */ edac_device_remove_main_sysfs_attributes(edac_dev); |
e27e3dac6
|
863 |
|
1c3631ff1
|
864 |
/* remove the device sym link */ |
e27e3dac6
|
865 |
sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK); |
1c3631ff1
|
866 867 |
/* walk the instance/block kobject tree, deconstructing it */ edac_device_delete_instances(edac_dev); |
e27e3dac6
|
868 |
} |