diff options
author | jhb <jhb@FreeBSD.org> | 2010-05-24 15:45:05 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2010-05-24 15:45:05 +0000 |
commit | 9e6f9b1e86420cd7da13f1d568950ab52ff2428f (patch) | |
tree | a9d317cc94e24d55cd44eed2052b78547f4a46f9 /sys/x86 | |
parent | ce59c74efda35e47b39621f57c0fe63e02c7af5b (diff) | |
download | FreeBSD-src-9e6f9b1e86420cd7da13f1d568950ab52ff2428f.zip FreeBSD-src-9e6f9b1e86420cd7da13f1d568950ab52ff2428f.tar.gz |
Add support for corrected machine check interrupts. CMCI is a new local
APIC interrupt that fires when a threshold of corrected machine check
events is reached. CMCI also includes a count of events when reporting
corrected errors in the bank's status register. Note that individual
banks may or may not support CMCI. If they do, each bank includes its own
threshold register that determines when the interrupt fires. Currently
the code uses a very simple strategy where it doubles the threshold on
each interrupt until it succeeds in throttling the interrupt to occur
only once a minute (this interval can be tuned via sysctl). The threshold
is also adjusted on each hourly poll which will lower the threshold once
events stop occurring.
Tested by: Sailaja Bangaru sbappana at yahoo com
MFC after: 1 month
Diffstat (limited to 'sys/x86')
-rw-r--r-- | sys/x86/x86/local_apic.c | 40 |
1 files changed, 39 insertions, 1 deletions
diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c index f40026b..7fec4f6 100644 --- a/sys/x86/x86/local_apic.c +++ b/sys/x86/x86/local_apic.c @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); #include <machine/frame.h> #include <machine/intr_machdep.h> #include <machine/apicvar.h> +#include <machine/mca.h> #include <machine/md_var.h> #include <machine/smp.h> #include <machine/specialreg.h> @@ -130,6 +131,7 @@ static struct lvt lvts[LVT_MAX + 1] = { { 1, 1, 0, 1, APIC_LVT_DM_FIXED, APIC_ERROR_INT }, /* Error */ { 1, 1, 1, 1, APIC_LVT_DM_NMI, 0 }, /* PMC */ { 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_THERMAL_INT }, /* Thermal */ + { 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_CMC_INT }, /* CMCI */ }; static inthand_t *ioint_handlers[] = { @@ -235,6 +237,9 @@ lapic_init(vm_paddr_t addr) setidt(APIC_ERROR_INT, IDTVEC(errorint), SDT_APIC, SEL_KPL, GSEL_APIC); /* XXX: Thermal interrupt */ + + /* Local APIC CMCI. */ + setidt(APIC_CMC_INT, IDTVEC(cmcint), SDT_APICT, SEL_KPL, GSEL_APIC); } /* @@ -260,7 +265,7 @@ lapic_create(u_int apic_id, int boot_cpu) */ lapics[apic_id].la_present = 1; lapics[apic_id].la_id = apic_id; - for (i = 0; i < LVT_MAX; i++) { + for (i = 0; i <= LVT_MAX; i++) { lapics[apic_id].la_lvts[i] = lvts[i]; lapics[apic_id].la_lvts[i].lvt_active = 0; } @@ -290,6 +295,7 @@ lapic_dump(const char* str) printf(" timer: 0x%08x therm: 0x%08x err: 0x%08x pmc: 0x%08x\n", lapic->lvt_timer, lapic->lvt_thermal, lapic->lvt_error, lapic->lvt_pcint); + printf(" cmci: 0x%08x\n", lapic->lvt_cmci); } void @@ -341,6 +347,10 @@ lapic_setup(int boot) /* XXX: Thermal LVT */ + /* Program the CMCI LVT entry if present. */ + if (maxlvt >= LVT_CMCI) + lapic->lvt_cmci = lvt_mode(la, LVT_CMCI, lapic->lvt_cmci); + intr_restore(eflags); } @@ -838,6 +848,34 @@ lapic_timer_enable_intr(void) } void +lapic_handle_cmc(void) +{ + + lapic_eoi(); + cmc_intr(); +} + +/* + * Called from the mca_init() to activate the CMC interrupt if this CPU is + * responsible for monitoring any MC banks for CMC events. Since mca_init() + * is called prior to lapic_setup() during boot, this just needs to unmask + * this CPU's LVT_CMCI entry. + */ +void +lapic_enable_cmc(void) +{ + u_int apic_id; + + apic_id = PCPU_GET(apic_id); + KASSERT(lapics[apic_id].la_present, + ("%s: missing APIC %u", __func__, apic_id)); + lapics[apic_id].la_lvts[LVT_CMCI].lvt_masked = 0; + lapics[apic_id].la_lvts[LVT_CMCI].lvt_active = 1; + if (bootverbose) + printf("lapic%u: CMCI unmasked\n", apic_id); +} + +void lapic_handle_error(void) { u_int32_t esr; |