/* * edac_device_register_sysfs_main_kobj * * perform the high level setup for the new edac_device instance * * Return: 0 SUCCESS * !0 FAILURE */ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev) { struct bus_type *edac_subsys; int err; edac_dbg(1, "\n"); /* get the /sys/devices/system/edac reference */ edac_subsys = edac_get_sysfs_subsys(); if (edac_subsys == NULL) { edac_dbg(1, "no edac_subsys error\n"); err = -ENODEV; goto err_out; } /* Point to the 'edac_subsys' this instance 'reports' to */ edac_dev->edac_subsys = edac_subsys; /* Init the devices's kobject */ memset(&edac_dev->kobj, 0, sizeof(struct kobject)); /* Record which module 'owns' this control structure * and bump the ref count of the module */ edac_dev->owner = THIS_MODULE; if (!try_module_get(edac_dev->owner)) { err = -ENODEV; goto err_mod_get; } /* register */ err = kobject_init_and_add(&edac_dev->kobj, &ktype_device_ctrl, &edac_subsys->dev_root->kobj, "%s", edac_dev->name); if (err) { edac_dbg(1, "Failed to register '.../edac/%s'\n", edac_dev->name); goto err_kobj_reg; } kobject_uevent(&edac_dev->kobj, KOBJ_ADD); /* At this point, to 'free' the control struct, * edac_device_unregister_sysfs_main_kobj() must be used */ edac_dbg(4, "Registered '.../edac/%s' kobject\n", edac_dev->name); return 0; /* Error exit stack */ err_kobj_reg: module_put(edac_dev->owner); err_mod_get: edac_put_sysfs_subsys(); err_out: return err; }
/* * Init/exit code for the module. Basically, creates/removes /sys/class/rc */ int __init edac_mc_sysfs_init(void) { struct bus_type *edac_subsys; int err; /* get the /sys/devices/system/edac subsys reference */ edac_subsys = edac_get_sysfs_subsys(); if (edac_subsys == NULL) { edac_dbg(1, "no edac_subsys\n"); return -EINVAL; } mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL); mci_pdev->bus = edac_subsys; mci_pdev->type = &mc_attr_type; device_initialize(mci_pdev); dev_set_name(mci_pdev, "mc"); err = device_add(mci_pdev); if (err < 0) return err; edac_dbg(0, "device %s created\n", dev_name(mci_pdev)); return 0; }
/* * edac_device_create_block */ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev, struct edac_device_instance *instance, struct edac_device_block *block) { int i; int err; struct edac_dev_sysfs_block_attribute *sysfs_attrib; struct kobject *main_kobj; edac_dbg(4, "Instance '%s' inst_p=%p block '%s' block_p=%p\n", instance->name, instance, block->name, block); edac_dbg(4, "block kobj=%p block kobj->parent=%p\n", &block->kobj, &block->kobj.parent); /* init this block's kobject */ memset(&block->kobj, 0, sizeof(struct kobject)); /* bump the main kobject's reference count for this controller * and this instance is dependent on the main */ main_kobj = kobject_get(&edac_dev->kobj); if (!main_kobj) { err = -ENODEV; goto err_out; } /* Add this block's kobject */ err = kobject_init_and_add(&block->kobj, &ktype_block_ctrl, &instance->kobj, "%s", block->name); if (err) { edac_dbg(1, "Failed to register instance '%s'\n", block->name); kobject_put(main_kobj); err = -ENODEV; goto err_out; } /* If there are driver level block attributes, then added them * to the block kobject */ sysfs_attrib = block->block_attributes; if (sysfs_attrib && block->nr_attribs) { for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) { edac_dbg(4, "creating block attrib='%s' attrib->%p to kobj=%p\n", sysfs_attrib->attr.name, sysfs_attrib, &block->kobj); /* Create each block_attribute file */ err = sysfs_create_file(&block->kobj, &sysfs_attrib->attr); if (err) goto err_on_attrib; } } kobject_uevent(&block->kobj, KOBJ_ADD); return 0; /* Error un
/* * * edac_pci_create_sysfs * * Create the controls/attributes for the specified EDAC PCI device */ int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci) { int err; struct kobject *edac_kobj = &pci->kobj; edac_dbg(0, "idx=%d\n", pci->pci_idx); /* create the top main EDAC PCI kobject, IF needed */ err = edac_pci_main_kobj_setup(); if (err) return err; /* Create this instance's kobject under the MAIN kobject */ err = edac_pci_create_instance_kobj(pci, pci->pci_idx); if (err) goto unregister_cleanup; err = sysfs_create_link(edac_kobj, &pci->dev->kobj, EDAC_PCI_SYMLINK); if (err) { edac_dbg(0, "sysfs_create_link() returned err= %d\n", err); goto symlink_fail; } return 0; /* Error unwind stack */ symlink_fail: edac_pci_unregister_sysfs_instance_kobj(pci); unregister_cleanup: edac_pci_main_kobj_teardown(); return err; }
/* * edac_pci_create_instance_kobj * * construct one EDAC PCI instance's kobject for use */ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx) { struct kobject *main_kobj; int err; edac_dbg(0, "\n"); /* First bump the ref count on the top main kobj, which will * track the number of PCI instances we have, and thus nest * properly on keeping the module loaded */ main_kobj = kobject_get(edac_pci_top_main_kobj); if (!main_kobj) { err = -ENODEV; goto error_out; } /* And now register this new kobject under the main kobj */ err = kobject_init_and_add(&pci->kobj, &ktype_pci_instance, edac_pci_top_main_kobj, "pci%d", idx); if (err != 0) { edac_dbg(2, "failed to register instance pci%d\n", idx); kobject_put(edac_pci_top_main_kobj); goto error_out; } kobject_uevent(&pci->kobj, KOBJ_ADD); edac_dbg(1, "Register instance 'pci%d' kobject\n", idx); return 0; /* Error unwind statck */ error_out: return err; }
/* * edac_pci_main_kobj_teardown() * * if no longer linked (needed) remove the top level EDAC PCI * kobject with its controls and attributes */ static void edac_pci_main_kobj_teardown(void) { edac_dbg(0, "\n"); /* Decrement the count and only if no more controller instances * are connected perform the unregisteration of the top level * main kobj */ if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) { edac_dbg(0, "called kobject_put on main kobj\n"); kobject_put(edac_pci_top_main_kobj); } }
static int how_many_channels(struct pci_dev *pdev) { unsigned char capid0_8b; /* 8th byte of CAPID0 */ pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b); if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */ edac_dbg(0, "In single channel mode\n"); return 1; } else { edac_dbg(0, "In dual channel mode\n"); return 2; } }
/* * edac_device_create_sysfs() Constructor * * accept a created edac_device control structure * and 'export' it to sysfs. The 'main' kobj should already have been * created. 'instance' and 'block' kobjects should be registered * along with any 'block' attributes from the low driver. In addition, * the main attributes (if any) are connected to the main kobject of * the control structure. * * Return: * 0 Success * !0 Failure */ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev) { int err; struct kobject *edac_kobj = &edac_dev->kobj; edac_dbg(0, "idx=%d\n", edac_dev->dev_idx); /* go create any main attributes callers wants */ err = edac_device_add_main_sysfs_attributes(edac_dev); if (err) { edac_dbg(0, "failed to add sysfs attribs\n"); goto err_out; } /* create a symlink from the edac device * to the platform 'device' being used for this */ err = sysfs_create_link(edac_kobj, &edac_dev->dev->kobj, EDAC_DEVICE_SYMLINK); if (err) { edac_dbg(0, "sysfs_create_link() returned err= %d\n", err); goto err_remove_main_attribs; } /* Create the first level instance directories * In turn, the nested blocks beneath the instances will * be registered as well */ err = edac_device_create_instances(edac_dev); if (err) { edac_dbg(0, "edac_device_create_instances() returned err= %d\n", err); goto err_remove_link; } edac_dbg(4, "create-instances done, idx=%d\n", edac_dev->dev_idx); return 0; /* Error unwind stack */ err_remove_link: /* remove the sym link */ sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK); err_remove_main_attribs: edac_device_remove_main_sysfs_attributes(edac_dev); err_out: return err; }
/* * edac_device_unregister_sysfs_main_kobj: * the '..../edac/<name>' kobject */ void edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev) { edac_dbg(0, "\n"); edac_dbg(4, "name of kobject is: %s\n", kobject_name(&dev->kobj)); /* * Unregister the edac device's kobject and * allow for reference count to reach 0 at which point * the callback will be called to: * a) module_put() this module * b) 'kfree' the memory */ kobject_put(&dev->kobj); }
/* * Init/exit code for the module. Basically, creates/removes /sys/class/rc */ int __init edac_mc_sysfs_init(void) { int err; mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL); if (!mci_pdev) { err = -ENOMEM; goto out; } mci_pdev->bus = edac_get_sysfs_subsys(); mci_pdev->type = &mc_attr_type; device_initialize(mci_pdev); dev_set_name(mci_pdev, "mc"); err = device_add(mci_pdev); if (err < 0) goto out_dev_free; edac_dbg(0, "device %s created\n", dev_name(mci_pdev)); return 0; out_dev_free: kfree(mci_pdev); out: return err; }
/* * edac_pci_do_parity_check * * performs the actual PCI parity check operation */ void edac_pci_do_parity_check(void) { int before_count; edac_dbg(3, "\n"); /* if policy has PCI check off, leave now */ if (!check_pci_errors) return; before_count = atomic_read(&pci_parity_count); /* scan all PCI devices looking for a Parity Error on devices and * bridges. * The iterator calls pci_get_device() which might sleep, thus * we cannot disable interrupts in this scan. */ edac_pci_dev_parity_iterator(edac_pci_dev_parity_test); /* Only if operator has selected panic on PCI Error */ if (edac_pci_get_panic_on_pe()) { /* If the count is different 'after' from 'before' */ if (before_count != atomic_read(&pci_parity_count)) panic("EDAC: PCI Parity Error"); } }
static void mci_attr_release(struct device *dev) { struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev)); kfree(mci); }
static void dimm_attr_release(struct device *dev) { struct dimm_info *dimm = container_of(dev, struct dimm_info, dev); edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev)); kfree(dimm); }
static void i5100_init_csrows(struct mem_ctl_info *mci) { int i; struct i5100_priv *priv = mci->pvt_info; for (i = 0; i < mci->tot_dimms; i++) { struct dimm_info *dimm; const unsigned long npages = i5100_npages(mci, i); const unsigned chan = i5100_csrow_to_chan(mci, i); const unsigned rank = i5100_csrow_to_rank(mci, i); if (!npages) continue; dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers, chan, rank, 0); dimm->nr_pages = npages; dimm->grain = 32; dimm->dtype = (priv->mtr[chan][rank].width == 4) ? DEV_X4 : DEV_X8; dimm->mtype = MEM_RDDR2; dimm->edac_mode = EDAC_SECDED; snprintf(dimm->label, sizeof(dimm->label), "DIMM%u", i5100_rank_to_slot(mci, chan, rank)); edac_dbg(2, "dimm channel %d, rank %d, size %ld\n", chan, rank, (long)PAGES_TO_MiB(npages)); } }
/* Create a DIMM object under specifed memory controller device */ static int edac_create_dimm_object(struct mem_ctl_info *mci, struct dimm_info *dimm, int index) { int err; dimm->mci = mci; dimm->dev.type = &dimm_attr_type; dimm->dev.bus = mci->bus; device_initialize(&dimm->dev); dimm->dev.parent = &mci->dev; if (mci->csbased) dev_set_name(&dimm->dev, "rank%d", index); else dev_set_name(&dimm->dev, "dimm%d", index); dev_set_drvdata(&dimm->dev, dimm); pm_runtime_forbid(&mci->dev); err = device_add(&dimm->dev); edac_dbg(0, "creating rank/dimm device %s\n", dev_name(&dimm->dev)); return err; }
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); }
void edac_unregister_sysfs(struct mem_ctl_info *mci) { edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev)); device_unregister(&mci->dev); bus_unregister(mci->bus); kfree(mci->bus->name); }
/* 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; }
/* * edac_pci_remove_sysfs * * remove the controls and attributes for this EDAC PCI device */ void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci) { edac_dbg(0, "index=%d\n", pci->pci_idx); /* Remove the symlink */ sysfs_remove_link(&pci->kobj, EDAC_PCI_SYMLINK); /* remove this PCI instance's sysfs entries */ edac_pci_unregister_sysfs_instance_kobj(pci); /* Call the main unregister function, which will determine * if this 'pci' is the last instance. * If it is, the main kobject will be unregistered as a result */ edac_dbg(0, "calling edac_pci_main_kobj_teardown()\n"); edac_pci_main_kobj_teardown(); }
/* * edac_exit() * module exit/termination function */ static void __exit edac_exit(void) { edac_dbg(0, "\n"); /* tear down the various subsystems */ edac_workqueue_teardown(); edac_mc_sysfs_exit(); edac_debugfs_exit(); }
static void mc_attr_release(struct device *dev) { /* * There's no container structure here, as this is just the mci * parent device, used to create the /sys/devices/mc sysfs node. * So, there are no attributes on it. */ edac_dbg(1, "Releasing device %s\n", dev_name(dev)); kfree(dev); }
void edac_unregister_sysfs(struct mem_ctl_info *mci) { struct bus_type *bus = mci->bus; const char *name = mci->bus->name; edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev)); device_unregister(&mci->dev); bus_unregister(bus); kfree(name); }
/* * edac_pci_unregister_sysfs_instance_kobj * * unregister the kobj for the EDAC PCI instance */ static void edac_pci_unregister_sysfs_instance_kobj( struct edac_pci_ctl_info *pci) { edac_dbg(0, "\n"); /* Unregister the instance kobject and allow its release * function release the main reference count and then * kfree the memory */ kobject_put(&pci->kobj); }
/* * edac_pci_release_main_kobj * * This release function is called when the reference count to the * passed kobj goes to zero. * * This kobj is the 'main' kobject that EDAC PCI instances * link to, and thus provide for proper nesting counts */ static void edac_pci_release_main_kobj(struct kobject *kobj) { edac_dbg(0, "here to module_put(THIS_MODULE)\n"); kfree(kobj); /* last reference to top EDAC PCI kobject has been removed, * NOW release our ref count on the core module */ module_put(THIS_MODULE); }
/* 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) { int err, chan; if (csrow->nr_channels >= EDAC_NR_CHANNELS) return -ENODEV; csrow->dev.type = &csrow_attr_type; csrow->dev.bus = mci->bus; 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)); 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; }
/* * remove a Memory Controller instance */ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) { int i; edac_dbg(0, "\n"); #ifdef CONFIG_EDAC_DEBUG debugfs_remove(mci->debugfs); #endif #ifdef CONFIG_EDAC_LEGACY_SYSFS edac_delete_csrow_objects(mci); #endif for (i = 0; i < mci->tot_dimms; i++) { struct dimm_info *dimm = mci->dimms[i]; if (dimm->nr_pages == 0) continue; edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev)); device_unregister(&dimm->dev); } }
/* DEVICE instance kobject release() function */ static void edac_device_ctrl_instance_release(struct kobject *kobj) { struct edac_device_instance *instance; edac_dbg(1, "\n"); /* map from this kobj to the main control struct * and then dec the main kobj count */ instance = to_instance(kobj); kobject_put(&instance->ctl->kobj); }
/* * edac_device_remove_sysfs() destructor * * given an edac_device struct, tear down the kobject resources */ void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev) { edac_dbg(0, "\n"); /* remove any main attributes for this device */ edac_device_remove_main_sysfs_attributes(edac_dev); /* remove the device sym link */ sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK); /* walk the instance/block kobject tree, deconstructing it */ edac_device_delete_instances(edac_dev); }
static int how_many_channels(struct pci_dev *pdev) { int n_channels; unsigned char capid0_8b; /* 8th byte of CAPID0 */ pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b); if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */ edac_dbg(0, "In single channel mode\n"); n_channels = 1; } else { edac_dbg(0, "In dual channel mode\n"); n_channels = 2; } if (capid0_8b & 0x10) /* check if both channels are filled */ edac_dbg(0, "2 DIMMS per channel disabled\n"); else edac_dbg(0, "2 DIMMS per channel enabled\n"); return n_channels; }
/* DEVICE block kobject release() function */ static void edac_device_ctrl_block_release(struct kobject *kobj) { struct edac_device_block *block; edac_dbg(1, "\n"); /* get the container of the kobj */ block = to_block(kobj); /* map from 'block kobj' to 'block->instance->controller->main_kobj' * now 'release' the block kobject */ kobject_put(&block->instance->ctl->kobj); }