diff options
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/edac_core.h | 67 | ||||
-rw-r--r-- | drivers/edac/edac_device.c | 94 | ||||
-rw-r--r-- | drivers/edac/edac_device_sysfs.c | 22 |
3 files changed, 115 insertions, 68 deletions
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index e49dce069d1f..fca1990945a9 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h | |||
@@ -468,32 +468,34 @@ struct edac_device_counter { | |||
468 | u32 ce_count; | 468 | u32 ce_count; |
469 | }; | 469 | }; |
470 | 470 | ||
471 | /* | 471 | /* forward reference */ |
472 | * An array of these is passed to the alloc() function | 472 | struct edac_device_ctl_info; |
473 | * to specify attributes of the edac_block | 473 | struct edac_device_block; |
474 | */ | ||
475 | struct edac_attrib_spec { | ||
476 | char name[EDAC_DEVICE_NAME_LEN + 1]; | ||
477 | 474 | ||
478 | int type; | 475 | /* edac_dev_sysfs_attribute structure |
479 | #define EDAC_ATTR_INT 0x01 | 476 | * used for driver sysfs attributes in mem_ctl_info |
480 | #define EDAC_ATTR_CHAR 0x02 | 477 | * for extra controls and attributes: |
478 | * like high level error Injection controls | ||
479 | */ | ||
480 | struct edac_dev_sysfs_attribute { | ||
481 | struct attribute attr; | ||
482 | ssize_t (*show)(struct edac_device_ctl_info *, char *); | ||
483 | ssize_t (*store)(struct edac_device_ctl_info *, const char *, size_t); | ||
481 | }; | 484 | }; |
482 | 485 | ||
483 | /* Attribute control structure | 486 | /* edac_dev_sysfs_block_attribute structure |
484 | * In this structure is a pointer to the driver's edac_attrib_spec | 487 | * used in leaf 'block' nodes for adding controls/attributes |
485 | * The life of this pointer is inclusive in the life of the driver's | ||
486 | * life cycle. | ||
487 | */ | 488 | */ |
488 | struct edac_attrib { | 489 | struct edac_dev_sysfs_block_attribute { |
489 | struct edac_device_block *block; /* Up Pointer */ | 490 | struct attribute attr; |
490 | 491 | ssize_t (*show)(struct kobject *, struct attribute *, char *); | |
491 | struct edac_attrib_spec *spec; /* ptr to module spec entry */ | 492 | ssize_t (*store)(struct kobject *, struct attribute *, |
492 | 493 | const char *, size_t); | |
493 | union { /* actual value */ | 494 | struct edac_device_block *block; |
494 | int edac_attrib_int_value; | 495 | |
495 | char edac_attrib_char_value[EDAC_ATTRIB_VALUE_LEN + 1]; | 496 | /* low driver use */ |
496 | } edac_attrib_value; | 497 | void *arg; |
498 | unsigned int value; | ||
497 | }; | 499 | }; |
498 | 500 | ||
499 | /* device block control structure */ | 501 | /* device block control structure */ |
@@ -504,7 +506,9 @@ struct edac_device_block { | |||
504 | struct edac_device_counter counters; /* basic UE and CE counters */ | 506 | struct edac_device_counter counters; /* basic UE and CE counters */ |
505 | 507 | ||
506 | int nr_attribs; /* how many attributes */ | 508 | int nr_attribs; /* how many attributes */ |
507 | struct edac_attrib *attribs; /* this block's attributes */ | 509 | |
510 | /* this block's attributes, could be NULL */ | ||
511 | struct edac_dev_sysfs_block_attribute *block_attributes; | ||
508 | 512 | ||
509 | /* edac sysfs device control */ | 513 | /* edac sysfs device control */ |
510 | struct kobject kobj; | 514 | struct kobject kobj; |
@@ -526,15 +530,6 @@ struct edac_device_instance { | |||
526 | struct completion kobj_complete; | 530 | struct completion kobj_complete; |
527 | }; | 531 | }; |
528 | 532 | ||
529 | /* edac_dev_sysfs_attribute structure | ||
530 | * used for driver sysfs attributes and in mem_ctl_info | ||
531 | * sysfs top level entries | ||
532 | */ | ||
533 | struct edac_dev_sysfs_attribute { | ||
534 | struct attribute attr; | ||
535 | ssize_t (*show)(struct edac_device_ctl_info *,char *); | ||
536 | ssize_t (*store)(struct edac_device_ctl_info *, const char *,size_t); | ||
537 | }; | ||
538 | 533 | ||
539 | /* | 534 | /* |
540 | * Abstract edac_device control info structure | 535 | * Abstract edac_device control info structure |
@@ -635,12 +630,10 @@ struct edac_device_ctl_info { | |||
635 | */ | 630 | */ |
636 | extern struct edac_device_ctl_info *edac_device_alloc_ctl_info( | 631 | extern struct edac_device_ctl_info *edac_device_alloc_ctl_info( |
637 | unsigned sizeof_private, | 632 | unsigned sizeof_private, |
638 | char *edac_device_name, | 633 | char *edac_device_name, unsigned nr_instances, |
639 | unsigned nr_instances, | 634 | char *edac_block_name, unsigned nr_blocks, |
640 | char *edac_block_name, | ||
641 | unsigned nr_blocks, | ||
642 | unsigned offset_value, | 635 | unsigned offset_value, |
643 | struct edac_attrib_spec *attrib_spec, | 636 | struct edac_dev_sysfs_block_attribute *block_attributes, |
644 | unsigned nr_attribs); | 637 | unsigned nr_attribs); |
645 | 638 | ||
646 | /* The offset value can be: | 639 | /* The offset value can be: |
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index b53e8d51d9a5..8264e3728c79 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c | |||
@@ -67,12 +67,12 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( | |||
67 | char *edac_device_name, unsigned nr_instances, | 67 | char *edac_device_name, unsigned nr_instances, |
68 | char *edac_block_name, unsigned nr_blocks, | 68 | char *edac_block_name, unsigned nr_blocks, |
69 | unsigned offset_value, /* zero, 1, or other based offset */ | 69 | unsigned offset_value, /* zero, 1, or other based offset */ |
70 | struct edac_attrib_spec *attrib_spec, unsigned nr_attribs) | 70 | struct edac_dev_sysfs_block_attribute *attrib_spec, unsigned nr_attrib) |
71 | { | 71 | { |
72 | struct edac_device_ctl_info *dev_ctl; | 72 | struct edac_device_ctl_info *dev_ctl; |
73 | struct edac_device_instance *dev_inst, *inst; | 73 | struct edac_device_instance *dev_inst, *inst; |
74 | struct edac_device_block *dev_blk, *blk_p, *blk; | 74 | struct edac_device_block *dev_blk, *blk_p, *blk; |
75 | struct edac_attrib *dev_attrib, *attrib_p, *attrib; | 75 | struct edac_dev_sysfs_block_attribute *dev_attrib, *attrib_p, *attrib; |
76 | unsigned total_size; | 76 | unsigned total_size; |
77 | unsigned count; | 77 | unsigned count; |
78 | unsigned instance, block, attr; | 78 | unsigned instance, block, attr; |
@@ -81,29 +81,47 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( | |||
81 | debugf1("%s() instances=%d blocks=%d\n", | 81 | debugf1("%s() instances=%d blocks=%d\n", |
82 | __func__, nr_instances, nr_blocks); | 82 | __func__, nr_instances, nr_blocks); |
83 | 83 | ||
84 | /* Figure out the offsets of the various items from the start of an | 84 | /* Calculate the size of memory we need to allocate AND |
85 | * ctl_info structure. We want the alignment of each item | 85 | * determine the offsets of the various item arrays |
86 | * (instance,block,attrib) from the start of an allocated structure. | ||
87 | * We want the alignment of each item (instance,block,attrib) | ||
86 | * to be at least as stringent as what the compiler would | 88 | * to be at least as stringent as what the compiler would |
87 | * provide if we could simply hardcode everything into a single struct. | 89 | * provide if we could simply hardcode everything into a single struct. |
88 | */ | 90 | */ |
89 | dev_ctl = (struct edac_device_ctl_info *)NULL; | 91 | dev_ctl = (struct edac_device_ctl_info *)NULL; |
90 | 92 | ||
91 | /* Calc the 'end' offset past the ctl_info structure */ | 93 | /* Calc the 'end' offset past end of ONE ctl_info structure |
94 | * which will become the start of the 'instance' array | ||
95 | */ | ||
92 | dev_inst = edac_align_ptr(&dev_ctl[1], sizeof(*dev_inst)); | 96 | dev_inst = edac_align_ptr(&dev_ctl[1], sizeof(*dev_inst)); |
93 | 97 | ||
94 | /* Calc the 'end' offset past the instance array */ | 98 | /* Calc the 'end' offset past the instance array within the ctl_info |
99 | * which will become the start of the block array | ||
100 | */ | ||
95 | dev_blk = edac_align_ptr(&dev_inst[nr_instances], sizeof(*dev_blk)); | 101 | dev_blk = edac_align_ptr(&dev_inst[nr_instances], sizeof(*dev_blk)); |
96 | 102 | ||
97 | /* Calc the 'end' offset past the dev_blk array */ | 103 | /* Calc the 'end' offset past the dev_blk array |
104 | * which will become the start of the attrib array, if any. | ||
105 | */ | ||
98 | count = nr_instances * nr_blocks; | 106 | count = nr_instances * nr_blocks; |
99 | dev_attrib = edac_align_ptr(&dev_blk[count], sizeof(*dev_attrib)); | 107 | dev_attrib = edac_align_ptr(&dev_blk[count], sizeof(*dev_attrib)); |
100 | 108 | ||
101 | /* Check for case of NO attributes specified */ | 109 | /* Check for case of when an attribute array is specified */ |
102 | if (nr_attribs > 0) | 110 | if (nr_attrib > 0) { |
103 | count *= nr_attribs; | 111 | /* calc how many nr_attrib we need */ |
112 | count *= nr_attrib; | ||
104 | 113 | ||
105 | /* Calc the 'end' offset past the attributes array */ | 114 | /* Calc the 'end' offset past the attributes array */ |
106 | pvt = edac_align_ptr(&dev_attrib[count], sz_private); | 115 | pvt = edac_align_ptr(&dev_attrib[count], sz_private); |
116 | } else { | ||
117 | /* no attribute array specificed */ | ||
118 | pvt = edac_align_ptr(dev_attrib, sz_private); | ||
119 | } | ||
120 | |||
121 | /* 'pvt' now points to where the private data area is. | ||
122 | * At this point 'pvt' (like dev_inst,dev_blk and dev_attrib) | ||
123 | * is baselined at ZERO | ||
124 | */ | ||
107 | total_size = ((unsigned long)pvt) + sz_private; | 125 | total_size = ((unsigned long)pvt) + sz_private; |
108 | 126 | ||
109 | /* Allocate the amount of memory for the set of control structures */ | 127 | /* Allocate the amount of memory for the set of control structures */ |
@@ -111,17 +129,22 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( | |||
111 | if (dev_ctl == NULL) | 129 | if (dev_ctl == NULL) |
112 | return NULL; | 130 | return NULL; |
113 | 131 | ||
114 | /* Adjust pointers so they point within the memory we just allocated | 132 | /* Adjust pointers so they point within the actual memory we |
115 | * rather than an imaginary chunk of memory located at address 0. | 133 | * just allocated rather than an imaginary chunk of memory |
134 | * located at address 0. | ||
135 | * 'dev_ctl' points to REAL memory, while the others are | ||
136 | * ZERO based and thus need to be adjusted to point within | ||
137 | * the allocated memory. | ||
116 | */ | 138 | */ |
117 | dev_inst = (struct edac_device_instance *) | 139 | dev_inst = (struct edac_device_instance *) |
118 | (((char *)dev_ctl) + ((unsigned long)dev_inst)); | 140 | (((char *)dev_ctl) + ((unsigned long)dev_inst)); |
119 | dev_blk = (struct edac_device_block *) | 141 | dev_blk = (struct edac_device_block *) |
120 | (((char *)dev_ctl) + ((unsigned long)dev_blk)); | 142 | (((char *)dev_ctl) + ((unsigned long)dev_blk)); |
121 | dev_attrib = (struct edac_attrib *) | 143 | dev_attrib = (struct edac_dev_sysfs_block_attribute *) |
122 | (((char *)dev_ctl) + ((unsigned long)dev_attrib)); | 144 | (((char *)dev_ctl) + ((unsigned long)dev_attrib)); |
123 | pvt = sz_private ? (((char *)dev_ctl) + ((unsigned long)pvt)) : NULL; | 145 | pvt = sz_private ? (((char *)dev_ctl) + ((unsigned long)pvt)) : NULL; |
124 | 146 | ||
147 | /* Begin storing the information into the control info structure */ | ||
125 | dev_ctl->nr_instances = nr_instances; | 148 | dev_ctl->nr_instances = nr_instances; |
126 | dev_ctl->instances = dev_inst; | 149 | dev_ctl->instances = dev_inst; |
127 | dev_ctl->pvt_info = pvt; | 150 | dev_ctl->pvt_info = pvt; |
@@ -145,28 +168,37 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( | |||
145 | for (block = 0; block < nr_blocks; block++) { | 168 | for (block = 0; block < nr_blocks; block++) { |
146 | blk = &blk_p[block]; | 169 | blk = &blk_p[block]; |
147 | blk->instance = inst; | 170 | blk->instance = inst; |
148 | blk->nr_attribs = nr_attribs; | ||
149 | attrib_p = &dev_attrib[block * nr_attribs]; | ||
150 | blk->attribs = attrib_p; | ||
151 | snprintf(blk->name, sizeof(blk->name), | 171 | snprintf(blk->name, sizeof(blk->name), |
152 | "%s%d", edac_block_name, block+offset_value); | 172 | "%s%d", edac_block_name, block+offset_value); |
153 | 173 | ||
154 | debugf1("%s() instance=%d block=%d name=%s\n", | 174 | debugf1("%s() instance=%d block=%d name=%s\n", |
155 | __func__, instance, block, blk->name); | 175 | __func__, instance, block, blk->name); |
156 | 176 | ||
157 | if (attrib_spec != NULL) { | 177 | /* if there are NO attributes OR no attribute pointer |
158 | /* when there is an attrib_spec passed int then | 178 | * then continue on to next block iteration |
159 | * Initialize every attrib of each block | 179 | */ |
160 | */ | 180 | if ((nr_attrib == 0) || (attrib_spec == NULL)) |
161 | for (attr = 0; attr < nr_attribs; attr++) { | 181 | continue; |
162 | attrib = &attrib_p[attr]; | 182 | |
163 | attrib->block = blk; | 183 | /* setup the attribute array for this block */ |
164 | 184 | blk->nr_attribs = nr_attrib; | |
165 | /* Link each attribute to the caller's | 185 | attrib_p = &dev_attrib[block*nr_instances*nr_attrib]; |
166 | * spec entry, for name and type | 186 | blk->block_attributes = attrib_p; |
167 | */ | 187 | |
168 | attrib->spec = &attrib_spec[attr]; | 188 | /* Initialize every user specified attribute in this |
169 | } | 189 | * block with the data the caller passed in |
190 | */ | ||
191 | for (attr = 0; attr < nr_attrib; attr++) { | ||
192 | attrib = &attrib_p[attr]; | ||
193 | attrib->attr = attrib_spec->attr; | ||
194 | attrib->show = attrib_spec->show; | ||
195 | attrib->store = attrib_spec->store; | ||
196 | |||
197 | /* up reference this block */ | ||
198 | attrib->block = blk; | ||
199 | |||
200 | /* bump the attrib_spec */ | ||
201 | attrib_spec++; | ||
170 | } | 202 | } |
171 | } | 203 | } |
172 | } | 204 | } |
diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c index 7a233e6e2b36..235b4c79355d 100644 --- a/drivers/edac/edac_device_sysfs.c +++ b/drivers/edac/edac_device_sysfs.c | |||
@@ -456,8 +456,10 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev, | |||
456 | struct edac_device_instance *instance, | 456 | struct edac_device_instance *instance, |
457 | int idx) | 457 | int idx) |
458 | { | 458 | { |
459 | int i; | ||
459 | int err; | 460 | int err; |
460 | struct edac_device_block *block; | 461 | struct edac_device_block *block; |
462 | struct edac_dev_sysfs_block_attribute *sysfs_attrib; | ||
461 | 463 | ||
462 | block = &instance->blocks[idx]; | 464 | block = &instance->blocks[idx]; |
463 | 465 | ||
@@ -480,7 +482,27 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev, | |||
480 | return err; | 482 | return err; |
481 | } | 483 | } |
482 | 484 | ||
485 | /* If there are driver level block attributes, then added them | ||
486 | * to the block kobject | ||
487 | */ | ||
488 | sysfs_attrib = block->block_attributes; | ||
489 | if (sysfs_attrib != NULL) { | ||
490 | for (i = 0; i < block->nr_attribs; i++) { | ||
491 | err = sysfs_create_file(&block->kobj, | ||
492 | (struct attribute *) &sysfs_attrib[i]); | ||
493 | if (err) | ||
494 | goto err_on_attrib; | ||
495 | |||
496 | sysfs_attrib++; | ||
497 | } | ||
498 | } | ||
499 | |||
483 | return 0; | 500 | return 0; |
501 | |||
502 | err_on_attrib: | ||
503 | kobject_unregister(&block->kobj); | ||
504 | |||
505 | return err; | ||
484 | } | 506 | } |
485 | 507 | ||
486 | /* | 508 | /* |