X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fedac%2Fedac_mc_sysfs.c;h=3c8f19f5ac8183ed2dd2fd13fce9c89c9896a7e4;hb=58594a804233d863ee344adf7554571d831f6c23;hp=67610a6ebf875765682cb50bec4226b42f956fc3;hpb=91e14b294f3fe4d8b28516b21ceef187ea9b1bdf;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 67610a6ebf87..3c8f19f5ac81 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -7,7 +7,7 @@ * * Written Doug Thompson www.softwarebitmaker.com * - * (c) 2012-2013 - Mauro Carvalho Chehab + * (c) 2012-2013 - Mauro Carvalho Chehab * The entire API were re-written, and ported to use struct device * */ @@ -52,16 +52,20 @@ int edac_mc_get_poll_msec(void) static int edac_set_poll_msec(const char *val, struct kernel_param *kp) { - long l; + unsigned long l; int ret; if (!val) return -EINVAL; - ret = strict_strtol(val, 0, &l); - if (ret == -EINVAL || ((int)l != l)) + ret = kstrtoul(val, 0, &l); + if (ret) + return ret; + + if (l < 1000) return -EINVAL; - *((int *)kp->arg) = l; + + *((unsigned long *)kp->arg) = l; /* notify edac_mc engine to reset the poll period */ edac_mc_reset_delay_period(l); @@ -104,7 +108,9 @@ static const char * const mem_types[] = { [MEM_RDDR2] = "Registered-DDR2", [MEM_XDR] = "XDR", [MEM_DDR3] = "Unbuffered-DDR3", - [MEM_RDDR3] = "Registered-DDR3" + [MEM_RDDR3] = "Registered-DDR3", + [MEM_DDR4] = "Unbuffered-DDR4", + [MEM_RDDR4] = "Registered-DDR4" }; static const char * const dev_types[] = { @@ -151,7 +157,7 @@ struct dev_ch_attribute { }; #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \ - struct dev_ch_attribute dev_attr_legacy_##_name = \ + static struct dev_ch_attribute dev_attr_legacy_##_name = \ { __ATTR(_name, _mode, _show, _store), (_var) } #define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel) @@ -223,7 +229,7 @@ static ssize_t channel_dimm_label_show(struct device *dev, if (!rank->dimm->label[0]) return 0; - return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", + return snprintf(data, sizeof(rank->dimm->label) + 1, "%s\n", rank->dimm->label); } @@ -234,14 +240,21 @@ static ssize_t channel_dimm_label_store(struct device *dev, struct csrow_info *csrow = to_csrow(dev); unsigned chan = to_channel(mattr); struct rank_info *rank = csrow->channels[chan]; + size_t copy_count = count; + + if (count == 0) + return -EINVAL; - ssize_t max_size = 0; + if (data[count - 1] == '\0' || data[count - 1] == '\n') + copy_count -= 1; - max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); - strncpy(rank->dimm->label, data, max_size); - rank->dimm->label[max_size] = '\0'; + if (copy_count == 0 || copy_count >= sizeof(rank->dimm->label)) + return -EINVAL; + + strncpy(rank->dimm->label, data, copy_count); + rank->dimm->label[copy_count] = '\0'; - return max_size; + return count; } /* show function for dynamic chX_ce_count attribute */ @@ -300,9 +313,6 @@ static struct device_type csrow_attr_type = { * possible dynamic channel DIMM Label attribute files * */ - -#define EDAC_NR_CHANNELS 6 - DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 0); DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR, @@ -315,15 +325,22 @@ DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 4); DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 5); +DEVICE_CHANNEL(ch6_dimm_label, S_IRUGO | S_IWUSR, + channel_dimm_label_show, channel_dimm_label_store, 6); +DEVICE_CHANNEL(ch7_dimm_label, S_IRUGO | S_IWUSR, + channel_dimm_label_show, channel_dimm_label_store, 7); /* Total possible dynamic DIMM Label attribute file table */ -static struct device_attribute *dynamic_csrow_dimm_attr[] = { - &dev_attr_legacy_ch0_dimm_label.attr, - &dev_attr_legacy_ch1_dimm_label.attr, - &dev_attr_legacy_ch2_dimm_label.attr, - &dev_attr_legacy_ch3_dimm_label.attr, - &dev_attr_legacy_ch4_dimm_label.attr, - &dev_attr_legacy_ch5_dimm_label.attr +static struct attribute *dynamic_csrow_dimm_attr[] = { + &dev_attr_legacy_ch0_dimm_label.attr.attr, + &dev_attr_legacy_ch1_dimm_label.attr.attr, + &dev_attr_legacy_ch2_dimm_label.attr.attr, + &dev_attr_legacy_ch3_dimm_label.attr.attr, + &dev_attr_legacy_ch4_dimm_label.attr.attr, + &dev_attr_legacy_ch5_dimm_label.attr.attr, + &dev_attr_legacy_ch6_dimm_label.attr.attr, + &dev_attr_legacy_ch7_dimm_label.attr.attr, + NULL }; /* possible dynamic channel ce_count attribute files */ @@ -339,15 +356,60 @@ DEVICE_CHANNEL(ch4_ce_count, S_IRUGO, channel_ce_count_show, NULL, 4); DEVICE_CHANNEL(ch5_ce_count, S_IRUGO, channel_ce_count_show, NULL, 5); +DEVICE_CHANNEL(ch6_ce_count, S_IRUGO, + channel_ce_count_show, NULL, 6); +DEVICE_CHANNEL(ch7_ce_count, S_IRUGO, + channel_ce_count_show, NULL, 7); /* Total possible dynamic ce_count attribute file table */ -static struct device_attribute *dynamic_csrow_ce_count_attr[] = { - &dev_attr_legacy_ch0_ce_count.attr, - &dev_attr_legacy_ch1_ce_count.attr, - &dev_attr_legacy_ch2_ce_count.attr, - &dev_attr_legacy_ch3_ce_count.attr, - &dev_attr_legacy_ch4_ce_count.attr, - &dev_attr_legacy_ch5_ce_count.attr +static struct attribute *dynamic_csrow_ce_count_attr[] = { + &dev_attr_legacy_ch0_ce_count.attr.attr, + &dev_attr_legacy_ch1_ce_count.attr.attr, + &dev_attr_legacy_ch2_ce_count.attr.attr, + &dev_attr_legacy_ch3_ce_count.attr.attr, + &dev_attr_legacy_ch4_ce_count.attr.attr, + &dev_attr_legacy_ch5_ce_count.attr.attr, + &dev_attr_legacy_ch6_ce_count.attr.attr, + &dev_attr_legacy_ch7_ce_count.attr.attr, + NULL +}; + +static umode_t csrow_dev_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct csrow_info *csrow = container_of(dev, struct csrow_info, dev); + + if (idx >= csrow->nr_channels) + return 0; + + if (idx >= ARRAY_SIZE(dynamic_csrow_ce_count_attr) - 1) { + WARN_ONCE(1, "idx: %d\n", idx); + return 0; + } + + /* Only expose populated DIMMs */ + if (!csrow->channels[idx]->dimm->nr_pages) + return 0; + + return attr->mode; +} + + +static const struct attribute_group csrow_dev_dimm_group = { + .attrs = dynamic_csrow_dimm_attr, + .is_visible = csrow_dev_is_visible, +}; + +static const struct attribute_group csrow_dev_ce_count_group = { + .attrs = dynamic_csrow_ce_count_attr, + .is_visible = csrow_dev_is_visible, +}; + +static const struct attribute_group *csrow_dev_groups[] = { + &csrow_dev_dimm_group, + &csrow_dev_ce_count_group, + NULL }; static inline int nr_pages_per_csrow(struct csrow_info *csrow) @@ -364,13 +426,9 @@ static inline int nr_pages_per_csrow(struct csrow_info *csrow) static int edac_create_csrow_object(struct mem_ctl_info *mci, struct csrow_info *csrow, int index) { - int err, chan; - - if (csrow->nr_channels >= EDAC_NR_CHANNELS) - return -ENODEV; - csrow->dev.type = &csrow_attr_type; - csrow->dev.bus = &mci->bus; + csrow->dev.bus = mci->bus; + csrow->dev.groups = csrow_dev_groups; device_initialize(&csrow->dev); csrow->dev.parent = &mci->dev; csrow->mci = mci; @@ -380,45 +438,13 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci, edac_dbg(0, "creating (virtual) csrow node %s\n", dev_name(&csrow->dev)); - err = device_add(&csrow->dev); - if (err < 0) - return err; - - for (chan = 0; chan < csrow->nr_channels; chan++) { - /* Only expose populated DIMMs */ - if (!csrow->channels[chan]->dimm->nr_pages) - continue; - err = device_create_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - if (err < 0) - goto error; - err = device_create_file(&csrow->dev, - dynamic_csrow_ce_count_attr[chan]); - if (err < 0) { - device_remove_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - goto error; - } - } - - return 0; - -error: - for (--chan; chan >= 0; chan--) { - device_remove_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - device_remove_file(&csrow->dev, - dynamic_csrow_ce_count_attr[chan]); - } - put_device(&csrow->dev); - - return err; + return device_add(&csrow->dev); } /* Create a CSROW object under specifed edac_mc_device */ static int edac_create_csrow_objects(struct mem_ctl_info *mci) { - int err, i, chan; + int err, i; struct csrow_info *csrow; for (i = 0; i < mci->nr_csrows; i++) { @@ -440,14 +466,6 @@ error: csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; - for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { - if (!csrow->channels[chan]->dimm->nr_pages) - continue; - device_remove_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - device_remove_file(&csrow->dev, - dynamic_csrow_ce_count_attr[chan]); - } put_device(&mci->csrows[i]->dev); } @@ -456,23 +474,13 @@ error: static void edac_delete_csrow_objects(struct mem_ctl_info *mci) { - int i, chan; + int i; struct csrow_info *csrow; for (i = mci->nr_csrows - 1; i >= 0; i--) { csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; - for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { - if (!csrow->channels[chan]->dimm->nr_pages) - continue; - edac_dbg(1, "Removing csrow %d channel %d sysfs nodes\n", - i, chan); - device_remove_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - device_remove_file(&csrow->dev, - dynamic_csrow_ce_count_attr[chan]); - } device_unregister(&mci->csrows[i]->dev); } } @@ -502,7 +510,7 @@ static ssize_t dimmdev_label_show(struct device *dev, if (!dimm->label[0]) return 0; - return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label); + return snprintf(data, sizeof(dimm->label) + 1, "%s\n", dimm->label); } static ssize_t dimmdev_label_store(struct device *dev, @@ -511,14 +519,21 @@ static ssize_t dimmdev_label_store(struct device *dev, size_t count) { struct dimm_info *dimm = to_dimm(dev); + size_t copy_count = count; - ssize_t max_size = 0; + if (count == 0) + return -EINVAL; + + if (data[count - 1] == '\0' || data[count - 1] == '\n') + copy_count -= 1; + + if (copy_count == 0 || copy_count >= sizeof(dimm->label)) + return -EINVAL; - max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); - strncpy(dimm->label, data, max_size); - dimm->label[max_size] = '\0'; + strncpy(dimm->label, data, copy_count); + dimm->label[copy_count] = '\0'; - return max_size; + return count; } static ssize_t dimmdev_size_show(struct device *dev, @@ -605,7 +620,7 @@ static int edac_create_dimm_object(struct mem_ctl_info *mci, dimm->mci = mci; dimm->dev.type = &dimm_attr_type; - dimm->dev.bus = &mci->bus; + dimm->dev.bus = mci->bus; device_initialize(&dimm->dev); dimm->dev.parent = &mci->dev; @@ -678,7 +693,7 @@ static ssize_t mci_sdram_scrub_rate_store(struct device *dev, unsigned long bandwidth = 0; int new_bw = 0; - if (strict_strtoul(data, 10, &bandwidth) < 0) + if (kstrtoul(data, 10, &bandwidth) < 0) return -EINVAL; new_bw = mci->set_sdram_scrub_rate(mci, bandwidth); @@ -802,62 +817,22 @@ static ssize_t mci_max_location_show(struct device *dev, return p - data; } -#ifdef CONFIG_EDAC_DEBUG -static ssize_t edac_fake_inject_write(struct file *file, - const char __user *data, - size_t count, loff_t *ppos) -{ - struct device *dev = file->private_data; - struct mem_ctl_info *mci = to_mci(dev); - static enum hw_event_mc_err_type type; - u16 errcount = mci->fake_inject_count; - - if (!errcount) - errcount = 1; - - type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED - : HW_EVENT_ERR_CORRECTED; - - printk(KERN_DEBUG - "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", - errcount, - (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE", - errcount > 1 ? "s" : "", - mci->fake_inject_layer[0], - mci->fake_inject_layer[1], - mci->fake_inject_layer[2] - ); - edac_mc_handle_error(type, mci, errcount, 0, 0, 0, - mci->fake_inject_layer[0], - mci->fake_inject_layer[1], - mci->fake_inject_layer[2], - "FAKE ERROR", "for EDAC testing only"); - - return count; -} - -static const struct file_operations debug_fake_inject_fops = { - .open = simple_open, - .write = edac_fake_inject_write, - .llseek = generic_file_llseek, -}; -#endif - /* default Control file */ -DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); +static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); /* default Attribute files */ -DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); -DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); -DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); -DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); -DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); -DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); -DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); -DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL); +static DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); +static DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); +static DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); +static DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); +static DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); +static DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); +static DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); +static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL); /* memory scrubber attribute file */ -DEVICE_ATTR(sdram_scrub_rate, 0, NULL, NULL); +DEVICE_ATTR(sdram_scrub_rate, 0, mci_sdram_scrub_rate_show, + mci_sdram_scrub_rate_store); /* umode set later in is_visible */ static struct attribute *mci_attrs[] = { &dev_attr_reset_counters.attr, @@ -869,11 +844,29 @@ static struct attribute *mci_attrs[] = { &dev_attr_ue_count.attr, &dev_attr_ce_count.attr, &dev_attr_max_location.attr, + &dev_attr_sdram_scrub_rate.attr, NULL }; +static umode_t mci_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct mem_ctl_info *mci = to_mci(dev); + umode_t mode = 0; + + if (attr != &dev_attr_sdram_scrub_rate.attr) + return attr->mode; + if (mci->get_sdram_scrub_rate) + mode |= S_IRUGO; + if (mci->set_sdram_scrub_rate) + mode |= S_IWUSR; + return mode; +} + static struct attribute_group mci_attr_grp = { .attrs = mci_attrs, + .is_visible = mci_attr_is_visible, }; static const struct attribute_group *mci_attr_groups[] = { @@ -894,71 +887,6 @@ static struct device_type mci_attr_type = { .release = mci_attr_release, }; -#ifdef CONFIG_EDAC_DEBUG -static struct dentry *edac_debugfs; - -int __init edac_debugfs_init(void) -{ - edac_debugfs = debugfs_create_dir("edac", NULL); - if (IS_ERR(edac_debugfs)) { - edac_debugfs = NULL; - return -ENOMEM; - } - return 0; -} - -void __exit edac_debugfs_exit(void) -{ - debugfs_remove(edac_debugfs); -} - -int edac_create_debug_nodes(struct mem_ctl_info *mci) -{ - struct dentry *d, *parent; - char name[80]; - int i; - - if (!edac_debugfs) - return -ENODEV; - - d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs); - if (!d) - return -ENOMEM; - parent = d; - - for (i = 0; i < mci->n_layers; i++) { - sprintf(name, "fake_inject_%s", - edac_layer_name[mci->layers[i].type]); - d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent, - &mci->fake_inject_layer[i]); - if (!d) - goto nomem; - } - - d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent, - &mci->fake_inject_ue); - if (!d) - goto nomem; - - d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent, - &mci->fake_inject_count); - if (!d) - goto nomem; - - d = debugfs_create_file("fake_inject", S_IWUSR, parent, - &mci->dev, - &debug_fake_inject_fops); - if (!d) - goto nomem; - - mci->debugfs = parent; - return 0; -nomem: - debugfs_remove(mci->debugfs); - return -ENOMEM; -} -#endif - /* * Create a new Memory Controller kobject instance, * mc under the 'mc' directory @@ -967,28 +895,37 @@ nomem: * 0 Success * !0 Failure */ -int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) +int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, + const struct attribute_group **groups) { + char *name; int i, err; /* * The memory controller needs its own bus, in order to avoid * namespace conflicts at /sys/bus/edac. */ - mci->bus.name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx); - if (!mci->bus.name) + name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx); + if (!name) return -ENOMEM; - edac_dbg(0, "creating bus %s\n", mci->bus.name); - err = bus_register(&mci->bus); - if (err < 0) + + mci->bus->name = name; + + edac_dbg(0, "creating bus %s\n", mci->bus->name); + + err = bus_register(mci->bus); + if (err < 0) { + kfree(name); return err; + } /* get the /sys/devices/system/edac subsys reference */ mci->dev.type = &mci_attr_type; device_initialize(&mci->dev); mci->dev.parent = mci_pdev; - mci->dev.bus = &mci->bus; + mci->dev.bus = mci->bus; + mci->dev.groups = groups; dev_set_name(&mci->dev, "mc%d", mci->mc_idx); dev_set_drvdata(&mci->dev, mci); pm_runtime_forbid(&mci->dev); @@ -997,35 +934,18 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) err = device_add(&mci->dev); if (err < 0) { edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev)); - bus_unregister(&mci->bus); - kfree(mci->bus.name); - return err; + goto fail_unregister_bus; } - if (mci->set_sdram_scrub_rate || mci->get_sdram_scrub_rate) { - if (mci->get_sdram_scrub_rate) { - dev_attr_sdram_scrub_rate.attr.mode |= S_IRUGO; - dev_attr_sdram_scrub_rate.show = &mci_sdram_scrub_rate_show; - } - if (mci->set_sdram_scrub_rate) { - dev_attr_sdram_scrub_rate.attr.mode |= S_IWUSR; - dev_attr_sdram_scrub_rate.store = &mci_sdram_scrub_rate_store; - } - err = device_create_file(&mci->dev, - &dev_attr_sdram_scrub_rate); - if (err) { - edac_dbg(1, "failure: create sdram_scrub_rate\n"); - goto fail2; - } - } /* * Create the dimm/rank devices */ for (i = 0; i < mci->tot_dimms; i++) { struct dimm_info *dimm = mci->dimms[i]; /* Only expose populated DIMMs */ - if (dimm->nr_pages == 0) + if (!dimm->nr_pages) continue; + #ifdef CONFIG_EDAC_DEBUG edac_dbg(1, "creating dimm%d, located at ", i); if (edac_debug_level >= 1) { @@ -1040,32 +960,32 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) err = edac_create_dimm_object(mci, dimm, i); if (err) { edac_dbg(1, "failure: create dimm %d obj\n", i); - goto fail; + goto fail_unregister_dimm; } } #ifdef CONFIG_EDAC_LEGACY_SYSFS err = edac_create_csrow_objects(mci); if (err < 0) - goto fail; + goto fail_unregister_dimm; #endif -#ifdef CONFIG_EDAC_DEBUG - edac_create_debug_nodes(mci); -#endif + edac_create_debugfs_nodes(mci); return 0; -fail: +fail_unregister_dimm: for (i--; i >= 0; i--) { struct dimm_info *dimm = mci->dimms[i]; - if (dimm->nr_pages == 0) + if (!dimm->nr_pages) continue; + device_unregister(&dimm->dev); } -fail2: device_unregister(&mci->dev); - bus_unregister(&mci->bus); - kfree(mci->bus.name); +fail_unregister_bus: + bus_unregister(mci->bus); + kfree(name); + return err; } @@ -1079,7 +999,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) edac_dbg(0, "\n"); #ifdef CONFIG_EDAC_DEBUG - debugfs_remove(mci->debugfs); + edac_debugfs_remove_recursive(mci->debugfs); #endif #ifdef CONFIG_EDAC_LEGACY_SYSFS edac_delete_csrow_objects(mci); @@ -1096,10 +1016,12 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) void edac_unregister_sysfs(struct mem_ctl_info *mci) { + const char *name = mci->bus->name; + edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev)); device_unregister(&mci->dev); - bus_unregister(&mci->bus); - kfree(mci->bus.name); + bus_unregister(mci->bus); + kfree(name); } static void mc_attr_release(struct device *dev) @@ -1159,7 +1081,7 @@ int __init edac_mc_sysfs_init(void) return err; } -void __exit edac_mc_sysfs_exit(void) +void edac_mc_sysfs_exit(void) { device_unregister(mci_pdev); edac_put_sysfs_subsys();