cregit-Linux how code gets into the kernel

Release 4.11 drivers/edac/edac_mc_sysfs.c

Directory: drivers/edac
/*
 * edac_mc kernel module
 * (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 <norsk5@xmission.com> www.softwarebitmaker.com
 *
 * (c) 2012-2013 - Mauro Carvalho Chehab
 *      The entire API were re-written, and ported to use struct device
 *
 */

#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/edac.h>
#include <linux/bug.h>
#include <linux/pm_runtime.h>
#include <linux/uaccess.h>

#include "edac_mc.h"
#include "edac_module.h"

/* MC EDAC Controls, setable by module parameter, and sysfs */

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_mc_get_log_ue(void) { return edac_mc_log_ue; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Jiang550.00%266.67%
Doug Thompson550.00%133.33%
Total10100.00%3100.00%


int edac_mc_get_log_ce(void) { return edac_mc_log_ce; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson550.00%133.33%
Dave Jiang550.00%266.67%
Total10100.00%3100.00%


int edac_mc_get_panic_on_ue(void) { return edac_mc_panic_on_ue; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson550.00%133.33%
Dave Jiang550.00%266.67%
Total10100.00%3100.00%

/* this is temporary */
int edac_mc_get_poll_msec(void) { return edac_mc_poll_msec; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Jiang880.00%266.67%
Doug Thompson220.00%133.33%
Total10100.00%3100.00%


static int edac_set_poll_msec(const char *val, struct kernel_param *kp) { unsigned long l; int ret; if (!val) return -EINVAL; ret = kstrtoul(val, 0, &l); if (ret) return ret; if (l < 1000) return -EINVAL; *((unsigned long *)kp->arg) = l; /* notify edac_mc engine to reset the poll period */ edac_mc_reset_delay_period(l); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Arthur Jones7285.71%133.33%
Jingoo Han67.14%133.33%
Borislav Petkov67.14%133.33%
Total84100.00%3100.00%

/* Parameter declarations for above */ 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_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, &edac_mc_poll_msec, 0644); MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); static struct device *mci_pdev; /* * various constants for Memory Controllers */ static const char * const mem_types[] = { [MEM_EMPTY] = "Empty", [MEM_RESERVED] = "Reserved", [MEM_UNKNOWN] = "Unknown", [MEM_FPM] = "FPM", [MEM_EDO] = "EDO", [MEM_BEDO] = "BEDO", [MEM_SDR] = "Unbuffered-SDR", [MEM_RDR] = "Registered-SDR", [MEM_DDR] = "Unbuffered-DDR", [MEM_RDDR] = "Registered-DDR", [MEM_RMBS] = "RMBS", [MEM_DDR2] = "Unbuffered-DDR2", [MEM_FB_DDR2] = "FullyBuffered-DDR2", [MEM_RDDR2] = "Registered-DDR2", [MEM_XDR] = "XDR", [MEM_DDR3] = "Unbuffered-DDR3", [MEM_RDDR3] = "Registered-DDR3", [MEM_DDR4] = "Unbuffered-DDR4", [MEM_RDDR4] = "Registered-DDR4" }; static const char * const dev_types[] = { [DEV_UNKNOWN] = "Unknown", [DEV_X1] = "x1", [DEV_X2] = "x2", [DEV_X4] = "x4", [DEV_X8] = "x8", [DEV_X16] = "x16", [DEV_X32] = "x32", [DEV_X64] = "x64" }; static const char * const edac_caps[] = { [EDAC_UNKNOWN] = "Unknown", [EDAC_NONE] = "None", [EDAC_RESERVED] = "Reserved", [EDAC_PARITY] = "PARITY", [EDAC_EC] = "EC", [EDAC_SECDED] = "SECDED", [EDAC_S2ECD2ED] = "S2ECD2ED", [EDAC_S4ECD4ED] = "S4ECD4ED", [EDAC_S8ECD8ED] = "S8ECD8ED", [EDAC_S16ECD16ED] = "S16ECD16ED" }; #ifdef CONFIG_EDAC_LEGACY_SYSFS /* * EDAC sysfs CSROW data structures and methods */ #define to_csrow(k) container_of(k, struct csrow_info, dev) /* * We need it to avoid namespace conflicts between the legacy API * and the per-dimm/per-rank one */ #define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \ static struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store) struct dev_ch_attribute { struct device_attribute attr; int channel; }; #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \ 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) /* Set of more default csrow<id> attribute show/store functions */
static ssize_t csrow_ue_count_show(struct device *dev, struct device_attribute *mattr, char *data) { struct csrow_info *csrow = to_csrow(dev); return sprintf(data, "%u\n", csrow->ue_count); }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson2559.52%266.67%
Mauro Carvalho Chehab1740.48%133.33%
Total42100.00%3100.00%


static ssize_t csrow_ce_count_show(struct device *dev, struct device_attribute *mattr, char *data) { struct csrow_info *csrow = to_csrow(dev); return sprintf(data, "%u\n", csrow->ce_count); }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson2559.52%266.67%
Mauro Carvalho Chehab1740.48%133.33%
Total42100.00%3100.00%


static ssize_t csrow_size_show(struct device *dev, struct device_attribute *mattr, char *data) { struct csrow_info *csrow = to_csrow(dev); int i; u32 nr_pages = 0; for (i = 0; i < csrow->nr_channels; i++) nr_pages += csrow->channels[i]->dimm->nr_pages; return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages)); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab5367.09%360.00%
Doug Thompson2632.91%240.00%
Total79100.00%5100.00%


static ssize_t csrow_mem_type_show(struct device *dev, struct device_attribute *mattr, char *data) { struct csrow_info *csrow = to_csrow(dev); return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]); }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson2853.85%240.00%
Mauro Carvalho Chehab2446.15%360.00%
Total52100.00%5100.00%


static ssize_t csrow_dev_type_show(struct device *dev, struct device_attribute *mattr, char *data) { struct csrow_info *csrow = to_csrow(dev); return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]); }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson2853.85%240.00%
Mauro Carvalho Chehab2446.15%360.00%
Total52100.00%5100.00%


static ssize_t csrow_edac_mode_show(struct device *dev, struct device_attribute *mattr, char *data) { struct csrow_info *csrow = to_csrow(dev); return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]); }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson2853.85%240.00%
Mauro Carvalho Chehab2446.15%360.00%
Total52100.00%5100.00%

/* show/store functions for DIMM Label attributes */
static ssize_t channel_dimm_label_show(struct device *dev, struct device_attribute *mattr, char *data) { struct csrow_info *csrow = to_csrow(dev); unsigned chan = to_channel(mattr); struct rank_info *rank = csrow->channels[chan]; /* if field has not been initialized, there is nothing to send */ if (!rank->dimm->label[0]) return 0; return snprintf(data, sizeof(rank->dimm->label) + 1, "%s\n", rank->dimm->label); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab4347.25%233.33%
Doug Thompson2426.37%233.33%
Arthur Jones1415.38%116.67%
Toshi Kani1010.99%116.67%
Total91100.00%6100.00%


static ssize_t channel_dimm_label_store(struct device *dev, struct device_attribute *mattr, const char *data, size_t count) { 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; if (data[count - 1] == '\0' || data[count - 1] == '\n') copy_count -= 1; 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 count; }

Contributors

PersonTokensPropCommitsCommitProp
Toshi Kani5740.43%233.33%
Mauro Carvalho Chehab4431.21%233.33%
Doug Thompson4028.37%233.33%
Total141100.00%6100.00%

/* show function for dynamic chX_ce_count attribute */
static ssize_t channel_ce_count_show(struct device *dev, struct device_attribute *mattr, char *data) { struct csrow_info *csrow = to_csrow(dev); unsigned chan = to_channel(mattr); struct rank_info *rank = csrow->channels[chan]; return sprintf(data, "%u\n", rank->ce_count); }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson3962.90%375.00%
Mauro Carvalho Chehab2337.10%125.00%
Total62100.00%4100.00%

/* cwrow<id>/attribute files */ DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL); DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL); DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL); DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL); DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL); DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL); /* default attributes of the CSROW<id> object */ static struct attribute *csrow_attrs[] = { &dev_attr_legacy_dev_type.attr, &dev_attr_legacy_mem_type.attr, &dev_attr_legacy_edac_mode.attr, &dev_attr_legacy_size_mb.attr, &dev_attr_legacy_ue_count.attr, &dev_attr_legacy_ce_count.attr, NULL, }; static struct attribute_group csrow_attr_grp = { .attrs = csrow_attrs, }; static const struct attribute_group *csrow_attr_groups[] = { &csrow_attr_grp, NULL };
static void csrow_attr_release(struct device *dev) { struct csrow_info *csrow = container_of(dev, struct csrow_info, dev); edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev)); kfree(csrow); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab4093.02%266.67%
Joe Perches36.98%133.33%
Total43100.00%3100.00%

static struct device_type csrow_attr_type = { .groups = csrow_attr_groups, .release = csrow_attr_release, }; /* * possible dynamic channel DIMM Label attribute files * */ 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, channel_dimm_label_show, channel_dimm_label_store, 1); DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 2); DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 3); 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 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 */ DEVICE_CHANNEL(ch0_ce_count, S_IRUGO, channel_ce_count_show, NULL, 0); DEVICE_CHANNEL(ch1_ce_count, S_IRUGO, channel_ce_count_show, NULL, 1); DEVICE_CHANNEL(ch2_ce_count, S_IRUGO, channel_ce_count_show, NULL, 2); DEVICE_CHANNEL(ch3_ce_count, S_IRUGO, channel_ce_count_show, NULL, 3); 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 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; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai7875.73%150.00%
Borislav Petkov2524.27%150.00%
Total103100.00%2100.00%

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) { int chan, nr_pages = 0; for (chan = 0; chan < csrow->nr_channels; chan++) nr_pages += csrow->channels[chan]->dimm->nr_pages; return nr_pages; }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab50100.00%2100.00%
Total50100.00%2100.00%

/* Create a CSROW object under specifed edac_mc_device */
static int edac_create_csrow_object(struct mem_ctl_info *mci, struct csrow_info *csrow, int index) { csrow->dev.type = &csrow_attr_type; csrow->dev.bus = mci->bus; csrow->dev.groups = csrow_dev_groups; device_initialize(&csrow->dev); csrow->dev.parent = &mci->dev; csrow->mci = mci; dev_set_name(&csrow->dev, "csrow%d", index); dev_set_drvdata(&csrow->dev, csrow); edac_dbg(0, "creating (virtual) csrow node %s\n", dev_name(&csrow->dev)); return device_add(&csrow->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab8471.79%228.57%
Doug Thompson1512.82%228.57%
Takashi Iwai97.69%114.29%
Borislav Petkov65.13%114.29%
Joe Perches32.56%114.29%
Total117100.00%7100.00%

/* Create a CSROW object under specifed edac_mc_device */
static int edac_create_csrow_objects(struct mem_ctl_info *mci) { int err, i; struct csrow_info *csrow; for (i = 0; i < mci->nr_csrows; i++) { csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; err = edac_create_csrow_object(mci, mci->csrows[i], i); if (err < 0) { edac_dbg(1, "failure: create csrow objects for csrow %d\n", i); goto error; } } return 0; error: for (--i; i >= 0; i--) { csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; put_device(&mci->csrows[i]->dev); } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab9867.59%450.00%
Doug Thompson3222.07%225.00%
Takashi Iwai106.90%112.50%
Greg Kroah-Hartman53.45%112.50%
Total145100.00%8100.00%


static void edac_delete_csrow_objects(struct mem_ctl_info *mci) { 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; device_unregister(&mci->csrows[i]->dev); } }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab6594.20%350.00%
Doug Thompson34.35%233.33%
Lans Zhang11.45%116.67%
Total69100.00%6100.00%

#endif /* * Per-dimm (or per-rank) devices */ #define to_dimm(k) container_of(k, struct dimm_info, dev) /* show/store functions for DIMM Label attributes */
static ssize_t dimmdev_location_show(struct device *dev, struct device_attribute *mattr, char *data) { struct dimm_info *dimm = to_dimm(dev); return edac_dimm_info_location(dimm, data, PAGE_SIZE); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab2767.50%360.00%
Doug Thompson1332.50%240.00%
Total40100.00%5100.00%


static ssize_t dimmdev_label_show(struct device *dev, struct device_attribute *mattr, char *data) { struct dimm_info *dimm = to_dimm(dev); /* if field has not been initialized, there is nothing to send */ if (!dimm->label[0]) return 0; return snprintf(data, sizeof(dimm->label) + 1, "%s\n", dimm->label); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab3350.77%233.33%
Doug Thompson1726.15%233.33%
Toshi Kani812.31%116.67%
Borislav Petkov710.77%116.67%
Total65100.00%6100.00%


static ssize_t dimmdev_label_store(struct device *dev, struct device_attribute *mattr, const char *data, size_t count) { struct dimm_info *dimm = to_dimm(dev); size_t copy_count = count; 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; strncpy(dimm->label, data, copy_count); dimm->label[copy_count] = '\0'; return count; }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab6052.17%133.33%
Toshi Kani5547.83%266.67%
Total115100.00%3100.00%


static ssize_t dimmdev_size_show(struct device *dev, struct device_attribute *mattr, char *data) { struct dimm_info *dimm = to_dimm(dev); return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages)); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab45100.00%1100.00%
Total45100.00%1100.00%


static ssize_t dimmdev_mem_type_show(struct device *dev, struct device_attribute *mattr, char *data) { struct dimm_info *dimm = to_dimm(dev); return sprintf(data, "%s\n", mem_types[dimm->mtype]); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab45100.00%1100.00%
Total45100.00%1100.00%


static ssize_t dimmdev_dev_type_show(struct device *dev, struct device_attribute *mattr, char *data) { struct dimm_info *dimm = to_dimm(dev); return sprintf(data, "%s\n", dev_types[dimm->dtype]); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab45100.00%1100.00%
Total45100.00%1100.00%


static ssize_t dimmdev_edac_mode_show(struct device *dev, struct device_attribute *mattr, char *data) { struct dimm_info *dimm = to_dimm(dev); return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]); }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab45100.00%1100.00%
Total45100.00%1100.00%


static ssize_t dimmdev_ce_count_show(struct device *dev, struct device_attribute *mattr, char *data) { struct dimm_info *dimm = to_dimm(dev); u32 count; int off; off = EDAC_DIMM_OFF(dimm->mci->layers, dimm->mci->n_layers, dimm->location[0], dimm->location[1], dimm->location[2]); count = dimm->mci->ce_per_layer[dimm->mci->n_layers-1][off]; return sprintf(data, "%u\n", count); }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Miller104100.00%1100.00%
Total104100.00%1100.00%


static ssize_t dimmdev_ue_count_show(struct device *dev, struct device_attribute *mattr, char *data) { struct dimm_info *dimm = to_dimm(dev); u32 count; int off; off = EDAC_DIMM_OFF(dimm->mci->layers, dimm->mci->n_layers, dimm->location[0], dimm->location[1], dimm->location[2]); count = dimm->mci->ue_per_layer[dimm->mci->n_layers-1][off]; return sprintf(data, "%u\n", count); }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Miller104100.00%1100.00%
Total104100.00%1100.00%

/* dimm/rank attribute files */ static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR, dimmdev_label_show, dimmdev_label_store); static DEVICE_ATTR(dimm_location, S_IRUGO