2 * edac_mc kernel module
3 * (C) 2005, 2006 Linux Networx (http://lnxi.com)
4 * This file may be distributed under the terms of the
5 * GNU General Public License.
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/
11 * Modified by Dave Peterson and Doug Thompson
15 #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>
28 #include <linux/ctype.h>
29 #include <linux/edac.h>
30 #include <asm/uaccess.h>
33 #include "edac_core.h"
34 #include "edac_module.h"
36 /* lock to memory controller's control array */
37 static DEFINE_MUTEX(mem_ctls_mutex);
38 static LIST_HEAD(mc_devices);
40 #ifdef CONFIG_EDAC_DEBUG
42 static void edac_mc_dump_channel(struct rank_info *chan)
44 debugf4("\tchannel = %p\n", chan);
45 debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
46 debugf4("\tchannel->csrow = %p\n\n", chan->csrow);
47 debugf4("\tdimm->ce_count = %d\n", chan->dimm->ce_count);
48 debugf4("\tdimm->label = '%s'\n", chan->dimm->label);
49 debugf4("\tdimm->nr_pages = 0x%x\n", chan->dimm->nr_pages);
52 static void edac_mc_dump_csrow(struct csrow_info *csrow)
54 debugf4("\tcsrow = %p\n", csrow);
55 debugf4("\tcsrow->csrow_idx = %d\n", csrow->csrow_idx);
56 debugf4("\tcsrow->first_page = 0x%lx\n", csrow->first_page);
57 debugf4("\tcsrow->last_page = 0x%lx\n", csrow->last_page);
58 debugf4("\tcsrow->page_mask = 0x%lx\n", csrow->page_mask);
59 debugf4("\tcsrow->nr_channels = %d\n", csrow->nr_channels);
60 debugf4("\tcsrow->channels = %p\n", csrow->channels);
61 debugf4("\tcsrow->mci = %p\n\n", csrow->mci);
64 static void edac_mc_dump_mci(struct mem_ctl_info *mci)
66 debugf3("\tmci = %p\n", mci);
67 debugf3("\tmci->mtype_cap = %lx\n", mci->mtype_cap);
68 debugf3("\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
69 debugf3("\tmci->edac_cap = %lx\n", mci->edac_cap);
70 debugf4("\tmci->edac_check = %p\n", mci->edac_check);
71 debugf3("\tmci->nr_csrows = %d, csrows = %p\n",
72 mci->nr_csrows, mci->csrows);
73 debugf3("\tdev = %p\n", mci->dev);
74 debugf3("\tmod_name:ctl_name = %s:%s\n", mci->mod_name, mci->ctl_name);
75 debugf3("\tpvt_info = %p\n\n", mci->pvt_info);
78 #endif /* CONFIG_EDAC_DEBUG */
81 * keep those in sync with the enum mem_type
83 const char *edac_mem_types[] = {
85 "Reserved csrow type",
88 "Extended data out RAM",
89 "Burst Extended data out RAM",
90 "Single data rate SDRAM",
91 "Registered single data rate SDRAM",
92 "Double data rate SDRAM",
93 "Registered Double data rate SDRAM",
95 "Unbuffered DDR2 RAM",
96 "Fully buffered DDR2",
97 "Registered DDR2 RAM",
99 "Unbuffered DDR3 RAM",
100 "Registered DDR3 RAM",
102 EXPORT_SYMBOL_GPL(edac_mem_types);
104 /* 'ptr' points to a possibly unaligned item X such that sizeof(X) is 'size'.
105 * Adjust 'ptr' so that its alignment is at least as stringent as what the
106 * compiler would provide for X and return the aligned result.
108 * If 'size' is a constant, the compiler will optimize this whole function
109 * down to either a no-op or the addition of a constant to the value of 'ptr'.
111 void *edac_align_ptr(void *ptr, unsigned size)
115 /* Here we assume that the alignment of a "long long" is the most
116 * stringent alignment that the compiler will ever provide by default.
117 * As far as I know, this is a reasonable assumption.
119 if (size > sizeof(long))
120 align = sizeof(long long);
121 else if (size > sizeof(int))
122 align = sizeof(long);
123 else if (size > sizeof(short))
125 else if (size > sizeof(char))
126 align = sizeof(short);
135 return (void *)(((unsigned long)ptr) + align - r);
139 * edac_mc_alloc: Allocate a struct mem_ctl_info structure
140 * @size_pvt: size of private storage needed
141 * @nr_csrows: Number of CWROWS needed for this MC
142 * @nr_chans: Number of channels for the MC
144 * Everything is kmalloc'ed as one big chunk - more efficient.
145 * Only can be used if all structures have the same lifetime - otherwise
146 * you have to allocate and initialize your own structures.
148 * Use edac_mc_free() to free mc structures allocated by this function.
151 * NULL allocation failed
152 * struct mem_ctl_info pointer
154 struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
155 unsigned nr_chans, int edac_index)
157 struct mem_ctl_info *mci;
158 struct csrow_info *csi, *csrow;
159 struct rank_info *chi, *chp, *chan;
160 struct dimm_info *dimm;
166 /* Figure out the offsets of the various items from the start of an mc
167 * structure. We want the alignment of each item to be at least as
168 * stringent as what the compiler would provide if we could simply
169 * hardcode everything into a single struct.
171 mci = (struct mem_ctl_info *)0;
172 csi = edac_align_ptr(&mci[1], sizeof(*csi));
173 chi = edac_align_ptr(&csi[nr_csrows], sizeof(*chi));
174 dimm = edac_align_ptr(&chi[nr_chans * nr_csrows], sizeof(*dimm));
175 pvt = edac_align_ptr(&dimm[nr_chans * nr_csrows], sz_pvt);
176 size = ((unsigned long)pvt) + sz_pvt;
178 mci = kzalloc(size, GFP_KERNEL);
182 /* Adjust pointers so they point within the memory we just allocated
183 * rather than an imaginary chunk of memory located at address 0.
185 csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
186 chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi));
187 dimm = (struct dimm_info *)(((char *)mci) + ((unsigned long)dimm));
188 pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
190 /* setup index and various internal pointers */
191 mci->mc_idx = edac_index;
195 mci->nr_csrows = nr_csrows;
198 * For now, assumes that a per-csrow arrangement for dimms.
199 * This will be latter changed.
203 for (row = 0; row < nr_csrows; row++) {
205 csrow->csrow_idx = row;
207 csrow->nr_channels = nr_chans;
208 chp = &chi[row * nr_chans];
209 csrow->channels = chp;
211 for (chn = 0; chn < nr_chans; chn++) {
213 chan->chan_idx = chn;
216 mci->csrows[row].channels[chn].dimm = dimm;
218 dimm->csrow_channel = chn;
224 mci->op_state = OP_ALLOC;
225 INIT_LIST_HEAD(&mci->grp_kobj_list);
228 * Initialize the 'root' kobj for the edac_mc controller
230 err = edac_mc_register_sysfs_main_kobj(mci);
236 /* at this point, the root kobj is valid, and in order to
237 * 'free' the object, then the function:
238 * edac_mc_unregister_sysfs_main_kobj() must be called
239 * which will perform kobj unregistration and the actual free
240 * will occur during the kobject callback operation
244 EXPORT_SYMBOL_GPL(edac_mc_alloc);
248 * 'Free' a previously allocated 'mci' structure
249 * @mci: pointer to a struct mem_ctl_info structure
251 void edac_mc_free(struct mem_ctl_info *mci)
253 debugf1("%s()\n", __func__);
255 edac_mc_unregister_sysfs_main_kobj(mci);
257 /* free the mci instance memory here */
260 EXPORT_SYMBOL_GPL(edac_mc_free);
266 * scan list of controllers looking for the one that manages
268 * @dev: pointer to a struct device related with the MCI
270 struct mem_ctl_info *find_mci_by_dev(struct device *dev)
272 struct mem_ctl_info *mci;
273 struct list_head *item;
275 debugf3("%s()\n", __func__);
277 list_for_each(item, &mc_devices) {
278 mci = list_entry(item, struct mem_ctl_info, link);
286 EXPORT_SYMBOL_GPL(find_mci_by_dev);
289 * handler for EDAC to check if NMI type handler has asserted interrupt
291 static int edac_mc_assert_error_check_and_clear(void)
295 if (edac_op_state == EDAC_OPSTATE_POLL)
298 old_state = edac_err_assert;
305 * edac_mc_workq_function
306 * performs the operation scheduled by a workq request
308 static void edac_mc_workq_function(struct work_struct *work_req)
310 struct delayed_work *d_work = to_delayed_work(work_req);
311 struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
313 mutex_lock(&mem_ctls_mutex);
315 /* if this control struct has movd to offline state, we are done */
316 if (mci->op_state == OP_OFFLINE) {
317 mutex_unlock(&mem_ctls_mutex);
321 /* Only poll controllers that are running polled and have a check */
322 if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL))
323 mci->edac_check(mci);
325 mutex_unlock(&mem_ctls_mutex);
328 queue_delayed_work(edac_workqueue, &mci->work,
329 msecs_to_jiffies(edac_mc_get_poll_msec()));
333 * edac_mc_workq_setup
334 * initialize a workq item for this mci
335 * passing in the new delay period in msec
339 * called with the mem_ctls_mutex held
341 static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
343 debugf0("%s()\n", __func__);
345 /* if this instance is not in the POLL state, then simply return */
346 if (mci->op_state != OP_RUNNING_POLL)
349 INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
350 queue_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
354 * edac_mc_workq_teardown
355 * stop the workq processing on this mci
359 * called WITHOUT lock held
361 static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
365 if (mci->op_state != OP_RUNNING_POLL)
368 status = cancel_delayed_work(&mci->work);
370 debugf0("%s() not canceled, flush the queue\n",
373 /* workq instance might be running, wait for it */
374 flush_workqueue(edac_workqueue);
379 * edac_mc_reset_delay_period(unsigned long value)
381 * user space has updated our poll period value, need to
382 * reset our workq delays
384 void edac_mc_reset_delay_period(int value)
386 struct mem_ctl_info *mci;
387 struct list_head *item;
389 mutex_lock(&mem_ctls_mutex);
391 /* scan the list and turn off all workq timers, doing so under lock
393 list_for_each(item, &mc_devices) {
394 mci = list_entry(item, struct mem_ctl_info, link);
396 if (mci->op_state == OP_RUNNING_POLL)
397 cancel_delayed_work(&mci->work);
400 mutex_unlock(&mem_ctls_mutex);
403 /* re-walk the list, and reset the poll delay */
404 mutex_lock(&mem_ctls_mutex);
406 list_for_each(item, &mc_devices) {
407 mci = list_entry(item, struct mem_ctl_info, link);
409 edac_mc_workq_setup(mci, (unsigned long) value);
412 mutex_unlock(&mem_ctls_mutex);
417 /* Return 0 on success, 1 on failure.
418 * Before calling this function, caller must
419 * assign a unique value to mci->mc_idx.
423 * called with the mem_ctls_mutex lock held
425 static int add_mc_to_global_list(struct mem_ctl_info *mci)
427 struct list_head *item, *insert_before;
428 struct mem_ctl_info *p;
430 insert_before = &mc_devices;
432 p = find_mci_by_dev(mci->dev);
433 if (unlikely(p != NULL))
436 list_for_each(item, &mc_devices) {
437 p = list_entry(item, struct mem_ctl_info, link);
439 if (p->mc_idx >= mci->mc_idx) {
440 if (unlikely(p->mc_idx == mci->mc_idx))
443 insert_before = item;
448 list_add_tail_rcu(&mci->link, insert_before);
449 atomic_inc(&edac_handlers);
453 edac_printk(KERN_WARNING, EDAC_MC,
454 "%s (%s) %s %s already assigned %d\n", dev_name(p->dev),
455 edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
459 edac_printk(KERN_WARNING, EDAC_MC,
460 "bug in low-level driver: attempt to assign\n"
461 " duplicate mc_idx %d in %s()\n", p->mc_idx, __func__);
465 static void del_mc_from_global_list(struct mem_ctl_info *mci)
467 atomic_dec(&edac_handlers);
468 list_del_rcu(&mci->link);
470 /* these are for safe removal of devices from global list while
471 * NMI handlers may be traversing list
474 INIT_LIST_HEAD(&mci->link);
478 * edac_mc_find: Search for a mem_ctl_info structure whose index is 'idx'.
480 * If found, return a pointer to the structure.
483 * Caller must hold mem_ctls_mutex.
485 struct mem_ctl_info *edac_mc_find(int idx)
487 struct list_head *item;
488 struct mem_ctl_info *mci;
490 list_for_each(item, &mc_devices) {
491 mci = list_entry(item, struct mem_ctl_info, link);
493 if (mci->mc_idx >= idx) {
494 if (mci->mc_idx == idx)
503 EXPORT_SYMBOL(edac_mc_find);
506 * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and
507 * create sysfs entries associated with mci structure
508 * @mci: pointer to the mci structure to be added to the list
509 * @mc_idx: A unique numeric identifier to be assigned to the 'mci' structure.
516 /* FIXME - should a warning be printed if no error detection? correction? */
517 int edac_mc_add_mc(struct mem_ctl_info *mci)
519 debugf0("%s()\n", __func__);
521 #ifdef CONFIG_EDAC_DEBUG
522 if (edac_debug_level >= 3)
523 edac_mc_dump_mci(mci);
525 if (edac_debug_level >= 4) {
528 for (i = 0; i < mci->nr_csrows; i++) {
531 edac_mc_dump_csrow(&mci->csrows[i]);
532 for (j = 0; j < mci->csrows[i].nr_channels; j++)
533 edac_mc_dump_channel(&mci->csrows[i].
538 mutex_lock(&mem_ctls_mutex);
540 if (add_mc_to_global_list(mci))
543 /* set load time so that error rate can be tracked */
544 mci->start_time = jiffies;
546 if (edac_create_sysfs_mci_device(mci)) {
547 edac_mc_printk(mci, KERN_WARNING,
548 "failed to create sysfs device\n");
552 /* If there IS a check routine, then we are running POLLED */
553 if (mci->edac_check != NULL) {
554 /* This instance is NOW RUNNING */
555 mci->op_state = OP_RUNNING_POLL;
557 edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
559 mci->op_state = OP_RUNNING_INTERRUPT;
562 /* Report action taken */
563 edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':"
564 " DEV %s\n", mci->mod_name, mci->ctl_name, edac_dev_name(mci));
566 mutex_unlock(&mem_ctls_mutex);
570 del_mc_from_global_list(mci);
573 mutex_unlock(&mem_ctls_mutex);
576 EXPORT_SYMBOL_GPL(edac_mc_add_mc);
579 * edac_mc_del_mc: Remove sysfs entries for specified mci structure and
580 * remove mci structure from global list
581 * @pdev: Pointer to 'struct device' representing mci structure to remove.
583 * Return pointer to removed mci structure, or NULL if device not found.
585 struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
587 struct mem_ctl_info *mci;
589 debugf0("%s()\n", __func__);
591 mutex_lock(&mem_ctls_mutex);
593 /* find the requested mci struct in the global list */
594 mci = find_mci_by_dev(dev);
596 mutex_unlock(&mem_ctls_mutex);
600 del_mc_from_global_list(mci);
601 mutex_unlock(&mem_ctls_mutex);
603 /* flush workq processes */
604 edac_mc_workq_teardown(mci);
606 /* marking MCI offline */
607 mci->op_state = OP_OFFLINE;
609 /* remove from sysfs */
610 edac_remove_sysfs_mci_device(mci);
612 edac_printk(KERN_INFO, EDAC_MC,
613 "Removed device %d for %s %s: DEV %s\n", mci->mc_idx,
614 mci->mod_name, mci->ctl_name, edac_dev_name(mci));
618 EXPORT_SYMBOL_GPL(edac_mc_del_mc);
620 static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
625 unsigned long flags = 0;
627 debugf3("%s()\n", __func__);
629 /* ECC error page was not in our memory. Ignore it. */
630 if (!pfn_valid(page))
633 /* Find the actual page structure then map it and fix */
634 pg = pfn_to_page(page);
637 local_irq_save(flags);
639 virt_addr = kmap_atomic(pg);
641 /* Perform architecture specific atomic scrub operation */
642 atomic_scrub(virt_addr + offset, size);
644 /* Unmap and complete */
645 kunmap_atomic(virt_addr);
648 local_irq_restore(flags);
651 /* FIXME - should return -1 */
652 int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
654 struct csrow_info *csrows = mci->csrows;
657 debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page);
660 for (i = 0; i < mci->nr_csrows; i++) {
661 struct csrow_info *csrow = &csrows[i];
663 for (j = 0; j < csrow->nr_channels; j++) {
664 struct dimm_info *dimm = csrow->channels[j].dimm;
670 debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) "
671 "mask(0x%lx)\n", mci->mc_idx, __func__,
672 csrow->first_page, page, csrow->last_page,
675 if ((page >= csrow->first_page) &&
676 (page <= csrow->last_page) &&
677 ((page & csrow->page_mask) ==
678 (csrow->first_page & csrow->page_mask))) {
685 edac_mc_printk(mci, KERN_ERR,
686 "could not look up page error address %lx\n",
687 (unsigned long)page);
691 EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
693 /* FIXME - setable log (warning/emerg) levels */
694 /* FIXME - integrate with evlog: http://evlog.sourceforge.net/ */
695 void edac_mc_handle_ce(struct mem_ctl_info *mci,
696 unsigned long page_frame_number,
697 unsigned long offset_in_page, unsigned long syndrome,
698 int row, int channel, const char *msg)
700 unsigned long remapped_page;
704 debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
706 /* FIXME - maybe make panic on INTERNAL ERROR an option */
707 if (row >= mci->nr_csrows || row < 0) {
708 /* something is wrong */
709 edac_mc_printk(mci, KERN_ERR,
710 "INTERNAL ERROR: row out of range "
711 "(%d >= %d)\n", row, mci->nr_csrows);
712 edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
716 if (channel >= mci->csrows[row].nr_channels || channel < 0) {
717 /* something is wrong */
718 edac_mc_printk(mci, KERN_ERR,
719 "INTERNAL ERROR: channel out of range "
720 "(%d >= %d)\n", channel,
721 mci->csrows[row].nr_channels);
722 edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
726 label = mci->csrows[row].channels[channel].dimm->label;
727 grain = mci->csrows[row].channels[channel].dimm->grain;
729 if (edac_mc_get_log_ce())
730 /* FIXME - put in DIMM location */
731 edac_mc_printk(mci, KERN_WARNING,
732 "CE page 0x%lx, offset 0x%lx, grain %d, syndrome "
733 "0x%lx, row %d, channel %d, label \"%s\": %s\n",
734 page_frame_number, offset_in_page,
735 grain, syndrome, row, channel,
739 mci->csrows[row].ce_count++;
740 mci->csrows[row].channels[channel].dimm->ce_count++;
741 mci->csrows[row].channels[channel].ce_count++;
743 if (mci->scrub_mode & SCRUB_SW_SRC) {
745 * Some MC's can remap memory so that it is still available
746 * at a different address when PCI devices map into memory.
747 * MC's that can't do this lose the memory where PCI devices
748 * are mapped. This mapping is MC dependent and so we call
749 * back into the MC driver for it to map the MC page to
750 * a physical (CPU) page which can then be mapped to a virtual
751 * page - which can then be scrubbed.
753 remapped_page = mci->ctl_page_to_phys ?
754 mci->ctl_page_to_phys(mci, page_frame_number) :
757 edac_mc_scrub_block(remapped_page, offset_in_page, grain);
760 EXPORT_SYMBOL_GPL(edac_mc_handle_ce);
762 void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg)
764 if (edac_mc_get_log_ce())
765 edac_mc_printk(mci, KERN_WARNING,
766 "CE - no information available: %s\n", msg);
768 mci->ce_noinfo_count++;
771 EXPORT_SYMBOL_GPL(edac_mc_handle_ce_no_info);
773 void edac_mc_handle_ue(struct mem_ctl_info *mci,
774 unsigned long page_frame_number,
775 unsigned long offset_in_page, int row, const char *msg)
777 int len = EDAC_MC_LABEL_LEN * 4;
778 char labels[len + 1];
785 debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
787 /* FIXME - maybe make panic on INTERNAL ERROR an option */
788 if (row >= mci->nr_csrows || row < 0) {
789 /* something is wrong */
790 edac_mc_printk(mci, KERN_ERR,
791 "INTERNAL ERROR: row out of range "
792 "(%d >= %d)\n", row, mci->nr_csrows);
793 edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
797 grain = mci->csrows[row].channels[0].dimm->grain;
798 label = mci->csrows[row].channels[0].dimm->label;
799 chars = snprintf(pos, len + 1, "%s", label);
803 for (chan = 1; (chan < mci->csrows[row].nr_channels) && (len > 0);
805 label = mci->csrows[row].channels[chan].dimm->label;
806 chars = snprintf(pos, len + 1, ":%s", label);
811 if (edac_mc_get_log_ue())
812 edac_mc_printk(mci, KERN_EMERG,
813 "UE page 0x%lx, offset 0x%lx, grain %d, row %d, "
814 "labels \"%s\": %s\n", page_frame_number,
815 offset_in_page, grain, row, labels, msg);
817 if (edac_mc_get_panic_on_ue())
818 panic("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, "
819 "row %d, labels \"%s\": %s\n", mci->mc_idx,
820 page_frame_number, offset_in_page,
821 grain, row, labels, msg);
824 mci->csrows[row].ue_count++;
826 EXPORT_SYMBOL_GPL(edac_mc_handle_ue);
828 void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg)
830 if (edac_mc_get_panic_on_ue())
831 panic("EDAC MC%d: Uncorrected Error", mci->mc_idx);
833 if (edac_mc_get_log_ue())
834 edac_mc_printk(mci, KERN_WARNING,
835 "UE - no information available: %s\n", msg);
836 mci->ue_noinfo_count++;
839 EXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info);
841 /*************************************************************
842 * On Fully Buffered DIMM modules, this help function is
843 * called to process UE events
845 void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
847 unsigned int channela,
848 unsigned int channelb, char *msg)
850 int len = EDAC_MC_LABEL_LEN * 4;
851 char labels[len + 1];
856 if (csrow >= mci->nr_csrows) {
857 /* something is wrong */
858 edac_mc_printk(mci, KERN_ERR,
859 "INTERNAL ERROR: row out of range (%d >= %d)\n",
860 csrow, mci->nr_csrows);
861 edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
865 if (channela >= mci->csrows[csrow].nr_channels) {
866 /* something is wrong */
867 edac_mc_printk(mci, KERN_ERR,
868 "INTERNAL ERROR: channel-a out of range "
870 channela, mci->csrows[csrow].nr_channels);
871 edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
875 if (channelb >= mci->csrows[csrow].nr_channels) {
876 /* something is wrong */
877 edac_mc_printk(mci, KERN_ERR,
878 "INTERNAL ERROR: channel-b out of range "
880 channelb, mci->csrows[csrow].nr_channels);
881 edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
886 mci->csrows[csrow].ue_count++;
888 /* Generate the DIMM labels from the specified channels */
889 label = mci->csrows[csrow].channels[channela].dimm->label;
890 chars = snprintf(pos, len + 1, "%s", label);
894 chars = snprintf(pos, len + 1, "-%s",
895 mci->csrows[csrow].channels[channelb].dimm->label);
897 if (edac_mc_get_log_ue())
898 edac_mc_printk(mci, KERN_EMERG,
899 "UE row %d, channel-a= %d channel-b= %d "
900 "labels \"%s\": %s\n", csrow, channela, channelb,
903 if (edac_mc_get_panic_on_ue())
904 panic("UE row %d, channel-a= %d channel-b= %d "
905 "labels \"%s\": %s\n", csrow, channela,
906 channelb, labels, msg);
908 EXPORT_SYMBOL(edac_mc_handle_fbd_ue);
910 /*************************************************************
911 * On Fully Buffered DIMM modules, this help function is
912 * called to process CE events
914 void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
915 unsigned int csrow, unsigned int channel, char *msg)
919 /* Ensure boundary values */
920 if (csrow >= mci->nr_csrows) {
921 /* something is wrong */
922 edac_mc_printk(mci, KERN_ERR,
923 "INTERNAL ERROR: row out of range (%d >= %d)\n",
924 csrow, mci->nr_csrows);
925 edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
928 if (channel >= mci->csrows[csrow].nr_channels) {
929 /* something is wrong */
930 edac_mc_printk(mci, KERN_ERR,
931 "INTERNAL ERROR: channel out of range (%d >= %d)\n",
932 channel, mci->csrows[csrow].nr_channels);
933 edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
937 label = mci->csrows[csrow].channels[channel].dimm->label;
939 if (edac_mc_get_log_ce())
940 /* FIXME - put in DIMM location */
941 edac_mc_printk(mci, KERN_WARNING,
942 "CE row %d, channel %d, label \"%s\": %s\n",
943 csrow, channel, label, msg);
946 mci->csrows[csrow].ce_count++;
947 mci->csrows[csrow].channels[channel].dimm->ce_count++;
948 mci->csrows[csrow].channels[channel].ce_count++;
950 EXPORT_SYMBOL(edac_mc_handle_fbd_ce);