blob: 34eb9703ed33a52b5e83227c8b7ccf01f3a55034 [file] [log] [blame]
Alan Coxda9bb1d2006-01-18 17:44:13 -08001/*
2 * edac_mc kernel module
Doug Thompson49c0dab72006-07-10 04:45:19 -07003 * (C) 2005, 2006 Linux Networx (http://lnxi.com)
Alan Coxda9bb1d2006-01-18 17:44:13 -08004 * This file may be distributed under the terms of the
5 * GNU General Public License.
6 *
7 * Written by Thayne Harbaugh
8 * Based on work by Dan Hollis <goemon at anime dot net> and others.
9 * http://www.anime.net/~goemon/linux-ecc/
10 *
11 * Modified by Dave Peterson and Doug Thompson
12 *
13 */
14
Alan Coxda9bb1d2006-01-18 17:44:13 -080015#include <linux/module.h>
16#include <linux/proc_fs.h>
17#include <linux/kernel.h>
18#include <linux/types.h>
19#include <linux/smp.h>
20#include <linux/init.h>
21#include <linux/sysctl.h>
22#include <linux/highmem.h>
23#include <linux/timer.h>
24#include <linux/slab.h>
25#include <linux/jiffies.h>
26#include <linux/spinlock.h>
27#include <linux/list.h>
Alan Coxda9bb1d2006-01-18 17:44:13 -080028#include <linux/ctype.h>
Dave Jiangc0d12172007-07-19 01:49:46 -070029#include <linux/edac.h>
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -030030#include <linux/bitops.h>
Alan Coxda9bb1d2006-01-18 17:44:13 -080031#include <asm/uaccess.h>
32#include <asm/page.h>
33#include <asm/edac.h>
Douglas Thompson20bcb7a2007-07-19 01:49:47 -070034#include "edac_core.h"
Douglas Thompson7c9281d2007-07-19 01:49:33 -070035#include "edac_module.h"
Alan Coxda9bb1d2006-01-18 17:44:13 -080036
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -030037#define CREATE_TRACE_POINTS
38#define TRACE_INCLUDE_PATH ../../include/ras
39#include <ras/ras_event.h>
40
Alan Coxda9bb1d2006-01-18 17:44:13 -080041/* lock to memory controller's control array */
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -070042static DEFINE_MUTEX(mem_ctls_mutex);
Robert P. J. Dayff6ac2a2008-04-29 01:03:17 -070043static LIST_HEAD(mc_devices);
Alan Coxda9bb1d2006-01-18 17:44:13 -080044
Mauro Carvalho Chehab80cc7d82012-10-31 10:42:29 -030045/*
46 * Used to lock EDAC MC to just one module, avoiding two drivers e. g.
47 * apei/ghes and i7core_edac to be used at the same time.
48 */
49static void const *edac_mc_owner;
50
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030051unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf,
52 unsigned len)
53{
54 struct mem_ctl_info *mci = dimm->mci;
55 int i, n, count = 0;
56 char *p = buf;
57
58 for (i = 0; i < mci->n_layers; i++) {
59 n = snprintf(p, len, "%s %d ",
60 edac_layer_name[mci->layers[i].type],
61 dimm->location[i]);
62 p += n;
63 len -= n;
64 count += n;
65 if (!len)
66 break;
67 }
68
69 return count;
70}
71
Alan Coxda9bb1d2006-01-18 17:44:13 -080072#ifdef CONFIG_EDAC_DEBUG
73
Mauro Carvalho Chehaba4b4be32012-01-27 10:26:13 -030074static void edac_mc_dump_channel(struct rank_info *chan)
Alan Coxda9bb1d2006-01-18 17:44:13 -080075{
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030076 edac_dbg(4, " channel->chan_idx = %d\n", chan->chan_idx);
77 edac_dbg(4, " channel = %p\n", chan);
78 edac_dbg(4, " channel->csrow = %p\n", chan->csrow);
79 edac_dbg(4, " channel->dimm = %p\n", chan->dimm);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -030080}
81
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030082static void edac_mc_dump_dimm(struct dimm_info *dimm, int number)
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -030083{
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030084 char location[80];
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -030085
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030086 edac_dimm_info_location(dimm, location, sizeof(location));
87
88 edac_dbg(4, "%s%i: %smapped as virtual row %d, chan %d\n",
89 dimm->mci->mem_is_per_rank ? "rank" : "dimm",
90 number, location, dimm->csrow, dimm->cschannel);
91 edac_dbg(4, " dimm = %p\n", dimm);
92 edac_dbg(4, " dimm->label = '%s'\n", dimm->label);
93 edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages);
94 edac_dbg(4, " dimm->grain = %d\n", dimm->grain);
95 edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages);
Alan Coxda9bb1d2006-01-18 17:44:13 -080096}
97
Adrian Bunk2da1c112007-07-19 01:49:32 -070098static void edac_mc_dump_csrow(struct csrow_info *csrow)
Alan Coxda9bb1d2006-01-18 17:44:13 -080099{
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -0300100 edac_dbg(4, "csrow->csrow_idx = %d\n", csrow->csrow_idx);
101 edac_dbg(4, " csrow = %p\n", csrow);
102 edac_dbg(4, " csrow->first_page = 0x%lx\n", csrow->first_page);
103 edac_dbg(4, " csrow->last_page = 0x%lx\n", csrow->last_page);
104 edac_dbg(4, " csrow->page_mask = 0x%lx\n", csrow->page_mask);
105 edac_dbg(4, " csrow->nr_channels = %d\n", csrow->nr_channels);
106 edac_dbg(4, " csrow->channels = %p\n", csrow->channels);
107 edac_dbg(4, " csrow->mci = %p\n", csrow->mci);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800108}
109
Adrian Bunk2da1c112007-07-19 01:49:32 -0700110static void edac_mc_dump_mci(struct mem_ctl_info *mci)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800111{
Joe Perches956b9ba2012-04-29 17:08:39 -0300112 edac_dbg(3, "\tmci = %p\n", mci);
113 edac_dbg(3, "\tmci->mtype_cap = %lx\n", mci->mtype_cap);
114 edac_dbg(3, "\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
115 edac_dbg(3, "\tmci->edac_cap = %lx\n", mci->edac_cap);
116 edac_dbg(4, "\tmci->edac_check = %p\n", mci->edac_check);
117 edac_dbg(3, "\tmci->nr_csrows = %d, csrows = %p\n",
118 mci->nr_csrows, mci->csrows);
119 edac_dbg(3, "\tmci->nr_dimms = %d, dimms = %p\n",
120 mci->tot_dimms, mci->dimms);
121 edac_dbg(3, "\tdev = %p\n", mci->pdev);
122 edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n",
123 mci->mod_name, mci->ctl_name);
124 edac_dbg(3, "\tpvt_info = %p\n\n", mci->pvt_info);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800125}
126
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200127#endif /* CONFIG_EDAC_DEBUG */
128
Borislav Petkov239642f2009-11-12 15:33:16 +0100129/*
130 * keep those in sync with the enum mem_type
131 */
132const char *edac_mem_types[] = {
133 "Empty csrow",
134 "Reserved csrow type",
135 "Unknown csrow type",
136 "Fast page mode RAM",
137 "Extended data out RAM",
138 "Burst Extended data out RAM",
139 "Single data rate SDRAM",
140 "Registered single data rate SDRAM",
141 "Double data rate SDRAM",
142 "Registered Double data rate SDRAM",
143 "Rambus DRAM",
144 "Unbuffered DDR2 RAM",
145 "Fully buffered DDR2",
146 "Registered DDR2 RAM",
147 "Rambus XDR",
148 "Unbuffered DDR3 RAM",
149 "Registered DDR3 RAM",
150};
151EXPORT_SYMBOL_GPL(edac_mem_types);
152
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300153/**
154 * edac_align_ptr - Prepares the pointer offsets for a single-shot allocation
155 * @p: pointer to a pointer with the memory offset to be used. At
156 * return, this will be incremented to point to the next offset
157 * @size: Size of the data structure to be reserved
158 * @n_elems: Number of elements that should be reserved
Alan Coxda9bb1d2006-01-18 17:44:13 -0800159 *
160 * If 'size' is a constant, the compiler will optimize this whole function
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300161 * down to either a no-op or the addition of a constant to the value of '*p'.
162 *
163 * The 'p' pointer is absolutely needed to keep the proper advancing
164 * further in memory to the proper offsets when allocating the struct along
165 * with its embedded structs, as edac_device_alloc_ctl_info() does it
166 * above, for example.
167 *
168 * At return, the pointer 'p' will be incremented to be used on a next call
169 * to this function.
Alan Coxda9bb1d2006-01-18 17:44:13 -0800170 */
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300171void *edac_align_ptr(void **p, unsigned size, int n_elems)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800172{
173 unsigned align, r;
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300174 void *ptr = *p;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800175
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300176 *p += size * n_elems;
177
178 /*
179 * 'p' can possibly be an unaligned item X such that sizeof(X) is
180 * 'size'. Adjust 'p' so that its alignment is at least as
181 * stringent as what the compiler would provide for X and return
182 * the aligned result.
183 * Here we assume that the alignment of a "long long" is the most
Alan Coxda9bb1d2006-01-18 17:44:13 -0800184 * stringent alignment that the compiler will ever provide by default.
185 * As far as I know, this is a reasonable assumption.
186 */
187 if (size > sizeof(long))
188 align = sizeof(long long);
189 else if (size > sizeof(int))
190 align = sizeof(long);
191 else if (size > sizeof(short))
192 align = sizeof(int);
193 else if (size > sizeof(char))
194 align = sizeof(short);
195 else
Douglas Thompson079708b2007-07-19 01:49:58 -0700196 return (char *)ptr;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800197
Chris Metcalf8447c4d12012-06-06 13:11:05 -0400198 r = (unsigned long)p % align;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800199
200 if (r == 0)
Douglas Thompson079708b2007-07-19 01:49:58 -0700201 return (char *)ptr;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800202
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300203 *p += align - r;
204
Douglas Thompson7391c6d2007-07-19 01:50:21 -0700205 return (void *)(((unsigned long)ptr) + align - r);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800206}
207
Shaun Ruffellfaa2ad02012-09-22 20:26:38 -0500208static void _edac_mc_free(struct mem_ctl_info *mci)
209{
210 int i, chn, row;
211 struct csrow_info *csr;
212 const unsigned int tot_dimms = mci->tot_dimms;
213 const unsigned int tot_channels = mci->num_cschannel;
214 const unsigned int tot_csrows = mci->nr_csrows;
215
216 if (mci->dimms) {
217 for (i = 0; i < tot_dimms; i++)
218 kfree(mci->dimms[i]);
219 kfree(mci->dimms);
220 }
221 if (mci->csrows) {
222 for (row = 0; row < tot_csrows; row++) {
223 csr = mci->csrows[row];
224 if (csr) {
225 if (csr->channels) {
226 for (chn = 0; chn < tot_channels; chn++)
227 kfree(csr->channels[chn]);
228 kfree(csr->channels);
229 }
230 kfree(csr);
231 }
232 }
233 kfree(mci->csrows);
234 }
235 kfree(mci);
236}
237
Alan Coxda9bb1d2006-01-18 17:44:13 -0800238/**
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300239 * edac_mc_alloc: Allocate and partially fill a struct mem_ctl_info structure
240 * @mc_num: Memory controller number
241 * @n_layers: Number of MC hierarchy layers
242 * layers: Describes each layer as seen by the Memory Controller
243 * @size_pvt: size of private storage needed
244 *
Alan Coxda9bb1d2006-01-18 17:44:13 -0800245 *
246 * Everything is kmalloc'ed as one big chunk - more efficient.
247 * Only can be used if all structures have the same lifetime - otherwise
248 * you have to allocate and initialize your own structures.
249 *
250 * Use edac_mc_free() to free mc structures allocated by this function.
251 *
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300252 * NOTE: drivers handle multi-rank memories in different ways: in some
253 * drivers, one multi-rank memory stick is mapped as one entry, while, in
254 * others, a single multi-rank memory stick would be mapped into several
255 * entries. Currently, this function will allocate multiple struct dimm_info
256 * on such scenarios, as grouping the multiple ranks require drivers change.
257 *
Alan Coxda9bb1d2006-01-18 17:44:13 -0800258 * Returns:
Mauro Carvalho Chehabca0907b2012-05-02 14:37:00 -0300259 * On failure: NULL
260 * On success: struct mem_ctl_info pointer
Alan Coxda9bb1d2006-01-18 17:44:13 -0800261 */
Mauro Carvalho Chehabca0907b2012-05-02 14:37:00 -0300262struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
263 unsigned n_layers,
264 struct edac_mc_layer *layers,
265 unsigned sz_pvt)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800266{
267 struct mem_ctl_info *mci;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300268 struct edac_mc_layer *layer;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300269 struct csrow_info *csr;
270 struct rank_info *chan;
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300271 struct dimm_info *dimm;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300272 u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS];
273 unsigned pos[EDAC_MAX_LAYERS];
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300274 unsigned size, tot_dimms = 1, count = 1;
275 unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0;
Mauro Carvalho Chehab5926ff52012-02-09 11:05:20 -0300276 void *pvt, *p, *ptr = NULL;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300277 int i, j, row, chn, n, len, off;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300278 bool per_rank = false;
279
280 BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0);
281 /*
282 * Calculate the total amount of dimms and csrows/cschannels while
283 * in the old API emulation mode
284 */
285 for (i = 0; i < n_layers; i++) {
286 tot_dimms *= layers[i].size;
287 if (layers[i].is_virt_csrow)
288 tot_csrows *= layers[i].size;
289 else
290 tot_channels *= layers[i].size;
291
292 if (layers[i].type == EDAC_MC_LAYER_CHIP_SELECT)
293 per_rank = true;
294 }
Alan Coxda9bb1d2006-01-18 17:44:13 -0800295
296 /* Figure out the offsets of the various items from the start of an mc
297 * structure. We want the alignment of each item to be at least as
298 * stringent as what the compiler would provide if we could simply
299 * hardcode everything into a single struct.
300 */
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300301 mci = edac_align_ptr(&ptr, sizeof(*mci), 1);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300302 layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300303 for (i = 0; i < n_layers; i++) {
304 count *= layers[i].size;
Joe Perches956b9ba2012-04-29 17:08:39 -0300305 edac_dbg(4, "errcount layer %d size %d\n", i, count);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300306 ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
307 ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
308 tot_errcount += 2 * count;
309 }
310
Joe Perches956b9ba2012-04-29 17:08:39 -0300311 edac_dbg(4, "allocating %d error counters\n", tot_errcount);
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300312 pvt = edac_align_ptr(&ptr, sz_pvt, 1);
Douglas Thompson079708b2007-07-19 01:49:58 -0700313 size = ((unsigned long)pvt) + sz_pvt;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800314
Joe Perches956b9ba2012-04-29 17:08:39 -0300315 edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n",
316 size,
317 tot_dimms,
318 per_rank ? "ranks" : "dimms",
319 tot_csrows * tot_channels);
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300320
Doug Thompson8096cfa2007-07-19 01:50:27 -0700321 mci = kzalloc(size, GFP_KERNEL);
322 if (mci == NULL)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800323 return NULL;
324
325 /* Adjust pointers so they point within the memory we just allocated
326 * rather than an imaginary chunk of memory located at address 0.
327 */
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300328 layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer));
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300329 for (i = 0; i < n_layers; i++) {
330 mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i]));
331 mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i]));
332 }
Douglas Thompson079708b2007-07-19 01:49:58 -0700333 pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800334
Doug Thompsonb8f6f972007-07-19 01:50:26 -0700335 /* setup index and various internal pointers */
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300336 mci->mc_idx = mc_num;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300337 mci->tot_dimms = tot_dimms;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800338 mci->pvt_info = pvt;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300339 mci->n_layers = n_layers;
340 mci->layers = layer;
341 memcpy(mci->layers, layers, sizeof(*layer) * n_layers);
342 mci->nr_csrows = tot_csrows;
343 mci->num_cschannel = tot_channels;
344 mci->mem_is_per_rank = per_rank;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800345
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300346 /*
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300347 * Alocate and fill the csrow/channels structs
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300348 */
Joe Perchesd3d09e12013-01-26 11:24:00 -0800349 mci->csrows = kcalloc(tot_csrows, sizeof(*mci->csrows), GFP_KERNEL);
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300350 if (!mci->csrows)
351 goto error;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300352 for (row = 0; row < tot_csrows; row++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300353 csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL);
354 if (!csr)
355 goto error;
356 mci->csrows[row] = csr;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300357 csr->csrow_idx = row;
358 csr->mci = mci;
359 csr->nr_channels = tot_channels;
Joe Perchesd3d09e12013-01-26 11:24:00 -0800360 csr->channels = kcalloc(tot_channels, sizeof(*csr->channels),
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300361 GFP_KERNEL);
362 if (!csr->channels)
363 goto error;
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300364
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300365 for (chn = 0; chn < tot_channels; chn++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300366 chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL);
367 if (!chan)
368 goto error;
369 csr->channels[chn] = chan;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800370 chan->chan_idx = chn;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300371 chan->csrow = csr;
372 }
373 }
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300374
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300375 /*
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300376 * Allocate and fill the dimm structs
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300377 */
Joe Perchesd3d09e12013-01-26 11:24:00 -0800378 mci->dimms = kcalloc(tot_dimms, sizeof(*mci->dimms), GFP_KERNEL);
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300379 if (!mci->dimms)
380 goto error;
381
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300382 memset(&pos, 0, sizeof(pos));
383 row = 0;
384 chn = 0;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300385 for (i = 0; i < tot_dimms; i++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300386 chan = mci->csrows[row]->channels[chn];
387 off = EDAC_DIMM_OFF(layer, n_layers, pos[0], pos[1], pos[2]);
388 if (off < 0 || off >= tot_dimms) {
389 edac_mc_printk(mci, KERN_ERR, "EDAC core bug: EDAC_DIMM_OFF is trying to do an illegal data access\n");
390 goto error;
391 }
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300392
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300393 dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL);
Dan Carpenter08a4a132012-05-18 15:51:02 +0300394 if (!dimm)
395 goto error;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300396 mci->dimms[off] = dimm;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300397 dimm->mci = mci;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300398
Mauro Carvalho Chehab5926ff52012-02-09 11:05:20 -0300399 /*
400 * Copy DIMM location and initialize it.
401 */
402 len = sizeof(dimm->label);
403 p = dimm->label;
404 n = snprintf(p, len, "mc#%u", mc_num);
405 p += n;
406 len -= n;
407 for (j = 0; j < n_layers; j++) {
408 n = snprintf(p, len, "%s#%u",
409 edac_layer_name[layers[j].type],
410 pos[j]);
411 p += n;
412 len -= n;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300413 dimm->location[j] = pos[j];
414
Mauro Carvalho Chehab5926ff52012-02-09 11:05:20 -0300415 if (len <= 0)
416 break;
417 }
418
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300419 /* Link it to the csrows old API data */
420 chan->dimm = dimm;
421 dimm->csrow = row;
422 dimm->cschannel = chn;
423
424 /* Increment csrow location */
Mauro Carvalho Chehab24bef662012-10-24 10:30:01 -0200425 if (layers[0].is_virt_csrow) {
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300426 chn++;
Mauro Carvalho Chehab24bef662012-10-24 10:30:01 -0200427 if (chn == tot_channels) {
428 chn = 0;
429 row++;
430 }
431 } else {
432 row++;
433 if (row == tot_csrows) {
434 row = 0;
435 chn++;
436 }
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300437 }
438
439 /* Increment dimm location */
440 for (j = n_layers - 1; j >= 0; j--) {
441 pos[j]++;
442 if (pos[j] < layers[j].size)
443 break;
444 pos[j] = 0;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800445 }
446 }
447
Dave Jiang81d87cb2007-07-19 01:49:52 -0700448 mci->op_state = OP_ALLOC;
Doug Thompson8096cfa2007-07-19 01:50:27 -0700449
Alan Coxda9bb1d2006-01-18 17:44:13 -0800450 return mci;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300451
452error:
Shaun Ruffellfaa2ad02012-09-22 20:26:38 -0500453 _edac_mc_free(mci);
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300454
455 return NULL;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800456}
Dave Peterson91105402006-03-26 01:38:55 -0800457EXPORT_SYMBOL_GPL(edac_mc_alloc);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800458
Alan Coxda9bb1d2006-01-18 17:44:13 -0800459/**
Doug Thompson8096cfa2007-07-19 01:50:27 -0700460 * edac_mc_free
461 * 'Free' a previously allocated 'mci' structure
Alan Coxda9bb1d2006-01-18 17:44:13 -0800462 * @mci: pointer to a struct mem_ctl_info structure
Alan Coxda9bb1d2006-01-18 17:44:13 -0800463 */
464void edac_mc_free(struct mem_ctl_info *mci)
465{
Joe Perches956b9ba2012-04-29 17:08:39 -0300466 edac_dbg(1, "\n");
Mauro Carvalho Chehabbbc560a2010-08-16 18:22:43 -0300467
Shaun Ruffellfaa2ad02012-09-22 20:26:38 -0500468 /* If we're not yet registered with sysfs free only what was allocated
469 * in edac_mc_alloc().
470 */
471 if (!device_is_registered(&mci->dev)) {
472 _edac_mc_free(mci);
473 return;
474 }
475
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300476 /* the mci instance is freed here, when the sysfs object is dropped */
Mauro Carvalho Chehab7a623c02012-04-16 16:41:11 -0300477 edac_unregister_sysfs(mci);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800478}
Dave Peterson91105402006-03-26 01:38:55 -0800479EXPORT_SYMBOL_GPL(edac_mc_free);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800480
Doug Thompsonbce19682007-07-26 10:41:14 -0700481
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300482/**
Doug Thompsonbce19682007-07-26 10:41:14 -0700483 * find_mci_by_dev
484 *
485 * scan list of controllers looking for the one that manages
486 * the 'dev' device
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300487 * @dev: pointer to a struct device related with the MCI
Doug Thompsonbce19682007-07-26 10:41:14 -0700488 */
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300489struct mem_ctl_info *find_mci_by_dev(struct device *dev)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800490{
491 struct mem_ctl_info *mci;
492 struct list_head *item;
493
Joe Perches956b9ba2012-04-29 17:08:39 -0300494 edac_dbg(3, "\n");
Alan Coxda9bb1d2006-01-18 17:44:13 -0800495
496 list_for_each(item, &mc_devices) {
497 mci = list_entry(item, struct mem_ctl_info, link);
498
Mauro Carvalho Chehabfd687502012-03-16 07:44:18 -0300499 if (mci->pdev == dev)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800500 return mci;
501 }
502
503 return NULL;
504}
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300505EXPORT_SYMBOL_GPL(find_mci_by_dev);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800506
Dave Jiang81d87cb2007-07-19 01:49:52 -0700507/*
508 * handler for EDAC to check if NMI type handler has asserted interrupt
509 */
510static int edac_mc_assert_error_check_and_clear(void)
511{
Dave Jiang66ee2f92007-07-19 01:49:54 -0700512 int old_state;
Dave Jiang81d87cb2007-07-19 01:49:52 -0700513
Douglas Thompson079708b2007-07-19 01:49:58 -0700514 if (edac_op_state == EDAC_OPSTATE_POLL)
Dave Jiang81d87cb2007-07-19 01:49:52 -0700515 return 1;
516
Dave Jiang66ee2f92007-07-19 01:49:54 -0700517 old_state = edac_err_assert;
518 edac_err_assert = 0;
Dave Jiang81d87cb2007-07-19 01:49:52 -0700519
Dave Jiang66ee2f92007-07-19 01:49:54 -0700520 return old_state;
Dave Jiang81d87cb2007-07-19 01:49:52 -0700521}
522
523/*
524 * edac_mc_workq_function
525 * performs the operation scheduled by a workq request
526 */
Dave Jiang81d87cb2007-07-19 01:49:52 -0700527static void edac_mc_workq_function(struct work_struct *work_req)
528{
Jean Delvarefbeb4382009-04-13 14:40:21 -0700529 struct delayed_work *d_work = to_delayed_work(work_req);
Dave Jiang81d87cb2007-07-19 01:49:52 -0700530 struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
Dave Jiang81d87cb2007-07-19 01:49:52 -0700531
532 mutex_lock(&mem_ctls_mutex);
533
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700534 /* if this control struct has movd to offline state, we are done */
535 if (mci->op_state == OP_OFFLINE) {
536 mutex_unlock(&mem_ctls_mutex);
537 return;
538 }
539
Dave Jiang81d87cb2007-07-19 01:49:52 -0700540 /* Only poll controllers that are running polled and have a check */
541 if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL))
542 mci->edac_check(mci);
543
Dave Jiang81d87cb2007-07-19 01:49:52 -0700544 mutex_unlock(&mem_ctls_mutex);
545
546 /* Reschedule */
Dave Jiang4de78c62007-07-19 01:49:54 -0700547 queue_delayed_work(edac_workqueue, &mci->work,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700548 msecs_to_jiffies(edac_mc_get_poll_msec()));
Dave Jiang81d87cb2007-07-19 01:49:52 -0700549}
550
551/*
552 * edac_mc_workq_setup
553 * initialize a workq item for this mci
554 * passing in the new delay period in msec
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700555 *
556 * locking model:
557 *
558 * called with the mem_ctls_mutex held
Dave Jiang81d87cb2007-07-19 01:49:52 -0700559 */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700560static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
Dave Jiang81d87cb2007-07-19 01:49:52 -0700561{
Joe Perches956b9ba2012-04-29 17:08:39 -0300562 edac_dbg(0, "\n");
Dave Jiang81d87cb2007-07-19 01:49:52 -0700563
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700564 /* if this instance is not in the POLL state, then simply return */
565 if (mci->op_state != OP_RUNNING_POLL)
566 return;
567
Dave Jiang81d87cb2007-07-19 01:49:52 -0700568 INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
Tejun Heo41f63c52012-08-03 10:30:47 -0700569 mod_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
Dave Jiang81d87cb2007-07-19 01:49:52 -0700570}
571
572/*
573 * edac_mc_workq_teardown
574 * stop the workq processing on this mci
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700575 *
576 * locking model:
577 *
578 * called WITHOUT lock held
Dave Jiang81d87cb2007-07-19 01:49:52 -0700579 */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700580static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
Dave Jiang81d87cb2007-07-19 01:49:52 -0700581{
582 int status;
583
Borislav Petkov00740c52010-09-26 12:42:23 +0200584 if (mci->op_state != OP_RUNNING_POLL)
585 return;
586
Doug Thompsonbce19682007-07-26 10:41:14 -0700587 status = cancel_delayed_work(&mci->work);
588 if (status == 0) {
Joe Perches956b9ba2012-04-29 17:08:39 -0300589 edac_dbg(0, "not canceled, flush the queue\n");
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700590
Doug Thompsonbce19682007-07-26 10:41:14 -0700591 /* workq instance might be running, wait for it */
592 flush_workqueue(edac_workqueue);
Dave Jiang81d87cb2007-07-19 01:49:52 -0700593 }
594}
595
596/*
Doug Thompsonbce19682007-07-26 10:41:14 -0700597 * edac_mc_reset_delay_period(unsigned long value)
598 *
599 * user space has updated our poll period value, need to
600 * reset our workq delays
Dave Jiang81d87cb2007-07-19 01:49:52 -0700601 */
Doug Thompsonbce19682007-07-26 10:41:14 -0700602void edac_mc_reset_delay_period(int value)
Dave Jiang81d87cb2007-07-19 01:49:52 -0700603{
Doug Thompsonbce19682007-07-26 10:41:14 -0700604 struct mem_ctl_info *mci;
605 struct list_head *item;
Dave Jiang81d87cb2007-07-19 01:49:52 -0700606
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700607 mutex_lock(&mem_ctls_mutex);
608
Doug Thompsonbce19682007-07-26 10:41:14 -0700609 list_for_each(item, &mc_devices) {
610 mci = list_entry(item, struct mem_ctl_info, link);
611
612 edac_mc_workq_setup(mci, (unsigned long) value);
613 }
Dave Jiang81d87cb2007-07-19 01:49:52 -0700614
615 mutex_unlock(&mem_ctls_mutex);
616}
617
Doug Thompsonbce19682007-07-26 10:41:14 -0700618
619
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700620/* Return 0 on success, 1 on failure.
621 * Before calling this function, caller must
622 * assign a unique value to mci->mc_idx.
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700623 *
624 * locking model:
625 *
626 * called with the mem_ctls_mutex lock held
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700627 */
Douglas Thompson079708b2007-07-19 01:49:58 -0700628static int add_mc_to_global_list(struct mem_ctl_info *mci)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800629{
630 struct list_head *item, *insert_before;
631 struct mem_ctl_info *p;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800632
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700633 insert_before = &mc_devices;
634
Mauro Carvalho Chehabfd687502012-03-16 07:44:18 -0300635 p = find_mci_by_dev(mci->pdev);
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700636 if (unlikely(p != NULL))
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700637 goto fail0;
638
639 list_for_each(item, &mc_devices) {
640 p = list_entry(item, struct mem_ctl_info, link);
641
642 if (p->mc_idx >= mci->mc_idx) {
643 if (unlikely(p->mc_idx == mci->mc_idx))
644 goto fail1;
645
646 insert_before = item;
647 break;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800648 }
Alan Coxda9bb1d2006-01-18 17:44:13 -0800649 }
650
651 list_add_tail_rcu(&mci->link, insert_before);
Dave Jiangc0d12172007-07-19 01:49:46 -0700652 atomic_inc(&edac_handlers);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800653 return 0;
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700654
Douglas Thompson052dfb42007-07-19 01:50:13 -0700655fail0:
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700656 edac_printk(KERN_WARNING, EDAC_MC,
Mauro Carvalho Chehabfd687502012-03-16 07:44:18 -0300657 "%s (%s) %s %s already assigned %d\n", dev_name(p->pdev),
Stephen Rothwell17aa7e02008-05-05 13:54:19 +1000658 edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700659 return 1;
660
Douglas Thompson052dfb42007-07-19 01:50:13 -0700661fail1:
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700662 edac_printk(KERN_WARNING, EDAC_MC,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700663 "bug in low-level driver: attempt to assign\n"
664 " duplicate mc_idx %d in %s()\n", p->mc_idx, __func__);
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700665 return 1;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800666}
667
Mauro Carvalho Chehab80cc7d82012-10-31 10:42:29 -0300668static int del_mc_from_global_list(struct mem_ctl_info *mci)
Dave Petersona1d03fc2006-03-26 01:38:46 -0800669{
Mauro Carvalho Chehab80cc7d82012-10-31 10:42:29 -0300670 int handlers = atomic_dec_return(&edac_handlers);
Dave Petersona1d03fc2006-03-26 01:38:46 -0800671 list_del_rcu(&mci->link);
Lai Jiangshane2e77092011-05-26 16:25:58 -0700672
673 /* these are for safe removal of devices from global list while
674 * NMI handlers may be traversing list
675 */
676 synchronize_rcu();
677 INIT_LIST_HEAD(&mci->link);
Mauro Carvalho Chehab80cc7d82012-10-31 10:42:29 -0300678
679 return handlers;
Dave Petersona1d03fc2006-03-26 01:38:46 -0800680}
681
Alan Coxda9bb1d2006-01-18 17:44:13 -0800682/**
Douglas Thompson5da08312007-07-19 01:49:31 -0700683 * edac_mc_find: Search for a mem_ctl_info structure whose index is 'idx'.
684 *
685 * If found, return a pointer to the structure.
686 * Else return NULL.
687 *
688 * Caller must hold mem_ctls_mutex.
689 */
Douglas Thompson079708b2007-07-19 01:49:58 -0700690struct mem_ctl_info *edac_mc_find(int idx)
Douglas Thompson5da08312007-07-19 01:49:31 -0700691{
692 struct list_head *item;
693 struct mem_ctl_info *mci;
694
695 list_for_each(item, &mc_devices) {
696 mci = list_entry(item, struct mem_ctl_info, link);
697
698 if (mci->mc_idx >= idx) {
699 if (mci->mc_idx == idx)
700 return mci;
701
702 break;
703 }
704 }
705
706 return NULL;
707}
708EXPORT_SYMBOL(edac_mc_find);
709
710/**
Dave Peterson472678e2006-03-26 01:38:49 -0800711 * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and
712 * create sysfs entries associated with mci structure
Alan Coxda9bb1d2006-01-18 17:44:13 -0800713 * @mci: pointer to the mci structure to be added to the list
714 *
715 * Return:
716 * 0 Success
717 * !0 Failure
718 */
719
720/* FIXME - should a warning be printed if no error detection? correction? */
Doug Thompsonb8f6f972007-07-19 01:50:26 -0700721int edac_mc_add_mc(struct mem_ctl_info *mci)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800722{
Mauro Carvalho Chehab80cc7d82012-10-31 10:42:29 -0300723 int ret = -EINVAL;
Joe Perches956b9ba2012-04-29 17:08:39 -0300724 edac_dbg(0, "\n");
Doug Thompsonb8f6f972007-07-19 01:50:26 -0700725
Alan Coxda9bb1d2006-01-18 17:44:13 -0800726#ifdef CONFIG_EDAC_DEBUG
727 if (edac_debug_level >= 3)
728 edac_mc_dump_mci(mci);
Dave Petersone7ecd892006-03-26 01:38:52 -0800729
Alan Coxda9bb1d2006-01-18 17:44:13 -0800730 if (edac_debug_level >= 4) {
731 int i;
732
733 for (i = 0; i < mci->nr_csrows; i++) {
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -0300734 struct csrow_info *csrow = mci->csrows[i];
735 u32 nr_pages = 0;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800736 int j;
Dave Petersone7ecd892006-03-26 01:38:52 -0800737
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -0300738 for (j = 0; j < csrow->nr_channels; j++)
739 nr_pages += csrow->channels[j]->dimm->nr_pages;
740 if (!nr_pages)
741 continue;
742 edac_mc_dump_csrow(csrow);
743 for (j = 0; j < csrow->nr_channels; j++)
744 if (csrow->channels[j]->dimm->nr_pages)
745 edac_mc_dump_channel(csrow->channels[j]);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800746 }
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300747 for (i = 0; i < mci->tot_dimms; i++)
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -0300748 if (mci->dimms[i]->nr_pages)
749 edac_mc_dump_dimm(mci->dimms[i], i);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800750 }
751#endif
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700752 mutex_lock(&mem_ctls_mutex);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800753
Mauro Carvalho Chehab80cc7d82012-10-31 10:42:29 -0300754 if (edac_mc_owner && edac_mc_owner != mci->mod_name) {
755 ret = -EPERM;
756 goto fail0;
757 }
758
Alan Coxda9bb1d2006-01-18 17:44:13 -0800759 if (add_mc_to_global_list(mci))
Dave Peterson028a7b62006-03-26 01:38:47 -0800760 goto fail0;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800761
762 /* set load time so that error rate can be tracked */
763 mci->start_time = jiffies;
764
eric wollesen9794f332007-02-12 00:53:08 -0800765 if (edac_create_sysfs_mci_device(mci)) {
766 edac_mc_printk(mci, KERN_WARNING,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700767 "failed to create sysfs device\n");
eric wollesen9794f332007-02-12 00:53:08 -0800768 goto fail1;
769 }
Alan Coxda9bb1d2006-01-18 17:44:13 -0800770
Dave Jiang81d87cb2007-07-19 01:49:52 -0700771 /* If there IS a check routine, then we are running POLLED */
772 if (mci->edac_check != NULL) {
773 /* This instance is NOW RUNNING */
774 mci->op_state = OP_RUNNING_POLL;
775
776 edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
777 } else {
778 mci->op_state = OP_RUNNING_INTERRUPT;
779 }
780
Alan Coxda9bb1d2006-01-18 17:44:13 -0800781 /* Report action taken */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700782 edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':"
Stephen Rothwell17aa7e02008-05-05 13:54:19 +1000783 " DEV %s\n", mci->mod_name, mci->ctl_name, edac_dev_name(mci));
Alan Coxda9bb1d2006-01-18 17:44:13 -0800784
Mauro Carvalho Chehab80cc7d82012-10-31 10:42:29 -0300785 edac_mc_owner = mci->mod_name;
786
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700787 mutex_unlock(&mem_ctls_mutex);
Dave Peterson028a7b62006-03-26 01:38:47 -0800788 return 0;
789
Douglas Thompson052dfb42007-07-19 01:50:13 -0700790fail1:
Dave Peterson028a7b62006-03-26 01:38:47 -0800791 del_mc_from_global_list(mci);
792
Douglas Thompson052dfb42007-07-19 01:50:13 -0700793fail0:
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700794 mutex_unlock(&mem_ctls_mutex);
Mauro Carvalho Chehab80cc7d82012-10-31 10:42:29 -0300795 return ret;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800796}
Dave Peterson91105402006-03-26 01:38:55 -0800797EXPORT_SYMBOL_GPL(edac_mc_add_mc);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800798
Alan Coxda9bb1d2006-01-18 17:44:13 -0800799/**
Dave Peterson472678e2006-03-26 01:38:49 -0800800 * edac_mc_del_mc: Remove sysfs entries for specified mci structure and
801 * remove mci structure from global list
Doug Thompson37f04582006-06-30 01:56:07 -0700802 * @pdev: Pointer to 'struct device' representing mci structure to remove.
Alan Coxda9bb1d2006-01-18 17:44:13 -0800803 *
Dave Peterson18dbc332006-03-26 01:38:50 -0800804 * Return pointer to removed mci structure, or NULL if device not found.
Alan Coxda9bb1d2006-01-18 17:44:13 -0800805 */
Douglas Thompson079708b2007-07-19 01:49:58 -0700806struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800807{
Dave Peterson18dbc332006-03-26 01:38:50 -0800808 struct mem_ctl_info *mci;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800809
Joe Perches956b9ba2012-04-29 17:08:39 -0300810 edac_dbg(0, "\n");
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700811
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700812 mutex_lock(&mem_ctls_mutex);
Dave Peterson18dbc332006-03-26 01:38:50 -0800813
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700814 /* find the requested mci struct in the global list */
815 mci = find_mci_by_dev(dev);
816 if (mci == NULL) {
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700817 mutex_unlock(&mem_ctls_mutex);
Dave Peterson18dbc332006-03-26 01:38:50 -0800818 return NULL;
819 }
820
Mauro Carvalho Chehab80cc7d82012-10-31 10:42:29 -0300821 if (!del_mc_from_global_list(mci))
822 edac_mc_owner = NULL;
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700823 mutex_unlock(&mem_ctls_mutex);
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700824
Borislav Petkovbb31b3122010-12-02 17:48:35 +0100825 /* flush workq processes */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700826 edac_mc_workq_teardown(mci);
Borislav Petkovbb31b3122010-12-02 17:48:35 +0100827
828 /* marking MCI offline */
829 mci->op_state = OP_OFFLINE;
830
831 /* remove from sysfs */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700832 edac_remove_sysfs_mci_device(mci);
833
Dave Peterson537fba22006-03-26 01:38:40 -0800834 edac_printk(KERN_INFO, EDAC_MC,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700835 "Removed device %d for %s %s: DEV %s\n", mci->mc_idx,
Stephen Rothwell17aa7e02008-05-05 13:54:19 +1000836 mci->mod_name, mci->ctl_name, edac_dev_name(mci));
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700837
Dave Peterson18dbc332006-03-26 01:38:50 -0800838 return mci;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800839}
Dave Peterson91105402006-03-26 01:38:55 -0800840EXPORT_SYMBOL_GPL(edac_mc_del_mc);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800841
Adrian Bunk2da1c112007-07-19 01:49:32 -0700842static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
843 u32 size)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800844{
845 struct page *pg;
846 void *virt_addr;
847 unsigned long flags = 0;
848
Joe Perches956b9ba2012-04-29 17:08:39 -0300849 edac_dbg(3, "\n");
Alan Coxda9bb1d2006-01-18 17:44:13 -0800850
851 /* ECC error page was not in our memory. Ignore it. */
Douglas Thompson079708b2007-07-19 01:49:58 -0700852 if (!pfn_valid(page))
Alan Coxda9bb1d2006-01-18 17:44:13 -0800853 return;
854
855 /* Find the actual page structure then map it and fix */
856 pg = pfn_to_page(page);
857
858 if (PageHighMem(pg))
859 local_irq_save(flags);
860
Cong Wang4e5df7c2011-11-25 23:14:19 +0800861 virt_addr = kmap_atomic(pg);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800862
863 /* Perform architecture specific atomic scrub operation */
864 atomic_scrub(virt_addr + offset, size);
865
866 /* Unmap and complete */
Cong Wang4e5df7c2011-11-25 23:14:19 +0800867 kunmap_atomic(virt_addr);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800868
869 if (PageHighMem(pg))
870 local_irq_restore(flags);
871}
872
Alan Coxda9bb1d2006-01-18 17:44:13 -0800873/* FIXME - should return -1 */
Dave Petersone7ecd892006-03-26 01:38:52 -0800874int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800875{
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300876 struct csrow_info **csrows = mci->csrows;
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -0300877 int row, i, j, n;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800878
Joe Perches956b9ba2012-04-29 17:08:39 -0300879 edac_dbg(1, "MC%d: 0x%lx\n", mci->mc_idx, page);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800880 row = -1;
881
882 for (i = 0; i < mci->nr_csrows; i++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300883 struct csrow_info *csrow = csrows[i];
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -0300884 n = 0;
885 for (j = 0; j < csrow->nr_channels; j++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300886 struct dimm_info *dimm = csrow->channels[j]->dimm;
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -0300887 n += dimm->nr_pages;
888 }
889 if (n == 0)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800890 continue;
891
Joe Perches956b9ba2012-04-29 17:08:39 -0300892 edac_dbg(3, "MC%d: first(0x%lx) page(0x%lx) last(0x%lx) mask(0x%lx)\n",
893 mci->mc_idx,
894 csrow->first_page, page, csrow->last_page,
895 csrow->page_mask);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800896
897 if ((page >= csrow->first_page) &&
898 (page <= csrow->last_page) &&
899 ((page & csrow->page_mask) ==
900 (csrow->first_page & csrow->page_mask))) {
901 row = i;
902 break;
903 }
904 }
905
906 if (row == -1)
Dave Peterson537fba22006-03-26 01:38:40 -0800907 edac_mc_printk(mci, KERN_ERR,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700908 "could not look up page error address %lx\n",
909 (unsigned long)page);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800910
911 return row;
912}
Dave Peterson91105402006-03-26 01:38:55 -0800913EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800914
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300915const char *edac_layer_name[] = {
916 [EDAC_MC_LAYER_BRANCH] = "branch",
917 [EDAC_MC_LAYER_CHANNEL] = "channel",
918 [EDAC_MC_LAYER_SLOT] = "slot",
919 [EDAC_MC_LAYER_CHIP_SELECT] = "csrow",
Mauro Carvalho Chehabc66b5a72013-02-15 07:21:08 -0300920 [EDAC_MC_LAYER_ALL_MEM] = "memory",
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300921};
922EXPORT_SYMBOL_GPL(edac_layer_name);
923
924static void edac_inc_ce_error(struct mem_ctl_info *mci,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300925 bool enable_per_layer_report,
926 const int pos[EDAC_MAX_LAYERS],
927 const u16 count)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800928{
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300929 int i, index = 0;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800930
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300931 mci->ce_mc += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300932
933 if (!enable_per_layer_report) {
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300934 mci->ce_noinfo_count += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300935 return;
936 }
937
938 for (i = 0; i < mci->n_layers; i++) {
939 if (pos[i] < 0)
940 break;
941 index += pos[i];
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300942 mci->ce_per_layer[i][index] += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300943
944 if (i < mci->n_layers - 1)
945 index *= mci->layers[i + 1].size;
946 }
947}
948
949static void edac_inc_ue_error(struct mem_ctl_info *mci,
950 bool enable_per_layer_report,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300951 const int pos[EDAC_MAX_LAYERS],
952 const u16 count)
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300953{
954 int i, index = 0;
955
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300956 mci->ue_mc += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300957
958 if (!enable_per_layer_report) {
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300959 mci->ce_noinfo_count += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300960 return;
961 }
962
963 for (i = 0; i < mci->n_layers; i++) {
964 if (pos[i] < 0)
965 break;
966 index += pos[i];
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300967 mci->ue_per_layer[i][index] += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300968
969 if (i < mci->n_layers - 1)
970 index *= mci->layers[i + 1].size;
971 }
972}
973
974static void edac_ce_error(struct mem_ctl_info *mci,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300975 const u16 error_count,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300976 const int pos[EDAC_MAX_LAYERS],
977 const char *msg,
978 const char *location,
979 const char *label,
980 const char *detail,
981 const char *other_detail,
982 const bool enable_per_layer_report,
983 const unsigned long page_frame_number,
984 const unsigned long offset_in_page,
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -0300985 long grain)
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300986{
987 unsigned long remapped_page;
Borislav Petkovf430d572012-09-10 18:36:09 +0200988 char *msg_aux = "";
989
990 if (*msg)
991 msg_aux = " ";
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300992
993 if (edac_mc_get_log_ce()) {
994 if (other_detail && *other_detail)
995 edac_mc_printk(mci, KERN_WARNING,
Borislav Petkovf430d572012-09-10 18:36:09 +0200996 "%d CE %s%son %s (%s %s - %s)\n",
997 error_count, msg, msg_aux, label,
998 location, detail, other_detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300999 else
1000 edac_mc_printk(mci, KERN_WARNING,
Borislav Petkovf430d572012-09-10 18:36:09 +02001001 "%d CE %s%son %s (%s %s)\n",
1002 error_count, msg, msg_aux, label,
1003 location, detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001004 }
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001005 edac_inc_ce_error(mci, enable_per_layer_report, pos, error_count);
Alan Coxda9bb1d2006-01-18 17:44:13 -08001006
1007 if (mci->scrub_mode & SCRUB_SW_SRC) {
1008 /*
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001009 * Some memory controllers (called MCs below) can remap
1010 * memory so that it is still available at a different
1011 * address when PCI devices map into memory.
1012 * MC's that can't do this, lose the memory where PCI
1013 * devices are mapped. This mapping is MC-dependent
1014 * and so we call back into the MC driver for it to
1015 * map the MC page to a physical (CPU) page which can
1016 * then be mapped to a virtual page - which can then
1017 * be scrubbed.
1018 */
Alan Coxda9bb1d2006-01-18 17:44:13 -08001019 remapped_page = mci->ctl_page_to_phys ?
Douglas Thompson052dfb42007-07-19 01:50:13 -07001020 mci->ctl_page_to_phys(mci, page_frame_number) :
1021 page_frame_number;
Alan Coxda9bb1d2006-01-18 17:44:13 -08001022
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001023 edac_mc_scrub_block(remapped_page,
1024 offset_in_page, grain);
Alan Coxda9bb1d2006-01-18 17:44:13 -08001025 }
1026}
1027
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001028static void edac_ue_error(struct mem_ctl_info *mci,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001029 const u16 error_count,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001030 const int pos[EDAC_MAX_LAYERS],
1031 const char *msg,
1032 const char *location,
1033 const char *label,
1034 const char *detail,
1035 const char *other_detail,
1036 const bool enable_per_layer_report)
Alan Coxda9bb1d2006-01-18 17:44:13 -08001037{
Borislav Petkovf430d572012-09-10 18:36:09 +02001038 char *msg_aux = "";
1039
1040 if (*msg)
1041 msg_aux = " ";
1042
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001043 if (edac_mc_get_log_ue()) {
1044 if (other_detail && *other_detail)
1045 edac_mc_printk(mci, KERN_WARNING,
Borislav Petkovf430d572012-09-10 18:36:09 +02001046 "%d UE %s%son %s (%s %s - %s)\n",
1047 error_count, msg, msg_aux, label,
1048 location, detail, other_detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001049 else
1050 edac_mc_printk(mci, KERN_WARNING,
Borislav Petkovf430d572012-09-10 18:36:09 +02001051 "%d UE %s%son %s (%s %s)\n",
1052 error_count, msg, msg_aux, label,
1053 location, detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001054 }
Dave Petersone7ecd892006-03-26 01:38:52 -08001055
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001056 if (edac_mc_get_panic_on_ue()) {
1057 if (other_detail && *other_detail)
Borislav Petkovf430d572012-09-10 18:36:09 +02001058 panic("UE %s%son %s (%s%s - %s)\n",
1059 msg, msg_aux, label, location, detail, other_detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001060 else
Borislav Petkovf430d572012-09-10 18:36:09 +02001061 panic("UE %s%son %s (%s%s)\n",
1062 msg, msg_aux, label, location, detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001063 }
1064
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001065 edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count);
Alan Coxda9bb1d2006-01-18 17:44:13 -08001066}
1067
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001068#define OTHER_LABEL " or "
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001069
1070/**
1071 * edac_mc_handle_error - reports a memory event to userspace
1072 *
1073 * @type: severity of the error (CE/UE/Fatal)
1074 * @mci: a struct mem_ctl_info pointer
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001075 * @error_count: Number of errors of the same type
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001076 * @page_frame_number: mem page where the error occurred
1077 * @offset_in_page: offset of the error inside the page
1078 * @syndrome: ECC syndrome
1079 * @top_layer: Memory layer[0] position
1080 * @mid_layer: Memory layer[1] position
1081 * @low_layer: Memory layer[2] position
1082 * @msg: Message meaningful to the end users that
1083 * explains the event
1084 * @other_detail: Technical details about the event that
1085 * may help hardware manufacturers and
1086 * EDAC developers to analyse the event
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001087 */
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001088void edac_mc_handle_error(const enum hw_event_mc_err_type type,
1089 struct mem_ctl_info *mci,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001090 const u16 error_count,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001091 const unsigned long page_frame_number,
1092 const unsigned long offset_in_page,
1093 const unsigned long syndrome,
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001094 const int top_layer,
1095 const int mid_layer,
1096 const int low_layer,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001097 const char *msg,
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001098 const char *other_detail)
Alan Coxda9bb1d2006-01-18 17:44:13 -08001099{
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001100 /* FIXME: too much for stack: move it to some pre-alocated area */
1101 char detail[80], location[80];
1102 char label[(EDAC_MC_LABEL_LEN + 1 + sizeof(OTHER_LABEL)) * mci->tot_dimms];
1103 char *p;
1104 int row = -1, chan = -1;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001105 int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer };
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001106 int i;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001107 long grain;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001108 bool enable_per_layer_report = false;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001109 u8 grain_bits;
Alan Coxda9bb1d2006-01-18 17:44:13 -08001110
Joe Perches956b9ba2012-04-29 17:08:39 -03001111 edac_dbg(3, "MC%d\n", mci->mc_idx);
Alan Coxda9bb1d2006-01-18 17:44:13 -08001112
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001113 /*
1114 * Check if the event report is consistent and if the memory
1115 * location is known. If it is known, enable_per_layer_report will be
1116 * true, the DIMM(s) label info will be filled and the per-layer
1117 * error counters will be incremented.
1118 */
1119 for (i = 0; i < mci->n_layers; i++) {
1120 if (pos[i] >= (int)mci->layers[i].size) {
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001121
1122 edac_mc_printk(mci, KERN_ERR,
1123 "INTERNAL ERROR: %s value is out of range (%d >= %d)\n",
1124 edac_layer_name[mci->layers[i].type],
1125 pos[i], mci->layers[i].size);
1126 /*
1127 * Instead of just returning it, let's use what's
1128 * known about the error. The increment routines and
1129 * the DIMM filter logic will do the right thing by
1130 * pointing the likely damaged DIMMs.
1131 */
1132 pos[i] = -1;
1133 }
1134 if (pos[i] >= 0)
1135 enable_per_layer_report = true;
Alan Coxda9bb1d2006-01-18 17:44:13 -08001136 }
1137
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001138 /*
1139 * Get the dimm label/grain that applies to the match criteria.
1140 * As the error algorithm may not be able to point to just one memory
1141 * stick, the logic here will get all possible labels that could
1142 * pottentially be affected by the error.
1143 * On FB-DIMM memory controllers, for uncorrected errors, it is common
1144 * to have only the MC channel and the MC dimm (also called "branch")
1145 * but the channel is not known, as the memory is arranged in pairs,
1146 * where each memory belongs to a separate channel within the same
1147 * branch.
1148 */
1149 grain = 0;
1150 p = label;
1151 *p = '\0';
Borislav Petkov4da1b7b2012-09-10 17:57:44 +02001152
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001153 for (i = 0; i < mci->tot_dimms; i++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03001154 struct dimm_info *dimm = mci->dimms[i];
Dave Petersone7ecd892006-03-26 01:38:52 -08001155
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001156 if (top_layer >= 0 && top_layer != dimm->location[0])
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001157 continue;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001158 if (mid_layer >= 0 && mid_layer != dimm->location[1])
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001159 continue;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001160 if (low_layer >= 0 && low_layer != dimm->location[2])
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001161 continue;
1162
1163 /* get the max grain, over the error match range */
1164 if (dimm->grain > grain)
1165 grain = dimm->grain;
1166
1167 /*
1168 * If the error is memory-controller wide, there's no need to
1169 * seek for the affected DIMMs because the whole
1170 * channel/memory controller/... may be affected.
1171 * Also, don't show errors for empty DIMM slots.
1172 */
1173 if (enable_per_layer_report && dimm->nr_pages) {
1174 if (p != label) {
1175 strcpy(p, OTHER_LABEL);
1176 p += strlen(OTHER_LABEL);
1177 }
1178 strcpy(p, dimm->label);
1179 p += strlen(p);
1180 *p = '\0';
1181
1182 /*
1183 * get csrow/channel of the DIMM, in order to allow
1184 * incrementing the compat API counters
1185 */
Joe Perches956b9ba2012-04-29 17:08:39 -03001186 edac_dbg(4, "%s csrows map: (%d,%d)\n",
1187 mci->mem_is_per_rank ? "rank" : "dimm",
1188 dimm->csrow, dimm->cschannel);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001189 if (row == -1)
1190 row = dimm->csrow;
1191 else if (row >= 0 && row != dimm->csrow)
1192 row = -2;
1193
1194 if (chan == -1)
1195 chan = dimm->cschannel;
1196 else if (chan >= 0 && chan != dimm->cschannel)
1197 chan = -2;
1198 }
Alan Coxda9bb1d2006-01-18 17:44:13 -08001199 }
1200
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001201 if (!enable_per_layer_report) {
1202 strcpy(label, "any memory");
1203 } else {
Joe Perches956b9ba2012-04-29 17:08:39 -03001204 edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001205 if (p == label)
1206 strcpy(label, "unknown memory");
1207 if (type == HW_EVENT_ERR_CORRECTED) {
1208 if (row >= 0) {
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001209 mci->csrows[row]->ce_count += error_count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001210 if (chan >= 0)
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001211 mci->csrows[row]->channels[chan]->ce_count += error_count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001212 }
1213 } else
1214 if (row >= 0)
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001215 mci->csrows[row]->ue_count += error_count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001216 }
Alan Coxda9bb1d2006-01-18 17:44:13 -08001217
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001218 /* Fill the RAM location data */
1219 p = location;
Borislav Petkov4da1b7b2012-09-10 17:57:44 +02001220
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001221 for (i = 0; i < mci->n_layers; i++) {
1222 if (pos[i] < 0)
1223 continue;
1224
1225 p += sprintf(p, "%s:%d ",
1226 edac_layer_name[mci->layers[i].type],
1227 pos[i]);
1228 }
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001229 if (p > location)
1230 *(p - 1) = '\0';
1231
1232 /* Report the error via the trace interface */
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001233 grain_bits = fls_long(grain) + 1;
1234 trace_mc_event(type, msg, label, error_count,
1235 mci->mc_idx, top_layer, mid_layer, low_layer,
1236 PAGES_TO_MiB(page_frame_number) | offset_in_page,
1237 grain_bits, syndrome, other_detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001238
1239 /* Memory type dependent details about the error */
1240 if (type == HW_EVENT_ERR_CORRECTED) {
1241 snprintf(detail, sizeof(detail),
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001242 "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx",
Douglas Thompson052dfb42007-07-19 01:50:13 -07001243 page_frame_number, offset_in_page,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001244 grain, syndrome);
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001245 edac_ce_error(mci, error_count, pos, msg, location, label,
1246 detail, other_detail, enable_per_layer_report,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001247 page_frame_number, offset_in_page, grain);
1248 } else {
1249 snprintf(detail, sizeof(detail),
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001250 "page:0x%lx offset:0x%lx grain:%ld",
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001251 page_frame_number, offset_in_page, grain);
Alan Coxda9bb1d2006-01-18 17:44:13 -08001252
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001253 edac_ue_error(mci, error_count, pos, msg, location, label,
1254 detail, other_detail, enable_per_layer_report);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001255 }
Alan Coxda9bb1d2006-01-18 17:44:13 -08001256}
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001257EXPORT_SYMBOL_GPL(edac_mc_handle_error);