Changeset View
Standalone View
sys/x86/x86/intr_machdep.c
Show First 20 Lines • Show All 175 Lines • ▼ Show 20 Lines | #endif | ||||
/* | /* | ||||
* - 1 ??? dummy counter. | * - 1 ??? dummy counter. | ||||
* - 2 counters for each I/O interrupt. | * - 2 counters for each I/O interrupt. | ||||
* - 1 counter for each CPU for lapic timer. | * - 1 counter for each CPU for lapic timer. | ||||
* - 1 counter for each CPU for the Hyper-V vmbus driver. | * - 1 counter for each CPU for the Hyper-V vmbus driver. | ||||
* - 8 counters for each CPU for IPI counters for SMP. | * - 8 counters for each CPU for IPI counters for SMP. | ||||
*/ | */ | ||||
nintrcnt = 1 + num_io_irqs * 2 + mp_ncpus * 2; | nintrcnt = 1 + num_io_irqs * 2 + mp_ncpus * 2; | ||||
#ifdef COUNT_IPIS | #ifdef COUNT_IPIS | ||||
if (mp_ncpus > 1) | if (mp_ncpus > 1) | ||||
nintrcnt += 8 * mp_ncpus; | nintrcnt += 8 * mp_ncpus; | ||||
#endif | #endif | ||||
ehem_freebsd_m5p.com: As with PowerPC, I'm suspicious about this calculation. The result seems far too small, which… | |||||
Done Inline ActionsIPI counters are registered in mp_ipi_intrcnt(), and it seems they need to be enabled with the COUNT_IPIS kernel config option (off by default). To me the count adds up, but the MPASS assertion in intrcnt_add() should help you verify this. mhorne: IPI counters are registered in `mp_ipi_intrcnt()`, and it seems they need to be enabled with… | |||||
intrcnt = mallocarray(nintrcnt, sizeof(u_long), M_INTR, M_WAITOK | | intrcnt = mallocarray(nintrcnt, sizeof(u_long), M_INTR, M_WAITOK | | ||||
M_ZERO); | M_ZERO); | ||||
intrnames = mallocarray(nintrcnt, INTRNAME_LEN, M_INTR, M_WAITOK | | intrnames = mallocarray(nintrcnt, INTRNAME_LEN, M_INTR, M_WAITOK | | ||||
M_ZERO); | M_ZERO); | ||||
Done Inline Actionsintrnames = mallocarray(); this is clearly interpreting "intrnames" as an array of fixed-length strings. This matches with intrcnt_setname() using intrnames + (MAXCOMLEN + 1) * index below. ehem_freebsd_m5p.com: `intrnames = mallocarray();` this is clearly interpreting "intrnames" as an array of fixed… | |||||
intrcnt_setname("???", 0); | intrcnt_setname("???", 0); | ||||
intrcnt_index = 1; | intrcnt_index = 1; | ||||
Done Inline ActionsAgain, the mysterious questionable interrupt. No idea, so I'm opting to believe this had value at some time in the far distant past, but is unneeded now. ehem_freebsd_m5p.com: Again, the mysterious questionable interrupt. No idea, so I'm opting to believe this had value… | |||||
Done Inline ActionsI would speculate the same. mhorne: I would speculate the same. | |||||
Done Inline ActionsThis was broken off into D37869, since this isn't strictly required for this task. ehem_freebsd_m5p.com: This was broken off into D37869, since this isn't strictly required for this task. | |||||
/* | /* | ||||
* NB: intrpic_lock is not held here to avoid LORs due to | * NB: intrpic_lock is not held here to avoid LORs due to | ||||
* malloc() in intr_register_source(). However, we are still | * malloc() in intr_register_source(). However, we are still | ||||
* single-threaded at this point in startup so the list of | * single-threaded at this point in startup so the list of | ||||
* PICs shouldn't change. | * PICs shouldn't change. | ||||
*/ | */ | ||||
TAILQ_FOREACH(pic, &pics, pics) { | TAILQ_FOREACH(pic, &pics, pics) { | ||||
if (pic->pic_register_sources != NULL) | if (pic->pic_register_sources != NULL) | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame) | ||||
vector = isrc->is_pic->pic_vector(isrc); | vector = isrc->is_pic->pic_vector(isrc); | ||||
if (vector == 0) | if (vector == 0) | ||||
clkintr_pending = 1; | clkintr_pending = 1; | ||||
/* | /* | ||||
* For stray interrupts, mask and EOI the source, bump the | * For stray interrupts, mask and EOI the source, bump the | ||||
* stray count, and log the condition. | * stray count, and log the condition. | ||||
*/ | */ | ||||
if ((strays = intr_event_handle(ie, frame)) != 0) { | if ((strays = intr_event_handle(ie, frame)) != 0) { | ||||
isrc->is_pic->pic_disable_source(isrc, PIC_EOI); | isrc->is_pic->pic_disable_source(isrc, PIC_EOI); | ||||
(*isrc->is_straycount)++; | (*isrc->is_straycount)++; | ||||
if (strays < INTR_STRAY_LOG_MAX) | if (strays < INTR_STRAY_LOG_MAX) | ||||
log(LOG_ERR, "stray irq%d\n", vector); | log(LOG_ERR, "stray irq%d\n", vector); | ||||
else if (strays == INTR_STRAY_LOG_MAX) | else if (strays == INTR_STRAY_LOG_MAX) | ||||
Done Inline ActionsTaking advantage of the new intr_event_handle() returning the stray count. Previously callers had only cared about 0 versus !0, for whether to interpret the value as a stray. Now this provides a handy way for callers to provide limited messages like this. ehem_freebsd_m5p.com: Taking advantage of the new `intr_event_handle()` returning the stray count. Previously… | |||||
log(LOG_CRIT, | log(LOG_CRIT, | ||||
"too many stray irq %d's: not logging anymore\n", | "too many stray irq %d's: not logging anymore\n", | ||||
vector); | vector); | ||||
} | } | ||||
} | } | ||||
void | void | ||||
intr_resume(bool suspend_cancelled) | intr_resume(bool suspend_cancelled) | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
intrcnt_register(struct intsrc *is) | intrcnt_register(struct intsrc *is) | ||||
{ | { | ||||
char straystr[INTRNAME_LEN]; | char straystr[INTRNAME_LEN]; | ||||
KASSERT(is->is_event != NULL, ("%s: isrc with no event", __func__)); | KASSERT(is->is_event != NULL, ("%s: isrc with no event", __func__)); | ||||
mtx_lock_spin(&intrcnt_lock); | mtx_lock_spin(&intrcnt_lock); | ||||
MPASS(intrcnt_index + 2 <= nintrcnt); | MPASS(intrcnt_index + 2 <= nintrcnt); | ||||
is->is_index = intrcnt_index; | is->is_index = nintrcnt - 2; | ||||
intrcnt_index += 2; | |||||
snprintf(straystr, sizeof(straystr), "stray irq%d", | snprintf(straystr, sizeof(straystr), "stray irq%d", | ||||
is->is_pic->pic_vector(is)); | is->is_pic->pic_vector(is)); | ||||
intrcnt_updatename(is); | intrcnt_updatename(is); | ||||
is->is_count = &intrcnt[is->is_index]; | is->is_count = &intrcnt[is->is_index]; | ||||
intrcnt_setname(straystr, is->is_index + 1); | intrcnt_setname(straystr, is->is_index + 1); | ||||
is->is_straycount = &intrcnt[is->is_index + 1]; | is->is_straycount = &intrcnt[is->is_index + 1]; | ||||
mtx_unlock_spin(&intrcnt_lock); | mtx_unlock_spin(&intrcnt_lock); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 273 Lines • ▼ Show 20 Lines | for (i = 0; i < num_io_irqs; i++) { | ||||
isrc = interrupt_sources[i]; | isrc = interrupt_sources[i]; | ||||
if (isrc == NULL) | if (isrc == NULL) | ||||
continue; | continue; | ||||
sbuf_printf(&sbuf, "%s:%d @cpu%d(domain%d): %ld\n", | sbuf_printf(&sbuf, "%s:%d @cpu%d(domain%d): %ld\n", | ||||
isrc->is_event->ie_fullname, | isrc->is_event->ie_fullname, | ||||
isrc->is_index, | isrc->is_index, | ||||
isrc->is_cpu, | isrc->is_cpu, | ||||
isrc->is_domain, | isrc->is_domain, | ||||
*isrc->is_count); | isrc->is_event->ie_intrcnt[0]); | ||||
} | } | ||||
sx_sunlock(&intrsrc_lock); | sx_sunlock(&intrsrc_lock); | ||||
error = sbuf_finish(&sbuf); | error = sbuf_finish(&sbuf); | ||||
sbuf_delete(&sbuf); | sbuf_delete(&sbuf); | ||||
return (error); | return (error); | ||||
} | } | ||||
SYSCTL_PROC(_hw, OID_AUTO, intrs, | SYSCTL_PROC(_hw, OID_AUTO, intrs, | ||||
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, | ||||
0, 0, sysctl_hw_intrs, "A", | 0, 0, sysctl_hw_intrs, "A", | ||||
"interrupt:number @cpu: count"); | "interrupt:number @cpu: count"); | ||||
/* | /* | ||||
* Compare two, possibly NULL, entries in the interrupt source array | * Compare two, possibly NULL, entries in the interrupt source array | ||||
* by load. | * by load. | ||||
*/ | */ | ||||
static int | static int | ||||
intrcmp(const void *one, const void *two) | intrcmp(const void *one, const void *two) | ||||
{ | { | ||||
const struct intsrc *i1, *i2; | const struct intsrc *i1, *i2; | ||||
i1 = *(const struct intsrc * const *)one; | i1 = *(const struct intsrc * const *)one; | ||||
i2 = *(const struct intsrc * const *)two; | i2 = *(const struct intsrc * const *)two; | ||||
if (i1 != NULL && i2 != NULL) | if (i1 != NULL && i2 != NULL) | ||||
return (*i1->is_count - *i2->is_count); | return (i1->is_event->ie_intrcnt[0] - i2->is_event->ie_intrcnt[0]); | ||||
if (i1 != NULL) | if (i1 != NULL) | ||||
return (1); | return (1); | ||||
if (i2 != NULL) | if (i2 != NULL) | ||||
return (-1); | return (-1); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
/* | /* | ||||
* Sysctls used by systat and others: hw.intrnames and hw.intrcnt. | * Sysctls used by systat and others: hw.intrnames and hw.intrcnt. | ||||
*/ | */ | ||||
static int | static int | ||||
sysctl_intrnames(SYSCTL_HANDLER_ARGS) | sysctl_intrnames(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
return (sysctl_handle_opaque(oidp, intrnames, | int error; | ||||
intrcnt_index * INTRNAME_LEN, req)); | |||||
error = sysctl_handle_opaque(oidp, intrnames, | |||||
intrcnt_index * INTRNAME_LEN, req); | |||||
if (error != 0) | |||||
return (error); | |||||
return (intr_event_sysctl_intrnames(oidp, arg1, arg2, req)); | |||||
} | } | ||||
SYSCTL_PROC(_hw, OID_AUTO, intrnames, | SYSCTL_PROC(_hw, OID_AUTO, intrnames, | ||||
CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, | CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, | ||||
sysctl_intrnames, | sysctl_intrnames, | ||||
"", "Interrupt Names"); | "", "Interrupt Names"); | ||||
static int | static int | ||||
sysctl_intrcnt(SYSCTL_HANDLER_ARGS) | sysctl_intrcnt(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error; | |||||
#ifdef SCTL_MASK32 | #ifdef SCTL_MASK32 | ||||
uint32_t *intrcnt32; | uint32_t *intrcnt32; | ||||
unsigned i; | unsigned i; | ||||
int error; | |||||
if (req->flags & SCTL_MASK32) { | if (req->flags & SCTL_MASK32) { | ||||
if (!req->oldptr) | if (!req->oldptr) | ||||
return (sysctl_handle_opaque(oidp, NULL, | return (sysctl_handle_opaque(oidp, NULL, | ||||
intrcnt_index * sizeof(uint32_t), req)); | intrcnt_index * sizeof(uint32_t), req)); | ||||
intrcnt32 = malloc(intrcnt_index * sizeof(uint32_t), M_TEMP, | intrcnt32 = malloc(intrcnt_index * sizeof(uint32_t), M_TEMP, | ||||
M_NOWAIT); | M_NOWAIT); | ||||
if (intrcnt32 == NULL) | if (intrcnt32 == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
for (i = 0; i < intrcnt_index; i++) | for (i = 0; i < intrcnt_index; i++) | ||||
intrcnt32[i] = intrcnt[i]; | intrcnt32[i] = intrcnt[i]; | ||||
error = sysctl_handle_opaque(oidp, intrcnt32, | error = sysctl_handle_opaque(oidp, intrcnt32, | ||||
intrcnt_index * sizeof(uint32_t), req); | intrcnt_index * sizeof(uint32_t), req); | ||||
free(intrcnt32, M_TEMP); | free(intrcnt32, M_TEMP); | ||||
return (error); | } else | ||||
} | |||||
#endif | #endif | ||||
return (sysctl_handle_opaque(oidp, intrcnt, | error = sysctl_handle_opaque(oidp, intrcnt, | ||||
intrcnt_index * sizeof(u_long), req)); | intrcnt_index * sizeof(u_long), req); | ||||
return (error == 0 ? intr_event_sysctl_intrcnt(oidp, arg1, arg2, req) : | |||||
error); | |||||
} | } | ||||
SYSCTL_PROC(_hw, OID_AUTO, intrcnt, | SYSCTL_PROC(_hw, OID_AUTO, intrcnt, | ||||
CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, | CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, | ||||
sysctl_intrcnt, | sysctl_intrcnt, | ||||
"", "Interrupt Counts"); | "", "Interrupt Counts"); | ||||
#ifdef DDB | #ifdef DDB | ||||
/* | /* | ||||
* DDB command to dump the interrupt statistics. | * DDB command to dump the IPI interrupt statistics. | ||||
*/ | */ | ||||
DB_SHOW_COMMAND_FLAGS(intrcnt, db_show_intrcnt, DB_CMD_MEMSAFE) | DB_SHOW_COMMAND_FLAGS(ipicnt, db_show_ipicnt, DB_CMD_MEMSAFE) | ||||
{ | { | ||||
u_int i; | u_int i; | ||||
for (i = 0; i < intrcnt_index && !db_pager_quit; ++i) | for (i = 0; i < intrcnt_index && !db_pager_quit; ++i) | ||||
if (intrcnt[i] != 0) | if (intrcnt[i] != 0) | ||||
db_printf("%s\t%lu\n", intrnames + i * INTRNAME_LEN, | db_printf("%s\t%lu\n", intrnames + i * INTRNAME_LEN, | ||||
intrcnt[i]); | intrcnt[i]); | ||||
} | } | ||||
#endif | #endif |
As with PowerPC, I'm suspicious about this calculation. The result seems far too small, which leaves me suspicious not enough interrupt counters have been allocated for IPI interrupts, but in the past this was mitigated by lots of spare I/O interrupt counters. Now this will likely break.
Both Xen and Hyper-V allocate a set of IPI interrupt counters, but you're rather unlikely to have both on the same hardware at the same time.