X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fedac%2Fedac_mc_sysfs.c;h=021d18795145cbc364e676d763ed8369ccea8ee1;hb=c805df3d6205573fa7a8ef2334b1c8d67381dc8b;hp=6e2785bd011c138fabe191aeb31727e020ffd33d;hpb=20bcb7a81dee21bfa3408f03f46b2891c9b5c84b;p=linux-2.6 diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 6e2785bd01..021d187951 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -1,57 +1,60 @@ /* * edac_mc kernel module - * (C) 2005, 2006 Linux Networx (http://lnxi.com) + * (C) 2005-2007 Linux Networx (http://lnxi.com) + * * This file may be distributed under the terms of the * GNU General Public License. * - * Written Doug Thompson + * Written Doug Thompson www.softwarebitmaker.com * */ -#include -#include #include +#include #include "edac_core.h" #include "edac_module.h" + /* MC EDAC Controls, setable by module parameter, and sysfs */ -static int log_ue = 1; -static int log_ce = 1; -static int panic_on_ue; -static int poll_msec = 1000; +static int edac_mc_log_ue = 1; +static int edac_mc_log_ce = 1; +static int edac_mc_panic_on_ue; +static int edac_mc_poll_msec = 1000; /* Getter functions for above */ -int edac_get_log_ue() +int edac_mc_get_log_ue(void) { - return log_ue; + return edac_mc_log_ue; } -int edac_get_log_ce() +int edac_mc_get_log_ce(void) { - return log_ce; + return edac_mc_log_ce; } -int edac_get_panic_on_ue() +int edac_mc_get_panic_on_ue(void) { - return panic_on_ue; + return edac_mc_panic_on_ue; } -int edac_get_poll_msec() +/* this is temporary */ +int edac_mc_get_poll_msec(void) { - return poll_msec; + return edac_mc_poll_msec; } /* Parameter declarations for above */ -module_param(panic_on_ue, int, 0644); -MODULE_PARM_DESC(panic_on_ue, "Panic on uncorrected error: 0=off 1=on"); -module_param(log_ue, int, 0644); -MODULE_PARM_DESC(log_ue, "Log uncorrectable error to console: 0=off 1=on"); -module_param(log_ce, int, 0644); -MODULE_PARM_DESC(log_ce, "Log correctable error to console: 0=off 1=on"); -module_param(poll_msec, int, 0644); -MODULE_PARM_DESC(poll_msec, "Polling period in milliseconds"); - +module_param(edac_mc_panic_on_ue, int, 0644); +MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on"); +module_param(edac_mc_log_ue, int, 0644); +MODULE_PARM_DESC(edac_mc_log_ue, + "Log uncorrectable error to console: 0=off 1=on"); +module_param(edac_mc_log_ce, int, 0644); +MODULE_PARM_DESC(edac_mc_log_ce, + "Log correctable error to console: 0=off 1=on"); +module_param(edac_mc_poll_msec, int, 0644); +MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); /* * various constants for Memory Controllers @@ -70,7 +73,8 @@ static const char *mem_types[] = { [MEM_RMBS] = "RMBS", [MEM_DDR2] = "Unbuffered-DDR2", [MEM_FB_DDR2] = "FullyBuffered-DDR2", - [MEM_RDDR2] = "Registered-DDR2" + [MEM_RDDR2] = "Registered-DDR2", + [MEM_XDR] = "XDR" }; static const char *dev_types[] = { @@ -97,15 +101,7 @@ static const char *edac_caps[] = { [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; @@ -113,13 +109,13 @@ static struct completion edac_memctrl_kobj_complete; */ static ssize_t memctrl_int_show(void *ptr, char *buffer) { - int *value = (int*) ptr; + int *value = (int *)ptr; return sprintf(buffer, "%u\n", *value); } static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count) { - int *value = (int*) ptr; + int *value = (int *)ptr; if (isdigit(*buffer)) *value = simple_strtoul(buffer, NULL, 0); @@ -127,145 +123,21 @@ static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t 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 control files */ -MEMCTRL_ATTR(panic_on_ue,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); -MEMCTRL_ATTR(log_ue,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); -MEMCTRL_ATTR(log_ce,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); -MEMCTRL_ATTR(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_panic_on_ue, - &attr_log_ue, - &attr_log_ce, - &attr_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 +/* + * mc poll_msec time value */ -int edac_sysfs_memctrl_setup(void) +static ssize_t poll_msec_int_store(void *ptr, const char *buffer, size_t count) { - 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; - } + int *value = (int *)ptr; - /* 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; - } + if (isdigit(*buffer)) { + *value = simple_strtoul(buffer, NULL, 0); - /* 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; + /* notify edac_mc engine to reset the poll period */ + edac_mc_reset_delay_period(*value); } - 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); + return count; } @@ -274,57 +146,56 @@ void edac_sysfs_memctrl_teardown(void) /* Set of more default csrow attribute show/store functions */ static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, - int private) + int private) { - return sprintf(data,"%u\n", csrow->ue_count); + return sprintf(data, "%u\n", csrow->ue_count); } static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, - int private) + int private) { - return sprintf(data,"%u\n", csrow->ce_count); + return sprintf(data, "%u\n", csrow->ce_count); } static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, - int private) + int private) { - return sprintf(data,"%u\n", PAGES_TO_MiB(csrow->nr_pages)); + return sprintf(data, "%u\n", PAGES_TO_MiB(csrow->nr_pages)); } static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, - int private) + int private) { - return sprintf(data,"%s\n", mem_types[csrow->mtype]); + return sprintf(data, "%s\n", mem_types[csrow->mtype]); } static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, - int private) + int private) { - return sprintf(data,"%s\n", dev_types[csrow->dtype]); + return sprintf(data, "%s\n", dev_types[csrow->dtype]); } static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, - int private) + int private) { - return sprintf(data,"%s\n", edac_caps[csrow->edac_mode]); + return sprintf(data, "%s\n", edac_caps[csrow->edac_mode]); } /* show/store functions for DIMM Label attributes */ static ssize_t channel_dimm_label_show(struct csrow_info *csrow, - char *data, int channel) + char *data, int channel) { - return snprintf(data, EDAC_MC_LABEL_LEN,"%s", + return snprintf(data, EDAC_MC_LABEL_LEN, "%s", csrow->channels[channel].label); } static ssize_t channel_dimm_label_store(struct csrow_info *csrow, - const char *data, - size_t count, - int channel) + const char *data, + size_t count, int channel) { ssize_t max_size = 0; - max_size = min((ssize_t)count,(ssize_t)EDAC_MC_LABEL_LEN-1); + max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); strncpy(csrow->channels[channel].label, data, max_size); csrow->channels[channel].label[max_size] = '\0'; @@ -333,8 +204,7 @@ static ssize_t channel_dimm_label_store(struct csrow_info *csrow, /* show function for dynamic chX_ce_count attribute */ static ssize_t channel_ce_count_show(struct csrow_info *csrow, - char *data, - int channel) + char *data, int channel) { return sprintf(data, "%u\n", csrow->channels[channel].ce_count); } @@ -342,9 +212,9 @@ static ssize_t channel_ce_count_show(struct csrow_info *csrow, /* csrow specific attribute structure */ struct csrowdev_attribute { struct attribute attr; - ssize_t (*show)(struct csrow_info *,char *,int); - ssize_t (*store)(struct csrow_info *, const char *,size_t,int); - int private; + ssize_t(*show) (struct csrow_info *, char *, int); + ssize_t(*store) (struct csrow_info *, const char *, size_t, int); + int private; }; #define to_csrow(k) container_of(k, struct csrow_info, kobj) @@ -352,36 +222,33 @@ struct csrowdev_attribute { /* Set of show/store higher level functions for default csrow attributes */ static ssize_t csrowdev_show(struct kobject *kobj, - struct attribute *attr, - char *buffer) + struct attribute *attr, char *buffer) { struct csrow_info *csrow = to_csrow(kobj); struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); if (csrowdev_attr->show) return csrowdev_attr->show(csrow, - buffer, - csrowdev_attr->private); + buffer, csrowdev_attr->private); return -EIO; } static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) + const char *buffer, size_t count) { struct csrow_info *csrow = to_csrow(kobj); - struct csrowdev_attribute * csrowdev_attr = to_csrowdev_attr(attr); + struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); if (csrowdev_attr->store) return csrowdev_attr->store(csrow, buffer, - count, - csrowdev_attr->private); + count, csrowdev_attr->private); return -EIO; } static struct sysfs_ops csrowfs_ops = { - .show = csrowdev_show, - .store = csrowdev_store + .show = csrowdev_show, + .store = csrowdev_store }; #define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ @@ -393,12 +260,12 @@ static struct csrowdev_attribute attr_##_name = { \ }; /* default cwrow/attribute files */ -CSROWDEV_ATTR(size_mb,S_IRUGO,csrow_size_show,NULL,0); -CSROWDEV_ATTR(dev_type,S_IRUGO,csrow_dev_type_show,NULL,0); -CSROWDEV_ATTR(mem_type,S_IRUGO,csrow_mem_type_show,NULL,0); -CSROWDEV_ATTR(edac_mode,S_IRUGO,csrow_edac_mode_show,NULL,0); -CSROWDEV_ATTR(ue_count,S_IRUGO,csrow_ue_count_show,NULL,0); -CSROWDEV_ATTR(ce_count,S_IRUGO,csrow_ce_count_show,NULL,0); +CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0); +CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0); +CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0); +CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0); +CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0); +CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0); /* default attributes of the CSROW object */ static struct csrowdev_attribute *default_csrow_attr[] = { @@ -411,98 +278,68 @@ static struct csrowdev_attribute *default_csrow_attr[] = { NULL, }; - /* possible dynamic channel DIMM Label attribute files */ -CSROWDEV_ATTR(ch0_dimm_label,S_IRUGO|S_IWUSR, - channel_dimm_label_show, - channel_dimm_label_store, - 0 ); -CSROWDEV_ATTR(ch1_dimm_label,S_IRUGO|S_IWUSR, - channel_dimm_label_show, - channel_dimm_label_store, - 1 ); -CSROWDEV_ATTR(ch2_dimm_label,S_IRUGO|S_IWUSR, - channel_dimm_label_show, - channel_dimm_label_store, - 2 ); -CSROWDEV_ATTR(ch3_dimm_label,S_IRUGO|S_IWUSR, - channel_dimm_label_show, - channel_dimm_label_store, - 3 ); -CSROWDEV_ATTR(ch4_dimm_label,S_IRUGO|S_IWUSR, - channel_dimm_label_show, - channel_dimm_label_store, - 4 ); -CSROWDEV_ATTR(ch5_dimm_label,S_IRUGO|S_IWUSR, - channel_dimm_label_show, - channel_dimm_label_store, - 5 ); +CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR, + channel_dimm_label_show, channel_dimm_label_store, 0); +CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR, + channel_dimm_label_show, channel_dimm_label_store, 1); +CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR, + channel_dimm_label_show, channel_dimm_label_store, 2); +CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR, + channel_dimm_label_show, channel_dimm_label_store, 3); +CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR, + channel_dimm_label_show, channel_dimm_label_store, 4); +CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR, + channel_dimm_label_show, channel_dimm_label_store, 5); /* Total possible dynamic DIMM Label attribute file table */ static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { - &attr_ch0_dimm_label, - &attr_ch1_dimm_label, - &attr_ch2_dimm_label, - &attr_ch3_dimm_label, - &attr_ch4_dimm_label, - &attr_ch5_dimm_label + &attr_ch0_dimm_label, + &attr_ch1_dimm_label, + &attr_ch2_dimm_label, + &attr_ch3_dimm_label, + &attr_ch4_dimm_label, + &attr_ch5_dimm_label }; /* possible dynamic channel ce_count attribute files */ -CSROWDEV_ATTR(ch0_ce_count,S_IRUGO|S_IWUSR, - channel_ce_count_show, - NULL, - 0 ); -CSROWDEV_ATTR(ch1_ce_count,S_IRUGO|S_IWUSR, - channel_ce_count_show, - NULL, - 1 ); -CSROWDEV_ATTR(ch2_ce_count,S_IRUGO|S_IWUSR, - channel_ce_count_show, - NULL, - 2 ); -CSROWDEV_ATTR(ch3_ce_count,S_IRUGO|S_IWUSR, - channel_ce_count_show, - NULL, - 3 ); -CSROWDEV_ATTR(ch4_ce_count,S_IRUGO|S_IWUSR, - channel_ce_count_show, - NULL, - 4 ); -CSROWDEV_ATTR(ch5_ce_count,S_IRUGO|S_IWUSR, - channel_ce_count_show, - NULL, - 5 ); +CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0); +CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1); +CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2); +CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3); +CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4); +CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5); /* Total possible dynamic ce_count attribute file table */ static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { - &attr_ch0_ce_count, - &attr_ch1_ce_count, - &attr_ch2_ce_count, - &attr_ch3_ce_count, - &attr_ch4_ce_count, - &attr_ch5_ce_count + &attr_ch0_ce_count, + &attr_ch1_ce_count, + &attr_ch2_ce_count, + &attr_ch3_ce_count, + &attr_ch4_ce_count, + &attr_ch5_ce_count }; - #define EDAC_NR_CHANNELS 6 /* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ static int edac_create_channel_files(struct kobject *kobj, int chan) { - int err=-ENODEV; + int err = -ENODEV; if (chan >= EDAC_NR_CHANNELS) return err; /* create the DIMM label attribute file */ err = sysfs_create_file(kobj, - (struct attribute *) dynamic_csrow_dimm_attr[chan]); + (struct attribute *) + dynamic_csrow_dimm_attr[chan]); if (!err) { /* create the CE Count attribute file */ err = sysfs_create_file(kobj, - (struct attribute *)dynamic_csrow_ce_count_attr[chan]); + (struct attribute *) + dynamic_csrow_ce_count_attr[chan]); } else { debugf1("%s() dimm labels and ce_count files created", __func__); @@ -514,61 +351,81 @@ static int edac_create_channel_files(struct kobject *kobj, int chan) /* No memory to release for this kobj */ static void edac_csrow_instance_release(struct kobject *kobj) { + struct mem_ctl_info *mci; struct csrow_info *cs; + debugf1("%s()\n", __func__); + 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 */ static struct kobj_type ktype_csrow = { .release = edac_csrow_instance_release, .sysfs_ops = &csrowfs_ops, - .default_attrs = (struct attribute **) default_csrow_attr, + .default_attrs = (struct attribute **)default_csrow_attr, }; /* Create a CSROW object under specifed edac_mc_device */ -static int edac_create_csrow_object( - struct kobject *edac_mci_kobj, - struct csrow_info *csrow, - int index) +static int edac_create_csrow_object(struct mem_ctl_info *mci, + struct csrow_info *csrow, int index) { - int err = 0; + struct kobject *kobj_mci = &mci->edac_mci_kobj; + struct kobject *kobj; int chan; - - memset(&csrow->kobj, 0, sizeof(csrow->kobj)); + int err; /* generate ..../edac/mc/mc/csrow */ + memset(&csrow->kobj, 0, sizeof(csrow->kobj)); + csrow->mci = mci; /* include container up link */ - csrow->kobj.parent = edac_mci_kobj; - csrow->kobj.ktype = &ktype_csrow; + /* bump the mci instance's kobject's ref count */ + kobj = kobject_get(&mci->edac_mci_kobj); + if (!kobj) { + err = -ENODEV; + goto err_out; + } - /* name this instance of csrow */ - err = kobject_set_name(&csrow->kobj,"csrow%d",index); + /* Instanstiate the csrow object */ + err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci, + "csrow%d", index); if (err) - goto error_exit; + goto err_release_top_kobj; - /* Instanstiate the csrow object */ - err = kobject_register(&csrow->kobj); - if (!err) { - /* Create the dyanmic attribute files on this csrow, - * namely, the DIMM labels and the channel ce_count - */ - for (chan = 0; chan < csrow->nr_channels; chan++) { - err = edac_create_channel_files(&csrow->kobj,chan); - if (err) - break; + /* At this point, to release a csrow kobj, one must + * call the kobject_put and allow that tear down + * to work the releasing + */ + + /* Create the dyanmic attribute files on this csrow, + * namely, the DIMM labels and the channel ce_count + */ + for (chan = 0; chan < csrow->nr_channels; chan++) { + err = edac_create_channel_files(&csrow->kobj, chan); + if (err) { + /* special case the unregister here */ + kobject_put(&csrow->kobj); + goto err_out; } } + kobject_uevent(&csrow->kobj, KOBJ_ADD); + return 0; -error_exit: + /* error unwind stack */ +err_release_top_kobj: + kobject_put(&mci->edac_mci_kobj); + +err_out: return err; } /* default sysfs methods and data structures for the main MCI kobject */ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, - const char *data, size_t count) + const char *data, size_t count) { int row, chan; @@ -601,7 +458,7 @@ static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, memctrl_int_store(&bandwidth, data, count); - if (!(*mci->set_sdram_scrub_rate)(mci, &bandwidth)) { + if (!(*mci->set_sdram_scrub_rate) (mci, &bandwidth)) { edac_printk(KERN_DEBUG, EDAC_MC, "Scrub rate set successfully, applied: %d\n", bandwidth); @@ -624,7 +481,7 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) u32 bandwidth = -1; if (mci->get_sdram_scrub_rate) { - if (!(*mci->get_sdram_scrub_rate)(mci, &bandwidth)) { + if (!(*mci->get_sdram_scrub_rate) (mci, &bandwidth)) { edac_printk(KERN_DEBUG, EDAC_MC, "Scrub rate successfully, fetched: %d\n", bandwidth); @@ -645,32 +502,32 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) /* default attribute files for the MCI object */ static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) { - return sprintf(data,"%d\n", mci->ue_count); + return sprintf(data, "%d\n", mci->ue_count); } static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) { - return sprintf(data,"%d\n", mci->ce_count); + return sprintf(data, "%d\n", mci->ce_count); } static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) { - return sprintf(data,"%d\n", mci->ce_noinfo_count); + return sprintf(data, "%d\n", mci->ce_noinfo_count); } static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) { - return sprintf(data,"%d\n", mci->ue_noinfo_count); + return sprintf(data, "%d\n", mci->ue_noinfo_count); } static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) { - return sprintf(data,"%ld\n", (jiffies - mci->start_time) / HZ); + return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ); } static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) { - return sprintf(data,"%s\n", mci->ctl_name); + return sprintf(data, "%s\n", mci->ctl_name); } static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) @@ -678,7 +535,7 @@ static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) int total_pages, csrow_idx; for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows; - csrow_idx++) { + csrow_idx++) { struct csrow_info *csrow = &mci->csrows[csrow_idx]; if (!csrow->nr_pages) @@ -687,24 +544,18 @@ static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) total_pages += csrow->nr_pages; } - return sprintf(data,"%u\n", PAGES_TO_MiB(total_pages)); + return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages)); } -struct mcidev_attribute { - struct attribute attr; - ssize_t (*show)(struct mem_ctl_info *,char *); - ssize_t (*store)(struct mem_ctl_info *, const char *,size_t); -}; - #define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) -#define to_mcidev_attr(a) container_of(a, struct mcidev_attribute, attr) +#define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr) /* MCI show/store functions for top most object */ static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, - char *buffer) + char *buffer) { struct mem_ctl_info *mem_ctl_info = to_mci(kobj); - struct mcidev_attribute * mcidev_attr = to_mcidev_attr(attr); + struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); if (mcidev_attr->show) return mcidev_attr->show(mem_ctl_info, buffer); @@ -713,10 +564,10 @@ static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, } static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) + const char *buffer, size_t count) { struct mem_ctl_info *mem_ctl_info = to_mci(kobj); - struct mcidev_attribute * mcidev_attr = to_mcidev_attr(attr); + struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); if (mcidev_attr->store) return mcidev_attr->store(mem_ctl_info, buffer, count); @@ -724,35 +575,36 @@ static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, return -EIO; } +/* Intermediate show/store table */ static struct sysfs_ops mci_ops = { .show = mcidev_show, .store = mcidev_store }; #define MCIDEV_ATTR(_name,_mode,_show,_store) \ -static struct mcidev_attribute mci_attr_##_name = { \ +static struct mcidev_sysfs_attribute mci_attr_##_name = { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ }; /* default Control file */ -MCIDEV_ATTR(reset_counters,S_IWUSR,NULL,mci_reset_counters_store); +MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); /* default Attribute files */ -MCIDEV_ATTR(mc_name,S_IRUGO,mci_ctl_name_show,NULL); -MCIDEV_ATTR(size_mb,S_IRUGO,mci_size_mb_show,NULL); -MCIDEV_ATTR(seconds_since_reset,S_IRUGO,mci_seconds_show,NULL); -MCIDEV_ATTR(ue_noinfo_count,S_IRUGO,mci_ue_noinfo_show,NULL); -MCIDEV_ATTR(ce_noinfo_count,S_IRUGO,mci_ce_noinfo_show,NULL); -MCIDEV_ATTR(ue_count,S_IRUGO,mci_ue_count_show,NULL); -MCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL); +MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); +MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); +MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); +MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); +MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); +MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); +MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); /* memory scrubber attribute file */ -MCIDEV_ATTR(sdram_scrub_rate,S_IRUGO|S_IWUSR,mci_sdram_scrub_rate_show,\ - mci_sdram_scrub_rate_store); +MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, + mci_sdram_scrub_rate_store); -static struct mcidev_attribute *mci_attr[] = { +static struct mcidev_sysfs_attribute *mci_attr[] = { &mci_attr_reset_counters, &mci_attr_mc_name, &mci_attr_size_mb, @@ -765,27 +617,254 @@ static struct mcidev_attribute *mci_attr[] = { NULL }; + /* * 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; 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 = { - .release = edac_mci_instance_release, + .release = edac_mci_control_release, .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 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, poll_msec_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 = {.ktype = &ktype_mc_set_attribs }, +}; + + +/* + * 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)); + + /* 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; + } + + /* this instance become part of the mc_kset */ + kobj_mci->kset = &mc_kset; + + /* register the mc kobject to the mc_kset */ + err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL, + "mc%d", mci->mc_idx); + if (err) { + debugf1("%s()Failed to register '.../edac/mc%d'\n", + __func__, mci->mc_idx); + goto kobj_reg_fail; + } + kobject_uevent(kobj_mci, KOBJ_ADD); + + /* 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_put(&mci->edac_mci_kobj); +} #define EDAC_DEVICE_SYMLINK "device" +/* + * edac_create_mci_instance_attributes + * create MC driver specific attributes at the topmost level + * directory of this mci instance. + */ +static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci) +{ + int err; + 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; + + while (sysfs_attrib && sysfs_attrib->attr.name) { + err = sysfs_create_file(&mci->edac_mci_kobj, + (struct attribute*) sysfs_attrib); + if (err) { + return err; + } + + sysfs_attrib++; + } + + 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, * mc under the 'mc' directory @@ -799,42 +878,43 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) int i; int err; 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); - memset(edac_mci_kobj, 0, sizeof(*edac_mci_kobj)); - - /* set the name of the mc 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 kobject */ - err = kobject_register(edac_mci_kobj); - if (err) - return err; /* 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); - if (err) + if (err) { + debugf1("%s() failure to create symlink\n", __func__); goto fail0; + } - /* Make directories for each CSROW object - * under the mc kobject + /* If the low level driver desires some attributes, + * then create them now for the driver. + */ + if (mci->mc_driver_sysfs_attributes) { + err = edac_create_mci_instance_attributes(mci); + if (err) { + debugf1("%s() failure to create mci attributes\n", + __func__); + goto fail0; + } + } + + /* Make directories for each CSROW object under the mc kobject */ for (i = 0; i < mci->nr_csrows; i++) { csrow = &mci->csrows[i]; /* Only expose populated CSROWs */ if (csrow->nr_pages > 0) { - err = edac_create_csrow_object(edac_mci_kobj,csrow,i); - if (err) + err = edac_create_csrow_object(mci, csrow, i); + if (err) { + debugf1("%s() failure: create csrow %d obj\n", + __func__, i); goto fail1; + } } } @@ -842,18 +922,19 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) /* CSROW error: backout what has already been registered, */ fail1: - for ( i--; i >= 0; i--) { + for (i--; i >= 0; i--) { if (csrow->nr_pages > 0) { - init_completion(&csrow->kobj_complete); - kobject_unregister(&mci->csrows[i].kobj); - wait_for_completion(&csrow->kobj_complete); + kobject_put(&mci->csrows[i].kobj); } } + /* 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: - init_completion(&mci->kobj_complete); - kobject_unregister(edac_mci_kobj); - wait_for_completion(&mci->kobj_complete); return err; } @@ -869,16 +950,84 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) /* remove all csrow kobjects */ for (i = 0; i < mci->nr_csrows; i++) { if (mci->csrows[i].nr_pages > 0) { - init_completion(&mci->csrows[i].kobj_complete); - kobject_unregister(&mci->csrows[i].kobj); - wait_for_completion(&mci->csrows[i].kobj_complete); + debugf0("%s() unreg csrow-%d\n", __func__, i); + kobject_put(&mci->csrows[i].kobj); } } + debugf0("%s() remove_link\n", __func__); + + /* remove the symlink */ sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); - init_completion(&mci->kobj_complete); - kobject_unregister(&mci->edac_mci_kobj); - wait_for_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_put(&mci->edac_mci_kobj); } + + +/* + * 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 */ + kobject_set_name(&mc_kset.kobj, "mc"); + 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); +} +