X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fedac%2Fedac_device.c;h=5fcd3d89c75d91cda65acd42fe34be8d987e9c67;hb=a8cac817764a494705aebd99fd51bdf6cdc28ec9;hp=d60f5df87af5f21729378d6a6861c4fef3210ee8;hpb=7391c6dcab3094610cb99bbd559beaa282582eac;p=linux-2.6 diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index d60f5df87a..5fcd3d89c7 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c @@ -32,9 +32,11 @@ #include "edac_core.h" #include "edac_module.h" -/* lock to memory controller's control array 'edac_device_list' */ -static DECLARE_MUTEX(device_ctls_mutex); -static struct list_head edac_device_list = LIST_HEAD_INIT(edac_device_list); +/* lock for the list: 'edac_device_list', manipulation of this list + * is protected by the 'device_ctls_mutex' lock + */ +static DEFINE_MUTEX(device_ctls_mutex); +static LIST_HEAD(edac_device_list); #ifdef CONFIG_EDAC_DEBUG static void edac_device_dump_device(struct edac_device_ctl_info *edac_dev) @@ -48,6 +50,7 @@ static void edac_device_dump_device(struct edac_device_ctl_info *edac_dev) } #endif /* CONFIG_EDAC_DEBUG */ + /* * edac_device_alloc_ctl_info() * Allocate a new edac device control info structure @@ -67,43 +70,63 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( char *edac_device_name, unsigned nr_instances, char *edac_block_name, unsigned nr_blocks, 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, + int device_index) { struct edac_device_ctl_info *dev_ctl; struct edac_device_instance *dev_inst, *inst; 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 count; unsigned instance, block, attr; void *pvt; + int err; - debugf1("%s() instances=%d blocks=%d\n", + debugf4("%s() instances=%d blocks=%d\n", __func__, nr_instances, nr_blocks); - /* Figure out the offsets of the various items from the start of an - * ctl_info structure. We want the alignment of each item + /* Calculate the size of memory we need to allocate AND + * 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 * provide if we could simply hardcode everything into a single struct. */ 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)); - /* 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)); - /* 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; dev_attrib = edac_align_ptr(&dev_blk[count], sizeof(*dev_attrib)); - /* Check for case of NO attributes specified */ - if (nr_attribs > 0) - count *= nr_attribs; + /* Check for case of when an attribute array is specified */ + if (nr_attrib > 0) { + /* calc how many nr_attrib we need */ + count *= nr_attrib; + + /* Calc the 'end' offset past the attributes array */ + pvt = edac_align_ptr(&dev_attrib[count], sz_private); + } else { + /* no attribute array specificed */ + pvt = edac_align_ptr(dev_attrib, sz_private); + } - /* Calc the 'end' offset past the attributes array */ - pvt = edac_align_ptr(&dev_attrib[count], 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; /* Allocate the amount of memory for the set of control structures */ @@ -111,24 +134,37 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( if (dev_ctl == NULL) return NULL; - /* Adjust pointers so they point within the memory we just allocated - * rather than an imaginary chunk of memory located at address 0. + /* Adjust pointers so they point within the actual memory we + * 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 *) (((char *)dev_ctl) + ((unsigned long)dev_inst)); dev_blk = (struct edac_device_block *) (((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)); pvt = sz_private ? (((char *)dev_ctl) + ((unsigned long)pvt)) : NULL; + /* Begin storing the information into the control info structure */ + dev_ctl->dev_idx = device_index; dev_ctl->nr_instances = nr_instances; dev_ctl->instances = dev_inst; dev_ctl->pvt_info = pvt; + /* Default logging of CEs and UEs */ + dev_ctl->log_ce = 1; + dev_ctl->log_ue = 1; + /* Name of this edac device */ snprintf(dev_ctl->name,sizeof(dev_ctl->name),"%s",edac_device_name); + debugf4("%s() edac_dev=%p next after end=%p\n", + __func__, dev_ctl, pvt + sz_private ); + /* Initialize every Instance */ for (instance = 0; instance < nr_instances; instance++) { inst = &dev_inst[instance]; @@ -145,28 +181,51 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( for (block = 0; block < nr_blocks; block++) { blk = &blk_p[block]; 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), "%s%d", edac_block_name, block+offset_value); - debugf1("%s() instance=%d block=%d name=%s\n", - __func__, instance, block, blk->name); - - if (attrib_spec != NULL) { - /* when there is an attrib_spec passed int then - * Initialize every attrib of each block + debugf4("%s() instance=%d inst_p=%p block=#%d " + "block_p=%p name='%s'\n", + __func__, instance, inst, block, + blk, blk->name); + + /* if there are NO attributes OR no attribute pointer + * then continue on to next block iteration + */ + if ((nr_attrib == 0) || (attrib_spec == NULL)) + continue; + + /* setup the attribute array for this block */ + blk->nr_attribs = nr_attrib; + attrib_p = &dev_attrib[block*nr_instances*nr_attrib]; + blk->block_attributes = attrib_p; + + debugf4("%s() THIS BLOCK_ATTRIB=%p\n", + __func__, blk->block_attributes); + + /* Initialize every user specified attribute in this + * block with the data the caller passed in + * Each block gets its own copy of pointers, + * and its unique 'value' + */ + for (attr = 0; attr < nr_attrib; attr++) { + attrib = &attrib_p[attr]; + + /* populate the unique per attrib + * with the code pointers and info */ - for (attr = 0; attr < nr_attribs; attr++) { - attrib = &attrib_p[attr]; - attrib->block = blk; - - /* Link each attribute to the caller's - * spec entry, for name and type - */ - attrib->spec = &attrib_spec[attr]; - } + attrib->attr = attrib_spec[attr].attr; + attrib->show = attrib_spec[attr].show; + attrib->store = attrib_spec[attr].store; + + attrib->block = blk; /* up link */ + + debugf4("%s() alloc-attrib=%p attrib_name='%s' " + "attrib-spec=%p spec-name=%s\n", + __func__, attrib, attrib->attr.name, + &attrib_spec[attr], + attrib_spec[attr].attr.name + ); } } } @@ -174,6 +233,22 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( /* Mark this instance as merely ALLOCATED */ dev_ctl->op_state = OP_ALLOC; + /* + * Initialize the 'root' kobj for the edac_device controller + */ + err = edac_device_register_sysfs_main_kobj(dev_ctl); + if (err) { + kfree(dev_ctl); + return NULL; + } + + /* at this point, the root kobj is valid, and in order to + * 'free' the object, then the function: + * edac_device_unregister_sysfs_main_kobj() must be called + * which will perform kobj unregistration and the actual free + * will occur during the kobject callback operation + */ + return dev_ctl; } EXPORT_SYMBOL_GPL(edac_device_alloc_ctl_info); @@ -185,7 +260,7 @@ EXPORT_SYMBOL_GPL(edac_device_alloc_ctl_info); */ void edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info) { - kfree(ctl_info); + edac_device_unregister_sysfs_main_kobj(ctl_info); } EXPORT_SYMBOL_GPL(edac_device_free_ctl_info); @@ -204,7 +279,7 @@ static struct edac_device_ctl_info *find_edac_device_by_dev(struct device *dev) struct edac_device_ctl_info *edac_dev; struct list_head *item; - debugf3("%s()\n", __func__); + debugf0("%s()\n", __func__); list_for_each(item, &edac_device_list) { edac_dev = list_entry(item, struct edac_device_ctl_info, link); @@ -258,7 +333,7 @@ static int add_edac_dev_to_global_list(struct edac_device_ctl_info *edac_dev) fail0: edac_printk(KERN_WARNING, EDAC_MC, "%s (%s) %s %s already assigned %d\n", - rover->dev->bus_id, dev_name(rover), + rover->dev->bus_id, edac_dev_name(rover), rover->mod_name, rover->ctl_name, rover->dev_idx); return 1; @@ -281,66 +356,43 @@ static void complete_edac_device_list_del(struct rcu_head *head) edac_dev = container_of(head, struct edac_device_ctl_info, rcu); INIT_LIST_HEAD(&edac_dev->link); - complete(&edac_dev->complete); + complete(&edac_dev->removal_complete); } /* * del_edac_device_from_global_list * - * remove the RCU, setup for a callback call, then wait for the - * callback to occur + * remove the RCU, setup for a callback call, + * then wait for the callback to occur */ static void del_edac_device_from_global_list(struct edac_device_ctl_info *edac_device) { list_del_rcu(&edac_device->link); - init_completion(&edac_device->complete); - call_rcu(&edac_device->rcu, complete_edac_device_list_del); - wait_for_completion(&edac_device->complete); -} - -/** - * edac_device_find - * Search for a edac_device_ctl_info structure whose index is 'idx'. - * - * If found, return a pointer to the structure. - * Else return NULL. - * - * Caller must hold device_ctls_mutex. - */ -struct edac_device_ctl_info *edac_device_find(int idx) -{ - struct list_head *item; - struct edac_device_ctl_info *edac_dev; - - /* Iterate over list, looking for exact match of ID */ - list_for_each(item, &edac_device_list) { - edac_dev = list_entry(item, struct edac_device_ctl_info, link); - - if (edac_dev->dev_idx >= idx) { - if (edac_dev->dev_idx == idx) - return edac_dev; - - /* not on list, so terminate early */ - break; - } - } - return NULL; + init_completion(&edac_device->removal_complete); + call_rcu(&edac_device->rcu, complete_edac_device_list_del); + wait_for_completion(&edac_device->removal_complete); } -EXPORT_SYMBOL_GPL(edac_device_find); /* * edac_device_workq_function * performs the operation scheduled by a workq request + * + * this workq is embedded within an edac_device_ctl_info + * structure, that needs to be polled for possible error events. + * + * This operation is to acquire the list mutex lock + * (thus preventing insertation or deletion) + * and then call the device's poll function IFF this device is + * running polled and there is a poll function defined. */ static void edac_device_workq_function(struct work_struct *work_req) { struct delayed_work *d_work = (struct delayed_work *)work_req; struct edac_device_ctl_info *edac_dev = to_edac_device_ctl_work(d_work); - //debugf0("%s() here and running\n", __func__); - down(&device_ctls_mutex); + mutex_lock(&device_ctls_mutex); /* Only poll controllers that are running polled and have a check */ if ((edac_dev->op_state == OP_RUNNING_POLL) && @@ -348,10 +400,19 @@ static void edac_device_workq_function(struct work_struct *work_req) edac_dev->edac_check(edac_dev); } - up(&device_ctls_mutex); + mutex_unlock(&device_ctls_mutex); - /* Reschedule */ - queue_delayed_work(edac_workqueue, &edac_dev->work, edac_dev->delay); + /* Reschedule the workq for the next time period to start again + * if the number of msec is for 1 sec, then adjust to the next + * whole one second to save timers fireing all over the period + * between integral seconds + */ + if (edac_dev->poll_msec == 1000) + queue_delayed_work(edac_workqueue, &edac_dev->work, + round_jiffies_relative(edac_dev->delay)); + else + queue_delayed_work(edac_workqueue, &edac_dev->work, + edac_dev->delay); } /* @@ -364,11 +425,26 @@ void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev, { debugf0("%s()\n", __func__); + /* take the arg 'msec' and set it into the control structure + * to used in the time period calculation + * then calc the number of jiffies that represents + */ edac_dev->poll_msec = msec; - edac_dev->delay = msecs_to_jiffies(msec); /* Calc delay jiffies */ + edac_dev->delay = msecs_to_jiffies(msec); INIT_DELAYED_WORK(&edac_dev->work, edac_device_workq_function); - queue_delayed_work(edac_workqueue, &edac_dev->work, edac_dev->delay); + + /* optimize here for the 1 second case, which will be normal value, to + * fire ON the 1 second time event. This helps reduce all sorts of + * timers firing on sub-second basis, while they are happy + * to fire together on the 1 second exactly + */ + if (edac_dev->poll_msec == 1000) + queue_delayed_work(edac_workqueue, &edac_dev->work, + round_jiffies_relative(edac_dev->delay)); + else + queue_delayed_work(edac_workqueue, &edac_dev->work, + edac_dev->delay); } /* @@ -388,20 +464,24 @@ void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev) /* * edac_device_reset_delay_period + * + * need to stop any outstanding workq queued up at this time + * because we will be resetting the sleep time. + * Then restart the workq on the new delay */ - void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned long value) { - down(&device_ctls_mutex); - - /* cancel the current workq request */ + /* cancel the current workq request, without the mutex lock */ edac_device_workq_teardown(edac_dev); + /* acquire the mutex before doing the workq setup */ + mutex_lock(&device_ctls_mutex); + /* restart the workq request, with new delay value */ edac_device_workq_setup(edac_dev, value); - up(&device_ctls_mutex); + mutex_unlock(&device_ctls_mutex); } /** @@ -409,23 +489,21 @@ void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, * edac_device global list and create sysfs entries associated with * edac_device structure. * @edac_device: pointer to the edac_device structure to be added to the list - * @edac_idx: A unique numeric identifier to be assigned to the * 'edac_device' structure. * * Return: * 0 Success * !0 Failure */ -int edac_device_add_device(struct edac_device_ctl_info *edac_dev, int edac_idx) +int edac_device_add_device(struct edac_device_ctl_info *edac_dev) { debugf0("%s()\n", __func__); - edac_dev->dev_idx = edac_idx; #ifdef CONFIG_EDAC_DEBUG if (edac_debug_level >= 3) edac_device_dump_device(edac_dev); #endif - down(&device_ctls_mutex); + mutex_lock(&device_ctls_mutex); if (add_edac_dev_to_global_list(edac_dev)) goto fail0; @@ -460,10 +538,10 @@ int edac_device_add_device(struct edac_device_ctl_info *edac_dev, int edac_idx) "'%s': DEV '%s' (%s)\n", edac_dev->mod_name, edac_dev->ctl_name, - dev_name(edac_dev), - edac_op_state_toString(edac_dev->op_state)); + edac_dev_name(edac_dev), + edac_op_state_to_string(edac_dev->op_state)); - up(&device_ctls_mutex); + mutex_unlock(&device_ctls_mutex); return 0; fail1: @@ -471,7 +549,7 @@ fail1: del_edac_device_from_global_list(edac_dev); fail0: - up(&device_ctls_mutex); + mutex_unlock(&device_ctls_mutex); return 1; } EXPORT_SYMBOL_GPL(edac_device_add_device); @@ -493,14 +571,14 @@ struct edac_device_ctl_info *edac_device_del_device(struct device *dev) { struct edac_device_ctl_info *edac_dev; - debugf0("MC: %s()\n", __func__); + debugf0("%s()\n", __func__); - down(&device_ctls_mutex); + mutex_lock(&device_ctls_mutex); /* Find the structure on the list, if not there, then leave */ edac_dev = find_edac_device_by_dev(dev); if (edac_dev == NULL) { - up(&device_ctls_mutex); + mutex_unlock(&device_ctls_mutex); return NULL; } @@ -510,18 +588,18 @@ struct edac_device_ctl_info *edac_device_del_device(struct device *dev) /* clear workq processing on this instance */ edac_device_workq_teardown(edac_dev); - /* Tear down the sysfs entries for this instance */ - edac_device_remove_sysfs(edac_dev); - /* deregister from global list */ del_edac_device_from_global_list(edac_dev); - up(&device_ctls_mutex); + mutex_unlock(&device_ctls_mutex); + + /* Tear down the sysfs entries for this instance */ + edac_device_remove_sysfs(edac_dev); edac_printk(KERN_INFO, EDAC_MC, "Removed device %d for %s %s: DEV %s\n", edac_dev->dev_idx, - edac_dev->mod_name, edac_dev->ctl_name, dev_name(edac_dev)); + edac_dev->mod_name, edac_dev->ctl_name, edac_dev_name(edac_dev)); return edac_dev; }