// SPDX-License-Identifier: GPL-2.0 /* * * Shared code by both skx_edac and i10nm_edac. Originally split out * from the skx_edac driver. * * This file is linked into both skx_edac and i10nm_edac drivers. In * order to avoid link errors, this file must be like a pure library * without including symbols and defines which would otherwise conflict, * when linked once into a module and into a built-in object, at the * same time. For example, __this_module symbol references when that * file is being linked into a built-in object. * * Copyright (c) 2018, Intel Corporation.
*/
staticvoid skx_init_mc_mapping(struct skx_dev *d)
{ /* * By default, the BIOS presents all memory controllers within each * socket to the EDAC driver. The physical indices are the same as * the logical indices of the memory controllers enumerated by the * EDAC driver.
*/ for (int i = 0; i < NUM_IMC; i++)
d->mc_mapping[i] = i;
}
void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc)
{
edac_dbg(0, "Set the mapping of mc phy idx to logical idx: %02d -> %02d\n",
pmc, lmc);
static u8 skx_get_mc_mapping(struct skx_dev *d, u8 pmc)
{
edac_dbg(0, "Get the mapping of mc phy idx to logical idx: %02d -> %02d\n",
pmc, d->mc_mapping[pmc]);
return d->mc_mapping[pmc];
}
staticbool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src)
{ struct skx_dev *d; int i, len = 0;
if (res->addr >= skx_tohm || (res->addr >= skx_tolm &&
res->addr < BIT_ULL(32))) {
edac_dbg(0, "Address 0x%llx out of range\n", res->addr); returnfalse;
}
if (adxl_decode(res->addr, adxl_values)) {
edac_dbg(0, "Failed to decode 0x%llx\n", res->addr); returnfalse;
}
/* * GNR with a Flat2LM memory configuration may mistakenly classify * a near-memory error(DDR5) as a far-memory error(CXL), resulting * in the incorrect selection of decoded ADXL components. * To address this, prefetch the decoded far-memory controller ID * and adjust the error source to near-memory if the far-memory * controller ID is invalid.
*/ if (skx_res_cfg && skx_res_cfg->type == GNR && err_src == ERR_SRC_2LM_FM) {
res->imc = (int)adxl_values[component_indices[INDEX_MEMCTRL]]; if (res->imc == -1) {
err_src = ERR_SRC_2LM_NM;
edac_dbg(0, "Adjust the error source to near-memory.\n");
}
}
skx_printk(KERN_ERR, "Failed to get package ID from NUMA information\n"); return -ENODEV;
}
int skx_get_src_id(struct skx_dev *d, int off, u8 *id)
{
u32 reg;
/* * The 3-bit source IDs in PCI configuration space registers are limited * to 8 unique IDs, and each ID is local to a UPI/QPI domain. * * Source IDs cannot be used to map devices to sockets on UV systems * because they can exceed 8 sockets and have multiple UPI/QPI domains * with identical, repeating source IDs.
*/ if (is_uv_system()) return skx_get_pkg_id(d, id);
if (pci_read_config_dword(d->util_all, off, ®)) {
skx_printk(KERN_ERR, "Failed to read src id\n"); return -ENODEV;
}
staticint get_width(u32 mtr)
{ switch (GET_BITFIELD(mtr, 8, 9)) { case 0: return DEV_X4; case 1: return DEV_X8; case 2: return DEV_X16;
} return DEV_UNKNOWN;
}
/* * We use the per-socket device @cfg->did to count how many sockets are present, * and to detemine which PCI buses are associated with each socket. Allocate * and build the full list of all the skx_dev structures that we need here.
*/ int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list)
{ struct pci_dev *pdev, *prev; struct skx_dev *d;
u32 reg; int ndev = 0;
prev = NULL; for (;;) {
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, cfg->decs_did, prev); if (!pdev) break;
ndev++;
d = kzalloc(sizeof(*d), GFP_KERNEL); if (!d) {
pci_dev_put(pdev); return -ENOMEM;
}
if (pci_read_config_dword(pdev, cfg->busno_cfg_offset, ®)) {
kfree(d);
pci_dev_put(pdev);
skx_printk(KERN_ERR, "Failed to read bus idx\n"); return -ENODEV;
}
staticint skx_get_dimm_attr(u32 reg, int lobit, int hibit, int add, int minval, int maxval, constchar *name)
{
u32 val = GET_BITFIELD(reg, lobit, hibit);
if (val < minval || val > maxval) {
edac_dbg(2, "bad %s = %d (raw=0x%x)\n", name, val, reg); return -EINVAL;
} return val + add;
}
rc = get_dimm_config(mci, cfg); if (rc < 0) goto fail;
/* Record ptr to the generic device */
mci->pdev = &pdev->dev;
/* Add this new MC control structure to EDAC's list of MCs */ if (unlikely(edac_mc_add_mc(mci))) {
edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
rc = -EINVAL; goto fail;
}
void skx_remove(void)
{ int i, j; struct skx_dev *d, *tmp;
edac_dbg(0, "\n");
list_for_each_entry_safe(d, tmp, &dev_edac_list, list) {
list_del(&d->list); for (i = 0; i < NUM_IMC; i++) { if (d->imc[i].mci)
skx_unregister_mci(&d->imc[i]);
if (d->imc[i].mdev)
pci_dev_put(d->imc[i].mdev);
if (d->imc[i].mbase)
iounmap(d->imc[i].mbase);
for (j = 0; j < NUM_CHANNELS; j++) { if (d->imc[i].chan[j].cdev)
pci_dev_put(d->imc[i].chan[j].cdev);
}
} if (d->util_all)
pci_dev_put(d->util_all); if (d->pcu_cr3)
pci_dev_put(d->pcu_cr3); if (d->sad_all)
pci_dev_put(d->sad_all); if (d->uracu)
pci_dev_put(d->uracu);
kfree(d);
}
}
EXPORT_SYMBOL_GPL(skx_remove);
#ifdef CONFIG_EDAC_DEBUG /* * Debug feature. * Exercise the address decode logic by writing an address to * /sys/kernel/debug/edac/{skx,i10nm}_test/addr.
*/ staticstruct dentry *skx_test;
staticint debugfs_u64_set(void *data, u64 val)
{ struct mce m;
pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.