#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/thread.h>
#include <sys/proc.h>
#include <sys/random.h>
#include <sys/serialize.h>
#include <sys/interrupt.h>
#include <sys/bus.h>
#include <sys/machintr.h>
#include <machine/frame.h>
#include <sys/thread2.h>
#include <sys/mplock2.h>
struct intr_info;
typedef struct intrec {
struct intrec *next;
struct intr_info *info;
inthand2_t *handler;
void *argument;
char *name;
int intr;
int intr_flags;
struct lwkt_serialize *serializer;
} *intrec_t;
struct intr_info {
intrec_t i_reclist;
struct thread *i_thread;
struct random_softc i_random;
long i_count;
int i_running;
short i_mplock_required;
short i_flags;
int i_fast;
int i_slow;
int i_state;
int i_errorticks;
unsigned long i_straycount;
int i_cpuid;
int i_intr;
};
struct intr_info_block {
struct intr_info ary[MAXCPU][MAX_INTS];
};
static struct intr_info_block *intr_block;
static struct intr_info *swi_info_ary[MAX_SOFTINTS];
static int max_installed_hard_intr[MAXCPU];
MALLOC_DEFINE(M_INTRMNG, "intrmng", "interrupt management");
#define EMERGENCY_INTR_POLLING_FREQ_MAX 20000
#ifdef INVARIANTS
#define TD_INVARIANTS_DECLARE \
int spincount; \
lwkt_tokref_t curstop
#define TD_INVARIANTS_GET(td) \
do { \
spincount = (td)->td_gd->gd_spinlocks; \
curstop = (td)->td_toks_stop; \
} while(0)
#define TD_INVARIANTS_TEST(td, name) \
do { \
KASSERT(spincount == (td)->td_gd->gd_spinlocks, \
("spincount mismatch after interrupt handler %s", \
name)); \
KASSERT(curstop == (td)->td_toks_stop, \
("token count mismatch after interrupt handler %s", \
name)); \
} while(0)
#else
#define TD_INVARIANTS_DECLARE
#define TD_INVARIANTS_GET(td)
#define TD_INVARIANTS_TEST(td, name)
#endif
static int sysctl_emergency_freq(SYSCTL_HANDLER_ARGS);
static int sysctl_emergency_enable(SYSCTL_HANDLER_ARGS);
static void emergency_intr_timer_callback(systimer_t, int, struct intrframe *);
static void ithread_handler(void *arg);
static void ithread_emergency(void *arg);
static void report_stray_interrupt(struct intr_info *info, const char *func);
static void int_moveto_destcpu(int *, int);
static void int_moveto_origcpu(int, int);
static void sched_ithd_intern(struct intr_info *info);
static struct systimer emergency_intr_timer[MAXCPU];
static struct thread *emergency_intr_thread[MAXCPU];
#define ISTATE_NOTHREAD 0
#define ISTATE_NORMAL 1
#define ISTATE_LIVELOCKED 2
static int livelock_limit = 40000;
static int livelock_limit_hi = 120000;
static int livelock_lowater = 20000;
static int livelock_debug = -1;
SYSCTL_INT(_kern, OID_AUTO, livelock_limit,
CTLFLAG_RW, &livelock_limit, 0, "Livelock interrupt rate limit");
SYSCTL_INT(_kern, OID_AUTO, livelock_limit_hi,
CTLFLAG_RW, &livelock_limit_hi, 0,
"Livelock interrupt rate limit (high frequency)");
SYSCTL_INT(_kern, OID_AUTO, livelock_lowater,
CTLFLAG_RW, &livelock_lowater, 0, "Livelock low-water mark restore");
SYSCTL_INT(_kern, OID_AUTO, livelock_debug,
CTLFLAG_RW, &livelock_debug, 0, "Livelock debug intr#");
static int emergency_intr_enable = 0;
TUNABLE_INT("kern.emergency_intr_enable", &emergency_intr_enable);
SYSCTL_PROC(_kern, OID_AUTO, emergency_intr_enable, CTLTYPE_INT | CTLFLAG_RW,
0, 0, sysctl_emergency_enable, "I", "Emergency Interrupt Poll Enable");
static int emergency_intr_freq = 10;
TUNABLE_INT("kern.emergency_intr_freq", &emergency_intr_freq);
SYSCTL_PROC(_kern, OID_AUTO, emergency_intr_freq, CTLTYPE_INT | CTLFLAG_RW,
0, 0, sysctl_emergency_freq, "I", "Emergency Interrupt Poll Frequency");
static int
sysctl_emergency_enable(SYSCTL_HANDLER_ARGS)
{
int error, enabled, cpuid, freq, origcpu;
enabled = emergency_intr_enable;
error = sysctl_handle_int(oidp, &enabled, 0, req);
if (error || req->newptr == NULL)
return error;
emergency_intr_enable = enabled;
if (emergency_intr_enable)
freq = emergency_intr_freq;
else
freq = 1;
origcpu = mycpuid;
for (cpuid = 0; cpuid < ncpus; ++cpuid) {
lwkt_migratecpu(cpuid);
systimer_adjust_periodic(&emergency_intr_timer[cpuid], freq);
}
lwkt_migratecpu(origcpu);
return 0;
}
static int
sysctl_emergency_freq(SYSCTL_HANDLER_ARGS)
{
int error, phz, cpuid, freq, origcpu;
phz = emergency_intr_freq;
error = sysctl_handle_int(oidp, &phz, 0, req);
if (error || req->newptr == NULL)
return error;
if (phz <= 0)
return EINVAL;
else if (phz > EMERGENCY_INTR_POLLING_FREQ_MAX)
phz = EMERGENCY_INTR_POLLING_FREQ_MAX;
emergency_intr_freq = phz;
if (emergency_intr_enable)
freq = emergency_intr_freq;
else
freq = 1;
origcpu = mycpuid;
for (cpuid = 0; cpuid < ncpus; ++cpuid) {
lwkt_migratecpu(cpuid);
systimer_adjust_periodic(&emergency_intr_timer[cpuid], freq);
}
lwkt_migratecpu(origcpu);
return 0;
}
void *
register_swi(int intr, inthand2_t *handler, void *arg, const char *name,
struct lwkt_serialize *serializer, int cpuid)
{
if (intr < FIRST_SOFTINT || intr >= MAX_INTS)
panic("register_swi: bad intr %d", intr);
if (cpuid < 0)
cpuid = intr % ncpus;
return(register_int(intr, handler, arg, name, serializer, 0, cpuid));
}
void *
register_swi_mp(int intr, inthand2_t *handler, void *arg, const char *name,
struct lwkt_serialize *serializer, int cpuid)
{
if (intr < FIRST_SOFTINT || intr >= MAX_INTS)
panic("register_swi: bad intr %d", intr);
if (cpuid < 0)
cpuid = intr % ncpus;
return(register_int(intr, handler, arg, name, serializer,
INTR_MPSAFE, cpuid));
}
void *
register_int(int intr, inthand2_t *handler, void *arg, const char *name,
struct lwkt_serialize *serializer, int intr_flags, int cpuid)
{
struct intr_info *info;
struct intrec **list;
intrec_t rec = NULL;
int orig_cpuid;
KKASSERT(cpuid >= 0 && cpuid < ncpus);
if (intr < 0 || intr >= MAX_INTS)
panic("register_int: bad intr %d", intr);
if (name == NULL)
name = "???";
info = &intr_block->ary[cpuid][intr];
int_moveto_destcpu(&orig_cpuid, cpuid);
if (info->i_flags & INTR_EXCL)
goto done;
list = &info->i_reclist;
if ((intr_flags & INTR_EXCL) && *list != NULL)
goto done;
rec = kmalloc(sizeof(struct intrec), M_DEVBUF, M_INTWAIT);
rec->name = kmalloc(strlen(name) + 1, M_DEVBUF, M_INTWAIT);
strcpy(rec->name, name);
rec->info = info;
rec->handler = handler;
rec->argument = arg;
rec->intr = intr;
rec->intr_flags = intr_flags;
rec->next = NULL;
rec->serializer = serializer;
if (emergency_intr_thread[cpuid] == NULL) {
emergency_intr_thread[cpuid] = kmalloc(sizeof(struct thread), M_DEVBUF,
M_INTWAIT | M_ZERO);
lwkt_create(ithread_emergency, NULL, NULL,
emergency_intr_thread[cpuid],
TDF_NOSTART | TDF_INTTHREAD, cpuid, "ithreadE %d",
cpuid);
systimer_init_periodic_nq(&emergency_intr_timer[cpuid],
emergency_intr_timer_callback,
emergency_intr_thread[cpuid],
(emergency_intr_enable ? emergency_intr_freq : 1));
}
if (info->i_state == ISTATE_NOTHREAD) {
info->i_state = ISTATE_NORMAL;
info->i_thread = kmalloc(sizeof(struct thread), M_DEVBUF,
M_INTWAIT | M_ZERO);
lwkt_create(ithread_handler, (void *)(intptr_t)intr, NULL,
info->i_thread, TDF_NOSTART | TDF_INTTHREAD, cpuid,
"ithread%d %d", intr, cpuid);
if (intr >= FIRST_SOFTINT)
lwkt_setpri(info->i_thread, TDPRI_SOFT_NORM);
else
lwkt_setpri(info->i_thread, TDPRI_INT_MED);
info->i_thread->td_preemptable = lwkt_preempt;
}
if ((intr_flags & INTR_MPSAFE) == 0) {
info->i_mplock_required = 1;
kprintf("interrupt uses mplock: %s\n", name);
}
if (intr_flags & INTR_CLOCK) {
atomic_set_int(&info->i_thread->td_flags, TDF_CLKTHREAD);
++info->i_fast;
} else {
++info->i_slow;
}
info->i_flags |= (intr_flags & INTR_EXCL);
if (info->i_slow + info->i_fast == 1 && (intr_flags & INTR_HIFREQ)) {
info->i_flags |= INTR_HIFREQ;
} else {
info->i_flags &= ~INTR_HIFREQ;
}
if ((intr_flags & INTR_NOENTROPY) == 0 && info->i_random.sc_enabled == 0) {
info->i_random.sc_enabled = 1;
info->i_random.sc_intr = intr;
}
crit_enter();
while (*list != NULL)
list = &(*list)->next;
*list = rec;
crit_exit();
if (intr < FIRST_SOFTINT) {
if (max_installed_hard_intr[cpuid] <= intr)
max_installed_hard_intr[cpuid] = intr + 1;
}
if (intr >= FIRST_SOFTINT)
swi_info_ary[intr - FIRST_SOFTINT] = info;
if (intr < FIRST_SOFTINT && info->i_slow + info->i_fast == 1)
machintr_intr_setup(intr, intr_flags);
done:
int_moveto_origcpu(orig_cpuid, cpuid);
return(rec);
}
void
unregister_swi(void *id, int intr, int cpuid)
{
if (cpuid < 0)
cpuid = intr % ncpus;
unregister_int(id, cpuid);
}
void
unregister_int(void *id, int cpuid)
{
struct intr_info *info;
struct intrec **list;
intrec_t rec;
int intr, orig_cpuid;
KKASSERT(cpuid >= 0 && cpuid < ncpus);
intr = ((intrec_t)id)->intr;
if (intr < 0 || intr >= MAX_INTS)
panic("register_int: bad intr %d", intr);
info = &intr_block->ary[cpuid][intr];
int_moveto_destcpu(&orig_cpuid, cpuid);
crit_enter();
list = &info->i_reclist;
while ((rec = *list) != NULL) {
if (rec == id)
break;
list = &rec->next;
}
if (rec) {
intrec_t rec0;
*list = rec->next;
if (rec->intr_flags & INTR_CLOCK)
--info->i_fast;
else
--info->i_slow;
if (intr < FIRST_SOFTINT && info->i_fast + info->i_slow == 0)
machintr_intr_teardown(intr);
for (rec0 = info->i_reclist; rec0; rec0 = rec0->next) {
if ((rec0->intr_flags & INTR_MPSAFE) == 0)
break;
}
if (rec0 == NULL)
info->i_mplock_required = 0;
}
if (info->i_reclist == NULL) {
info->i_flags = 0;
if (intr >= FIRST_SOFTINT)
swi_info_ary[intr - FIRST_SOFTINT] = NULL;
} else if (info->i_fast + info->i_slow == 1 &&
(info->i_reclist->intr_flags & INTR_HIFREQ)) {
info->i_flags |= INTR_HIFREQ;
}
crit_exit();
int_moveto_origcpu(orig_cpuid, cpuid);
if (rec != NULL) {
kfree(rec->name, M_DEVBUF);
kfree(rec, M_DEVBUF);
} else {
kprintf("warning: unregister_int: int %d handler for %s not found\n",
intr, ((intrec_t)id)->name);
}
}
long
get_interrupt_counter(int intr, int cpuid)
{
struct intr_info *info;
KKASSERT(cpuid >= 0 && cpuid < ncpus);
if (intr < 0 || intr >= MAX_INTS)
panic("register_int: bad intr %d", intr);
info = &intr_block->ary[cpuid][intr];
return(info->i_count);
}
void
register_randintr(int intr)
{
struct intr_info *info;
int cpuid;
if (intr < 0 || intr >= MAX_INTS)
panic("register_randintr: bad intr %d", intr);
for (cpuid = 0; cpuid < ncpus; ++cpuid) {
info = &intr_block->ary[cpuid][intr];
info->i_random.sc_intr = intr;
info->i_random.sc_enabled = 1;
}
}
void
unregister_randintr(int intr)
{
struct intr_info *info;
int cpuid;
if (intr < 0 || intr >= MAX_INTS)
panic("register_swi: bad intr %d", intr);
for (cpuid = 0; cpuid < ncpus; ++cpuid) {
info = &intr_block->ary[cpuid][intr];
info->i_random.sc_enabled = -1;
}
}
int
next_registered_randintr(int intr)
{
struct intr_info *info;
if (intr < 0 || intr >= MAX_INTS)
panic("register_swi: bad intr %d", intr);
while (intr < MAX_INTS) {
int cpuid;
for (cpuid = 0; cpuid < ncpus; ++cpuid) {
info = &intr_block->ary[cpuid][intr];
if (info->i_random.sc_enabled > 0)
return intr;
}
++intr;
}
return intr;
}
static void
sched_ithd_remote(void *arg)
{
sched_ithd_intern(arg);
}
static void
sched_ithd_intern(struct intr_info *info)
{
++info->i_count;
if (info->i_state != ISTATE_NOTHREAD) {
if (info->i_reclist == NULL) {
report_stray_interrupt(info, "sched_ithd");
} else {
if (info->i_thread->td_gd == mycpu) {
if (info->i_running == 0) {
info->i_running = 1;
if (info->i_state != ISTATE_LIVELOCKED)
lwkt_schedule(info->i_thread);
}
} else {
lwkt_send_ipiq(info->i_thread->td_gd, sched_ithd_remote, info);
}
}
} else {
report_stray_interrupt(info, "sched_ithd");
}
}
void
sched_ithd_soft(int intr)
{
struct intr_info *info;
KKASSERT(intr >= FIRST_SOFTINT && intr < MAX_INTS);
info = swi_info_ary[intr - FIRST_SOFTINT];
if (info != NULL) {
sched_ithd_intern(info);
} else {
kprintf("unregistered softint %d got scheduled on cpu%d\n",
intr, mycpuid);
}
}
void
sched_ithd_hard(int intr)
{
KKASSERT(intr >= 0 && intr < MAX_HARDINTS);
sched_ithd_intern(&intr_block->ary[mycpuid][intr]);
}
#ifdef _KERNEL_VIRTUAL
void
sched_ithd_hard_virtual(int intr)
{
KKASSERT(intr >= 0 && intr < MAX_HARDINTS);
sched_ithd_intern(&intr_block->ary[0][intr]);
}
void *
register_int_virtual(int intr, inthand2_t *handler, void *arg, const char *name,
struct lwkt_serialize *serializer, int intr_flags)
{
return register_int(intr, handler, arg, name, serializer, intr_flags, 0);
}
void
unregister_int_virtual(void *id)
{
unregister_int(id, 0);
}
#endif
static void
report_stray_interrupt(struct intr_info *info, const char *func)
{
++info->i_straycount;
if (info->i_straycount < 10) {
if (info->i_errorticks == ticks)
return;
info->i_errorticks = ticks;
kprintf("%s: stray interrupt %d on cpu%d\n",
func, info->i_intr, mycpuid);
} else if (info->i_straycount == 10) {
kprintf("%s: %ld stray interrupts %d on cpu%d - "
"there will be no further reports\n", func,
info->i_straycount, info->i_intr, mycpuid);
}
}
static void
ithread_livelock_wakeup(systimer_t st, int in_ipi __unused,
struct intrframe *frame __unused)
{
struct intr_info *info;
info = &intr_block->ary[mycpuid][(int)(intptr_t)st->data];
if (info->i_state != ISTATE_NOTHREAD)
lwkt_schedule(info->i_thread);
}
static __inline void
ithread_fast_sched(int intr, thread_t td)
{
++td->td_nest_count;
crit_exit_quick(td);
sched_ithd_hard(intr);
crit_enter_quick(td);
--td->td_nest_count;
}
int ithread_fast_handler(struct intrframe *frame);
int
ithread_fast_handler(struct intrframe *frame)
{
int intr;
struct intr_info *info;
struct intrec **list;
int must_schedule;
int got_mplock;
TD_INVARIANTS_DECLARE;
intrec_t rec, nrec;
globaldata_t gd;
thread_t td;
intr = frame->if_vec;
gd = mycpu;
td = curthread;
KKASSERT(td->td_critcount);
if (intr_block == NULL)
return 0;
info = &intr_block->ary[mycpuid][intr];
if (info->i_fast == 0) {
++gd->gd_cnt.v_intr;
ithread_fast_sched(intr, td);
return(1);
}
if (info->i_running)
return(1);
++gd->gd_intr_nesting_level;
++gd->gd_cnt.v_intr;
must_schedule = info->i_slow;
got_mplock = 0;
TD_INVARIANTS_GET(td);
list = &info->i_reclist;
for (rec = *list; rec; rec = nrec) {
nrec = rec->next;
if (rec->intr_flags & INTR_CLOCK) {
if ((rec->intr_flags & INTR_MPSAFE) == 0 && got_mplock == 0) {
if (try_mplock() == 0) {
must_schedule = 1;
break;
}
got_mplock = 1;
}
if (rec->serializer) {
must_schedule += lwkt_serialize_handler_try(
rec->serializer, rec->handler,
rec->argument, frame);
} else {
rec->handler(rec->argument, frame);
}
TD_INVARIANTS_TEST(td, rec->name);
}
}
--gd->gd_intr_nesting_level;
if (got_mplock)
rel_mplock();
if (must_schedule > 0)
ithread_fast_sched(intr, td);
else if (must_schedule == 0)
++info->i_count;
return(must_schedule);
}
#define LIVELOCK_TIMEFRAME(freq) ((freq) >> 2)
static void
ithread_handler(void *arg)
{
struct intr_info *info;
int use_limit;
uint32_t lseconds;
int intr, cpuid = mycpuid;
int mpheld;
struct intrec **list;
intrec_t rec, nrec;
globaldata_t gd;
struct systimer ill_timer;
u_int ill_count;
int upper_limit;
TD_INVARIANTS_DECLARE;
ill_count = 0;
intr = (int)(intptr_t)arg;
info = &intr_block->ary[cpuid][intr];
list = &info->i_reclist;
gd = mycpu;
lseconds = gd->gd_time_seconds;
crit_enter_gd(gd);
mpheld = 0;
for (;;) {
if (info->i_mplock_required != mpheld) {
if (info->i_mplock_required) {
KKASSERT(mpheld == 0);
get_mplock();
mpheld = 1;
} else {
KKASSERT(mpheld != 0);
rel_mplock();
mpheld = 0;
}
}
TD_INVARIANTS_GET(gd->gd_curthread);
if (info->i_running) {
++ill_count;
info->i_running = 0;
if (*list == NULL)
report_stray_interrupt(info, "ithread_handler");
for (rec = *list; rec; rec = nrec) {
nrec = rec->next;
if (rec->handler == NULL) {
kprintf("NULL HANDLER %s\n", rec->name);
} else
if (rec->serializer) {
lwkt_serialize_handler_call(rec->serializer, rec->handler,
rec->argument, NULL);
} else {
rec->handler(rec->argument, NULL);
}
TD_INVARIANTS_TEST(gd->gd_curthread, rec->name);
}
}
if (info->i_random.sc_enabled > 0)
add_interrupt_randomness(intr);
if (intr < FIRST_SOFTINT && *list)
machintr_intr_enable(intr);
crit_exit_gd(gd);
crit_enter_gd(gd);
switch(info->i_state) {
case ISTATE_NORMAL:
if (lseconds != gd->gd_time_seconds) {
lseconds = gd->gd_time_seconds;
ill_count = 0;
}
if (info->i_flags & INTR_HIFREQ)
upper_limit = livelock_limit_hi;
else
upper_limit = livelock_limit;
if (ill_count <= upper_limit) {
if (info->i_running == 0) {
lwkt_deschedule_self(gd->gd_curthread);
lwkt_switch();
}
break;
}
kprintf("intr %d on cpu%d at %d/%d hz, livelocked limit engaged!\n",
intr, cpuid, ill_count, upper_limit);
info->i_state = ISTATE_LIVELOCKED;
if ((use_limit = upper_limit) < 100)
use_limit = 100;
else if (use_limit > 500000)
use_limit = 500000;
systimer_init_periodic_nq(&ill_timer, ithread_livelock_wakeup,
(void *)(intptr_t)intr, use_limit);
case ISTATE_LIVELOCKED:
lwkt_deschedule_self(gd->gd_curthread);
lwkt_switch();
if (lseconds != gd->gd_time_seconds) {
lseconds = gd->gd_time_seconds;
if (ill_count < livelock_lowater) {
info->i_state = ISTATE_NORMAL;
systimer_del(&ill_timer);
kprintf("intr %d on cpu%d at %d/%d hz, livelock removed\n",
intr, cpuid, ill_count, livelock_lowater);
} else if (livelock_debug == intr ||
(bootverbose && cold)) {
kprintf("intr %d on cpu%d at %d/%d hz, in livelock\n",
intr, cpuid, ill_count, livelock_lowater);
}
ill_count = 0;
}
break;
}
}
}
static void
ithread_emergency(void *arg __unused)
{
globaldata_t gd = mycpu;
struct intr_info *info;
intrec_t rec, nrec;
int intr, cpuid = mycpuid;
TD_INVARIANTS_DECLARE;
get_mplock();
crit_enter_gd(gd);
TD_INVARIANTS_GET(gd->gd_curthread);
for (;;) {
for (intr = 0; intr < max_installed_hard_intr[cpuid]; ++intr) {
info = &intr_block->ary[cpuid][intr];
for (rec = info->i_reclist; rec; rec = nrec) {
nrec = rec->next;
if ((rec->intr_flags & INTR_NOPOLL) == 0) {
if (rec->serializer) {
lwkt_serialize_handler_try(rec->serializer,
rec->handler, rec->argument, NULL);
} else {
rec->handler(rec->argument, NULL);
}
TD_INVARIANTS_TEST(gd->gd_curthread, rec->name);
}
}
}
lwkt_deschedule_self(gd->gd_curthread);
lwkt_switch();
}
}
static
void
emergency_intr_timer_callback(systimer_t info, int in_ipi __unused,
struct intrframe *frame __unused)
{
if (emergency_intr_enable)
lwkt_schedule(info->data);
}
static int
sysctl_intrnames(SYSCTL_HANDLER_ARGS)
{
struct intr_info *info;
intrec_t rec;
int error = 0;
int len;
int intr, cpuid;
char buf[64];
for (cpuid = 0; cpuid < ncpus; ++cpuid) {
for (intr = 0; error == 0 && intr < MAX_INTS; ++intr) {
info = &intr_block->ary[cpuid][intr];
len = 0;
buf[0] = 0;
for (rec = info->i_reclist; rec; rec = rec->next) {
ksnprintf(buf + len, sizeof(buf) - len, "%s%s",
(len ? "/" : ""), rec->name);
len += strlen(buf + len);
}
if (len == 0) {
ksnprintf(buf, sizeof(buf), "irq%d", intr);
len = strlen(buf);
}
error = SYSCTL_OUT(req, buf, len + 1);
}
}
return (error);
}
SYSCTL_PROC(_hw, OID_AUTO, intrnames, CTLTYPE_OPAQUE | CTLFLAG_RD,
NULL, 0, sysctl_intrnames, "", "Interrupt Names");
static int
sysctl_intrcnt_all(SYSCTL_HANDLER_ARGS)
{
struct intr_info *info;
int error = 0;
int intr, cpuid;
for (cpuid = 0; cpuid < ncpus; ++cpuid) {
for (intr = 0; intr < MAX_INTS; ++intr) {
info = &intr_block->ary[cpuid][intr];
error = SYSCTL_OUT(req, &info->i_count, sizeof(info->i_count));
if (error)
goto failed;
}
}
failed:
return(error);
}
SYSCTL_PROC(_hw, OID_AUTO, intrcnt_all, CTLTYPE_OPAQUE | CTLFLAG_RD,
NULL, 0, sysctl_intrcnt_all, "", "Interrupt Counts");
SYSCTL_PROC(_hw, OID_AUTO, intrcnt, CTLTYPE_OPAQUE | CTLFLAG_RD,
NULL, 0, sysctl_intrcnt_all, "", "Interrupt Counts");
static void
int_moveto_destcpu(int *orig_cpuid0, int cpuid)
{
int orig_cpuid = mycpuid;
if (cpuid != orig_cpuid)
lwkt_migratecpu(cpuid);
*orig_cpuid0 = orig_cpuid;
}
static void
int_moveto_origcpu(int orig_cpuid, int cpuid)
{
if (cpuid != orig_cpuid)
lwkt_migratecpu(orig_cpuid);
}
static void
intr_init(void *dummy __unused)
{
int cpuid;
kprintf("Initialize MI interrupts for %d cpus\n", ncpus);
intr_block = kmalloc(offsetof(struct intr_info_block, ary[ncpus][0]),
M_INTRMNG, M_INTWAIT | M_ZERO);
for (cpuid = 0; cpuid < ncpus; ++cpuid) {
int intr;
for (intr = 0; intr < MAX_INTS; ++intr) {
struct intr_info *info = &intr_block->ary[cpuid][intr];
info->i_cpuid = cpuid;
info->i_intr = intr;
}
}
}
SYSINIT(intr_init, SI_BOOT2_FINISH_PIC, SI_ORDER_ANY, intr_init, NULL);