Changeset View
Standalone View
sys/kern/subr_intr.c
Show All 36 Lines | |||||
*/ | */ | ||||
#include "opt_ddb.h" | #include "opt_ddb.h" | ||||
#include "opt_hwpmc_hooks.h" | #include "opt_hwpmc_hooks.h" | ||||
#include "opt_iommu.h" | #include "opt_iommu.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bitstring.h> | |||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/cpuset.h> | #include <sys/cpuset.h> | ||||
#include <sys/interrupt.h> | #include <sys/interrupt.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | |||||
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 MI statistics reporting. */ | /* Data for MI statistics reporting. */ | ||||
u_long *intrcnt; | u_long *intrcnt; | ||||
char *intrnames; | char *intrnames; | ||||
size_t sintrcnt; | size_t sintrcnt; | ||||
size_t sintrnames; | size_t sintrnames; | ||||
static u_int intrcnt_index; | int nintrcnt; | ||||
static bitstr_t *intrcnt_bitmap; | |||||
ehem_freebsd_m5p.com: I believe `vmstat` is merely checking for the existence of `nintrcnt`, not the type. As such I… | |||||
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); | ||||
/* | /* | ||||
* Interrupt framework initialization routine. | * Interrupt framework initialization routine. | ||||
*/ | */ | ||||
static void | static void | ||||
intr_irq_init(void *dummy __unused) | intr_irq_init(void *dummy __unused) | ||||
{ | { | ||||
u_int intrcnt_count; | |||||
SLIST_INIT(&pic_list); | SLIST_INIT(&pic_list); | ||||
mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF); | mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF); | ||||
mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF); | mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF); | ||||
/* | /* | ||||
* - 2 counters for each I/O interrupt. | * - 2 counters for each I/O interrupt. | ||||
* - MAXCPU counters for each IPI counters for SMP. | * - MAXCPU counters for each IPI counters for SMP. | ||||
*/ | */ | ||||
intrcnt_count = intr_nirq * 2; | nintrcnt = intr_nirq * 2; | ||||
#ifdef SMP | #ifdef SMP | ||||
intrcnt_count += INTR_IPI_COUNT * MAXCPU; | nintrcnt += INTR_IPI_COUNT * MAXCPU; | ||||
#endif | #endif | ||||
intrcnt = mallocarray(intrcnt_count, sizeof(u_long), M_INTRNG, | intrcnt = mallocarray(nintrcnt, sizeof(u_long), M_INTRNG, | ||||
Not Done Inline Actionswhile perhaps beyond the scope of this patch, this needs to be converted to a per-cpu alloc mjg: while perhaps beyond the scope of this patch, this needs to be converted to a per-cpu alloc | |||||
Done Inline ActionsIndeed. There is quite a bit of complexity/assumptions around this simple interface (I'm talking about intrcnt/intrnames arrays), which makes such a conversion a bigger lift than it would seem at first glance. Most interrupts are bound to a single CPU for their entire lifetime, unless explicitly rebound. So I guess you are speaking mostly about having per-CPU pools of counters to pull from when setting up a new isrc. mhorne: Indeed. There is quite a bit of complexity/assumptions around this simple interface (I'm… | |||||
Not Done Inline ActionsIMHO this should be solved by extending counter(9) with an operation on a given (largest, depending on the platform) data type that allows atomic reads (and writes) without locking (so it allows incrementing in an interrupt context while maintaining consistent reads). mmel: IMHO this should be solved by extending counter(9) with an operation on a given (largest… | |||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
intrnames = mallocarray(intrcnt_count, INTRNAME_LEN, M_INTRNG, | intrnames = mallocarray(nintrcnt, INTRNAME_LEN, M_INTRNG, | ||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
sintrcnt = intrcnt_count * sizeof(u_long); | sintrcnt = nintrcnt * sizeof(u_long); | ||||
sintrnames = intrcnt_count * INTRNAME_LEN; | sintrnames = nintrcnt * INTRNAME_LEN; | ||||
/* Allocate the bitmap tracking counter allocations. */ | |||||
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); | ||||
static void | static void | ||||
intrcnt_setname(const char *name, int index) | intrcnt_setname(const char *name, int index) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* 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) | ||||
{ | { | ||||
u_int index; | int index; | ||||
mtx_assert(&isrc_table_lock, MA_OWNED); | |||||
/* | /* | ||||
* XXX - it does not work well with removable controllers and | * Allocate two counter values, the second tracking "stray" interrupts. | ||||
* interrupt sources !!! | |||||
*/ | */ | ||||
index = atomic_fetchadd_int(&intrcnt_index, 2); | 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); | |||||
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; | |||||
panic("%s: not implemented", __func__); | mtx_assert(&isrc_table_lock, MA_OWNED); | ||||
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]; | ||||
index = atomic_fetchadd_int(&intrcnt_index, MAXCPU); | mtx_lock(&isrc_table_lock); | ||||
/* | |||||
* We should never have a problem finding MAXCPU contiguous counters, | |||||
* in practice. Interrupts will be allocated sequentially during boot, | |||||
* 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 | |||||
* allocate more IPIs once the system is running. | |||||
*/ | |||||
bit_ffc_area(intrcnt_bitmap, nintrcnt, MAXCPU, &index); | |||||
if (index == -1) | |||||
Not Done Inline Actionsthis should be a non-invariants check, as in if (index == -1) panic("...."); mjg: this should be a non-invariants check, as in
```
if (index == -1)
panic("....");
``` | |||||
panic("Failed to allocate %d counters. Array exhausted?", | |||||
MAXCPU); | |||||
bit_nset(intrcnt_bitmap, index, index + MAXCPU - 1); | |||||
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); | |||||
return (&intrcnt[index]); | return (&intrcnt[index]); | ||||
} | } | ||||
#endif | #endif | ||||
/* | /* | ||||
* 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. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 1,430 Lines • Show Last 20 Lines |
I believe vmstat is merely checking for the existence of nintrcnt, not the type. As such I would leave this as a u_int (unless I'm wrong about my understanding of vmstat).