18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * edac_mc kernel module 38c2ecf20Sopenharmony_ci * (C) 2005, 2006 Linux Networx (http://lnxi.com) 48c2ecf20Sopenharmony_ci * This file may be distributed under the terms of the 58c2ecf20Sopenharmony_ci * GNU General Public License. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Written by Thayne Harbaugh 88c2ecf20Sopenharmony_ci * Based on work by Dan Hollis <goemon at anime dot net> and others. 98c2ecf20Sopenharmony_ci * http://www.anime.net/~goemon/linux-ecc/ 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Modified by Dave Peterson and Doug Thompson 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/types.h> 198c2ecf20Sopenharmony_ci#include <linux/smp.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 228c2ecf20Sopenharmony_ci#include <linux/highmem.h> 238c2ecf20Sopenharmony_ci#include <linux/timer.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 268c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 278c2ecf20Sopenharmony_ci#include <linux/list.h> 288c2ecf20Sopenharmony_ci#include <linux/ctype.h> 298c2ecf20Sopenharmony_ci#include <linux/edac.h> 308c2ecf20Sopenharmony_ci#include <linux/bitops.h> 318c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 328c2ecf20Sopenharmony_ci#include <asm/page.h> 338c2ecf20Sopenharmony_ci#include "edac_mc.h" 348c2ecf20Sopenharmony_ci#include "edac_module.h" 358c2ecf20Sopenharmony_ci#include <ras/ras_event.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ATOMIC_SCRUB 388c2ecf20Sopenharmony_ci#include <asm/edac.h> 398c2ecf20Sopenharmony_ci#else 408c2ecf20Sopenharmony_ci#define edac_atomic_scrub(va, size) do { } while (0) 418c2ecf20Sopenharmony_ci#endif 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciint edac_op_state = EDAC_OPSTATE_INVAL; 448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_op_state); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* lock to memory controller's control array */ 478c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(mem_ctls_mutex); 488c2ecf20Sopenharmony_cistatic LIST_HEAD(mc_devices); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * Used to lock EDAC MC to just one module, avoiding two drivers e. g. 528c2ecf20Sopenharmony_ci * apei/ghes and i7core_edac to be used at the same time. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic const char *edac_mc_owner; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic struct mem_ctl_info *error_desc_to_mci(struct edac_raw_error_desc *e) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return container_of(e, struct mem_ctl_info, error_desc); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ciunsigned int edac_dimm_info_location(struct dimm_info *dimm, char *buf, 628c2ecf20Sopenharmony_ci unsigned int len) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = dimm->mci; 658c2ecf20Sopenharmony_ci int i, n, count = 0; 668c2ecf20Sopenharmony_ci char *p = buf; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci for (i = 0; i < mci->n_layers; i++) { 698c2ecf20Sopenharmony_ci n = snprintf(p, len, "%s %d ", 708c2ecf20Sopenharmony_ci edac_layer_name[mci->layers[i].type], 718c2ecf20Sopenharmony_ci dimm->location[i]); 728c2ecf20Sopenharmony_ci p += n; 738c2ecf20Sopenharmony_ci len -= n; 748c2ecf20Sopenharmony_ci count += n; 758c2ecf20Sopenharmony_ci if (!len) 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return count; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void edac_mc_dump_channel(struct rank_info *chan) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci edac_dbg(4, " channel->chan_idx = %d\n", chan->chan_idx); 878c2ecf20Sopenharmony_ci edac_dbg(4, " channel = %p\n", chan); 888c2ecf20Sopenharmony_ci edac_dbg(4, " channel->csrow = %p\n", chan->csrow); 898c2ecf20Sopenharmony_ci edac_dbg(4, " channel->dimm = %p\n", chan->dimm); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void edac_mc_dump_dimm(struct dimm_info *dimm) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci char location[80]; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!dimm->nr_pages) 978c2ecf20Sopenharmony_ci return; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci edac_dimm_info_location(dimm, location, sizeof(location)); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci edac_dbg(4, "%s%i: %smapped as virtual row %d, chan %d\n", 1028c2ecf20Sopenharmony_ci dimm->mci->csbased ? "rank" : "dimm", 1038c2ecf20Sopenharmony_ci dimm->idx, location, dimm->csrow, dimm->cschannel); 1048c2ecf20Sopenharmony_ci edac_dbg(4, " dimm = %p\n", dimm); 1058c2ecf20Sopenharmony_ci edac_dbg(4, " dimm->label = '%s'\n", dimm->label); 1068c2ecf20Sopenharmony_ci edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages); 1078c2ecf20Sopenharmony_ci edac_dbg(4, " dimm->grain = %d\n", dimm->grain); 1088c2ecf20Sopenharmony_ci edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void edac_mc_dump_csrow(struct csrow_info *csrow) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci edac_dbg(4, "csrow->csrow_idx = %d\n", csrow->csrow_idx); 1148c2ecf20Sopenharmony_ci edac_dbg(4, " csrow = %p\n", csrow); 1158c2ecf20Sopenharmony_ci edac_dbg(4, " csrow->first_page = 0x%lx\n", csrow->first_page); 1168c2ecf20Sopenharmony_ci edac_dbg(4, " csrow->last_page = 0x%lx\n", csrow->last_page); 1178c2ecf20Sopenharmony_ci edac_dbg(4, " csrow->page_mask = 0x%lx\n", csrow->page_mask); 1188c2ecf20Sopenharmony_ci edac_dbg(4, " csrow->nr_channels = %d\n", csrow->nr_channels); 1198c2ecf20Sopenharmony_ci edac_dbg(4, " csrow->channels = %p\n", csrow->channels); 1208c2ecf20Sopenharmony_ci edac_dbg(4, " csrow->mci = %p\n", csrow->mci); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void edac_mc_dump_mci(struct mem_ctl_info *mci) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci edac_dbg(3, "\tmci = %p\n", mci); 1268c2ecf20Sopenharmony_ci edac_dbg(3, "\tmci->mtype_cap = %lx\n", mci->mtype_cap); 1278c2ecf20Sopenharmony_ci edac_dbg(3, "\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap); 1288c2ecf20Sopenharmony_ci edac_dbg(3, "\tmci->edac_cap = %lx\n", mci->edac_cap); 1298c2ecf20Sopenharmony_ci edac_dbg(4, "\tmci->edac_check = %p\n", mci->edac_check); 1308c2ecf20Sopenharmony_ci edac_dbg(3, "\tmci->nr_csrows = %d, csrows = %p\n", 1318c2ecf20Sopenharmony_ci mci->nr_csrows, mci->csrows); 1328c2ecf20Sopenharmony_ci edac_dbg(3, "\tmci->nr_dimms = %d, dimms = %p\n", 1338c2ecf20Sopenharmony_ci mci->tot_dimms, mci->dimms); 1348c2ecf20Sopenharmony_ci edac_dbg(3, "\tdev = %p\n", mci->pdev); 1358c2ecf20Sopenharmony_ci edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n", 1368c2ecf20Sopenharmony_ci mci->mod_name, mci->ctl_name); 1378c2ecf20Sopenharmony_ci edac_dbg(3, "\tpvt_info = %p\n\n", mci->pvt_info); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_DEBUG */ 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciconst char * const edac_mem_types[] = { 1438c2ecf20Sopenharmony_ci [MEM_EMPTY] = "Empty", 1448c2ecf20Sopenharmony_ci [MEM_RESERVED] = "Reserved", 1458c2ecf20Sopenharmony_ci [MEM_UNKNOWN] = "Unknown", 1468c2ecf20Sopenharmony_ci [MEM_FPM] = "FPM", 1478c2ecf20Sopenharmony_ci [MEM_EDO] = "EDO", 1488c2ecf20Sopenharmony_ci [MEM_BEDO] = "BEDO", 1498c2ecf20Sopenharmony_ci [MEM_SDR] = "Unbuffered-SDR", 1508c2ecf20Sopenharmony_ci [MEM_RDR] = "Registered-SDR", 1518c2ecf20Sopenharmony_ci [MEM_DDR] = "Unbuffered-DDR", 1528c2ecf20Sopenharmony_ci [MEM_RDDR] = "Registered-DDR", 1538c2ecf20Sopenharmony_ci [MEM_RMBS] = "RMBS", 1548c2ecf20Sopenharmony_ci [MEM_DDR2] = "Unbuffered-DDR2", 1558c2ecf20Sopenharmony_ci [MEM_FB_DDR2] = "FullyBuffered-DDR2", 1568c2ecf20Sopenharmony_ci [MEM_RDDR2] = "Registered-DDR2", 1578c2ecf20Sopenharmony_ci [MEM_XDR] = "XDR", 1588c2ecf20Sopenharmony_ci [MEM_DDR3] = "Unbuffered-DDR3", 1598c2ecf20Sopenharmony_ci [MEM_RDDR3] = "Registered-DDR3", 1608c2ecf20Sopenharmony_ci [MEM_LRDDR3] = "Load-Reduced-DDR3-RAM", 1618c2ecf20Sopenharmony_ci [MEM_DDR4] = "Unbuffered-DDR4", 1628c2ecf20Sopenharmony_ci [MEM_RDDR4] = "Registered-DDR4", 1638c2ecf20Sopenharmony_ci [MEM_LRDDR4] = "Load-Reduced-DDR4-RAM", 1648c2ecf20Sopenharmony_ci [MEM_NVDIMM] = "Non-volatile-RAM", 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_mem_types); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/** 1698c2ecf20Sopenharmony_ci * edac_align_ptr - Prepares the pointer offsets for a single-shot allocation 1708c2ecf20Sopenharmony_ci * @p: pointer to a pointer with the memory offset to be used. At 1718c2ecf20Sopenharmony_ci * return, this will be incremented to point to the next offset 1728c2ecf20Sopenharmony_ci * @size: Size of the data structure to be reserved 1738c2ecf20Sopenharmony_ci * @n_elems: Number of elements that should be reserved 1748c2ecf20Sopenharmony_ci * 1758c2ecf20Sopenharmony_ci * If 'size' is a constant, the compiler will optimize this whole function 1768c2ecf20Sopenharmony_ci * down to either a no-op or the addition of a constant to the value of '*p'. 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * The 'p' pointer is absolutely needed to keep the proper advancing 1798c2ecf20Sopenharmony_ci * further in memory to the proper offsets when allocating the struct along 1808c2ecf20Sopenharmony_ci * with its embedded structs, as edac_device_alloc_ctl_info() does it 1818c2ecf20Sopenharmony_ci * above, for example. 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * At return, the pointer 'p' will be incremented to be used on a next call 1848c2ecf20Sopenharmony_ci * to this function. 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_civoid *edac_align_ptr(void **p, unsigned int size, int n_elems) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci unsigned int align, r; 1898c2ecf20Sopenharmony_ci void *ptr = *p; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci *p += size * n_elems; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci * 'p' can possibly be an unaligned item X such that sizeof(X) is 1958c2ecf20Sopenharmony_ci * 'size'. Adjust 'p' so that its alignment is at least as 1968c2ecf20Sopenharmony_ci * stringent as what the compiler would provide for X and return 1978c2ecf20Sopenharmony_ci * the aligned result. 1988c2ecf20Sopenharmony_ci * Here we assume that the alignment of a "long long" is the most 1998c2ecf20Sopenharmony_ci * stringent alignment that the compiler will ever provide by default. 2008c2ecf20Sopenharmony_ci * As far as I know, this is a reasonable assumption. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci if (size > sizeof(long)) 2038c2ecf20Sopenharmony_ci align = sizeof(long long); 2048c2ecf20Sopenharmony_ci else if (size > sizeof(int)) 2058c2ecf20Sopenharmony_ci align = sizeof(long); 2068c2ecf20Sopenharmony_ci else if (size > sizeof(short)) 2078c2ecf20Sopenharmony_ci align = sizeof(int); 2088c2ecf20Sopenharmony_ci else if (size > sizeof(char)) 2098c2ecf20Sopenharmony_ci align = sizeof(short); 2108c2ecf20Sopenharmony_ci else 2118c2ecf20Sopenharmony_ci return (char *)ptr; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci r = (unsigned long)ptr % align; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (r == 0) 2168c2ecf20Sopenharmony_ci return (char *)ptr; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci *p += align - r; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return (void *)(((unsigned long)ptr) + align - r); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void _edac_mc_free(struct mem_ctl_info *mci) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci put_device(&mci->dev); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void mci_release(struct device *dev) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); 2318c2ecf20Sopenharmony_ci struct csrow_info *csr; 2328c2ecf20Sopenharmony_ci int i, chn, row; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (mci->dimms) { 2358c2ecf20Sopenharmony_ci for (i = 0; i < mci->tot_dimms; i++) 2368c2ecf20Sopenharmony_ci kfree(mci->dimms[i]); 2378c2ecf20Sopenharmony_ci kfree(mci->dimms); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (mci->csrows) { 2418c2ecf20Sopenharmony_ci for (row = 0; row < mci->nr_csrows; row++) { 2428c2ecf20Sopenharmony_ci csr = mci->csrows[row]; 2438c2ecf20Sopenharmony_ci if (!csr) 2448c2ecf20Sopenharmony_ci continue; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (csr->channels) { 2478c2ecf20Sopenharmony_ci for (chn = 0; chn < mci->num_cschannel; chn++) 2488c2ecf20Sopenharmony_ci kfree(csr->channels[chn]); 2498c2ecf20Sopenharmony_ci kfree(csr->channels); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci kfree(csr); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci kfree(mci->csrows); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci kfree(mci); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int edac_mc_alloc_csrows(struct mem_ctl_info *mci) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci unsigned int tot_channels = mci->num_cschannel; 2618c2ecf20Sopenharmony_ci unsigned int tot_csrows = mci->nr_csrows; 2628c2ecf20Sopenharmony_ci unsigned int row, chn; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* 2658c2ecf20Sopenharmony_ci * Alocate and fill the csrow/channels structs 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci mci->csrows = kcalloc(tot_csrows, sizeof(*mci->csrows), GFP_KERNEL); 2688c2ecf20Sopenharmony_ci if (!mci->csrows) 2698c2ecf20Sopenharmony_ci return -ENOMEM; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci for (row = 0; row < tot_csrows; row++) { 2728c2ecf20Sopenharmony_ci struct csrow_info *csr; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL); 2758c2ecf20Sopenharmony_ci if (!csr) 2768c2ecf20Sopenharmony_ci return -ENOMEM; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci mci->csrows[row] = csr; 2798c2ecf20Sopenharmony_ci csr->csrow_idx = row; 2808c2ecf20Sopenharmony_ci csr->mci = mci; 2818c2ecf20Sopenharmony_ci csr->nr_channels = tot_channels; 2828c2ecf20Sopenharmony_ci csr->channels = kcalloc(tot_channels, sizeof(*csr->channels), 2838c2ecf20Sopenharmony_ci GFP_KERNEL); 2848c2ecf20Sopenharmony_ci if (!csr->channels) 2858c2ecf20Sopenharmony_ci return -ENOMEM; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci for (chn = 0; chn < tot_channels; chn++) { 2888c2ecf20Sopenharmony_ci struct rank_info *chan; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL); 2918c2ecf20Sopenharmony_ci if (!chan) 2928c2ecf20Sopenharmony_ci return -ENOMEM; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci csr->channels[chn] = chan; 2958c2ecf20Sopenharmony_ci chan->chan_idx = chn; 2968c2ecf20Sopenharmony_ci chan->csrow = csr; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int edac_mc_alloc_dimms(struct mem_ctl_info *mci) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci unsigned int pos[EDAC_MAX_LAYERS]; 3068c2ecf20Sopenharmony_ci unsigned int row, chn, idx; 3078c2ecf20Sopenharmony_ci int layer; 3088c2ecf20Sopenharmony_ci void *p; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* 3118c2ecf20Sopenharmony_ci * Allocate and fill the dimm structs 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci mci->dimms = kcalloc(mci->tot_dimms, sizeof(*mci->dimms), GFP_KERNEL); 3148c2ecf20Sopenharmony_ci if (!mci->dimms) 3158c2ecf20Sopenharmony_ci return -ENOMEM; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci memset(&pos, 0, sizeof(pos)); 3188c2ecf20Sopenharmony_ci row = 0; 3198c2ecf20Sopenharmony_ci chn = 0; 3208c2ecf20Sopenharmony_ci for (idx = 0; idx < mci->tot_dimms; idx++) { 3218c2ecf20Sopenharmony_ci struct dimm_info *dimm; 3228c2ecf20Sopenharmony_ci struct rank_info *chan; 3238c2ecf20Sopenharmony_ci int n, len; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci chan = mci->csrows[row]->channels[chn]; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL); 3288c2ecf20Sopenharmony_ci if (!dimm) 3298c2ecf20Sopenharmony_ci return -ENOMEM; 3308c2ecf20Sopenharmony_ci mci->dimms[idx] = dimm; 3318c2ecf20Sopenharmony_ci dimm->mci = mci; 3328c2ecf20Sopenharmony_ci dimm->idx = idx; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* 3358c2ecf20Sopenharmony_ci * Copy DIMM location and initialize it. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci len = sizeof(dimm->label); 3388c2ecf20Sopenharmony_ci p = dimm->label; 3398c2ecf20Sopenharmony_ci n = snprintf(p, len, "mc#%u", mci->mc_idx); 3408c2ecf20Sopenharmony_ci p += n; 3418c2ecf20Sopenharmony_ci len -= n; 3428c2ecf20Sopenharmony_ci for (layer = 0; layer < mci->n_layers; layer++) { 3438c2ecf20Sopenharmony_ci n = snprintf(p, len, "%s#%u", 3448c2ecf20Sopenharmony_ci edac_layer_name[mci->layers[layer].type], 3458c2ecf20Sopenharmony_ci pos[layer]); 3468c2ecf20Sopenharmony_ci p += n; 3478c2ecf20Sopenharmony_ci len -= n; 3488c2ecf20Sopenharmony_ci dimm->location[layer] = pos[layer]; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (len <= 0) 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Link it to the csrows old API data */ 3558c2ecf20Sopenharmony_ci chan->dimm = dimm; 3568c2ecf20Sopenharmony_ci dimm->csrow = row; 3578c2ecf20Sopenharmony_ci dimm->cschannel = chn; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* Increment csrow location */ 3608c2ecf20Sopenharmony_ci if (mci->layers[0].is_virt_csrow) { 3618c2ecf20Sopenharmony_ci chn++; 3628c2ecf20Sopenharmony_ci if (chn == mci->num_cschannel) { 3638c2ecf20Sopenharmony_ci chn = 0; 3648c2ecf20Sopenharmony_ci row++; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci } else { 3678c2ecf20Sopenharmony_ci row++; 3688c2ecf20Sopenharmony_ci if (row == mci->nr_csrows) { 3698c2ecf20Sopenharmony_ci row = 0; 3708c2ecf20Sopenharmony_ci chn++; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* Increment dimm location */ 3758c2ecf20Sopenharmony_ci for (layer = mci->n_layers - 1; layer >= 0; layer--) { 3768c2ecf20Sopenharmony_ci pos[layer]++; 3778c2ecf20Sopenharmony_ci if (pos[layer] < mci->layers[layer].size) 3788c2ecf20Sopenharmony_ci break; 3798c2ecf20Sopenharmony_ci pos[layer] = 0; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistruct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, 3878c2ecf20Sopenharmony_ci unsigned int n_layers, 3888c2ecf20Sopenharmony_ci struct edac_mc_layer *layers, 3898c2ecf20Sopenharmony_ci unsigned int sz_pvt) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 3928c2ecf20Sopenharmony_ci struct edac_mc_layer *layer; 3938c2ecf20Sopenharmony_ci unsigned int idx, size, tot_dimms = 1; 3948c2ecf20Sopenharmony_ci unsigned int tot_csrows = 1, tot_channels = 1; 3958c2ecf20Sopenharmony_ci void *pvt, *ptr = NULL; 3968c2ecf20Sopenharmony_ci bool per_rank = false; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (WARN_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0)) 3998c2ecf20Sopenharmony_ci return NULL; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * Calculate the total amount of dimms and csrows/cschannels while 4038c2ecf20Sopenharmony_ci * in the old API emulation mode 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci for (idx = 0; idx < n_layers; idx++) { 4068c2ecf20Sopenharmony_ci tot_dimms *= layers[idx].size; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (layers[idx].is_virt_csrow) 4098c2ecf20Sopenharmony_ci tot_csrows *= layers[idx].size; 4108c2ecf20Sopenharmony_ci else 4118c2ecf20Sopenharmony_ci tot_channels *= layers[idx].size; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (layers[idx].type == EDAC_MC_LAYER_CHIP_SELECT) 4148c2ecf20Sopenharmony_ci per_rank = true; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* Figure out the offsets of the various items from the start of an mc 4188c2ecf20Sopenharmony_ci * structure. We want the alignment of each item to be at least as 4198c2ecf20Sopenharmony_ci * stringent as what the compiler would provide if we could simply 4208c2ecf20Sopenharmony_ci * hardcode everything into a single struct. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci mci = edac_align_ptr(&ptr, sizeof(*mci), 1); 4238c2ecf20Sopenharmony_ci layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); 4248c2ecf20Sopenharmony_ci pvt = edac_align_ptr(&ptr, sz_pvt, 1); 4258c2ecf20Sopenharmony_ci size = ((unsigned long)pvt) + sz_pvt; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n", 4288c2ecf20Sopenharmony_ci size, 4298c2ecf20Sopenharmony_ci tot_dimms, 4308c2ecf20Sopenharmony_ci per_rank ? "ranks" : "dimms", 4318c2ecf20Sopenharmony_ci tot_csrows * tot_channels); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci mci = kzalloc(size, GFP_KERNEL); 4348c2ecf20Sopenharmony_ci if (mci == NULL) 4358c2ecf20Sopenharmony_ci return NULL; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci mci->dev.release = mci_release; 4388c2ecf20Sopenharmony_ci device_initialize(&mci->dev); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* Adjust pointers so they point within the memory we just allocated 4418c2ecf20Sopenharmony_ci * rather than an imaginary chunk of memory located at address 0. 4428c2ecf20Sopenharmony_ci */ 4438c2ecf20Sopenharmony_ci layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer)); 4448c2ecf20Sopenharmony_ci pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* setup index and various internal pointers */ 4478c2ecf20Sopenharmony_ci mci->mc_idx = mc_num; 4488c2ecf20Sopenharmony_ci mci->tot_dimms = tot_dimms; 4498c2ecf20Sopenharmony_ci mci->pvt_info = pvt; 4508c2ecf20Sopenharmony_ci mci->n_layers = n_layers; 4518c2ecf20Sopenharmony_ci mci->layers = layer; 4528c2ecf20Sopenharmony_ci memcpy(mci->layers, layers, sizeof(*layer) * n_layers); 4538c2ecf20Sopenharmony_ci mci->nr_csrows = tot_csrows; 4548c2ecf20Sopenharmony_ci mci->num_cschannel = tot_channels; 4558c2ecf20Sopenharmony_ci mci->csbased = per_rank; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (edac_mc_alloc_csrows(mci)) 4588c2ecf20Sopenharmony_ci goto error; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (edac_mc_alloc_dimms(mci)) 4618c2ecf20Sopenharmony_ci goto error; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci mci->op_state = OP_ALLOC; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return mci; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cierror: 4688c2ecf20Sopenharmony_ci _edac_mc_free(mci); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return NULL; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_mc_alloc); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_civoid edac_mc_free(struct mem_ctl_info *mci) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci edac_dbg(1, "\n"); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci _edac_mc_free(mci); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_mc_free); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cibool edac_has_mcs(void) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci bool ret; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci mutex_lock(&mem_ctls_mutex); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = list_empty(&mc_devices); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return !ret; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_has_mcs); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/* Caller must hold mem_ctls_mutex */ 4978c2ecf20Sopenharmony_cistatic struct mem_ctl_info *__find_mci_by_dev(struct device *dev) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 5008c2ecf20Sopenharmony_ci struct list_head *item; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci edac_dbg(3, "\n"); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci list_for_each(item, &mc_devices) { 5058c2ecf20Sopenharmony_ci mci = list_entry(item, struct mem_ctl_info, link); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (mci->pdev == dev) 5088c2ecf20Sopenharmony_ci return mci; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return NULL; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci/** 5158c2ecf20Sopenharmony_ci * find_mci_by_dev 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * scan list of controllers looking for the one that manages 5188c2ecf20Sopenharmony_ci * the 'dev' device 5198c2ecf20Sopenharmony_ci * @dev: pointer to a struct device related with the MCI 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_cistruct mem_ctl_info *find_mci_by_dev(struct device *dev) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct mem_ctl_info *ret; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci mutex_lock(&mem_ctls_mutex); 5268c2ecf20Sopenharmony_ci ret = __find_mci_by_dev(dev); 5278c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return ret; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(find_mci_by_dev); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* 5348c2ecf20Sopenharmony_ci * edac_mc_workq_function 5358c2ecf20Sopenharmony_ci * performs the operation scheduled by a workq request 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_cistatic void edac_mc_workq_function(struct work_struct *work_req) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct delayed_work *d_work = to_delayed_work(work_req); 5408c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci mutex_lock(&mem_ctls_mutex); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (mci->op_state != OP_RUNNING_POLL) { 5458c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 5468c2ecf20Sopenharmony_ci return; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (edac_op_state == EDAC_OPSTATE_POLL) 5508c2ecf20Sopenharmony_ci mci->edac_check(mci); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Queue ourselves again. */ 5558c2ecf20Sopenharmony_ci edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec())); 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci/* 5598c2ecf20Sopenharmony_ci * edac_mc_reset_delay_period(unsigned long value) 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci * user space has updated our poll period value, need to 5628c2ecf20Sopenharmony_ci * reset our workq delays 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_civoid edac_mc_reset_delay_period(unsigned long value) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 5678c2ecf20Sopenharmony_ci struct list_head *item; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci mutex_lock(&mem_ctls_mutex); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci list_for_each(item, &mc_devices) { 5728c2ecf20Sopenharmony_ci mci = list_entry(item, struct mem_ctl_info, link); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (mci->op_state == OP_RUNNING_POLL) 5758c2ecf20Sopenharmony_ci edac_mod_work(&mci->work, value); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci/* Return 0 on success, 1 on failure. 5838c2ecf20Sopenharmony_ci * Before calling this function, caller must 5848c2ecf20Sopenharmony_ci * assign a unique value to mci->mc_idx. 5858c2ecf20Sopenharmony_ci * 5868c2ecf20Sopenharmony_ci * locking model: 5878c2ecf20Sopenharmony_ci * 5888c2ecf20Sopenharmony_ci * called with the mem_ctls_mutex lock held 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_cistatic int add_mc_to_global_list(struct mem_ctl_info *mci) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct list_head *item, *insert_before; 5938c2ecf20Sopenharmony_ci struct mem_ctl_info *p; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci insert_before = &mc_devices; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci p = __find_mci_by_dev(mci->pdev); 5988c2ecf20Sopenharmony_ci if (unlikely(p != NULL)) 5998c2ecf20Sopenharmony_ci goto fail0; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci list_for_each(item, &mc_devices) { 6028c2ecf20Sopenharmony_ci p = list_entry(item, struct mem_ctl_info, link); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (p->mc_idx >= mci->mc_idx) { 6058c2ecf20Sopenharmony_ci if (unlikely(p->mc_idx == mci->mc_idx)) 6068c2ecf20Sopenharmony_ci goto fail1; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci insert_before = item; 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci list_add_tail_rcu(&mci->link, insert_before); 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cifail0: 6178c2ecf20Sopenharmony_ci edac_printk(KERN_WARNING, EDAC_MC, 6188c2ecf20Sopenharmony_ci "%s (%s) %s %s already assigned %d\n", dev_name(p->pdev), 6198c2ecf20Sopenharmony_ci edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx); 6208c2ecf20Sopenharmony_ci return 1; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cifail1: 6238c2ecf20Sopenharmony_ci edac_printk(KERN_WARNING, EDAC_MC, 6248c2ecf20Sopenharmony_ci "bug in low-level driver: attempt to assign\n" 6258c2ecf20Sopenharmony_ci " duplicate mc_idx %d in %s()\n", p->mc_idx, __func__); 6268c2ecf20Sopenharmony_ci return 1; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic int del_mc_from_global_list(struct mem_ctl_info *mci) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci list_del_rcu(&mci->link); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* these are for safe removal of devices from global list while 6348c2ecf20Sopenharmony_ci * NMI handlers may be traversing list 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci synchronize_rcu(); 6378c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mci->link); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return list_empty(&mc_devices); 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistruct mem_ctl_info *edac_mc_find(int idx) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 6458c2ecf20Sopenharmony_ci struct list_head *item; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci mutex_lock(&mem_ctls_mutex); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci list_for_each(item, &mc_devices) { 6508c2ecf20Sopenharmony_ci mci = list_entry(item, struct mem_ctl_info, link); 6518c2ecf20Sopenharmony_ci if (mci->mc_idx == idx) 6528c2ecf20Sopenharmony_ci goto unlock; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci mci = NULL; 6568c2ecf20Sopenharmony_ciunlock: 6578c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 6588c2ecf20Sopenharmony_ci return mci; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(edac_mc_find); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ciconst char *edac_get_owner(void) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci return edac_mc_owner; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_get_owner); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci/* FIXME - should a warning be printed if no error detection? correction? */ 6698c2ecf20Sopenharmony_ciint edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, 6708c2ecf20Sopenharmony_ci const struct attribute_group **groups) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci int ret = -EINVAL; 6738c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 6768c2ecf20Sopenharmony_ci if (edac_debug_level >= 3) 6778c2ecf20Sopenharmony_ci edac_mc_dump_mci(mci); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (edac_debug_level >= 4) { 6808c2ecf20Sopenharmony_ci struct dimm_info *dimm; 6818c2ecf20Sopenharmony_ci int i; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci for (i = 0; i < mci->nr_csrows; i++) { 6848c2ecf20Sopenharmony_ci struct csrow_info *csrow = mci->csrows[i]; 6858c2ecf20Sopenharmony_ci u32 nr_pages = 0; 6868c2ecf20Sopenharmony_ci int j; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci for (j = 0; j < csrow->nr_channels; j++) 6898c2ecf20Sopenharmony_ci nr_pages += csrow->channels[j]->dimm->nr_pages; 6908c2ecf20Sopenharmony_ci if (!nr_pages) 6918c2ecf20Sopenharmony_ci continue; 6928c2ecf20Sopenharmony_ci edac_mc_dump_csrow(csrow); 6938c2ecf20Sopenharmony_ci for (j = 0; j < csrow->nr_channels; j++) 6948c2ecf20Sopenharmony_ci if (csrow->channels[j]->dimm->nr_pages) 6958c2ecf20Sopenharmony_ci edac_mc_dump_channel(csrow->channels[j]); 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci mci_for_each_dimm(mci, dimm) 6998c2ecf20Sopenharmony_ci edac_mc_dump_dimm(dimm); 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci#endif 7028c2ecf20Sopenharmony_ci mutex_lock(&mem_ctls_mutex); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (edac_mc_owner && edac_mc_owner != mci->mod_name) { 7058c2ecf20Sopenharmony_ci ret = -EPERM; 7068c2ecf20Sopenharmony_ci goto fail0; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (add_mc_to_global_list(mci)) 7108c2ecf20Sopenharmony_ci goto fail0; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* set load time so that error rate can be tracked */ 7138c2ecf20Sopenharmony_ci mci->start_time = jiffies; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci mci->bus = edac_get_sysfs_subsys(); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (edac_create_sysfs_mci_device(mci, groups)) { 7188c2ecf20Sopenharmony_ci edac_mc_printk(mci, KERN_WARNING, 7198c2ecf20Sopenharmony_ci "failed to create sysfs device\n"); 7208c2ecf20Sopenharmony_ci goto fail1; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (mci->edac_check) { 7248c2ecf20Sopenharmony_ci mci->op_state = OP_RUNNING_POLL; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function); 7278c2ecf20Sopenharmony_ci edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec())); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci } else { 7308c2ecf20Sopenharmony_ci mci->op_state = OP_RUNNING_INTERRUPT; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* Report action taken */ 7348c2ecf20Sopenharmony_ci edac_mc_printk(mci, KERN_INFO, 7358c2ecf20Sopenharmony_ci "Giving out device to module %s controller %s: DEV %s (%s)\n", 7368c2ecf20Sopenharmony_ci mci->mod_name, mci->ctl_name, mci->dev_name, 7378c2ecf20Sopenharmony_ci edac_op_state_to_string(mci->op_state)); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci edac_mc_owner = mci->mod_name; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 7428c2ecf20Sopenharmony_ci return 0; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cifail1: 7458c2ecf20Sopenharmony_ci del_mc_from_global_list(mci); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cifail0: 7488c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 7498c2ecf20Sopenharmony_ci return ret; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_mc_add_mc_with_groups); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistruct mem_ctl_info *edac_mc_del_mc(struct device *dev) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci mutex_lock(&mem_ctls_mutex); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* find the requested mci struct in the global list */ 7628c2ecf20Sopenharmony_ci mci = __find_mci_by_dev(dev); 7638c2ecf20Sopenharmony_ci if (mci == NULL) { 7648c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 7658c2ecf20Sopenharmony_ci return NULL; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci /* mark MCI offline: */ 7698c2ecf20Sopenharmony_ci mci->op_state = OP_OFFLINE; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (del_mc_from_global_list(mci)) 7728c2ecf20Sopenharmony_ci edac_mc_owner = NULL; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci mutex_unlock(&mem_ctls_mutex); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (mci->edac_check) 7778c2ecf20Sopenharmony_ci edac_stop_work(&mci->work); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* remove from sysfs */ 7808c2ecf20Sopenharmony_ci edac_remove_sysfs_mci_device(mci); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci edac_printk(KERN_INFO, EDAC_MC, 7838c2ecf20Sopenharmony_ci "Removed device %d for %s %s: DEV %s\n", mci->mc_idx, 7848c2ecf20Sopenharmony_ci mci->mod_name, mci->ctl_name, edac_dev_name(mci)); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return mci; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_mc_del_mc); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic void edac_mc_scrub_block(unsigned long page, unsigned long offset, 7918c2ecf20Sopenharmony_ci u32 size) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct page *pg; 7948c2ecf20Sopenharmony_ci void *virt_addr; 7958c2ecf20Sopenharmony_ci unsigned long flags = 0; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci edac_dbg(3, "\n"); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* ECC error page was not in our memory. Ignore it. */ 8008c2ecf20Sopenharmony_ci if (!pfn_valid(page)) 8018c2ecf20Sopenharmony_ci return; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci /* Find the actual page structure then map it and fix */ 8048c2ecf20Sopenharmony_ci pg = pfn_to_page(page); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (PageHighMem(pg)) 8078c2ecf20Sopenharmony_ci local_irq_save(flags); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci virt_addr = kmap_atomic(pg); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* Perform architecture specific atomic scrub operation */ 8128c2ecf20Sopenharmony_ci edac_atomic_scrub(virt_addr + offset, size); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* Unmap and complete */ 8158c2ecf20Sopenharmony_ci kunmap_atomic(virt_addr); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (PageHighMem(pg)) 8188c2ecf20Sopenharmony_ci local_irq_restore(flags); 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci/* FIXME - should return -1 */ 8228c2ecf20Sopenharmony_ciint edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct csrow_info **csrows = mci->csrows; 8258c2ecf20Sopenharmony_ci int row, i, j, n; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci edac_dbg(1, "MC%d: 0x%lx\n", mci->mc_idx, page); 8288c2ecf20Sopenharmony_ci row = -1; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci for (i = 0; i < mci->nr_csrows; i++) { 8318c2ecf20Sopenharmony_ci struct csrow_info *csrow = csrows[i]; 8328c2ecf20Sopenharmony_ci n = 0; 8338c2ecf20Sopenharmony_ci for (j = 0; j < csrow->nr_channels; j++) { 8348c2ecf20Sopenharmony_ci struct dimm_info *dimm = csrow->channels[j]->dimm; 8358c2ecf20Sopenharmony_ci n += dimm->nr_pages; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci if (n == 0) 8388c2ecf20Sopenharmony_ci continue; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci edac_dbg(3, "MC%d: first(0x%lx) page(0x%lx) last(0x%lx) mask(0x%lx)\n", 8418c2ecf20Sopenharmony_ci mci->mc_idx, 8428c2ecf20Sopenharmony_ci csrow->first_page, page, csrow->last_page, 8438c2ecf20Sopenharmony_ci csrow->page_mask); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if ((page >= csrow->first_page) && 8468c2ecf20Sopenharmony_ci (page <= csrow->last_page) && 8478c2ecf20Sopenharmony_ci ((page & csrow->page_mask) == 8488c2ecf20Sopenharmony_ci (csrow->first_page & csrow->page_mask))) { 8498c2ecf20Sopenharmony_ci row = i; 8508c2ecf20Sopenharmony_ci break; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (row == -1) 8558c2ecf20Sopenharmony_ci edac_mc_printk(mci, KERN_ERR, 8568c2ecf20Sopenharmony_ci "could not look up page error address %lx\n", 8578c2ecf20Sopenharmony_ci (unsigned long)page); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci return row; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ciconst char *edac_layer_name[] = { 8648c2ecf20Sopenharmony_ci [EDAC_MC_LAYER_BRANCH] = "branch", 8658c2ecf20Sopenharmony_ci [EDAC_MC_LAYER_CHANNEL] = "channel", 8668c2ecf20Sopenharmony_ci [EDAC_MC_LAYER_SLOT] = "slot", 8678c2ecf20Sopenharmony_ci [EDAC_MC_LAYER_CHIP_SELECT] = "csrow", 8688c2ecf20Sopenharmony_ci [EDAC_MC_LAYER_ALL_MEM] = "memory", 8698c2ecf20Sopenharmony_ci}; 8708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_layer_name); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic void edac_inc_ce_error(struct edac_raw_error_desc *e) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; 8758c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = error_desc_to_mci(e); 8768c2ecf20Sopenharmony_ci struct dimm_info *dimm = edac_get_dimm(mci, pos[0], pos[1], pos[2]); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci mci->ce_mc += e->error_count; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (dimm) 8818c2ecf20Sopenharmony_ci dimm->ce_count += e->error_count; 8828c2ecf20Sopenharmony_ci else 8838c2ecf20Sopenharmony_ci mci->ce_noinfo_count += e->error_count; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic void edac_inc_ue_error(struct edac_raw_error_desc *e) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; 8898c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = error_desc_to_mci(e); 8908c2ecf20Sopenharmony_ci struct dimm_info *dimm = edac_get_dimm(mci, pos[0], pos[1], pos[2]); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci mci->ue_mc += e->error_count; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (dimm) 8958c2ecf20Sopenharmony_ci dimm->ue_count += e->error_count; 8968c2ecf20Sopenharmony_ci else 8978c2ecf20Sopenharmony_ci mci->ue_noinfo_count += e->error_count; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic void edac_ce_error(struct edac_raw_error_desc *e) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = error_desc_to_mci(e); 9038c2ecf20Sopenharmony_ci unsigned long remapped_page; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci if (edac_mc_get_log_ce()) { 9068c2ecf20Sopenharmony_ci edac_mc_printk(mci, KERN_WARNING, 9078c2ecf20Sopenharmony_ci "%d CE %s%son %s (%s page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx%s%s)\n", 9088c2ecf20Sopenharmony_ci e->error_count, e->msg, 9098c2ecf20Sopenharmony_ci *e->msg ? " " : "", 9108c2ecf20Sopenharmony_ci e->label, e->location, e->page_frame_number, e->offset_in_page, 9118c2ecf20Sopenharmony_ci e->grain, e->syndrome, 9128c2ecf20Sopenharmony_ci *e->other_detail ? " - " : "", 9138c2ecf20Sopenharmony_ci e->other_detail); 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci edac_inc_ce_error(e); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (mci->scrub_mode == SCRUB_SW_SRC) { 9198c2ecf20Sopenharmony_ci /* 9208c2ecf20Sopenharmony_ci * Some memory controllers (called MCs below) can remap 9218c2ecf20Sopenharmony_ci * memory so that it is still available at a different 9228c2ecf20Sopenharmony_ci * address when PCI devices map into memory. 9238c2ecf20Sopenharmony_ci * MC's that can't do this, lose the memory where PCI 9248c2ecf20Sopenharmony_ci * devices are mapped. This mapping is MC-dependent 9258c2ecf20Sopenharmony_ci * and so we call back into the MC driver for it to 9268c2ecf20Sopenharmony_ci * map the MC page to a physical (CPU) page which can 9278c2ecf20Sopenharmony_ci * then be mapped to a virtual page - which can then 9288c2ecf20Sopenharmony_ci * be scrubbed. 9298c2ecf20Sopenharmony_ci */ 9308c2ecf20Sopenharmony_ci remapped_page = mci->ctl_page_to_phys ? 9318c2ecf20Sopenharmony_ci mci->ctl_page_to_phys(mci, e->page_frame_number) : 9328c2ecf20Sopenharmony_ci e->page_frame_number; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci edac_mc_scrub_block(remapped_page, e->offset_in_page, e->grain); 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic void edac_ue_error(struct edac_raw_error_desc *e) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = error_desc_to_mci(e); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (edac_mc_get_log_ue()) { 9438c2ecf20Sopenharmony_ci edac_mc_printk(mci, KERN_WARNING, 9448c2ecf20Sopenharmony_ci "%d UE %s%son %s (%s page:0x%lx offset:0x%lx grain:%ld%s%s)\n", 9458c2ecf20Sopenharmony_ci e->error_count, e->msg, 9468c2ecf20Sopenharmony_ci *e->msg ? " " : "", 9478c2ecf20Sopenharmony_ci e->label, e->location, e->page_frame_number, e->offset_in_page, 9488c2ecf20Sopenharmony_ci e->grain, 9498c2ecf20Sopenharmony_ci *e->other_detail ? " - " : "", 9508c2ecf20Sopenharmony_ci e->other_detail); 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci edac_inc_ue_error(e); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if (edac_mc_get_panic_on_ue()) { 9568c2ecf20Sopenharmony_ci panic("UE %s%son %s (%s page:0x%lx offset:0x%lx grain:%ld%s%s)\n", 9578c2ecf20Sopenharmony_ci e->msg, 9588c2ecf20Sopenharmony_ci *e->msg ? " " : "", 9598c2ecf20Sopenharmony_ci e->label, e->location, e->page_frame_number, e->offset_in_page, 9608c2ecf20Sopenharmony_ci e->grain, 9618c2ecf20Sopenharmony_ci *e->other_detail ? " - " : "", 9628c2ecf20Sopenharmony_ci e->other_detail); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic void edac_inc_csrow(struct edac_raw_error_desc *e, int row, int chan) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = error_desc_to_mci(e); 9698c2ecf20Sopenharmony_ci enum hw_event_mc_err_type type = e->type; 9708c2ecf20Sopenharmony_ci u16 count = e->error_count; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (row < 0) 9738c2ecf20Sopenharmony_ci return; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (type == HW_EVENT_ERR_CORRECTED) { 9788c2ecf20Sopenharmony_ci mci->csrows[row]->ce_count += count; 9798c2ecf20Sopenharmony_ci if (chan >= 0) 9808c2ecf20Sopenharmony_ci mci->csrows[row]->channels[chan]->ce_count += count; 9818c2ecf20Sopenharmony_ci } else { 9828c2ecf20Sopenharmony_ci mci->csrows[row]->ue_count += count; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_civoid edac_raw_mc_handle_error(struct edac_raw_error_desc *e) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = error_desc_to_mci(e); 9898c2ecf20Sopenharmony_ci u8 grain_bits; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci /* Sanity-check driver-supplied grain value. */ 9928c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!e->grain)) 9938c2ecf20Sopenharmony_ci e->grain = 1; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci grain_bits = fls_long(e->grain - 1); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* Report the error via the trace interface */ 9988c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_RAS)) 9998c2ecf20Sopenharmony_ci trace_mc_event(e->type, e->msg, e->label, e->error_count, 10008c2ecf20Sopenharmony_ci mci->mc_idx, e->top_layer, e->mid_layer, 10018c2ecf20Sopenharmony_ci e->low_layer, 10028c2ecf20Sopenharmony_ci (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page, 10038c2ecf20Sopenharmony_ci grain_bits, e->syndrome, e->other_detail); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (e->type == HW_EVENT_ERR_CORRECTED) 10068c2ecf20Sopenharmony_ci edac_ce_error(e); 10078c2ecf20Sopenharmony_ci else 10088c2ecf20Sopenharmony_ci edac_ue_error(e); 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_raw_mc_handle_error); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_civoid edac_mc_handle_error(const enum hw_event_mc_err_type type, 10138c2ecf20Sopenharmony_ci struct mem_ctl_info *mci, 10148c2ecf20Sopenharmony_ci const u16 error_count, 10158c2ecf20Sopenharmony_ci const unsigned long page_frame_number, 10168c2ecf20Sopenharmony_ci const unsigned long offset_in_page, 10178c2ecf20Sopenharmony_ci const unsigned long syndrome, 10188c2ecf20Sopenharmony_ci const int top_layer, 10198c2ecf20Sopenharmony_ci const int mid_layer, 10208c2ecf20Sopenharmony_ci const int low_layer, 10218c2ecf20Sopenharmony_ci const char *msg, 10228c2ecf20Sopenharmony_ci const char *other_detail) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct dimm_info *dimm; 10258c2ecf20Sopenharmony_ci char *p; 10268c2ecf20Sopenharmony_ci int row = -1, chan = -1; 10278c2ecf20Sopenharmony_ci int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer }; 10288c2ecf20Sopenharmony_ci int i, n_labels = 0; 10298c2ecf20Sopenharmony_ci struct edac_raw_error_desc *e = &mci->error_desc; 10308c2ecf20Sopenharmony_ci bool any_memory = true; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci edac_dbg(3, "MC%d\n", mci->mc_idx); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci /* Fills the error report buffer */ 10358c2ecf20Sopenharmony_ci memset(e, 0, sizeof (*e)); 10368c2ecf20Sopenharmony_ci e->error_count = error_count; 10378c2ecf20Sopenharmony_ci e->type = type; 10388c2ecf20Sopenharmony_ci e->top_layer = top_layer; 10398c2ecf20Sopenharmony_ci e->mid_layer = mid_layer; 10408c2ecf20Sopenharmony_ci e->low_layer = low_layer; 10418c2ecf20Sopenharmony_ci e->page_frame_number = page_frame_number; 10428c2ecf20Sopenharmony_ci e->offset_in_page = offset_in_page; 10438c2ecf20Sopenharmony_ci e->syndrome = syndrome; 10448c2ecf20Sopenharmony_ci /* need valid strings here for both: */ 10458c2ecf20Sopenharmony_ci e->msg = msg ?: ""; 10468c2ecf20Sopenharmony_ci e->other_detail = other_detail ?: ""; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci /* 10498c2ecf20Sopenharmony_ci * Check if the event report is consistent and if the memory location is 10508c2ecf20Sopenharmony_ci * known. If it is, the DIMM(s) label info will be filled and the DIMM's 10518c2ecf20Sopenharmony_ci * error counters will be incremented. 10528c2ecf20Sopenharmony_ci */ 10538c2ecf20Sopenharmony_ci for (i = 0; i < mci->n_layers; i++) { 10548c2ecf20Sopenharmony_ci if (pos[i] >= (int)mci->layers[i].size) { 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci edac_mc_printk(mci, KERN_ERR, 10578c2ecf20Sopenharmony_ci "INTERNAL ERROR: %s value is out of range (%d >= %d)\n", 10588c2ecf20Sopenharmony_ci edac_layer_name[mci->layers[i].type], 10598c2ecf20Sopenharmony_ci pos[i], mci->layers[i].size); 10608c2ecf20Sopenharmony_ci /* 10618c2ecf20Sopenharmony_ci * Instead of just returning it, let's use what's 10628c2ecf20Sopenharmony_ci * known about the error. The increment routines and 10638c2ecf20Sopenharmony_ci * the DIMM filter logic will do the right thing by 10648c2ecf20Sopenharmony_ci * pointing the likely damaged DIMMs. 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_ci pos[i] = -1; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci if (pos[i] >= 0) 10698c2ecf20Sopenharmony_ci any_memory = false; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* 10738c2ecf20Sopenharmony_ci * Get the dimm label/grain that applies to the match criteria. 10748c2ecf20Sopenharmony_ci * As the error algorithm may not be able to point to just one memory 10758c2ecf20Sopenharmony_ci * stick, the logic here will get all possible labels that could 10768c2ecf20Sopenharmony_ci * pottentially be affected by the error. 10778c2ecf20Sopenharmony_ci * On FB-DIMM memory controllers, for uncorrected errors, it is common 10788c2ecf20Sopenharmony_ci * to have only the MC channel and the MC dimm (also called "branch") 10798c2ecf20Sopenharmony_ci * but the channel is not known, as the memory is arranged in pairs, 10808c2ecf20Sopenharmony_ci * where each memory belongs to a separate channel within the same 10818c2ecf20Sopenharmony_ci * branch. 10828c2ecf20Sopenharmony_ci */ 10838c2ecf20Sopenharmony_ci p = e->label; 10848c2ecf20Sopenharmony_ci *p = '\0'; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci mci_for_each_dimm(mci, dimm) { 10878c2ecf20Sopenharmony_ci if (top_layer >= 0 && top_layer != dimm->location[0]) 10888c2ecf20Sopenharmony_ci continue; 10898c2ecf20Sopenharmony_ci if (mid_layer >= 0 && mid_layer != dimm->location[1]) 10908c2ecf20Sopenharmony_ci continue; 10918c2ecf20Sopenharmony_ci if (low_layer >= 0 && low_layer != dimm->location[2]) 10928c2ecf20Sopenharmony_ci continue; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci /* get the max grain, over the error match range */ 10958c2ecf20Sopenharmony_ci if (dimm->grain > e->grain) 10968c2ecf20Sopenharmony_ci e->grain = dimm->grain; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* 10998c2ecf20Sopenharmony_ci * If the error is memory-controller wide, there's no need to 11008c2ecf20Sopenharmony_ci * seek for the affected DIMMs because the whole channel/memory 11018c2ecf20Sopenharmony_ci * controller/... may be affected. Also, don't show errors for 11028c2ecf20Sopenharmony_ci * empty DIMM slots. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_ci if (!dimm->nr_pages) 11058c2ecf20Sopenharmony_ci continue; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci n_labels++; 11088c2ecf20Sopenharmony_ci if (n_labels > EDAC_MAX_LABELS) { 11098c2ecf20Sopenharmony_ci p = e->label; 11108c2ecf20Sopenharmony_ci *p = '\0'; 11118c2ecf20Sopenharmony_ci } else { 11128c2ecf20Sopenharmony_ci if (p != e->label) { 11138c2ecf20Sopenharmony_ci strcpy(p, OTHER_LABEL); 11148c2ecf20Sopenharmony_ci p += strlen(OTHER_LABEL); 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci strcpy(p, dimm->label); 11178c2ecf20Sopenharmony_ci p += strlen(p); 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* 11218c2ecf20Sopenharmony_ci * get csrow/channel of the DIMM, in order to allow 11228c2ecf20Sopenharmony_ci * incrementing the compat API counters 11238c2ecf20Sopenharmony_ci */ 11248c2ecf20Sopenharmony_ci edac_dbg(4, "%s csrows map: (%d,%d)\n", 11258c2ecf20Sopenharmony_ci mci->csbased ? "rank" : "dimm", 11268c2ecf20Sopenharmony_ci dimm->csrow, dimm->cschannel); 11278c2ecf20Sopenharmony_ci if (row == -1) 11288c2ecf20Sopenharmony_ci row = dimm->csrow; 11298c2ecf20Sopenharmony_ci else if (row >= 0 && row != dimm->csrow) 11308c2ecf20Sopenharmony_ci row = -2; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (chan == -1) 11338c2ecf20Sopenharmony_ci chan = dimm->cschannel; 11348c2ecf20Sopenharmony_ci else if (chan >= 0 && chan != dimm->cschannel) 11358c2ecf20Sopenharmony_ci chan = -2; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (any_memory) 11398c2ecf20Sopenharmony_ci strcpy(e->label, "any memory"); 11408c2ecf20Sopenharmony_ci else if (!*e->label) 11418c2ecf20Sopenharmony_ci strcpy(e->label, "unknown memory"); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci edac_inc_csrow(e, row, chan); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci /* Fill the RAM location data */ 11468c2ecf20Sopenharmony_ci p = e->location; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci for (i = 0; i < mci->n_layers; i++) { 11498c2ecf20Sopenharmony_ci if (pos[i] < 0) 11508c2ecf20Sopenharmony_ci continue; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci p += sprintf(p, "%s:%d ", 11538c2ecf20Sopenharmony_ci edac_layer_name[mci->layers[i].type], 11548c2ecf20Sopenharmony_ci pos[i]); 11558c2ecf20Sopenharmony_ci } 11568c2ecf20Sopenharmony_ci if (p > e->location) 11578c2ecf20Sopenharmony_ci *(p - 1) = '\0'; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci edac_raw_mc_handle_error(e); 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_mc_handle_error); 1162