mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 22:21:42 +00:00
drivers/edac: fix leaf sysfs attribute
This patch fixes and enhances the driver level set of sysfs attributes that can be added to the 'block' level of an edac_device type of driver. There is a controller information structure, which contains one or more instances of device. Each instance will have one or more blocks of device specific counters. This patch fixes the ability to have more detailed attributes/controls for each of the 'blocks', providing for the addition of controls/attributes from the low level driver to user space via sysfs. Cc: Alan Cox alan@lxorguk.ukuu.org.uk Signed-off-by: Douglas Thompson <dougthompson@xmission.com> Cc: Greg KH <greg@kroah.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
7d8536fb48
commit
fd309a9d8e
@ -468,32 +468,34 @@ struct edac_device_counter {
|
|||||||
u32 ce_count;
|
u32 ce_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/* forward reference */
|
||||||
* An array of these is passed to the alloc() function
|
struct edac_device_ctl_info;
|
||||||
* to specify attributes of the edac_block
|
struct edac_device_block;
|
||||||
*/
|
|
||||||
struct edac_attrib_spec {
|
|
||||||
char name[EDAC_DEVICE_NAME_LEN + 1];
|
|
||||||
|
|
||||||
int type;
|
/* edac_dev_sysfs_attribute structure
|
||||||
#define EDAC_ATTR_INT 0x01
|
* used for driver sysfs attributes in mem_ctl_info
|
||||||
#define EDAC_ATTR_CHAR 0x02
|
* for extra controls and attributes:
|
||||||
|
* like high level error Injection controls
|
||||||
|
*/
|
||||||
|
struct edac_dev_sysfs_attribute {
|
||||||
|
struct attribute attr;
|
||||||
|
ssize_t (*show)(struct edac_device_ctl_info *, char *);
|
||||||
|
ssize_t (*store)(struct edac_device_ctl_info *, const char *, size_t);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Attribute control structure
|
/* edac_dev_sysfs_block_attribute structure
|
||||||
* In this structure is a pointer to the driver's edac_attrib_spec
|
* used in leaf 'block' nodes for adding controls/attributes
|
||||||
* The life of this pointer is inclusive in the life of the driver's
|
|
||||||
* life cycle.
|
|
||||||
*/
|
*/
|
||||||
struct edac_attrib {
|
struct edac_dev_sysfs_block_attribute {
|
||||||
struct edac_device_block *block; /* Up Pointer */
|
struct attribute attr;
|
||||||
|
ssize_t (*show)(struct kobject *, struct attribute *, char *);
|
||||||
|
ssize_t (*store)(struct kobject *, struct attribute *,
|
||||||
|
const char *, size_t);
|
||||||
|
struct edac_device_block *block;
|
||||||
|
|
||||||
struct edac_attrib_spec *spec; /* ptr to module spec entry */
|
/* low driver use */
|
||||||
|
void *arg;
|
||||||
union { /* actual value */
|
unsigned int value;
|
||||||
int edac_attrib_int_value;
|
|
||||||
char edac_attrib_char_value[EDAC_ATTRIB_VALUE_LEN + 1];
|
|
||||||
} edac_attrib_value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* device block control structure */
|
/* device block control structure */
|
||||||
@ -504,7 +506,9 @@ struct edac_device_block {
|
|||||||
struct edac_device_counter counters; /* basic UE and CE counters */
|
struct edac_device_counter counters; /* basic UE and CE counters */
|
||||||
|
|
||||||
int nr_attribs; /* how many attributes */
|
int nr_attribs; /* how many attributes */
|
||||||
struct edac_attrib *attribs; /* this block's attributes */
|
|
||||||
|
/* this block's attributes, could be NULL */
|
||||||
|
struct edac_dev_sysfs_block_attribute *block_attributes;
|
||||||
|
|
||||||
/* edac sysfs device control */
|
/* edac sysfs device control */
|
||||||
struct kobject kobj;
|
struct kobject kobj;
|
||||||
@ -526,15 +530,6 @@ struct edac_device_instance {
|
|||||||
struct completion kobj_complete;
|
struct completion kobj_complete;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* edac_dev_sysfs_attribute structure
|
|
||||||
* used for driver sysfs attributes and in mem_ctl_info
|
|
||||||
* sysfs top level entries
|
|
||||||
*/
|
|
||||||
struct edac_dev_sysfs_attribute {
|
|
||||||
struct attribute attr;
|
|
||||||
ssize_t (*show)(struct edac_device_ctl_info *,char *);
|
|
||||||
ssize_t (*store)(struct edac_device_ctl_info *, const char *,size_t);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Abstract edac_device control info structure
|
* Abstract edac_device control info structure
|
||||||
@ -635,12 +630,10 @@ struct edac_device_ctl_info {
|
|||||||
*/
|
*/
|
||||||
extern struct edac_device_ctl_info *edac_device_alloc_ctl_info(
|
extern struct edac_device_ctl_info *edac_device_alloc_ctl_info(
|
||||||
unsigned sizeof_private,
|
unsigned sizeof_private,
|
||||||
char *edac_device_name,
|
char *edac_device_name, unsigned nr_instances,
|
||||||
unsigned nr_instances,
|
char *edac_block_name, unsigned nr_blocks,
|
||||||
char *edac_block_name,
|
|
||||||
unsigned nr_blocks,
|
|
||||||
unsigned offset_value,
|
unsigned offset_value,
|
||||||
struct edac_attrib_spec *attrib_spec,
|
struct edac_dev_sysfs_block_attribute *block_attributes,
|
||||||
unsigned nr_attribs);
|
unsigned nr_attribs);
|
||||||
|
|
||||||
/* The offset value can be:
|
/* The offset value can be:
|
||||||
|
@ -67,12 +67,12 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
|
|||||||
char *edac_device_name, unsigned nr_instances,
|
char *edac_device_name, unsigned nr_instances,
|
||||||
char *edac_block_name, unsigned nr_blocks,
|
char *edac_block_name, unsigned nr_blocks,
|
||||||
unsigned offset_value, /* zero, 1, or other based offset */
|
unsigned offset_value, /* zero, 1, or other based offset */
|
||||||
struct edac_attrib_spec *attrib_spec, unsigned nr_attribs)
|
struct edac_dev_sysfs_block_attribute *attrib_spec, unsigned nr_attrib)
|
||||||
{
|
{
|
||||||
struct edac_device_ctl_info *dev_ctl;
|
struct edac_device_ctl_info *dev_ctl;
|
||||||
struct edac_device_instance *dev_inst, *inst;
|
struct edac_device_instance *dev_inst, *inst;
|
||||||
struct edac_device_block *dev_blk, *blk_p, *blk;
|
struct edac_device_block *dev_blk, *blk_p, *blk;
|
||||||
struct edac_attrib *dev_attrib, *attrib_p, *attrib;
|
struct edac_dev_sysfs_block_attribute *dev_attrib, *attrib_p, *attrib;
|
||||||
unsigned total_size;
|
unsigned total_size;
|
||||||
unsigned count;
|
unsigned count;
|
||||||
unsigned instance, block, attr;
|
unsigned instance, block, attr;
|
||||||
@ -81,29 +81,47 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
|
|||||||
debugf1("%s() instances=%d blocks=%d\n",
|
debugf1("%s() instances=%d blocks=%d\n",
|
||||||
__func__, nr_instances, nr_blocks);
|
__func__, nr_instances, nr_blocks);
|
||||||
|
|
||||||
/* Figure out the offsets of the various items from the start of an
|
/* Calculate the size of memory we need to allocate AND
|
||||||
* ctl_info structure. We want the alignment of each item
|
* determine the offsets of the various item arrays
|
||||||
|
* (instance,block,attrib) from the start of an allocated structure.
|
||||||
|
* We want the alignment of each item (instance,block,attrib)
|
||||||
* to be at least as stringent as what the compiler would
|
* to be at least as stringent as what the compiler would
|
||||||
* provide if we could simply hardcode everything into a single struct.
|
* provide if we could simply hardcode everything into a single struct.
|
||||||
*/
|
*/
|
||||||
dev_ctl = (struct edac_device_ctl_info *)NULL;
|
dev_ctl = (struct edac_device_ctl_info *)NULL;
|
||||||
|
|
||||||
/* Calc the 'end' offset past the ctl_info structure */
|
/* Calc the 'end' offset past end of ONE ctl_info structure
|
||||||
|
* which will become the start of the 'instance' array
|
||||||
|
*/
|
||||||
dev_inst = edac_align_ptr(&dev_ctl[1], sizeof(*dev_inst));
|
dev_inst = edac_align_ptr(&dev_ctl[1], sizeof(*dev_inst));
|
||||||
|
|
||||||
/* Calc the 'end' offset past the instance array */
|
/* Calc the 'end' offset past the instance array within the ctl_info
|
||||||
|
* which will become the start of the block array
|
||||||
|
*/
|
||||||
dev_blk = edac_align_ptr(&dev_inst[nr_instances], sizeof(*dev_blk));
|
dev_blk = edac_align_ptr(&dev_inst[nr_instances], sizeof(*dev_blk));
|
||||||
|
|
||||||
/* Calc the 'end' offset past the dev_blk array */
|
/* Calc the 'end' offset past the dev_blk array
|
||||||
|
* which will become the start of the attrib array, if any.
|
||||||
|
*/
|
||||||
count = nr_instances * nr_blocks;
|
count = nr_instances * nr_blocks;
|
||||||
dev_attrib = edac_align_ptr(&dev_blk[count], sizeof(*dev_attrib));
|
dev_attrib = edac_align_ptr(&dev_blk[count], sizeof(*dev_attrib));
|
||||||
|
|
||||||
/* Check for case of NO attributes specified */
|
/* Check for case of when an attribute array is specified */
|
||||||
if (nr_attribs > 0)
|
if (nr_attrib > 0) {
|
||||||
count *= nr_attribs;
|
/* calc how many nr_attrib we need */
|
||||||
|
count *= nr_attrib;
|
||||||
|
|
||||||
/* Calc the 'end' offset past the attributes array */
|
/* Calc the 'end' offset past the attributes array */
|
||||||
pvt = edac_align_ptr(&dev_attrib[count], sz_private);
|
pvt = edac_align_ptr(&dev_attrib[count], sz_private);
|
||||||
|
} else {
|
||||||
|
/* no attribute array specificed */
|
||||||
|
pvt = edac_align_ptr(dev_attrib, sz_private);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'pvt' now points to where the private data area is.
|
||||||
|
* At this point 'pvt' (like dev_inst,dev_blk and dev_attrib)
|
||||||
|
* is baselined at ZERO
|
||||||
|
*/
|
||||||
total_size = ((unsigned long)pvt) + sz_private;
|
total_size = ((unsigned long)pvt) + sz_private;
|
||||||
|
|
||||||
/* Allocate the amount of memory for the set of control structures */
|
/* 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(
|
|||||||
if (dev_ctl == NULL)
|
if (dev_ctl == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Adjust pointers so they point within the memory we just allocated
|
/* Adjust pointers so they point within the actual memory we
|
||||||
* rather than an imaginary chunk of memory located at address 0.
|
* just allocated rather than an imaginary chunk of memory
|
||||||
|
* located at address 0.
|
||||||
|
* 'dev_ctl' points to REAL memory, while the others are
|
||||||
|
* ZERO based and thus need to be adjusted to point within
|
||||||
|
* the allocated memory.
|
||||||
*/
|
*/
|
||||||
dev_inst = (struct edac_device_instance *)
|
dev_inst = (struct edac_device_instance *)
|
||||||
(((char *)dev_ctl) + ((unsigned long)dev_inst));
|
(((char *)dev_ctl) + ((unsigned long)dev_inst));
|
||||||
dev_blk = (struct edac_device_block *)
|
dev_blk = (struct edac_device_block *)
|
||||||
(((char *)dev_ctl) + ((unsigned long)dev_blk));
|
(((char *)dev_ctl) + ((unsigned long)dev_blk));
|
||||||
dev_attrib = (struct edac_attrib *)
|
dev_attrib = (struct edac_dev_sysfs_block_attribute *)
|
||||||
(((char *)dev_ctl) + ((unsigned long)dev_attrib));
|
(((char *)dev_ctl) + ((unsigned long)dev_attrib));
|
||||||
pvt = sz_private ? (((char *)dev_ctl) + ((unsigned long)pvt)) : NULL;
|
pvt = sz_private ? (((char *)dev_ctl) + ((unsigned long)pvt)) : NULL;
|
||||||
|
|
||||||
|
/* Begin storing the information into the control info structure */
|
||||||
dev_ctl->nr_instances = nr_instances;
|
dev_ctl->nr_instances = nr_instances;
|
||||||
dev_ctl->instances = dev_inst;
|
dev_ctl->instances = dev_inst;
|
||||||
dev_ctl->pvt_info = pvt;
|
dev_ctl->pvt_info = pvt;
|
||||||
@ -145,28 +168,37 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
|
|||||||
for (block = 0; block < nr_blocks; block++) {
|
for (block = 0; block < nr_blocks; block++) {
|
||||||
blk = &blk_p[block];
|
blk = &blk_p[block];
|
||||||
blk->instance = inst;
|
blk->instance = inst;
|
||||||
blk->nr_attribs = nr_attribs;
|
|
||||||
attrib_p = &dev_attrib[block * nr_attribs];
|
|
||||||
blk->attribs = attrib_p;
|
|
||||||
snprintf(blk->name, sizeof(blk->name),
|
snprintf(blk->name, sizeof(blk->name),
|
||||||
"%s%d", edac_block_name, block+offset_value);
|
"%s%d", edac_block_name, block+offset_value);
|
||||||
|
|
||||||
debugf1("%s() instance=%d block=%d name=%s\n",
|
debugf1("%s() instance=%d block=%d name=%s\n",
|
||||||
__func__, instance, block, blk->name);
|
__func__, instance, block, blk->name);
|
||||||
|
|
||||||
if (attrib_spec != NULL) {
|
/* if there are NO attributes OR no attribute pointer
|
||||||
/* when there is an attrib_spec passed int then
|
* then continue on to next block iteration
|
||||||
* Initialize every attrib of each block
|
*/
|
||||||
*/
|
if ((nr_attrib == 0) || (attrib_spec == NULL))
|
||||||
for (attr = 0; attr < nr_attribs; attr++) {
|
continue;
|
||||||
attrib = &attrib_p[attr];
|
|
||||||
attrib->block = blk;
|
|
||||||
|
|
||||||
/* Link each attribute to the caller's
|
/* setup the attribute array for this block */
|
||||||
* spec entry, for name and type
|
blk->nr_attribs = nr_attrib;
|
||||||
*/
|
attrib_p = &dev_attrib[block*nr_instances*nr_attrib];
|
||||||
attrib->spec = &attrib_spec[attr];
|
blk->block_attributes = attrib_p;
|
||||||
}
|
|
||||||
|
/* Initialize every user specified attribute in this
|
||||||
|
* block with the data the caller passed in
|
||||||
|
*/
|
||||||
|
for (attr = 0; attr < nr_attrib; attr++) {
|
||||||
|
attrib = &attrib_p[attr];
|
||||||
|
attrib->attr = attrib_spec->attr;
|
||||||
|
attrib->show = attrib_spec->show;
|
||||||
|
attrib->store = attrib_spec->store;
|
||||||
|
|
||||||
|
/* up reference this block */
|
||||||
|
attrib->block = blk;
|
||||||
|
|
||||||
|
/* bump the attrib_spec */
|
||||||
|
attrib_spec++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,8 +456,10 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
|
|||||||
struct edac_device_instance *instance,
|
struct edac_device_instance *instance,
|
||||||
int idx)
|
int idx)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
int err;
|
int err;
|
||||||
struct edac_device_block *block;
|
struct edac_device_block *block;
|
||||||
|
struct edac_dev_sysfs_block_attribute *sysfs_attrib;
|
||||||
|
|
||||||
block = &instance->blocks[idx];
|
block = &instance->blocks[idx];
|
||||||
|
|
||||||
@ -480,7 +482,27 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If there are driver level block attributes, then added them
|
||||||
|
* to the block kobject
|
||||||
|
*/
|
||||||
|
sysfs_attrib = block->block_attributes;
|
||||||
|
if (sysfs_attrib != NULL) {
|
||||||
|
for (i = 0; i < block->nr_attribs; i++) {
|
||||||
|
err = sysfs_create_file(&block->kobj,
|
||||||
|
(struct attribute *) &sysfs_attrib[i]);
|
||||||
|
if (err)
|
||||||
|
goto err_on_attrib;
|
||||||
|
|
||||||
|
sysfs_attrib++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_on_attrib:
|
||||||
|
kobject_unregister(&block->kobj);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user