Commit 8096cfaf authored by Doug Thompson's avatar Doug Thompson Committed by Linus Torvalds

drivers/edac: fix edac_mc sysfs completion code

This patch refactors the 'releasing' of kobjects for the edac_mc type of
device.  The correct pattern of kobject release is followed.

As internal kobjs are allocated they bump a ref count on the top level kobj.
It in turn has a module ref count on the edac_core module.  When internal
kobjects are released, they dec the ref count on the top level kobj.  When the
top level kobj reaches zero, it decrements the ref count on the edac_core
object, allow it to be unloaded, as all resources have all now been released.

Cc: Alan Cox alan@lxorguk.ukuu.org.uk
Signed-off-by: default avatarDoug Thompson <dougthompson@xmission.com>
Acked-by: default avatarGreg KH <greg@kroah.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d45e7823
...@@ -317,9 +317,8 @@ struct csrow_info { ...@@ -317,9 +317,8 @@ struct csrow_info {
struct mem_ctl_info *mci; /* the parent */ struct mem_ctl_info *mci; /* the parent */
struct kobject kobj; /* sysfs kobject for this csrow */ struct kobject kobj; /* sysfs kobject for this csrow */
struct completion kobj_complete;
/* FIXME the number of CHANNELs might need to become dynamic */ /* channel information for this csrow */
u32 nr_channels; u32 nr_channels;
struct channel_info *channels; struct channel_info *channels;
}; };
...@@ -403,7 +402,6 @@ struct mem_ctl_info { ...@@ -403,7 +402,6 @@ struct mem_ctl_info {
/* edac sysfs device control */ /* edac sysfs device control */
struct kobject edac_mci_kobj; struct kobject edac_mci_kobj;
struct completion kobj_complete;
/* Additional top controller level attributes, but specified /* Additional top controller level attributes, but specified
* by the low level driver. * by the low level driver.
......
...@@ -137,6 +137,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, ...@@ -137,6 +137,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
void *pvt; void *pvt;
unsigned size; unsigned size;
int row, chn; int row, chn;
int err;
/* Figure out the offsets of the various items from the start of an mc /* Figure out the offsets of the various items from the start of an mc
* structure. We want the alignment of each item to be at least as * structure. We want the alignment of each item to be at least as
...@@ -149,7 +150,8 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, ...@@ -149,7 +150,8 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
pvt = edac_align_ptr(&chi[nr_chans * nr_csrows], sz_pvt); pvt = edac_align_ptr(&chi[nr_chans * nr_csrows], sz_pvt);
size = ((unsigned long)pvt) + sz_pvt; size = ((unsigned long)pvt) + sz_pvt;
if ((mci = kmalloc(size, GFP_KERNEL)) == NULL) mci = kzalloc(size, GFP_KERNEL);
if (mci == NULL)
return NULL; return NULL;
/* Adjust pointers so they point within the memory we just allocated /* Adjust pointers so they point within the memory we just allocated
...@@ -182,20 +184,34 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, ...@@ -182,20 +184,34 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
mci->op_state = OP_ALLOC; mci->op_state = OP_ALLOC;
/*
* Initialize the 'root' kobj for the edac_mc controller
*/
err = edac_mc_register_sysfs_main_kobj(mci);
if (err) {
kfree(mci);
return NULL;
}
/* at this point, the root kobj is valid, and in order to
* 'free' the object, then the function:
* edac_mc_unregister_sysfs_main_kobj() must be called
* which will perform kobj unregistration and the actual free
* will occur during the kobject callback operation
*/
return mci; return mci;
} }
EXPORT_SYMBOL_GPL(edac_mc_alloc); EXPORT_SYMBOL_GPL(edac_mc_alloc);
/** /**
* edac_mc_free: Free a previously allocated 'mci' structure * edac_mc_free
* 'Free' a previously allocated 'mci' structure
* @mci: pointer to a struct mem_ctl_info structure * @mci: pointer to a struct mem_ctl_info structure
*/ */
void edac_mc_free(struct mem_ctl_info *mci) void edac_mc_free(struct mem_ctl_info *mci)
{ {
kfree(mci); edac_mc_unregister_sysfs_main_kobj(mci);
} }
EXPORT_SYMBOL_GPL(edac_mc_free); EXPORT_SYMBOL_GPL(edac_mc_free);
static struct mem_ctl_info *find_mci_by_dev(struct device *dev) static struct mem_ctl_info *find_mci_by_dev(struct device *dev)
...@@ -391,7 +407,6 @@ struct mem_ctl_info *edac_mc_find(int idx) ...@@ -391,7 +407,6 @@ struct mem_ctl_info *edac_mc_find(int idx)
return NULL; return NULL;
} }
EXPORT_SYMBOL(edac_mc_find); EXPORT_SYMBOL(edac_mc_find);
/** /**
...@@ -465,7 +480,6 @@ int edac_mc_add_mc(struct mem_ctl_info *mci) ...@@ -465,7 +480,6 @@ int edac_mc_add_mc(struct mem_ctl_info *mci)
mutex_unlock(&mem_ctls_mutex); mutex_unlock(&mem_ctls_mutex);
return 1; return 1;
} }
EXPORT_SYMBOL_GPL(edac_mc_add_mc); EXPORT_SYMBOL_GPL(edac_mc_add_mc);
/** /**
...@@ -501,7 +515,6 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev) ...@@ -501,7 +515,6 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
mci->mod_name, mci->ctl_name, dev_name(mci)); mci->mod_name, mci->ctl_name, dev_name(mci));
return mci; return mci;
} }
EXPORT_SYMBOL_GPL(edac_mc_del_mc); EXPORT_SYMBOL_GPL(edac_mc_del_mc);
static void edac_mc_scrub_block(unsigned long page, unsigned long offset, static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
...@@ -571,7 +584,6 @@ int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page) ...@@ -571,7 +584,6 @@ int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
return row; return row;
} }
EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page); EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
/* FIXME - setable log (warning/emerg) levels */ /* FIXME - setable log (warning/emerg) levels */
...@@ -636,7 +648,6 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci, ...@@ -636,7 +648,6 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci,
mci->csrows[row].grain); mci->csrows[row].grain);
} }
} }
EXPORT_SYMBOL_GPL(edac_mc_handle_ce); EXPORT_SYMBOL_GPL(edac_mc_handle_ce);
void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg) void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg)
...@@ -648,7 +659,6 @@ void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg) ...@@ -648,7 +659,6 @@ void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg)
mci->ce_noinfo_count++; mci->ce_noinfo_count++;
mci->ce_count++; mci->ce_count++;
} }
EXPORT_SYMBOL_GPL(edac_mc_handle_ce_no_info); EXPORT_SYMBOL_GPL(edac_mc_handle_ce_no_info);
void edac_mc_handle_ue(struct mem_ctl_info *mci, void edac_mc_handle_ue(struct mem_ctl_info *mci,
...@@ -702,7 +712,6 @@ void edac_mc_handle_ue(struct mem_ctl_info *mci, ...@@ -702,7 +712,6 @@ void edac_mc_handle_ue(struct mem_ctl_info *mci,
mci->ue_count++; mci->ue_count++;
mci->csrows[row].ue_count++; mci->csrows[row].ue_count++;
} }
EXPORT_SYMBOL_GPL(edac_mc_handle_ue); EXPORT_SYMBOL_GPL(edac_mc_handle_ue);
void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg) void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg)
...@@ -716,7 +725,6 @@ void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg) ...@@ -716,7 +725,6 @@ void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg)
mci->ue_noinfo_count++; mci->ue_noinfo_count++;
mci->ue_count++; mci->ue_count++;
} }
EXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info); EXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info);
/************************************************************* /*************************************************************
...@@ -784,7 +792,6 @@ void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci, ...@@ -784,7 +792,6 @@ void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
"labels \"%s\": %s\n", csrow, channela, "labels \"%s\": %s\n", csrow, channela,
channelb, labels, msg); channelb, labels, msg);
} }
EXPORT_SYMBOL(edac_mc_handle_fbd_ue); EXPORT_SYMBOL(edac_mc_handle_fbd_ue);
/************************************************************* /*************************************************************
...@@ -824,7 +831,6 @@ void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, ...@@ -824,7 +831,6 @@ void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
mci->csrows[csrow].ce_count++; mci->csrows[csrow].ce_count++;
mci->csrows[csrow].channels[channel].ce_count++; mci->csrows[csrow].channels[channel].ce_count++;
} }
EXPORT_SYMBOL(edac_mc_handle_fbd_ce); EXPORT_SYMBOL(edac_mc_handle_fbd_ce);
/* /*
......
...@@ -10,10 +10,12 @@ ...@@ -10,10 +10,12 @@
*/ */
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/bug.h>
#include "edac_core.h" #include "edac_core.h"
#include "edac_module.h" #include "edac_module.h"
/* MC EDAC Controls, setable by module parameter, and sysfs */ /* MC EDAC Controls, setable by module parameter, and sysfs */
static int edac_mc_log_ue = 1; static int edac_mc_log_ue = 1;
static int edac_mc_log_ce = 1; static int edac_mc_log_ce = 1;
...@@ -98,15 +100,7 @@ static const char *edac_caps[] = { ...@@ -98,15 +100,7 @@ static const char *edac_caps[] = {
[EDAC_S16ECD16ED] = "S16ECD16ED" [EDAC_S16ECD16ED] = "S16ECD16ED"
}; };
/* sysfs object:
* /sys/devices/system/edac/mc
*/
static struct kobject edac_memctrl_kobj;
/* We use these to wait for the reference counts on edac_memctrl_kobj and
* edac_pci_kobj to reach 0.
*/
static struct completion edac_memctrl_kobj_complete;
/* /*
* /sys/devices/system/edac/mc; * /sys/devices/system/edac/mc;
...@@ -128,153 +122,6 @@ static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count) ...@@ -128,153 +122,6 @@ static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count)
return count; return count;
} }
struct memctrl_dev_attribute {
struct attribute attr;
void *value;
ssize_t(*show) (void *, char *);
ssize_t(*store) (void *, const char *, size_t);
};
/* Set of show/store abstract level functions for memory control object */
static ssize_t memctrl_dev_show(struct kobject *kobj,
struct attribute *attr, char *buffer)
{
struct memctrl_dev_attribute *memctrl_dev;
memctrl_dev = (struct memctrl_dev_attribute *)attr;
if (memctrl_dev->show)
return memctrl_dev->show(memctrl_dev->value, buffer);
return -EIO;
}
static ssize_t memctrl_dev_store(struct kobject *kobj, struct attribute *attr,
const char *buffer, size_t count)
{
struct memctrl_dev_attribute *memctrl_dev;
memctrl_dev = (struct memctrl_dev_attribute *)attr;
if (memctrl_dev->store)
return memctrl_dev->store(memctrl_dev->value, buffer, count);
return -EIO;
}
static struct sysfs_ops memctrlfs_ops = {
.show = memctrl_dev_show,
.store = memctrl_dev_store
};
#define MEMCTRL_ATTR(_name,_mode,_show,_store) \
static struct memctrl_dev_attribute attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.value = &_name, \
.show = _show, \
.store = _store, \
};
#define MEMCTRL_STRING_ATTR(_name,_data,_mode,_show,_store) \
static struct memctrl_dev_attribute attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.value = _data, \
.show = _show, \
.store = _store, \
};
/* csrow<id> control files */
MEMCTRL_ATTR(edac_mc_panic_on_ue,
S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
MEMCTRL_ATTR(edac_mc_log_ue,
S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
MEMCTRL_ATTR(edac_mc_log_ce,
S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
MEMCTRL_ATTR(edac_mc_poll_msec,
S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
/* Base Attributes of the memory ECC object */
static struct memctrl_dev_attribute *memctrl_attr[] = {
&attr_edac_mc_panic_on_ue,
&attr_edac_mc_log_ue,
&attr_edac_mc_log_ce,
&attr_edac_mc_poll_msec,
NULL,
};
/* Main MC kobject release() function */
static void edac_memctrl_master_release(struct kobject *kobj)
{
debugf1("%s()\n", __func__);
complete(&edac_memctrl_kobj_complete);
}
static struct kobj_type ktype_memctrl = {
.release = edac_memctrl_master_release,
.sysfs_ops = &memctrlfs_ops,
.default_attrs = (struct attribute **)memctrl_attr,
};
/* Initialize the main sysfs entries for edac:
* /sys/devices/system/edac
*
* and children
*
* Return: 0 SUCCESS
* !0 FAILURE
*/
int edac_sysfs_memctrl_setup(void)
{
int err = 0;
struct sysdev_class *edac_class;
debugf1("%s()\n", __func__);
/* get the /sys/devices/system/edac class reference */
edac_class = edac_get_edac_class();
if (edac_class == NULL) {
debugf1("%s() no edac_class error=%d\n", __func__, err);
return err;
}
/* Init the MC's kobject */
memset(&edac_memctrl_kobj, 0, sizeof(edac_memctrl_kobj));
edac_memctrl_kobj.parent = &edac_class->kset.kobj;
edac_memctrl_kobj.ktype = &ktype_memctrl;
/* generate sysfs "..../edac/mc" */
err = kobject_set_name(&edac_memctrl_kobj, "mc");
if (err) {
debugf1("%s() Failed to set name '.../edac/mc'\n", __func__);
return err;
}
/* FIXME: maybe new sysdev_create_subdir() */
err = kobject_register(&edac_memctrl_kobj);
if (err) {
debugf1("%s() Failed to register '.../edac/mc'\n", __func__);
return err;
}
debugf1("%s() Registered '.../edac/mc' kobject\n", __func__);
return 0;
}
/*
* MC teardown:
* the '..../edac/mc' kobject followed by '..../edac' itself
*/
void edac_sysfs_memctrl_teardown(void)
{
debugf0("MC: " __FILE__ ": %s()\n", __func__);
/* Unregister the MC's kobject and wait for reference count to reach 0.
*/
init_completion(&edac_memctrl_kobj_complete);
kobject_unregister(&edac_memctrl_kobj);
wait_for_completion(&edac_memctrl_kobj_complete);
}
/* EDAC sysfs CSROW data structures and methods /* EDAC sysfs CSROW data structures and methods
*/ */
...@@ -486,10 +333,15 @@ static int edac_create_channel_files(struct kobject *kobj, int chan) ...@@ -486,10 +333,15 @@ static int edac_create_channel_files(struct kobject *kobj, int chan)
/* No memory to release for this kobj */ /* No memory to release for this kobj */
static void edac_csrow_instance_release(struct kobject *kobj) static void edac_csrow_instance_release(struct kobject *kobj)
{ {
struct mem_ctl_info *mci;
struct csrow_info *cs; struct csrow_info *cs;
debugf1("%s()\n", __func__);
cs = container_of(kobj, struct csrow_info, kobj); cs = container_of(kobj, struct csrow_info, kobj);
complete(&cs->kobj_complete); mci = cs->mci;
kobject_put(&mci->edac_mci_kobj);
} }
/* the kobj_type instance for a CSROW */ /* the kobj_type instance for a CSROW */
...@@ -500,38 +352,61 @@ static struct kobj_type ktype_csrow = { ...@@ -500,38 +352,61 @@ static struct kobj_type ktype_csrow = {
}; };
/* Create a CSROW object under specifed edac_mc_device */ /* Create a CSROW object under specifed edac_mc_device */
static int edac_create_csrow_object(struct kobject *edac_mci_kobj, static int edac_create_csrow_object(struct mem_ctl_info *mci,
struct csrow_info *csrow, int index) struct csrow_info *csrow, int index)
{ {
int err = 0; struct kobject *kobj_mci = &mci->edac_mci_kobj;
struct kobject *kobj;
int chan; int chan;
int err;
memset(&csrow->kobj, 0, sizeof(csrow->kobj));
/* generate ..../edac/mc/mc<id>/csrow<index> */ /* generate ..../edac/mc/mc<id>/csrow<index> */
memset(&csrow->kobj, 0, sizeof(csrow->kobj));
csrow->kobj.parent = edac_mci_kobj; csrow->mci = mci; /* include container up link */
csrow->kobj.parent = kobj_mci;
csrow->kobj.ktype = &ktype_csrow; csrow->kobj.ktype = &ktype_csrow;
/* name this instance of csrow<id> */ /* name this instance of csrow<id> */
err = kobject_set_name(&csrow->kobj, "csrow%d", index); err = kobject_set_name(&csrow->kobj, "csrow%d", index);
if (err) if (err)
goto error_exit; goto err_out;
/* bump the mci instance's kobject's ref count */
kobj = kobject_get(&mci->edac_mci_kobj);
if (!kobj) {
err = -ENODEV;
goto err_out;
}
/* Instanstiate the csrow object */ /* Instanstiate the csrow object */
err = kobject_register(&csrow->kobj); err = kobject_register(&csrow->kobj);
if (!err) { if (err)
goto err_release_top_kobj;
/* At this point, to release a csrow kobj, one must
* call the kobject_unregister and allow that tear down
* to work the releasing
*/
/* Create the dyanmic attribute files on this csrow, /* Create the dyanmic attribute files on this csrow,
* namely, the DIMM labels and the channel ce_count * namely, the DIMM labels and the channel ce_count
*/ */
for (chan = 0; chan < csrow->nr_channels; chan++) { for (chan = 0; chan < csrow->nr_channels; chan++) {
err = edac_create_channel_files(&csrow->kobj, chan); err = edac_create_channel_files(&csrow->kobj, chan);
if (err) if (err) {
break; /* special case the unregister here */
kobject_unregister(&csrow->kobj);
goto err_out;
} }
} }
error_exit: return 0;
/* error unwind stack */
err_release_top_kobj:
kobject_put(&mci->edac_mci_kobj);
err_out:
return err; return err;
} }
...@@ -688,6 +563,7 @@ static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, ...@@ -688,6 +563,7 @@ static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr,
return -EIO; return -EIO;
} }
/* Intermediate show/store table */
static struct sysfs_ops mci_ops = { static struct sysfs_ops mci_ops = {
.show = mcidev_show, .show = mcidev_show,
.store = mcidev_store .store = mcidev_store
...@@ -729,32 +605,213 @@ static struct mcidev_sysfs_attribute *mci_attr[] = { ...@@ -729,32 +605,213 @@ static struct mcidev_sysfs_attribute *mci_attr[] = {
NULL NULL
}; };
/* /*
* Release of a MC controlling instance * Release of a MC controlling instance
*
* each MC control instance has the following resources upon entry:
* a) a ref count on the top memctl kobj
* b) a ref count on this module
*
* this function must decrement those ref counts and then
* issue a free on the instance's memory
*/ */
static void edac_mci_instance_release(struct kobject *kobj) static void edac_mci_control_release(struct kobject *kobj)
{ {
struct mem_ctl_info *mci; struct mem_ctl_info *mci;
mci = to_mci(kobj); mci = to_mci(kobj);
debugf0("%s() idx=%d\n", __func__, mci->mc_idx);
complete(&mci->kobj_complete); debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx);
/* decrement the module ref count */
module_put(mci->owner);
/* free the mci instance memory here */
kfree(mci);
} }
static struct kobj_type ktype_mci = { static struct kobj_type ktype_mci = {
.release = edac_mci_instance_release, .release = edac_mci_control_release,
.sysfs_ops = &mci_ops, .sysfs_ops = &mci_ops,
.default_attrs = (struct attribute **)mci_attr, .default_attrs = (struct attribute **)mci_attr,
}; };
/* show/store, tables, etc for the MC kset */
struct memctrl_dev_attribute {
struct attribute attr;
void *value;
ssize_t(*show) (void *, char *);
ssize_t(*store) (void *, const char *, size_t);
};
/* Set of show/store abstract level functions for memory control object */
static ssize_t memctrl_dev_show(struct kobject *kobj,
struct attribute *attr, char *buffer)
{
struct memctrl_dev_attribute *memctrl_dev;
memctrl_dev = (struct memctrl_dev_attribute *)attr;
if (memctrl_dev->show)
return memctrl_dev->show(memctrl_dev->value, buffer);
return -EIO;
}
static ssize_t memctrl_dev_store(struct kobject *kobj, struct attribute *attr,
const char *buffer, size_t count)
{
struct memctrl_dev_attribute *memctrl_dev;
memctrl_dev = (struct memctrl_dev_attribute *)attr;
if (memctrl_dev->store)
return memctrl_dev->store(memctrl_dev->value, buffer, count);
return -EIO;
}
static struct sysfs_ops memctrlfs_ops = {
.show = memctrl_dev_show,
.store = memctrl_dev_store
};
#define MEMCTRL_ATTR(_name, _mode, _show, _store) \
static struct memctrl_dev_attribute attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.value = &_name, \
.show = _show, \
.store = _store, \
};
#define MEMCTRL_STRING_ATTR(_name, _data, _mode, _show, _store) \
static struct memctrl_dev_attribute attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.value = _data, \
.show = _show, \
.store = _store, \
};
/* csrow<id> control files */
MEMCTRL_ATTR(edac_mc_panic_on_ue,
S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
MEMCTRL_ATTR(edac_mc_log_ue,
S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
MEMCTRL_ATTR(edac_mc_log_ce,
S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
MEMCTRL_ATTR(edac_mc_poll_msec,
S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
/* Base Attributes of the memory ECC object */
static struct memctrl_dev_attribute *memctrl_attr[] = {
&attr_edac_mc_panic_on_ue,
&attr_edac_mc_log_ue,
&attr_edac_mc_log_ce,
&attr_edac_mc_poll_msec,
NULL,
};
/* the ktype for the mc_kset internal kobj */
static struct kobj_type ktype_mc_set_attribs = {
.sysfs_ops = &memctrlfs_ops,
.default_attrs = (struct attribute **)memctrl_attr,
};
/* EDAC memory controller sysfs kset:
* /sys/devices/system/edac/mc
*/
static struct kset mc_kset = {
.kobj = {.name = "mc", .ktype = &ktype_mc_set_attribs },
.ktype = &ktype_mci,
};
/*
* edac_mc_register_sysfs_main_kobj
*
* setups and registers the main kobject for each mci
*/
int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci)
{
struct kobject *kobj_mci;
int err;
debugf1("%s()\n", __func__);
kobj_mci = &mci->edac_mci_kobj;
/* Init the mci's kobject */
memset(kobj_mci, 0, sizeof(*kobj_mci));
/* this instance become part of the mc_kset */
kobj_mci->kset = &mc_kset;
/* set the name of the mc<id> object */
err = kobject_set_name(kobj_mci, "mc%d", mci->mc_idx);
if (err)
goto fail_out;
/* Record which module 'owns' this control structure
* and bump the ref count of the module
*/
mci->owner = THIS_MODULE;
/* bump ref count on this module */
if (!try_module_get(mci->owner)) {
err = -ENODEV;
goto fail_out;
}
/* register the mc<id> kobject to the mc_kset */
err = kobject_register(kobj_mci);
if (err) {
debugf1("%s()Failed to register '.../edac/mc%d'\n",
__func__, mci->mc_idx);
goto kobj_reg_fail;
}
/* At this point, to 'free' the control struct,
* edac_mc_unregister_sysfs_main_kobj() must be used
*/
debugf1("%s() Registered '.../edac/mc%d' kobject\n",
__func__, mci->mc_idx);
return 0;
/* Error exit stack */
kobj_reg_fail:
module_put(mci->owner);
fail_out:
return err;
}
/*
* edac_mc_register_sysfs_main_kobj
*
* tears down and the main mci kobject from the mc_kset
*/
void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci)
{
/* delete the kobj from the mc_kset */
kobject_unregister(&mci->edac_mci_kobj);
}
#define EDAC_DEVICE_SYMLINK "device" #define EDAC_DEVICE_SYMLINK "device"
/* /*
* edac_create_driver_attributes * edac_create_mci_instance_attributes
* create MC driver specific attributes at the topmost level * create MC driver specific attributes at the topmost level
* directory of this mci instance. * directory of this mci instance.
*/ */
static int edac_create_driver_attributes(struct mem_ctl_info *mci) static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci)
{ {
int err; int err;
struct mcidev_sysfs_attribute *sysfs_attrib; struct mcidev_sysfs_attribute *sysfs_attrib;
...@@ -764,7 +821,7 @@ static int edac_create_driver_attributes(struct mem_ctl_info *mci) ...@@ -764,7 +821,7 @@ static int edac_create_driver_attributes(struct mem_ctl_info *mci)
*/ */
sysfs_attrib = mci->mc_driver_sysfs_attributes; sysfs_attrib = mci->mc_driver_sysfs_attributes;
while (sysfs_attrib->attr.name != NULL) { while (sysfs_attrib && sysfs_attrib->attr.name) {
err = sysfs_create_file(&mci->edac_mci_kobj, err = sysfs_create_file(&mci->edac_mci_kobj,
(struct attribute*) sysfs_attrib); (struct attribute*) sysfs_attrib);
if (err) { if (err) {
...@@ -777,6 +834,29 @@ static int edac_create_driver_attributes(struct mem_ctl_info *mci) ...@@ -777,6 +834,29 @@ static int edac_create_driver_attributes(struct mem_ctl_info *mci)
return 0; return 0;
} }
/*
* edac_remove_mci_instance_attributes
* remove MC driver specific attributes at the topmost level
* directory of this mci instance.
*/
static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci)
{
struct mcidev_sysfs_attribute *sysfs_attrib;
/* point to the start of the array and iterate over it
* adding each attribute listed to this mci instance's kobject
*/
sysfs_attrib = mci->mc_driver_sysfs_attributes;
/* loop if there are attributes and until we hit a NULL entry */
while (sysfs_attrib && sysfs_attrib->attr.name) {
sysfs_remove_file(&mci->edac_mci_kobj,
(struct attribute *) sysfs_attrib);
sysfs_attrib++;
}
}
/* /*
* Create a new Memory Controller kobject instance, * Create a new Memory Controller kobject instance,
* mc<id> under the 'mc' directory * mc<id> under the 'mc' directory
...@@ -790,53 +870,45 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) ...@@ -790,53 +870,45 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
int i; int i;
int err; int err;
struct csrow_info *csrow; struct csrow_info *csrow;
struct kobject *edac_mci_kobj = &mci->edac_mci_kobj; struct kobject *kobj_mci = &mci->edac_mci_kobj;
debugf0("%s() idx=%d\n", __func__, mci->mc_idx); debugf0("%s() idx=%d\n", __func__, mci->mc_idx);
memset(edac_mci_kobj, 0, sizeof(*edac_mci_kobj));
/* set the name of the mc<id> object */
err = kobject_set_name(edac_mci_kobj, "mc%d", mci->mc_idx);
if (err)
return err;
/* link to our parent the '..../edac/mc' object */
edac_mci_kobj->parent = &edac_memctrl_kobj;
edac_mci_kobj->ktype = &ktype_mci;
/* register the mc<id> kobject */
err = kobject_register(edac_mci_kobj);
if (err)
return err;
/* create a symlink for the device */ /* create a symlink for the device */
err = sysfs_create_link(edac_mci_kobj, &mci->dev->kobj, err = sysfs_create_link(kobj_mci, &mci->dev->kobj,
EDAC_DEVICE_SYMLINK); EDAC_DEVICE_SYMLINK);
if (err) if (err) {
debugf1("%s() failure to create symlink\n", __func__);
goto fail0; goto fail0;
}
/* If the low level driver desires some attributes, /* If the low level driver desires some attributes,
* then create them now for the driver. * then create them now for the driver.
*/ */
if (mci->mc_driver_sysfs_attributes) { if (mci->mc_driver_sysfs_attributes) {
err = edac_create_driver_attributes(mci); err = edac_create_mci_instance_attributes(mci);
if (err) if (err) {
debugf1("%s() failure to create mci attributes\n",
__func__);
goto fail0; goto fail0;
} }
}
/* Make directories for each CSROW object /* Make directories for each CSROW object under the mc<id> kobject
* under the mc<id> kobject
*/ */
for (i = 0; i < mci->nr_csrows; i++) { for (i = 0; i < mci->nr_csrows; i++) {
csrow = &mci->csrows[i]; csrow = &mci->csrows[i];
/* Only expose populated CSROWs */ /* Only expose populated CSROWs */
if (csrow->nr_pages > 0) { if (csrow->nr_pages > 0) {
err = edac_create_csrow_object(edac_mci_kobj, csrow, i); err = edac_create_csrow_object(mci, csrow, i);
if (err) if (err) {
debugf1("%s() failure: create csrow %d obj\n",
__func__, i);
goto fail1; goto fail1;
} }
} }
}
return 0; return 0;
...@@ -844,16 +916,17 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) ...@@ -844,16 +916,17 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
fail1: fail1:
for (i--; i >= 0; i--) { for (i--; i >= 0; i--) {
if (csrow->nr_pages > 0) { if (csrow->nr_pages > 0) {
init_completion(&csrow->kobj_complete);
kobject_unregister(&mci->csrows[i].kobj); kobject_unregister(&mci->csrows[i].kobj);
wait_for_completion(&csrow->kobj_complete);
} }
} }
/* remove the mci instance's attributes, if any */
edac_remove_mci_instance_attributes(mci);
/* remove the symlink */
sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK);
fail0: fail0:
init_completion(&mci->kobj_complete);
kobject_unregister(edac_mci_kobj);
wait_for_completion(&mci->kobj_complete);
return err; return err;
} }
...@@ -869,14 +942,83 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) ...@@ -869,14 +942,83 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
/* remove all csrow kobjects */ /* remove all csrow kobjects */
for (i = 0; i < mci->nr_csrows; i++) { for (i = 0; i < mci->nr_csrows; i++) {
if (mci->csrows[i].nr_pages > 0) { if (mci->csrows[i].nr_pages > 0) {
init_completion(&mci->csrows[i].kobj_complete); debugf0("%s() unreg csrow-%d\n", __func__, i);
kobject_unregister(&mci->csrows[i].kobj); kobject_unregister(&mci->csrows[i].kobj);
wait_for_completion(&mci->csrows[i].kobj_complete);
} }
} }
debugf0("%s() remove_link\n", __func__);
/* remove the symlink */
sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK);
init_completion(&mci->kobj_complete);
debugf0("%s() remove_mci_instance\n", __func__);
/* remove this mci instance's attribtes */
edac_remove_mci_instance_attributes(mci);
debugf0("%s() unregister this mci kobj\n", __func__);
/* unregister this instance's kobject */
kobject_unregister(&mci->edac_mci_kobj); kobject_unregister(&mci->edac_mci_kobj);
wait_for_completion(&mci->kobj_complete);
} }
/*
* edac_setup_sysfs_mc_kset(void)
*
* Initialize the mc_kset for the 'mc' entry
* This requires creating the top 'mc' directory with a kset
* and its controls/attributes.
*
* To this 'mc' kset, instance 'mci' will be grouped as children.
*
* Return: 0 SUCCESS
* !0 FAILURE error code
*/
int edac_sysfs_setup_mc_kset(void)
{
int err = 0;
struct sysdev_class *edac_class;
debugf1("%s()\n", __func__);
/* get the /sys/devices/system/edac class reference */
edac_class = edac_get_edac_class();
if (edac_class == NULL) {
debugf1("%s() no edac_class error=%d\n", __func__, err);
goto fail_out;
}
/* Init the MC's kobject */
mc_kset.kobj.parent = &edac_class->kset.kobj;
/* register the mc_kset */
err = kset_register(&mc_kset);
if (err) {
debugf1("%s() Failed to register '.../edac/mc'\n", __func__);
goto fail_out;
}
debugf1("%s() Registered '.../edac/mc' kobject\n", __func__);
return 0;
/* error unwind stack */
fail_out:
return err;
}
/*
* edac_sysfs_teardown_mc_kset
*
* deconstruct the mc_ket for memory controllers
*/
void edac_sysfs_teardown_mc_kset(void)
{
kset_unregister(&mc_kset);
}
...@@ -14,11 +14,11 @@ ...@@ -14,11 +14,11 @@
#include "edac_core.h" #include "edac_core.h"
#include "edac_module.h" #include "edac_module.h"
#define EDAC_MC_VERSION "Ver: 2.0.4 " __DATE__ #define EDAC_MC_VERSION "Ver: 2.0.5 " __DATE__
#ifdef CONFIG_EDAC_DEBUG #ifdef CONFIG_EDAC_DEBUG
/* Values of 0 to 4 will generate output */ /* Values of 0 to 4 will generate output */
int edac_debug_level = 1; int edac_debug_level = 2;
EXPORT_SYMBOL_GPL(edac_debug_level); EXPORT_SYMBOL_GPL(edac_debug_level);
#endif #endif
...@@ -153,7 +153,7 @@ static int __init edac_init(void) ...@@ -153,7 +153,7 @@ static int __init edac_init(void)
edac_pci_clear_parity_errors(); edac_pci_clear_parity_errors();
/* /*
* perform the registration of the /sys/devices/system/edac object * perform the registration of the /sys/devices/system/edac class object
*/ */
if (edac_register_sysfs_edac_name()) { if (edac_register_sysfs_edac_name()) {
edac_printk(KERN_ERR, EDAC_MC, edac_printk(KERN_ERR, EDAC_MC,
...@@ -162,29 +162,29 @@ static int __init edac_init(void) ...@@ -162,29 +162,29 @@ static int __init edac_init(void)
goto error; goto error;
} }
/* Create the MC sysfs entries, must be first /*
* now set up the mc_kset under the edac class object
*/ */
if (edac_sysfs_memctrl_setup()) { err = edac_sysfs_setup_mc_kset();
edac_printk(KERN_ERR, EDAC_MC, if (err)
"Error initializing sysfs code\n"); goto sysfs_setup_fail;
err = -ENODEV;
goto error_sysfs;
}
/* Setup/Initialize the edac_device system */ /* Setup/Initialize the workq for this core */
err = edac_workqueue_setup(); err = edac_workqueue_setup();
if (err) { if (err) {
edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n");
goto error_mem; goto workq_fail;
} }
return 0; return 0;
/* Error teardown stack */ /* Error teardown stack */
error_mem: workq_fail:
edac_sysfs_memctrl_teardown(); edac_sysfs_teardown_mc_kset();
error_sysfs:
sysfs_setup_fail:
edac_unregister_sysfs_edac_name(); edac_unregister_sysfs_edac_name();
error: error:
return err; return err;
} }
...@@ -199,7 +199,7 @@ static void __exit edac_exit(void) ...@@ -199,7 +199,7 @@ static void __exit edac_exit(void)
/* tear down the various subsystems */ /* tear down the various subsystems */
edac_workqueue_teardown(); edac_workqueue_teardown();
edac_sysfs_memctrl_teardown(); edac_sysfs_teardown_mc_kset();
edac_unregister_sysfs_edac_name(); edac_unregister_sysfs_edac_name();
} }
......
...@@ -18,11 +18,15 @@ ...@@ -18,11 +18,15 @@
* INTERNAL EDAC MODULE: * INTERNAL EDAC MODULE:
* EDAC memory controller sysfs create/remove functions * EDAC memory controller sysfs create/remove functions
* and setup/teardown functions * and setup/teardown functions
*
* edac_mc objects
*/ */
extern int edac_sysfs_setup_mc_kset(void);
extern void edac_sysfs_teardown_mc_kset(void);
extern int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci);
extern void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci);
extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci); extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci);
extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci); extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci);
extern int edac_sysfs_memctrl_setup(void);
extern void edac_sysfs_memctrl_teardown(void);
extern void edac_check_mc_devices(void); extern void edac_check_mc_devices(void);
extern int edac_get_log_ue(void); extern int edac_get_log_ue(void);
extern int edac_get_log_ce(void); extern int edac_get_log_ce(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment