Changeset View
Standalone View
sys/x86/x86/mca.c
Show First 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | |||||
struct amd_et_state { | struct amd_et_state { | ||||
int cur_threshold; | int cur_threshold; | ||||
time_t last_intr; | time_t last_intr; | ||||
}; | }; | ||||
#endif | #endif | ||||
struct mca_internal { | struct mca_internal { | ||||
struct mca_record rec; | struct mca_record rec; | ||||
int logged; | |||||
STAILQ_ENTRY(mca_internal) link; | STAILQ_ENTRY(mca_internal) link; | ||||
}; | }; | ||||
struct mca_enumerator_ops { | struct mca_enumerator_ops { | ||||
unsigned int (*ctl)(int); | unsigned int (*ctl)(int); | ||||
unsigned int (*status)(int); | unsigned int (*status)(int); | ||||
unsigned int (*addr)(int); | unsigned int (*addr)(int); | ||||
unsigned int (*misc)(int); | unsigned int (*misc)(int); | ||||
}; | }; | ||||
static MALLOC_DEFINE(M_MCA, "MCA", "Machine Check Architecture"); | static MALLOC_DEFINE(M_MCA, "MCA", "Machine Check Architecture"); | ||||
static volatile int mca_count; /* Number of records stored. */ | static volatile int mca_count; /* Number of records stored. */ | ||||
static int mca_banks; /* Number of per-CPU register banks. */ | static int mca_banks; /* Number of per-CPU register banks. */ | ||||
static int mca_maxcount = -1; /* Limit on records stored. (-1 = unlimited) */ | |||||
static SYSCTL_NODE(_hw, OID_AUTO, mca, CTLFLAG_RD, NULL, | static SYSCTL_NODE(_hw, OID_AUTO, mca, CTLFLAG_RD, NULL, | ||||
"Machine Check Architecture"); | "Machine Check Architecture"); | ||||
static int mca_enabled = 1; | static int mca_enabled = 1; | ||||
SYSCTL_INT(_hw_mca, OID_AUTO, enabled, CTLFLAG_RDTUN, &mca_enabled, 0, | SYSCTL_INT(_hw_mca, OID_AUTO, enabled, CTLFLAG_RDTUN, &mca_enabled, 0, | ||||
"Administrative toggle for machine check support"); | "Administrative toggle for machine check support"); | ||||
static int amd10h_L1TP = 1; | static int amd10h_L1TP = 1; | ||||
SYSCTL_INT(_hw_mca, OID_AUTO, amd10h_L1TP, CTLFLAG_RDTUN, &amd10h_L1TP, 0, | SYSCTL_INT(_hw_mca, OID_AUTO, amd10h_L1TP, CTLFLAG_RDTUN, &amd10h_L1TP, 0, | ||||
"Administrative toggle for logging of level one TLB parity (L1TP) errors"); | "Administrative toggle for logging of level one TLB parity (L1TP) errors"); | ||||
static int intel6h_HSD131; | static int intel6h_HSD131; | ||||
SYSCTL_INT(_hw_mca, OID_AUTO, intel6h_HSD131, CTLFLAG_RDTUN, &intel6h_HSD131, 0, | SYSCTL_INT(_hw_mca, OID_AUTO, intel6h_HSD131, CTLFLAG_RDTUN, &intel6h_HSD131, 0, | ||||
"Administrative toggle for logging of spurious corrected errors"); | "Administrative toggle for logging of spurious corrected errors"); | ||||
int workaround_erratum383; | int workaround_erratum383; | ||||
SYSCTL_INT(_hw_mca, OID_AUTO, erratum383, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_mca, OID_AUTO, erratum383, CTLFLAG_RDTUN, | ||||
&workaround_erratum383, 0, | &workaround_erratum383, 0, | ||||
"Is the workaround for Erratum 383 on AMD Family 10h processors enabled?"); | "Is the workaround for Erratum 383 on AMD Family 10h processors enabled?"); | ||||
static STAILQ_HEAD(, mca_internal) mca_freelist; | static STAILQ_HEAD(, mca_internal) mca_freelist; | ||||
jhb: Extra blank line? | |||||
Done Inline ActionsRemoved. jtl: Removed. | |||||
static int mca_freecount; | static int mca_freecount; | ||||
static STAILQ_HEAD(, mca_internal) mca_records; | static STAILQ_HEAD(, mca_internal) mca_records; | ||||
static STAILQ_HEAD(, mca_internal) mca_pending; | |||||
static struct callout mca_timer; | static struct callout mca_timer; | ||||
static int mca_ticks = 3600; /* Check hourly by default. */ | static int mca_ticks = 3600; /* Check hourly by default. */ | ||||
static struct taskqueue *mca_tq; | static struct taskqueue *mca_tq; | ||||
static struct task mca_refill_task, mca_scan_task; | static struct task mca_resize_task, mca_scan_task; | ||||
static struct mtx mca_lock; | static struct mtx mca_lock; | ||||
static unsigned int | static unsigned int | ||||
mca_ia32_ctl_reg(int bank) | mca_ia32_ctl_reg(int bank) | ||||
{ | { | ||||
return (MSR_MC_CTL(bank)); | return (MSR_MC_CTL(bank)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 412 Lines • ▼ Show 20 Lines | mca_check_status(int bank, struct mca_record *rec) | ||||
if (!(rec->mr_status & (MC_STATUS_PCC | MC_STATUS_UC))) { | if (!(rec->mr_status & (MC_STATUS_PCC | MC_STATUS_UC))) { | ||||
wrmsr(mca_msr_ops.status(bank), 0); | wrmsr(mca_msr_ops.status(bank), 0); | ||||
do_cpuid(0, p); | do_cpuid(0, p); | ||||
} | } | ||||
return (1); | return (1); | ||||
} | } | ||||
static void | static void | ||||
mca_fill_freelist(void) | mca_resize_freelist(void) | ||||
Done Inline ActionsThis should perhaps be renamed to 'mca_resize_freelist' since it can now shrink as well as grow it. jhb: This should perhaps be renamed to 'mca_resize_freelist' since it can now shrink as well as grow… | |||||
Done Inline ActionsDone. I also changed other "refill" things to "resize". jtl: Done. I also changed other "refill" things to "resize". | |||||
{ | { | ||||
struct mca_internal *rec; | struct mca_internal *next, *rec; | ||||
int desired; | STAILQ_HEAD(, mca_internal) tmplist; | ||||
int desired_max, desired_min, count; | |||||
Done Inline ActionsI have a slight preference for 'desired_max, desired_min' instead of 'low' and 'high'. jhb: I have a slight preference for 'desired_max, desired_min' instead of 'low' and 'high'. | |||||
Done Inline ActionsChanged. (Doh! count is now not in alphabetical order. I'll try to remember to fix that before committing.) jtl: Changed. (Doh! count is now not in alphabetical order. I'll try to remember to fix that before… | |||||
/* | /* | ||||
* Ensure we have at least one record for each bank and one | * Ensure we have at least one record for each bank and one | ||||
* record per CPU. | * record per CPU, but no more than twice that amount. | ||||
Done Inline ActionsMaybe shorten a bit by saying something like: * Ensure we have at least one record fore each bank and * one record per CPU, but no more than twice that amount. * Free records first in case a concurrent thread uses records * while this thread is freeing them. One thing you might consider is to do a batched free to avoid banging on the lock as much. Something like this: STAILQ_HEAD(, mca_internal) tofree; struct mca_internal *rec, *next; int desired_min, desired_max; desired_min = ... desired_max = ... mtx_lock_spin(&mca_lock); if (mca_freecount > desired_max) { STAILQ_INIT(&tofree); while (mca_freecount > desired_max) { rec = STAILQ_FIRST(&mca_freelist); STAILQ_REMOVE_HEAD(&mca_freelist, link); STAILQ_INSERT_TAIL(&tofree, rec, link); mca_freecount--; } mtx_unlock_spin(&mca_lock); STAILQ_FOREACH_SAFE(rec, &tofree, link, next) { free(rec, M_MCA); } mtx_lock_spin(&mca_lock); } while (mca_freecount < desired_min) { ... } jhb: Maybe shorten a bit by saying something like:
```
* Ensure we have at least one record fore… | |||||
Done Inline ActionsI mostly did this, but implemented it slightly differently. jtl: I mostly did this, but implemented it slightly differently. | |||||
*/ | */ | ||||
desired = imax(mp_ncpus, mca_banks); | desired_min = imax(mp_ncpus, mca_banks); | ||||
desired_max = imax(mp_ncpus, mca_banks) * 2; | |||||
STAILQ_INIT(&tmplist); | |||||
mtx_lock_spin(&mca_lock); | mtx_lock_spin(&mca_lock); | ||||
while (mca_freecount < desired) { | while (mca_freecount > desired_max) { | ||||
rec = STAILQ_FIRST(&mca_freelist); | |||||
KASSERT(rec != NULL, ("mca_freecount is %d, but list is empty", | |||||
mca_freecount)); | |||||
STAILQ_REMOVE_HEAD(&mca_freelist, link); | |||||
mca_freecount--; | |||||
STAILQ_INSERT_TAIL(&tmplist, rec, link); | |||||
} | |||||
while (mca_freecount < desired_min) { | |||||
count = desired_min - mca_freecount; | |||||
mtx_unlock_spin(&mca_lock); | mtx_unlock_spin(&mca_lock); | ||||
while (count--) { | |||||
rec = malloc(sizeof(*rec), M_MCA, M_WAITOK); | rec = malloc(sizeof(*rec), M_MCA, M_WAITOK); | ||||
STAILQ_INSERT_TAIL(&tmplist, rec, link); | |||||
} | |||||
mtx_lock_spin(&mca_lock); | mtx_lock_spin(&mca_lock); | ||||
STAILQ_FOREACH_SAFE(rec, &tmplist, link, next) { | |||||
jhbUnsubmitted Done Inline ActionsFor this I think you can use STAILQ_CONCAT to join the lists as a single O(1) operation. mtx_lock_spin(&mca_lock); STAILQ_CONCAT(&mca_freelist, &tmplist); } jhb: For this I think you can use STAILQ_CONCAT to join the lists as a single O(1) operation.
```… | |||||
jtlAuthorUnsubmitted Done Inline ActionsGood point! I'll make the change. jtl: Good point! I'll make the change. | |||||
STAILQ_INSERT_TAIL(&mca_freelist, rec, link); | STAILQ_INSERT_TAIL(&mca_freelist, rec, link); | ||||
mca_freecount++; | mca_freecount++; | ||||
} | } | ||||
STAILQ_INIT(&tmplist); | |||||
} | |||||
mtx_unlock_spin(&mca_lock); | mtx_unlock_spin(&mca_lock); | ||||
STAILQ_FOREACH_SAFE(rec, &tmplist, link, next) | |||||
free(rec, M_MCA); | |||||
} | } | ||||
static void | static void | ||||
mca_refill(void *context, int pending) | mca_resize(void *context, int pending) | ||||
{ | { | ||||
mca_fill_freelist(); | mca_resize_freelist(); | ||||
} | } | ||||
static void | static void | ||||
mca_record_entry(enum scan_mode mode, const struct mca_record *record) | mca_record_entry(enum scan_mode mode, const struct mca_record *record) | ||||
{ | { | ||||
struct mca_internal *rec; | struct mca_internal *rec; | ||||
if (mode == POLLED) { | if (mode == POLLED) { | ||||
rec = malloc(sizeof(*rec), M_MCA, M_WAITOK); | rec = malloc(sizeof(*rec), M_MCA, M_WAITOK); | ||||
mtx_lock_spin(&mca_lock); | mtx_lock_spin(&mca_lock); | ||||
} else { | } else { | ||||
mtx_lock_spin(&mca_lock); | mtx_lock_spin(&mca_lock); | ||||
rec = STAILQ_FIRST(&mca_freelist); | rec = STAILQ_FIRST(&mca_freelist); | ||||
if (rec == NULL) { | if (rec == NULL) { | ||||
printf("MCA: Unable to allocate space for an event.\n"); | printf("MCA: Unable to allocate space for an event.\n"); | ||||
mca_log(record); | mca_log(record); | ||||
mtx_unlock_spin(&mca_lock); | mtx_unlock_spin(&mca_lock); | ||||
return; | return; | ||||
} | } | ||||
STAILQ_REMOVE_HEAD(&mca_freelist, link); | STAILQ_REMOVE_HEAD(&mca_freelist, link); | ||||
mca_freecount--; | mca_freecount--; | ||||
} | } | ||||
rec->rec = *record; | rec->rec = *record; | ||||
rec->logged = 0; | STAILQ_INSERT_TAIL(&mca_pending, rec, link); | ||||
STAILQ_INSERT_TAIL(&mca_records, rec, link); | |||||
mca_count++; | |||||
mtx_unlock_spin(&mca_lock); | mtx_unlock_spin(&mca_lock); | ||||
if (mode == CMCI && !cold) | if (mode == CMCI && !cold) | ||||
jhbUnsubmitted Done Inline ActionsI feel like we can maybe drop this now since you are doing this in the interrupt handler that calls this as well. In particular, this might queue the task even though the interrupt handler is going to free the record after logging it. jhb: I feel like we can maybe drop this now since you are doing this in the interrupt handler that… | |||||
jtlAuthorUnsubmitted Done Inline ActionsThe interrupt handler will only queue the task if it actually does free a record. By default (hw.mca.maxcount == -1), it won't free any records, so it won't queue the task. If a user changes hw.mca.maxcount to a non-negative number, we will probably queue the task more than necessary. AFAICT, the only reliable way around that is to only queue the task in the interrupt handler after logging. However, that may delay refilling the free list longer than may be desirable. Or, is that not an issue and we should just unconditionally queue the task at the end of the interrupt handler? jtl: The interrupt handler will only queue the task if it actually does free a record. By default… | |||||
jhbUnsubmitted Done Inline ActionsIt probably is fine to just wait for the end of the interrupt handler and do it if the scan found anything. (So just 'if (!cold)' instead of needing the 'doresize' check.) It will be simpler all around I think. jhb: It probably is fine to just wait for the end of the interrupt handler and do it if the scan… | |||||
taskqueue_enqueue(mca_tq, &mca_refill_task); | taskqueue_enqueue(mca_tq, &mca_resize_task); | ||||
} | } | ||||
#ifdef DEV_APIC | #ifdef DEV_APIC | ||||
/* | /* | ||||
* Update the interrupt threshold for a CMCI. The strategy is to use | * Update the interrupt threshold for a CMCI. The strategy is to use | ||||
* a low trigger that interrupts as soon as the first event occurs. | * a low trigger that interrupts as soon as the first event occurs. | ||||
* However, if a steady stream of events arrive, the threshold is | * However, if a steady stream of events arrive, the threshold is | ||||
* increased until the interrupts are throttled to once every | * increased until the interrupts are throttled to once every | ||||
▲ Show 20 Lines • Show All 160 Lines • ▼ Show 20 Lines | if (PCPU_GET(cmci_mask) & 1 << i) { | ||||
if (cmc_state != NULL) | if (cmc_state != NULL) | ||||
cmci_update(mode, i, valid, &rec); | cmci_update(mode, i, valid, &rec); | ||||
else | else | ||||
amd_thresholding_update(mode, i, valid); | amd_thresholding_update(mode, i, valid); | ||||
} | } | ||||
#endif | #endif | ||||
} | } | ||||
if (mode == POLLED) | if (mode == POLLED) | ||||
mca_fill_freelist(); | mca_resize_freelist(); | ||||
if (recoverablep != NULL) | if (recoverablep != NULL) | ||||
*recoverablep = recoverable; | *recoverablep = recoverable; | ||||
return (count); | return (count); | ||||
} | } | ||||
/* | /* | ||||
* Store a new record on the mca_records list while enforcing | |||||
* mca_maxcount. | |||||
*/ | |||||
static void | |||||
mca_store_record(struct mca_internal *mca) | |||||
{ | |||||
/* | |||||
* If we are storing no records (mca_maxcount == 0), | |||||
* we just free this record. | |||||
* | |||||
* If we are storing records (mca_maxcount != 0) and | |||||
* we have free space on the list, store the record | |||||
* and increment mca_count. | |||||
* | |||||
* If we are storing records and we do not have free | |||||
* space on the list, store the new record at the | |||||
* tail and free the oldest one from the head. | |||||
*/ | |||||
if (mca_maxcount != 0) | |||||
STAILQ_INSERT_TAIL(&mca_records, mca, link); | |||||
if (mca_maxcount < 0 || mca_count < mca_maxcount) | |||||
mca_count++; | |||||
else { | |||||
if (mca_maxcount != 0) { | |||||
mca = STAILQ_FIRST(&mca_records); | |||||
STAILQ_REMOVE_HEAD(&mca_records, link); | |||||
} | |||||
STAILQ_INSERT_TAIL(&mca_freelist, mca, link); | |||||
mca_freecount++; | |||||
} | |||||
} | |||||
/* | |||||
* Scan the machine check banks on all CPUs by binding to each CPU in | * Scan the machine check banks on all CPUs by binding to each CPU in | ||||
* turn. If any of the CPUs contained new machine check records, log | * turn. If any of the CPUs contained new machine check records, log | ||||
* them to the console. | * them to the console. | ||||
*/ | */ | ||||
static void | static void | ||||
mca_scan_cpus(void *context, int pending) | mca_scan_cpus(void *context, int pending) | ||||
{ | { | ||||
struct mca_internal *mca; | struct mca_internal *mca; | ||||
struct thread *td; | struct thread *td; | ||||
int count, cpu; | int count, cpu; | ||||
mca_fill_freelist(); | mca_resize_freelist(); | ||||
td = curthread; | td = curthread; | ||||
count = 0; | count = 0; | ||||
thread_lock(td); | thread_lock(td); | ||||
CPU_FOREACH(cpu) { | CPU_FOREACH(cpu) { | ||||
sched_bind(td, cpu); | sched_bind(td, cpu); | ||||
thread_unlock(td); | thread_unlock(td); | ||||
count += mca_scan(POLLED, NULL); | count += mca_scan(POLLED, NULL); | ||||
thread_lock(td); | thread_lock(td); | ||||
sched_unbind(td); | sched_unbind(td); | ||||
} | } | ||||
thread_unlock(td); | thread_unlock(td); | ||||
if (count != 0) { | if (count != 0) { | ||||
mtx_lock_spin(&mca_lock); | mtx_lock_spin(&mca_lock); | ||||
STAILQ_FOREACH(mca, &mca_records, link) { | while ((mca = STAILQ_FIRST(&mca_pending)) != NULL) { | ||||
if (!mca->logged) { | STAILQ_REMOVE_HEAD(&mca_pending, link); | ||||
mca->logged = 1; | |||||
mca_log(&mca->rec); | mca_log(&mca->rec); | ||||
mca_store_record(mca); | |||||
} | } | ||||
} | |||||
mtx_unlock_spin(&mca_lock); | mtx_unlock_spin(&mca_lock); | ||||
mca_resize_freelist(); | |||||
} | } | ||||
} | } | ||||
static void | static void | ||||
mca_periodic_scan(void *arg) | mca_periodic_scan(void *arg) | ||||
{ | { | ||||
taskqueue_enqueue(mca_tq, &mca_scan_task); | taskqueue_enqueue(mca_tq, &mca_scan_task); | ||||
Show All 9 Lines | sysctl_mca_scan(SYSCTL_HANDLER_ARGS) | ||||
error = sysctl_handle_int(oidp, &i, 0, req); | error = sysctl_handle_int(oidp, &i, 0, req); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
if (i) | if (i) | ||||
taskqueue_enqueue(mca_tq, &mca_scan_task); | taskqueue_enqueue(mca_tq, &mca_scan_task); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
sysctl_mca_maxcount(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct mca_internal *mca; | |||||
int error, i; | |||||
bool doresize; | |||||
i = mca_maxcount; | |||||
error = sysctl_handle_int(oidp, &i, 0, req); | |||||
if (error || req->newptr == NULL) | |||||
return (error); | |||||
mtx_lock_spin(&mca_lock); | |||||
mca_maxcount = i; | |||||
doresize = false; | |||||
if (mca_maxcount >= 0) | |||||
while (mca_count > mca_maxcount) { | |||||
mca = STAILQ_FIRST(&mca_records); | |||||
STAILQ_REMOVE_HEAD(&mca_records, link); | |||||
mca_count--; | |||||
STAILQ_INSERT_TAIL(&mca_freelist, mca, link); | |||||
mca_freecount++; | |||||
doresize = true; | |||||
} | |||||
mtx_unlock_spin(&mca_lock); | |||||
if (doresize && !cold) | |||||
taskqueue_enqueue(mca_tq, &mca_resize_task); | |||||
return (error); | |||||
} | |||||
static void | static void | ||||
mca_createtq(void *dummy) | mca_createtq(void *dummy) | ||||
{ | { | ||||
if (mca_banks <= 0) | if (mca_banks <= 0) | ||||
return; | return; | ||||
mca_tq = taskqueue_create_fast("mca", M_WAITOK, | mca_tq = taskqueue_create_fast("mca", M_WAITOK, | ||||
taskqueue_thread_enqueue, &mca_tq); | taskqueue_thread_enqueue, &mca_tq); | ||||
taskqueue_start_threads(&mca_tq, 1, PI_SWI(SWI_TQ), "mca taskq"); | taskqueue_start_threads(&mca_tq, 1, PI_SWI(SWI_TQ), "mca taskq"); | ||||
/* CMCIs during boot may have claimed items from the freelist. */ | /* CMCIs during boot may have claimed items from the freelist. */ | ||||
mca_fill_freelist(); | mca_resize_freelist(); | ||||
} | } | ||||
SYSINIT(mca_createtq, SI_SUB_CONFIGURE, SI_ORDER_ANY, mca_createtq, NULL); | SYSINIT(mca_createtq, SI_SUB_CONFIGURE, SI_ORDER_ANY, mca_createtq, NULL); | ||||
static void | static void | ||||
mca_startup(void *dummy) | mca_startup(void *dummy) | ||||
{ | { | ||||
if (mca_banks <= 0) | if (mca_banks <= 0) | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | mca_setup(uint64_t mcg_cap) | ||||
*/ | */ | ||||
if (cpu_vendor_id == CPU_VENDOR_AMD && | if (cpu_vendor_id == CPU_VENDOR_AMD && | ||||
CPUID_TO_FAMILY(cpu_id) == 0x10 && amd10h_L1TP) | CPUID_TO_FAMILY(cpu_id) == 0x10 && amd10h_L1TP) | ||||
workaround_erratum383 = 1; | workaround_erratum383 = 1; | ||||
mca_banks = mcg_cap & MCG_CAP_COUNT; | mca_banks = mcg_cap & MCG_CAP_COUNT; | ||||
mtx_init(&mca_lock, "mca", NULL, MTX_SPIN); | mtx_init(&mca_lock, "mca", NULL, MTX_SPIN); | ||||
STAILQ_INIT(&mca_records); | STAILQ_INIT(&mca_records); | ||||
STAILQ_INIT(&mca_pending); | |||||
TASK_INIT(&mca_scan_task, 0, mca_scan_cpus, NULL); | TASK_INIT(&mca_scan_task, 0, mca_scan_cpus, NULL); | ||||
callout_init(&mca_timer, 1); | callout_init(&mca_timer, 1); | ||||
STAILQ_INIT(&mca_freelist); | STAILQ_INIT(&mca_freelist); | ||||
TASK_INIT(&mca_refill_task, 0, mca_refill, NULL); | TASK_INIT(&mca_resize_task, 0, mca_resize, NULL); | ||||
mca_fill_freelist(); | mca_resize_freelist(); | ||||
SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca), OID_AUTO, | SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca), OID_AUTO, | ||||
"count", CTLFLAG_RD, (int *)(uintptr_t)&mca_count, 0, | "count", CTLFLAG_RD, (int *)(uintptr_t)&mca_count, 0, | ||||
"Record count"); | "Record count"); | ||||
SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca), OID_AUTO, | SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca), OID_AUTO, | ||||
"maxcount", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, | |||||
&mca_maxcount, 0, sysctl_mca_maxcount, "I", | |||||
"Maximum record count (-1 is unlimited)"); | |||||
SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca), OID_AUTO, | |||||
"interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, &mca_ticks, | "interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, &mca_ticks, | ||||
0, sysctl_positive_int, "I", | 0, sysctl_positive_int, "I", | ||||
"Periodic interval in seconds to scan for machine checks"); | "Periodic interval in seconds to scan for machine checks"); | ||||
SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca), OID_AUTO, | SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca), OID_AUTO, | ||||
"records", CTLFLAG_RD, sysctl_mca_records, "Machine check records"); | "records", CTLFLAG_RD, sysctl_mca_records, "Machine check records"); | ||||
SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca), OID_AUTO, | SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca), OID_AUTO, | ||||
"force_scan", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0, | "force_scan", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0, | ||||
sysctl_mca_scan, "I", "Force an immediate scan for machine checks"); | sysctl_mca_scan, "I", "Force an immediate scan for machine checks"); | ||||
▲ Show 20 Lines • Show All 365 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
#ifdef DEV_APIC | #ifdef DEV_APIC | ||||
/* Called for a CMCI (correctable machine check interrupt). */ | /* Called for a CMCI (correctable machine check interrupt). */ | ||||
void | void | ||||
cmc_intr(void) | cmc_intr(void) | ||||
{ | { | ||||
struct mca_internal *mca; | struct mca_internal *mca; | ||||
int count; | int count, savedfreecnt; | ||||
bool doresize; | |||||
/* | /* | ||||
* Serialize MCA bank scanning to prevent collisions from | * Serialize MCA bank scanning to prevent collisions from | ||||
* sibling threads. | * sibling threads. | ||||
*/ | */ | ||||
count = mca_scan(CMCI, NULL); | count = mca_scan(CMCI, NULL); | ||||
/* If we found anything, log them to the console. */ | /* If we found anything, log them to the console. */ | ||||
if (count != 0) { | if (count != 0) { | ||||
mtx_lock_spin(&mca_lock); | mtx_lock_spin(&mca_lock); | ||||
STAILQ_FOREACH(mca, &mca_records, link) { | savedfreecnt = mca_freecount; | ||||
if (!mca->logged) { | while ((mca = STAILQ_FIRST(&mca_pending)) != NULL) { | ||||
mca->logged = 1; | STAILQ_REMOVE_HEAD(&mca_pending, link); | ||||
mca_log(&mca->rec); | mca_log(&mca->rec); | ||||
mca_store_record(mca); | |||||
} | } | ||||
} | doresize = mca_freecount != savedfreecnt; | ||||
mtx_unlock_spin(&mca_lock); | mtx_unlock_spin(&mca_lock); | ||||
if (doresize && !cold) | |||||
taskqueue_enqueue(mca_tq, &mca_resize_task); | |||||
} | } | ||||
} | } | ||||
#endif | #endif |
Extra blank line?