Changeset View
Standalone View
sys/kern/subr_intr.c
Show First 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | |||||
u_int intr_nirq = NIRQ; | u_int intr_nirq = NIRQ; | ||||
SYSCTL_UINT(_machdep, OID_AUTO, nirq, CTLFLAG_RDTUN, &intr_nirq, 0, | SYSCTL_UINT(_machdep, OID_AUTO, nirq, CTLFLAG_RDTUN, &intr_nirq, 0, | ||||
"Number of IRQs"); | "Number of IRQs"); | ||||
/* Data for statistics reporting. */ | /* Data for statistics reporting. */ | ||||
static u_long *intrcnt; | static u_long *intrcnt; | ||||
static char *intrnames; | static char *intrnames; | ||||
static u_int intrcnt_index; | static u_int intrcnt_index; | ||||
static int nintrcnt; | static int nintrcnt; | ||||
static bitstr_t *intrcnt_bitmap; | static bitstr_t *intrcnt_bitmap; | ||||
ehem_freebsd_m5p.com: I chose to match what x86 and PowerPC are using. `vmstat -i` is supposed to be able to… | |||||
Done Inline ActionsI was not aware of this functionality... this actually puts some real limitations on how the interface can be changed. For example, moving regular I/O interrupt counts out of the intrcnt array totally breaks reading those counts from a crash dump. I will need to think about this. mhorne: I was not aware of this functionality... this actually puts some real limitations on how the… | |||||
Done Inline ActionsI believe what could be claimed to be supported functionality would be vmstat -M kernelcore -i should work. Otherwise intrcnt looks very much like an albatross at this point and prevents any useful redesign work being done. ehem_freebsd_m5p.com: I believe what could be claimed to be supported functionality would be `vmstat -M kernelcore… | |||||
static struct intr_irqsrc *intr_map_get_isrc(u_int res_id); | static struct intr_irqsrc *intr_map_get_isrc(u_int res_id); | ||||
static void intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc); | static void intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc); | ||||
static struct intr_map_data * intr_map_get_map_data(u_int res_id); | static struct intr_map_data * intr_map_get_map_data(u_int res_id); | ||||
static void intr_map_copy_map_data(u_int res_id, device_t *dev, intptr_t *xref, | static void intr_map_copy_map_data(u_int res_id, device_t *dev, intptr_t *xref, | ||||
struct intr_map_data **data); | struct intr_map_data **data); | ||||
/* | /* | ||||
Show All 14 Lines | intr_irq_init(void *dummy __unused) | ||||
*/ | */ | ||||
nintrcnt = intr_nirq * 2; | nintrcnt = intr_nirq * 2; | ||||
#ifdef SMP | #ifdef SMP | ||||
nintrcnt += INTR_IPI_COUNT * MAXCPU; | nintrcnt += INTR_IPI_COUNT * MAXCPU; | ||||
#endif | #endif | ||||
intrcnt = mallocarray(nintrcnt, sizeof(u_long), M_INTRNG, | intrcnt = mallocarray(nintrcnt, sizeof(u_long), M_INTRNG, | ||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
intrnames = mallocarray(nintrcnt, INTRNAME_LEN, M_INTRNG, | intrnames = mallocarray(nintrcnt, INTRNAME_LEN, M_INTRNG, | ||||
M_WAITOK | M_ZERO); | 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 + INTRNAME_LEN * index, just below. ehem_freebsd_m5p.com: `intrnames = mallocarray();` this is clearly interpreting `intrnames` as an array of fixed… | |||||
/* Allocate the bitmap tracking counter allocations. */ | /* Allocate the bitmap tracking counter allocations. */ | ||||
intrcnt_bitmap = bit_alloc(nintrcnt, M_INTRNG, M_WAITOK | M_ZERO); | intrcnt_bitmap = bit_alloc(nintrcnt, M_INTRNG, M_WAITOK | M_ZERO); | ||||
irq_sources = mallocarray(intr_nirq, sizeof(struct intr_irqsrc*), | irq_sources = mallocarray(intr_nirq, sizeof(struct intr_irqsrc*), | ||||
M_INTRNG, M_WAITOK | M_ZERO); | M_INTRNG, M_WAITOK | M_ZERO); | ||||
} | } | ||||
SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL); | SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL); | ||||
Show All 35 Lines | |||||
* Virtualization for interrupt source interrupt stray counter increment. | * Virtualization for interrupt source interrupt stray counter increment. | ||||
*/ | */ | ||||
static inline void | static inline void | ||||
isrc_increment_straycount(struct intr_irqsrc *isrc) | isrc_increment_straycount(struct intr_irqsrc *isrc) | ||||
{ | { | ||||
isrc->isrc_count[1]++; | isrc->isrc_count[1]++; | ||||
} | } | ||||
/* | /* | ||||
* Virtualization for interrupt source interrupt name update. | * Virtualization for interrupt source interrupt name update. | ||||
*/ | */ | ||||
static void | static void | ||||
isrc_update_name(struct intr_irqsrc *isrc, const char *name) | isrc_update_name(struct intr_irqsrc *isrc, const char *name) | ||||
{ | { | ||||
char str[INTRNAME_LEN]; | char str[INTRNAME_LEN]; | ||||
mtx_assert(&isrc_table_lock, MA_OWNED); | mtx_assert(&isrc_table_lock, MA_OWNED); | ||||
if (name != NULL) { | if (name != NULL) { | ||||
snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); | snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); | ||||
intrcnt_setname(str, isrc->isrc_index); | intrcnt_setname(str, isrc->isrc_index); | ||||
snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, | snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, | ||||
name); | name); | ||||
intrcnt_setname(str, isrc->isrc_index + 1); | intrcnt_setname(str, isrc->isrc_index + 1); | ||||
} else { | } else { | ||||
snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); | snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); | ||||
intrcnt_setname(str, isrc->isrc_index); | intrcnt_setname(str, isrc->isrc_index); | ||||
snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); | snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); | ||||
intrcnt_setname(str, isrc->isrc_index + 1); | intrcnt_setname(str, isrc->isrc_index + 1); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Virtualization for interrupt source interrupt counters setup. | * Virtualization for interrupt source interrupt counters setup. | ||||
*/ | */ | ||||
static void | static void | ||||
isrc_setup_counters(struct intr_irqsrc *isrc) | isrc_setup_counters(struct intr_irqsrc *isrc) | ||||
{ | { | ||||
int index; | int index; | ||||
mtx_assert(&isrc_table_lock, MA_OWNED); | mtx_assert(&isrc_table_lock, MA_OWNED); | ||||
/* | index = nintrcnt - 2; | ||||
* Allocate two counter values, the second tracking "stray" interrupts. | |||||
*/ | |||||
bit_ffc_area(intrcnt_bitmap, nintrcnt, 2, &index); | |||||
if (index == -1) | |||||
panic("Failed to allocate 2 counters. Array exhausted?"); | |||||
bit_nset(intrcnt_bitmap, index, index + 1); | |||||
if (index >= intrcnt_index) | |||||
intrcnt_index = index + 2; | |||||
isrc->isrc_index = index; | isrc->isrc_index = index; | ||||
isrc->isrc_count = &intrcnt[index]; | isrc->isrc_count = &intrcnt[index]; | ||||
isrc_update_name(isrc, NULL); | isrc_update_name(isrc, NULL); | ||||
} | } | ||||
/* | /* | ||||
* Virtualization for interrupt source interrupt counters release. | * Virtualization for interrupt source interrupt counters release. | ||||
*/ | */ | ||||
static void | static void | ||||
isrc_release_counters(struct intr_irqsrc *isrc) | isrc_release_counters(struct intr_irqsrc *isrc) | ||||
{ | { | ||||
int idx = isrc->isrc_index; | int idx = isrc->isrc_index; | ||||
mtx_assert(&isrc_table_lock, MA_OWNED); | mtx_assert(&isrc_table_lock, MA_OWNED); | ||||
bit_nclear(intrcnt_bitmap, idx, idx + 1); | bit_nclear(intrcnt_bitmap, idx, idx + 1); | ||||
} | } | ||||
#ifdef SMP | #ifdef SMP | ||||
/* | /* | ||||
* Virtualization for interrupt source IPI counters setup. | * Virtualization for interrupt source IPI counters setup. | ||||
*/ | */ | ||||
u_long * | u_long * | ||||
intr_ipi_setup_counters(const char *name) | intr_ipi_setup_counters(const char *name) | ||||
{ | { | ||||
u_int index, i; | u_int index, i; | ||||
char str[INTRNAME_LEN]; | char str[INTRNAME_LEN]; | ||||
mtx_lock(&isrc_table_lock); | mtx_lock(&isrc_table_lock); | ||||
/* | /* | ||||
* We should never have a problem finding MAXCPU contiguous counters, | * We should never have a problem finding MAXCPU contiguous counters, | ||||
* in practice. Interrupts will be allocated sequentially during boot, | * in practice. Interrupts will be allocated sequentially during boot, | ||||
* so the array should fill from low to high index. Once reserved, the | * so the array should fill from low to high index. Once reserved, the | ||||
* IPI counters will never be released. Similarly, we will not need to | * IPI counters will never be released. Similarly, we will not need to | ||||
* allocate more IPIs once the system is running. | * allocate more IPIs once the system is running. | ||||
*/ | */ | ||||
bit_ffc_area(intrcnt_bitmap, nintrcnt, MAXCPU, &index); | bit_ffc_area(intrcnt_bitmap, nintrcnt, MAXCPU, &index); | ||||
if (index == -1) | if (index == -1) | ||||
panic("Failed to allocate %d counters. Array exhausted?", | panic("Failed to allocate %d counters. Array exhausted?", | ||||
MAXCPU); | MAXCPU); | ||||
bit_nset(intrcnt_bitmap, index, index + MAXCPU - 1); | bit_nset(intrcnt_bitmap, index, index + MAXCPU - 1); | ||||
if (index >= intrcnt_index) | if (index >= intrcnt_index) | ||||
intrcnt_index = index + MAXCPU; | intrcnt_index = index + MAXCPU; | ||||
for (i = 0; i < MAXCPU; i++) { | for (i = 0; i < MAXCPU; i++) { | ||||
snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); | snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); | ||||
intrcnt_setname(str, index + i); | intrcnt_setname(str, index + i); | ||||
} | } | ||||
mtx_unlock(&isrc_table_lock); | mtx_unlock(&isrc_table_lock); | ||||
return (&intrcnt[index]); | return (&intrcnt[index]); | ||||
} | } | ||||
#endif | #endif | ||||
Done Inline ActionsI hope D35607 gets approved soon, since then this entire section could be nuked. ehem_freebsd_m5p.com: I hope D35607 gets approved soon, since then this entire section could be nuked. | |||||
Done Inline ActionsInterestingly when/if D35898 goes through, the ii_names field which I had originally been suggesting to remove could be the basis of an alternative implementation of the sysctls. This in turn would allow purging the remains of the interrupt table. ehem_freebsd_m5p.com: Interestingly when/if D35898 goes through, the `ii_names` field which I had originally been… | |||||
/* | /* | ||||
* Main interrupt dispatch handler. It's called straight | * Main interrupt dispatch handler. It's called straight | ||||
* from the assembler, where CPU interrupt is served. | * from the assembler, where CPU interrupt is served. | ||||
*/ | */ | ||||
void | void | ||||
intr_irq_handler(struct trapframe *tf) | intr_irq_handler(struct trapframe *tf) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 1,243 Lines • ▼ Show 20 Lines | DB_SHOW_COMMAND_FLAGS(irqs, db_show_irqs, DB_CMD_MEMSAFE) | ||||
u_long num; | u_long num; | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
for (irqsum = 0, i = 0; i < intr_nirq; i++) { | for (irqsum = 0, i = 0; i < intr_nirq; i++) { | ||||
isrc = irq_sources[i]; | isrc = irq_sources[i]; | ||||
if (isrc == NULL) | if (isrc == NULL) | ||||
continue; | continue; | ||||
num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0; | num = isrc->isrc_event != NULL ? isrc->isrc_event->ie_intrcnt[0] : 0; | ||||
db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, | db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, | ||||
isrc->isrc_name, isrc->isrc_cpu.__bits[0], | isrc->isrc_name, isrc->isrc_cpu.__bits[0], | ||||
isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); | isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); | ||||
irqsum += num; | irqsum += num; | ||||
} | } | ||||
db_printf("irq total %u\n", irqsum); | db_printf("irq total %u\n", irqsum); | ||||
} | } | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 171 Lines • ▼ Show 20 Lines | intr_map_init(void *dummy __unused) | ||||
irq_map = mallocarray(irq_map_count, sizeof(struct intr_map_entry*), | irq_map = mallocarray(irq_map_count, sizeof(struct intr_map_entry*), | ||||
M_INTRNG, M_WAITOK | M_ZERO); | M_INTRNG, M_WAITOK | M_ZERO); | ||||
} | } | ||||
SYSINIT(intr_map_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_map_init, NULL); | SYSINIT(intr_map_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_map_init, NULL); | ||||
/* | /* | ||||
* 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); | |||||
Done Inline ActionsThis is what I had suggested. Use something near-identical to existing behavior for handling reporting of IPI interrupts, then use a hook into intr_event for reporting normal interrupts. Unfortunately this requires sintrcnt/sintrnames to match the size of the valid table, which means they get large increments. ehem_freebsd_m5p.com: This is what I had suggested. Use something near-identical to existing behavior for handling… | |||||
} | } | ||||
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 |
I chose to match what x86 and PowerPC are using. vmstat -i is supposed to be able to retrieve this from kernel core dumps, so matching seems the way to go.