blob: 78d8c7d6e76a11d29342558a178b4858e3cfde88 [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 Chehab6e84d352012-04-30 10:24:43 -030045unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf,
46 unsigned len)
47{
48 struct mem_ctl_info *mci = dimm->mci;
49 int i, n, count = 0;
50 char *p = buf;
51
52 for (i = 0; i < mci->n_layers; i++) {
53 n = snprintf(p, len, "%s %d ",
54 edac_layer_name[mci->layers[i].type],
55 dimm->location[i]);
56 p += n;
57 len -= n;
58 count += n;
59 if (!len)
60 break;
61 }
62
63 return count;
64}
65
Alan Coxda9bb1d2006-01-18 17:44:13 -080066#ifdef CONFIG_EDAC_DEBUG
67
Mauro Carvalho Chehaba4b4be32012-01-27 10:26:13 -030068static void edac_mc_dump_channel(struct rank_info *chan)
Alan Coxda9bb1d2006-01-18 17:44:13 -080069{
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030070 edac_dbg(4, " channel->chan_idx = %d\n", chan->chan_idx);
71 edac_dbg(4, " channel = %p\n", chan);
72 edac_dbg(4, " channel->csrow = %p\n", chan->csrow);
73 edac_dbg(4, " channel->dimm = %p\n", chan->dimm);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -030074}
75
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030076static void edac_mc_dump_dimm(struct dimm_info *dimm, int number)
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -030077{
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030078 char location[80];
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -030079
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030080 edac_dimm_info_location(dimm, location, sizeof(location));
81
82 edac_dbg(4, "%s%i: %smapped as virtual row %d, chan %d\n",
83 dimm->mci->mem_is_per_rank ? "rank" : "dimm",
84 number, location, dimm->csrow, dimm->cschannel);
85 edac_dbg(4, " dimm = %p\n", dimm);
86 edac_dbg(4, " dimm->label = '%s'\n", dimm->label);
87 edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages);
88 edac_dbg(4, " dimm->grain = %d\n", dimm->grain);
89 edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages);
Alan Coxda9bb1d2006-01-18 17:44:13 -080090}
91
Adrian Bunk2da1c112007-07-19 01:49:32 -070092static void edac_mc_dump_csrow(struct csrow_info *csrow)
Alan Coxda9bb1d2006-01-18 17:44:13 -080093{
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -030094 edac_dbg(4, "csrow->csrow_idx = %d\n", csrow->csrow_idx);
95 edac_dbg(4, " csrow = %p\n", csrow);
96 edac_dbg(4, " csrow->first_page = 0x%lx\n", csrow->first_page);
97 edac_dbg(4, " csrow->last_page = 0x%lx\n", csrow->last_page);
98 edac_dbg(4, " csrow->page_mask = 0x%lx\n", csrow->page_mask);
99 edac_dbg(4, " csrow->nr_channels = %d\n", csrow->nr_channels);
100 edac_dbg(4, " csrow->channels = %p\n", csrow->channels);
101 edac_dbg(4, " csrow->mci = %p\n", csrow->mci);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800102}
103
Adrian Bunk2da1c112007-07-19 01:49:32 -0700104static void edac_mc_dump_mci(struct mem_ctl_info *mci)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800105{
Joe Perches956b9ba2012-04-29 17:08:39 -0300106 edac_dbg(3, "\tmci = %p\n", mci);
107 edac_dbg(3, "\tmci->mtype_cap = %lx\n", mci->mtype_cap);
108 edac_dbg(3, "\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
109 edac_dbg(3, "\tmci->edac_cap = %lx\n", mci->edac_cap);
110 edac_dbg(4, "\tmci->edac_check = %p\n", mci->edac_check);
111 edac_dbg(3, "\tmci->nr_csrows = %d, csrows = %p\n",
112 mci->nr_csrows, mci->csrows);
113 edac_dbg(3, "\tmci->nr_dimms = %d, dimms = %p\n",
114 mci->tot_dimms, mci->dimms);
115 edac_dbg(3, "\tdev = %p\n", mci->pdev);
116 edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n",
117 mci->mod_name, mci->ctl_name);
118 edac_dbg(3, "\tpvt_info = %p\n\n", mci->pvt_info);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800119}
120
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200121#endif /* CONFIG_EDAC_DEBUG */
122
Borislav Petkov239642f2009-11-12 15:33:16 +0100123/*
124 * keep those in sync with the enum mem_type
125 */
126const char *edac_mem_types[] = {
127 "Empty csrow",
128 "Reserved csrow type",
129 "Unknown csrow type",
130 "Fast page mode RAM",
131 "Extended data out RAM",
132 "Burst Extended data out RAM",
133 "Single data rate SDRAM",
134 "Registered single data rate SDRAM",
135 "Double data rate SDRAM",
136 "Registered Double data rate SDRAM",
137 "Rambus DRAM",
138 "Unbuffered DDR2 RAM",
139 "Fully buffered DDR2",
140 "Registered DDR2 RAM",
141 "Rambus XDR",
142 "Unbuffered DDR3 RAM",
143 "Registered DDR3 RAM",
144};
145EXPORT_SYMBOL_GPL(edac_mem_types);
146
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300147/**
148 * edac_align_ptr - Prepares the pointer offsets for a single-shot allocation
149 * @p: pointer to a pointer with the memory offset to be used. At
150 * return, this will be incremented to point to the next offset
151 * @size: Size of the data structure to be reserved
152 * @n_elems: Number of elements that should be reserved
Alan Coxda9bb1d2006-01-18 17:44:13 -0800153 *
154 * If 'size' is a constant, the compiler will optimize this whole function
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300155 * down to either a no-op or the addition of a constant to the value of '*p'.
156 *
157 * The 'p' pointer is absolutely needed to keep the proper advancing
158 * further in memory to the proper offsets when allocating the struct along
159 * with its embedded structs, as edac_device_alloc_ctl_info() does it
160 * above, for example.
161 *
162 * At return, the pointer 'p' will be incremented to be used on a next call
163 * to this function.
Alan Coxda9bb1d2006-01-18 17:44:13 -0800164 */
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300165void *edac_align_ptr(void **p, unsigned size, int n_elems)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800166{
167 unsigned align, r;
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300168 void *ptr = *p;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800169
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300170 *p += size * n_elems;
171
172 /*
173 * 'p' can possibly be an unaligned item X such that sizeof(X) is
174 * 'size'. Adjust 'p' so that its alignment is at least as
175 * stringent as what the compiler would provide for X and return
176 * the aligned result.
177 * Here we assume that the alignment of a "long long" is the most
Alan Coxda9bb1d2006-01-18 17:44:13 -0800178 * stringent alignment that the compiler will ever provide by default.
179 * As far as I know, this is a reasonable assumption.
180 */
181 if (size > sizeof(long))
182 align = sizeof(long long);
183 else if (size > sizeof(int))
184 align = sizeof(long);
185 else if (size > sizeof(short))
186 align = sizeof(int);
187 else if (size > sizeof(char))
188 align = sizeof(short);
189 else
Douglas Thompson079708b2007-07-19 01:49:58 -0700190 return (char *)ptr;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800191
Chris Metcalf8447c4d12012-06-06 13:11:05 -0400192 r = (unsigned long)p % align;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800193
194 if (r == 0)
Douglas Thompson079708b2007-07-19 01:49:58 -0700195 return (char *)ptr;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800196
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300197 *p += align - r;
198
Douglas Thompson7391c6d2007-07-19 01:50:21 -0700199 return (void *)(((unsigned long)ptr) + align - r);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800200}
201
Shaun Ruffellfaa2ad02012-09-22 20:26:38 -0500202static void _edac_mc_free(struct mem_ctl_info *mci)
203{
204 int i, chn, row;
205 struct csrow_info *csr;
206 const unsigned int tot_dimms = mci->tot_dimms;
207 const unsigned int tot_channels = mci->num_cschannel;
208 const unsigned int tot_csrows = mci->nr_csrows;
209
210 if (mci->dimms) {
211 for (i = 0; i < tot_dimms; i++)
212 kfree(mci->dimms[i]);
213 kfree(mci->dimms);
214 }
215 if (mci->csrows) {
216 for (row = 0; row < tot_csrows; row++) {
217 csr = mci->csrows[row];
218 if (csr) {
219 if (csr->channels) {
220 for (chn = 0; chn < tot_channels; chn++)
221 kfree(csr->channels[chn]);
222 kfree(csr->channels);
223 }
224 kfree(csr);
225 }
226 }
227 kfree(mci->csrows);
228 }
229 kfree(mci);
230}
231
Alan Coxda9bb1d2006-01-18 17:44:13 -0800232/**
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300233 * edac_mc_alloc: Allocate and partially fill a struct mem_ctl_info structure
234 * @mc_num: Memory controller number
235 * @n_layers: Number of MC hierarchy layers
236 * layers: Describes each layer as seen by the Memory Controller
237 * @size_pvt: size of private storage needed
238 *
Alan Coxda9bb1d2006-01-18 17:44:13 -0800239 *
240 * Everything is kmalloc'ed as one big chunk - more efficient.
241 * Only can be used if all structures have the same lifetime - otherwise
242 * you have to allocate and initialize your own structures.
243 *
244 * Use edac_mc_free() to free mc structures allocated by this function.
245 *
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300246 * NOTE: drivers handle multi-rank memories in different ways: in some
247 * drivers, one multi-rank memory stick is mapped as one entry, while, in
248 * others, a single multi-rank memory stick would be mapped into several
249 * entries. Currently, this function will allocate multiple struct dimm_info
250 * on such scenarios, as grouping the multiple ranks require drivers change.
251 *
Alan Coxda9bb1d2006-01-18 17:44:13 -0800252 * Returns:
Mauro Carvalho Chehabca0907b2012-05-02 14:37:00 -0300253 * On failure: NULL
254 * On success: struct mem_ctl_info pointer
Alan Coxda9bb1d2006-01-18 17:44:13 -0800255 */
Mauro Carvalho Chehabca0907b2012-05-02 14:37:00 -0300256struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
257 unsigned n_layers,
258 struct edac_mc_layer *layers,
259 unsigned sz_pvt)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800260{
261 struct mem_ctl_info *mci;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300262 struct edac_mc_layer *layer;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300263 struct csrow_info *csr;
264 struct rank_info *chan;
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300265 struct dimm_info *dimm;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300266 u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS];
267 unsigned pos[EDAC_MAX_LAYERS];
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300268 unsigned size, tot_dimms = 1, count = 1;
269 unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0;
Mauro Carvalho Chehab5926ff52012-02-09 11:05:20 -0300270 void *pvt, *p, *ptr = NULL;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300271 int i, j, row, chn, n, len, off;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300272 bool per_rank = false;
273
274 BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0);
275 /*
276 * Calculate the total amount of dimms and csrows/cschannels while
277 * in the old API emulation mode
278 */
279 for (i = 0; i < n_layers; i++) {
280 tot_dimms *= layers[i].size;
281 if (layers[i].is_virt_csrow)
282 tot_csrows *= layers[i].size;
283 else
284 tot_channels *= layers[i].size;
285
286 if (layers[i].type == EDAC_MC_LAYER_CHIP_SELECT)
287 per_rank = true;
288 }
Alan Coxda9bb1d2006-01-18 17:44:13 -0800289
290 /* Figure out the offsets of the various items from the start of an mc
291 * structure. We want the alignment of each item to be at least as
292 * stringent as what the compiler would provide if we could simply
293 * hardcode everything into a single struct.
294 */
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300295 mci = edac_align_ptr(&ptr, sizeof(*mci), 1);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300296 layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300297 for (i = 0; i < n_layers; i++) {
298 count *= layers[i].size;
Joe Perches956b9ba2012-04-29 17:08:39 -0300299 edac_dbg(4, "errcount layer %d size %d\n", i, count);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300300 ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
301 ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
302 tot_errcount += 2 * count;
303 }
304
Joe Perches956b9ba2012-04-29 17:08:39 -0300305 edac_dbg(4, "allocating %d error counters\n", tot_errcount);
Mauro Carvalho Chehab93e4fe62012-04-16 10:18:12 -0300306 pvt = edac_align_ptr(&ptr, sz_pvt, 1);
Douglas Thompson079708b2007-07-19 01:49:58 -0700307 size = ((unsigned long)pvt) + sz_pvt;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800308
Joe Perches956b9ba2012-04-29 17:08:39 -0300309 edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n",
310 size,
311 tot_dimms,
312 per_rank ? "ranks" : "dimms",
313 tot_csrows * tot_channels);
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300314
Doug Thompson8096cfa2007-07-19 01:50:27 -0700315 mci = kzalloc(size, GFP_KERNEL);
316 if (mci == NULL)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800317 return NULL;
318
319 /* Adjust pointers so they point within the memory we just allocated
320 * rather than an imaginary chunk of memory located at address 0.
321 */
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300322 layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer));
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300323 for (i = 0; i < n_layers; i++) {
324 mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i]));
325 mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i]));
326 }
Douglas Thompson079708b2007-07-19 01:49:58 -0700327 pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800328
Doug Thompsonb8f6f972007-07-19 01:50:26 -0700329 /* setup index and various internal pointers */
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300330 mci->mc_idx = mc_num;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300331 mci->tot_dimms = tot_dimms;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800332 mci->pvt_info = pvt;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300333 mci->n_layers = n_layers;
334 mci->layers = layer;
335 memcpy(mci->layers, layers, sizeof(*layer) * n_layers);
336 mci->nr_csrows = tot_csrows;
337 mci->num_cschannel = tot_channels;
338 mci->mem_is_per_rank = per_rank;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800339
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300340 /*
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300341 * Alocate and fill the csrow/channels structs
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300342 */
Joe Perchesd3d09e12013-01-26 11:24:00 -0800343 mci->csrows = kcalloc(tot_csrows, sizeof(*mci->csrows), GFP_KERNEL);
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300344 if (!mci->csrows)
345 goto error;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300346 for (row = 0; row < tot_csrows; row++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300347 csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL);
348 if (!csr)
349 goto error;
350 mci->csrows[row] = csr;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300351 csr->csrow_idx = row;
352 csr->mci = mci;
353 csr->nr_channels = tot_channels;
Joe Perchesd3d09e12013-01-26 11:24:00 -0800354 csr->channels = kcalloc(tot_channels, sizeof(*csr->channels),
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300355 GFP_KERNEL);
356 if (!csr->channels)
357 goto error;
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300358
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300359 for (chn = 0; chn < tot_channels; chn++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300360 chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL);
361 if (!chan)
362 goto error;
363 csr->channels[chn] = chan;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800364 chan->chan_idx = chn;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300365 chan->csrow = csr;
366 }
367 }
Mauro Carvalho Chehaba7d7d2e2012-01-27 14:12:32 -0300368
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300369 /*
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300370 * Allocate and fill the dimm structs
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300371 */
Joe Perchesd3d09e12013-01-26 11:24:00 -0800372 mci->dimms = kcalloc(tot_dimms, sizeof(*mci->dimms), GFP_KERNEL);
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300373 if (!mci->dimms)
374 goto error;
375
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300376 memset(&pos, 0, sizeof(pos));
377 row = 0;
378 chn = 0;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300379 for (i = 0; i < tot_dimms; i++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300380 chan = mci->csrows[row]->channels[chn];
381 off = EDAC_DIMM_OFF(layer, n_layers, pos[0], pos[1], pos[2]);
382 if (off < 0 || off >= tot_dimms) {
383 edac_mc_printk(mci, KERN_ERR, "EDAC core bug: EDAC_DIMM_OFF is trying to do an illegal data access\n");
384 goto error;
385 }
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300386
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300387 dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL);
Dan Carpenter08a4a132012-05-18 15:51:02 +0300388 if (!dimm)
389 goto error;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300390 mci->dimms[off] = dimm;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300391 dimm->mci = mci;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300392
Mauro Carvalho Chehab5926ff52012-02-09 11:05:20 -0300393 /*
394 * Copy DIMM location and initialize it.
395 */
396 len = sizeof(dimm->label);
397 p = dimm->label;
398 n = snprintf(p, len, "mc#%u", mc_num);
399 p += n;
400 len -= n;
401 for (j = 0; j < n_layers; j++) {
402 n = snprintf(p, len, "%s#%u",
403 edac_layer_name[layers[j].type],
404 pos[j]);
405 p += n;
406 len -= n;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300407 dimm->location[j] = pos[j];
408
Mauro Carvalho Chehab5926ff52012-02-09 11:05:20 -0300409 if (len <= 0)
410 break;
411 }
412
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300413 /* Link it to the csrows old API data */
414 chan->dimm = dimm;
415 dimm->csrow = row;
416 dimm->cschannel = chn;
417
418 /* Increment csrow location */
Mauro Carvalho Chehab24bef662012-10-24 10:30:01 -0200419 if (layers[0].is_virt_csrow) {
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300420 chn++;
Mauro Carvalho Chehab24bef662012-10-24 10:30:01 -0200421 if (chn == tot_channels) {
422 chn = 0;
423 row++;
424 }
425 } else {
426 row++;
427 if (row == tot_csrows) {
428 row = 0;
429 chn++;
430 }
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300431 }
432
433 /* Increment dimm location */
434 for (j = n_layers - 1; j >= 0; j--) {
435 pos[j]++;
436 if (pos[j] < layers[j].size)
437 break;
438 pos[j] = 0;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800439 }
440 }
441
Dave Jiang81d87cb2007-07-19 01:49:52 -0700442 mci->op_state = OP_ALLOC;
Doug Thompson8096cfa2007-07-19 01:50:27 -0700443
Alan Coxda9bb1d2006-01-18 17:44:13 -0800444 return mci;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300445
446error:
Shaun Ruffellfaa2ad02012-09-22 20:26:38 -0500447 _edac_mc_free(mci);
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300448
449 return NULL;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800450}
Dave Peterson91105402006-03-26 01:38:55 -0800451EXPORT_SYMBOL_GPL(edac_mc_alloc);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800452
Alan Coxda9bb1d2006-01-18 17:44:13 -0800453/**
Doug Thompson8096cfa2007-07-19 01:50:27 -0700454 * edac_mc_free
455 * 'Free' a previously allocated 'mci' structure
Alan Coxda9bb1d2006-01-18 17:44:13 -0800456 * @mci: pointer to a struct mem_ctl_info structure
Alan Coxda9bb1d2006-01-18 17:44:13 -0800457 */
458void edac_mc_free(struct mem_ctl_info *mci)
459{
Joe Perches956b9ba2012-04-29 17:08:39 -0300460 edac_dbg(1, "\n");
Mauro Carvalho Chehabbbc560a2010-08-16 18:22:43 -0300461
Shaun Ruffellfaa2ad02012-09-22 20:26:38 -0500462 /* If we're not yet registered with sysfs free only what was allocated
463 * in edac_mc_alloc().
464 */
465 if (!device_is_registered(&mci->dev)) {
466 _edac_mc_free(mci);
467 return;
468 }
469
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300470 /* the mci instance is freed here, when the sysfs object is dropped */
Mauro Carvalho Chehab7a623c02012-04-16 16:41:11 -0300471 edac_unregister_sysfs(mci);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800472}
Dave Peterson91105402006-03-26 01:38:55 -0800473EXPORT_SYMBOL_GPL(edac_mc_free);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800474
Doug Thompsonbce19682007-07-26 10:41:14 -0700475
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300476/**
Doug Thompsonbce19682007-07-26 10:41:14 -0700477 * find_mci_by_dev
478 *
479 * scan list of controllers looking for the one that manages
480 * the 'dev' device
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300481 * @dev: pointer to a struct device related with the MCI
Doug Thompsonbce19682007-07-26 10:41:14 -0700482 */
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300483struct mem_ctl_info *find_mci_by_dev(struct device *dev)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800484{
485 struct mem_ctl_info *mci;
486 struct list_head *item;
487
Joe Perches956b9ba2012-04-29 17:08:39 -0300488 edac_dbg(3, "\n");
Alan Coxda9bb1d2006-01-18 17:44:13 -0800489
490 list_for_each(item, &mc_devices) {
491 mci = list_entry(item, struct mem_ctl_info, link);
492
Mauro Carvalho Chehabfd687502012-03-16 07:44:18 -0300493 if (mci->pdev == dev)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800494 return mci;
495 }
496
497 return NULL;
498}
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300499EXPORT_SYMBOL_GPL(find_mci_by_dev);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800500
Dave Jiang81d87cb2007-07-19 01:49:52 -0700501/*
502 * handler for EDAC to check if NMI type handler has asserted interrupt
503 */
504static int edac_mc_assert_error_check_and_clear(void)
505{
Dave Jiang66ee2f92007-07-19 01:49:54 -0700506 int old_state;
Dave Jiang81d87cb2007-07-19 01:49:52 -0700507
Douglas Thompson079708b2007-07-19 01:49:58 -0700508 if (edac_op_state == EDAC_OPSTATE_POLL)
Dave Jiang81d87cb2007-07-19 01:49:52 -0700509 return 1;
510
Dave Jiang66ee2f92007-07-19 01:49:54 -0700511 old_state = edac_err_assert;
512 edac_err_assert = 0;
Dave Jiang81d87cb2007-07-19 01:49:52 -0700513
Dave Jiang66ee2f92007-07-19 01:49:54 -0700514 return old_state;
Dave Jiang81d87cb2007-07-19 01:49:52 -0700515}
516
517/*
518 * edac_mc_workq_function
519 * performs the operation scheduled by a workq request
520 */
Dave Jiang81d87cb2007-07-19 01:49:52 -0700521static void edac_mc_workq_function(struct work_struct *work_req)
522{
Jean Delvarefbeb4382009-04-13 14:40:21 -0700523 struct delayed_work *d_work = to_delayed_work(work_req);
Dave Jiang81d87cb2007-07-19 01:49:52 -0700524 struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
Dave Jiang81d87cb2007-07-19 01:49:52 -0700525
526 mutex_lock(&mem_ctls_mutex);
527
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700528 /* if this control struct has movd to offline state, we are done */
529 if (mci->op_state == OP_OFFLINE) {
530 mutex_unlock(&mem_ctls_mutex);
531 return;
532 }
533
Dave Jiang81d87cb2007-07-19 01:49:52 -0700534 /* Only poll controllers that are running polled and have a check */
535 if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL))
536 mci->edac_check(mci);
537
Dave Jiang81d87cb2007-07-19 01:49:52 -0700538 mutex_unlock(&mem_ctls_mutex);
539
540 /* Reschedule */
Dave Jiang4de78c62007-07-19 01:49:54 -0700541 queue_delayed_work(edac_workqueue, &mci->work,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700542 msecs_to_jiffies(edac_mc_get_poll_msec()));
Dave Jiang81d87cb2007-07-19 01:49:52 -0700543}
544
545/*
546 * edac_mc_workq_setup
547 * initialize a workq item for this mci
548 * passing in the new delay period in msec
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700549 *
550 * locking model:
551 *
552 * called with the mem_ctls_mutex held
Dave Jiang81d87cb2007-07-19 01:49:52 -0700553 */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700554static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
Dave Jiang81d87cb2007-07-19 01:49:52 -0700555{
Joe Perches956b9ba2012-04-29 17:08:39 -0300556 edac_dbg(0, "\n");
Dave Jiang81d87cb2007-07-19 01:49:52 -0700557
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700558 /* if this instance is not in the POLL state, then simply return */
559 if (mci->op_state != OP_RUNNING_POLL)
560 return;
561
Dave Jiang81d87cb2007-07-19 01:49:52 -0700562 INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
Tejun Heo41f63c52012-08-03 10:30:47 -0700563 mod_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
Dave Jiang81d87cb2007-07-19 01:49:52 -0700564}
565
566/*
567 * edac_mc_workq_teardown
568 * stop the workq processing on this mci
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700569 *
570 * locking model:
571 *
572 * called WITHOUT lock held
Dave Jiang81d87cb2007-07-19 01:49:52 -0700573 */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700574static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
Dave Jiang81d87cb2007-07-19 01:49:52 -0700575{
576 int status;
577
Borislav Petkov00740c52010-09-26 12:42:23 +0200578 if (mci->op_state != OP_RUNNING_POLL)
579 return;
580
Doug Thompsonbce19682007-07-26 10:41:14 -0700581 status = cancel_delayed_work(&mci->work);
582 if (status == 0) {
Joe Perches956b9ba2012-04-29 17:08:39 -0300583 edac_dbg(0, "not canceled, flush the queue\n");
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700584
Doug Thompsonbce19682007-07-26 10:41:14 -0700585 /* workq instance might be running, wait for it */
586 flush_workqueue(edac_workqueue);
Dave Jiang81d87cb2007-07-19 01:49:52 -0700587 }
588}
589
590/*
Doug Thompsonbce19682007-07-26 10:41:14 -0700591 * edac_mc_reset_delay_period(unsigned long value)
592 *
593 * user space has updated our poll period value, need to
594 * reset our workq delays
Dave Jiang81d87cb2007-07-19 01:49:52 -0700595 */
Doug Thompsonbce19682007-07-26 10:41:14 -0700596void edac_mc_reset_delay_period(int value)
Dave Jiang81d87cb2007-07-19 01:49:52 -0700597{
Doug Thompsonbce19682007-07-26 10:41:14 -0700598 struct mem_ctl_info *mci;
599 struct list_head *item;
Dave Jiang81d87cb2007-07-19 01:49:52 -0700600
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700601 mutex_lock(&mem_ctls_mutex);
602
Doug Thompsonbce19682007-07-26 10:41:14 -0700603 list_for_each(item, &mc_devices) {
604 mci = list_entry(item, struct mem_ctl_info, link);
605
606 edac_mc_workq_setup(mci, (unsigned long) value);
607 }
Dave Jiang81d87cb2007-07-19 01:49:52 -0700608
609 mutex_unlock(&mem_ctls_mutex);
610}
611
Doug Thompsonbce19682007-07-26 10:41:14 -0700612
613
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700614/* Return 0 on success, 1 on failure.
615 * Before calling this function, caller must
616 * assign a unique value to mci->mc_idx.
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700617 *
618 * locking model:
619 *
620 * called with the mem_ctls_mutex lock held
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700621 */
Douglas Thompson079708b2007-07-19 01:49:58 -0700622static int add_mc_to_global_list(struct mem_ctl_info *mci)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800623{
624 struct list_head *item, *insert_before;
625 struct mem_ctl_info *p;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800626
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700627 insert_before = &mc_devices;
628
Mauro Carvalho Chehabfd687502012-03-16 07:44:18 -0300629 p = find_mci_by_dev(mci->pdev);
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700630 if (unlikely(p != NULL))
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700631 goto fail0;
632
633 list_for_each(item, &mc_devices) {
634 p = list_entry(item, struct mem_ctl_info, link);
635
636 if (p->mc_idx >= mci->mc_idx) {
637 if (unlikely(p->mc_idx == mci->mc_idx))
638 goto fail1;
639
640 insert_before = item;
641 break;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800642 }
Alan Coxda9bb1d2006-01-18 17:44:13 -0800643 }
644
645 list_add_tail_rcu(&mci->link, insert_before);
Dave Jiangc0d12172007-07-19 01:49:46 -0700646 atomic_inc(&edac_handlers);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800647 return 0;
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700648
Douglas Thompson052dfb42007-07-19 01:50:13 -0700649fail0:
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700650 edac_printk(KERN_WARNING, EDAC_MC,
Mauro Carvalho Chehabfd687502012-03-16 07:44:18 -0300651 "%s (%s) %s %s already assigned %d\n", dev_name(p->pdev),
Stephen Rothwell17aa7e02008-05-05 13:54:19 +1000652 edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700653 return 1;
654
Douglas Thompson052dfb42007-07-19 01:50:13 -0700655fail1:
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700656 edac_printk(KERN_WARNING, EDAC_MC,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700657 "bug in low-level driver: attempt to assign\n"
658 " duplicate mc_idx %d in %s()\n", p->mc_idx, __func__);
Doug Thompson2d7bbb92006-06-30 01:56:08 -0700659 return 1;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800660}
661
Dave Petersone7ecd892006-03-26 01:38:52 -0800662static void del_mc_from_global_list(struct mem_ctl_info *mci)
Dave Petersona1d03fc2006-03-26 01:38:46 -0800663{
Dave Jiangc0d12172007-07-19 01:49:46 -0700664 atomic_dec(&edac_handlers);
Dave Petersona1d03fc2006-03-26 01:38:46 -0800665 list_del_rcu(&mci->link);
Lai Jiangshane2e77092011-05-26 16:25:58 -0700666
667 /* these are for safe removal of devices from global list while
668 * NMI handlers may be traversing list
669 */
670 synchronize_rcu();
671 INIT_LIST_HEAD(&mci->link);
Dave Petersona1d03fc2006-03-26 01:38:46 -0800672}
673
Alan Coxda9bb1d2006-01-18 17:44:13 -0800674/**
Douglas Thompson5da08312007-07-19 01:49:31 -0700675 * edac_mc_find: Search for a mem_ctl_info structure whose index is 'idx'.
676 *
677 * If found, return a pointer to the structure.
678 * Else return NULL.
679 *
680 * Caller must hold mem_ctls_mutex.
681 */
Douglas Thompson079708b2007-07-19 01:49:58 -0700682struct mem_ctl_info *edac_mc_find(int idx)
Douglas Thompson5da08312007-07-19 01:49:31 -0700683{
684 struct list_head *item;
685 struct mem_ctl_info *mci;
686
687 list_for_each(item, &mc_devices) {
688 mci = list_entry(item, struct mem_ctl_info, link);
689
690 if (mci->mc_idx >= idx) {
691 if (mci->mc_idx == idx)
692 return mci;
693
694 break;
695 }
696 }
697
698 return NULL;
699}
700EXPORT_SYMBOL(edac_mc_find);
701
702/**
Dave Peterson472678e2006-03-26 01:38:49 -0800703 * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and
704 * create sysfs entries associated with mci structure
Alan Coxda9bb1d2006-01-18 17:44:13 -0800705 * @mci: pointer to the mci structure to be added to the list
706 *
707 * Return:
708 * 0 Success
709 * !0 Failure
710 */
711
712/* FIXME - should a warning be printed if no error detection? correction? */
Doug Thompsonb8f6f972007-07-19 01:50:26 -0700713int edac_mc_add_mc(struct mem_ctl_info *mci)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800714{
Joe Perches956b9ba2012-04-29 17:08:39 -0300715 edac_dbg(0, "\n");
Doug Thompsonb8f6f972007-07-19 01:50:26 -0700716
Alan Coxda9bb1d2006-01-18 17:44:13 -0800717#ifdef CONFIG_EDAC_DEBUG
718 if (edac_debug_level >= 3)
719 edac_mc_dump_mci(mci);
Dave Petersone7ecd892006-03-26 01:38:52 -0800720
Alan Coxda9bb1d2006-01-18 17:44:13 -0800721 if (edac_debug_level >= 4) {
722 int i;
723
724 for (i = 0; i < mci->nr_csrows; i++) {
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -0300725 struct csrow_info *csrow = mci->csrows[i];
726 u32 nr_pages = 0;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800727 int j;
Dave Petersone7ecd892006-03-26 01:38:52 -0800728
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -0300729 for (j = 0; j < csrow->nr_channels; j++)
730 nr_pages += csrow->channels[j]->dimm->nr_pages;
731 if (!nr_pages)
732 continue;
733 edac_mc_dump_csrow(csrow);
734 for (j = 0; j < csrow->nr_channels; j++)
735 if (csrow->channels[j]->dimm->nr_pages)
736 edac_mc_dump_channel(csrow->channels[j]);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800737 }
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300738 for (i = 0; i < mci->tot_dimms; i++)
Mauro Carvalho Chehab6e84d352012-04-30 10:24:43 -0300739 if (mci->dimms[i]->nr_pages)
740 edac_mc_dump_dimm(mci->dimms[i], i);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800741 }
742#endif
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700743 mutex_lock(&mem_ctls_mutex);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800744
745 if (add_mc_to_global_list(mci))
Dave Peterson028a7b62006-03-26 01:38:47 -0800746 goto fail0;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800747
748 /* set load time so that error rate can be tracked */
749 mci->start_time = jiffies;
750
eric wollesen9794f332007-02-12 00:53:08 -0800751 if (edac_create_sysfs_mci_device(mci)) {
752 edac_mc_printk(mci, KERN_WARNING,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700753 "failed to create sysfs device\n");
eric wollesen9794f332007-02-12 00:53:08 -0800754 goto fail1;
755 }
Alan Coxda9bb1d2006-01-18 17:44:13 -0800756
Dave Jiang81d87cb2007-07-19 01:49:52 -0700757 /* If there IS a check routine, then we are running POLLED */
758 if (mci->edac_check != NULL) {
759 /* This instance is NOW RUNNING */
760 mci->op_state = OP_RUNNING_POLL;
761
762 edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
763 } else {
764 mci->op_state = OP_RUNNING_INTERRUPT;
765 }
766
Alan Coxda9bb1d2006-01-18 17:44:13 -0800767 /* Report action taken */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700768 edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':"
Stephen Rothwell17aa7e02008-05-05 13:54:19 +1000769 " DEV %s\n", mci->mod_name, mci->ctl_name, edac_dev_name(mci));
Alan Coxda9bb1d2006-01-18 17:44:13 -0800770
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700771 mutex_unlock(&mem_ctls_mutex);
Dave Peterson028a7b62006-03-26 01:38:47 -0800772 return 0;
773
Douglas Thompson052dfb42007-07-19 01:50:13 -0700774fail1:
Dave Peterson028a7b62006-03-26 01:38:47 -0800775 del_mc_from_global_list(mci);
776
Douglas Thompson052dfb42007-07-19 01:50:13 -0700777fail0:
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700778 mutex_unlock(&mem_ctls_mutex);
Dave Peterson028a7b62006-03-26 01:38:47 -0800779 return 1;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800780}
Dave Peterson91105402006-03-26 01:38:55 -0800781EXPORT_SYMBOL_GPL(edac_mc_add_mc);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800782
Alan Coxda9bb1d2006-01-18 17:44:13 -0800783/**
Dave Peterson472678e2006-03-26 01:38:49 -0800784 * edac_mc_del_mc: Remove sysfs entries for specified mci structure and
785 * remove mci structure from global list
Doug Thompson37f04582006-06-30 01:56:07 -0700786 * @pdev: Pointer to 'struct device' representing mci structure to remove.
Alan Coxda9bb1d2006-01-18 17:44:13 -0800787 *
Dave Peterson18dbc332006-03-26 01:38:50 -0800788 * Return pointer to removed mci structure, or NULL if device not found.
Alan Coxda9bb1d2006-01-18 17:44:13 -0800789 */
Douglas Thompson079708b2007-07-19 01:49:58 -0700790struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800791{
Dave Peterson18dbc332006-03-26 01:38:50 -0800792 struct mem_ctl_info *mci;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800793
Joe Perches956b9ba2012-04-29 17:08:39 -0300794 edac_dbg(0, "\n");
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700795
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700796 mutex_lock(&mem_ctls_mutex);
Dave Peterson18dbc332006-03-26 01:38:50 -0800797
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700798 /* find the requested mci struct in the global list */
799 mci = find_mci_by_dev(dev);
800 if (mci == NULL) {
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700801 mutex_unlock(&mem_ctls_mutex);
Dave Peterson18dbc332006-03-26 01:38:50 -0800802 return NULL;
803 }
804
Alan Coxda9bb1d2006-01-18 17:44:13 -0800805 del_mc_from_global_list(mci);
Matthias Kaehlcke63b7df92007-07-19 01:49:38 -0700806 mutex_unlock(&mem_ctls_mutex);
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700807
Borislav Petkovbb31b3122010-12-02 17:48:35 +0100808 /* flush workq processes */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700809 edac_mc_workq_teardown(mci);
Borislav Petkovbb31b3122010-12-02 17:48:35 +0100810
811 /* marking MCI offline */
812 mci->op_state = OP_OFFLINE;
813
814 /* remove from sysfs */
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700815 edac_remove_sysfs_mci_device(mci);
816
Dave Peterson537fba22006-03-26 01:38:40 -0800817 edac_printk(KERN_INFO, EDAC_MC,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700818 "Removed device %d for %s %s: DEV %s\n", mci->mc_idx,
Stephen Rothwell17aa7e02008-05-05 13:54:19 +1000819 mci->mod_name, mci->ctl_name, edac_dev_name(mci));
Doug Thompsonbf52fa42007-07-19 01:50:30 -0700820
Dave Peterson18dbc332006-03-26 01:38:50 -0800821 return mci;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800822}
Dave Peterson91105402006-03-26 01:38:55 -0800823EXPORT_SYMBOL_GPL(edac_mc_del_mc);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800824
Adrian Bunk2da1c112007-07-19 01:49:32 -0700825static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
826 u32 size)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800827{
828 struct page *pg;
829 void *virt_addr;
830 unsigned long flags = 0;
831
Joe Perches956b9ba2012-04-29 17:08:39 -0300832 edac_dbg(3, "\n");
Alan Coxda9bb1d2006-01-18 17:44:13 -0800833
834 /* ECC error page was not in our memory. Ignore it. */
Douglas Thompson079708b2007-07-19 01:49:58 -0700835 if (!pfn_valid(page))
Alan Coxda9bb1d2006-01-18 17:44:13 -0800836 return;
837
838 /* Find the actual page structure then map it and fix */
839 pg = pfn_to_page(page);
840
841 if (PageHighMem(pg))
842 local_irq_save(flags);
843
Cong Wang4e5df7c2011-11-25 23:14:19 +0800844 virt_addr = kmap_atomic(pg);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800845
846 /* Perform architecture specific atomic scrub operation */
847 atomic_scrub(virt_addr + offset, size);
848
849 /* Unmap and complete */
Cong Wang4e5df7c2011-11-25 23:14:19 +0800850 kunmap_atomic(virt_addr);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800851
852 if (PageHighMem(pg))
853 local_irq_restore(flags);
854}
855
Alan Coxda9bb1d2006-01-18 17:44:13 -0800856/* FIXME - should return -1 */
Dave Petersone7ecd892006-03-26 01:38:52 -0800857int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800858{
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300859 struct csrow_info **csrows = mci->csrows;
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -0300860 int row, i, j, n;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800861
Joe Perches956b9ba2012-04-29 17:08:39 -0300862 edac_dbg(1, "MC%d: 0x%lx\n", mci->mc_idx, page);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800863 row = -1;
864
865 for (i = 0; i < mci->nr_csrows; i++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300866 struct csrow_info *csrow = csrows[i];
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -0300867 n = 0;
868 for (j = 0; j < csrow->nr_channels; j++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -0300869 struct dimm_info *dimm = csrow->channels[j]->dimm;
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -0300870 n += dimm->nr_pages;
871 }
872 if (n == 0)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800873 continue;
874
Joe Perches956b9ba2012-04-29 17:08:39 -0300875 edac_dbg(3, "MC%d: first(0x%lx) page(0x%lx) last(0x%lx) mask(0x%lx)\n",
876 mci->mc_idx,
877 csrow->first_page, page, csrow->last_page,
878 csrow->page_mask);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800879
880 if ((page >= csrow->first_page) &&
881 (page <= csrow->last_page) &&
882 ((page & csrow->page_mask) ==
883 (csrow->first_page & csrow->page_mask))) {
884 row = i;
885 break;
886 }
887 }
888
889 if (row == -1)
Dave Peterson537fba22006-03-26 01:38:40 -0800890 edac_mc_printk(mci, KERN_ERR,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700891 "could not look up page error address %lx\n",
892 (unsigned long)page);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800893
894 return row;
895}
Dave Peterson91105402006-03-26 01:38:55 -0800896EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800897
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300898const char *edac_layer_name[] = {
899 [EDAC_MC_LAYER_BRANCH] = "branch",
900 [EDAC_MC_LAYER_CHANNEL] = "channel",
901 [EDAC_MC_LAYER_SLOT] = "slot",
902 [EDAC_MC_LAYER_CHIP_SELECT] = "csrow",
Mauro Carvalho Chehabc66b5a72013-02-15 07:21:08 -0300903 [EDAC_MC_LAYER_ALL_MEM] = "memory",
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300904};
905EXPORT_SYMBOL_GPL(edac_layer_name);
906
907static void edac_inc_ce_error(struct mem_ctl_info *mci,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300908 bool enable_per_layer_report,
909 const int pos[EDAC_MAX_LAYERS],
910 const u16 count)
Alan Coxda9bb1d2006-01-18 17:44:13 -0800911{
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300912 int i, index = 0;
Alan Coxda9bb1d2006-01-18 17:44:13 -0800913
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300914 mci->ce_mc += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300915
916 if (!enable_per_layer_report) {
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300917 mci->ce_noinfo_count += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300918 return;
919 }
920
921 for (i = 0; i < mci->n_layers; i++) {
922 if (pos[i] < 0)
923 break;
924 index += pos[i];
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300925 mci->ce_per_layer[i][index] += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300926
927 if (i < mci->n_layers - 1)
928 index *= mci->layers[i + 1].size;
929 }
930}
931
932static void edac_inc_ue_error(struct mem_ctl_info *mci,
933 bool enable_per_layer_report,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300934 const int pos[EDAC_MAX_LAYERS],
935 const u16 count)
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300936{
937 int i, index = 0;
938
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300939 mci->ue_mc += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300940
941 if (!enable_per_layer_report) {
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300942 mci->ce_noinfo_count += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300943 return;
944 }
945
946 for (i = 0; i < mci->n_layers; i++) {
947 if (pos[i] < 0)
948 break;
949 index += pos[i];
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300950 mci->ue_per_layer[i][index] += count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300951
952 if (i < mci->n_layers - 1)
953 index *= mci->layers[i + 1].size;
954 }
955}
956
957static void edac_ce_error(struct mem_ctl_info *mci,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300958 const u16 error_count,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300959 const int pos[EDAC_MAX_LAYERS],
960 const char *msg,
961 const char *location,
962 const char *label,
963 const char *detail,
964 const char *other_detail,
965 const bool enable_per_layer_report,
966 const unsigned long page_frame_number,
967 const unsigned long offset_in_page,
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -0300968 long grain)
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300969{
970 unsigned long remapped_page;
Borislav Petkovf430d572012-09-10 18:36:09 +0200971 char *msg_aux = "";
972
973 if (*msg)
974 msg_aux = " ";
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300975
976 if (edac_mc_get_log_ce()) {
977 if (other_detail && *other_detail)
978 edac_mc_printk(mci, KERN_WARNING,
Borislav Petkovf430d572012-09-10 18:36:09 +0200979 "%d CE %s%son %s (%s %s - %s)\n",
980 error_count, msg, msg_aux, label,
981 location, detail, other_detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300982 else
983 edac_mc_printk(mci, KERN_WARNING,
Borislav Petkovf430d572012-09-10 18:36:09 +0200984 "%d CE %s%son %s (%s %s)\n",
985 error_count, msg, msg_aux, label,
986 location, detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300987 }
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -0300988 edac_inc_ce_error(mci, enable_per_layer_report, pos, error_count);
Alan Coxda9bb1d2006-01-18 17:44:13 -0800989
990 if (mci->scrub_mode & SCRUB_SW_SRC) {
991 /*
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -0300992 * Some memory controllers (called MCs below) can remap
993 * memory so that it is still available at a different
994 * address when PCI devices map into memory.
995 * MC's that can't do this, lose the memory where PCI
996 * devices are mapped. This mapping is MC-dependent
997 * and so we call back into the MC driver for it to
998 * map the MC page to a physical (CPU) page which can
999 * then be mapped to a virtual page - which can then
1000 * be scrubbed.
1001 */
Alan Coxda9bb1d2006-01-18 17:44:13 -08001002 remapped_page = mci->ctl_page_to_phys ?
Douglas Thompson052dfb42007-07-19 01:50:13 -07001003 mci->ctl_page_to_phys(mci, page_frame_number) :
1004 page_frame_number;
Alan Coxda9bb1d2006-01-18 17:44:13 -08001005
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001006 edac_mc_scrub_block(remapped_page,
1007 offset_in_page, grain);
Alan Coxda9bb1d2006-01-18 17:44:13 -08001008 }
1009}
1010
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001011static void edac_ue_error(struct mem_ctl_info *mci,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001012 const u16 error_count,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001013 const int pos[EDAC_MAX_LAYERS],
1014 const char *msg,
1015 const char *location,
1016 const char *label,
1017 const char *detail,
1018 const char *other_detail,
1019 const bool enable_per_layer_report)
Alan Coxda9bb1d2006-01-18 17:44:13 -08001020{
Borislav Petkovf430d572012-09-10 18:36:09 +02001021 char *msg_aux = "";
1022
1023 if (*msg)
1024 msg_aux = " ";
1025
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001026 if (edac_mc_get_log_ue()) {
1027 if (other_detail && *other_detail)
1028 edac_mc_printk(mci, KERN_WARNING,
Borislav Petkovf430d572012-09-10 18:36:09 +02001029 "%d UE %s%son %s (%s %s - %s)\n",
1030 error_count, msg, msg_aux, label,
1031 location, detail, other_detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001032 else
1033 edac_mc_printk(mci, KERN_WARNING,
Borislav Petkovf430d572012-09-10 18:36:09 +02001034 "%d UE %s%son %s (%s %s)\n",
1035 error_count, msg, msg_aux, label,
1036 location, detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001037 }
Dave Petersone7ecd892006-03-26 01:38:52 -08001038
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001039 if (edac_mc_get_panic_on_ue()) {
1040 if (other_detail && *other_detail)
Borislav Petkovf430d572012-09-10 18:36:09 +02001041 panic("UE %s%son %s (%s%s - %s)\n",
1042 msg, msg_aux, label, location, detail, other_detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001043 else
Borislav Petkovf430d572012-09-10 18:36:09 +02001044 panic("UE %s%son %s (%s%s)\n",
1045 msg, msg_aux, label, location, detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001046 }
1047
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001048 edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count);
Alan Coxda9bb1d2006-01-18 17:44:13 -08001049}
1050
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001051#define OTHER_LABEL " or "
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001052
1053/**
1054 * edac_mc_handle_error - reports a memory event to userspace
1055 *
1056 * @type: severity of the error (CE/UE/Fatal)
1057 * @mci: a struct mem_ctl_info pointer
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001058 * @error_count: Number of errors of the same type
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001059 * @page_frame_number: mem page where the error occurred
1060 * @offset_in_page: offset of the error inside the page
1061 * @syndrome: ECC syndrome
1062 * @top_layer: Memory layer[0] position
1063 * @mid_layer: Memory layer[1] position
1064 * @low_layer: Memory layer[2] position
1065 * @msg: Message meaningful to the end users that
1066 * explains the event
1067 * @other_detail: Technical details about the event that
1068 * may help hardware manufacturers and
1069 * EDAC developers to analyse the event
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001070 */
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001071void edac_mc_handle_error(const enum hw_event_mc_err_type type,
1072 struct mem_ctl_info *mci,
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001073 const u16 error_count,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001074 const unsigned long page_frame_number,
1075 const unsigned long offset_in_page,
1076 const unsigned long syndrome,
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001077 const int top_layer,
1078 const int mid_layer,
1079 const int low_layer,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001080 const char *msg,
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001081 const char *other_detail)
Alan Coxda9bb1d2006-01-18 17:44:13 -08001082{
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001083 /* FIXME: too much for stack: move it to some pre-alocated area */
1084 char detail[80], location[80];
1085 char label[(EDAC_MC_LABEL_LEN + 1 + sizeof(OTHER_LABEL)) * mci->tot_dimms];
1086 char *p;
1087 int row = -1, chan = -1;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001088 int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer };
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001089 int i;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001090 long grain;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001091 bool enable_per_layer_report = false;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001092 u8 grain_bits;
Alan Coxda9bb1d2006-01-18 17:44:13 -08001093
Joe Perches956b9ba2012-04-29 17:08:39 -03001094 edac_dbg(3, "MC%d\n", mci->mc_idx);
Alan Coxda9bb1d2006-01-18 17:44:13 -08001095
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001096 /*
1097 * Check if the event report is consistent and if the memory
1098 * location is known. If it is known, enable_per_layer_report will be
1099 * true, the DIMM(s) label info will be filled and the per-layer
1100 * error counters will be incremented.
1101 */
1102 for (i = 0; i < mci->n_layers; i++) {
1103 if (pos[i] >= (int)mci->layers[i].size) {
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001104
1105 edac_mc_printk(mci, KERN_ERR,
1106 "INTERNAL ERROR: %s value is out of range (%d >= %d)\n",
1107 edac_layer_name[mci->layers[i].type],
1108 pos[i], mci->layers[i].size);
1109 /*
1110 * Instead of just returning it, let's use what's
1111 * known about the error. The increment routines and
1112 * the DIMM filter logic will do the right thing by
1113 * pointing the likely damaged DIMMs.
1114 */
1115 pos[i] = -1;
1116 }
1117 if (pos[i] >= 0)
1118 enable_per_layer_report = true;
Alan Coxda9bb1d2006-01-18 17:44:13 -08001119 }
1120
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001121 /*
1122 * Get the dimm label/grain that applies to the match criteria.
1123 * As the error algorithm may not be able to point to just one memory
1124 * stick, the logic here will get all possible labels that could
1125 * pottentially be affected by the error.
1126 * On FB-DIMM memory controllers, for uncorrected errors, it is common
1127 * to have only the MC channel and the MC dimm (also called "branch")
1128 * but the channel is not known, as the memory is arranged in pairs,
1129 * where each memory belongs to a separate channel within the same
1130 * branch.
1131 */
1132 grain = 0;
1133 p = label;
1134 *p = '\0';
Borislav Petkov4da1b7b2012-09-10 17:57:44 +02001135
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001136 for (i = 0; i < mci->tot_dimms; i++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03001137 struct dimm_info *dimm = mci->dimms[i];
Dave Petersone7ecd892006-03-26 01:38:52 -08001138
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001139 if (top_layer >= 0 && top_layer != dimm->location[0])
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001140 continue;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001141 if (mid_layer >= 0 && mid_layer != dimm->location[1])
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001142 continue;
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001143 if (low_layer >= 0 && low_layer != dimm->location[2])
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001144 continue;
1145
1146 /* get the max grain, over the error match range */
1147 if (dimm->grain > grain)
1148 grain = dimm->grain;
1149
1150 /*
1151 * If the error is memory-controller wide, there's no need to
1152 * seek for the affected DIMMs because the whole
1153 * channel/memory controller/... may be affected.
1154 * Also, don't show errors for empty DIMM slots.
1155 */
1156 if (enable_per_layer_report && dimm->nr_pages) {
1157 if (p != label) {
1158 strcpy(p, OTHER_LABEL);
1159 p += strlen(OTHER_LABEL);
1160 }
1161 strcpy(p, dimm->label);
1162 p += strlen(p);
1163 *p = '\0';
1164
1165 /*
1166 * get csrow/channel of the DIMM, in order to allow
1167 * incrementing the compat API counters
1168 */
Joe Perches956b9ba2012-04-29 17:08:39 -03001169 edac_dbg(4, "%s csrows map: (%d,%d)\n",
1170 mci->mem_is_per_rank ? "rank" : "dimm",
1171 dimm->csrow, dimm->cschannel);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001172 if (row == -1)
1173 row = dimm->csrow;
1174 else if (row >= 0 && row != dimm->csrow)
1175 row = -2;
1176
1177 if (chan == -1)
1178 chan = dimm->cschannel;
1179 else if (chan >= 0 && chan != dimm->cschannel)
1180 chan = -2;
1181 }
Alan Coxda9bb1d2006-01-18 17:44:13 -08001182 }
1183
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001184 if (!enable_per_layer_report) {
1185 strcpy(label, "any memory");
1186 } else {
Joe Perches956b9ba2012-04-29 17:08:39 -03001187 edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001188 if (p == label)
1189 strcpy(label, "unknown memory");
1190 if (type == HW_EVENT_ERR_CORRECTED) {
1191 if (row >= 0) {
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001192 mci->csrows[row]->ce_count += error_count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001193 if (chan >= 0)
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001194 mci->csrows[row]->channels[chan]->ce_count += error_count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001195 }
1196 } else
1197 if (row >= 0)
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001198 mci->csrows[row]->ue_count += error_count;
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001199 }
Alan Coxda9bb1d2006-01-18 17:44:13 -08001200
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001201 /* Fill the RAM location data */
1202 p = location;
Borislav Petkov4da1b7b2012-09-10 17:57:44 +02001203
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001204 for (i = 0; i < mci->n_layers; i++) {
1205 if (pos[i] < 0)
1206 continue;
1207
1208 p += sprintf(p, "%s:%d ",
1209 edac_layer_name[mci->layers[i].type],
1210 pos[i]);
1211 }
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001212 if (p > location)
1213 *(p - 1) = '\0';
1214
1215 /* Report the error via the trace interface */
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001216 grain_bits = fls_long(grain) + 1;
1217 trace_mc_event(type, msg, label, error_count,
1218 mci->mc_idx, top_layer, mid_layer, low_layer,
1219 PAGES_TO_MiB(page_frame_number) | offset_in_page,
1220 grain_bits, syndrome, other_detail);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001221
1222 /* Memory type dependent details about the error */
1223 if (type == HW_EVENT_ERR_CORRECTED) {
1224 snprintf(detail, sizeof(detail),
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001225 "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx",
Douglas Thompson052dfb42007-07-19 01:50:13 -07001226 page_frame_number, offset_in_page,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001227 grain, syndrome);
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001228 edac_ce_error(mci, error_count, pos, msg, location, label,
1229 detail, other_detail, enable_per_layer_report,
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001230 page_frame_number, offset_in_page, grain);
1231 } else {
1232 snprintf(detail, sizeof(detail),
Mauro Carvalho Chehab53f2d022012-02-23 08:10:34 -03001233 "page:0x%lx offset:0x%lx grain:%ld",
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001234 page_frame_number, offset_in_page, grain);
Alan Coxda9bb1d2006-01-18 17:44:13 -08001235
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001236 edac_ue_error(mci, error_count, pos, msg, location, label,
1237 detail, other_detail, enable_per_layer_report);
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001238 }
Alan Coxda9bb1d2006-01-18 17:44:13 -08001239}
Mauro Carvalho Chehab4275be62012-04-18 15:20:50 -03001240EXPORT_SYMBOL_GPL(edac_mc_handle_error);