Changeset View
Standalone View
sys/x86/x86/intr_machdep.c
Show First 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | |||||
#include <x86/isa/icu.h> | #include <x86/isa/icu.h> | ||||
#include <isa/isareg.h> | #include <isa/isareg.h> | ||||
#endif | #endif | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
typedef void (*mask_fn)(void *); | typedef void (*mask_fn)(void *); | ||||
static int intrcnt_index; | |||||
static struct intsrc **interrupt_sources; | static struct intsrc **interrupt_sources; | ||||
#ifdef SMP | #ifdef SMP | ||||
static struct intsrc **interrupt_sorted; | static struct intsrc **interrupt_sorted; | ||||
static int intrbalance; | static int intrbalance; | ||||
SYSCTL_INT(_hw, OID_AUTO, intrbalance, CTLFLAG_RWTUN, &intrbalance, 0, | SYSCTL_INT(_hw, OID_AUTO, intrbalance, CTLFLAG_RWTUN, &intrbalance, 0, | ||||
"Interrupt auto-balance interval (seconds). Zero disables."); | "Interrupt auto-balance interval (seconds). Zero disables."); | ||||
static struct timeout_task intrbalance_task; | static struct timeout_task intrbalance_task; | ||||
#endif | #endif | ||||
static struct sx intrsrc_lock; | static struct sx intrsrc_lock; | ||||
static struct mtx intrpic_lock; | static struct mtx intrpic_lock; | ||||
static struct mtx intrcnt_lock; | |||||
static TAILQ_HEAD(pics_head, pic) pics; | static TAILQ_HEAD(pics_head, pic) pics; | ||||
u_int num_io_irqs; | u_int num_io_irqs; | ||||
#if defined(SMP) && !defined(EARLY_AP_STARTUP) | #if defined(SMP) && !defined(EARLY_AP_STARTUP) | ||||
static int assign_cpu; | static int assign_cpu; | ||||
#endif | #endif | ||||
u_long *intrcnt; | |||||
char *intrnames; | |||||
size_t sintrcnt = sizeof(intrcnt); | |||||
size_t sintrnames = sizeof(intrnames); | |||||
int nintrcnt; | int nintrcnt; | ||||
static MALLOC_DEFINE(M_INTR, "intr", "Interrupt Sources"); | static MALLOC_DEFINE(M_INTR, "intr", "Interrupt Sources"); | ||||
static int intr_assign_cpu(void *arg, int cpu); | static int intr_assign_cpu(void *arg, int cpu); | ||||
static void intr_disable_src(void *arg); | static void intr_disable_src(void *arg); | ||||
static void intr_init(void *__dummy); | static void intr_init(void *__dummy); | ||||
static int intr_pic_registered(struct pic *pic); | static int intr_pic_registered(struct pic *pic); | ||||
static void intrcnt_setname(const char *name, int index); | |||||
static void intrcnt_updatename(struct intsrc *is); | |||||
static void intrcnt_register(u_int vector, struct intsrc *is); | |||||
/* | /* | ||||
* SYSINIT levels for SI_SUB_INTR: | * SYSINIT levels for SI_SUB_INTR: | ||||
* | * | ||||
* SI_ORDER_FIRST: Initialize locks and pics TAILQ, xen_hvm_cpu_init | * SI_ORDER_FIRST: Initialize locks and pics TAILQ, xen_hvm_cpu_init | ||||
* SI_ORDER_SECOND: Xen PICs | * SI_ORDER_SECOND: Xen PICs | ||||
* SI_ORDER_THIRD: Add I/O APIC PICs, alloc MSI and Xen IRQ ranges | * SI_ORDER_THIRD: Add I/O APIC PICs, alloc MSI and Xen IRQ ranges | ||||
* SI_ORDER_FOURTH: Add 8259A PICs | * SI_ORDER_FOURTH: Add 8259A PICs | ||||
▲ Show 20 Lines • Show All 56 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 | | |||||
M_ZERO); | |||||
intrnames = mallocarray(nintrcnt, MAXCOMLEN + 1, M_INTR, M_WAITOK | | |||||
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… | |||||
sintrcnt = nintrcnt * sizeof(u_long); | |||||
sintrnames = nintrcnt * (MAXCOMLEN + 1); | |||||
intrcnt_setname("???", 0); | |||||
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 All 28 Lines | intr_register_source(u_int vector, struct intsrc *isrc) | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
sx_xlock(&intrsrc_lock); | sx_xlock(&intrsrc_lock); | ||||
if (interrupt_sources[vector] != NULL) { | if (interrupt_sources[vector] != NULL) { | ||||
sx_xunlock(&intrsrc_lock); | sx_xunlock(&intrsrc_lock); | ||||
intr_event_destroy(isrc->is_event); | intr_event_destroy(isrc->is_event); | ||||
return (EEXIST); | return (EEXIST); | ||||
} | } | ||||
intrcnt_register(vector, isrc); | |||||
interrupt_sources[vector] = isrc; | interrupt_sources[vector] = isrc; | ||||
isrc->is_handlers = 0; | isrc->is_handlers = 0; | ||||
sx_xunlock(&intrsrc_lock); | sx_xunlock(&intrsrc_lock); | ||||
return (0); | return (0); | ||||
} | } | ||||
interrupt_t * | interrupt_t * | ||||
intr_lookup(u_int vector) | intr_lookup(u_int vector) | ||||
Show All 10 Lines | intr_add_handler(const char *name, struct intsrc *isrc, driver_filter_t filter, | ||||
int domain) | int domain) | ||||
{ | { | ||||
int error; | int error; | ||||
error = intr_event_add_handler(isrc->is_event, name, filter, handler, | error = intr_event_add_handler(isrc->is_event, name, filter, handler, | ||||
arg, intr_priority(flags), flags, cookiep); | arg, intr_priority(flags), flags, cookiep); | ||||
if (error == 0) { | if (error == 0) { | ||||
sx_xlock(&intrsrc_lock); | sx_xlock(&intrsrc_lock); | ||||
intrcnt_updatename(isrc); | |||||
isrc->is_handlers++; | isrc->is_handlers++; | ||||
if (isrc->is_handlers == 1) { | if (isrc->is_handlers == 1) { | ||||
isrc->is_domain = domain; | isrc->is_domain = domain; | ||||
isrc->is_pic->pic_enable_intr(isrc); | isrc->is_pic->pic_enable_intr(isrc); | ||||
isrc->is_pic->pic_enable_source(isrc); | isrc->is_pic->pic_enable_source(isrc); | ||||
} | } | ||||
sx_xunlock(&intrsrc_lock); | sx_xunlock(&intrsrc_lock); | ||||
} | } | ||||
Show All 10 Lines | intr_remove_handler(void *cookie) | ||||
error = intr_event_remove_handler(cookie); | error = intr_event_remove_handler(cookie); | ||||
if (error == 0) { | if (error == 0) { | ||||
sx_xlock(&intrsrc_lock); | sx_xlock(&intrsrc_lock); | ||||
isrc->is_handlers--; | isrc->is_handlers--; | ||||
if (isrc->is_handlers == 0) { | if (isrc->is_handlers == 0) { | ||||
isrc->is_pic->pic_disable_source(isrc, PIC_NO_EOI); | isrc->is_pic->pic_disable_source(isrc, PIC_NO_EOI); | ||||
isrc->is_pic->pic_disable_intr(isrc); | isrc->is_pic->pic_disable_intr(isrc); | ||||
} | } | ||||
intrcnt_updatename(isrc); | |||||
sx_xunlock(&intrsrc_lock); | sx_xunlock(&intrsrc_lock); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
intr_config_intr(struct intsrc *isrc, enum intr_trigger trig, | intr_config_intr(struct intsrc *isrc, enum intr_trigger trig, | ||||
enum intr_polarity pol) | enum intr_polarity pol) | ||||
Show All 18 Lines | intr_execute_handlers(u_int vector, struct intsrc *isrc, | ||||
struct intr_event *ie; | struct intr_event *ie; | ||||
/* | /* | ||||
* We count software interrupts when we process them. The | * We count software interrupts when we process them. The | ||||
* code here follows previous practice, but there's an | * code here follows previous practice, but there's an | ||||
* argument for counting hardware interrupts when they're | * argument for counting hardware interrupts when they're | ||||
* processed too. | * processed too. | ||||
*/ | */ | ||||
(*isrc->is_count)++; | |||||
VM_CNT_INC(v_intr); | VM_CNT_INC(v_intr); | ||||
ie = isrc->is_event; | ie = isrc->is_event; | ||||
++ie->ie_intrcnt; | |||||
/* | /* | ||||
* XXX: We assume that IRQ 0 is only used for the ISA timer | * XXX: We assume that IRQ 0 is only used for the ISA timer | ||||
* device (clk). | * device (clk). | ||||
*/ | */ | ||||
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 (intr_event_handle(ie, frame) != 0) { | if (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)++; | ++ie->ie_stray; | ||||
if (*isrc->is_straycount < INTR_STRAY_LOG_MAX) | if (ie->ie_stray < INTR_STRAY_LOG_MAX) | ||||
log(LOG_ERR, "stray irq%d\n", vector); | log(LOG_ERR, "stray irq%d\n", vector); | ||||
else if (*isrc->is_straycount == INTR_STRAY_LOG_MAX) | else if (ie->ie_stray == 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); | ||||
return (*isrc->is_straycount); | return (ie->ie_stray); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
intr_resume(bool suspend_cancelled) | intr_resume(bool suspend_cancelled) | ||||
{ | { | ||||
struct pic *pic; | struct pic *pic; | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | #endif | ||||
error = 0; | error = 0; | ||||
return (error); | return (error); | ||||
#else | #else | ||||
return (EOPNOTSUPP); | return (EOPNOTSUPP); | ||||
#endif | #endif | ||||
} | } | ||||
static void | static void | ||||
intrcnt_setname(const char *name, int index) | |||||
{ | |||||
snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s", | |||||
MAXCOMLEN, name); | |||||
} | |||||
static void | |||||
intrcnt_updatename(struct intsrc *is) | |||||
{ | |||||
intrcnt_setname(is->is_event->ie_fullname, is->is_index); | |||||
} | |||||
static void | |||||
intrcnt_register(u_int vector, struct intsrc *is) | |||||
{ | |||||
char straystr[MAXCOMLEN + 1]; | |||||
KASSERT(is->is_event != NULL, ("%s: isrc with no event", __func__)); | |||||
mtx_lock_spin(&intrcnt_lock); | |||||
MPASS(intrcnt_index + 2 <= nintrcnt); | |||||
is->is_index = intrcnt_index; | |||||
intrcnt_index += 2; | |||||
snprintf(straystr, MAXCOMLEN + 1, "stray irq%u", vector); | |||||
intrcnt_updatename(is); | |||||
is->is_count = &intrcnt[is->is_index]; | |||||
intrcnt_setname(straystr, is->is_index + 1); | |||||
is->is_straycount = &intrcnt[is->is_index + 1]; | |||||
mtx_unlock_spin(&intrcnt_lock); | |||||
} | |||||
void | |||||
intrcnt_add(const char *name, u_long **countp) | |||||
{ | |||||
mtx_lock_spin(&intrcnt_lock); | |||||
MPASS(intrcnt_index < nintrcnt); | |||||
*countp = &intrcnt[intrcnt_index]; | |||||
intrcnt_setname(name, intrcnt_index); | |||||
intrcnt_index++; | |||||
mtx_unlock_spin(&intrcnt_lock); | |||||
} | |||||
static void | |||||
intr_init(void *dummy __unused) | intr_init(void *dummy __unused) | ||||
{ | { | ||||
TAILQ_INIT(&pics); | TAILQ_INIT(&pics); | ||||
mtx_init(&intrpic_lock, "intrpic", NULL, MTX_DEF); | mtx_init(&intrpic_lock, "intrpic", NULL, MTX_DEF); | ||||
sx_init(&intrsrc_lock, "intrsrc"); | sx_init(&intrsrc_lock, "intrsrc"); | ||||
mtx_init(&intrcnt_lock, "intrcnt", NULL, MTX_SPIN); | |||||
} | } | ||||
SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL); | SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL); | ||||
static void | static void | ||||
intr_init_final(void *dummy __unused) | intr_init_final(void *dummy __unused) | ||||
{ | { | ||||
/* | /* | ||||
Show All 28 Lines | atpic_reset(void) | ||||
outb(IO_ICU2, OCW3_SEL | OCW3_RR); | outb(IO_ICU2, OCW3_SEL | OCW3_RR); | ||||
} | } | ||||
#endif | #endif | ||||
/* Add a description to an active interrupt handler. */ | /* Add a description to an active interrupt handler. */ | ||||
int | int | ||||
intr_describe(struct intsrc *isrc, void *ih, const char *descr) | intr_describe(struct intsrc *isrc, void *ih, const char *descr) | ||||
{ | { | ||||
int error; | |||||
error = intr_event_describe_handler(isrc->is_event, ih, descr); | return (intr_event_describe_handler(isrc->is_event, ih, descr)); | ||||
if (error) | |||||
return (error); | |||||
intrcnt_updatename(isrc); | |||||
return (0); | |||||
} | } | ||||
void | void | ||||
intr_reprogram(void) | intr_reprogram(void) | ||||
{ | { | ||||
struct intsrc *is; | struct intsrc *is; | ||||
u_int v; | u_int v; | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | if (error != 0) | ||||
return (error); | return (error); | ||||
sbuf_new_for_sysctl(&sbuf, NULL, 128, req); | sbuf_new_for_sysctl(&sbuf, NULL, 128, req); | ||||
sx_slock(&intrsrc_lock); | sx_slock(&intrsrc_lock); | ||||
for (i = 0; i < num_io_irqs; i++) { | 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 @cpu%d(domain%d): %lu\n", | ||||
isrc->is_event->ie_fullname, | isrc->is_event->ie_fullname, | ||||
isrc->is_index, | |||||
isrc->is_cpu, | isrc->is_cpu, | ||||
isrc->is_domain, | isrc->is_domain, | ||||
*isrc->is_count); | isrc->is_event->ie_intrcnt); | ||||
} | } | ||||
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 - i2->is_event->ie_intrcnt); | ||||
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 71 Lines • Show Last 20 Lines |
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.