Blame view

drivers/edac/edac_mc.c 23.3 KB
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
1
2
  /*
   * edac_mc kernel module
49c0dab7e   Doug Thompson   [PATCH] Fix and e...
3
   * (C) 2005, 2006 Linux Networx (http://lnxi.com)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
4
5
6
7
8
9
10
11
12
13
   * This file may be distributed under the terms of the
   * GNU General Public License.
   *
   * Written by Thayne Harbaugh
   * Based on work by Dan Hollis <goemon at anime dot net> and others.
   *	http://www.anime.net/~goemon/linux-ecc/
   *
   * Modified by Dave Peterson and Doug Thompson
   *
   */
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  #include <linux/module.h>
  #include <linux/proc_fs.h>
  #include <linux/kernel.h>
  #include <linux/types.h>
  #include <linux/smp.h>
  #include <linux/init.h>
  #include <linux/sysctl.h>
  #include <linux/highmem.h>
  #include <linux/timer.h>
  #include <linux/slab.h>
  #include <linux/jiffies.h>
  #include <linux/spinlock.h>
  #include <linux/list.h>
  #include <linux/sysdev.h>
  #include <linux/ctype.h>
c0d121720   Dave Jiang   drivers/edac: add...
29
  #include <linux/edac.h>
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
30
31
32
  #include <asm/uaccess.h>
  #include <asm/page.h>
  #include <asm/edac.h>
20bcb7a81   Douglas Thompson   drivers/edac: mod...
33
  #include "edac_core.h"
7c9281d76   Douglas Thompson   drivers/edac: spl...
34
  #include "edac_module.h"
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
35

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
36
  /* lock to memory controller's control array */
63b7df910   Matthias Kaehlcke   drivers/edac: cha...
37
  static DEFINE_MUTEX(mem_ctls_mutex);
ff6ac2a61   Robert P. J. Day   edac: use the sho...
38
  static LIST_HEAD(mc_devices);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
39

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
40
  #ifdef CONFIG_EDAC_DEBUG
2da1c119f   Adrian Bunk   drivers/edac: cor...
41
  static void edac_mc_dump_channel(struct channel_info *chan)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
42
43
44
45
46
47
48
49
50
51
52
53
54
  {
  	debugf4("\tchannel = %p
  ", chan);
  	debugf4("\tchannel->chan_idx = %d
  ", chan->chan_idx);
  	debugf4("\tchannel->ce_count = %d
  ", chan->ce_count);
  	debugf4("\tchannel->label = '%s'
  ", chan->label);
  	debugf4("\tchannel->csrow = %p
  
  ", chan->csrow);
  }
2da1c119f   Adrian Bunk   drivers/edac: cor...
55
  static void edac_mc_dump_csrow(struct csrow_info *csrow)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
56
57
58
59
60
  {
  	debugf4("\tcsrow = %p
  ", csrow);
  	debugf4("\tcsrow->csrow_idx = %d
  ", csrow->csrow_idx);
079708b91   Douglas Thompson   drivers/edac: cor...
61
62
  	debugf4("\tcsrow->first_page = 0x%lx
  ", csrow->first_page);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
63
64
65
66
67
68
  	debugf4("\tcsrow->last_page = 0x%lx
  ", csrow->last_page);
  	debugf4("\tcsrow->page_mask = 0x%lx
  ", csrow->page_mask);
  	debugf4("\tcsrow->nr_pages = 0x%x
  ", csrow->nr_pages);
079708b91   Douglas Thompson   drivers/edac: cor...
69
70
  	debugf4("\tcsrow->nr_channels = %d
  ", csrow->nr_channels);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
71
72
73
74
75
76
  	debugf4("\tcsrow->channels = %p
  ", csrow->channels);
  	debugf4("\tcsrow->mci = %p
  
  ", csrow->mci);
  }
2da1c119f   Adrian Bunk   drivers/edac: cor...
77
  static void edac_mc_dump_mci(struct mem_ctl_info *mci)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  {
  	debugf3("\tmci = %p
  ", mci);
  	debugf3("\tmci->mtype_cap = %lx
  ", mci->mtype_cap);
  	debugf3("\tmci->edac_ctl_cap = %lx
  ", mci->edac_ctl_cap);
  	debugf3("\tmci->edac_cap = %lx
  ", mci->edac_cap);
  	debugf4("\tmci->edac_check = %p
  ", mci->edac_check);
  	debugf3("\tmci->nr_csrows = %d, csrows = %p
  ",
  		mci->nr_csrows, mci->csrows);
37f04581a   Doug Thompson   [PATCH] EDAC: PCI...
92
93
  	debugf3("\tdev = %p
  ", mci->dev);
079708b91   Douglas Thompson   drivers/edac: cor...
94
95
  	debugf3("\tmod_name:ctl_name = %s:%s
  ", mci->mod_name, mci->ctl_name);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
96
97
98
99
  	debugf3("\tpvt_info = %p
  
  ", mci->pvt_info);
  }
239642fe1   Borislav Petkov   edac: add memory ...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  /*
   * keep those in sync with the enum mem_type
   */
  const char *edac_mem_types[] = {
  	"Empty csrow",
  	"Reserved csrow type",
  	"Unknown csrow type",
  	"Fast page mode RAM",
  	"Extended data out RAM",
  	"Burst Extended data out RAM",
  	"Single data rate SDRAM",
  	"Registered single data rate SDRAM",
  	"Double data rate SDRAM",
  	"Registered Double data rate SDRAM",
  	"Rambus DRAM",
  	"Unbuffered DDR2 RAM",
  	"Fully buffered DDR2",
  	"Registered DDR2 RAM",
  	"Rambus XDR",
  	"Unbuffered DDR3 RAM",
  	"Registered DDR3 RAM",
  };
  EXPORT_SYMBOL_GPL(edac_mem_types);
079708b91   Douglas Thompson   drivers/edac: cor...
123
  #endif				/* CONFIG_EDAC_DEBUG */
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
124
125
126
127
128
129
130
131
  
  /* 'ptr' points to a possibly unaligned item X such that sizeof(X) is 'size'.
   * Adjust 'ptr' so that its alignment is at least as stringent as what the
   * compiler would provide for X and return the aligned result.
   *
   * If 'size' is a constant, the compiler will optimize this whole function
   * down to either a no-op or the addition of a constant to the value of 'ptr'.
   */
7391c6dca   Douglas Thompson   drivers/edac: mod...
132
  void *edac_align_ptr(void *ptr, unsigned size)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  {
  	unsigned align, r;
  
  	/* Here we assume that the alignment of a "long long" is the most
  	 * stringent alignment that the compiler will ever provide by default.
  	 * As far as I know, this is a reasonable assumption.
  	 */
  	if (size > sizeof(long))
  		align = sizeof(long long);
  	else if (size > sizeof(int))
  		align = sizeof(long);
  	else if (size > sizeof(short))
  		align = sizeof(int);
  	else if (size > sizeof(char))
  		align = sizeof(short);
  	else
079708b91   Douglas Thompson   drivers/edac: cor...
149
  		return (char *)ptr;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
150
151
152
153
  
  	r = size % align;
  
  	if (r == 0)
079708b91   Douglas Thompson   drivers/edac: cor...
154
  		return (char *)ptr;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
155

7391c6dca   Douglas Thompson   drivers/edac: mod...
156
  	return (void *)(((unsigned long)ptr) + align - r);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
157
  }
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  /**
   * edac_mc_alloc: Allocate a struct mem_ctl_info structure
   * @size_pvt:	size of private storage needed
   * @nr_csrows:	Number of CWROWS needed for this MC
   * @nr_chans:	Number of channels for the MC
   *
   * Everything is kmalloc'ed as one big chunk - more efficient.
   * Only can be used if all structures have the same lifetime - otherwise
   * you have to allocate and initialize your own structures.
   *
   * Use edac_mc_free() to free mc structures allocated by this function.
   *
   * Returns:
   *	NULL allocation failed
   *	struct mem_ctl_info pointer
   */
  struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
b8f6f9755   Doug Thompson   drivers/edac: fix...
175
  				unsigned nr_chans, int edac_index)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
176
177
178
179
180
181
182
  {
  	struct mem_ctl_info *mci;
  	struct csrow_info *csi, *csrow;
  	struct channel_info *chi, *chp, *chan;
  	void *pvt;
  	unsigned size;
  	int row, chn;
8096cfafb   Doug Thompson   drivers/edac: fix...
183
  	int err;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
184
185
186
187
188
189
  
  	/* Figure out the offsets of the various items from the start of an mc
  	 * structure.  We want the alignment of each item to be at least as
  	 * stringent as what the compiler would provide if we could simply
  	 * hardcode everything into a single struct.
  	 */
079708b91   Douglas Thompson   drivers/edac: cor...
190
  	mci = (struct mem_ctl_info *)0;
7391c6dca   Douglas Thompson   drivers/edac: mod...
191
192
  	csi = edac_align_ptr(&mci[1], sizeof(*csi));
  	chi = edac_align_ptr(&csi[nr_csrows], sizeof(*chi));
e27e3dac6   Douglas Thompson   drivers/edac: add...
193
  	pvt = edac_align_ptr(&chi[nr_chans * nr_csrows], sz_pvt);
079708b91   Douglas Thompson   drivers/edac: cor...
194
  	size = ((unsigned long)pvt) + sz_pvt;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
195

8096cfafb   Doug Thompson   drivers/edac: fix...
196
197
  	mci = kzalloc(size, GFP_KERNEL);
  	if (mci == NULL)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
198
199
200
201
202
  		return NULL;
  
  	/* Adjust pointers so they point within the memory we just allocated
  	 * rather than an imaginary chunk of memory located at address 0.
  	 */
079708b91   Douglas Thompson   drivers/edac: cor...
203
204
205
  	csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
  	chi = (struct channel_info *)(((char *)mci) + ((unsigned long)chi));
  	pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
206

b8f6f9755   Doug Thompson   drivers/edac: fix...
207
208
  	/* setup index and various internal pointers */
  	mci->mc_idx = edac_index;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  	mci->csrows = csi;
  	mci->pvt_info = pvt;
  	mci->nr_csrows = nr_csrows;
  
  	for (row = 0; row < nr_csrows; row++) {
  		csrow = &csi[row];
  		csrow->csrow_idx = row;
  		csrow->mci = mci;
  		csrow->nr_channels = nr_chans;
  		chp = &chi[row * nr_chans];
  		csrow->channels = chp;
  
  		for (chn = 0; chn < nr_chans; chn++) {
  			chan = &chp[chn];
  			chan->chan_idx = chn;
  			chan->csrow = csrow;
  		}
  	}
81d87cb13   Dave Jiang   drivers/edac: mod...
227
  	mci->op_state = OP_ALLOC;
8096cfafb   Doug Thompson   drivers/edac: fix...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
  	/*
  	 * Initialize the 'root' kobj for the edac_mc controller
  	 */
  	err = edac_mc_register_sysfs_main_kobj(mci);
  	if (err) {
  		kfree(mci);
  		return NULL;
  	}
  
  	/* at this point, the root kobj is valid, and in order to
  	 * 'free' the object, then the function:
  	 *      edac_mc_unregister_sysfs_main_kobj() must be called
  	 * which will perform kobj unregistration and the actual free
  	 * will occur during the kobject callback operation
  	 */
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
243
244
  	return mci;
  }
9110540f7   Dave Peterson   [PATCH] EDAC: use...
245
  EXPORT_SYMBOL_GPL(edac_mc_alloc);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
246

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
247
  /**
8096cfafb   Doug Thompson   drivers/edac: fix...
248
249
   * edac_mc_free
   *	'Free' a previously allocated 'mci' structure
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
250
   * @mci: pointer to a struct mem_ctl_info structure
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
251
252
253
   */
  void edac_mc_free(struct mem_ctl_info *mci)
  {
8096cfafb   Doug Thompson   drivers/edac: fix...
254
  	edac_mc_unregister_sysfs_main_kobj(mci);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
255
  }
9110540f7   Dave Peterson   [PATCH] EDAC: use...
256
  EXPORT_SYMBOL_GPL(edac_mc_free);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
257

bce19683c   Doug Thompson   drivers/edac: fix...
258
259
260
261
262
263
264
  
  /*
   * find_mci_by_dev
   *
   *	scan list of controllers looking for the one that manages
   *	the 'dev' device
   */
37f04581a   Doug Thompson   [PATCH] EDAC: PCI...
265
  static struct mem_ctl_info *find_mci_by_dev(struct device *dev)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
266
267
268
  {
  	struct mem_ctl_info *mci;
  	struct list_head *item;
537fba289   Dave Peterson   [PATCH] EDAC: pri...
269
270
  	debugf3("%s()
  ", __func__);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
271
272
273
  
  	list_for_each(item, &mc_devices) {
  		mci = list_entry(item, struct mem_ctl_info, link);
37f04581a   Doug Thompson   [PATCH] EDAC: PCI...
274
  		if (mci->dev == dev)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
275
276
277
278
279
  			return mci;
  	}
  
  	return NULL;
  }
81d87cb13   Dave Jiang   drivers/edac: mod...
280
281
282
283
284
  /*
   * handler for EDAC to check if NMI type handler has asserted interrupt
   */
  static int edac_mc_assert_error_check_and_clear(void)
  {
66ee2f940   Dave Jiang   drivers/edac: mod...
285
  	int old_state;
81d87cb13   Dave Jiang   drivers/edac: mod...
286

079708b91   Douglas Thompson   drivers/edac: cor...
287
  	if (edac_op_state == EDAC_OPSTATE_POLL)
81d87cb13   Dave Jiang   drivers/edac: mod...
288
  		return 1;
66ee2f940   Dave Jiang   drivers/edac: mod...
289
290
  	old_state = edac_err_assert;
  	edac_err_assert = 0;
81d87cb13   Dave Jiang   drivers/edac: mod...
291

66ee2f940   Dave Jiang   drivers/edac: mod...
292
  	return old_state;
81d87cb13   Dave Jiang   drivers/edac: mod...
293
294
295
296
297
298
  }
  
  /*
   * edac_mc_workq_function
   *	performs the operation scheduled by a workq request
   */
81d87cb13   Dave Jiang   drivers/edac: mod...
299
300
  static void edac_mc_workq_function(struct work_struct *work_req)
  {
fbeb43847   Jean Delvare   edac: use to_dela...
301
  	struct delayed_work *d_work = to_delayed_work(work_req);
81d87cb13   Dave Jiang   drivers/edac: mod...
302
  	struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
81d87cb13   Dave Jiang   drivers/edac: mod...
303
304
  
  	mutex_lock(&mem_ctls_mutex);
bf52fa4a2   Doug Thompson   drivers/edac: fix...
305
306
307
308
309
  	/* if this control struct has movd to offline state, we are done */
  	if (mci->op_state == OP_OFFLINE) {
  		mutex_unlock(&mem_ctls_mutex);
  		return;
  	}
81d87cb13   Dave Jiang   drivers/edac: mod...
310
311
312
  	/* Only poll controllers that are running polled and have a check */
  	if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL))
  		mci->edac_check(mci);
81d87cb13   Dave Jiang   drivers/edac: mod...
313
314
315
  	mutex_unlock(&mem_ctls_mutex);
  
  	/* Reschedule */
4de78c687   Dave Jiang   drivers/edac: mod...
316
  	queue_delayed_work(edac_workqueue, &mci->work,
052dfb45c   Douglas Thompson   drivers/edac: cle...
317
  			msecs_to_jiffies(edac_mc_get_poll_msec()));
81d87cb13   Dave Jiang   drivers/edac: mod...
318
319
320
321
322
323
  }
  
  /*
   * edac_mc_workq_setup
   *	initialize a workq item for this mci
   *	passing in the new delay period in msec
bf52fa4a2   Doug Thompson   drivers/edac: fix...
324
325
326
327
   *
   *	locking model:
   *
   *		called with the mem_ctls_mutex held
81d87cb13   Dave Jiang   drivers/edac: mod...
328
   */
bf52fa4a2   Doug Thompson   drivers/edac: fix...
329
  static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
81d87cb13   Dave Jiang   drivers/edac: mod...
330
331
332
  {
  	debugf0("%s()
  ", __func__);
bf52fa4a2   Doug Thompson   drivers/edac: fix...
333
334
335
  	/* if this instance is not in the POLL state, then simply return */
  	if (mci->op_state != OP_RUNNING_POLL)
  		return;
81d87cb13   Dave Jiang   drivers/edac: mod...
336
  	INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
81d87cb13   Dave Jiang   drivers/edac: mod...
337
338
339
340
341
342
  	queue_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
  }
  
  /*
   * edac_mc_workq_teardown
   *	stop the workq processing on this mci
bf52fa4a2   Doug Thompson   drivers/edac: fix...
343
344
345
346
   *
   *	locking model:
   *
   *		called WITHOUT lock held
81d87cb13   Dave Jiang   drivers/edac: mod...
347
   */
bf52fa4a2   Doug Thompson   drivers/edac: fix...
348
  static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
81d87cb13   Dave Jiang   drivers/edac: mod...
349
350
  {
  	int status;
00740c585   Borislav Petkov   amd64_edac: Fix d...
351
352
  	if (mci->op_state != OP_RUNNING_POLL)
  		return;
bce19683c   Doug Thompson   drivers/edac: fix...
353
354
355
356
357
  	status = cancel_delayed_work(&mci->work);
  	if (status == 0) {
  		debugf0("%s() not canceled, flush the queue
  ",
  			__func__);
bf52fa4a2   Doug Thompson   drivers/edac: fix...
358

bce19683c   Doug Thompson   drivers/edac: fix...
359
360
  		/* workq instance might be running, wait for it */
  		flush_workqueue(edac_workqueue);
81d87cb13   Dave Jiang   drivers/edac: mod...
361
362
363
364
  	}
  }
  
  /*
bce19683c   Doug Thompson   drivers/edac: fix...
365
366
367
368
   * edac_mc_reset_delay_period(unsigned long value)
   *
   *	user space has updated our poll period value, need to
   *	reset our workq delays
81d87cb13   Dave Jiang   drivers/edac: mod...
369
   */
bce19683c   Doug Thompson   drivers/edac: fix...
370
  void edac_mc_reset_delay_period(int value)
81d87cb13   Dave Jiang   drivers/edac: mod...
371
  {
bce19683c   Doug Thompson   drivers/edac: fix...
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  	struct mem_ctl_info *mci;
  	struct list_head *item;
  
  	mutex_lock(&mem_ctls_mutex);
  
  	/* scan the list and turn off all workq timers, doing so under lock
  	 */
  	list_for_each(item, &mc_devices) {
  		mci = list_entry(item, struct mem_ctl_info, link);
  
  		if (mci->op_state == OP_RUNNING_POLL)
  			cancel_delayed_work(&mci->work);
  	}
  
  	mutex_unlock(&mem_ctls_mutex);
81d87cb13   Dave Jiang   drivers/edac: mod...
387

bce19683c   Doug Thompson   drivers/edac: fix...
388
389
  
  	/* re-walk the list, and reset the poll delay */
bf52fa4a2   Doug Thompson   drivers/edac: fix...
390
  	mutex_lock(&mem_ctls_mutex);
bce19683c   Doug Thompson   drivers/edac: fix...
391
392
393
394
395
  	list_for_each(item, &mc_devices) {
  		mci = list_entry(item, struct mem_ctl_info, link);
  
  		edac_mc_workq_setup(mci, (unsigned long) value);
  	}
81d87cb13   Dave Jiang   drivers/edac: mod...
396
397
398
  
  	mutex_unlock(&mem_ctls_mutex);
  }
bce19683c   Doug Thompson   drivers/edac: fix...
399

2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
400
401
402
  /* Return 0 on success, 1 on failure.
   * Before calling this function, caller must
   * assign a unique value to mci->mc_idx.
bf52fa4a2   Doug Thompson   drivers/edac: fix...
403
404
405
406
   *
   *	locking model:
   *
   *		called with the mem_ctls_mutex lock held
2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
407
   */
079708b91   Douglas Thompson   drivers/edac: cor...
408
  static int add_mc_to_global_list(struct mem_ctl_info *mci)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
409
410
411
  {
  	struct list_head *item, *insert_before;
  	struct mem_ctl_info *p;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
412

2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
413
  	insert_before = &mc_devices;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
414

bf52fa4a2   Doug Thompson   drivers/edac: fix...
415
416
  	p = find_mci_by_dev(mci->dev);
  	if (unlikely(p != NULL))
2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
417
  		goto fail0;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
418

2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
419
420
  	list_for_each(item, &mc_devices) {
  		p = list_entry(item, struct mem_ctl_info, link);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
421

2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
422
423
424
  		if (p->mc_idx >= mci->mc_idx) {
  			if (unlikely(p->mc_idx == mci->mc_idx))
  				goto fail1;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
425

2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
426
427
  			insert_before = item;
  			break;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
428
  		}
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
429
430
431
  	}
  
  	list_add_tail_rcu(&mci->link, insert_before);
c0d121720   Dave Jiang   drivers/edac: add...
432
  	atomic_inc(&edac_handlers);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
433
  	return 0;
2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
434

052dfb45c   Douglas Thompson   drivers/edac: cle...
435
  fail0:
2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
436
  	edac_printk(KERN_WARNING, EDAC_MC,
281efb17d   Kay Sievers   edac: struct devi...
437
438
  		"%s (%s) %s %s already assigned %d
  ", dev_name(p->dev),
17aa7e034   Stephen Rothwell   dev_name introduc...
439
  		edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
440
  	return 1;
052dfb45c   Douglas Thompson   drivers/edac: cle...
441
  fail1:
2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
442
  	edac_printk(KERN_WARNING, EDAC_MC,
052dfb45c   Douglas Thompson   drivers/edac: cle...
443
444
445
446
  		"bug in low-level driver: attempt to assign
  "
  		"    duplicate mc_idx %d in %s()
  ", p->mc_idx, __func__);
2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
447
  	return 1;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
448
  }
e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
449
  static void complete_mc_list_del(struct rcu_head *head)
a1d03fcc1   Dave Peterson   [PATCH] EDAC: eda...
450
451
452
453
454
  {
  	struct mem_ctl_info *mci;
  
  	mci = container_of(head, struct mem_ctl_info, rcu);
  	INIT_LIST_HEAD(&mci->link);
a1d03fcc1   Dave Peterson   [PATCH] EDAC: eda...
455
  }
e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
456
  static void del_mc_from_global_list(struct mem_ctl_info *mci)
a1d03fcc1   Dave Peterson   [PATCH] EDAC: eda...
457
  {
c0d121720   Dave Jiang   drivers/edac: add...
458
  	atomic_dec(&edac_handlers);
a1d03fcc1   Dave Peterson   [PATCH] EDAC: eda...
459
  	list_del_rcu(&mci->link);
a1d03fcc1   Dave Peterson   [PATCH] EDAC: eda...
460
  	call_rcu(&mci->rcu, complete_mc_list_del);
458e5ff13   Jesper Dangaard Brouer   edac: core: remov...
461
  	rcu_barrier();
a1d03fcc1   Dave Peterson   [PATCH] EDAC: eda...
462
  }
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
463
  /**
5da0831c5   Douglas Thompson   drivers/edac: add...
464
465
466
467
468
469
470
   * edac_mc_find: Search for a mem_ctl_info structure whose index is 'idx'.
   *
   * If found, return a pointer to the structure.
   * Else return NULL.
   *
   * Caller must hold mem_ctls_mutex.
   */
079708b91   Douglas Thompson   drivers/edac: cor...
471
  struct mem_ctl_info *edac_mc_find(int idx)
5da0831c5   Douglas Thompson   drivers/edac: add...
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
  {
  	struct list_head *item;
  	struct mem_ctl_info *mci;
  
  	list_for_each(item, &mc_devices) {
  		mci = list_entry(item, struct mem_ctl_info, link);
  
  		if (mci->mc_idx >= idx) {
  			if (mci->mc_idx == idx)
  				return mci;
  
  			break;
  		}
  	}
  
  	return NULL;
  }
  EXPORT_SYMBOL(edac_mc_find);
  
  /**
472678ebd   Dave Peterson   [PATCH] EDAC: kob...
492
493
   * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and
   *                 create sysfs entries associated with mci structure
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
494
   * @mci: pointer to the mci structure to be added to the list
2d7bbb91c   Doug Thompson   [PATCH] EDAC: mc ...
495
   * @mc_idx: A unique numeric identifier to be assigned to the 'mci' structure.
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
496
497
498
499
500
501
502
   *
   * Return:
   *	0	Success
   *	!0	Failure
   */
  
  /* FIXME - should a warning be printed if no error detection? correction? */
b8f6f9755   Doug Thompson   drivers/edac: fix...
503
  int edac_mc_add_mc(struct mem_ctl_info *mci)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
504
  {
537fba289   Dave Peterson   [PATCH] EDAC: pri...
505
506
  	debugf0("%s()
  ", __func__);
b8f6f9755   Doug Thompson   drivers/edac: fix...
507

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
508
509
510
  #ifdef CONFIG_EDAC_DEBUG
  	if (edac_debug_level >= 3)
  		edac_mc_dump_mci(mci);
e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
511

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
512
513
514
515
516
  	if (edac_debug_level >= 4) {
  		int i;
  
  		for (i = 0; i < mci->nr_csrows; i++) {
  			int j;
e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
517

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
518
519
  			edac_mc_dump_csrow(&mci->csrows[i]);
  			for (j = 0; j < mci->csrows[i].nr_channels; j++)
079708b91   Douglas Thompson   drivers/edac: cor...
520
  				edac_mc_dump_channel(&mci->csrows[i].
052dfb45c   Douglas Thompson   drivers/edac: cle...
521
  						channels[j]);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
522
523
524
  		}
  	}
  #endif
63b7df910   Matthias Kaehlcke   drivers/edac: cha...
525
  	mutex_lock(&mem_ctls_mutex);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
526
527
  
  	if (add_mc_to_global_list(mci))
028a7b6d3   Dave Peterson   [PATCH] EDAC: eda...
528
  		goto fail0;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
529
530
531
  
  	/* set load time so that error rate can be tracked */
  	mci->start_time = jiffies;
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
532
533
  	if (edac_create_sysfs_mci_device(mci)) {
  		edac_mc_printk(mci, KERN_WARNING,
052dfb45c   Douglas Thompson   drivers/edac: cle...
534
535
  			"failed to create sysfs device
  ");
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
536
537
  		goto fail1;
  	}
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
538

81d87cb13   Dave Jiang   drivers/edac: mod...
539
540
541
542
543
544
545
546
547
  	/* If there IS a check routine, then we are running POLLED */
  	if (mci->edac_check != NULL) {
  		/* This instance is NOW RUNNING */
  		mci->op_state = OP_RUNNING_POLL;
  
  		edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
  	} else {
  		mci->op_state = OP_RUNNING_INTERRUPT;
  	}
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
548
  	/* Report action taken */
bf52fa4a2   Doug Thompson   drivers/edac: fix...
549
  	edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':"
17aa7e034   Stephen Rothwell   dev_name introduc...
550
551
  		" DEV %s
  ", mci->mod_name, mci->ctl_name, edac_dev_name(mci));
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
552

63b7df910   Matthias Kaehlcke   drivers/edac: cha...
553
  	mutex_unlock(&mem_ctls_mutex);
028a7b6d3   Dave Peterson   [PATCH] EDAC: eda...
554
  	return 0;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
555

052dfb45c   Douglas Thompson   drivers/edac: cle...
556
  fail1:
028a7b6d3   Dave Peterson   [PATCH] EDAC: eda...
557
  	del_mc_from_global_list(mci);
052dfb45c   Douglas Thompson   drivers/edac: cle...
558
  fail0:
63b7df910   Matthias Kaehlcke   drivers/edac: cha...
559
  	mutex_unlock(&mem_ctls_mutex);
028a7b6d3   Dave Peterson   [PATCH] EDAC: eda...
560
  	return 1;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
561
  }
9110540f7   Dave Peterson   [PATCH] EDAC: use...
562
  EXPORT_SYMBOL_GPL(edac_mc_add_mc);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
563

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
564
  /**
472678ebd   Dave Peterson   [PATCH] EDAC: kob...
565
566
   * edac_mc_del_mc: Remove sysfs entries for specified mci structure and
   *                 remove mci structure from global list
37f04581a   Doug Thompson   [PATCH] EDAC: PCI...
567
   * @pdev: Pointer to 'struct device' representing mci structure to remove.
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
568
   *
18dbc337a   Dave Peterson   [PATCH] EDAC: pro...
569
   * Return pointer to removed mci structure, or NULL if device not found.
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
570
   */
079708b91   Douglas Thompson   drivers/edac: cor...
571
  struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
572
  {
18dbc337a   Dave Peterson   [PATCH] EDAC: pro...
573
  	struct mem_ctl_info *mci;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
574

bf52fa4a2   Doug Thompson   drivers/edac: fix...
575
576
  	debugf0("%s()
  ", __func__);
63b7df910   Matthias Kaehlcke   drivers/edac: cha...
577
  	mutex_lock(&mem_ctls_mutex);
18dbc337a   Dave Peterson   [PATCH] EDAC: pro...
578

bf52fa4a2   Doug Thompson   drivers/edac: fix...
579
580
581
  	/* find the requested mci struct in the global list */
  	mci = find_mci_by_dev(dev);
  	if (mci == NULL) {
63b7df910   Matthias Kaehlcke   drivers/edac: cha...
582
  		mutex_unlock(&mem_ctls_mutex);
18dbc337a   Dave Peterson   [PATCH] EDAC: pro...
583
584
  		return NULL;
  	}
81d87cb13   Dave Jiang   drivers/edac: mod...
585
586
  	/* marking MCI offline */
  	mci->op_state = OP_OFFLINE;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
587
  	del_mc_from_global_list(mci);
63b7df910   Matthias Kaehlcke   drivers/edac: cha...
588
  	mutex_unlock(&mem_ctls_mutex);
bf52fa4a2   Doug Thompson   drivers/edac: fix...
589
590
591
592
  
  	/* flush workq processes and remove sysfs */
  	edac_mc_workq_teardown(mci);
  	edac_remove_sysfs_mci_device(mci);
537fba289   Dave Peterson   [PATCH] EDAC: pri...
593
  	edac_printk(KERN_INFO, EDAC_MC,
052dfb45c   Douglas Thompson   drivers/edac: cle...
594
595
  		"Removed device %d for %s %s: DEV %s
  ", mci->mc_idx,
17aa7e034   Stephen Rothwell   dev_name introduc...
596
  		mci->mod_name, mci->ctl_name, edac_dev_name(mci));
bf52fa4a2   Doug Thompson   drivers/edac: fix...
597

18dbc337a   Dave Peterson   [PATCH] EDAC: pro...
598
  	return mci;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
599
  }
9110540f7   Dave Peterson   [PATCH] EDAC: use...
600
  EXPORT_SYMBOL_GPL(edac_mc_del_mc);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
601

2da1c119f   Adrian Bunk   drivers/edac: cor...
602
603
  static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
  				u32 size)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
604
605
606
607
  {
  	struct page *pg;
  	void *virt_addr;
  	unsigned long flags = 0;
537fba289   Dave Peterson   [PATCH] EDAC: pri...
608
609
  	debugf3("%s()
  ", __func__);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
610
611
  
  	/* ECC error page was not in our memory. Ignore it. */
079708b91   Douglas Thompson   drivers/edac: cor...
612
  	if (!pfn_valid(page))
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
  		return;
  
  	/* Find the actual page structure then map it and fix */
  	pg = pfn_to_page(page);
  
  	if (PageHighMem(pg))
  		local_irq_save(flags);
  
  	virt_addr = kmap_atomic(pg, KM_BOUNCE_READ);
  
  	/* Perform architecture specific atomic scrub operation */
  	atomic_scrub(virt_addr + offset, size);
  
  	/* Unmap and complete */
  	kunmap_atomic(virt_addr, KM_BOUNCE_READ);
  
  	if (PageHighMem(pg))
  		local_irq_restore(flags);
  }
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
632
  /* FIXME - should return -1 */
e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
633
  int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
634
635
636
  {
  	struct csrow_info *csrows = mci->csrows;
  	int row, i;
537fba289   Dave Peterson   [PATCH] EDAC: pri...
637
638
  	debugf1("MC%d: %s(): 0x%lx
  ", mci->mc_idx, __func__, page);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
639
640
641
642
643
644
645
  	row = -1;
  
  	for (i = 0; i < mci->nr_csrows; i++) {
  		struct csrow_info *csrow = &csrows[i];
  
  		if (csrow->nr_pages == 0)
  			continue;
537fba289   Dave Peterson   [PATCH] EDAC: pri...
646
647
648
649
650
  		debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) "
  			"mask(0x%lx)
  ", mci->mc_idx, __func__,
  			csrow->first_page, page, csrow->last_page,
  			csrow->page_mask);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
651
652
653
654
655
656
657
658
659
660
661
  
  		if ((page >= csrow->first_page) &&
  		    (page <= csrow->last_page) &&
  		    ((page & csrow->page_mask) ==
  		     (csrow->first_page & csrow->page_mask))) {
  			row = i;
  			break;
  		}
  	}
  
  	if (row == -1)
537fba289   Dave Peterson   [PATCH] EDAC: pri...
662
  		edac_mc_printk(mci, KERN_ERR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
663
664
665
  			"could not look up page error address %lx
  ",
  			(unsigned long)page);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
666
667
668
  
  	return row;
  }
9110540f7   Dave Peterson   [PATCH] EDAC: use...
669
  EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
670

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
671
672
673
  /* FIXME - setable log (warning/emerg) levels */
  /* FIXME - integrate with evlog: http://evlog.sourceforge.net/ */
  void edac_mc_handle_ce(struct mem_ctl_info *mci,
052dfb45c   Douglas Thompson   drivers/edac: cle...
674
675
676
  		unsigned long page_frame_number,
  		unsigned long offset_in_page, unsigned long syndrome,
  		int row, int channel, const char *msg)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
677
678
  {
  	unsigned long remapped_page;
537fba289   Dave Peterson   [PATCH] EDAC: pri...
679
680
  	debugf3("MC%d: %s()
  ", mci->mc_idx, __func__);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
681
682
683
684
  
  	/* FIXME - maybe make panic on INTERNAL ERROR an option */
  	if (row >= mci->nr_csrows || row < 0) {
  		/* something is wrong */
537fba289   Dave Peterson   [PATCH] EDAC: pri...
685
  		edac_mc_printk(mci, KERN_ERR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
686
687
688
  			"INTERNAL ERROR: row out of range "
  			"(%d >= %d)
  ", row, mci->nr_csrows);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
689
690
691
  		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
  		return;
  	}
e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
692

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
693
694
  	if (channel >= mci->csrows[row].nr_channels || channel < 0) {
  		/* something is wrong */
537fba289   Dave Peterson   [PATCH] EDAC: pri...
695
  		edac_mc_printk(mci, KERN_ERR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
696
697
698
699
  			"INTERNAL ERROR: channel out of range "
  			"(%d >= %d)
  ", channel,
  			mci->csrows[row].nr_channels);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
700
701
702
  		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
  		return;
  	}
4de78c687   Dave Jiang   drivers/edac: mod...
703
  	if (edac_mc_get_log_ce())
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
704
  		/* FIXME - put in DIMM location */
537fba289   Dave Peterson   [PATCH] EDAC: pri...
705
  		edac_mc_printk(mci, KERN_WARNING,
052dfb45c   Douglas Thompson   drivers/edac: cle...
706
707
708
709
710
711
  			"CE page 0x%lx, offset 0x%lx, grain %d, syndrome "
  			"0x%lx, row %d, channel %d, label \"%s\": %s
  ",
  			page_frame_number, offset_in_page,
  			mci->csrows[row].grain, syndrome, row, channel,
  			mci->csrows[row].channels[channel].label, msg);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
  
  	mci->ce_count++;
  	mci->csrows[row].ce_count++;
  	mci->csrows[row].channels[channel].ce_count++;
  
  	if (mci->scrub_mode & SCRUB_SW_SRC) {
  		/*
  		 * Some MC's can remap memory so that it is still available
  		 * at a different address when PCI devices map into memory.
  		 * MC's that can't do this lose the memory where PCI devices
  		 * are mapped.  This mapping is MC dependant and so we call
  		 * back into the MC driver for it to map the MC page to
  		 * a physical (CPU) page which can then be mapped to a virtual
  		 * page - which can then be scrubbed.
  		 */
  		remapped_page = mci->ctl_page_to_phys ?
052dfb45c   Douglas Thompson   drivers/edac: cle...
728
729
  			mci->ctl_page_to_phys(mci, page_frame_number) :
  			page_frame_number;
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
730
731
  
  		edac_mc_scrub_block(remapped_page, offset_in_page,
052dfb45c   Douglas Thompson   drivers/edac: cle...
732
  				mci->csrows[row].grain);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
733
734
  	}
  }
9110540f7   Dave Peterson   [PATCH] EDAC: use...
735
  EXPORT_SYMBOL_GPL(edac_mc_handle_ce);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
736

e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
737
  void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
738
  {
4de78c687   Dave Jiang   drivers/edac: mod...
739
  	if (edac_mc_get_log_ce())
537fba289   Dave Peterson   [PATCH] EDAC: pri...
740
  		edac_mc_printk(mci, KERN_WARNING,
052dfb45c   Douglas Thompson   drivers/edac: cle...
741
742
  			"CE - no information available: %s
  ", msg);
e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
743

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
744
745
746
  	mci->ce_noinfo_count++;
  	mci->ce_count++;
  }
9110540f7   Dave Peterson   [PATCH] EDAC: use...
747
  EXPORT_SYMBOL_GPL(edac_mc_handle_ce_no_info);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
748

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
749
  void edac_mc_handle_ue(struct mem_ctl_info *mci,
052dfb45c   Douglas Thompson   drivers/edac: cle...
750
751
  		unsigned long page_frame_number,
  		unsigned long offset_in_page, int row, const char *msg)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
752
753
754
755
756
757
  {
  	int len = EDAC_MC_LABEL_LEN * 4;
  	char labels[len + 1];
  	char *pos = labels;
  	int chan;
  	int chars;
537fba289   Dave Peterson   [PATCH] EDAC: pri...
758
759
  	debugf3("MC%d: %s()
  ", mci->mc_idx, __func__);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
760
761
762
763
  
  	/* FIXME - maybe make panic on INTERNAL ERROR an option */
  	if (row >= mci->nr_csrows || row < 0) {
  		/* something is wrong */
537fba289   Dave Peterson   [PATCH] EDAC: pri...
764
  		edac_mc_printk(mci, KERN_ERR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
765
766
767
  			"INTERNAL ERROR: row out of range "
  			"(%d >= %d)
  ", row, mci->nr_csrows);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
768
769
770
771
772
  		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
  		return;
  	}
  
  	chars = snprintf(pos, len + 1, "%s",
079708b91   Douglas Thompson   drivers/edac: cor...
773
  			 mci->csrows[row].channels[0].label);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
774
775
  	len -= chars;
  	pos += chars;
e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
776

da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
777
  	for (chan = 1; (chan < mci->csrows[row].nr_channels) && (len > 0);
052dfb45c   Douglas Thompson   drivers/edac: cle...
778
  		chan++) {
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
779
  		chars = snprintf(pos, len + 1, ":%s",
079708b91   Douglas Thompson   drivers/edac: cor...
780
  				 mci->csrows[row].channels[chan].label);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
781
782
783
  		len -= chars;
  		pos += chars;
  	}
4de78c687   Dave Jiang   drivers/edac: mod...
784
  	if (edac_mc_get_log_ue())
537fba289   Dave Peterson   [PATCH] EDAC: pri...
785
  		edac_mc_printk(mci, KERN_EMERG,
052dfb45c   Douglas Thompson   drivers/edac: cle...
786
787
788
789
790
  			"UE page 0x%lx, offset 0x%lx, grain %d, row %d, "
  			"labels \"%s\": %s
  ", page_frame_number,
  			offset_in_page, mci->csrows[row].grain, row,
  			labels, msg);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
791

4de78c687   Dave Jiang   drivers/edac: mod...
792
  	if (edac_mc_get_panic_on_ue())
e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
793
  		panic("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, "
052dfb45c   Douglas Thompson   drivers/edac: cle...
794
795
796
797
  			"row %d, labels \"%s\": %s
  ", mci->mc_idx,
  			page_frame_number, offset_in_page,
  			mci->csrows[row].grain, row, labels, msg);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
798
799
800
801
  
  	mci->ue_count++;
  	mci->csrows[row].ue_count++;
  }
9110540f7   Dave Peterson   [PATCH] EDAC: use...
802
  EXPORT_SYMBOL_GPL(edac_mc_handle_ue);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
803

e7ecd8910   Dave Peterson   [PATCH] EDAC: for...
804
  void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg)
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
805
  {
4de78c687   Dave Jiang   drivers/edac: mod...
806
  	if (edac_mc_get_panic_on_ue())
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
807
  		panic("EDAC MC%d: Uncorrected Error", mci->mc_idx);
4de78c687   Dave Jiang   drivers/edac: mod...
808
  	if (edac_mc_get_log_ue())
537fba289   Dave Peterson   [PATCH] EDAC: pri...
809
  		edac_mc_printk(mci, KERN_WARNING,
052dfb45c   Douglas Thompson   drivers/edac: cle...
810
811
  			"UE - no information available: %s
  ", msg);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
812
813
814
  	mci->ue_noinfo_count++;
  	mci->ue_count++;
  }
079708b91   Douglas Thompson   drivers/edac: cor...
815
  EXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info);
da9bb1d27   Alan Cox   [PATCH] EDAC: cor...
816

9794f33dd   eric wollesen   [PATCH] EDAC: Add...
817
818
819
820
821
  /*************************************************************
   * On Fully Buffered DIMM modules, this help function is
   * called to process UE events
   */
  void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
052dfb45c   Douglas Thompson   drivers/edac: cle...
822
823
824
  			unsigned int csrow,
  			unsigned int channela,
  			unsigned int channelb, char *msg)
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
825
826
827
828
829
830
831
832
833
  {
  	int len = EDAC_MC_LABEL_LEN * 4;
  	char labels[len + 1];
  	char *pos = labels;
  	int chars;
  
  	if (csrow >= mci->nr_csrows) {
  		/* something is wrong */
  		edac_mc_printk(mci, KERN_ERR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
834
835
836
  			"INTERNAL ERROR: row out of range (%d >= %d)
  ",
  			csrow, mci->nr_csrows);
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
837
838
839
840
841
842
843
  		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
  		return;
  	}
  
  	if (channela >= mci->csrows[csrow].nr_channels) {
  		/* something is wrong */
  		edac_mc_printk(mci, KERN_ERR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
844
845
846
847
  			"INTERNAL ERROR: channel-a out of range "
  			"(%d >= %d)
  ",
  			channela, mci->csrows[csrow].nr_channels);
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
848
849
850
851
852
853
854
  		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
  		return;
  	}
  
  	if (channelb >= mci->csrows[csrow].nr_channels) {
  		/* something is wrong */
  		edac_mc_printk(mci, KERN_ERR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
855
856
857
858
  			"INTERNAL ERROR: channel-b out of range "
  			"(%d >= %d)
  ",
  			channelb, mci->csrows[csrow].nr_channels);
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
859
860
861
862
863
864
865
866
867
868
  		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
  		return;
  	}
  
  	mci->ue_count++;
  	mci->csrows[csrow].ue_count++;
  
  	/* Generate the DIMM labels from the specified channels */
  	chars = snprintf(pos, len + 1, "%s",
  			 mci->csrows[csrow].channels[channela].label);
079708b91   Douglas Thompson   drivers/edac: cor...
869
870
  	len -= chars;
  	pos += chars;
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
871
872
  	chars = snprintf(pos, len + 1, "-%s",
  			 mci->csrows[csrow].channels[channelb].label);
4de78c687   Dave Jiang   drivers/edac: mod...
873
  	if (edac_mc_get_log_ue())
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
874
  		edac_mc_printk(mci, KERN_EMERG,
052dfb45c   Douglas Thompson   drivers/edac: cle...
875
876
877
878
  			"UE row %d, channel-a= %d channel-b= %d "
  			"labels \"%s\": %s
  ", csrow, channela, channelb,
  			labels, msg);
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
879

4de78c687   Dave Jiang   drivers/edac: mod...
880
  	if (edac_mc_get_panic_on_ue())
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
881
  		panic("UE row %d, channel-a= %d channel-b= %d "
052dfb45c   Douglas Thompson   drivers/edac: cle...
882
883
884
  			"labels \"%s\": %s
  ", csrow, channela,
  			channelb, labels, msg);
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
885
886
887
888
889
890
891
892
  }
  EXPORT_SYMBOL(edac_mc_handle_fbd_ue);
  
  /*************************************************************
   * On Fully Buffered DIMM modules, this help function is
   * called to process CE events
   */
  void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
052dfb45c   Douglas Thompson   drivers/edac: cle...
893
  			unsigned int csrow, unsigned int channel, char *msg)
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
894
895
896
897
898
899
  {
  
  	/* Ensure boundary values */
  	if (csrow >= mci->nr_csrows) {
  		/* something is wrong */
  		edac_mc_printk(mci, KERN_ERR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
900
901
902
  			"INTERNAL ERROR: row out of range (%d >= %d)
  ",
  			csrow, mci->nr_csrows);
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
903
904
905
906
907
908
  		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
  		return;
  	}
  	if (channel >= mci->csrows[csrow].nr_channels) {
  		/* something is wrong */
  		edac_mc_printk(mci, KERN_ERR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
909
910
911
  			"INTERNAL ERROR: channel out of range (%d >= %d)
  ",
  			channel, mci->csrows[csrow].nr_channels);
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
912
913
914
  		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
  		return;
  	}
4de78c687   Dave Jiang   drivers/edac: mod...
915
  	if (edac_mc_get_log_ce())
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
916
917
  		/* FIXME - put in DIMM location */
  		edac_mc_printk(mci, KERN_WARNING,
052dfb45c   Douglas Thompson   drivers/edac: cle...
918
919
920
921
  			"CE row %d, channel %d, label \"%s\": %s
  ",
  			csrow, channel,
  			mci->csrows[csrow].channels[channel].label, msg);
9794f33dd   eric wollesen   [PATCH] EDAC: Add...
922
923
924
925
926
  
  	mci->ce_count++;
  	mci->csrows[csrow].ce_count++;
  	mci->csrows[csrow].channels[channel].ce_count++;
  }
079708b91   Douglas Thompson   drivers/edac: cor...
927
  EXPORT_SYMBOL(edac_mc_handle_fbd_ce);